diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ad636ce..fe2f9ca 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -54,6 +54,12 @@ default: .frontend-changes: &frontend-changes - "ordinis-admin-ui/**/*" +# Documentation changes — Diplodoc-built integrator guide. +# docs-internal/ исключено явно: оно НЕ build'ится Diplodoc'ом и не должно +# триггерить docs job. +.docs-changes: &docs-changes + - "docs/**/*" + # ─── TEST ────────────────────────────────────── maven-test: stage: test @@ -112,6 +118,38 @@ maven-package: - ordinis-migrations/src/main/resources/db/changelog/** expire_in: 1 day +# ─── BUILD: Integrator Guide (Diplodoc) ──────── +# Build static HTML documentation для consumers через @diplodoc/cli (yfm). +# Source — docs/ (Markdown + toc.yaml). Output — docs/_dist/ HTML. +# +# Triggers: +# - auto при изменении docs/** +# - manual через web UI (для force-rebuild без code changes) +# +# Artifact `docs/_dist/` сохраняется неделю — pages-deploy job (TBD, +# когда host решён — GitLab Pages либо k8s ingress) подхватит из него. +build-docs: + stage: build + image: repo.nstart.cloud/library/node:20-alpine + before_script: + - corepack enable + - corepack prepare pnpm@9.0.0 --activate + script: + - cd docs + - pnpm install --frozen-lockfile + - pnpm build + - echo "Built $(find _dist -name '*.html' | wc -l) HTML pages, total size:" + - du -sh _dist + artifacts: + name: "ordinis-docs-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA" + paths: + - docs/_dist + expire_in: 1 week + rules: + - changes: *docs-changes + - if: '$CI_PIPELINE_SOURCE == "web"' + when: manual + # ─── PUBLISH (Docker images) ─────────────────── .docker_base: stage: publish diff --git a/docs-internal/README.md b/docs-internal/README.md new file mode 100644 index 0000000..172dde4 --- /dev/null +++ b/docs-internal/README.md @@ -0,0 +1,57 @@ +# Ordinis — internal documentation + +Технические детали реализации, архитектура, operator runbooks и project state +snapshots. **НЕ deployment'ится** через Diplodoc — это внутренние артефакты для +команды Ordinis (не для consumers). + +Consumer-facing документация находится в [`docs/`](../docs/) и build'ится через +Diplodoc → static HTML. + +## Что внутри + +``` +docs-internal/ +├── ops/ # Operator runbook (on-call, incidents, recovery) +│ ├── README.md +│ ├── slo.md +│ ├── outbox-dlq.md +│ ├── db-migrations.md +│ ├── vault-unseal.md +│ └── kafka-restart.md +├── status/ # Project state snapshots (по дням сессий) +│ └── *-state.md +└── tech/ # Архитектурные deep-dives, legacy документы + └── api-integration.md # legacy (мигрирован в docs/integration/api/) +``` + +## Когда что использовать + +| Situation | Read | +|---|---| +| Я — consumer интегрирующий свой backend | [docs/integration/api/](../docs/integration/api/index.md) (Diplodoc) | +| Я — on-call инженер с alert | [ops/README.md](ops/README.md) | +| Я — Ordinis dev планирующий feature | [status/](status/) latest snapshot + design docs в `~/.gstack/projects/claude/` | +| Я — новый разработчик в команде | Сначала consumer docs, потом ops/, потом status/ для context | + +## Архитектурные artifacts (вне репозитория) + +Design docs / план-плансы хранятся в `~/.gstack/projects/claude/`: +- `zimin-main-design-production-prep-20260507-123032.md` — prod stand-up plan +- `zimin-main-design-approval-workflow-v2-20260507-121632.md` — approval flow v2 (DEFERRED) +- `zimin-main-design-dict-relationships-v2-20260507-230237.md` — lineage + cascade rules +- `checkpoints/*.md` — session state checkpoints для context-restore + +## Rationale разделения + +Diplodoc документация — **public contract** для интеграторов: +- Может быть опубликована на public Pages +- Не должна leak'ать internal architecture (security) +- Должна быть стабильной (consumer'ы строят на ней свои интеграции) + +`docs-internal/` — **operational state**: +- Runbooks меняются часто (incidents, lessons learned) +- Status snapshots — historical record для команды +- Tech deep-dives — для разработчиков, не consumers + +Это разделение делает Diplodoc более фокусированным и менее шумным +для consumers. diff --git a/docs/ops/README.md b/docs-internal/ops/README.md similarity index 100% rename from docs/ops/README.md rename to docs-internal/ops/README.md diff --git a/docs/ops/db-migrations.md b/docs-internal/ops/db-migrations.md similarity index 100% rename from docs/ops/db-migrations.md rename to docs-internal/ops/db-migrations.md diff --git a/docs/ops/kafka-restart.md b/docs-internal/ops/kafka-restart.md similarity index 100% rename from docs/ops/kafka-restart.md rename to docs-internal/ops/kafka-restart.md diff --git a/docs/ops/outbox-dlq.md b/docs-internal/ops/outbox-dlq.md similarity index 100% rename from docs/ops/outbox-dlq.md rename to docs-internal/ops/outbox-dlq.md diff --git a/docs/ops/slo.md b/docs-internal/ops/slo.md similarity index 100% rename from docs/ops/slo.md rename to docs-internal/ops/slo.md diff --git a/docs/ops/vault-unseal.md b/docs-internal/ops/vault-unseal.md similarity index 100% rename from docs/ops/vault-unseal.md rename to docs-internal/ops/vault-unseal.md diff --git a/docs/status/2026-05-05-state.md b/docs-internal/status/2026-05-05-state.md similarity index 100% rename from docs/status/2026-05-05-state.md rename to docs-internal/status/2026-05-05-state.md diff --git a/docs/status/2026-05-06-state.md b/docs-internal/status/2026-05-06-state.md similarity index 100% rename from docs/status/2026-05-06-state.md rename to docs-internal/status/2026-05-06-state.md diff --git a/docs/status/2026-05-07-state.md b/docs-internal/status/2026-05-07-state.md similarity index 100% rename from docs/status/2026-05-07-state.md rename to docs-internal/status/2026-05-07-state.md diff --git a/docs/tech/api-integration.md b/docs-internal/tech/api-integration.md similarity index 100% rename from docs/tech/api-integration.md rename to docs-internal/tech/api-integration.md diff --git a/docs/.yfm b/docs/.yfm index 49c48c8..b13b37c 100644 --- a/docs/.yfm +++ b/docs/.yfm @@ -8,5 +8,4 @@ strict: false ignore: - node_modules - _dist - - status/*-state.md addSystemMeta: true diff --git a/docs/README.md b/docs/README.md index c50b1cd..d6846a1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,42 +1,44 @@ -# Ordinis docs (Diplodoc / YFM) +# Ordinis Integrator Guide (Diplodoc / YFM) -Документация Ordinis НСИ ЦУОД ОДХ построена через **Diplodoc** (open-source -documentation platform от Yandex). Markdown source — YFM (Yandex Flavored -Markdown) с расширениями для tabs, notes, includes, mermaid диаграмм. +Consumer-facing документация Ordinis НСИ ЦУОД. Build через **Diplodoc** +(open-source documentation platform от Yandex), source — YFM (Yandex +Flavored Markdown). + +**Scope:** только то, что нужно интеграторам (Альтум team, BI, third-party +consumers). Operator runbooks, архитектурные deep-dives и project state — +во внутренней документации в [`../docs-internal/`](../docs-internal/), +не в Diplodoc build. ## Структура ``` docs/ -├── package.json # @diplodoc/cli + scripts -├── .yfm # Diplodoc config (langs, plugins) -├── toc.yaml # Содержание / навигация (рендерится в sidebar) -├── index.md # Главная страница -├── integration/ # Интеграция по API -│ ├── api/ # Главы для consumers -│ │ ├── index.md # Обзор -│ │ ├── auth.md # Keycloak / JWT -│ │ ├── read-endpoints.md -│ │ ├── events.md # Kafka outbox -│ │ ├── webhooks.md -│ │ ├── bitemporal.md -│ │ ├── x-references.md -│ │ ├── caching.md -│ │ ├── errors.md -│ │ ├── best-practices.md -│ │ └── bundle-cuod.md -│ └── altum-imaging-order.md # Прикладной сценарий -├── ops/ # Operator runbook -│ ├── README.md -│ ├── slo.md -│ ├── outbox-dlq.md -│ ├── db-migrations.md -│ ├── vault-unseal.md -│ └── kafka-restart.md -├── status/ # State snapshots (исключено из build) -│ └── *-state.md -└── tech/ # Legacy технические страницы - └── api-integration.md +├── package.json # @diplodoc/cli + build scripts +├── .yfm # Diplodoc config +├── toc.yaml # Содержание / sidebar +├── index.md # Главная (integrator-focused tabs) +└── integration/ + ├── api/ # 11 глав про API integration + │ ├── index.md + │ ├── auth.md + │ ├── read-endpoints.md + │ ├── events.md + │ ├── webhooks.md + │ ├── bitemporal.md + │ ├── x-references.md + │ ├── caching.md + │ ├── errors.md + │ ├── best-practices.md + │ └── bundle-cuod.md + └── altum-imaging-order.md +``` + +Внутренние материалы (НЕ в Diplodoc build): +``` +docs-internal/ +├── ops/ # Operator runbook (on-call) +├── status/ # Project state snapshots +└── tech/ # Legacy technical pages ``` ## Сборка @@ -48,62 +50,40 @@ cd docs pnpm install pnpm build # → ./_dist (HTML) pnpm serve # → http://localhost:8088 -# либо одной командой: -pnpm dev +pnpm dev # build + serve одной командой ``` -Build output (`_dist/`) — статический HTML, можно деплоить в: +Build output (`_dist/`) — статический HTML, deployable в: - GitLab Pages -- S3 + CloudFront -- nginx/ingress на cluster.142 +- S3 / object storage +- nginx/ingress на k8s cluster -## Автор содержимого +## Editorial guidelines -| Раздел | Owner | -|---|---| -| `/` (index, integration/api/*) | zimin.an@nstart.space | -| `ops/*` | on-call rotation team | -| `integration/altum-imaging-order.md` | Альтум team contact | +Этот set документации — **public contract** для интеграторов: -## Что не committable +1. **Не leak'аем internal architecture** — никаких internal alert names, + service paths, internal infrastructure URLs. +2. **API contract стабилен** — изменения требуют semver bump + announcement. +3. **Consumer-side examples** для каждой концепции (Python, Node, либо Spring). +4. **Версионирование** через bundle / API major. -- `_dist/` — build output (gitignore'нут) -- `node_modules/` — dependencies (gitignore'нут) - -## Deployment - -В roadmap — GitLab CI job `build-docs`: - -```yaml -build-docs: - stage: build - image: node:18 - script: - - cd docs - - pnpm install --frozen-lockfile - - pnpm build - artifacts: - paths: [docs/_dist] - expire_in: 1 week - rules: - - changes: ["docs/**/*"] -``` - -Затем deploy в GitLab Pages либо как отдельный k8s deployment с nginx. +Для operator-side / dev-side материалов — `docs-internal/` либо design docs +в `~/.gstack/projects/claude/`. ## YFM extensions используются - `{% list tabs %}` — табы для switching между audiences - `{% note info|warning|tip %}` — выделенные блоки - `mermaid` code blocks — диаграммы (graph LR, gantt) -- Cross-links в `toc.yaml` — sidebar navigation -- `varsPreset: external` — для shared includes (TBD when нужно) +- `toc.yaml` cross-links — sidebar navigation ## Roadmap +- [x] Базовая структура integration guide (11 страниц) - [ ] CI job `build-docs` в `.gitlab-ci.yml` -- [ ] GitLab Pages deployment -- [ ] Search через Diplodoc search plugin -- [ ] EN translation (i18n via `langs:` в `.yfm`) +- [ ] GitLab Pages / k8s ingress deployment +- [ ] Search plugin +- [ ] EN translation (через `langs:` в `.yfm`) - [ ] Includes для shared snippets (auth examples, error tables) -- [ ] Versioning — separate doc trees per Ordinis major version +- [ ] Versioning — separate doc trees per API major version diff --git a/docs/index.md b/docs/index.md index b0d3d88..8ee5088 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,8 @@ -# Ordinis НСИ ЦУОД ОДХ +# Ordinis НСИ ЦУОД ОДХ — Integrator Guide -Документация для разработчиков, интеграторов и операторов. +Документация для команд, интегрирующих свои сервисы с Ordinis НСИ ЦУОД. +Сюда не входят operator runbooks и архитектурные deep-dives — те материалы +во внутренней документации проекта. **Anchor v1 — production LIVE на cluster.142** (с 2026-05-07). Альтум consumers подключены, blue/green cutover завершён. @@ -26,14 +28,10 @@ - [Bundle cuod](integration/api/bundle-cuod.md) — список 40 справочников ЦУОД - [Best practices](integration/api/best-practices.md) — частые ошибки -- Я — оператор / on-call +- Я обрабатываю ошибки - - [Runbook indexed by symptom](ops/README.md) - - [SLO + alert thresholds](ops/slo.md) - - [Outbox DLQ](ops/outbox-dlq.md) - - [Database migrations](ops/db-migrations.md) - - [Vault unseal](ops/vault-unseal.md) - - [Kafka restart](ops/kafka-restart.md) + - [Ошибки и rate limits](integration/api/errors.md) — error codes, retry strategy + - [Лучшие практики](integration/api/best-practices.md) — robust integration {% endlist %} @@ -41,15 +39,14 @@ | Среда | URL | Auth | Назначение | |---|---|---|---| -| **prod** | `https://ordinis.k8s.264.nstart.cloud/` | JWT обязателен | Альтум real users | +| **prod** | `https://ordinis.k8s.264.nstart.cloud/` | JWT обязателен | Real users | | staging | `https://ordinis.k8s.265.nstart.cloud/` | optional (legacy mode) | Dev environment | | local | `http://localhost:8080` | off | docker-compose dev | ## Поддержка -- Issues / bug reports: `git.nstart.cloud:2-6/2-6-4/terravault/ordinis` -- Эскалация: `zimin.an@nstart.space` -- Запросы новых ролей / scope в Keycloak: тот же email. +- Запросы новых ролей / scope в Keycloak: `zimin.an@nstart.space` +- Bug reports / feature requests: через issue tracker (запросить URL у Ordinis team) ## Версионирование @@ -59,4 +56,4 @@ API стабилизирован под v1 — breaking changes только ч Bundle справочников ЦУОД versioned отдельно (`cuod-bundle:1.2.1`). Изменение schemas — minor bump, добавление словарей — minor bump, удаление — major bump -(никогда без уведомления consumers через `nsi.dictionary.changed` event). +(никогда без уведомления consumers через `record.closed` events). diff --git a/docs/integration/api/auth.md b/docs/integration/api/auth.md index 5e932c4..8c85189 100644 --- a/docs/integration/api/auth.md +++ b/docs/integration/api/auth.md @@ -69,7 +69,7 @@ Ordinis использует трёхуровневый доступ к слов Альтернативный синтаксис ролей (для compat с другими сервисами в realm): суффикс `-PUBLIC|INTERNAL|RESTRICTED` (например `altum-PUBLIC`, -`geoportal-INTERNAL`). Маппинг определяется в `ordinis-auth/.../ScopeContext.java`. +`geoportal-INTERNAL`). Маппинг встроен в auth layer Ordinis. {% note info %} diff --git a/docs/integration/api/bundle-cuod.md b/docs/integration/api/bundle-cuod.md index 8d98360..229d869 100644 --- a/docs/integration/api/bundle-cuod.md +++ b/docs/integration/api/bundle-cuod.md @@ -77,8 +77,9 @@ graph LR I --supportedLevelCodes--> PL ``` -(Это subset, для full графа см. отдельные schemas в коде: -`ordinis-cuod-bundle/src/main/resources/cuod/schemas/`.) +(Это subset. Полный граф можно получить через `GET /dictionaries` + +inspection schemas через `GET /dictionaries/{name}` — каждый возвращает +JSON Schema с x-references маркерами.) ## Типичные сценарии diff --git a/docs/integration/api/caching.md b/docs/integration/api/caching.md index 2a416e7..49b3ad7 100644 --- a/docs/integration/api/caching.md +++ b/docs/integration/api/caching.md @@ -24,7 +24,10 @@ useQuery({ }) ``` -### Server-side кэш (Caffeine + Spring) +### Server-side кэш (на consumer side) + +Любая стандартная in-memory cache library с TTL — Caffeine, Guava, +Redis с EXPIRE и т.д. Пример (Spring + Caffeine): ```java @Cacheable(value = "ordinis-records", key = "#dictionary + ':' + #lang") @@ -170,14 +173,13 @@ referencing records. Consumer должен слушать events и invalidate def on_event(event): cache.delete(f"ordinis:{event['dictionary']}:byKey:{event['businessKey']}") - # NEW v2: cascade source — заодно invalidate dependents + # NEW v2: cascade source — заодно invalidate источник if event.get('cascadeSource'): - # Источник тоже нужно invalidate (мы видим только закрытие dependent) src = event['cascadeSource'] cache.delete(f"ordinis:{src['dictionary']}:byKey:{src['businessKey']}") ``` -См. design doc `dict-relationships-v2` (внутренний artifact). +Spec появится перед deployment v2. ## Stale cache на 5xx / timeout diff --git a/docs/integration/api/errors.md b/docs/integration/api/errors.md index c0c7b0d..68616f6 100644 --- a/docs/integration/api/errors.md +++ b/docs/integration/api/errors.md @@ -97,9 +97,9 @@ Ordinis следует стандартам REST с расширениями: | Code | Описание | |---|---| | `internal_unexpected` | Server bug — escalate с traceId | -| `db_unavailable` | Postgres недоступен | -| `vault_unavailable` | Vault sealed либо unreachable | -| `kafka_publish_failed` | Outbox не смог publish (но запись в БД OK — eventually consistent) | +| `db_unavailable` | БД недоступна — retry с long backoff | +| `dependent_unavailable` | Зависимый сервис (config, secrets) недоступен — retry | +| `event_publish_failed` | Запись в БД ОК, но события не отправлены — eventually consistent (events доедут позже) | ## Rate limits @@ -114,9 +114,8 @@ limits через resource constraints: | Concurrent JWT verifications | bounded heap (~5k/sec практический максимум) | При превышении (типично DDoS-like patterns): -- HTTP `429 Too Many Requests` после ingress rate limit (ingress nginx default). +- HTTP `429 Too Many Requests` после ingress rate limit. - HTTP `503 Service Unavailable` при exhausting connection pool. -- `5xx error rate spike` → alert `OrdinisReadApiErrorBudgetBurnFast`. ### Recommended client-side limits @@ -155,13 +154,14 @@ def retry_with_backoff( ## Tracing Каждый response имеет `X-Trace-Id` header и `traceId` field в error body. -Ordinis эмитит OpenTelemetry traces в Tempo (`tempo.monitoring:4317`). При эскалации Ordinis team: 1. Скопируй `traceId` из response либо логов. 2. Создай issue с воспроизведением. -3. Ordinis team query'ит Tempo: `traceId="abc123-..."`. -4. Trace покажет full path: ingress → read-api → postgres → response. +3. Ordinis team поднимет trace по этому ID — full path запроса виден. + +Без `traceId` investigation занимает существенно больше времени — +обязательно сохраняй его при ошибках. ## Дальше diff --git a/docs/integration/api/events.md b/docs/integration/api/events.md index f5f0eef..11ac5f3 100644 --- a/docs/integration/api/events.md +++ b/docs/integration/api/events.md @@ -12,53 +12,27 @@ Ordinis публикует все изменения справочников в | `ordinis.cuod.events.internal` | INTERNAL | INTERNAL словари — нужна role `ordinis-internal`+. | | `ordinis.cuod.events.restricted` | RESTRICTED | RESTRICTED — role `ordinis-restricted`. | -Topology: 1 broker (single-node на cluster.142), 3+ partitions per topic -(historical setup имел 12 partitions, при cluster recreate 2026-05-07 -spec задал 3; existing topics annotated `strimzi.io/managed=false`, -broker partitions сохранены), `cleanup.policy: delete`, -`retention.ms: 604800000` (7 дней). +Параметры топиков (для consumer config): + +- `cleanup.policy: delete` +- `retention.ms: 604800000` (7 дней) +- Multi-partition (точное число — спросить Ordinis team при registration, + partition count может меняться по operational reasons). ## Подключение -### Адреса bootstrap +### Bootstrap address + creds -| Среда | Bootstrap servers | Auth | -|---|---|---| -| prod | `ordinis-kafka-kafka-bootstrap.ordinis-prod:9092` (TLS+SASL: `:9093`) | SCRAM-SHA-512 | -| staging | `ordinis-kafka-kafka-bootstrap.ordinis-staging:9092` | SCRAM-SHA-512 | +Запросить у Ordinis team: +- Bootstrap servers (внутренний DNS на per-environment basis) +- SASL credentials (SCRAM-SHA-512 username + password) +- Topic ACL (Read + Describe на нужные топики) +- Consumer group prefix (для group ACL) -### Получение SASL креденшелов +Auth: **SASL/SCRAM-SHA-512**. TLS опциональный (отдельный listener). -Запросить у Ordinis team создание Kafka user (Strimzi `KafkaUser` CR): - -```yaml -apiVersion: kafka.strimzi.io/v1beta2 -kind: KafkaUser -metadata: - name: altum-consumer - namespace: ordinis-prod - labels: - strimzi.io/cluster: ordinis-kafka -spec: - authentication: - type: scram-sha-512 - authorization: - type: simple - acls: - - resource: - type: topic - name: ordinis.cuod.events.public - patternType: literal - operations: [Read, Describe] - - resource: - type: group - name: altum-consumer- - patternType: prefix - operations: [Read] -``` - -Strimzi user-operator создаст Secret `altum-consumer` с полями `username`, -`password`. Используй их в Kafka client config. +Креденшелы выдаются per-consumer service. Не share между сервисами — +ACL и audit за каждым. ## Schema события @@ -143,18 +117,17 @@ each businessKey изолирован. ## At-least-once семантика -Outbox pattern гарантирует **at-least-once** delivery: - -1. Writer пишет запись в БД + outbox table в одной транзакции. -2. OutboxPoller (scheduled @1s) читает unpublished rows, отправляет в Kafka. -3. После Kafka ACK — отмечает row как published (`published_at` set). +Ordinis использует **outbox pattern** — изменение БД и публикация в Kafka +атомарны через transactional outbox. Гарантия: **at-least-once**. Что это значит для consumer'а: -- Можешь получить **дубликат** события если poller crashed после ACK но до - `published_at` write. Idempotency у consumer'а — must. -- При network partition между Kafka и poller — сообщения накапливаются в - outbox (alert `OrdinisOutboxLagHigh` срабатывает > 100 events). +- Можешь получить **дубликат** события (rare, при partial failures на + стороне Ordinis). Idempotency у consumer'а — must. +- При недоступности Kafka события накапливаются в Ordinis side и + доставляются после восстановления — **events не теряются**. +- Order — гарантирован per partition key (`dictionary:businessKey`), + не глобально. ## Idempotency @@ -167,13 +140,12 @@ if not seen: redis.setex(f"ordinis:event:...", 86400, "1") ``` -## DLQ (Dead-letter queue) +## Persistent failures (Ordinis side) -Если событие не публикуется > 1000 retry попыток (network issues, -Kafka unavailable) — переходит в DLQ table в Ordinis. Не теряется, но -требует ручного разбора. - -Симптом: alert `OrdinisOutboxDLQNonEmpty` → check `/api/v1/admin/audit?action=outbox.dlq`. +Если событие не публикуется > 1000 retry попыток (multi-day Kafka outage), +оно складывается в DLQ внутри Ordinis. Не теряется, но требует ручного +разбора Ordinis team. Consumer не видит таких событий до их +re-publishing — но это очень редкий сценарий. ## Топик для discovery diff --git a/docs/integration/api/webhooks.md b/docs/integration/api/webhooks.md index 6116059..336a650 100644 --- a/docs/integration/api/webhooks.md +++ b/docs/integration/api/webhooks.md @@ -50,8 +50,7 @@ Webhook URLs валидируются перед отправкой: - **Запрещено:** non-HTTPS scheme. Только `https://`. - **Запрещено:** URL > 2048 chars. -Метрика `nsi_webhook_ssrf_rejected_total` increments при попытках. См. -prometheus rule `OrdinisWebhookSsrfRejected`. +При нарушении registration возвращает 422 + список причин. ## Payload @@ -155,10 +154,11 @@ Consumer должен ответить `2xx` в течение 10 секунд: | 4 | через 10 мин | | 5 | через 1ч | | 6-10 | каждые 6ч | -| 1000+ | DLQ → ручной разбор | +| После всех попыток | Ordinis team помечает delivery как failed для ручного разбора | -После 1000 неудач delivery попадает в DLQ (`/api/v1/admin/webhooks/deliveries/dlq`). -Alert: `OrdinisWebhookDLQNonEmpty`. +Если consumer недоступен > 1000 retry попыток — Ordinis team будет +расследовать. Долгосрочное unavailability стоит coordinate (planned +maintenance window) чтобы не накапливать orphan deliveries. ## Test ping @@ -196,19 +196,8 @@ Response: сложить в очередь. 3. **HMAC verify первым** — до парсинга JSON. 4. **Metrics** — track received / processed / errors для своего side. - Сравнивай с Ordinis metrics при расследовании. -5. **Whitelist Ordinis IP** на firewall если строгие правила. IP cluster.142 - = `192.168.100.142`, но в production будет ingress IP. Спросить у Ordinis - team при registration. - -## Alerting (Ordinis side) - -| Alert | Trigger | -|---|---| -| `OrdinisWebhookHighFailureRate` | failure rate > 50% за 5 мин | -| `OrdinisWebhookDLQNonEmpty` | events в DLQ > 0 | -| `OrdinisWebhookDispatcherIdle` | 0 deliveries при > 0 active subscriptions | -| `OrdinisWebhookSsrfRejected` | SSRF guard сработал > 0 раз за 15 мин | +5. **Whitelist Ordinis IP** на firewall если строгие правила. Точный + адрес запросить у Ordinis team при registration. ## Дальше diff --git a/docs/integration/api/x-references.md b/docs/integration/api/x-references.md index c354929..f31dcf3 100644 --- a/docs/integration/api/x-references.md +++ b/docs/integration/api/x-references.md @@ -67,7 +67,7 @@ PUBLIC scope, ReferenceValidator возвращает тот же error (`x_references_target_not_found`) что и на real not-found. Ordinis **не leak'ает** existence of out-of-scope dictionary через differential errors. -## Orphan reference scanner +## Orphan references Возможна ситуация когда referenced record **закрывается**, а referencing record остаётся: @@ -78,19 +78,20 @@ record остаётся: 3. spacecraft.RESURS-P-3 теперь имеет dangling FK ``` -Periodic scanner (`OrphanReferenceScanner`, runs каждые 5 мин) находит -такие orphan'ы и emit'ит metric: +Ordinis периодически детектирует такие orphan'ы серверной стороной — данные +**не корректируются автоматически** (может быть intentional state до +update referencing записей), но Ordinis team получает уведомление и +может coordinate fix с data stewards. -``` -nsi_orphan_references_total{source_dict="spacecraft",target_dict="satellite_type"} 1 -``` - -Alert `OrdinisOrphanReferences` срабатывает при > 0 за 10 мин. +Consumer-side defensiveness: +- При получении event `record.closed` для записи на которую ты ссылаешься — + invalidate свой cache + либо fetch свежую версию referencing + записи (если она тоже обновилась), либо handle dangling FK явно. ## Cascade rules — roadmap (v2) Сегодня закрытие источника НЕ closes referencing automatically. Это -дolжно явно делаться через bulk close либо individual updates. +дoлжно явно делаться через bulk close либо individual updates. В v2 планируется schema extension `x-references-on-close`: @@ -106,21 +107,22 @@ Alert `OrdinisOrphanReferences` срабатывает при > 0 за 10 мин Опции: - **block** — закрытие источника невозможно пока есть active referencing (default). -- **warn** — закрытие проходит, в response warning + metric `nsi_cascade_warnings_total`. -- **cascade** — все referencing закрываются в той же транзакции. +- **warn** — закрытие проходит, в response warning. +- **cascade** — все referencing закрываются в той же транзакции, отдельные + events emit'ятся per record. -См. design doc `dict-relationships-v2` (внутренний artifact в -`~/.gstack/projects/claude/`) для полного scope. +Конкретный timeline и финальная семантика будут опубликованы перед +deployment v2 — следи за анонсами от Ordinis team. ## Lineage discovery — roadmap (v2) -Сегодня нет endpoint для "что depends on X". Admin должен вручную проходить -schemas чтобы найти ссылки. В v2 планируется: +Сегодня нет endpoint для "что depends on X". В v2 планируется: - `GET /api/v1/dictionaries/{name}/dependents` — словари которые ссылаются. - `GET /api/v1/records/{dict}/{key}/dependents` — конкретные записи. -См. design выше для подробностей. +Это позволит consumer'ам и UI узнавать связи без чтения всех schemas +руками. Spec будет опубликован перед deployment v2. ## Best practices diff --git a/docs/toc.yaml b/docs/toc.yaml index 15cd549..5481108 100644 --- a/docs/toc.yaml +++ b/docs/toc.yaml @@ -1,4 +1,4 @@ -title: Ordinis НСИ ЦУОД ОДХ +title: Ordinis НСИ ЦУОД ОДХ — Integrator Guide href: index.md items: @@ -34,23 +34,3 @@ items: items: - name: Альтум — заказ съёмки href: integration/altum-imaging-order.md - - - name: Operations - items: - - name: Runbook (индекс) - href: ops/README.md - - name: SLO - href: ops/slo.md - - name: Outbox DLQ - href: ops/outbox-dlq.md - - name: Database migrations - href: ops/db-migrations.md - - name: Vault unseal - href: ops/vault-unseal.md - - name: Kafka restart - href: ops/kafka-restart.md - - - name: Архитектура (legacy) - items: - - name: API integration (старая страница, см. /integration/api/) - href: tech/api-integration.md