симптомы
Выполняя нагрузочное тестирование моего приложения C ++, которое постоянно (и интенсивно) взаимодействует с использованием экземпляра SQL Server и подключением ODBC, я начал замечать утечки дескрипторов в диспетчере задач Windows. Они не были там раньше (пожалуйста, обуздать свой скептицизм и читать дальше), и я подозреваю, что они развивались во время упомянутого нагрузочного тестирования.
Запуск точно такого же двоичного файла на альтернативном компьютере не показывает те же симптомы, но вместо этого коррелирует с ожидаемым поведением, которое было стандартным и обычным до начала утечки. То есть нет утечки с тем же двоичным файлом на другом компьютере.
Я проследил эту утечку дескриптора в процессе подключения / отключения SQL и смог воссоздать с помощью консольного приложения, которое только открывает и закрывает соединение с экземпляром SQL Server, смоделированным на Пример SQLDriverConnect () MSDN. Код должен быть показан ниже.
Технические детали используемых компонентов
Что было опробовано без успеха
Подключение к другому экземпляру SQL-сервера.
Использование другого драйвера ODBC.
Восстановите Visual Studio 2013 Pro и восстановите бинарный файл.
Переустановите Visual Studio 2013 Pro и восстановите бинарный файл.
Переустановите SSMS (при попытке обновить локальные встроенные драйверы).
Удалите все компоненты, содержащие «SQL», с ПК и установите последнюю версию SSMS (чтобы уменьшить конфликты компонентов).
Извлечение только подозрительных компонентов в собственное консольное приложение
Попытайтесь SQLConnect (), используя системный DSN по умолчанию вместо SQLDriverConnect () со всеми явно указанными параметрами.
Обновлен драйвер ODBC для SQL Server 13 до 2015.131.4413.46.
Источник консольного приложения
// ConsoleTest.cpp : Defines the entry point for the console application.
//
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <string>
#include <sqlext.h>
#include <sqltypes.h>
#include <iostream>
#include "MyTypes.h"
#pragma comment(lib, "ODBC32.lib")
static s32 PrintSqlDiagRecords(SQLRETURN _sqlRet, SQLSMALLINT _sqlHandleType, SQLHANDLE _sqlHandle);
int _tmain(int argc, _TCHAR* argv[])
{
std::cout << "Starting test" << std::endl;
SQLHANDLE m_sqlHndlEnvironment = NULL;
SQLHANDLE m_sqlHndlConnection = NULL;
for (int k = 0; k < 500; ++k)
{
//Allocate environment handle
SQLRETURN ssiResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_sqlHndlEnvironment);
if ((ssiResult != SQL_SUCCESS) && (ssiResult != SQL_SUCCESS_WITH_INFO))
{
std::cout << "Error allocating ENV handle" << std::endl;
return -1;
}
//Set environment attribute
ssiResult = SQLSetEnvAttr(m_sqlHndlEnvironment, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
if ((ssiResult != SQL_SUCCESS) && (ssiResult != SQL_SUCCESS_WITH_INFO))
{
std::cout << "Error setting ODBC version" << std::endl;
return -1;
}
//Allocate connection handle
ssiResult = SQLAllocHandle(SQL_HANDLE_DBC, m_sqlHndlEnvironment, &m_sqlHndlConnection);
if ((ssiResult != SQL_SUCCESS) && (ssiResult != SQL_SUCCESS_WITH_INFO))
{
std::cout << "Error allocating DBC handle" << std::endl;
return -1;
}
(void)SQLSetConnectAttr(m_sqlHndlConnection, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
//Connect SQL
SQLCHAR acOutmsg[1024] = { 0 };
ssiResult = SQLDriverConnect(
m_sqlHndlConnection,
NULL,
(SQLCHAR*)"My Verified Connection String",
SQL_NTSL,
acOutmsg,
sizeof(acOutmsg),
NULL,
SQL_DRIVER_NOPROMPT);
if (ssiResult != SQL_SUCCESS)
{
(void) PrintSqlDiagRecords(ssiResult, SQL_HANDLE_DBC, m_sqlHndlConnection);
//If success with info, just dump diagnostic info
if (ssiResult == SQL_SUCCESS_WITH_INFO) {/*Do nothing for now*/ }
//Else error
else
{
std::cout << "Error connecting to DB" << std::endl;
return -1;
}
}
//Leave out actual DB operation to simplify execution path, but spin here for a while
Sleep(10);
//Free handles
SQLRETURN sqlRet = SQL_SUCCESS;
if (m_sqlHndlConnection != NULL)
{
sqlRet = SQLDisconnect(m_sqlHndlConnection);
if (sqlRet != SQL_SUCCESS)
{
(void)PrintSqlDiagRecords(ssiResult, SQL_HANDLE_DBC, m_sqlHndlConnection);
}
sqlRet = SQLFreeHandle(SQL_HANDLE_DBC, m_sqlHndlConnection);
if (sqlRet != SQL_SUCCESS)
{
std::cout << "Error freeing DBC handle" << std::endl;
return -1;
}
m_sqlHndlConnection = NULL;
}
//Free environment handle
if (m_sqlHndlEnvironment != NULL)
{
sqlRet = SQLFreeHandle(SQL_HANDLE_ENV, m_sqlHndlEnvironment);
if (sqlRet != SQL_SUCCESS)
{
std::cout << "Error freeing ENV handle" << std::endl;
return -1;
}
m_sqlHndlEnvironment = NULL;
}
}
std::cout << "Test complete" << std::endl;
Sleep(5000);
return 0;
}
s32 PrintSqlDiagRecords(SQLRETURN _sqlRet, SQLSMALLINT _sqlHandleType, SQLHANDLE _sqlHandle)
{
SQLRETURN sqlRetDiag = SQL_SUCCESS;
SQLINTEGER sqliNativeError = SQL_SUCCESS;
SQLSMALLINT sqlsiMsgLen = 0;
SQLCHAR acOutmsg[1024] = { 0 };
SQLCHAR acSqlState[1024] = { 0 };
int i = 1;
sqlRetDiag = SQLGetDiagRec(_sqlHandleType, _sqlHandle, i, acSqlState, &sqliNativeError, acOutmsg, sizeof(acOutmsg), &sqlsiMsgLen);
while ((sqlRetDiag != SQL_NO_DATA) && (i < 100))
{
//std::cout << "Msg[" << i <<"]: " << acOutmsg << "State: " << acSqlState << std::endl;
++i;
memset(acOutmsg, 0, sizeof(acOutmsg));
memset(acSqlState, 0, sizeof(acSqlState));
sqlRetDiag = SQLGetDiagRec(_sqlHandleType, _sqlHandle, i, acSqlState, &sqliNativeError, acOutmsg, sizeof(acOutmsg), &sqlsiMsgLen);
}
return 0;
}
наблюдения
Есть идеи, что является причиной утечки?
Любое понимание будет с благодарностью.
С уважением.
РЕДАКТИРОВАТЬ 7/7/2017: Обновите и измените цель вопроса на форуме, чтобы ориентироваться на исправления.
ОБНОВЛЕНИЕ 6/7/2017:
Отладка вышеуказанного приложения при попытке попытаться найти, какая часть процесса не освобождает все свои дескрипторы.
(A) Win 10 Pro, VS2013, VC ++ v120, SQL Server ODBC v10.00.15063.00 — 4 итерации, в среднем 1 утечка дескриптора за итерацию
(B) Win Server 2012 R2, VS2013, VC ++ v120, ODBC для SQL Server 6.03.9600.17415 — 4 итерации — постоянная использования сетевого дескриптора
Предоставляется довольно маленький размер выборки:
Задача ещё не решена.
Других решений пока нет …