В процессе разработки и сопровождения конфигураций на платформе 1С:Предприятие разработчики часто сталкиваются со сбоями в автонумерации документов или справочников. Для решения этой проблемы существует стандартный метод ОбновитьНумерациюОбъектов(). Однако, чтобы протестировать поведение системы и убедиться, что код исправления работает корректно, нам сначала необходимо искусственно воспроизвести ("сбить") нумерацию в тестовой базе данных. В этой статье мы подробно разберем, как устроен механизм нумерации изнутри, в чем разница между различными типами сбоев и какими способами можно гарантированно сбить кэш автонумерации для отладки.
Прежде чем переходить к практическим примерам, давайте проанализируем природу возникновения ошибок в номерах объектов. На практике выделяют два абсолютно разных механизма нарушения последовательности номеров:
0000-001/А), встроенный алгоритм генерации номеров 1С теряет ориентир. Платформа пытается распарсить строковый номер справа налево, чтобы найти числовую часть и инкрементировать ее. Не найдя число в конце строки, система не может прибавить единицу, из-за чего автонумерация "застревает" или начинает выдавать ошибки уникальности. Метод ОбновитьНумерациюОбъектов() в этом случае абсолютно бесполезен, так как некорректный номер физически записан в базе данных. Поможет только ручное или программное переименование ошибочного номера.00001, 00002, 00003), однако при создании нового документа платформа неожиданно выдает номер с огромным пропуском, например 00100. Это связано с тем, что сервер приложений 1С хранит кэш последнего выданного номера в оперативной памяти. Если максимальный документ с номером 00099 был физически удален из базы или перенесен в другой период, сервер "не замечает" этого в реальном времени и продолжает отсчет от ранее запомненного значения. Именно для устранения таких несоответствий и предназначен метод ОбновитьНумерациюОбъектов().Для оптимизации производительности в многопользовательском режиме платформа 1С не обращается к СУБД с тяжелыми запросами вроде SELECT MAX(Номер) при каждом создании нового документа. Вместо этого сервер приложений 1С (процесс rphost) кэширует диапазоны номеров в оперативной памяти.
Поведение этого кэша напрямую регулируется свойством объекта метаданных "Режим автонумерации объектов". Рассмотрим два режима его работы:
УстановитьНовыйНомер() в модуле объекта), этот номер считается занятым безвозвратно. Даже если пользователь закрыл документ без сохранения, этот номер больше никогда не будет предложен системой, что приводит к появлению естественных пропусков ("дыр") в нумерации.Теперь, понимая внутреннюю кухню платформы, мы можем рассмотреть практические способы воспроизведения кэш-уровневого сбоя для тестирования.
Рассмотрим самый простой программный метод, который позволяет быстро сбить кэш автонумерации без изменения настроек метаданных или прямого доступа к СУБД. Суть метода заключается в последовательном получении нескольких номеров, записи финального объекта и его последующем непосредственном удалении.
Разберем по шагам код обработки, выполняющей эту операцию:
// Создаем новый объект документа программно
НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
НовыйДокумент.Дата = ТекущаяДата();
// Запускаем цикл для искусственного увеличения счетчика автонумерации на сервере
Для Итератор = 1 По 5 Цикл
НовыйДокумент.УстановитьНовыйНомер();
КонецЦикла;
// Записываем документ с завышенным номером в базу данных
НовыйДокумент.Записать();
// Получаем ссылку на только что созданный документ
СсылкаНаУдаление = НовыйДокумент.Ссылка;
// Удаляем документ непосредственно из базы данных в обход корзины
СсылкаНаУдаление.ПолучитьОбъект().Удалить();
Почему это сбивает нумерацию? При вызове метода УстановитьНовыйНомер() пять раз подряд сервер приложений фиксирует в своем кэше последнее выданное значение. Мы записываем этот объект в базу данных (тем самым легализуя номер в системе), а затем сразу удаляем его методом Удалить(). В результате в базе данных последним остается документ, например, с номером 00005, но серверный кэш уверен, что последний выданный номер — 00010. При создании следующего документа система выдаст номер 00011, пропустив номера с 6 по 10. Вызов метода ОбновитьНумерациюОбъектов() после этой процедуры должен вернуть нумерацию к значению 00006.
Рассмотрим еще один очень жизненный сценарий, имитирующий ситуацию, когда бухгалтер вручную изменил номер последнего документа на меньший, нарушив логику автонумерации.
Выполним следующие шаги для воспроизведения сбоя:
00001 до 00010.00010 и программно или вручную изменим его номер на 00005 (убедившись, что этот номер свободен в текущем периоде, либо предварительно переименовав старый документ).00009.rphost, который все еще "помнит" выданный ранее номер 00010, и автоматически присвоит новому документу номер 00011. Налицо необоснованный пропуск номеров в базе данных.Для восстановления правильной последовательности нам потребуется очистить кэш сервера с помощью тестируемого метода.
Проанализируем метод симуляции сбоя, который идеально подходит для объектов, у которых в конфигураторе установлено свойство "Режим автонумерации объектов" в значение "Не освобождать автоматически".
Для этого мы можем использовать следующий программный код:
НачатьТранзакцию();
Для Счетчик = 1 По 10 Цикл
ТестовыйОбъект = Документы.ЗаказПокупателя.СоздатьДокумент();
ТестовыйОбъект.Дата = ТекущаяДата();
// Запрашиваем новый номер у сервера приложений
ТестовыйОбъект.УстановитьНовыйНомер();
КонецЦикла;
// Отменяем транзакцию, не записывая созданные объекты в базу данных
ОтменитьТранзакцию();
Поскольку транзакция была отменена, ни один из десяти сгенерированных документов не запишется в базу данных СУБД. Однако из-за специфики режима "Не освобождать автоматически" сервер приложений 1С зафиксирует эти 10 номеров как использованные. При создании следующего документа в пользовательском режиме нумерация начнется сразу с одиннадцатой позиции, создав огромную пустоту в реестре документов.
Этот способ отлично моделирует последствия аварийного восстановления таблиц СУБД из резервной копии или некорректной работы сторонних интеграционных утилит в клиент-серверном варианте работы 1С.
Проанализируем алгоритм действий:
00050. Сервер приложений 1С кэширует эту позицию как максимальную.
DELETE FROM _Document150 WHERE _Number FIELD LIKE '%00045' OR _Number FIELD LIKE '%00050';
_Document150 — имя физической таблицы документов в вашей базе данных).*00051, несмотря на то, что физически в базе данных последним остался документ с номером 00044.Этот жесткий рассинхрон оперативной памяти сервера приложений и физических таблиц базы данных гарантированно лечится только вызовом метода обновления нумерации объектов.
Рассмотрим специфический сценарий, характерный для территориально распределенных систем, использующих механизмы РИБ:
После того как мы успешно сбили нумерацию одним из вышеперечисленных способов, пришло время проверить, как работает механизм исправления. Для этого нам необходимо вызвать встроенную функцию платформы.
Обратите внимание, что метод ОбновитьНумерациюОбъектов() принимает на вход массив метаданных или конкретный объект метаданных. Разберем пример программного вызова для очистки кэша конкретного типа документов:
// Создаем массив с описанием типов объектов, для которых нужно сбросить кэш
МассивМетаданных = Новый Массив;
МассивМетаданных.Добавить(Метаданные.Документы.РеализацияТоваровУслуг);
// Вызываем метод платформы для обновления кэша автонумерации
ОбновитьНумерациюОбъектов(МассивМетаданных);
// Сообщаем пользователю об успешном завершении операции
Сообщить("Кэш автонумерации для документа 'Реализация товаров и услуг' успешно сброшен!");
После выполнения этого кода сервер 1С полностью очистит таблицы выданных номеров в оперативной памяти rphost, заново обратится к таблицам СУБД, найдет реальный физический максимум и возобновит корректную последовательную нумерацию со следующего свободного значения.