Возобновление обработки 1С с прерванного места после конфликта блокировок

Программист 1С v8.3 (Управляемые формы)
← На главную

При выполнении длительных операций в 1С, таких как массовое изменение и перепроведение документов из внешнего источника (например, файла Excel), разработчики часто сталкиваются с проблемой конфликтов блокировок. Эти конфликты могут привести к аварийному завершению обработки, вынуждая пользователя начинать процесс заново. Мы с вами рассмотрим подробные стратегии и практические примеры кода, которые помогут вашей обработке продолжить работу с того момента, на котором она остановилась, значительно повышая надежность и удобство использования.

Общая концепция решения: Попытка...Исключение и сохранение состояния

Основой для реализации механизма продолжения обработки после сбоя является комбинация двух ключевых подходов:

  1. Обработка исключений: Используем конструкцию Попытка...Исключение для перехвата ошибок, в частности, конфликтов блокировок.
  2. Сохранение состояния: Запоминаем текущий прогресс обработки (например, номер последней успешно обработанной строки из Excel), чтобы при следующем запуске можно было начать не с нуля.

Давайте разберем эти шаги подробнее и рассмотрим, как их реализовать в 1С.

Детализация хранения состояния обработки

Для того чтобы обработка могла "помнить", на какой строке она остановилась, нам необходимо где-то хранить эту информацию. В 1С существует несколько механизмов для этого, каждый со своими особенностями.

Хранилище общих настроек

Один из самых простых и быстрых способов сохранения небольших объемов данных, привязанных к сеансу или пользователю, это использование ХранилищаОбщихНастроек. Рассмотрим, как это можно сделать:

  1. Запись состояния: После каждой успешно обработанной строки или перед началом обработки следующей, мы записываем в хранилище служебную информацию. Это может быть имя файла Excel, который обрабатывается, и номер последней успешно обработанной строки.
  2. Чтение состояния: При запуске обработки мы проверяем наличие сохраненной информации в хранилище. Если она есть и соответствует текущему файлу, мы можем предложить пользователю продолжить с ранее прерванной строки.
  3. Очистка состояния: После полного и успешного завершения обработки мы очищаем сохраненные данные, чтобы следующий запуск начался с нуля, если это не продолжение прерванной ранее задачи.

Преимущества: Простота реализации, подходит для настроек пользователя.
Недостатки: Менее структурированно, чем регистры, может быть неудобно для хранения информации о нескольких файлах или длительных задач.

Регистры сведений

Для более структурированного, надежного и долговременного сохранения состояния обработки, особенно между различными сеансами или для централизованного управления фоновыми заданиями, мы рекомендуем использовать Регистры сведений. Это позволяет хранить произвольные данные в разрезах (измерениях), что очень удобно для наших целей:

  1. Создание регистра: Мы создаем новый регистр сведений (например, "СостояниеОбработок") с измерениями, такими как "ИмяФайлаОбработки", "ИдентификаторПользователя" или "ИдентификаторЗадания", и ресурсом "НомерПоследнейСтроки" (или "ПоследняяОбработаннаяСтрока"). Также можно добавить измерения для типа обработки, даты начала, даты последнего обновления.
  2. Запись и чтение: Работать с регистрами сведений мы можем стандартными средствами 1С: создавать записи, устанавливать фильтры по измерениям для получения нужных данных, изменять существующие записи.
  3. Надежность: Регистры сведений обеспечивают надежное хранение информации непосредственно в базе данных, что гарантирует сохранность данных даже при полном перезапуске системы.

Преимущества: Высокая надежность, структурированное хранение данных, возможность отслеживания состояния множества обработок.
Недостатки: Требует создания объекта метаданных, что несколько усложняет быструю реализацию.

Временное хранилище

Существует также Временное хранилище, которое используется для кратковременного хранения данных (файлов, изображений, текста) в рамках текущего сеанса или для передачи данных между клиентом и сервером. Однако, данные во временном хранилище привязаны к сеансу или форме и автоматически удаляются по завершении использования или закрытии сеанса. Это делает его менее подходящим для сохранения состояния обработки, которое должно быть восстановлено после полного завершения сеанса пользователя или при перезапуске обработки после сбоя.

Обработка исключений и пример кода

Давайте посмотрим на пример кода, который объединяет идею сохранения состояния и обработки исключений, основанный на предложениях сообщества.


Перем ПоследняяОбработаннаяСтрока;
Перем ИмяФайлаОбработки; // Добавим для хранения имени файла, чтобы отличать задачи

Процедура ПриОткрытии()
    // При открытии формы обработки, проверяем, нет ли сохраненного состояния
    ИмяФайлаОбработки = ""; // Инициализируем
    ПоследняяОбработаннаяСтрока = 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;
    КонецЦикла;
    Возврат Таблица;
