Реализованы сервисы ВМ2 - проверка сообщений и синхронизация с Б24 (деплой еще без перевода в боевой режим)
This commit is contained in:
+31
-26
@@ -1,15 +1,15 @@
|
||||
# module-04. Проектная спецификация Redis
|
||||
|
||||
> Статус: целевая спецификация Redis в едином Docker Compose MVP.
|
||||
> Статус: целевая спецификация Redis для двух Compose-контуров; legacy DB2 stub описан только до cutover.
|
||||
> Источники: [`README.md`](README.md), [`arch-00-glossary.md`](arch-00-glossary.md), [`arch-01-system-architecture.md`](arch-01-system-architecture.md), [`arch-02-api-contracts.md`](arch-02-api-contracts.md), [`arch-03-docker-compose-blueprint.md`](arch-03-docker-compose-blueprint.md), [`arch-04-settings-and-content.md`](arch-04-settings-and-content.md), [`arch-05-agent-development-process.md`](arch-05-agent-development-process.md), [`module-01-api-backend.md`](module-01-api-backend.md).
|
||||
|
||||
## 1. Назначение и инварианты
|
||||
|
||||
Один Redis-контейнер предоставляет быстрые ephemeral функции трём логическим DB:
|
||||
Redis разделён по deployment/security boundary:
|
||||
|
||||
- DB0 — `api-backend`: idempotency fast layer и API rate limits;
|
||||
- DB1 — realtime и coordination;
|
||||
- DB2 — `message-safety` stub tasks/cache.
|
||||
- Redis ВМ1: DB0 (`api-backend` idempotency/rate) и DB1 (realtime/coordination);
|
||||
- Redis Safety ВМ2: отдельный instance для hot cache, rate limiting и optional worker wake-up;
|
||||
- legacy DB2 ВМ1 существует только для test stub v1 до cutover и после него удаляется.
|
||||
|
||||
Redis не является бизнес-очередью, source of truth сообщений, sync tasks, audit, профилей или delivery checkpoint. Надёжные состояния остаются в managed PostgreSQL/S3. Потеря Redis может ухудшить сервис, но не должна создавать потерю подтверждённых сообщений либо дубль side effect: durable idempotency/outbox/checkpoint api-backend описаны в module-01.
|
||||
|
||||
@@ -17,9 +17,9 @@ OTP counters api-backend в Redis не хранит; они принадлежа
|
||||
|
||||
## 2. Версия и topology
|
||||
|
||||
Redis 7.x, image закреплён по digest. Одна primary instance на VM без replica/Sentinel в MVP. Клиенты используют connection pool, bounded timeouts и не выполняют опасные команды.
|
||||
Redis 7.x, image закреплён по digest. На каждой VM одна нужная primary instance без replica/Sentinel в MVP. Клиенты используют connection pool, bounded timeouts и не выполняют опасные команды.
|
||||
|
||||
Logical DB — изоляция имён, не security boundary и не независимый memory quota. При росте или разных eviction/SLA DB2 и DB0 выносятся в отдельные instances.
|
||||
Logical DB — изоляция имён, не security boundary. Safety уже вынесен в отдельный instance ВМ2; DB0/DB1 остаются на ВМ1.
|
||||
|
||||
## 3. Общие правила ключей
|
||||
|
||||
@@ -81,18 +81,20 @@ Redis Streams не используются как бизнес queue. Если
|
||||
|
||||
Acquire: `SET key owner NX PX ttl`; extend/release — Lua compare owner. Worker обязан опираться также на PostgreSQL row lease/`FOR UPDATE SKIP LOCKED`; Redis lock — оптимизация, не единственная защита. Fencing token рекомендуется для внешнего side effect, а уникальные DB constraints/idempotency остаются финальной защитой.
|
||||
|
||||
## 8. DB2: Message Safety stub
|
||||
## 8. Redis Safety ВМ2
|
||||
|
||||
| Key | Тип/value | TTL |
|
||||
|---|---|---|
|
||||
| `han:safety:task:{task_id}` | HASH/JSON v1: created, polls, optional seed/context | `MESSAGE_SAFETY_TASK_TTL_SEC` |
|
||||
| `han:safety:tasklock:{task_id}` | owner token | 5–30s |
|
||||
| `han:safety:rl:service:{caller}:{window}` | counter | window+jitter |
|
||||
| `han:safety:verdict:{content_hash}:{rules_version}` | optional cache | bounded technical TTL |
|
||||
| `han:safety:text:{analysis_hash}:{rules_version}` | hot text-rules result, monitor rule ids без raw text | active config, seed ≤48h |
|
||||
| `han:safety:verdict:{content_hash}:{config_version}:{detector_bundle}` | hot file verdict cache | active config, seed ≤30d |
|
||||
| `han:safety:link:{url_hash}:{rules_version}:{config_version}` | stable local policy cache | active config, seed ≤48h |
|
||||
| `han:safety:dns:{host_hash}:{rrtype}` | DNS answer; classification повторяется под текущей policy | actual TTL, active hard max seed 900s |
|
||||
| `han:safety:wakeup` | Pub/Sub notification only | no storage |
|
||||
|
||||
Для требуемой заглушки task — ephemeral contract state. Истечение task возвращает безопасный `404 task_not_found/expired` по internal error semantics. В production safety authoritative audit/cache может находиться в PostgreSQL `message_safety`; Redis DB2 не заменяет его.
|
||||
PostgreSQL `message_safety.safety_tasks` — единственный queue/lease source (`FOR UPDATE SKIP LOCKED`, fencing generation). Redis не хранит authoritative task state, locks или leases. Cache loss/restart безопасно восстанавливается из PostgreSQL; Redis outage не выключает core Safety.
|
||||
|
||||
Random verdict каждого GET по заданию независим; Redis хранит существование/TTL и счётчик polls для observability, но не предопределяет финал. В deterministic tests seed/RNG injected на уровне сервиса.
|
||||
Legacy v1 stub может временно использовать DB2 ВМ1 для random task state. Этот namespace не используется production v2 и удаляется вместе со stub.
|
||||
|
||||
## 9. Serialization и limits
|
||||
|
||||
@@ -112,8 +114,10 @@ Random verdict каждого GET по заданию независим; Redis
|
||||
| rate limit | window + 10–30% deterministic jitter |
|
||||
| realtime connection | 90s; set membership 120s |
|
||||
| coordination lock | 30s |
|
||||
| safety task | default 15m, обязательно > API poll max 300s + recovery margin |
|
||||
| safety cache | default 5–60m по rules version |
|
||||
| safety file hot cache | ≤30d; authoritative row/version в PostgreSQL |
|
||||
| safety text-rules cache | 48h; invalidation by rules version |
|
||||
| safety stable link policy cache | 48h |
|
||||
| safety DNS cache | actual DNS TTL, hard max 900s |
|
||||
|
||||
Новый key без TTL запрещён contract test, кроме Pub/Sub channel (не key) и ops metadata с явным обоснованием.
|
||||
|
||||
@@ -155,18 +159,18 @@ Eviction MVP: `volatile-lru`/`volatile-ttl`, так как все application ke
|
||||
DB0 rate = peak identities × routes × active windows × bytes/key
|
||||
DB0 idem = mutating requests/24h × avg sanitized record
|
||||
DB1 = peak connections × connection metadata + Pub/Sub buffers
|
||||
DB2 = safety tasks within TTL × avg task metadata
|
||||
total × 1.5 allocator/fragmentation × 1.3 growth reserve
|
||||
Redis Safety = hot verdict/link/DNS entries + rate windows + Pub/Sub buffers
|
||||
each instance total × 1.5 allocator/fragmentation × 1.3 growth reserve
|
||||
```
|
||||
|
||||
Pub/Sub output buffers и slow consumers имеют hard/soft limits. Load test фиксирует peak RPS, WS connections, idempotency response size и AOF rewrite headroom.
|
||||
|
||||
## 15. Auth, ACL и network boundary
|
||||
|
||||
Redis не публикует `6379` на host, подключён только к Docker `backend`. `protected-mode yes`, bind container interface, default user отключён. ACL users:
|
||||
Оба Redis не публикуют `6379` на host и подключены только к local Docker `backend` своей VM. `protected-mode yes`, default user отключён. ACL users:
|
||||
|
||||
- `api_backend`: DB0/DB1 key prefixes, нужные command categories;
|
||||
- `message_safety`: только DB2 prefixes;
|
||||
- `message_safety`: только Redis Safety prefixes;
|
||||
- `ops_health`: `PING`, ограниченный `INFO`;
|
||||
|
||||
Важно: Redis ACL не ограничивает logical DB напрямую надёжно; key-prefix patterns и разные credentials обязательны. `SELECT` запрещается, клиент URL сразу задаёт DB, но ACL prefix остаётся основной защитой.
|
||||
@@ -195,10 +199,10 @@ URL:
|
||||
```text
|
||||
REDIS_URL=redis://api_backend:<secret>@redis:6379/0
|
||||
REDIS_REALTIME_URL=redis://api_backend:<secret>@redis:6379/1
|
||||
MESSAGE_SAFETY_REDIS_URL=redis://message_safety:<secret>@redis:6379/2
|
||||
MESSAGE_SAFETY_REDIS_URL=redis://message_safety:<secret>@redis:6379/0
|
||||
```
|
||||
|
||||
Добавление credential env требует обновления arch-04 `.env.example`; до этого имена credential variables — TBD, URL может содержать injected secret.
|
||||
Первые два URL существуют только на ВМ1. На ВМ2 `MESSAGE_SAFETY_REDIS_URL=redis://message_safety:<secret>@redis:6379/0`; credential доставляется secret file и не входит в общий `.env`.
|
||||
|
||||
## 17. Health и degraded behavior
|
||||
|
||||
@@ -211,7 +215,8 @@ MESSAGE_SAFETY_REDIS_URL=redis://message_safety:<secret>@redis:6379/2
|
||||
- profile/history GET могут работать под edge limits;
|
||||
- public GET использует bounded local conservative limiter/cache;
|
||||
- realtime cross-instance publish/coordination деградирует; REST/polling остаётся source of truth;
|
||||
- safety stub для digit task не может гарантировать GET task state — check возвращает `503`, а существующие task GET — `503`; синхронные text allow/deny могут работать только если policy явно разрешает Redis-independent path;
|
||||
- production Safety продолжает task claim/poll через PostgreSQL; hot cache/rate/wakeup деградируют и прогреваются после восстановления Redis;
|
||||
- legacy stub v1 может стать недоступным при потере своей DB2 до cutover;
|
||||
- internal inbox не теряется из-за Redis, так как durable receipt в PostgreSQL.
|
||||
|
||||
При latency выше threshold clients используют short timeout/circuit, не создают бесконечные retry storms. Reconnect — exponential backoff+jitter.
|
||||
@@ -224,7 +229,7 @@ Redis backup не используется для бизнес restore. Runbook:
|
||||
2. при целостном AOF/RDB восстановить на отдельном instance и проверить;
|
||||
3. иначе поднять пустой Redis;
|
||||
4. api-backend прогревает idempotency по durable records, realtime восстанавливается reconnect/polling;
|
||||
5. незавершённые safety tasks обрабатываются по service semantics/expire; api-backend durable `safety_tasks` сообщает dependency error/recovery.
|
||||
5. production safety tasks продолжают обрабатываться из PostgreSQL; Redis Safety прогревается лениво.
|
||||
|
||||
Не копировать Redis dump в небезопасное место: keys содержат UUID и hashed identifiers.
|
||||
|
||||
@@ -240,7 +245,7 @@ Redis backup не используется для бизнес restore. Runbook:
|
||||
- script errors/NOSCRIPT/slowlog;
|
||||
- rate limit decisions, idempotency hit/conflict/fallback;
|
||||
- Pub/Sub subscribers/output buffer/slow disconnect;
|
||||
- safety task create/get/expire.
|
||||
- Safety hot-cache hit/miss, DNS TTL cap и wakeup subscribers.
|
||||
|
||||
Alerts: unavailable, p99 latency, >80/90% memory, any sustained evictions, AOF error, no recent persistence, client buffer pressure, unexpected keys without TTL.
|
||||
|
||||
@@ -252,7 +257,7 @@ Alerts: unavailable, p99 latency, >80/90% memory, any sustained evictions, AOF e
|
||||
- idempotency same/different fingerprint, lock ownership, expiry, Redis loss + PostgreSQL fallback;
|
||||
- realtime heartbeat cleanup, duplicate disconnect, Pub/Sub loss + REST recovery;
|
||||
- locks expiry/late owner/fencing;
|
||||
- safety task TTL, concurrent polls и missing task;
|
||||
- Safety cache loss/rebuild, DNS TTL cap и доказательство отсутствия task/lease state в Redis;
|
||||
- `NOSCRIPT` reload;
|
||||
- all application keys имеют TTL;
|
||||
- max value/invalid serialization;
|
||||
@@ -279,6 +284,6 @@ Alerts: unavailable, p99 latency, >80/90% memory, any sustained evictions, AOF e
|
||||
|
||||
**Решения:** один instance/три DB MVP; AOF everysec + RDB; Pub/Sub best effort; PostgreSQL durable fallback; prefix ACL; все application keys с TTL.
|
||||
|
||||
**Допущения:** одна VM и одна replica API на старте; Redis loss допустим без потери business truth.
|
||||
**Допущения:** по одной Redis instance на ВМ1/ВМ2 и одна Safety API replica на старте; Redis loss допустим без потери business truth.
|
||||
|
||||
**TBD:** R1 точный maxmemory после load profile; R2 eviction policy после измерений; R3 credential env names в arch-04; R4 Safety task TTL/recovery margin; R5 TLS при изменении network topology; R6 момент разделения DB на instances; R7 RPO/RTO ops target.
|
||||
|
||||
Reference in New Issue
Block a user