Ошибка «переполнение стека встроенного языка на сервере» является одной из самых неприятных при работе с механизмом «Конвертация данных 2.0». Она практически всегда свидетельствует о наличии бесконечной рекурсии — ситуации, когда программный код или логика правил обмена вызывают сами себя бесконечное количество раз, пока выделенная серверу память под стек вызовов не будет полностью исчерпана.
В рамках этой статьи мы подробно разберем, почему возникает эта ошибка при использовании обработки «Универсальный обмен данными XML», как локализовать проблемный участок и какие методы исправления существуют в арсенале разработчика 1С.
Разберем теорию: стек вызовов — это область памяти, где хранятся данные о вызванных процедурах и функциях. Когда процедура «А» вызывает процедуру «Б», в стек записывается адрес возврата. Если же процедура «А» вызывает саму себя (напрямую или через цепочку других функций) без условия выхода, стек заполняется до предела, и платформа аварийно завершает работу процесса.
В контексте обмена данными через КД 2.0 (Конвертация данных) это происходит в трех основных случаях:
ПриЗагрузке, ПослеЗагрузки или в модулях объектов базы-приемника инициирует повторную запись или загрузку.Рассмотрим подробнее настройки в конфигурации «Конвертация данных». Часто разработчики создают правила для объектов, имеющих иерархическую структуру или ссылающихся на объекты того же типа. Проанализируем типичный пример: справочник Контрагенты и его реквизит ГоловнойКонтрагент.
Если в ПКО для контрагента указано, что свойство ГоловнойКонтрагент также конвертируется по ПКО Контрагенты, и при загрузке система пытается найти или создать этого родителя, может возникнуть петля. Особенно это актуально, если включен поиск по полям, которые в базе-источнике заполнены некорректно (например, два контрагента ссылаются друг на друга как на головные организации).
Что нужно проверить в ПКО:
ВыгрузитьОбъект или ЗагрузитьОбъект.Алгоритм1 вызывает Алгоритм2, а тот, в свою очередь, снова обращается к Алгоритм1, стек переполнится мгновенно.Иногда правила обмена идеальны, но ошибка все равно возникает. Это происходит из-за того, что загрузка данных через XML инициирует стандартные механизмы записи объектов в базе-приемнике. Рассмотрим ситуацию, когда в регистре сведений или справочнике написан «кривой» код.
Посмотрим на пример кода в модуле набора записей регистра, который может вызвать проблему:
Процедура ПередЗаписью(Отказ, Замещение)
// Некорректный код, вызывающий перезапись этого же набора
Набор = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
Набор.Отбор.Номенклатура.Установить(ЭтотОбъект.Отбор.Номенклатура.Значение);
Набор.Записать(); // Вот она, рекурсия!
КонецПроцедуры
При загрузке данных через «Универсальный обмен», обработка вызывает метод Записать(). Это инициирует событие ПередЗаписью, которое снова вызывает Записать(), и так до бесконечности. Проанализируем модули объектов и подписки на события на предмет вызовов методов Записать() без проверки свойства ОбменДанными.Загрузка.
Важное правило: всегда используйте проверку режима загрузки в обработчиках базы-приемника:
Процедура ПередЗаписью(Отказ, Замещение)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Остальная логика только для интерактивной работы
КонецПроцедуры
Поскольку ошибка происходит на стороне сервера, обычное «Сообщить» может не помочь. Рассмотрим пошагово, как найти место «падения» стека.
В обработке «Универсальный обмен данными в формате XML» предусмотрен специальный механизм отладки. Перейдем на вкладку «Дополнительные настройки» и установим флаг «Использовать режим отладки обработчиков».
Система предложит сформировать внешний файл (модуль отладки). Сохраним его и подключим в конфигураторе. Теперь мы сможем установить точки останова непосредственно в коде, который генерирует «Конвертация данных» в момент выполнения обмена.
Проанализируем ситуацию через конфигуратор базы-приемника. Выполним следующие действия:
Переполнение стека встроенного языка.Когда ошибка возникнет, отладчик остановится на той строке кода, которая была вызвана последней перед критическим заполнением стека. Посмотрите на окно «Стек вызовов» — вы увидите там сотни одинаковых строк. Это и будет ответом на вопрос, какая процедура вызывает рекурсию.
Если отладка кода не дает результатов, проанализируем сам файл выгрузки. Откроем его любым текстовым редактором (лучше использовать Notepad++ или аналоги, способные открывать большие файлы). Ищите повторяющиеся блоки данных. Если один и тот же объект (с одинаковым UUID) встречается в иерархии вложений слишком часто, значит, проблема в зацикленности связей в базе-источнике.
Рассмотрим еще один сценарий: циклическая иерархия. В 1С программно можно (хотя и сложно) создать ситуацию, когда Группа 1 является родителем для Группы 2, а Группа 2 — родителем для Группы 1. При попытке выгрузки такого справочника «по ссылкам» механизм обхода дерева уйдет в бесконечный цикл. Перед выгрузкой рекомендуется проверять целостность базы данных встроенной проверкой «Тестирование и исправление» с поиском некорректных ссылок.
Существует техническая возможность увеличить размер стека в файле настроек conf.cfg сервера 1С. Однако, это крайне не рекомендуется как основное решение. Увеличение стека лишь отсрочит падение системы, но не устранит причину. Используйте этот метод только в исключительных случаях, когда глубина вложенности данных действительно легитимна и велика (например, очень сложная иерархия подразделений в 100+ уровней), но для стандартных обменов это всегда признак ошибки в коде.
Для успешного решения проблемы «Переполнение стека» выполним следующие действия:
ОбменДанными.Загрузка во все обработчики событий ПередЗаписью и ПриЗаписи в базе-приемнике.Помните, что рекурсия — это логическая ошибка. Внимательный анализ стека вызовов в 99% случаев позволяет найти «виновную» процедуру в течение нескольких минут.