docs: split integrator guide (Diplodoc) vs internal docs + add build-docs CI
Чёткое разделение audiences:
- docs/ — consumer-facing only, Diplodoc-built. Public contract для
интеграторов (Альтум, BI, third-party).
- docs-internal/ — operator runbooks, status snapshots, legacy tech pages.
НЕ в Diplodoc build, НЕ публикуются.
Изменения:
- Move docs/{ops,status,tech} → docs-internal/{ops,status,tech}
- docs/toc.yaml: убраны секции Operations, Архитектура (legacy), Status
- docs/index.md: убран tab "оператор / on-call", focus на integrators
- docs/README.md: rewritten — scope только integrator guide, editorial
guidelines (no leak internal architecture, stable API contract)
- .yfm: убран явный ignore status/* (теперь не нужно — папка переехала)
- docs-internal/README.md: index для внутренней документации с rationale
разделения
Scrubbing implementation details из Diplodoc страниц:
- events.md: убраны Strimzi/cluster.142 references, OutboxPoller детали,
internal alert names (OrdinisOutboxLagHigh, OrdinisOutboxDLQNonEmpty),
metric names (nsi_outbox_*). Заменены на consumer-observable contract.
- webhooks.md: убраны metric names (nsi_webhook_ssrf_rejected_total),
alert table (OrdinisWebhookHighFailureRate и т.д.). Retry policy
переписана на user-side perspective.
- errors.md: убран alert name OrdinisReadApiErrorBudgetBurnFast, Tempo
endpoint URL, internal connection details.
- x-references.md: OrphanReferenceScanner → general "Ordinis периодически
детектирует". Убраны metric/alert names. Cascade roadmap без link на
internal design doc.
- auth.md: убран file path ScopeContext.java.
- bundle-cuod.md: убран Maven module path.
- caching.md: cascade design link заменён на text reference.
CI:
- .gitlab-ci.yml: новый job build-docs (stage: build) — Node 20 alpine,
pnpm install --frozen-lockfile + pnpm build, artifact docs/_dist на
неделю. Triggers: auto на docs/**, либо manual web. Готов к follow-up
pages-deploy job когда host решён.
- Новый pattern .docs-changes для rules.
Build verified clean: 13 HTML pages, 6.9M, no broken links/refs.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -8,5 +8,4 @@ strict: false
|
||||
ignore:
|
||||
- node_modules
|
||||
- _dist
|
||||
- status/*-state.md
|
||||
addSystemMeta: true
|
||||
|
||||
+54
-74
@@ -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
|
||||
|
||||
+11
-14
@@ -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).
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
|
||||
@@ -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 маркерами.)
|
||||
|
||||
## Типичные сценарии
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 занимает существенно больше времени —
|
||||
обязательно сохраняй его при ошибках.
|
||||
|
||||
## Дальше
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
## Дальше
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+1
-21
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user