При работе в конфигурации 1С:Управление торговлей 10.3 (особенно в клиент-серверном варианте на MS SQL Server) программисты часто сталкиваются с проблемой взаимных блокировок при одновременной попытке нескольких кассиров пробить чек. Ситуация выглядит следующим образом: когда два кассира нажимают кнопку оплаты практически одновременно, один чек успешно печатается и проводится, а второй получает ошибку is blocked от SQL-сервера. Это приводит к рассинхронизации: чек на кассовом аппарате может быть пробит, а документ в базе 1С не создается или не проводится.
В этой статье мы подробно разберем причины возникновения таких блокировок и рассмотрим несколько эффективных стратегий их устранения, основываясь на опыте внедрения высоконагруженных систем РМК (Рабочее место кассира).
Прежде всего, проанализируем, почему типовой или самописный механизм в УТ 10.3 дает сбои. В старых конфигурациях 1С используется режим автоматических блокировок. В этом режиме MS SQL Server самостоятельно решает, какой объем данных заблокировать при выполнении транзакции. Часто вместо блокировки одной строки блокируется вся таблица (эскалация блокировок).
Основные факторы, провоцирующие проблему:
НачатьТранзакцию() и ЗафиксироватьТранзакцию()), то база данных остается заблокированной на все время работы железного принтера (от 2 до 5 секунд).ТоварыНаСкладах). Если два кассира продают один и тот же товар, они неизбежно встанут в очередь.Самый эффективный способ, который применяется в современных конфигурациях (например, «1С:Розница 2.3»), заключается в изменении последовательности действий. Рассмотрим стандартную (ошибочную) логику и оптимизированную.
Ошибочная логика: Пробить чек на ККТ -> Если успешно, провести документ в 1С.
Правильная логика:
ЧекККМ в базу (статус «не проведен»).Разберем, почему это лучше. Если документ провелся в 1С, значит, товар списан и все проверки пройдены. Если после этого касса выдаст ошибку (например, кончилась бумага), у вас останется проведенный документ, который можно повторно отправить на печать без блокировки остатков.
Если блокировка носит кратковременный характер (SQL просто не успел освободить таблицу), можно реализовать механизм «мягкого» ожидания. Вместо того чтобы сразу выкидывать ошибку кассиру, программа совершит несколько попыток проведения с небольшой паузой.
Рассмотрим пример кода для реализации такого механизма:
ПопыткаПроведения = 0;
МаксПопыток = 5;
УспешноПроведен = Ложь;
Пока ПопыткаПроведения < МаксПопыток Цикл
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
УспешноПроведен = Истина;
Прервать;
Исключение
ПопыткаПроведения = ПопыткаПроведения + 1;
// Пауза 1-2 секунды перед следующей попыткой
Предупреждение("База данных занята, ожидайте...", 1);
КонецПопытки;
КонецЦикла;
Если Не УспешноПроведен Тогда
Сообщить("Не удалось провести чек из-за длительной блокировки. Попробуйте позже.");
КонецЕсли;
Этот метод позволяет сгладить пиковые нагрузки, когда несколько кассиров одновременно закрывают чеки.
Проанализируем типовую функцию получения номера чека. В старых релизах УТ 10.3 часто встречается вызов функции, которая ищет максимальный номер чека в базе. Это «узкое место».
Рекомендуется пересмотреть использование функции КассовыеСменыВызовСервера.ТекущийНомерЧека. Вместо того чтобы запрашивать номер из базы 1С до пробития, лучше получать его напрямую из драйвера ККТ после успешной фискализации. Таким образом, мы исключаем лишний блокирующий запрос к тяжелой таблице документов перед началом транзакции.
Важнейшее правило: взаимодействие с внешним оборудованием не должно находиться внутри транзакции 1С. Рассмотрим, как правильно структурировать код в обработке печати чека:
// 1. Предварительная запись документа
ОбъектЧека.Записать(РежимЗаписиДокумента.Запись);
// 2. Попытка проведения (открывается короткая транзакция 1С)
Попытка
ОбъектЧека.Записать(РежимЗаписиДокумента.Проведение);
Исключение
Сообщить("Ошибка проведения: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// 3. Печать на ККТ (ВНЕ транзакции базы данных)
РезультатПечати = НапечататьЧекНаККТ(ОбъектЧека);
Если РезультатПечати.Успех Тогда
// 4. Записываем фискальные данные (еще одна короткая транзакция)
ОбъектЧека.НомерЧекаККМ = РезультатПечати.НомерЧека;
ОбъектЧека.Записать(РежимЗаписиДокумента.Запись);
Иначе
Сообщить("Чек не пробит на ККТ, но проведен в 1С. Требуется ручное вмешательство.");
КонецЕсли;
Такой подход гарантирует, что SQL-сервер будет заблокирован лишь на доли секунды (пока списываются остатки), а не на все время, пока кассовый аппарат «жужжит» бумагой.
Если программные методы не помогают полностью избавиться от Deadlocks или Timeout, следует обратить внимание на настройки SQL-сервера:
ЧекККМ в режим управления блокировками. Это позволит блокировать данные по конкретному складу или товару программно, избегая блокировки всей таблицы SQL.Подводя итог, можно сказать, что «железное» проведение чеков в УТ 10.3 достигается путем выноса процесса печати за пределы транзакции проведения и внедрения алгоритмов повторных попыток записи при возникновении конфликтов блокировок.