Каталог решений - Загрузи кучу данных и подчисти хвосты

Загрузи кучу данных и подчисти хвосты

Загрузи кучу данных и подчисти хвосты

В наличии

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

Категория:

Описание

Сценарий

  • Имеется длинная транзакция, которая должна либо завершиться, либо полностью отмениться
  • Внутри транзакции создается большое количество объектов в БД
  • Необходимо собрать все возможные ошибки обрабатываемых объектов, а не падать на первой попавшейся ошибке

Описание приложенной конфигурации

  • Документ1 — документ, инициирующий длинную транзакцию. Служит для сохранения ошибок
  • Документ2 — документ, способный вызвать исключение:
    • вне транзакции — события модуля объекта ОбработкаПроверкиЗаполнения. Генерируем случайное число. Если не делится на 2, то вызываем исключение
    • в транзакции — событие модуля объекта ПриЗаписи. Генерируем случайное число. Если не делится на 3, то вызываем исключение
  • Команда Обработать в форме списка документа Документ1
    • Создает документ Документ1
    • Начинает длинную транзакцию
    • Создает 100 документов Документ2 в цикле
    • Для каждого документа проверяется заполнение и запись — соответственно будет исключение в транзакции или без
    • Сохраняет ошибки создания документов Документ2
    • Записывает документ Документ1

 

Классическая схема

  • Правила использования транзакций описаны на сайте ИТС: Транзакции: правила использования
  • Пример кода из приложенной конфигурации:
    • Создается документ Документ1 — служит для хранения ошибок обработки
    • Начинается длинная транзакция
    • Внутри транзакции создаются документы Документ2 (код будет ниже), которые могут вызывать исключения (в транзакции или без)
Процедура СоздатьДокумент1()

  Док = Документы.Документ1.СоздатьДокумент();
  Док.Дата = ТекущаяДатаСеанса();

  НачатьТранзакцию();
  Попытка
    СоздатьДокументы2(Док);

    ЗафиксироватьТранзакцию();
  Исключение
    ОтменитьТранзакцию();
    Сообщить(ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
  КонецПопытки;

  Док.Записать();

КонецПроцедуры

МИНУСЫ:

  • Первое исключение внутри транзакции останавливает дальнейшую обработку объектов
  • Нет возможности увидеть ошибки после исключения (картинка ниже)

 

Вариант 1. Добавляем попытку

  • Каждый обрабатываемый объект внутри длинной транзакции оборачиваем в попытку
  • В случае исключения длинная транзакция продолжается
  • Если в попытке есть своя транзакция (явная или неявная) и в ней произошло исключение (кружок №2 на картинке), то все последующие обращения к БД будут вызывать ошибку В данной транзакции уже происходили ошибки
Процедура СоздатьДокументы2(Док)
 
  БылиОшибки = Ложь;
 
  Для Индекс = 1 По 100 Цикл
    НоваяСтрока = Док.Данные.Добавить(); // Новая строка в документе 1 (для ошибки)
    
    Попытка
      СоздатьДокумент2(); // Тут возможны исключения в транзакции и без
      
      НоваяСтрока.Ошибка = НСтр("ru = 'Ошибок нет'");
    Исключение
      БылиОшибки = Истина;
      НоваяСтрока.Ошибка = КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
    КонецПопытки;
  КонецЦикла;
 
  Если БылиОшибки Тогда
    ВызватьИсключение НСтр("ru = 'В процессе обработки были ошибки, изменения не сохраняем'");
  КонецЕсли;
 
КонецПроцедуры

МИНУСЫ:

  • После первого исключения внутри "маленькой" транзакции все последующие ошибки будут неинформативными (картинка ниже)
  • И даже если в обрабатываемой строке не должно быть ошибки (но есть запись в БД), то все равно будет ошибка В данной транзакции уже происходили ошибки

 

Вариант 2. Находим сломанные транзакции

  • Выявляем момент, когда в транзакции уже происходили ошибки
  • Это можно сделать выполнив простейший запрос ВЫБРАТЬ 1 (решение предложено тут Безопасная работа с транзакциями на встроенном языке)
  • Если такой момент выявили, то отменяем транзакцию и тут же начинаем новую
  • Получается мы "режем" длинную транзакцию на части, давая возможность продолжить обращаться к БД и выявлять все последующие ошибки
  • Если во время длинной транзакции не произошло ни одной ошибки, то она успешно завершится
  • Если произошла хотя бы одна ошибка в конце обработчика отменяем транзакцию
Процедура СоздатьДокументы2(Док)
 
  БылиОшибки = Ложь;
 
  Для Индекс = 1 По 100 Цикл
    НоваяСтрока = Док.Данные.Добавить(); // Новая строка в документе 1 (для ошибки)
    
    Попытка
      Если ВТранзакцииПроисходилиОшибки() Тогда
        ОтменитьТранзакцию();
        НачатьТранзакцию();
      КонецЕсли;
    
      СоздатьДокумент2(); // Тут возможны исключения в транзакции и без
      
      НоваяСтрока.Ошибка = НСтр("ru = 'Ошибок нет'");
    Исключение
      БылиОшибки = Истина;
      НоваяСтрока.Ошибка = КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
    КонецПопытки;
  КонецЦикла;
 
  Если БылиОшибки Тогда
    ВызватьИсключение НСтр("ru = 'В процессе обработки были ошибки, изменения не сохраняем'");
  КонецЕсли;
 
КонецПроцедуры

Ожидаемый результат:

 

PS. Вариант без транзакций

  • Создаем объекты (каждый в своей попытке) и сохраняем на них ссылки (в массив)
  • Если произошла ошибка, то сохраняем её (в массив ошибок)
  • Если в процессе обработки произошла хоть одна ошибка, то помечаем объекты массива на удаление и затем физически удаляем, а пользователю выводим ошибки из массива ошибок

МИНУСЫ:

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

Именно от такого подхода избавлялся

has been added to your cart:
Оформление заказа