При работе с внешними базами данных через COM-объект ADODB.Connection часто возникает ситуация, когда установленное соединение становится неактуальным. Это может произойти из-за сетевых сбоев, плановых работ на стороне SQL-сервера или его временной недоступности. Если ваша задача требует стабильной работы (например, формирование сложного отчета, использующего данные из внешнего источника), необходимо предусмотреть механизм оперативной проверки связи и автоматического переключения на резервный сервер. Разберем, как реализовать такую проверку «на лету» и какие нюансы следует учитывать.
Рассмотрим типичную ситуацию. У нас есть функция, которая инициализирует подключение к основному серверу, а в случае неудачи — к резервному. Проблема заключается в том, что после успешного открытия соединения (метод Open()) объект ADODB.Connection может храниться в памяти долгое время. Если за этот период сервер «упал» или соединение было разорвано со стороны SQL, объект в 1С все еще будет существовать, но любая попытка выполнить запрос приведет к фатальной ошибке.
Многие разработчики пытаются использовать свойство State(). Однако важно понимать: State возвращает состояние объекта на основе последней успешной операции. Если физический разрыв произошел мгновение назад, свойство может продолжать возвращать значение 1 (соответствует константе adStateOpen), пока вы не попытаетесь отправить данные в канал.
Проанализируем наиболее надежный вариант проверки — выполнение максимально простого SQL-запроса непосредственно перед выполнением основных расчетов. Это позволяет убедиться, что канал передачи данных активен и база данных готова к работе.
Рассмотрим пример реализации функции проверки:
Функция ПодключениеАктуально(ТекущееСоединение)
Если ТекущееСоединение = Неопределено Тогда
Возврат Ложь;
КонецЕсли;
// Проверяем статус объекта в памяти
Если ТекущееСоединение.State() = 0 Тогда
Возврат Ложь;
КонецЕсли;
Попытка
// Выполняем максимально легкий запрос к серверу
// CommandTimeout ставим минимальный, чтобы не вешать интерфейс
ТекущееСоединение.Execute("SELECT 1");
Возврат Истина;
Исключение
// Если запрос не прошел, значит связь потеряна
Возврат Ложь;
КонецПопытки;
КонецФункции
В этом примере мы используем запрос SELECT 1. Он практически не нагружает сервер, но гарантирует, что сессия активна. Если функция возвращает Ложь, программа должна инициировать процедуру переподключения к резервному узлу.
Иногда сервер доступен, но конкретная база данных переведена в режим OFFLINE или SINGLE_USER из-за технических работ. Чтобы понять, что пора переключаться на реплику, можно проверить состояние базы в системном представлении sys.databases. Разберем, как это реализовать:
ТекстЗапроса = "SELECT t.state_desc FROM sys.databases t WHERE t.name = 'ИмяВашейБазы'";
Recordset = Соединение.Execute(ТекстЗапроса);
Если СокрЛП(Recordset.Fields("state_desc").Value) <> "ONLINE" Тогда
// База недоступна для полноценной работы, нужно переподключение
ПереподключитьсяКРезерву();
КонецЕсли;
Важный нюанс: этот метод сработает только в том случае, если само соединение с сервером еще живо. Если упал весь SQL-сервер, обращение к sys.databases вызовет исключение, которое также нужно обрабатывать через Попытка — Исключение.
Проанализируем архитектурный подход. Если специфика вашей задачи позволяет, лучше не хранить объект Connection между вызовами. В среде SQL Server широко используется механизм Connection Pooling (пул соединений). Это означает, что когда вы вызываете Open() и Close(), физическое соединение не всегда разрывается мгновенно — драйвер ADO сохраняет его в пуле для повторного использования.
Если ваши расчеты не зависят от состояния сессии (например, вы не используете локальные временные таблицы SQL, начинающиеся с символа #), то наиболее надежным вариантом будет создание нового подключения на каждую операцию. Это автоматически снимет вопрос об актуальности «старого» соединения.
Выясним причину, по которой иногда нельзя просто переподключиться. Если ваш алгоритм в 1С строит сложную логику на стороне SQL с использованием #temp_tables, то переподключение уничтожит эти таблицы, так как они живут только в рамках одной сессии (одного конкретного соединения).
В такой ситуации разберем следующий алгоритм действий:
State() и выполняем тестовый запрос.cnn.Properties("Server Name").Value для понимания того, на каком сервере мы сейчас находимся, чтобы в будущем вернуть пользователя на основной сервер, как только он станет доступен.Чтобы сделать процесс переподключения более быстрым и стабильным, обратим внимание на параметры строки подключения (ConnectionString):
MSOLEDBSQL (Microsoft OLE DB Driver for SQL Server), так как старый драйвер SQL Server может некорректно обрабатывать современные протоколы безопасности и типы данных.Рассмотрим пример настройки свойств объекта перед открытием:
cnn = Новый COMОбъект("ADODB.Connection");
cnn.ConnectionString = "Provider=MSOLEDBSQL;Data Source=" + ИмяСервера + ";Initial Catalog=" + ИмяБазы + ";User ID=" + Логин + ";Password=" + Пароль + ";";
cnn.ConnectionTimeOut = 30; // Время ожидания самого подключения
cnn.CommandTimeOut = 600; // Время ожидания выполнения тяжелых запросов
cnn.CursorLocation = 3; // adUseClient
cnn.Open();
Использование CursorLocation = 3 (клиентский курсор) часто помогает избежать проблем с блокировками на сервере и позволяет более гибко работать с полученным набором записей (Recordset) внутри 1С.
Таким образом, комбинируя периодическую проверку «легким» запросом и грамотную обработку исключений, мы можем создать отказоустойчивую систему взаимодействия с внешними данными, которая незаметно для пользователя переключится на резервный сервер в случае аварии.