КонецФункции

// Заглушка для Задержки
Процедура Задержка(Миллисекунды)
    Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
    Пока ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало < Миллисекунды Цикл
        ОбработкаПрерыванияПользователя();
    КонецЦикла;
КонецПроцедуры

Давайте проанализируем этот пример:

  1. Переменные состояния: Мы используем глобальные переменные ПоследняяОбработаннаяСтрока и ИмяФайлаОбработки для отслеживания текущего прогресса и идентификации задачи.
  2. ПриОткрытии(): В этом обработчике события формы мы проверяем наличие сохраненного состояния. Если оно найдено, мы задаем пользователю вопрос о продолжении, сбросе или отмене. Это важный момент для удобства пользователя.
  3. Цикл обработки: Каждая строка из Excel (представленная здесь как СтрокаТЗ из ТаблицаДанныхИзExcel) обрабатывается в цикле. Мы используем условие Если ТекущийНомерСтроки <= ПоследняяОбработаннаяСтрока Тогда Продолжить;, чтобы пропустить уже обработанные ранее строки.
  4. Попытка...Исключение: Блок Попытка...Исключение окружает код, который непосредственно занимается проведением документов. Это позволяет нам перехватывать ошибки.
  5. Определение ошибки блокировки: В блоке Исключение мы пытаемся определить, является ли ошибка конфликтом блокировок. К сожалению, в 1С нет прямого типа исключения ОшибкаБлокировки, поэтому нам приходится анализировать текст ОписаниеОшибки() на наличие характерных фраз (например, "Конфликт блокировок").
  6. Сохранение состояния при ошибке: Если обнаружена ошибка блокировки, мы немедленно сохраняем текущее значение ПоследняяОбработаннаяСтрока с помощью функции ЗаписатьСостояние() и прекращаем выполнение обработки. Это гарантирует, что при следующем запуске мы сможем продолжить.
  7. Очистка состояния: После успешного завершения всей обработки (без ошибок) мы вызываем УдалитьСостояние(), чтобы очистить сохраненные данные.
  8. Функции ЗаписатьСостояние(), ЗагрузитьСостояние(), УдалитьСостояние(): Эти вспомогательные функции инкапсулируют логику работы с ХранилищемОбщихНастроек, делая основной код чище.

Этот подход позволяет пользователю перезапустить обработку после сбоя, и она автоматически подхватит выполнение с прерванного места.

Дополнительные методы борьбы с конфликтами блокировок и оптимизация

Помимо прямого сохранения состояния и обработки исключений, существует ряд стратегических и тактических приемов, которые помогут вам минимизировать количество блокировок и сделать обработку более стабильной и производительной. Мы рассмотрим их подробнее.

1. Использование фоновых заданий

Суть: Длительные и ресурсоемкие операции, такие как массовое перепроведение документов, рекомендуется выносить в фоновые задания. Это означает, что обработка будет выполняться на сервере 1С в отдельном процессе, не блокируя пользовательский интерфейс и позволяя пользователю продолжить работу с системой.

Как это помогает:

  1. Снижение блокировок интерактивных сеансов: Фоновое задание работает независимо, что снижает вероятность блокировок, возникающих из-за одновременного доступа к данным из интерактивного сеанса и вашей обработки.
  2. Устойчивость: Фоновые задания могут быть более устойчивы к кратковременным сетевым разрывам или проблемам на клиентской машине.
  3. Мониторинг и возобновление: Состояние выполнения фонового задания также можно сохранять в регистрах сведений для последующего мониторинга или возобновления, аналогично тому, как мы делали это для клиентской обработки.

Методы 1С: Используйте объекты ФоновыеЗадания, ПараметрыФоновогоЗадания, РегламентныеЗадания.

2. Минимизация длительности транзакций

Суть: Чем короче транзакция (период между НачатьТранзакцию() и ЗафиксироватьТранзакцию() или ОтменитьТранзакцию()), тем меньше вероятность возникновения блокировок, так как ресурсы удерживаются минимальное время.

Как это помогает:

  1. Быстрое освобождение ресурсов: Если документ проводится или изменяется в рамках одной короткой транзакции, связанные с ним таблицы базы данных быстро освобождаются для других операций.
  2. Оптимизация кода: Проанализируйте код проведения документов. Убедитесь, что внутри транзакций выполняются только необходимые действия. Избегайте "тяжелых" запросов, длительных циклов или обращений к внешним ресурсам внутри транзакции.
  3. Чтение без блокировок: Если данные нужны только для чтения, используйте запросы без установки блокировок. Например, при получении данных для заполнения документа, старайтесь не блокировать эти данные, если они не будут изменяться.

