Newer
Older
gn-synapse / docs / 04-database.md

04 — Схема БД (#34), v1

Реализация — app/models/ + миграции alembic/versions/ (7 таблиц; 0001 — исходная схема, 0002/0003 — description и полный Routing Engine). Постгрес в докере (postgres:17-alpine, том pgdata), Redis 7 (том redisdata).

Два контура данных: конфигурация (человек заводит через админку, меняется редко) и поток событий (пишется автоматически, растёт всегда).

Конфигурация

sources ──< api_keys
   │
   └──< notification_types          (уникальность: source_id + subject + action)
   │
routing_rules ──< routing_rule_actions ──> channel_targets
Таблица Что хранит Ключевые поля
sources Источники событий (регистрирует админ) name (slug, unique: monitoring), label, description («что это за сервис» для человека и ИИ-агента)
api_keys Ключи syn_… к источникам token_hash (sha256, unique), token_hint (последние 4 для UI), revoked_at (ротация: новый ключ, старая строка маркируется). Ключ — удостоверение; правила матчают source, не токен
notification_types Реестр типов из контракта тройка (source_id, subject, action) уникальна, payload_schema JSONB (опц., валидирует воркер)
channel_targets Цели каналов: куда доставлять channel ∈ telegram/email/s2s/internal_log (CHECK), config JSONB (chat_id, адрес, s2s-endpoint; s2s ещё token_ref — ссылка на секрет), description («что это за клиент-сервис»). Креды каналов (токен бота, SMTP, HMAC-секреты) — не здесь, а в .env
routing_rules Правила «условия → действия» conditions JSONB (source, subjects[], actions[], priority_min, payload — точный матч по top-level ключам), template (Jinja2 {{ payload.x }}), weight (порядок, важен в режиме first), throttle_seconds (шумодав: окно на (channel, target), critical проходит всегда), enabled
routing_rule_actions Действия правила rule_id, channel (CHECK), target_id → channel_targets (SET NULL при удалении цели), template — переопределение шаблона правила

Поток событий

events 1 ──< deliveries
Таблица Что хранит Ключевые поля
events Конверт события (контракт docs/05) UUID pk, source_id (RESTRICT — события переживают удаление источника), subject/action/priority (CHECK по шкале), payload JSONB, dedup_key (+индекс с created_at — окно дедупликации), expires_at (= created+ttl_seconds), scheduled_at (резерв), status ∈ queued/processing/done/failed (done — замаршрутизировано; живые статусы в deliveries)
deliveries Доставка в конкретную цель event_id (CASCADE), rule_id (SET NULL — правило удалим, историю оставим), channel, channel_target_id, status ∈ pending/delivered/failed/skipped (skipped — срезано шумодавом), attempts, last_error, next_retry_at (+индекс status,next_retry_at — скан ретраей beat'ом), rendered_message (аудит: что реально уйдёт в канал)

Что сознательно НЕ в таблицах

  • Пользователи — их делает gnexus-auth; Synapse пользователей не хранит.
  • Секреты каналов — .env/gnexus-creds, не БД (TELEGRAM_BOT_TOKEN, SMTP и т.д.).
  • tags события — убраны из v1 контракта (см. docs/05); вернутся, когда появится правило, требующее их.
  • История попыток отдельно от delivery — одна запись с attempts/next_retry_at; поэлементный лог попыток не нужен MVP.

Проверено

  • alembic upgrade head поднимает всё из пустой базы (docker: api применяет миграции на старте).
  • ORM roundtrip: Source → ApiKey → NotificationType → ChannelTarget → RoutingRule(+Action) → Event → Delivery — вставляется и читается; демо-данные удалены, схема оставлена пустой.