Каждый разработчик 1С рано или поздно сталкивается с ситуациями, которые сложно объяснить с точки зрения здравого смысла. Одной из таких мистических проблем является так называемая "волшебная" синтаксическая ошибка. Представьте ситуацию: вы открываете модуль, код написан абсолютно корректно, синтаксический контроль в Конфигураторе проходит без замечаний. Однако в режиме предприятия у пользователей внезапно начинает сыпаться ошибка выполнения, указывающая на пропущенный символ, точку или букву.
Например, абсолютно рабочий метод обращения к виртуальной таблице среза последних:
РегистрСведений.СпискиПациентовДМС.СрезПоследних(&ТД, )
Платформа внезапно отказывается компилировать и выдает ошибку, как будто в коде написано резПоследних или вовсе пропущена точка перед буквой «С». Перепечатывание слова заново и динамическое обновление спасают лишь на время, а спустя несколько часов или дней у случайных пользователей ошибка воспроизводится снова. Давайте подробно разберем, почему возникает этот сбой, проанализируем его внутренние технические причины и найдем надежные способы его устранения раз и навсегда.
Эта мистика не имеет отношения к качеству вашего кода или "неправильным" символам. Перед нами — классический, глубокий баг платформы 1С:Предприятие, связанный с механизмами кэширования метаданных и так называемым динамическим (в кругах разработчиков — "демоническим") обновлением.
Давайте проанализируем, что происходит под капотом платформы в этот момент:
Config).rphost.rphost начинают считывать скомпилированный код со смещением (offset) на несколько байт. Для интерпретатора 1С это выглядит так, будто первый символ (например, точка или буква «С») просто отсутствует в потоке данных.rphost перезапускаются по таймауту, освобождая поврежденные области оперативной памяти, и кэш считывается заново уже без смещения.Обычная очистка локального кэша на компьютерах пользователей в этой ситуации бессильна, поскольку искажение данных происходит на более глубоких уровнях: в кэше сервера приложений 1С и в структурах таблиц базы данных СУБД.
Рассмотрим по шагам комплекс методов, которые гарантированно помогут решить эту проблему.
---
Динамическое обновление — это очень удобный, но крайне опасный инструмент, особенно в базах с большим количеством активных пользователей (более 50-100 сеансов). Постоянное применение обновлений "на лету" рано или поздно приводит к расшатыванию стабильности конфигурации базы данных.
Рекомендация: Возьмите за жесткое правило выполнять любые обновления конфигурации (даже самые мелкие исправления в коде модулей) строго монопольно, с отключением всех пользователей. Проводите плановые обновления в технические окна (в нерабочее время) с обязательным завершением всех сеансов.
---
Если «битый» байт-код уже записался в таблицы базы данных, простое сохранение конфигурации не поможет. Нам необходимо принудительно заставить платформу пересчитать все индексы и структуры конфигурационных таблиц. Для этого выполним процедуру полной перезагрузки конфигурации из файла:
unloaded_config.cf).unloaded_config.cf. Важно: используйте именно команду "Загрузить", а не "Сравнить/объединить", так как первая полностью перезаписывает таблицы Config и Params на уровне СУБД, устраняя любые накопленные сдвиги в байт-коде.F7 (или выбрав пункт Конфигурация -> Обновить конфигурацию базы данных).---
Поскольку поврежденный кэш метаданных может удерживаться службой сервера 1С:Предприятие, стандартное удаление папок в каталоге %AppData% на клиенте не принесет долгосрочного результата. Проведем очистку кэша на стороне сервера приложений:
1C:Enterprise 8.3 Server Agent) через утилиту управления службами Windows (services.msc) или консоль администрирования.C:\Program Files\1cv8\srvinfo\ (или C:\ProgramData\1C\1cv8\).reg_1541) найдите подкаталоги с именами в виде уникальных идентификаторов (UUID, например: 1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d).snccntry (это файлы сессионных данных). Внимание: ни в коем случае не удаляйте файл реестра кластера 1CV8Reg.lst, так как в нем содержится список зарегистрированных на сервере баз данных.Для профилактики на стороне SQL-сервера также можно выполнить команду очистки процедурного кэша, запустив в СУБД следующий запрос:
DBCC FREEPROCCACHE
---
Если ошибка упорно воспроизводится в одном конкретном модуле формы или документа, это означает, что платформа "привязала" поврежденный кэш к уникальному внутреннему идентификатору (UUID) этого объекта метаданных. Мы можем обмануть платформу, заставив ее сгенерировать для этого модуля новый UUID.
Разберем, как это сделать без потери реквизитов и структуры формы:
Ctrl + Drag-and-drop), чтобы создать точную копию формы. Ей будет присвоено имя вроде ФормаЭлемента1. При этом платформа создаст абсолютно новый уникальный идентификатор для этой формы и ее модуля.ФормаЭлемента).---
Как показывает практика эксплуатации систем под высокой нагрузкой, данное поведение — это признанная ошибка работы с динамической памятью конкретных релизов платформы. Особенно сильно этому подвержены версии веток 8.3.13 (например, проблемный релиз 8.3.13.1513) и 8.3.14.
Если ваша конфигурация позволяет, спланируйте и осуществите переход на более стабильные и современные версии платформы 1С:Предприятие (например, последние сборки линеек 8.3.21, 8.3.22 или старше). В этих версиях механизмы серверного кэширования и динамического обновления были существенно доработаны, а критические утечки памяти и сдвиги указателей — устранены.
---
В качестве хорошего тона разработки и для минимизации вероятности проявления подобных багов платформы в будущем, давайте посмотрим на сам исходный запрос из примера. В нем используется обращение к виртуальной таблице среза последних с последующей фильтрацией в секции ГДЕ:
|ИЗ
| РегистрСведений.СпискиПациентовДМС.СрезПоследних(&ТД, ) КАК СпискиПациентовДМССрезПоследних
|ГДЕ
| СпискиПациентовДМССрезПоследних.Включен
| И СпискиПациентовДМССрезПоследних.Пациент = &Пациент
Передача условий фильтрации в секцию ГДЕ при работе с виртуальными таблицами заставляет СУБД сначала выбрать все последние записи по регистру на указанную дату, а уже потом фильтровать их по конкретному пациенту. Это крайне неэффективно с точки зрения производительности.
Перепишем запрос правильно, передав условия непосредственно в параметры виртуальной таблицы СрезПоследних:
ПроверкаВСписке = Новый Запрос;
ПроверкаВСписке.Текст =
"ВЫБРАТЬ
| СпискиПациентовДМССрезПоследних.СтраховаяКомпания,
| СпискиПациентовДМССрезПоследних.Договор,
| СпискиПациентовДМССрезПоследних.СтраховаяПрограмма КАК Программа,
| СпискиПациентовДМССрезПоследних.Пациент
|ИЗ
| РегистрСведений.СпискиПациентовДМС.СрезПоследних(&ТД, Пациент = &Пациент И Включен) КАК СпискиПациентовДМССрезПоследних";
ПроверкаВСписке.УстановитьПараметр("ТД", ТекущаяДата());
ПроверкаВСписке.УстановитьПараметр("Пациент", Пациент);
Такой подход не только повысит скорость выполнения запроса в десятки раз при росте базы данных, но и уменьшит нагрузку на кэш сервера 1С, предотвращая возникновение неожиданных ошибок компиляции.