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