Почему пропадают записи в регистре сведений 1С при быстрой записи и как это исправить

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

В процессе разработки на платформе 1С:Предприятие 8 часто возникает задача регистрации быстрых событий. Это может быть логирование отправки и получения HTTP-запросов, фиксация этапов выполнения длительных фоновых процессов или запись пакетов данных интеграции. На первый взгляд задача кажется тривиальной: создаем периодический регистр сведений, пишем туда события через менеджер записи или набор записей. Однако на практике разработчики часто сталкиваются с загадочным поведением системы: часть записей бесследно исчезает из базы данных.

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

Суть проблемы: почему пропадают записи?

Давайте проанализируем ситуацию на примере кода, предоставленного автором темы на форуме. Код выглядит следующим образом:


МенеджерЗаписи = РегистрыСведений.ЦеныНаТопливо.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Период = ТекущаяДата();
МенеджерЗаписи.ВидТоплива = ВидТоплива;
МенеджерЗаписи.ПоставщикТоплива = ПоставщикТоплива;
МенеджерЗаписи.Записать();

Для периодического регистра сведений уникальность каждой записи (ее ключ) определяется строго по комбинации: Период + Измерения. В рассматриваемом примере измерениями являются Период и ВидТоплива. Поле ПоставщикТоплива, судя по всему, является ресурсом или реквизитом.

Рассмотрим по шагам, что происходит при выполнении этого кода на высокой скорости:

  1. Дискретность времени в 1С: Стандартная функция ТекущаяДата() возвращает системное время с точностью до секунды. Миллисекунды этой функцией не учитываются.
  2. Высокая скорость процессов: Логирование отправки и получения HTTP-запросов происходит практически мгновенно. Весь цикл обмена данными может занять менее 100 миллисекунд. В результате и отправка запроса, и получение ответа происходят внутри одной и той же секунды.
  3. Механика Менеджера записи: Когда мы вызываем метод МенеджерЗаписи.Записать(), платформа 1С выполняет транзакционное действие в СУБД. Она проверяет, существует ли уже в базе данных запись с таким ключом (совпадающим периодом и измерениями).
    • Если записи с таким ключом еще нет, создается новая строка.
    • Если запись с таким ключом (та же секунда периода и тот же вид топлива) уже существует, платформа 1С производит обновление (перезапись) этой строки новыми значениями ресурсов и реквизитов. Старая информация при этом безвозвратно затирается.

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

Почему при отладке все работает правильно?

Разница в поведении кода при отладке и в режиме реальной работы — классический симптом рассинхронизации по времени. Когда разработчик идет по коду пошагово (нажимая клавишу F10 в конфигураторе), между выполнением строк кода проходит физическое время. На анализ одной строки уходит как минимум одна-две секунды. За это время системные часы успевают сдвинуться вперед. Функция ТекущаяДата() на каждом шаге возвращает новое уникальное значение секунды. Ключ записи каждый раз получается уникальным, и перезаписи не происходит. В обычном же режиме код выполняется за миллисекунды, и даты совпадают.


Как решить проблему: рабочие варианты

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

Вариант 1. Использование уникального идентификатора (GUID)

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

Пошаговый алгоритм:

  1. Добавим в структуру измерений нашего регистра сведений новое измерение ИдентификаторЗаписи с типом УникальныйИдентификатор (или Строка длиной 36 символов, если требуется совместимость со старыми конфигурациями).
  2. При формировании записи будем генерировать новый уникальный идентификатор.

Посмотрим на пример измененного кода:


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

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

Вариант 2. Использование миллисекунд (начиная с платформы 8.3.17)

Если для бизнес-логики крайне важно сохранять точную хронологическую последовательность записей с точностью выше одной секунды, стандартный период регистра нам не поможет. Начиная с версии платформы 1С 8.3.17, разработчикам доступна функция работы с миллисекундами.

Пошаговый алгоритм:

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

Рассмотрим пример кода:


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

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

Вариант 3. Переход на непериодический регистр сведений

Если для регистра сведений не требуется получать "срез последних" на определенную дату (что обычно и бывает при логировании), использование периодического регистра сведений избыточно. Гораздо правильнее спроектировать регистр как непериодический.

При такой структуре:

При этом платформа 1С не будет контролировать уникальность записей по времени, индекс таблицы СУБД станет более компактным, а запись логов будет происходить быстрее.


Архитектурные риски логирования в базу данных 1С

Запись логов HTTP-запросов и интеграционных пакетов непосредственно в транзакционную базу данных 1С таит в себе серьезные опасности, о которых должен знать каждый разработчик.

  1. Блокировки СУБД: Использование МенеджерЗаписи для частой фиксации событий создает значительную нагрузку. При вызове метода Записать() платформа сначала выполняет неявный запрос SELECT к базе данных для проверки существования записи, а затем INSERT или UPDATE. В условиях многопоточности это приводит к частым эскалациям блокировок таблиц СУБД, замедляя работу основных пользователей.
  2. Раздувание базы данных (Data Bloat): Логи HTTP-запросов часто содержат объемные XML или JSON-пакеты. Если записывать их в обычные таблицы 1С, база данных начнет лавинообразно расти. Это увеличивает время создания резервных копий и снижает скорость выполнения любых других запросов.
  3. Проблема откатов транзакций: Если логирование происходит внутри транзакции (например, при проведении документа происходит интеграционный запрос), и в процессе проведения возникает ошибка, вся транзакция откатывается. Вместе с ней из базы данных удалятся и записи логов. Вы потеряете историю интеграционного запроса, хотя физически запрос был отправлен на сторонний сервис.

Альтернативные варианты логирования

Для снижения нагрузки на основную базу данных рекомендуется использовать альтернативные подходы:

← На главную