ワンクリックで
background-jobs
For designing and debugging 1C background jobs
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
For designing and debugging 1C background jobs
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
When writing or reviewing BSL, apply 1C standards
При написании или ревью BSL применять стандарты 1С
Orchestrator: routing work and agent phases
Оркестратор: маршрутизация работы и фаз агентов
BSL LSP navigation: definitions, refs, call graph
Rules for using RLM tools for project search and navigation in 1C/BSL
| name | background-jobs |
| description | For designing and debugging 1C background jobs |
| skills | ["architect","developer-code"] |
Key principle: A background job can be interrupted, restarted, or started again at any moment. The job code must withstand this without data loss or duplicate work.
| Trigger | Action |
|---|---|
| A scheduled or background job is being designed | Define the contract: parameters, user, transaction, idempotency, locking, timeout |
| A job hangs, does not finish, duplicates work | Diagnose via the Event Log: find the first failure, check active background jobs and stale locks |
| There is an error in the job and retry logic is needed | Separate retryable and permanent errors, implement backoff |
| The job processes a large volume of data | Apply checkpointing and batch processing with intermediate commits |
| Parallel execution of multiple instances is possible | Implement a mutex via БлокировкаДанных or a flag constant |
All transaction examples below are the canonical
error-handlingpattern (Rule 2:НачатьТранзакцию()beforeПопытка,ЗафиксироватьТранзакцию()as the last operation,ОтменитьТранзакцию()first inИсключение, log after rollback) with task-specific context inside it; the pattern itself is not restated here.
All transaction examples below are the canonical
error-handlingpattern (Rule 2:НачатьТранзакцию()beforeПопытка,ЗафиксироватьТранзакцию()as the last operation,ОтменитьТранзакцию()first inИсключение, log after rollback) with the job-specific context inside it; the pattern itself is not restated here.
Context: You need to create a scheduled job that can be safely restarted and does not duplicate work.
Steps:
// Канонический паттерн идемпотентного захвата задачи
Функция ЗахватитьЗадачуДляОбработки(ЗадачаСсылка) Экспорт
НачатьТранзакцию();
Попытка
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СостоянияЗадач");
ЭлементБлокировки.УстановитьЗначение("Задача", ЗадачаСсылка);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// Читаем актуальный статус после блокировки
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СостоянияЗадач.Статус КАК Статус,
| СостоянияЗадач.НачалоОбработки КАК НачалоОбработки
|ИЗ
| РегистрСведений.СостоянияЗадач КАК СостоянияЗадач
|ГДЕ
| СостоянияЗадач.Задача = &Задача";
Запрос.УстановитьПараметр("Задача", ЗадачаСсылка);
Результат = Запрос.Выполнить();
Если НЕ Результат.Пустой() Тогда
Выборка = Результат.Выбрать();
Выборка.Следующий();
// Уже обработано — пропускаем
Если Выборка.Статус = Перечисления.СтатусыЗадач.Обработано Тогда
ОтменитьТранзакцию();
Возврат Ложь;
КонецЕсли;
// Кто-то уже взял задачу (и не завис) — пропускаем
Если Выборка.Статус = Перечисления.СтатусыЗадач.ВОбработке
И (ТекущаяДата() - Выборка.НачалоОбработки) < 3600 Тогда
ОтменитьТранзакцию();
Возврат Ложь;
КонецЕсли;
КонецЕсли;
// Захватываем задачу
НаборЗаписей = РегистрыСведений.СостоянияЗадач.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Задача.Установить(ЗадачаСсылка);
Запись = НаборЗаписей.Добавить();
Запись.Задача = ЗадачаСсылка;
Запись.Статус = Перечисления.СтатусыЗадач.ВОбработке;
Запись.НачалоОбработки = ТекущаяДата();
НаборЗаписей.Записать();
ЗафиксироватьТранзакцию();
Возврат Истина;
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации(
НСтр("ru = 'ФоновоеЗадание.ЗахватЗадачи'"),
УровеньЖурналаРегистрации.Ошибка,,,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение;
КонецПопытки;
КонецФункции
Context: The scheduled job must not run in two instances at the same time.
Steps:
Процедура ВыполнитьРегламентноеЗадание() Экспорт
// Попытка получить эксклюзивный лок
НачатьТранзакцию();
Попытка
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Константа.ФлагЗапускаЗадания");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Попытка
Блокировка.Заблокировать();
Исключение
// Другой экземпляр уже работает — нормальная ситуация
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации(
НСтр("ru = 'РегламентноеЗадание.ИмяЗадания'"),
УровеньЖурналаРегистрации.Предупреждение,,,
НСтр("ru = 'Пропущен запуск: задание уже выполняется.'"));
Возврат;
КонецПопытки;
// Основная логика задания — выполняется только в одном экземпляре
ВыполнитьОсновнуюЛогику();
ЗафиксироватьТранзакцию();
// canonical Exception block (see scenario 1 / error-handling, Rule 2),
// event name in the registration log: "РегламентноеЗадание.ИмяЗадания"
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации(...);
ЗаписьЖурналаРегистрации(...);
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
Context: The job processes thousands of objects. Progress must be saved so that after a restart it does not begin from zero.
Key rules:
Процедура ОбработатьОбъектыСCheckpoint(РазмерБатча = 100) Экспорт
// Читаем checkpoint (откуда продолжать)
НачальнаяПозиция = ПолучитьCheckpoint();
МассивОбъектов = ПолучитьОбъектыДляОбработки(НачальнаяПозиция, РазмерБатча);
Пока МассивОбъектов.Количество() > 0 Цикл
НачатьТранзакцию();
Попытка
Для Каждого Объект Из МассивОбъектов Цикл
ОбработатьОдинОбъект(Объект);
КонецЦикла;
// Checkpoint и данные фиксируются атомарно
СохранитьCheckpoint(МассивОбъектов[МассивОбъектов.ВГраница()]);
ЗафиксироватьТранзакцию();
// canonical Exception block (see scenario 1 / error-handling, Rule 2),
// event name in the registration log: "ФоновоеЗадание.ПакетнаяОбработка"
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации(...);
ВызватьИсключение;
КонецПопытки;
// Следующий батч
НачальнаяПозиция = ПолучитьCheckpoint();
МассивОбъектов = ПолучитьОбъектыДляОбработки(НачальнаяПозиция, РазмерБатча);
КонецЦикла;
КонецПроцедуры
Context: The task calls an external service or works with resources that may be temporarily unavailable.
Error classification:
| Type | Examples | Action |
|---|---|---|
| Retryable (temporary) | Network timeout, service unavailable (503), lock contention | Retry with backoff, record Warning |
| Permanent | Invalid data, business rule violated, 404/400 | Do not retry, record Error, move the task to status Rejected |
Функция ВыполнитьСRetry(ПараметрыЗадачи) Экспорт
МаксПопыток = 3;
ЗадержкаСекунд = 30; // для ФоновогоЗадания — через повторный запуск планировщиком
Для НомерПопытки = 1 По МаксПопыток Цикл
Попытка
Результат = ВызватьВнешнийСервис(ПараметрыЗадачи);
ЗафиксироватьУспех(ПараметрыЗадачи, Результат);
Возврат Истина;
Исключение
ИнфОшибки = ИнформацияОбОшибке();
Если ЭтоPermanentОшибка(ИнфОшибки) Тогда
// Повтор бессмысленен
ЗаписьЖурналаРегистрации(
НСтр("ru = 'ФоновоеЗадание.ВнешнийСервис'"),
УровеньЖурналаРегистрации.Ошибка,,,
СтрШаблон(НСтр("ru = 'Постоянная ошибка (повтор не поможет). %1'"),
ПодробноеПредставлениеОшибки(ИнфОшибки)));
ЗафиксироватьОтклонение(ПараметрыЗадачи, КраткоеПредставлениеОшибки(ИнфОшибки));
Возврат Ложь;
КонецЕсли;
// Retryable — логируем как предупреждение и продолжаем
ЗаписьЖурналаРегистрации(
НСтр("ru = 'ФоновоеЗадание.ВнешнийСервис'"),
?(НомерПопытки < МаксПопыток,
УровеньЖурналаРегистрации.Предупреждение,
УровеньЖурналаРегистрации.Ошибка),,,
СтрШаблон(НСтр("ru = 'Попытка %1/%2. %3'"),
НомерПопытки, МаксПопыток,
ПодробноеПредставлениеОшибки(ИнфОшибки)));
Если НомерПопытки = МаксПопыток Тогда
ВызватьИсключение;
КонецЕсли;
КонецПопытки;
КонецЦикла;
Возврат Ложь;
КонецФункции
Функция ЭтоPermanentОшибка(ИнфОшибки)
ТекстОшибки = КраткоеПредставлениеОшибки(ИнфОшибки);
// Признак permanent: HTTP 4xx, бизнес-ошибки, невалидные данные
Возврат СтрНайти(ТекстОшибки, "400") > 0
ИЛИ СтрНайти(ТекстОшибки, "404") > 0
ИЛИ СтрНайти(ТекстОшибки, "422") > 0;
КонецФункции
Run the job manually (for debugging and testing):
# Запустить конкретное регламентное задание через v8-runner
v8 run --ib <путь_к_ИБ> --execute "РегламентныеЗаданияСервер.ВыполнитьЗадание(<ИмяЗадания>)"
Diagnose stuck jobs via the event log (event-log-analysis):
# Посмотреть ошибки фоновых заданий за последние 2 часа
v8 run --ib <путь_к_ИБ> --event-log --filter "ФоновоеЗадание" --level Error --hours 2
Checking active background jobs in the event log:
Search for events named Фоновое задание. A stuck job is an event “Start” without a matching “Finish” and without “Error” - this is a candidate for a stale lock.
| Anti-pattern | Consequence |
|---|---|
| HTTP/external call inside a transaction | 30 sec timeout = 30 sec blocking for the entire database |
| One transaction for the whole volume | Restart = rollback of all work |
| Lack of idempotency | Duplicate data on rerun |
| Silent error swallowing | Data is lost, there are no traces in the event log |
| Infinite retry without a limit | The job will block the queue forever |
| Stale lock without TTL | The job does not start after a crash, the lock is not released |
Each job must write to the event log at start and finish:
// Старт задания
ЗаписьЖурналаРегистрации(
НСтр("ru = 'РегламентноеЗадание.<ИмяЗадания>.Старт'"),
УровеньЖурналаРегистрации.Информация,,,
СтрШаблон(НСтр("ru = 'Задание запущено. Параметры: %1'"),
<КраткоеОписаниеПараметров>));
// Финиш задания
ЗаписьЖурналаРегистрации(
НСтр("ru = 'РегламентноеЗадание.<ИмяЗадания>.Финиш'"),
УровеньЖурналаРегистрации.Информация,,,
СтрШаблон(НСтр("ru = 'Задание завершено. Обработано: %1, Ошибок: %2, Время: %3 сек'"),
КоличествоОбработано, КоличествоОшибок, Длительность));
БлокировкаДанных, the canonical НачатьТранзакцию/ЗафиксироватьТранзакцию/ОтменитьТранзакцию patterndepends_on: