docs: pin analytics queue safety limits

This commit is contained in:
2026-08-20 16:41:07 +03:30
parent ae551c02c1
commit 96a27e1b98
2 changed files with 30 additions and 3 deletions

View File

@@ -11,12 +11,26 @@ The hackathon dataset is static after its one-time bootstrap. Continuously refre
On first dashboard access, an authenticated idempotent `POST` creates or returns the current analytics run for that merchant/source/cohort/metric-definition revision. It enqueues four section ids—`overview`, `payment_performance`, `fees`, and `peer_comparison`—for bounded parallel calculation by a separate Go worker service. This worker is event-driven, not scheduled.
Use `github.com/hibiken/asynq` pinned to `v0.26.0` as the Redis-backed queue/worker library. Asynq is explicitly pre-`v1.0.0`, and its public API may change without a major-version increment. Do not float the dependency or upgrade it without reviewing release notes, API compatibility, Redis compatibility, retry/deduplication behavior, and integration tests.
Cache each completed typed section in Redis using a key derived from the merchant id, merchant source revision, relevant cohort revision, metric-definition version, and section id. Use a 30-day TTL. Redis-backed idempotency/single-flight coordination prevents duplicate equivalent jobs. Old-revision keys expire naturally and are never treated as current.
Expose a separate authenticated `GET` SSE endpoint for the run. Stream stable event ids and typed events for section started, section ready, section failed, overall complete, and heartbeat. Support safe reconnect without exposing another merchant’s jobs or values. One section failure does not suppress sections that succeeded.
Give each section attempt a 60-second timeout. Retry only classified transient failures at most twice, for a maximum of three attempts. Use exponential backoff with jitter (initial nominal delay 1 second, then 2 seconds; jitter prevents synchronized retry bursts). Do not retry authorization, validation, unsupported-metric, or other deterministic failures.
Default worker concurrency to 16 jobs globally and 4 per merchant. Enforce the global value in the worker and the merchant value in queue middleware/coordination; both are validated environment settings.
Apply authenticated rate limits independently from job deduplication:
- analytics start: 10 requests/minute in independent per-user and per-merchant buckets, burst 3;
- manual retry: 3 requests/10 minutes in independent per-user and per-merchant buckets, burst 1;
- SSE: 2 concurrent connections per user and 4 per merchant.
Return `429 Too Many Requests` with `Retry-After` before enqueuing work. A manual retry targets only one exhausted failed section for the current revisions and is idempotent; it cannot restart ready siblings.
Send SSE heartbeat events every 15 seconds. Retain run events for one hour for authorized `Last-Event-ID` replay. After replay retention expires, return/provide the current run snapshot and continue from new events rather than replaying another merchant’s or an unbounded event history.
Schema-owned PostgreSQL logic atomically increments the affected merchant revision whenever a related payment fact is inserted. Inserts affecting peer benchmarks also increment the corresponding category/cohort revision. This covers direct ZarinPal writes. Payment facts are append-only for the MVP; invalidation for update/delete remains out of scope.
Redis is a private disposable cache and coordination system, not a source of truth. PostgreSQL remains authoritative. Cached values contain approved aggregates only and exclude raw rows, payer-card keys, credentials, session tokens, and cross-tenant details.
@@ -27,5 +41,11 @@ Redis is a private disposable cache and coordination system, not a source of tru
- Redis loss or key expiry causes safe lazy recomputation; Redis is excluded from backup/restore requirements.
- No scheduled refresh or time-based freshness claim is required.
- The deployment includes private Redis and on-demand analytics-worker services.
- Global/per-merchant concurrency and SSE connection/replay-retention limits still require explicit values.
- Rate limits and concurrency values remain environment-configurable but changes require security/load evidence and a reviewed spec/config change.
- The pre-1.0 Asynq pin creates upgrade risk; CI uses the committed Go module/checksum files and upgrades are deliberate.
- Trigger/revision behavior, tenant isolation, job deduplication, partial failures, SSE reconnect, and cache data minimization require integration/security tests.
## References
- Asynq repository and stability notice: <https://github.com/hibiken/asynq>
- Pinned release: <https://github.com/hibiken/asynq/releases/tag/v0.26.0>