Как исправить утечку дескриптора в процессе подключения / отключения ODBC C ++?

симптомы

Выполняя нагрузочное тестирование моего приложения C ++, которое постоянно (и интенсивно) взаимодействует с использованием экземпляра SQL Server и подключением ODBC, я начал замечать утечки дескрипторов в диспетчере задач Windows. Они не были там раньше (пожалуйста, обуздать свой скептицизм и читать дальше), и я подозреваю, что они развивались во время упомянутого нагрузочного тестирования.

Запуск точно такого же двоичного файла на альтернативном компьютере не показывает те же симптомы, но вместо этого коррелирует с ожидаемым поведением, которое было стандартным и обычным до начала утечки. То есть нет утечки с тем же двоичным файлом на другом компьютере.

Я проследил эту утечку дескриптора в процессе подключения / отключения SQL и смог воссоздать с помощью консольного приложения, которое только открывает и закрывает соединение с экземпляром SQL Server, смоделированным на Пример SQLDriverConnect () MSDN. Код должен быть показан ниже.

  • Похоже, не все выделенные дескрипторы освобождены.
  • Возвращаемые значения показывают, что все операции SQL в коде выполняются без ошибок.

Технические детали используемых компонентов

  • Дев машина (утечка). Windows 7 Pro SP1.
    • Драйверы ODBC пробовали:
    • SQL Server 6.01.7601.17514
    • Драйвер ODBC 13 для SQL Server 2015.130.1601.05.
    • Драйвер ODBC 13 для SQL Server 2015.131.4413.46.
  • Разработка машины с обновленной ОС (утечка). Windows 10 Pro
    • Драйверы ODBC пробовали:
    • SQL Server 10.00.15063.00
    • Драйвер ODBC 13 для SQL Server 2017.140.500.272.
  • Альтернативный станок (без утечек). Windows Server 2012 R2.
    • Драйвер ODBC: Sql Server 6.03.9600.17415
  • Visual Studio 2013 (v120) Консольное приложение.
    • Стандартные библиотеки окон
    • Набор символов не установлен.
    • Нет поддержки CLR.
    • Нет всей программы или C ++ оптимизации.
  • Соединения с SQL Server пробовали с компьютера Dev.
    • Экспресс 9.0.5.
    • 13.0.1728.2.

Что было опробовано без успеха

  1. Подключение к другому экземпляру SQL-сервера.

  2. Использование другого драйвера ODBC.

  3. Восстановите Visual Studio 2013 Pro и восстановите бинарный файл.

  4. Переустановите Visual Studio 2013 Pro и восстановите бинарный файл.

  5. Переустановите SSMS (при попытке обновить локальные встроенные драйверы).

  6. Удалите все компоненты, содержащие «SQL», с ПК и установите последнюю версию SSMS (чтобы уменьшить конфликты компонентов).

  7. Извлечение только подозрительных компонентов в собственное консольное приложение

  8. Попытайтесь SQLConnect (), используя системный DSN по умолчанию вместо SQLDriverConnect () со всеми явно указанными параметрами.

  9. Обновлен драйвер 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;
}

наблюдения

  1. Один и тот же двоичный файл на другом компьютере, на котором нет этих утечек, указывает на проблему, локализованную на ПК разработчика.
  2. Воссоздание с различными версиями драйверов SQL Server снижает вероятность того, что он связан с драйвером ODBC.
  3. Воссоздание после обновления драйвера ODBC поддерживает 2., поскольку очевидно, что устаревшие / испорченные файлы драйвера были бы обновлены при новой установке.
  4. Повторяя процесс подключения / отключения 500 раз, остается ~ 580 ручек перед самым выходом из приложения.
    4.1 Прерывание выполнения после каждой итерации показывает 1 дескриптор утечки / итерации.
  5. Выполнение тестового приложения консоли на альтернативном компьютере показывает стабильное число дескрипторов на уровне ~ 135 во время выполнения и после завершения всех итераций.

Есть идеи, что является причиной утечки?

Любое понимание будет с благодарностью.

С уважением.


РЕДАКТИРОВАТЬ 7/7/2017: Обновите и измените цель вопроса на форуме, чтобы ориентироваться на исправления.

  • Исходный контекст вопроса должен был указать причину, чтобы я мог ее исправить. Сейчас акцент сместился на поиск решения и определение причины.
  • После ввода @JeroenMostert я сделал краткий анализ, чтобы попытаться определить причину для операции, которая может быть причиной проблемы.

ОБНОВЛЕНИЕ 6/7/2017:

  • Не упомянул, что недавно я восстановил свою ОС Win 7 Pro на SSD незадолго до появления симптомов. Утечка дескриптора отсутствовала в течение первых нескольких дней после перемещения ОС (более подробная информация представлена ​​выше).
  • Предполагается, что возможная ссылка на комбинацию Win 7 — SSD, так как консольное приложение не показывало утечек при попытке на компьютере с Windows 8.1 или Windows 10 (такое же поведение, как в Windows Server 2012 R2, упомянутом выше).
  • Чистая установка Windows 10 Pro на компьютере разработчика. Предварительные тесты с консольным приложением и оригинальным двоичным файлом приложения вскоре после установки ОС прошли успешно.
  • Установил VS2017 и сохранил исходные VC ++ lib и настройки целевой платформы без изменений.
  • Установлены разные компоненты, необходимые для повседневных операций.
  • Возобновляя разработку, утечка дескриптора снова присутствовала с той же моделью симптомов, когда двоичный файл на локальной машине реагировал иначе, чем тот же двоичный прогон на других доверенных машинах.
  • Чистое консольное приложение, созданное до обновления ОС и проверенное на других тестовых компьютерах, демонстрирует такое же поведение утечки только на моей машине.
  • Консольное приложение с использованием SQL Server ODBC v10.00.15063.00 и драйвера ODBC 13 для SQL Server 2017.140.500.272 продемонстрировало утечку.
  • временное решение на данный момент предполагается, что я не могу доверять потреблению дескриптора моей машины (по крайней мере, в том, что касается соединений SQL), и проверять использование дескриптора на нашем промежуточном сервере.

Отладка вышеуказанного приложения при попытке попытаться найти, какая часть процесса не освобождает все свои дескрипторы.

(A) Win 10 Pro, VS2013, VC ++ v120, SQL Server ODBC v10.00.15063.00 — 4 итерации, в среднем 1 утечка дескриптора за итерацию

  • Подключение: +5; +6; +6; +6
  • Отключить: -1; -1; -1; -1
  • DBC бесплатно: -1; -2; -2; -2
  • ENV бесплатно: -1; -2; -2; -1

(B) Win Server 2012 R2, VS2013, VC ++ v120, ODBC для SQL Server 6.03.9600.17415 — 4 итерации — постоянная использования сетевого дескриптора

  • Подключение: +10; +11; +10; +10
  • Отключить: -1; -1; -1; -1
  • DBC бесплатно: -2; -2; -3; -2
  • ENV бесплатно: -7; -7; -7; -7

Предоставляется довольно маленький размер выборки:

  • Подозреваемый ENV без ручки оп является виновником. Использование является постоянным в рабочем случае (B) и более переменным в (A).
  • Отключение op кажется постоянным в обоих случаях.
  • DBC free кажется довольно постоянным в обоих случаях (1 смена за 4 итерации)

2

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector