Перейти к основному содержимому

GET /api/v1/admin/handbook/sync-state — Состояние синхронизации справочников с Talent

Назначение

Отдаёт администратору платформы диагностическую сводку синка справочников из Talent Handbook: по каждому типу записей — счётчики чекпоинтов HandbookSyncState и лаг, списком — проблемные записи (Conflict / DeadLettered, требующие ручного действия), плюс момент последней завершённой полной пересинхронизации. Основной инструмент расследования DLQ-инцидентов и решения «нужен ли ресинк / archive» перед командами archive-restore-entry.md.

Metadata

ПолеЗначение
Сервис/контекстhandbook
API areaadmin (администратор платформы, ADR-0053)
Feature groupsync
Статусdraft
Документdocs/06-services/handbook/api/admin/get-sync-state.md

Актор и доступ

ПроверкаЗначение
Actor typeАдминистратор платформы через admin API — отдельная admin-система вне основной Identity (ADR-0053); m2m-токен admin-системы, audience pin.handbook.admin
Auth policy / Permissionshandbook.entries.manage
Scope (RBAC)платформа
Tenant isolationне применяется
Auditread (не sensitive) — служебные данные синка, PII нет; access-audit не пишется

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/v1/admin/handbook/sync-state?aggregateType=&status=
Success status200
Correlationсквозной trace через заголовок traceparent

Входные данные / параметры запроса

ПолеИсточникТипОбязательностьВалидация / Ограничения
aggregateTypequeryHandbookAggregateType?Нетenum строкой: Country / Region / City / Currency / Language; сужает сводку и список до одного типа; неизвестное значение → VALIDATION_FAILED
statusqueryHandbookSyncRecordStatus?Нетenum строкой: Synced / Conflict / DeadLettered; сужает список проблемных записей; неизвестное значение → VALIDATION_FAILED

Модель ответа HandbookSyncStateSummaryModel

Источник — служебная сущность HandbookSyncState (чекпоинт «одна строка на запись источника», таблица handbook.talent_sync_states — дом полей talent-replica.md) и отметка прогонов полной пересинхронизации (talent-initial-import.md).

ПолеТипОбязательностьИсточникОписание
AggregatesHandbookSyncAggregateSummary[]Даагрегация чекпоинтовСводка по типам (все пять либо один — по фильтру aggregateType).
RecordsHandbookSyncRecordModel[]Да (может быть пустым)чекпоинтыПроблемные записи: по умолчанию — Conflict и DeadLettered; фильтр status сужает до одного статуса. status = Synced списком не выдаётся (тысячи строк) — только сводкой. Порядок — UpdatedAt убыв.; не более 500 строк.
LastFullResyncAtDateTimeOffset?Нетотметка джобы пересинхронизацииМомент последнего успешно завершённого прогона полной пересинхронизации; null — не выполнялась. Точка отсчёта reconciliation и предупреждения NotSeenInLastResync при restore.
DlqTopicstringДаконстанта контекстаpin.handbook.talent-sync-dlq.v1 — куда смотреть при DeadLettered.

HandbookSyncAggregateSummary

ПолеТипОбязательностьОписание
AggregateTypeHandbookAggregateTypeДаТип записей источника.
TotalCountintДаВсего чекпоинтов типа (= записей реплики, прошедших через синк).
SyncedCountintДаВ статусе Synced.
ConflictCountintДаВ статусе Conflict (ISO-конфликты и пр.) — требуют ручного разрешения.
DeadLetteredCountintДаВ статусе DeadLettered — событие в DLQ, требуется расследование/replay.
LastEventAtDateTimeOffset?НетOccurredAt последнего применённого события типа; null — событий не было.
LagSecondslong?Нетnow − LastEventAt. Для редко меняющихся справочников большой «лаг» сам по себе не проблема — сигналом являются ConflictCount/DeadLetteredCount и метрики consumer-а.

HandbookSyncRecordModel

Проекция чекпоинта HandbookSyncState; дом полей — talent-replica.md.

