Траблшутинг сервера 1С: память rphost, блокировки PostgreSQL и сборка, которая «прошла» с ошибкой
Практикум из трёх лабораторных работ: каждая аварийная ситуация воспроизводится скриптом, диагностика запускается командой, а результат виден в выводе.
Практикум разбирает три ситуации: рабочий процесс растёт по памяти, пользователи стоят в очереди на проведении, сборка в CI зелёная при ошибке в конфигурации. К каждой приложены скрипт, который её воспроизводит, и команда диагностики с реальным выводом. Скрипты лежат в архиве к статье.
🔍 Что проверено и на чём
Числа и выводы в статье получены на этом стенде 29.09.2026. Версии указаны потому, что часть находок зависит от них.
| Компонент | Версия | Как использовался |
|---|---|---|
| PostgreSQL | 16.15, официальный образ postgres:16 |
Docker Compose из архива, работа №2 |
| Платформа 1С | 8.3.27.2130, Windows x86 | локальный кластер (ragent, ras), rac, DESIGNER; работы №1 и №3 |
| Linux-версия платформы | не запускалась | пути и права проверьте у себя |
| Скрипты | bash (Git Bash), Python 3.12 | весь архив |
| Технологический журнал | не включался | каталог conf платформы требует прав администратора, системные каталоги я не менял; парсер проверен на файле, собранном по описанию формата |
Образа сервера 1С в архиве нет: дистрибутивы платформы нельзя выкладывать в открытый реестр, поэтому сервер 1С вы ставите свой. СУБД для стенда поднимается одной командой:
🔍 Лабораторная работа №1. Память rphost
Сценарий
Рабочий процесс rphost растёт по памяти, кластер перезапускает процессы, сеансы пользователей рвутся.
Шаг 1. Включить технологический журнал
Файл logcfg.xml кладётся в каталог conf установки платформы (на стенде с Windows это C:\Program Files (x86)\1cv8\conf; для Linux смотрите документацию своей версии). Конфигурация собрана по документации ИТС; на живом сервере я её не включал:
Три места, где легко ошибиться (по документации ИТС):
- пространство имён -
http://v8.1c.ru/v8/tech-log; - каталог журнала задаёт атрибут
location; - событие называется
LEAKS(с буквой S), и собирать его нужно отдельным тегом<leaks collect="1">.
В архиве файл logcfg.xml дополнен событиями блокировок TLOCK, TTIMEOUT и TDEADLOCK для работы №2.
Шаг 2. Разобрать журнал парсером
Тяжёлые вызовы, события утечек и исключения по процессам собирает parse_tj_memory.py. Чтобы попробовать без своего сервера, fault_generator.sh tj_sample пишет учебный журнал:
[ПАРСЕР] Каталог ТЖ: samples/tj, файлов: 1, событий: 11
Вызовы с Memory >= 100 МБ:
rphost_4120: 5 вызовов, максимум 335 МБ (26092912.log, 40:11) Документ.РеализацияТоваровУслуг.Форма.ФормаДокумента.Форма : ОбработкаПроведения
События LEAKS по описанию:
4 Объект менеджера запросов не освобождён после завершения вызова
EXCP по процессам:
rphost_4120: 1
Этот журнал синтетический, а парсер читает тот же формат, что пишет сервер: мм:сс.мкс-длительность,СОБЫТИЕ,уровень,свойство=значение. Многострочные значения в кавычках склеиваются. Код возврата 2 означает, что найдены тяжёлые вызовы или LEAKS, поэтому скрипт удобно вешать на расписание.
Шаг 3. Посмотреть память процессов кластера
Журнал показывает, какой вызов тяжёлый. Сколько памяти занимает сам процесс, показывает rac (сервер администрирования ras должен быть запущен):
pid=21292 память= 98 МБ соединений=1 memory-excess-time=0
Поле memory-size в rac process list измеряется в килобайтах. На стенде значение 100156 совпало с PrivateMemorySize64 того же процесса, который показывает Get-Process в Windows (100156 КБ). Скрипт делит на 1024 и сравнивает с порогом в мегабайтах; при превышении печатает ВЫШЕ ПОРОГА и завершается кодом 2.
Шаг 4. Выставить лимиты и проверить, что они записались
Ограничения памяти задаются на рабочем сервере и в кластере. Справка rac help server даёт ключи и единицы: --critical-total-memory и --temporary-allowed-total-memory - в байтах, --temporary-allowed-total-memory-time-limit - в секундах.
Здесь стенд показал главную ловушку работы. Команда rac server update возвращает код 0 и молчит, даже если значение не записалось. На платформе 8.3.27.2130 три параметра остались нулями при любых значениях, которые я пробовал, остальные записались:
| Параметр | Записался |
|---|---|
critical-total-memory, temporary-allowed-total-memory, safe-call-memory-limit (сервер) |
да |
expiration-timeout (кластер) |
да |
memory-limit, safe-working-processes-memory-limit (сервер) |
нет, остались 0 |
lifetime-limit (кластер) |
нет, остался 0 |
Из-за этого rac_memory_watch.sh apply читает каждое значение обратно и завершается ошибкой, если оно не совпало:
[OK] critical-total-memory = 25769803776 [OK] temporary-allowed-total-memory = 32212254720 [OK] expiration-timeout = 300
Проверка на отказ тоже выполнялась: при попытке записать memory-limit скрипт печатает [НЕ ЗАПИСАЛОСЬ] memory-limit: просили 8388608, в кластере 0 и возвращает 1. Проверьте те же параметры на своей версии и на Linux: у меня это одна машина с одним рабочим сервером на платформе 8.3.27.2130.
Справка rac help cluster описывает lifetime-limit как период перезапуска рабочих процессов, а expiration-timeout как период принудительного завершения. Как процесс переносит сеансы при перезапуске, на стенде не проверялось: у нас не было базы с пользователями.
🔍 Лабораторная работа №2. Блокировки в PostgreSQL
Сценарий
Падает APDEX, пользователи стоят в очереди на проведение документов.
Шаг 1. Воспроизвести ожидание
Скрипт открывает в одном сеансе транзакцию с обновлением строки и держит её минуту. Второй сеанс обновляет ту же строку и ждёт.
Шаг 2. Найти, кто кого держит
Файл содержит три запроса. Первый использует pg_blocking_pids и отвечает на вопрос за секунды:
blocked_pid | blocked_user | waiting_for | wait_event_type | wait_event | blocking_pids | blocked_statement
-------------+--------------+-----------------+-----------------+---------------+---------------+------------------------------------------
166 | postgres | 00:00:04.137726 | Lock | transactionid | {159} | UPDATE lab_docs SET note='B' WHERE id=1;
Второй показывает, что делает блокирующий сеанс (state, возраст транзакции, последний запрос). Третий - классический запрос по pg_locks из вики PostgreSQL; в нём те же сеансы, но с деталями блокировки.
Частый блокировщик стоит в состоянии idle in transaction: транзакция открыта, а клиент ничего не делает. Здесь он active, потому что сценарий держит транзакцию через pg_sleep.
Шаг 3. Снять блокировку
Блокирующий сеанс завершается функцией pg_terminate_backend. На стенде проверено, что после этого второй сеанс сразу получил строку:
Транзакция блокировщика откатывается вместе с его изменениями. В рабочей базе сначала выясните по query и usename, чья это транзакция и что она делала.
Как отличить блокировку 1С от блокировки СУБД
У платформы есть собственный менеджер управляемых блокировок. В технологическом журнале он оставляет события TLOCK (установка), TTIMEOUT (не дождались) и TDEADLOCK (взаимная блокировка). Правило диагностики такое:
pg_blocking_pidsвозвращает пустой массив, а пользователи ждут - смотритеTLOCKиTTIMEOUTв журнале, причина на уровне 1С;pg_blocking_pidsвозвращает pid - причина в СУБД, дальше запрос №2.
Это правило я проверил на PostgreSQL. На сервере 1С с пользовательской нагрузкой оно не проверялось.
Шаг 4. Оповещение по расписанию
pg_lock_alert.sh каждую минуту (cron) смотрит, ждёт ли какой-нибудь сеанс дольше порога, и шлёт сообщение через telegram_alert.sh:
* * * * * PG_LOCK_SECONDS=30 TELEGRAM_BOT_TOKEN=... TELEGRAM_CHAT_ID=... /opt/lab/pg_lock_alert.sh
Проверен режим DRY_RUN=1: при ожидании 5 секунд и пороге 2 секунды скрипт печатает [ALERT] PostgreSQL ib_prod: 1 сеансов ждут блокировку, самый долгий - 5 с, при пороге 300 секунд молчит. Реальную отправку в Telegram я не запускал, у меня нет токена бота. Токен и чат берутся из переменных окружения; в скриптах их нет.
🔍 Лабораторная работа №3. Сборка «прошла», ошибка осталась
Сценарий
Пайплайн зелёный, а на продуктив уходит конфигурация, которая не проходит проверку модулей.
Что показал эксперимент
Собрана файловая база, в общий модуль положены два варианта тела: с синтаксической ошибкой и с необъявленной переменной. Каждый вариант пропущен через пакетный запуск DESIGNER с ключом /Out (журнал в файл). Результаты на платформе 8.3.27.2130:
| Команда | Модуль | Код возврата | Журнал |
|---|---|---|---|
/LoadConfigFromFiles /UpdateDBCfg |
необъявленная переменная | 0 | «Обновление конфигурации успешно завершено» |
/CheckModules |
необъявленная переменная | 101 | Переменная не определена (НеизвестнаяПеременная) |
/CheckModules |
синтаксическая ошибка | 101 | Ошибка в выражении |
/CheckModules |
чистый модуль | 0 | Синтаксических ошибок не обнаружено! |
/LoadConfigFromFiles, битый XML |
- | 1 | Ошибка разбора XML |
/CheckModules, база не найдена |
- | 1 | Информационная база не обнаружена! |
Из таблицы три вывода:
- Загрузка и обновление конфигурации не компилируют тела модулей. Пайплайн только из этих двух шагов зелёный даже с нерабочим модулем.
- Проверку выполняет
/CheckModules, и она возвращает 101. Если этого шага в пайплайне нет, ошибку никто не увидит. - Поиск слова «Ошибка» в журнале пропускает часть сбоев: сообщение
Переменная не определенаэтого слова не содержит. Надёжный признак здесь - код возврата; вторым служит маркер<<?>>, которым платформа отмечает место ошибки в строке кода.
Отдельно проверена кодировка: журнал /Out платформы 8.3.27 на Windows записан в UTF-8 с BOM. На Linux я платформу не запускал, поэтому гейт умеет перекодировать журнал из cp1251, но проверять его на этом случае нечем.
Гейт
check_designer_exit.sh <код> <журнал> завершается ошибкой, если код не 0, журнала нет или в журнале есть слова про ошибки или маркер <<?>>. Строку «Синтаксических ошибок не обнаружено!» он считает успехом. Полный прогон на реальных журналах платформы (fault_generator.sh designer_logs):
1. Загрузка модуля с необъявленной переменной, код 0: [PASS] <- сборка «прошла» 2. /CheckModules того же модуля, код 101: [FAIL] DESIGNER вернул код 101 3. Код потерян (0), журнал проверки читает гейт: [FAIL] Возврат <<?>>НеизвестнаяПеременная; 4. Синтаксическая ошибка, код потерян: [FAIL] Возврат 1 +<<?>>; 5. Чистая проверка: [PASS]
Строка 1 показывает суть работы: гейт по журналу загрузки ошибку не видит, её видит только шаг проверки модулей. В gitlab-ci-example.yml поэтому два шага, load_config и check_modules, и гейт стоит после каждого. Этот YAML я разобрал парсером, но в GitLab не запускал.
Утверждение «DESIGNER под Linux возвращает 0 при критической ошибке» я не подтвердил и не опроверг: платформы под Linux у меня нет. На Windows в проверенных случаях сбои давали коды 1 и 101. Если у вас на Linux код 0 при ошибке, пришлите в комментариях версию платформы и команду.
🔍 Экспресс-тест (5 вопросов)
- Как посмотреть, сколько памяти занимает каждый рабочий процесс кластера?
Ответ: rac process list, поле memory-size в килобайтах; скрипт rac_memory_watch.sh watch.
- Почему в журнале нет событий утечки, хотя
LEAKSвlogcfg.xmlесть?
Ответ: событие нужно включить отдельным тегом <leaks collect="1">.
- Как отличить блокировку 1С от блокировки СУБД?
Ответ: если pg_blocking_pids пуст, а пользователи ждут, ищите TLOCK и TTIMEOUT в журнале; если есть pid блокировщика, причина в СУБД.
- Почему после
rac server updateнельзя считать, что лимит выставлен?
Ответ: команда возвращает 0 и для незаписавшихся значений; нужно прочитать параметр обратно.
- Почему пайплайн из загрузки конфигурации и обновления БД остаётся зелёным при ошибке в модуле?
Ответ: эти шаги не компилируют тела модулей; ошибку показывает /CheckModules.
🔍 Чек-лист дежурного
- [ ] В
logcfg.xmlверное пространство имён,locationвместоdir, событиеLEAKSи тег<leaks collect="1">. - [ ]
rac_memory_watch.sh watchстоит в расписании, порог подобран под вашу нагрузку. - [ ] После любого
rac ... updateзначение прочитано обратно. - [ ] Запрос
pg_locks_diag.sqlсохранён рядом с runbook,pg_lock_alert.shработает в режимеDRY_RUN=1до отправки в чат. - [ ] В пайплайне есть шаг
/CheckModules, а гейт проверяет и код возврата, и журнал.
🔍 Что не проверено
- Linux-версия платформы: пути, права, поведение
DESIGNERбез графической сессии. - Технологический журнал живого сервера: конфигурация
logcfg.xmlсобрана по ИТС и не включалась; парсер проверен на синтетическом файле, а выводы по памяти получены черезrac. - Поведение сеансов при перезапуске процесса по
lifetime-limit: параметр на моём стенде не записывался. - Реальная отправка в Telegram.
- PostgreSQL версий, отличных от 16.15.
На каком шаге у вас разошёлся вывод с моим: версия платформы, ОС, какие параметры rac записались? Напишите в комментариях, дополню таблицы.
Архив с файлами лабораторной приложен к статье.
Вступайте в нашу телеграмм-группу Инфостарт