В процессе разработки на платформе «1С:Предприятие 8» разработчики часто сталкиваются с необходимостью передачи сложных структур данных между клиентской и серверной частями приложения. Иногда при выполнении такого вызова приложение аварийно завершает работу без вывода понятной ошибки, либо начинает циклически отправлять повторные запросы на сервер, после чего полностью «вылетает». Рассмотрим подробно, почему это происходит, как устроен механизм сериализации под капотом платформы, и как диагностировать и устранять подобные проблемы.
При передаче дерева значений, массива структур или соответствий с клиента на сервер (или возврате их с сервера на клиент) могут наблюдаться следующие симптомы:
rphost, обрабатывающего первый запрос, кластер серверов 1С пытается восстановить вызов и перенаправляет его на другой рабочий процесс.rphost или тонкого клиента.Многие разработчики ошибочно полагают, что дело в физическом ограничении на размер передаваемого массива или структуры. На самом деле, жесткого лимита на количество элементов в платформе нет — ограничением выступает лишь доступный объем оперативной памяти. Настоящая причина кроется в циклических ссылках внутри передаваемых данных.
Для того чтобы передать данные встроенного языка (например, Структура или Массив) по сетевому протоколу между клиентом и сервером, платформа должна выполнить их сериализацию — преобразовать объект в последовательность байт (обычно во внутренний формат JSON или XML/XDTO-пакет).
Процесс сериализации устроен рекурсивно:
Если в вашей структуре данных присутствует циклическая ссылка (когда объект напрямую или через цепочку других объектов ссылается сам на себя), рекурсивный алгоритм десериализации/сериализации уходит в бесконечный цикл. В результате стек вызовов переполняется, происходит критическая ошибка Stack Overflow, и операционная система аварийно завершает рабочий процесс 1С.
Проанализируем простейший пример кода, гарантированно приводящий к падению системы:
&НаКлиенте
Процедура Команда1(Команда)
Результат = Команда1НаСервере();
КонецПроцедуры
&НаСервере
Функция Команда1НаСервере()
А = Новый Структура;
А.Вставить("СамоНаСебя", А); // Создаем циклическую ссылку
Возврат А; // Попытка вернуть структуру на клиент вызовет крэш
КонецФункции
В данном примере структура А содержит свойство, значением которого является сама структура А. При попытке вернуть эту переменную на клиент платформа попытается сериализовать ее, уйдет в бесконечную рекурсию и упадет.
Начиная с версии платформы 8.3.10, разработчики добавили штатные инструменты, позволяющие контролировать появление циклических ссылок во встроенном языке и избегать утечек памяти.
Запуск клиентского приложения или службы сервера с ключом /EnableCheckScriptCircularRefs активирует автоматический контроль платформы за созданием циклических ссылок. Если система обнаруживает зацикливание при работе кода встроенного языка, она фиксирует это событие.
Вы можете программно проверять подозрительные объекты перед их отправкой через границу «клиент-сервер». Для этого используется специализированный метод:
// Пример проверки структуры перед отправкой
МассивОшибок = ПроверитьЦиклическиеСсылкиВстроенногоЯзыка(МояКоллекцияДанных);
Если МассивОшибок.Количество() > 0 Тогда
ЗаписьЖурналаРегистрации("Внимание! Обнаружены циклические ссылки в передаваемых данных.");
// Обработка ошибки или очистка связей
КонецЕсли;
Циклические ссылки опасны не только падением при вызове сервера, но и скрытыми утечками памяти (Memory Leaks). Сборщик мусора 1С работает по принципу подсчета ссылок. Если два объекта ссылаются друг на друга, счетчик никогда не станет равным нулю, и память под эти объекты не освободится до закрытия сеанса или перезапуска процесса rphost. Для расследования таких ситуаций настройте Технологический журнал на сбор события LEAKS.
Чтобы избежать падений платформы и существенно повысить скорость работы клиент-серверного взаимодействия, рекомендуем придерживаться следующих правил:
ПоместитьВоВременноеХранилище() и передайте на клиент только короткий текстовый адрес (GUID).ДанныеФормыКоллекция (или ДанныеФормыДерево). Платформа оптимизирует передачу таких коллекций, отправляя данные на клиент порционно по мере прокрутки экрана пользователем.