ПолеТипОбязательностьОписание
AggregateTypeHandbookAggregateTypeДаТип записи.
ExternalIdlongДаId записи в источнике (= id реплики; для short-типов — то же значение).
StatusHandbookSyncRecordStatusДаSynced / Conflict / DeadLettered.
LastAppliedVersionlong?НетПоследняя применённая версия источника; у регионов null — монотонность по времени события, версии у источника нет (region.md).
LastEventAtDateTimeOffset?НетOccurredAt последнего применённого события записи.
LastErrorstring?НетТекст последней ошибки (например, HANDBOOK_SYNC_ISO_CONFLICT, HANDBOOK_SYNC_GEO_MISMATCH, HANDBOOK_SYNC_COUNTRY_NOT_FOUND после исчерпания ретраев); без PII.
UpdatedAtDateTimeOffsetДаМомент последнего изменения чекпоинта.

Алгоритм

  1. Контекст и доступ. Операцию выполняет администратор платформы через admin API (permission handbook.entries.manage); read-only, без побочных эффектов.
  2. Проверка входа. aggregateType и status, если переданы, — из допустимых enum-значений; иначе VALIDATION_FAILED (400).
  3. Агрегация по типам. Чекпоинты HandbookSyncState группируются по типу (все пять либо один — по фильтру aggregateType) со счётчиками TotalCount/SyncedCount/ConflictCount/DeadLetteredCount, LastEventAt, LagSeconds.
  4. Список проблемных записей. Отбираются записи в статусе Conflict/DeadLettered (либо один статус — по фильтру status; Synced в списке не выдаётся, только в счётчиках), не более 500 строк, порядок — UpdatedAt убыв.
  5. Пересинхронизация. К ответу прикладывается LastFullResyncAt — момент последнего успешно завершённого прогона полной пересинхронизации.
  6. Результат. 200 + HandbookSyncStateSummaryModel; пустые сводка/список — валидный ответ (синк ещё не выполнялся).

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
— (auth/authz)401 / 403нет/невалиден токен admin-системы; нет handbook.entries.manageбез телаno retry
VALIDATION_FAILED400aggregateType или status вне enum-значений«Проверьте параметры запроса»no retry

Acceptance Criteria

  • Полная сводка. Given работающий синк по пяти типам; When GET /api/v1/admin/handbook/sync-state; Then 200, Aggregates содержит пять строк со счётчиками Total/Synced/Conflict/DeadLettered, LastEventAt/LagSeconds; Records — только Conflict/DeadLettered-записи.
  • Фильтр по типу. Given aggregateType=City; When запрос; Then сводка и записи — только по городам.
  • Фильтр по статусу. Given status=DeadLettered; When запрос; Then Records содержит только DeadLettered-записи (с LastError), сводка не меняется.
  • Synced списком не выдаётся. Given status=Synced; When запрос; Then 200, Records = [] (данные о Synced — в счётчиках сводки).
  • ISO-конфликт виден. Given событие валюты ушло в DLQ по HANDBOOK_SYNC_ISO_CONFLICT; When запрос; Then запись присутствует в Records со Status = DeadLettered и кодом в LastError; DeadLetteredCount по валютам ≥ 1.
  • Пересинхронизация. Given полная пересинхронизация успешно завершилась в момент T; When запрос; Then LastFullResyncAt = T; Given ни одной пересинхронизации — Then LastFullResyncAt = null.
  • Регион без версии. Given чекпоинт региона; When запрос; Then LastAppliedVersion = null, LastEventAt заполнен.
  • Неизвестный enum. Given aggregateType=District; When запрос; Then 400 VALIDATION_FAILED.

Совместимость и наблюдаемость

  • Расширения моделей сводки — additive (новые optional-поля, новые enum-значения HandbookAggregateType при расширении scope справочников — клиент admin-системы игнорирует неизвестные значения). Удаление/ переименование полей — breaking.
  • Операция — витрина над теми же данными, что и метрики consumer-а (talent-handbook-sync.md: handbook_sync_lag_seconds, handbook_sync_dlq_total, handbook_sync_stale_events_total) — алерты живут на метриках, API служит для расследования человеком.
  • Logs: факт запроса (фильтры, размер выдачи); metrics: handbook_admin_sync_state_requests_total, latency p95; traces — OTel (traceparent); dashboard handbook-overview.

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы