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

9.6 KiB
Raw Blame History

module-04-vm1. Redis ВМ1 HAN Chat

Статус: целевая спецификация Redis на ВМ1.
Канонический контракт (ключи, TTL, Lua, AOF, ACL, eviction) — arch-09-redis.md.
Обязательный host/container hardening baseline — arch-06-service-hosting-security.md.
Redis Safety ВМ2 — 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.

2. URL и ACL

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

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. Дополнительно: 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. Ссылки