При выполнении длительных операций в 1С, таких как массовое изменение и перепроведение документов из внешнего источника (например, файла Excel), разработчики часто сталкиваются с проблемой конфликтов блокировок. Эти конфликты могут привести к аварийному завершению обработки, вынуждая пользователя начинать процесс заново. Мы с вами рассмотрим подробные стратегии и практические примеры кода, которые помогут вашей обработке продолжить работу с того момента, на котором она остановилась, значительно повышая надежность и удобство использования.
Основой для реализации механизма продолжения обработки после сбоя является комбинация двух ключевых подходов:
Попытка...Исключение для перехвата ошибок, в частности, конфликтов блокировок.Давайте разберем эти шаги подробнее и рассмотрим, как их реализовать в 1С.
Для того чтобы обработка могла "помнить", на какой строке она остановилась, нам необходимо где-то хранить эту информацию. В 1С существует несколько механизмов для этого, каждый со своими особенностями.
Один из самых простых и быстрых способов сохранения небольших объемов данных, привязанных к сеансу или пользователю, это использование ХранилищаОбщихНастроек. Рассмотрим, как это можно сделать:
Преимущества: Простота реализации, подходит для настроек пользователя.
Недостатки: Менее структурированно, чем регистры, может быть неудобно для хранения информации о нескольких файлах или длительных задач.
Для более структурированного, надежного и долговременного сохранения состояния обработки, особенно между различными сеансами или для централизованного управления фоновыми заданиями, мы рекомендуем использовать Регистры сведений. Это позволяет хранить произвольные данные в разрезах (измерениях), что очень удобно для наших целей:
Преимущества: Высокая надежность, структурированное хранение данных, возможность отслеживания состояния множества обработок.
Недостатки: Требует создания объекта метаданных, что несколько усложняет быструю реализацию.
Существует также Временное хранилище, которое используется для кратковременного хранения данных (файлов, изображений, текста) в рамках текущего сеанса или для передачи данных между клиентом и сервером. Однако, данные во временном хранилище привязаны к сеансу или форме и автоматически удаляются по завершении использования или закрытии сеанса. Это делает его менее подходящим для сохранения состояния обработки, которое должно быть восстановлено после полного завершения сеанса пользователя или при перезапуске обработки после сбоя.
Давайте посмотрим на пример кода, который объединяет идею сохранения состояния и обработки исключений, основанный на предложениях сообщества.
Перем ПоследняяОбработаннаяСтрока;
Перем ИмяФайлаОбработки; // Добавим для хранения имени файла, чтобы отличать задачи
Процедура ПриОткрытии()
// При открытии формы обработки, проверяем, нет ли сохраненного состояния
ИмяФайлаОбработки = ""; // Инициализируем
ПоследняяОбработаннаяСтрока = 0; // Инициализируем
// Предположим, что мы получаем имя файла из пользовательского выбора
// Для примера, пусть будет "МойФайл.xlsx"
// В реальной обработке, это будет имя выбранного пользователем файла
// Например: ДиалогВыбораФайла.ПолноеИмя;
Если ЗначениеЗаполнено(ИмяФайлаОбработки) Тогда
// Попытаемся загрузить состояние
ПоследняяОбработаннаяСтрока = ЗагрузитьСостояние(ИмяФайлаОбработки);
Если ПоследняяОбработаннаяСтрока > 0 Тогда
Вопрос = Вопрос("Найдено прерванное задание для файла '" + ИмяФайлаОбработки + "' на строке №" + ПоследняяОбработаннаяСтрока + ". Продолжить выполнение?", РежимДиалогаВопрос.ДаНетОтмена);
Если Вопрос = КодВозвратаДиалога.Да Тогда
Сообщить("Продолжаем выполнение с строки №" + ПоследняяОбработаннаяСтрока);
ИначеЕсли Вопрос = КодВозвратаДиалога.Нет Тогда
ПоследняяОбработаннаяСтрока = 0; // Начать сначала
УдалитьСостояние(ИмяФайлаОбработки); // Очищаем старое состояние
Сообщить("Начинаем выполнение с начала.");
Иначе
Отказ = Истина; // Отмена выполнения обработки
Возврат;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Процедура ОбработатьФайл(ПолноеИмяФайлаExcel)
ИмяФайлаОбработки = ПолноеИмяФайлаExcel; // Сохраняем имя файла для текущей сессии
// Здесь должен быть код для чтения Excel файла
// Например:
// ОбъектExcel = Новый COMОбъект("Excel.Application");
// Книга = ОбъектExcel.Workbooks.Open(ПолноеИмяФайлаExcel);
// Лист = Книга.Sheets(1);
// Предположим, что у нас есть коллекция строк для обработки,
// где каждая строка имеет свой номер или индекс.
// Например, если мы загрузили все данные из Excel в ТаблицуЗначений:
ТаблицаДанныхИзExcel = ПолучитьДанныеИзExcel(ПолноеИмяФайлаExcel);
Для Каждого СтрокаТЗ Из ТаблицаДанныхИзExcel Цикл
// Мы используем условный "Номер" строки. В реальном коде это может быть
// ИндексСтроки + 1, если СтрокаТЗ - это элемент коллекции, или
// отдельная переменная-счетчик.
ТекущийНомерСтроки = СтрокаТЗ.Номер; // Допустим, в ТЗ есть колонка "Номер"
Если ТекущийНомерСтроки <= ПоследняяОбработаннаяСтрока Тогда
// Пропускаем уже обработанные строки
Продолжить;
КонецЕсли;
Попытка
// Код обработки документа для текущей строки
// Например:
// НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
// ЗаполнитьДокументИзСтроки(НовыйДокумент, СтрокаТЗ);
// НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
// Заглушка для примера:
Сообщить("Обработка строки №" + ТекущийНомерСтроки + "...");
ОбработатьДокумент(СтрокаТЗ); // Вызываем нашу функцию для обработки
// После успешной обработки документа, обновляем последнюю обработанную строку
ПоследняяОбработаннаяСтрока = ТекущийНомерСтроки;
ЗаписатьСостояние(ИмяФайлаОбработки, ПоследняяОбработаннаяСтрока); // Сохраняем состояние
Исключение
// Здесь обрабатываем исключения.
// Нам нужно определить, является ли ошибка конфликтом блокировок.
// В 1С это часто связано с сообщениями об ошибках транзакций.
// Точного типа ОшибкаБлокировки нет, но можно анализировать ОписаниеОшибки().
Если Найти(ОписаниеОшибки(), "Конфликт блокировок при выполнении транзакции") > 0
Или Найти(ОписаниеОшибки(), "Deadlock detected") > 0 Тогда
// Сохранение состояния обработки перед выходом
ЗаписатьСостояние(ИмяФайлаОбработки, ПоследняяОбработаннаяСтрока);
Сообщить("ВНИМАНИЕ! Обработка остановлена из-за конфликта блокировок на строке №" + ПоследняяОбработаннаяСтрока + ".");
// Мы останавливаем выполнение цикла. При следующем запуске
// обработка возобновится с сохраненной строки.
Возврат; // Выход из процедуры
Иначе
// Обработка других типов исключений
Сообщить("Произошла ошибка при обработке строки №" + ТекущийНомерСтроки + ": " + ОписаниеОшибки());
// В зависимости от критичности, можно либо продолжить, либо остановить.
// Для данного сценария, если ошибка не блокировка, останавливаем,
// чтобы пользователь мог разобраться.
Возврат;
КонецЕсли;
КонецПопытки;
КонецЦикла;
Сообщить("Обработка файла '" + ИмяФайлаОбработки + "' успешно завершена.");
// Очистка состояния после успешного завершения всех операций
УдалитьСостояние(ИмяФайлаОбработки);
ПоследняяОбработаннаяСтрока = 0; // Сбрасываем для следующей задачи
КонецПроцедуры
// Вспомогательная процедура (заглушка)
Процедура ОбработатьДокумент(СтрокаТЗ)
// Здесь должна быть реальная логика по созданию/модификации/проведению документа
// на основе данных из СтрокаТЗ.
// Например:
// Док = Документы.ПоступлениеТоваровУслуг.СоздатьДокумент();
// Док.Дата = СтрокаТЗ.ДатаПоступления;
// ...
// Док.Записать(РежимЗаписиДокумента.Проведение);
Задержка(100); // Имитация работы
КонецПроцедуры
// Функции для работы с состоянием (пример реализации через ХранилищеОбщихНастроек)
Функция ПолучитьКлючСостояния(ИмяФайла)
// Формируем уникальный ключ для нашего состояния
Возврат "СостояниеОбработки_" + ИмяФайла;
КонецФункции
Процедура ЗаписатьСостояние(ИмяФайла, НомерСтроки)
Если Не ЗначениеЗаполнено(ИмяФайла) Тогда
Возврат;
КонецЕсли;
Ключ = ПолучитьКлючСостояния(ИмяФайла);
ХранилищеОбщихНастроек.Сохранить(НомерСтроки, Ключ);
Сообщить("Состояние сохранено: " + НомерСтроки);
КонецПроцедуры
Функция ЗагрузитьСостояние(ИмяФайла)
Если Не ЗначениеЗаполнено(ИмяФайла) Тогда
Возврат 0;
КонецЕсли;
Ключ = ПолучитьКлючСостояния(ИмяФайла);
Состояние = ХранилищеОбщихНастроек.Загрузить(Ключ);
Если Состояние = Неопределено Тогда
Возврат 0;
Иначе
Возврат Состояние;
КонецЕсли;
КонецФункции
Процедура УдалитьСостояние(ИмяФайла)
Если Не ЗначениеЗаполнено(ИмяФайла) Тогда
Возврат;
КонецЕсли;
Ключ = ПолучитьКлючСостояния(ИмяФайла);
ХранилищеОбщихНастроек.Удалить(Ключ);
Сообщить("Состояние очищено.");
КонецПроцедуры
// Заглушка для получения данных из Excel
Функция ПолучитьДанныеИзExcel(ПолноеИмяФайлаExcel)
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Номер");
Таблица.Колонки.Добавить("Данные");
Для i = 1 По 100 Цикл // Предположим 100 строк в Excel
СтрокаТЗ = Таблица.Добавить();
СтрокаТЗ.Номер = i;
СтрокаТЗ.Данные = "Строка данных " + i;
КонецЦикла;
Возврат Таблица;
КонецФункции
// Заглушка для Задержки
Процедура Задержка(Миллисекунды)
Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
Пока ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало < Миллисекунды Цикл
ОбработкаПрерыванияПользователя();
КонецЦикла;
КонецПроцедуры
Давайте проанализируем этот пример:
ПоследняяОбработаннаяСтрока и ИмяФайлаОбработки для отслеживания текущего прогресса и идентификации задачи.ПриОткрытии(): В этом обработчике события формы мы проверяем наличие сохраненного состояния. Если оно найдено, мы задаем пользователю вопрос о продолжении, сбросе или отмене. Это важный момент для удобства пользователя.СтрокаТЗ из ТаблицаДанныхИзExcel) обрабатывается в цикле. Мы используем условие Если ТекущийНомерСтроки <= ПоследняяОбработаннаяСтрока Тогда Продолжить;, чтобы пропустить уже обработанные ранее строки.Попытка...Исключение: Блок Попытка...Исключение окружает код, который непосредственно занимается проведением документов. Это позволяет нам перехватывать ошибки.Исключение мы пытаемся определить, является ли ошибка конфликтом блокировок. К сожалению, в 1С нет прямого типа исключения ОшибкаБлокировки, поэтому нам приходится анализировать текст ОписаниеОшибки() на наличие характерных фраз (например, "Конфликт блокировок").ПоследняяОбработаннаяСтрока с помощью функции ЗаписатьСостояние() и прекращаем выполнение обработки. Это гарантирует, что при следующем запуске мы сможем продолжить.УдалитьСостояние(), чтобы очистить сохраненные данные.ЗаписатьСостояние(), ЗагрузитьСостояние(), УдалитьСостояние(): Эти вспомогательные функции инкапсулируют логику работы с ХранилищемОбщихНастроек, делая основной код чище.Этот подход позволяет пользователю перезапустить обработку после сбоя, и она автоматически подхватит выполнение с прерванного места.
Помимо прямого сохранения состояния и обработки исключений, существует ряд стратегических и тактических приемов, которые помогут вам минимизировать количество блокировок и сделать обработку более стабильной и производительной. Мы рассмотрим их подробнее.
Суть: Длительные и ресурсоемкие операции, такие как массовое перепроведение документов, рекомендуется выносить в фоновые задания. Это означает, что обработка будет выполняться на сервере 1С в отдельном процессе, не блокируя пользовательский интерфейс и позволяя пользователю продолжить работу с системой.
Как это помогает:
Методы 1С: Используйте объекты ФоновыеЗадания, ПараметрыФоновогоЗадания, РегламентныеЗадания.
Суть: Чем короче транзакция (период между НачатьТранзакцию() и ЗафиксироватьТранзакцию() или ОтменитьТранзакцию()), тем меньше вероятность возникновения блокировок, так как ресурсы удерживаются минимальное время.
Как это помогает:
Суть: В платформе 1С существует два режима блокировок: автоматический (по умолчанию для обычных форм и многих объектов в управляемых) и управляемый. Перевод конфигурации в режим управляемых блокировок (или использование его для отдельных объектов) дает программисту больше контроля.
Как это помогает:
БлокировкаДанных можно программно устанавливать необходимые блокировки на конкретные таблицы базы данных или даже отдельные записи, выбирая нужный уровень изоляции (например, "ДляИзменения", "ДляЧтения"). Это позволяет снизить избыточность блокировок, характерную для автоматического режима, когда платформа может блокировать больше, чем реально требуется.Суть: Если блокировки часто возникают на регистрах накопления или бухгалтерии, особенно при массовом формировании движений, можно разрешить разделение итогов для этих регистров.
Как это помогает:
Суть: Планируйте выполнение служебных обработок (например, удаление помеченных объектов, поиск дублей, массовое перепроведение, пересчет итогов) на нерабочее время.
Как это помогает:
Суть: Продумайте административные меры или программные ограничения, чтобы пользователи не могли одновременно работать с одними и теми же документами из нескольких своих сеансов. Например, если пользователь открыл документ в одной вкладке браузера, он не должен иметь возможность открыть тот же документ в другой вкладке, чтобы редактировать его.
Как это помогает:
Суть: Для выявления причин и виновников блокировок (пользователь, документ, контекст, длительность транзакции, конкретный SQL-запрос) используйте технологический журнал 1С.
Как это помогает:
DBMSSQL, TLOCK, TDEADLOCK), вы сможете точно определить, какие объекты блокируются, кто их блокирует, и какие операции приводят к конфликтам.Суть: В некоторых случаях, для ускорения группового проведения документов или выполнения других ресурсоемких операций, отдельные процедуры могут быть вынесены в привилегированный режим.
Как это помогает:
Применение этих методов в комплексе значительно повысит устойчивость и производительность ваших обработок в 1С, особенно при работе с большими объемами данных и в условиях высокой конкуренции за ресурсы базы данных. Мы рассмотрели основные подходы, от базового сохранения состояния до продвинутых техник управления блокировками. Выбирайте те решения, которые наилучшим образом подходят для вашей конкретной задачи и конфигурации.