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

Реплика справочников handbook в chessboard (chessboard.handbook_*)

Дом полей и правил локальной реплики справочников handbook внутри контекста chessboard: страны, регионы, города, валюты. Consumer handbook-sync и write-path-операции проектов ссылаются сюда, не переописывая поля и правила.

Назначение

chessboard — единственный сервис PIN с локальной репликой справочников (ADR-0063 п.6): гео и валюта участвуют в hot-path карточек/фильтров и в каждой мутации проекта, поэтому валидация ссылок CountryId/RegionId/CityId/CurrencyId на write-path выполняется по локальным таблицам без сетевого вызова в handbook. Вторая задача реплики — источник актуальных имён для refresh денормализованных полей проектов RegionName/CityName (project.md) при переименованиях в справочнике с переопубликацией project-upserted, чтобы проекции catalog догнали новое имя.

Реплика проекционная, а не полная копия handbook: хранятся только поля, которые chessboard реально использует. Справочник языков (pin.handbook.languages.v1) не потребляется — у chessboard нет ссылок на языки. Ведение данных остаётся в Talent Handbook → handbook; в chessboard реплику пишет только consumer.

Классификация

ПолеЗначение
Контекстchessboard
KindReplica (source of truth — handbook; транзитивно — Talent Handbook, ADR-0063)
Source of truthконтекст handbook: country / region / city / currency
Таблицыchessboard.handbook_countries, chessboard.handbook_regions, chessboard.handbook_cities, chessboard.handbook_currencies
Наполнениетолько consumer группы pin-chessboard-handbook-sync (handbook-sync) из compacted-топиков `pin.handbook.{countries
Tenant isolationне tenanted — платформенный справочник, общий для всех застройщиков

Поля

Идентификаторы — id источника (short/long, без Guid, ADR-0063 п.2); значения полей — полный snapshot из payload событий pin.handbook.* (контракты: country-upserted, region-upserted, city-upserted, currency-upserted).

HandbookCountryReplica (chessboard.handbook_countries)

ПолеТипОбязательностьОграниченияИсточник (payload country-upserted)Описание
IdshortДаPKIdИдентификатор страны (= id источника).
NamestringДаvarchar(64), min 1FullName (display-конвенция Name = FullName, snapshots.md)Название страны («Российская Федерация»).
IsoCode2stringДаchar(2), upperIsoCode2Код ISO 3166-1 alpha-2 (RU) — display/интеграции.
IsoCode3stringДаchar(3), upperIsoCode3Код ISO 3166-1 alpha-3 (RUS).
IsArchivedboolДаdefault falseIsArchivedSoft-архив источника: запись выведена из оборота (HR-5).
ReplicaVersionlongДа>= 1ReplicaVersionМонотонный version-guard (HR-2).
SyncedAtDateTimeOffsetДавремя применения событияШтамп применения; основа метрики staleness.
Что не хранится / seed-факт

Поля ShortName/DefaultCurrencyId/DefaultLanguageId события chessboard не читает и не хранит. Seed-факт источника: Россия — CountryId = 1 (дефолт страны проекта при создании).

HandbookRegionReplica (chessboard.handbook_regions)

ПолеТипОбязательностьОграниченияИсточник (payload region-upserted)Описание
IdlongДаPKIdИдентификатор региона (= id источника).
CountryIdshortДалогическая ссылка на handbook_countries (без FK, HR-4), index ix_handbook_regions_country_idCountryIdСтрана региона — проверка согласованности гео на write-path.
NamestringДаvarchar(256), min 1NameНазвание региона («Самарская область») — источник refresh Project.RegionName (HR-6).
IsArchivedboolДаdefault falseIsArchivedSoft-архив (HR-5).
LastEventAtDateTimeOffsetДаLastEventAtРоль версии: у региона нет ReplicaVersion источника — time-based guard (HR-2, контракт region-upserted).
SyncedAtDateTimeOffsetДавремя применения событияШтамп применения.

HandbookCityReplica (chessboard.handbook_cities)

ПолеТипОбязательностьОграниченияИсточник (payload city-upserted)Описание
IdlongДаPKIdИдентификатор города (= id источника).
CountryIdshortДалогическая ссылка (без FK, HR-4), index ix_handbook_cities_country_idCountryIdСтрана города.
RegionIdlong?Нетлогическая ссылка (без FK), index ix_handbook_cities_region_id (where not null)RegionIdРегион города; null — город вне региона (города федерального значения).
NamestringДаvarchar(256), min 1NameНазвание города («Самара») — источник refresh Project.CityName (HR-6).
IsArchivedboolДаdefault falseIsArchivedSoft-архив (HR-5).
ReplicaVersionlongДа>= 1ReplicaVersionМонотонный version-guard (HR-2).
SyncedAtDateTimeOffsetДавремя применения событияШтамп применения.
Вложенные снапшоты не хранятся

Вложенные снапшоты Country/Region payload-а chessboard не хранит: актуальные имена страны/региона ведутся собственными строками handbook_countries/handbook_regions по их топикам.

HandbookCurrencyReplica (chessboard.handbook_currencies)

ПолеТипОбязательностьОграниченияИсточник (payload currency-upserted)Описание
IdshortДаPKIdИдентификатор валюты (= id источника).
NamestringДаvarchar(64), min 1NameНазвание валюты («Российский рубль»).
IsoCodestringДаchar(3), upper, ISO-4217IsoCodeDisplay-код (RUB) — источник поля IsoCode CurrencySnapshot исходящих событий chessboard.
Signstring?Нетvarchar(16)SignЗнак валюты («₽») — для CurrencySnapshot.
IsArchivedboolДаdefault falseIsArchivedSoft-архив (HR-5).
ReplicaVersionlongДа>= 1ReplicaVersionМонотонный version-guard (HR-2).
SyncedAtDateTimeOffsetДавремя применения событияШтамп применения.
CurrencySnapshot и RUB-only

Name/IsoCode/Sign нужны chessboard не только для валидации CurrencyId: из них собирается CurrencySnapshot в исходящих событиях с денежными полями (снимается на момент публикации, правила snapshots.md). Справочник валют не означает мультивалютность — денежные операции остаются RUB-only (ADR-0048).

Связи

Логические ссылки (без FK, HR-4) связывают гео-сущности реплики; валюта (HandbookCurrencyReplica) самостоятельна — гео-таблицы не хранят ссылку на неё (DefaultCurrencyId источника не читается, см. выше).

Правила ведения реплики (HR-1..HR-7)

ПравилоСодержание
HR-1 SingleWriterРеплику пишет только consumer pin-chessboard-handbook-sync. HTTP-операций мутации реплики в chessboard нет (ни developer, ни admin); ведение данных — Talent Handbook, archive/restore — admin API handbook.
HR-2 VersionGuardСобытие применяется только при payload.ReplicaVersion > хранимой версии записи (регион — payload.LastEventAt > хранимого LastEventAt); устаревшее/повторное событие — идемпотентный skip + метрика stale. Компакция и полная пересинхронизация handbook дают «скачки» версии — каждое событие самодостаточно.
HR-3 SnapshotSemanticsPayload — полный snapshot: применённое событие перезаписывает все поля записи реплики; отсутствие optional-поля (RegionId города, Sign валюты) означает «значение очищено», а не «оставить прежнее».
HR-4 NoFkСсылки CountryId/RegionId внутри реплики — логические, без FK: порядок между топиками справочника не гарантирован (город может приехать раньше своей страны/региона). Нерезолвимая ссылка внутри реплики допустима и не блокирует применение события; полнота гео-цепочки проверяется только на write-path проектов.
HR-5 ArchiveSemanticsАрхивная запись (IsArchived = true) не предлагается в lookup-выдачах chessboard и не принимается при создании/изменении проекта (CHESSBOARD_GEO_REF_ARCHIVED); существующие ссылки проектов/объектов не ломаются и не чистятся — чтение карточек и резолв уже сохранённых id работают включая архивные записи. Физических удалений в реплике нет.
HR-6 NameRefreshПри применённом upsert-е региона/города, изменившем Name, chessboard в том же согласованном изменении обновляет денормализованные RegionName/CityName всех проектов с этим RegionId/CityId и переопубликует project-upserted каждого затронутого проекта — подробности ниже.
HR-7 BootstrapНачальное наполнение — чтение всех четырёх compacted-топиков pin.handbook.*.v1 с offset 0 (каждый топик хранит последнюю запись каждого id бессрочно); отдельного HTTP-импорта нет. Порядок топиков не важен (HR-4). Re-emit при пересинхронизации handbook — идемпотентный no-op (HR-2), кроме записей с фактическими изменениями.

Использование на write-path проектов

Валидация справочных ссылок выполняется по реплике при создании/изменении проекта (операции api/developers/create-project/update-project) и при изменении валютных ссылок цен. Публикация проекта требует резолвленных гео-id (INV-PRJ-3, project.md).

ПроверкаУсловиеОшибкаHTTP
Ссылка существуетCountryId/RegionId/CityId/CurrencyId найден в реплике (после fallback-резолва, см. ниже)CHESSBOARD_GEO_REF_UNKNOWN422
Ссылка не архивназапись не IsArchived — только при создании/изменении значения поля; уже сохранённое значение при правке других полей не перепроверяется (HR-5)CHESSBOARD_GEO_REF_ARCHIVED422
Гео-цепочка согласованаcity.CountryId = указанной стране; при заданном RegionIdcity.RegionId = указанному региону и region.CountryId = указанной странеCHESSBOARD_CITY_REGION_MISMATCH422

Коды распространяются на любую справочную ссылку chessboard, включая CurrencyId (для валюты применимы первые две проверки). Обработка ошибок для пользователя: обновить lookup-справочник (public lookup handbook) и поправить выбор; retry не поможет.

Fallback-резолв при лаге реплики

Если id не найден в локальной реплике, write-path делает разовый синхронный batch-резолв в handbook (resolve-references) — это закрывает окно eventual-consistency между появлением записи в справочнике и её доездом до реплики. Найденная запись применяется к валидации (в реплику не пишется — реплика ведётся только событиями, HR-1); не найденная — отказ CHESSBOARD_GEO_REF_UNKNOWN. Недоступность handbook при fallback не роняет операцию только в одном случае — id есть в реплике; при отсутствии и в реплике, и ответа resolve — отказ как «не найдено».

Refresh денормализованных имён (HR-6)

Проекты хранят канонический паттерн ссылки «id + display-имя»: RegionId + RegionName, CityId + CityName (project.md; имена снимаются из реплики при записи проекта). Переименование в справочнике должно доехать до карточек и фильтров — за это отвечает consumer:

  1. Триггер. Применённый upsert region-upserted/city-upserted, при котором Name изменилось относительно хранимого в реплике (включая первое появление записи, если проекты уже ссылаются на id — самопочинка после ручных разборов).
  2. Эффект. В одном согласованном изменении с upsert-ом реплики: у всех проектов с этим RegionId/CityId, чьё RegionName/CityName отличается от нового имени, — поле обновляется, версия проекта растёт, и для каждого затронутого проекта переопубликуется project-upserted (полный snapshot, key = project.Id) — проекции catalog, измерения dev-stats и поисковый индекс догоняют новое имя штатным путём, без специального «события переименования».
  3. Условие «отличается» делает refresh идемпотентным: replay топика и re-emit пересинхронизации не порождают повторных обновлений и лишних событий.
  4. Не триггерит refresh: archive/restore без смены имени; переименование страны (CountryName в проекте не денормализуется); переименование валюты (CurrencySnapshot исходящих событий фиксируется на момент публикации и не «догоняется» — правило 3 snapshots.md).
  5. Объём. Переименование региона затрагивает до всех проектов региона (масштаб платформы — тысячи проектов, ADR-0056): обновление выполняется батчево одним изменением, события — по одному на проект; витрина каталога догоняет асинхронно в пределах штатного окна применения project-upserted.

Согласованность и гарантии для потребителей chessboard

  • Read-after-write внутри chessboard: write-path валидация видит реплику в состоянии на момент запроса; окно отставания от handbook — доставка Kafka + применение (SLA консюмера < 1 мин, алерт по лагу — handbook-sync); окно закрывается fallback-резолвом.
  • Для downstream chessboard ничего не меняется: catalog/dev-stats/crm получают гео и валюту из snapshot-ов событий chessboard (RegionId+RegionName, CityId+CityName, CurrencyId+ CurrencySnapshot) и на pin.handbook.* не подписываются (стратегия потребления — ADR-0063 п.6).
  • Устаревание имени региона в источнике (гэп region-событий Talent) наследуется репликой и закрывается полной пересинхронизацией handbook (ADR-0063 п.4) — chessboard дополнительных компенсаций не делает.

Ошибки

КодHTTPУсловие
CHESSBOARD_GEO_REF_UNKNOWN422справочная ссылка (CountryId/RegionId/CityId/CurrencyId) не найдена ни в реплике, ни fallback-резолвом
CHESSBOARD_GEO_REF_ARCHIVED422при создании/изменении указана архивная запись справочника (HR-5)
CHESSBOARD_CITY_REGION_MISMATCH422город/регион/страна рассогласованы (гео-цепочка не сходится)

Индексы, хранение, безопасность

АспектЗначение
ИндексыPK по Id каждой таблицы; ix_handbook_regions_country_id; ix_handbook_cities_country_id; ix_handbook_cities_region_id (where not null). Уникальность ISO-кодов не дублируется — гарантируется источником (INV-HND-2, country.md)
Партиционирование / retentionnone — суммарно тысячи строк; retention бессрочный (физических удалений нет, HR-5)
Concurrencyсобытия применяются последовательно per-key (партиции compacted-топиков); гонка refresh-а с правкой проекта разрешается оптимистической блокировкой проекта (retry консюмера с перечитыванием)
Permissions / PIIреплика — internal-данные chessboard, собственного API не имеет; lookup справочников для UI — публичный API handbook. PII нет
AuditReplicaVersion/LastEventAt + SyncedAt; изменения проекта при refresh штампуются system-actor-ом консюмера

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

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

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