Содержание
-
Аннотация
-
Базовые понятия
-
2.1. Граф ссылок
-
2.2. Партиция
-
2.3. Связь графа и партиции
-
-
Проблема: смешение транспорта и сверки
-
Принцип one-step и его требования
-
Реконсайлер как отдельный слой
-
Трёхфазная модель как протокол поверх реконсайлера
-
Преимущества выделения реконсайлера
-
Альтернативы и почему они хуже
-
Практические следствия
-
Открытые вопросы
-
Резюме
1. Аннотация
В архитектуре репликации данных между гетерогенными системами (1С ↔ 1С, 1С ↔ внешняя система, БД ↔ БД) исторически смешиваются две принципиально разные задачи:
-
Перемещение данных — приём пакетов, замыкание графа ссылок, топологическая сортировка, выдача батчей.
-
Сверка состояния — выявление расхождений между источником и приёмником, управление ремонтом и актуализацией.
В данной статье обосновывается выделение второй задачи в отдельный архитектурный слой — реконсайлер, который:
-
не нарушает принцип one-step ETL;
-
не превращает посредника в оркестратор;
-
остаётся набором независимых одношаговых эндпоинтов;
-
позволяет строить поверх себя любые протоколы (трёхфазный, двухфазный, однофазный);
-
обеспечивает наблюдаемость, аудит и управляемость репликации.
Статья не привязана к конкретному продукту и описывает общий архитектурный паттерн, применимый к любому пассивному ETL-посреднику.
2. Базовые понятия
Прежде чем говорить о слоях, реконсайлере и протоколах, необходимо определить три вещи: что такое граф ссылок, что такое партиция и как они связаны.
2.1. Граф ссылок
2.1.1. Что это
В любой базе данных объекты ссылаются друг на друга. Документ «Заказ клиента» ссылается на справочник «Контрагенты». Справочник «Контрагенты» ссылается на справочник «Адреса». Справочник «Номенклатура» ссылается на справочник «Единицы измерения».
Если изобразить это стрелками, получится граф:
Заказ клиента → Контрагент → Адрес | |-→ Номенклатура → Единица измерения
-
Узлы графа — это объекты (документы, справочники, регистры).
-
Рёбра графа — это ссылки между ними.
-
Направление ребра — от того, кто ссылается, к тому, на кого ссылаются.
2.1.2. Почему это проблема при репликации
Нельзя загрузить «Заказ клиента» в приёмник, если там ещё нет «Контрагента». Нельзя загрузить «Контрагента», если нет «Адреса». Это ссылочная целостность.
Порядок загрузки должен быть таким:
1. Адрес 2. Контрагент 3. Единица измерения 4. Номенклатура 5. Заказ клиента
Это называется топологический порядок: зависимости идут раньше зависимых.
2.1.3. Замыкание графа
Если источник изменил «Заказ клиента», а в приёмнике нет «Адреса» — нужно догрузить не только «Заказ клиента», но и всю цепочку зависимостей.
Замыкание — это множество всех узлов, которые нужны, чтобы загрузить корневой объект. Оно включает:
-
корневой объект (Заказ клиента);
-
его прямые зависимости (Контрагент, Номенклатура);
-
зависимости зависимостей (Адрес, Единица измерения);
-
и так далее, пока не упрёмся в объекты, которые уже есть в приёмнике.
2.1.4. Кто строит граф
Граф строит посредник. Источник не знает, чего не хватает в приёмнике. Приёмник не знает, что есть в источнике. Только посредник видит обе стороны и может вычислить, какие узлы нужно догрузить.
2.1.5. Что даёт граф
| Свойство графа | Что оно даёт |
|---|---|
| Замыкание | Множество всех необходимых узлов |
| Глубина (depth) | Приоритет для топосортировки |
| Дедупликация (seen) | Защита от циклов и повторного обхода |
| Ограничение глубины | Защита от бесконечных графов |
| Топологический порядок | Гарантия ссылочной целостности |
2.2. Партиция
2.2.1. Что это
Партиция — это независимая часть данных, которую можно сверять отдельно от остальных.
Формат:
{тип объекта}/{аналитика}/{дата}
Примеры:
-
documents/company_1/store_001/2026-09-15— все документы типа «Заказ клиента» по компании 1, магазину 1, за 15 сентября. -
catalogs/company_1/2026-09-15— все справочники по компании 1 за 15 сентября. -
registers/company_1/store_001/2026-09— все регистры по компании 1, магазину 1, за сентябрь.
2.2.2. Зачем нужна
Если сверять всю базу целиком, то:
-
нужно сравнивать миллионы объектов;
-
любое расхождение требует полной перепроверки;
-
невозможно распараллелить;
-
невозможно точечно перезапустить.
Если сверять по партициям, то:
-
каждая партиция — это небольшой набор объектов;
-
расхождения локализованы;
-
можно сверять параллельно;
-
можно перезапустить только проблемную партицию.
2.2.3. Как формируется хеш партиции
Для каждой партиции вычисляется отпечаток — набор значений, который позволяет быстро понять, совпадают ли данные у источника и приёмника.
| Компонент | Что это | Когда используется |
|---|---|---|
count |
Число объектов в партиции | Всегда |
uid_hash |
Хеш множества UID объектов | Всегда |
body_hash |
Хеш тел объектов | Если версионирование отключено |
version_hash |
Хеш версий объектов | Если версионирование включено |
Пример:
У источника в партиции documents/company_1/store_001/2026-09-15:
-
1240 документов;
-
UID:
a1b2...,c3d4..., ...; -
версии: 172345, 172346, ....
У приёмника:
-
1238 документов;
-
UID:
a1b2...,c3d4..., ... (двух не хватает); -
версии: 172345, 172340, ... (одна устарела).
Хеши не совпадут. Реконсайлер поймёт: в этой партиции есть missing (два объекта) и version_mismatch (один объект).
2.2.4. Почему партиция — минимальная единица сверки
Потому что сверять по одному объекту — слишком дорого (миллионы запросов). Сверять всю базу — слишком грубо (любая ошибка требует полной перепроверки). Партиция — это компромисс: достаточно мелко, чтобы локализовать проблемы, и достаточно крупно, чтобы не гонять миллионы запросов.
2.2.5. Типы расхождений (discrepancies)
| Тип | Что означает | Что делать |
|---|---|---|
missing |
Объект есть в источнике, нет в приёмнике | Актуализация (INSERT) |
version_mismatch |
Объект есть, но версии различаются | Ремонт (UPDATE) |
extra |
Объект есть в приёмнике, нет в источнике | Политика: игнорировать / удалять |
ref_broken |
Ссылка на несуществующий объект | Триггер для актуализации |
2.3. Связь графа и партиции
| Понятие | Отвечает на вопрос | Кто использует |
|---|---|---|
| Граф | «Что на что ссылается и в каком порядке загружать?» | Слой репликации |
| Партиция | «Что с чем сравнивать и как найти расхождения?» | Слой реконсайлера |
Граф — это про порядок загрузки.
Партиция — это про обнаружение расхождений.
Реконсайлер говорит: «В партиции X не хватает объектов A, B, C».
Репликация говорит: «Чтобы загрузить A, нужны D и E; чтобы загрузить D, нужна F; вот порядок».
Оркестратор говорит: «Понял. Запускаю ремонт для B, актуализацию для A, C, D, E, F».
3. Проблема: смешение транспорта и сверки
В большинстве систем репликации логика перемещения данных и логика сверки состояния оказываются переплетены. Это приводит к нескольким проблемам:
| Проблема | Следствие |
|---|---|
| Посредник знает о фазах | Нарушение one-step, появление state machine внутри ядра |
| Посредник сравнивает состояние | Требует знания о партициях, хешах, версионировании |
| Посредник управляет ремонтом | Превращается в оркестратор, теряет пассивность |
| Логика сверки размазана по ядру | Сложно тестировать, сложно расширять, сложно наблюдать |
| Смешение прав доступа | Один и тот же токен для репликации и сверки |
Ключевое осознание: перемещение данных и сверка состояния — это две разные ответственности, и их смешение противоречит принципу one-step.
4. Принцип one-step и его требования
One-step ETL означает:
-
Каждый вызов — независимая операция.
-
Посредник не хранит состояние между вызовами в рамках длительной транзакции.
-
Посредник не оркестрирует.
-
Посредник не вызывает источник или приёмник.
-
Каждый эндпоинт — один шаг.
Если добавить в ядро логику сверки «на лету», принцип нарушается: появляется скрытое состояние (какие отчёты уже пришли, какие ещё нет), появляется оркестрация (когда запускать сравнение), появляется зависимость от расписания.
Решение: вынести сверку в отдельный слой, который сам состоит из one-step эндпоинтов.
5. Реконсайлер как отдельный слой
5.1. Определение
Реконсайлер — это архитектурный слой посредника, который предоставляет набор one-step эндпоинтов для сверки состояния данных между источником и приёмником.
Он не перемещает данные. Он не замыкает граф. Он не сортирует. Он только:
-
принимает отчёты о партициях;
-
сравнивает их;
-
хранит расхождения (discrepancies);
-
отдаёт расхождения источнику;
-
формирует задания на сверку;
-
отдаёт статус сверки.
5.2. Место в архитектуре

Слои независимы: репликация не знает о реконсайлере, реконсайлер не знает о репликации. Они разделяют только общие хранилища.
5.3. Эндпоинты реконсайлера
| Эндпоинт | Назначение |
|---|---|
POST /reconcile/report |
Приём отчёта о партиции от источника или приёмника |
GET /reconcile/discrepancies |
Выдача расхождений |
GET /reconcile/tasks |
Выдача задания на сверку |
GET /reconcile/status |
Статус сверки по партиции |
POST /reconcile/repair/start |
Запуск ремонта для списка расхождений |
5.4. Почему это не нарушает one-step
| Требование one-step | Как выполняется |
|---|---|
| Каждый вызов — независимая операция | POST /reconcile/report просто сохраняет отчёт. Всё. |
| Нет состояния между вызовами | Сравнение запускается как отдельная операция, когда оба отчёта уже сохранены |
| Нет оркестрации | Реконсайлер не решает, когда сравнивать. Он сравнивает по факту поступления данных |
| Нет вызовов источника/приёмника | Все вызовы инициирует сторона клиента |
| Каждый эндпоинт — один шаг | Каждый эндпоинт реконсайлера — один шаг |
Важно: сравнение отчётов — это асинхронная операция, которая запускается ядром при поступлении второго отчёта. Но это не нарушает one-step, потому что:
-
сравнение — это один шаг (принять отчёт → если пара собралась, сравнить → сохранить discrepancies);
-
с точки зрения вызывающей стороны каждый вызов остаётся one-step;
-
реконсайлер не хранит «текущую фазу» — он хранит только отчёты и discrepancies.
5.5. Форматы данных
Отчёт о партиции:
Discrepancy:
{
"id": "disc-001",
"run_id": "recv-001",
"partition_id": "documents/company_1/store_001/2026-09-15",
"entity": "Document_ЗаказКлиента",
"kind": "missing",
"source_uid": "a1b2c3d4-...",
"details": {
"depth": 3,
"requested_by": "Контрагент"
},
"status": "open",
"created_at": "2026-09-15T10:00:00Z"
}
6. Трёхфазная модель как протокол поверх реконсайлера
Трёхфазная модель — это не внутренняя логика посредника. Это протокол, который реализует оркестратор (сторона клиента), используя эндпоинты реконсайлера и репликации.
6.1. Фаза 1: Аудит
Оркестратор (источник) → POST /reconcile/report → Посредник (реконсайлер) Оркестратор (приёмник) → POST /reconcile/report → Посредник (реконсайлер) Оркестратор (источник) → GET /reconcile/discrepancies → Посредник (реконсайлер)
Цель: выявить расхождения без передачи объектов.
Шаги:
-
Источник формирует хеши партиций.
-
Приёмник формирует хеши партиций.
-
Реконсайлер сравнивает, формирует discrepancies.
-
Источник запрашивает discrepancies.
-
Источник разделяет по типам:
-
version_mismatch→ Фаза 2 (ремонт); -
missing→ Фаза 3 (актуализация); -
extra→ политика; -
ref_broken→ триггер для актуализации.
-
6.2. Фаза 2: Ремонт
Оркестратор (источник) → POST /packet (phase=repair) → Посредник (репликация) Оркестратор (приёмник) → GET /export/next → Посредник (репликация) Оркестратор (приёмник) → POST /export/ack → Посредник (репликация)
Цель: обновить уже загруженные объекты (version_mismatch).
Особенности:
-
BFS-замыкание не нужно — зависимости уже есть.
-
Топосортировка не нужна — объекты уже упорядочены.
-
Конфликты версий — политика: source wins / target wins / manual.
6.3. Фаза 3: Актуализация
Оркестратор (источник) → POST /packet (phase=actualization) → Посредник (репликация) Оркестратор (источник) → GET /missing → Посредник (репликация) Оркестратор (источник) → POST /packet (packet_id, missing) → Посредник (репликация) ... цикл ... Оркестратор (приёмник) → GET /export/next → Посредник (репликация) Оркестратор (приёмник) → POST /export/ack → Посредник (репликация)
Цель: загрузить отсутствующие объекты (missing).
Особенности:
-
BFS-замыкание обязательно — нужно найти все зависимости.
-
Ленивая докачка — только missing, а не всё.
-
Топосортировка — BFS + Кан.
6.4. Фаза 4: Re-аудит
Оркестратор (источник) → POST /reconcile/report → Посредник (реконсайлер) Оркестратор (приёмник) → POST /reconcile/report → Посредник (реконсайлер) Оркестратор (источник) → GET /reconcile/discrepancies → Посредник (реконсайлер)
Цель: доказать, что расхождения закрыты.
Критерий завершения:
consistency_ratio = 1 - (discrepancies_open / total_partitions)
Если consistency_ratio >= threshold (например, 0.99) — цикл успешен.
Вывод: трёхфазная модель — это сценарий оркестратора, а не состояние посредника. Реконсайлер и репликация — это наборы one-step эндпоинтов, из которых оркестратор строит протокол.
7. Преимущества выделения реконсайлера
7.1. Архитектурные
| Преимущество | Описание |
|---|---|
| Чистота one-step | Репликация и сверка — независимые слои, каждый состоит из one-step эндпоинтов |
| Пассивность посредника | Ядро не оркестрирует, не хранит фазы, не вызывает клиента |
| Разделение ответственности | Репликация перемещает, реконсайлер сверяет |
| Расширяемость | Можно добавить новые виды сверки, не трогая репликацию |
| Тестируемость | Каждый слой тестируется независимо |
7.2. Эксплуатационные
| Преимущество | Описание |
|---|---|
| Наблюдаемость | Отдельные метрики для сверки и репликации |
| Аудит | Все отчёты и discrepancies сохраняются |
| Управляемость | Можно запускать сверку независимо от репликации |
| Безопасность | Разные права для reconcile:* и replicate:* |
7.3. Организационные
| Преимущество | Описание |
|---|---|
| Параллельная разработка | Команды могут работать над слоями независимо |
| Чёткие границы | Понятно, кто за что отвечает |
| Простота обучения | Новые разработчики быстрее понимают архитектуру |
8. Альтернативы и почему они хуже
8.1. Встроить реконсайлер в репликацию
Минусы:
-
Нарушение one-step.
-
Появление state machine внутри ядра.
-
Смешение ответственностей.
-
Сложность тестирования.
8.2. Вынести реконсайлер в отдельный сервис
Минусы:
-
Дополнительная инфраструктура.
-
Сетевые задержки.
-
Дублирование хранилищ.
-
Усложнение развёртывания.
Вывод: выделение в отдельный слой внутри посредника — оптимальный баланс между чистотой архитектуры и простотой эксплуатации.
9. Практические следствия
9.1. Для разработчиков
-
Репликация и реконсайлер — независимые модули.
-
Общие хранилища — единственная точка соприкосновения.
-
Можно писать тесты для каждого слоя отдельно.
9.2. Для архитекторов
-
Трёхфазная модель — это протокол оркестратора, а не состояние посредника.
-
Посредник остаётся пассивным one-step ETL.
-
Реконсайлер — это набор one-step эндпоинтов для сверки.
9.3. Для менеджеров
-
Слои можно разрабатывать параллельно.
-
Развёртывание не усложняется.
-
Мониторинг становится прозрачнее.
10. Открытые вопросы
-
Должен ли реконсайлер иметь собственное хранилище или использовать общее?
-
Как запускать сравнение отчётов: синхронно при поступлении второго отчёта или асинхронно по расписанию?
-
Нужен ли отдельный эндпоинт для принудительного запуска сравнения?
-
Как обрабатывать ситуацию, когда один отчёт пришёл, а второй — нет (timeout)?
-
Нужна ли поддержка нескольких источников в реконсайлере?
-
Как быть с частичными отчётами (только count, без хешей)?
-
Нужна ли поддержка инкрементального аудита (только изменившиеся партиции)?
-
Конкретные значения
max_depth,max_closure_size,target_drain_seconds. -
Периодичность аудита по умолчанию.
-
Политика
extraв приёмнике.
11. Резюме
Граф — это модель ссылок между объектами. Он нужен, чтобы загружать объекты в правильном порядке (сначала зависимости, потом зависимые). Граф строит посредник, потому что только он видит обе стороны.
Партиция — это независимая часть данных для сверки. Она нужна, чтобы быстро находить расхождения и не перепроверять всю базу. Партиции формирует оркестратор, а сравнивает реконсайлер.
Реконсайлер — это отдельный архитектурный слой посредника, который:
-
предоставляет one-step эндпоинты для сверки состояния;
-
не нарушает принцип one-step;
-
не превращает посредника в оркестратор;
-
позволяет оркестратору строить поверх себя любые протоколы (трёхфазный, двухфазный, однофазный).
Репликация — это слой, который работает с графом: строит замыкание, топосортирует, отдаёт батчи.
Трёхфазная модель — это протокол оркестратора, а не внутренняя логика посредника.
Посредник остаётся пассивным, быстрым, one-step трансформером, который делает то, что попросили в одном вызове.
Выделение реконсайлера в отдельный слой — это архитектурно чистое решение, которое:
-
разделяет ответственности;
-
упрощает тестирование;
-
повышает наблюдаемость;
-
сохраняет one-step;
-
не усложняет эксплуатацию;
-
применимо к любому пассивному ETL-посреднику, независимо от конкретной реализации.
Вступайте в нашу телеграмм-группу Инфостарт