Тяжёлая форма 1С: четыре HTTP-запроса при открытии и таймаут на кассе

Жалоба от оператора склада: «Форма документа открывается сорок секунд. Раньше было нормально». Раньше — это месяц назад. С тех пор ничего не меняли. По крайней мере, так казалось.

Подключаемся, открываем форму на тестовой базе — открывается мгновенно. Просим оператора повторить — снова сорок секунд. Чудес не бывает: значит, разница в данных, в правах или в окружении. Включаем замер производительности и просим оператора нажать «Открыть» ещё раз. Замер показывает четыре HTTP-запроса к стороннему сервису, каждый по десять секунд таймаута.

Дальше — самое интересное. Эти четыре запроса — одинаковые. Один и тот же URL, один и тот же ключ, один и тот же ответ. Просто вызваны четыре раза подряд из одной процедуры открытия формы. И если бы внешний сервис отвечал нормально (по 200 миллисекунд), никто бы и не заметил. Но он лежал — и каждый из четырёх вызовов честно ждал десять секунд таймаута.

Откуда взялись четыре одинаковых вызова

Контекст: настраиваем электронный документооборот для подписания товарно-сопроводительных документов. Форма при открытии должна проверить, валиден ли ключ ЭДО у текущего пользователя. Для этого она идёт на CryptoProxy — локальный сервис, который держит сертификаты и отдаёт информацию о них через HTTP. Получает срок действия, проверяет, не просрочен ли. Показывает индикатор: зелёный — можно подписывать, красный — ключ просрочен или сервис недоступен.

Логика правильная. Реализация — нет.

Данные ключа хранились в регистре сведений НастройкиЭДО. Измерения регистра: Пользователь, Организация, Склад. У оператора четыре склада в обслуживании — четыре записи в регистре. В каждой одни и те же URL, KeyID и пароль. Потому что ключ ЭДО привязан к компьютеру и человеку, а не к складу. Но регистр построен так, что без склада запись не сохранить. Пришлось дублировать.

Код открытия формы шёл в цикле по всем записям регистра для текущего пользователя. Четыре строки — четыре HTTP-запроса к CryptoProxy. Один и тот же сертификат проверялся четыре раза. На быстром сервисе это незаметно. На зависшем — сорок секунд.

Схема открытия тяжёлой формы 1С — четыре HTTP-запроса и таймаут

Почему такое не ловится в тестах

Когда программист тестирует форму у себя, у него обычно одна организация и один склад. Одна запись в регистре — один HTTP-запрос — двести миллисекунд. Всё отлично. Когда форма попадает в продуктив к оператору, у которого четыре склада, — никто не пересматривает код, потому что «он же работал».

Более того, даже сам оператор не замечал проблему до тех пор, пока CryptoProxy работал нормально. Четыре раза по 200 миллисекунд — это 800 миллисекунд. Заметно, но не криминально. Никто не пишет в IT с жалобой «у меня форма открывается на 600 миллисекунд медленнее, чем должна». Жалоба пошла, когда CryptoProxy упал, и таймаут раскрыл скрытое умножение запросов.

Это типичный шаблон. Производительность деградирует не равномерно, а ступенькой. До определённого момента всё работает в пределах терпения пользователя, потом — резко выходит за грань, и вы получаете звонок. Если есть мониторинг APDEX, такой паттерн виден ещё до жалобы — операция выполняется в пределах нормы, но с большим разбросом. Если мониторинга нет, вы узнаёте от пользователя.

Где смотреть, когда форма «просто долго открывается»

Сначала — самое банальное. Откройте форму с включённым замером производительности. Не профайлер, не ТЖ, а встроенный в платформу замер: «Сервис → Замер производительности → Включить → выполнить операцию → Выключить». Замер покажет таблицу: какая строка кода сколько раз вызвана и сколько суммарно занимала. В девяти случаях из десяти первая строка в этой таблице — это уже ответ.

Если замер показывает, что время съели не вызовы кода, а просто что-то «вне 1С» — включайте ТЖ с событиями HTTP, EXTSRV, FILE. Так вы увидите внешние обращения: HTTP-запросы, чтения файлов, ожидание сервисов. Часто оказывается, что форма открывается долго не потому, что 1С медленная, а потому, что у неё в обработчике ПриСозданииНаСервере зашит вызов SOAP-сервиса, который никто не помнит.

Если время съели запросы к базе — переходите к ТЖ с событием DBMSSQL. Включите фильтр по длительности, например, больше 50 миллисекунд. Запустите открытие формы. Все длительные запросы будут в логе, и вы сразу увидите паттерны: запрос в цикле, виртуальные таблицы без параметров, неиндексированный отбор.

Если ничего из этого ничего не показало — а такое бывает — смотрите на саму форму. Форма с шестью динамическими списками, тридцатью полями и тремя табличными частями будет открываться долго даже на пустых данных. Не из-за запросов, а из-за того, что платформа собирает много DOM-объектов. Это лечится разбиением формы на страницы и ленивой загрузкой.

Структурное решение: выкинуть избыточный регистр

Возвращаемся к нашему ЭДО. Можно было пойти простым путём: закэшировать результат HTTP-запроса в переменной модуля и вызывать прокси один раз вместо четырёх. Это бы уменьшило время с 40 секунд до 10 — таймаут бы остался, но всего один.

Это не лечение. Это маскировка симптома. Корневая проблема в том, что данные ключа ЭДО хранятся не там, где должны.

Ключ привязан к пользователю и его рабочему месту. Не к складу. Регистр НастройкиЭДО с измерением «Склад» вынуждал хранить четыре копии одинаковых данных. Это и порождало цикл по записям, и порождало возможность рассинхронизации (вдруг в одной из четырёх строк кто-то поправит URL?), и порождало баг, который мы обнаружили попутно: если в одной из строк забыть заполнить URL, код подставлял дефолтный localhost:9999. У оператора CryptoProxy слушает другой адрес. Запрос уходит в пустоту, индикатор показывает серый, оператор звонит в поддержку. А три других строки при этом заполнены правильно.

Решение: вынести ключ туда, где он логически живёт. Добавили табличную часть КлючиЭДО в справочник Пользователи. Одна строка на организацию — без привязки к складу. URL, KeyID, пароль хранятся один раз. Регистр НастройкиЭДО остался для того, что действительно зависит от склада: UserID в системе оператора фискальных данных.

Запрос в форме переписали: вместо цикла по складам — выборка из табличной части по организации. Один пользователь, одна организация, один HTTP-запрос. Данные сертификата обновляются в той же табличной части, внутри транзакции с блокировкой. Баг с пустым URL стал невозможен конструктивно: нет дублирования — нечему рассинхронизироваться.

Сравнение времени открытия формы 1С до и после нормализации данных

Как не прийти к этому снова

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

Если без проверки ключа никак — делайте её асинхронно. Платформа поддерживает фоновые задания, которые отрабатывают после открытия формы. Форма открывается мгновенно с серой плашкой «проверяю ключ», через секунду плашка перекрашивается в зелёную или красную. Пользователь видит результат, но не ждёт его перед тем, как начать работу.

Второе правило: при чтении данных из регистров с составным ключом всегда задавайте вопрос «а что я ожидаю получить — одну запись или несколько?». Если одна — отбор должен быть полным по всем измерениям. Если несколько — обработайте честный сценарий: возможно, цикл по нескольким записям не нужен и вы дублируете запросы к чему-то, что должно быть в одном месте.

Третье — кэшируйте результаты повторяющихся вызовов в пределах одного контекста. У платформы есть штатные механизмы: модули с повторным использованием возвращаемых значений (директивы &НаКлиентеНаСервереБезКонтекста и ПовторноеИспользованиеВозвращаемыхЗначений). Если вы знаете, что результат не меняется в течение сеанса — оформите его через эти механизмы. Платформа сама вернёт кэш при повторном вызове. Подробнее об этом — в статье про кэширование в 1С: там же есть и подводные камни этого подхода.

Замер показал — а проблема в регистре

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

Если вы постоянно ускоряете формы и через два-три месяца возвращаетесь к тем же местам — стоит остановиться и пересмотреть схему данных. Это дольше и страшнее, чем точечная оптимизация, но это единственный способ перестать чинить одно и то же.

Правильное место хранения данных решает проблемы, которые код только маскирует. Это не красивая фраза — это диагноз. Когда вы видите цикл по «всем настройкам пользователя» или «всем правилам обмена», или «всем шаблонам уведомлений», и каждая итерация тянет HTTP-запрос — почти наверняка вы смотрите на регистр, который спроектирован неправильно. И никакая оптимизация цикла этого не исправит.

Чек-лист диагностики тяжёлой формы 1С — пять шагов от замера до фикса

Итог по нашей форме

Сорок секунд превратились в одну. Не потому, что мы ускорили HTTP-запрос, и не потому, что мы поставили заглушку при недоступности прокси. А потому, что мы перестали делать четыре одинаковых запроса вместо одного. Изменение в данных, изменение в форме — три файла, тридцать строк кода. Час работы.

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