3. Управляемые блокировки

Суть: В платформе 1С существует два режима блокировок: автоматический (по умолчанию для обычных форм и многих объектов в управляемых) и управляемый. Перевод конфигурации в режим управляемых блокировок (или использование его для отдельных объектов) дает программисту больше контроля.

Как это помогает:

  1. Точечная блокировка: С помощью объекта БлокировкаДанных можно программно устанавливать необходимые блокировки на конкретные таблицы базы данных или даже отдельные записи, выбирая нужный уровень изоляции (например, "ДляИзменения", "ДляЧтения"). Это позволяет снизить избыточность блокировок, характерную для автоматического режима, когда платформа может блокировать больше, чем реально требуется.
  2. Предотвращение взаимоблокировок: Критически важно блокировать ресурсы в едином, предсказуемом порядке (например, всегда сначала блокировать регистры, потом справочники, или в алфавитном порядке). Это помогает избежать так называемых "взаимоблокировок" (deadlocks), когда два или более процесса ожидают друг друга, имея частичное владение нужными ресурсами.

4. Разделение итогов

Суть: Если блокировки часто возникают на регистрах накопления или бухгалтерии, особенно при массовом формировании движений, можно разрешить разделение итогов для этих регистров.

Как это помогает:

  1. Повышение параллельности: Разделение итогов позволяет платформе записывать движения в регистры более параллельно, используя разные внутренние структуры для хранения итогов. Это значительно снижает вероятность конфликтов при одновременной записи движений разными пользователями или процессами.
  2. Конфигуратор: Эта настройка задается в конфигураторе для конкретного регистра.

5. Избегание "тяжелых" операций в рабочее время

Суть: Планируйте выполнение служебных обработок (например, удаление помеченных объектов, поиск дублей, массовое перепроведение, пересчет итогов) на нерабочее время.

Как это помогает:

  1. Минимум пользователей: В нерабочие часы количество активных пользователей в системе минимально или отсутствует, что резко снижает вероятность конфликтов блокировок.
  2. Отсутствие помех: Длительные обработки не будут мешать основной работе пользователей и не вызовут длительные блокировки, влияющие на производительность системы в целом.

6. Предотвращение самоблокировок пользователей

Суть: Продумайте административные меры или программные ограничения, чтобы пользователи не могли одновременно работать с одними и теми же документами из нескольких своих сеансов. Например, если пользователь открыл документ в одной вкладке браузера, он не должен иметь возможность открыть тот же документ в другой вкладке, чтобы редактировать его.

Как это помогает:

  1. Устранение простых конфликтов: Многие блокировки возникают из-за неосторожности самих пользователей, работающих с одним и тем же объектом.
  2. Повышение стабильности: Программные проверки при открытии форм документов могут помочь предотвратить такие ситуации.

7. Диагностика с помощью технологического журнала

Суть: Для выявления причин и виновников блокировок (пользователь, документ, контекст, длительность транзакции, конкретный SQL-запрос) используйте технологический журнал 1С.

Как это помогает:

  1. Точное определение проблемы: Технологический журнал (ТЖ) позволяет получить детальную информацию о работе платформы и базы данных. Настроив сбор событий блокировок (например, DBMSSQL, TLOCK, TDEADLOCK), вы сможете точно определить, какие объекты блокируются, кто их блокирует, и какие операции приводят к конфликтам.
  2. Анализ и оптимизация: Анализ журнала поможет определить, какие транзакции и запросы вызывают проблемы, что в свою очередь укажет на узкие места в коде, требующие оптимизации.

8. Привилегированный режим

Суть: В некоторых случаях, для ускорения группового проведения документов или выполнения других ресурсоемких операций, отдельные процедуры могут быть вынесены в привилегированный режим.

Как это помогает:

  1. Ускорение выполнения: Код, выполняемый в привилегированном режиме, обходит стандартные проверки прав доступа к данным, что может дать существенный прирост производительности при массовых операциях.
  2. Использование с осторожностью: Важно использовать привилегированный режим крайне осторожно, только для тех частей кода, где вы абсолютно уверены в корректности операций и где не требуется дополнительная проверка прав. Неконтролируемое использование привилегированного режима может создать серьезные дыры в безопасности системы.

Применение этих методов в комплексе значительно повысит устойчивость и производительность ваших обработок в 1С, особенно при работе с большими объемами данных и в условиях высокой конкуренции за ресурсы базы данных. Мы рассмотрели основные подходы, от базового сохранения состояния до продвинутых техник управления блокировками. Выбирайте те решения, которые наилучшим образом подходят для вашей конкретной задачи и конфигурации.

← На главную