Как предотвратить перезапись данных в регистре сведений при интенсивном обмене через API

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

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

Анализируем причину проблемы: почему данные пропадают?

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

Проанализируем ситуацию: если ваш скрипт обмена выполняет чтение, запись и логирование ошибок в течение 100–200 миллисекунд, все эти события получат одну и ту же отметку времени. Чтобы этого избежать, нам нужно либо изменить структуру хранения, либо искусственно повысить уникальность ключа записи.

Решение 1: Использование непериодического регистра с уникальным измерением

Самый простой и эффективный способ для логов — сделать регистр непериодическим. В этом случае поле Период исчезает, и уникальность определяется только измерениями. Однако нам все равно нужно соблюдать хронологию. Разберем, как это сделать правильно.

Добавим в регистр измерение МоментВремениЧислом с типом Число(15, 0). В него мы будем записывать значение, полученное функцией ТекущаяУниверсальнаяДатаВМиллисекундах(). Посмотрим на пример кода:


МенеджерЗаписи = РегистрыСведений.ЛогАПИ.СоздатьМенеджерЗаписи();
МенеджерЗаписи.ПериодЧислом = ТекущаяУниверсальнаяДатаВМиллисекундах();
МенеджерЗаписи.Событие = "Начало чтения данных";
МенеджерЗаписи.Сессия = ИдентификаторСессии;
МенеджерЗаписи.Записать();

Важный нюанс: Стандартный тип данных Дата в 1С хранит только секунды. Если вы попытаетесь записать миллисекунды в обычное поле даты, они будут отсечены. Именно поэтому мы используем числовое поле. Это позволит нам сортировать записи в отчетах по возрастанию этого числа, точно соблюдая порядок возникновения событий.

Решение 2: Добавление GUID для гарантированной уникальности

Если даже миллисекунд недостаточно (например, при многопоточной обработке, когда два потока могут вызвать функцию в один и тот же такт процессора), можно применить так называемый «грязный хак». Добавим измерение ИдентификаторЗаписи с типом УникальныйИдентификатор.

Выясним преимущества этого метода: УникальныйИдентификатор (GUID) гарантирует, что ни одна запись не будет перезаписана, так как ключ уникальности всегда будет разным. Проанализируем пример реализации:


НаборЗаписей = РегистрыСведений.ЛогАПИ.СоздатьНаборЗаписей();
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Период = ТекущаяДата();
НоваяЗапись.ИдентификаторЗаписи = Новый УникальныйИдентификатор();
НоваяЗапись.Сообщение = "Ошибка при парсинге JSON";
НаборЗаписей.Записать(Ложь); // Параметр Ложь позволяет дописывать данные, не затирая существующие

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

Решение 3: Объект «Документ» как контейнер для событий

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

Почему это удобно:

  1. Порядок строк в табличной части гарантирован платформой (индекс строки).
  2. Вся информация об одном сеансе обмена сгруппирована в одном месте.
  3. Легко реализовать механизмы очистки старых данных (удаление документов).

Рассмотрим пример заполнения такого лога в процессе итерации по массиву данных из API:


ДокЛог = Документы.ЛогСессииАПИ.СоздатьДокумент();
ДокЛог.Дата = ТекущаяДата();
ДокЛог.Статус = "В процессе";

Для Каждого СтрокаОтвета Из МассивДанныхАПИ Цикл
    НоваяСтрока = ДокЛог.События.Добавить();
    НоваяСтрока.Время = ТекущаяУниверсальнаяДатаВМиллисекундах();
    НоваяСтрока.Текст = "Обработка объекта: " + СтрокаОтвета.Имя;
КонецЦикла;

ДокЛог.Записать(РежимЗаписиДокумента.Запись);

Оптимизация производительности: Batch Writing (Пакетная запись)

Запись в регистр или документ на каждой итерации цикла обмена — это «смерть» для производительности, так как каждое обращение к базе данных инициирует новую транзакцию. Проанализируем ситуацию: если у вас 1000 записей, лучше накопить их в памяти и записать один раз.

Для этого удобно использовать объект ДополнительныеСвойства или просто временную ТаблицуЗначений. Посмотрим, как это реализовать:


ТЗ_Лог = Новый ТаблицаЗначений;
ТЗ_Лог.Колонки.Добавить("Событие");
ТЗ_Лог.Колонки.Добавить("Момент");

// ... в цикле наполняем ТЗ ...
НоваяСтрока = ТЗ_Лог.Добавить();
НоваяСтрока.Событие = "Данные получены";
НоваяСтрока.Момент = ТекущаяУниверсальнаяДатаВМиллисекундах();

// В конце процесса записываем всё одним набором
НаборЗаписей = РегистрыСведений.ЛогАПИ.СоздатьНаборЗаписей();
НаборЗаписей.Загрузить(ТЗ_Лог);
НаборЗаписей.Записать(Ложь);

Подведение итогов и рекомендации

Разберем по шагам, что нужно сделать, чтобы ваш лог обмена работал идеально:

  1. Определитесь с типом регистра: Для технических логов лучше использовать непериодический независимый регистр. Это снимет лишние ограничения на уникальность по времени.
  2. Добавьте составной ключ: Используйте комбинацию ИдентификаторСессии + НомерСтроки (или Миллисекунды). Это обеспечит 100% уникальность.
  3. Используйте управляемые блокировки: Если обмен идет в несколько потоков, убедитесь, что регистр работает в режиме управляемых блокировок, чтобы сессии не ждали друг друга при записи лога.
  4. Не забывайте про Журнал регистрации: Если вам не нужно строить сложные отчеты, возможно, системный метод ЗаписьЖурналаРегистрации() будет более уместным. Он работает асинхронно и практически не нагружает основную транзакцию.

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

← На главную