Files
han-app/VM1_app/documentation/module-04-redis-vm1.md
T

165 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# module-04-vm1. Redis ВМ1 HAN Chat
> Статус: целевая спецификация Redis на ВМ1.
> Канонический контракт (ключи, TTL, Lua, AOF, ACL, eviction) — [`arch-09-redis.md`](../../architectory/arch-09-redis.md).
> Обязательный host/container hardening baseline — [`arch-06-service-hosting-security.md`](../../architectory/arch-06-service-hosting-security.md).
> Redis Safety ВМ2 — [`module-04-redis-vm2.md`](../../VM2_services/documentation/module-04-redis-vm2.md). Hostname Redis ВМ2 не используется.
## 1. Назначение и границы
Redis ВМ1 обслуживает `api-backend`: DB0 (rate/idempotency) и DB1 (realtime/coordination). OTP counters здесь нет. Message Safety cache/rate/wakeup — на ВМ2.
Legacy DB2 на ВМ1 существует только для test stub v1 до cutover и после него удаляется. Production v2 не хранит Safety task state в этом Redis.
Durable idempotency/outbox/checkpoint — [`module-01-api-backend.md`](module-01-api-backend.md).
## 2. URL и ACL
```text
REDIS_URL=redis://api_backend:<secret>@redis:6379/0
REDIS_REALTIME_URL=redis://api_backend:<secret>@redis:6379/1
```
Только на ВМ1. `api_backend` ACL: prefixes `han:api:*`, `han:rt:*`, `han:coord:*`, нужные command categories. `SELECT` запрещён. `MESSAGE_SAFETY_REDIS_URL` на ВМ1 после cutover отсутствует.
## 3. DB0: API rate limiting
| Key | Тип/value | TTL |
|---|---|---|
| `han:api:rl:user:{user_id}:{route_hash}:{window}` | ZSET timestamps либо counter | window + jitter |
| `han:api:rl:ip:{ip_hmac}:{route_hash}:{window}` | ZSET/counter | window + jitter |
| `han:api:rl:dialog:{dialog_id}:message:{window}` | ZSET/counter | window + jitter |
| `han:api:rl:service:{service}:{route_hash}:{window}` | counter/token bucket | window + jitter |
| `han:api:jwks:negative:{kid_hash}` | marker отрицательного lookup | 30s |
Алгоритм — atomic Lua/function: удалить старые entries, посчитать, добавить текущий request, установить expiry, вернуть `allowed`, `remaining`, `retry_after_ms`, `reset_at`. Для fixed window `INCR` и первый `EXPIRE` выполняются в одном script, чтобы не оставить бессрочный key.
Clock — Redis `TIME`. Route labels — bounded allow-list/hash, исключающий cardinality attack.
## 4. DB0: idempotency
| Key | Значение | TTL |
|---|---|---|
| `han:api:idem:{scope}:{user_id}:{key_hmac}` | HASH/MessagePack: state, fingerprint, status, sanitized response, resource id, version | 24h |
| `han:api:idemlock:{scope}:{user_id}:{key_hmac}` | random owner token | 30s + heartbeat |
State transitions `absent → in_progress → completed`; fingerprint mismatch возвращает conflict. Создание/сравнение/lock выполняется Lua. Unlock/extend разрешены только если owner token совпадает (`compare-and-delete/expire` script).
Response не содержит tokens, cookies, presigned URL или PII. Transient 503/504 не фиксируется как окончательный completed. PostgreSQL `idempotency_records` — durable fallback; Redis — ускоритель. При cache loss API читает durable row и прогревает key.
## 5. DB1: realtime
| Key/channel | Формат | TTL |
|---|---|---|
| `han:rt:conn:{connection_id}` | HASH: user_id, instance, last_seen, subscriptions_count | 90s |
| `han:rt:user:{user_id}:connections` | ZSET connection_id → heartbeat | 120s |
| `han:rt:dialog:{dialog_id}` | Pub/Sub channel | нет хранения |
| `han:rt:user:{user_id}` | Pub/Sub channel | нет хранения |
Heartbeat атомарно обновляет connection и membership; cleanup удаляет stale ZSET entries bounded batches. Pub/Sub — at-most-once notification. Payload содержит только event id/type/entity UUID и DTO, допустимый realtime контрактом; DB остаётся source of truth. После reconnect frontend всегда делает REST reconciliation.
Redis Streams не используются как бизнес queue. Если позже понадобится durable realtime replay, сначала меняется архитектура и выбирается PostgreSQL outbox/event broker.
## 6. DB1: coordination locks
| Key | TTL |
|---|---|
| `han:coord:lock:safety-recovery:{task_id}` | 30s |
| `han:coord:lock:delivery:{message_id}` | 30s |
| `han:coord:lock:settings-refresh:{instance}` | 30s |
| `han:settings:snapshot:{version}` | 5m |
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 остаются финальной защитой.
Lock `safety-recovery` на ВМ1 относится к recovery caller/`api-backend`, не к Redis Safety ВМ2.
## 7. Legacy DB2 stub
До cutover test stub v1 может временно использовать DB2 ВМ1 для random task state. Этот namespace не используется production v2 и удаляется вместе со stub.
Если stub ещё жив: Safety task TTL в DB2 должен превышать poll/recovery budget; Lua `safety task get+increment poll` допустим только здесь. После cutover keys, ACL и DB2 удаляются.
## 8. Lua scripts ВМ1
Обязательные:
- rate-limit evaluate;
- idempotency reserve/complete/conflict;
- lock release/extend;
- realtime heartbeat/cleanup membership.
Правила хранения/тестов — arch-09 §6.
## 9. Sizing ВМ1
```text
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
total × 1.5 allocator/fragmentation × 1.3 growth reserve
```
При memory pressure eviction idempotency не создаёт дубль благодаря PostgreSQL fallback. Если instances разделят (R6), DB0 idempotency может получить `noeviction`.
## 10. Degraded behavior ВМ1
При Redis недоступен:
- message send, attachment init и download URL `api-backend` fail-closed `503`, если нельзя безопасно применить лимит/idempotency;
- completed idempotency восстанавливается из PostgreSQL;
- profile/history GET могут работать под edge limits;
- public GET использует bounded local conservative limiter/cache;
- realtime cross-instance publish/coordination деградирует; REST/polling остаётся source of truth;
- internal inbox не теряется из-за Redis, так как durable receipt в PostgreSQL;
- legacy stub v1 может стать недоступным при потере своей DB2 до cutover.
Production Safety на ВМ2 при этом продолжает PostgreSQL claim; это не runbook Redis ВМ1.
Restore: `api-backend` прогревает idempotency по durable records, realtime восстанавливается reconnect/polling.
## 11. Metrics ВМ1
Общие — arch-09 §14 и [`module-09-observability-vm1.md`](module-09-observability-vm1.md). Дополнительно: rate limit decisions, idempotency hit/conflict/fallback, Pub/Sub subscribers/output buffer.
## 12. Тесты ВМ1
- ACL: `api_backend` видит только свои prefix/commands; Safety prefixes отсутствуют;
- порт 6379 недоступен с host/public network;
- rate Lua concurrency и exact Retry-After;
- 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;
- `NOSCRIPT` reload;
- all application keys имеют TTL;
- max value/invalid serialization;
- restart with AOF/RDB, corrupted AOF rehearsal, empty restore;
- memory pressure/eviction и no duplicate business side effect;
- network partition, latency, reconnect backoff;
- logs/metrics не содержат secret/value/PII;
- после cutover DB2/stub keys отсутствуют.
## 13. Definition of Done ВМ1
Дополнительно к arch-09 §15:
- DB0/DB1 roles и prefixes реализованы;
- idempotency 24h и durable fallback доказаны;
- realtime loss восстанавливается REST;
- health/degraded policies §10 реализованы в `api-backend`;
- dashboards/alerts Redis ВМ1 готовы;
- до cutover: Safety DB2 task TTL превышает poll/recovery budget, если stub ещё включён;
- после cutover: DB2 и stub namespace удалены.
## 14. TBD ВМ1
- R1/R2: maxmemory и eviction после load profile ВМ1.
- R6: момент разделения DB0/DB1 на instances.
- R3: имена credential env — arch-04.
## 15. Ссылки
- Контракт: [`arch-09-redis.md`](../../architectory/arch-09-redis.md).
- ВМ2: [`module-04-redis-vm2.md`](../../VM2_services/documentation/module-04-redis-vm2.md).
- Указатель: [`module-04-redis.md`](module-04-redis.md).
- API: [`module-01-api-backend.md`](module-01-api-backend.md).