docs: define analytics job execution limits
This commit is contained in:
@@ -148,13 +148,13 @@ This contract applies only to the one-time challenge-data bootstrap and explicit
|
||||
- ZarinPal is the authoritative writer of future production payment sessions/attempts in the normalized PostgreSQL schema.
|
||||
- This project owns the normalized schema contract, migrations, read models, and analytics; it does not recreate ZarinPal's payment-write workflow.
|
||||
- Payment facts are append-only in the MVP. Existing session/attempt updates and hard deletion are out of scope; a future mutation requirement needs a new invalidation contract.
|
||||
- The first relevant dashboard access starts all approved analytics-section jobs. A bounded on-demand worker pool calculates independent sections in parallel rather than running a scheduled refresh.
|
||||
- Each completed section is serialized through its approved typed contract and cached in Redis under a key containing merchant id, merchant source revision, relevant cohort revision, metric-definition version, and section id. Cache entries use a configurable high TTL.
|
||||
- The authenticated SSE stream emits section progress and makes each successful section available immediately; the dashboard does not wait for every job to finish.
|
||||
- An idempotent authenticated `POST` starts or returns the current analytics run for the merchant/revision. It enqueues the four MVP sections—`overview`, `payment_performance`, `fees`, and `peer_comparison`—for bounded parallel calculation by a separate Go worker service.
|
||||
- Each completed section is serialized through its approved typed contract and cached in Redis under a key containing merchant id, merchant source revision, relevant cohort revision, metric-definition version, and section id. Cache entries have a 30-day TTL.
|
||||
- A separate authenticated `GET` SSE stream for that run emits section progress and makes each successful section available immediately; the dashboard does not wait for every job to finish.
|
||||
- Redis-backed idempotency/single-flight coordination prevents equivalent concurrent requests from duplicating the same section calculation. Parallelism is bounded globally and per merchant.
|
||||
- Every inserted related payment fact atomically advances the affected PostgreSQL merchant analytics revision. It also advances the relevant cohort/category revision when peer metrics can change. Database-managed revision changes cover direct ZarinPal inserts that bypass Go request handlers.
|
||||
- Revision changes invalidate logically by producing new cache keys; old values are not trusted and expire through TTL without requiring a Redis key scan.
|
||||
- Failed sections emit a typed failure event and remain retryable without marking the section ready. Other successful sections remain available.
|
||||
- Each section attempt has a 60-second timeout. Transient failures receive at most two automatic retries (three total attempts) with exponential backoff and jitter; deterministic validation/authorization failures are not retried. Exhausted sections emit a typed failure event without marking the section ready. Other successful sections remain available.
|
||||
- Redis is cache/coordination only. PostgreSQL normalized facts and revision rows remain authoritative; loss or flush of Redis causes safe lazy recomputation.
|
||||
- Redis and the on-demand job worker are private services, never exposed through Nginx. Cache values exclude payer-card keys, credentials, raw payment rows, and cross-tenant details.
|
||||
|
||||
@@ -179,11 +179,12 @@ This contract applies only to the one-time challenge-data bootstrap and explicit
|
||||
- Daily, weekly, Jalali-month, and Persian-season analytics reconcile to deterministic source queries for additive metrics.
|
||||
- Rates are recomputed from their numerator and denominator over the requested period rather than averaged from daily percentages.
|
||||
- Non-additive metrics such as unique payer-token counts are never produced by summing daily distinct counts.
|
||||
- First access starts every core analytics section; each section is delivered through authenticated SSE as soon as it is ready.
|
||||
- A second equivalent request reads current high-TTL Redis entries without repeating source-fact aggregation.
|
||||
- First access idempotently creates/returns one run, starts all four core analytics sections in bounded parallel, and delivers each through authenticated SSE as soon as it is ready.
|
||||
- A second equivalent request reads current 30-day Redis entries without repeating source-fact aggregation.
|
||||
- Inserting a related payment fact advances only the affected merchant revision and any affected cohort revision; the next relevant request recomputes once and publishes atomically.
|
||||
- Concurrent equivalent requests do not duplicate section jobs; configured global/per-merchant limits prevent unbounded query fan-out.
|
||||
- One failed section does not hide ready sections and does not create a ready cache entry.
|
||||
- A transient section failure performs no more than two retries with exponential backoff/jitter; each attempt ends within 60 seconds, while non-retryable failures fail immediately.
|
||||
- Redis loss produces a cold-cache calculation rather than loss of source data.
|
||||
- Reporting-date rows covering the dataset range reconcile deterministically between Gregorian and Jalali day, week, month, and season attributes.
|
||||
- Every reporting week starts Saturday at `00:00:00` and ends Friday at `23:59:59.999…` in `Asia/Tehran`; API ranges use an equivalent half-open `[start, next_start)` interval.
|
||||
@@ -204,7 +205,7 @@ Resolved: peer benchmarks exclude the active merchant and require at least 10 ot
|
||||
|
||||
Resolved: use normalized PostgreSQL source-of-truth entities plus a reporting-date dimension, then compute versioned analytics sections lazily into Redis. Daily, weekly, Jalali-month, and Persian-season results use explicit date boundaries. Keep fact tables unpartitioned initially and introduce partitioning only from measured need. See ADR 0007.
|
||||
|
||||
Resolved: ZarinPal writes future append-only production rows directly to normalized tables. First dashboard access starts all section jobs in bounded parallel, Redis caches each typed result with a high TTL, and authenticated SSE reveals sections as they become ready. PostgreSQL merchant/cohort revisions advanced on insert provide logical invalidation. See ADR 0011.
|
||||
Resolved: ZarinPal writes future append-only production rows directly to normalized tables. An idempotent `POST` starts four parallel sections in a separate Go worker; Redis caches each typed result for 30 days; an authenticated `GET` SSE stream reveals sections as ready. Each section has a 60-second attempt timeout and at most two transient retries using exponential backoff/jitter. PostgreSQL merchant/cohort revisions provide logical invalidation. See ADR 0011.
|
||||
|
||||
Resolved: holiday and named-event reporting are removed from scope. MVP reporting periods are limited to daily, weekly, Jalali-month, and Persian-season views. See ADR 0010.
|
||||
|
||||
@@ -212,4 +213,4 @@ Resolved: reporting weeks follow the Iranian week: Saturday through Friday in `A
|
||||
|
||||
Resolved: only source session status `Verified` is a definitely successful payment. Keep `Paid` separate until an authoritative ZarinPal definition changes the metric catalogue. See `METRIC-CATALOGUE.md`.
|
||||
|
||||
No broad architecture approval gates remain for this capability. Physical columns, indexes, constraints, Redis TTL/limits, SSE delivery details, analytics-section contracts, and migration details still require acceptance in the implementation-ready specs.
|
||||
No broad architecture approval gates remain for this capability. Physical columns, indexes, exact SSE schemas/reconnect retention, worker concurrency, retry classification, analytics-section response contracts, and migration details still require acceptance in the implementation-ready specs.
|
||||
|
||||
@@ -60,7 +60,7 @@ Both fact tables distinguish `bootstrap` from `zarinpal_live` origin. Bootstrap
|
||||
|
||||
No calculated dashboard result is stored in PostgreSQL. Analytics queries and Redis value contracts are finalized by the metric catalogue and API spec.
|
||||
|
||||
Inserts atomically increment affected revision rows through schema-owned database logic so direct upstream writes cannot bypass invalidation. Redis keys include merchant/cohort revisions, metric-definition version, and analytics-section id. Old revision keys expire under a configurable high TTL; invalidation does not scan or delete arbitrary keys. Redis values follow typed, versioned response schemas and contain aggregates only.
|
||||
Inserts atomically increment affected revision rows through schema-owned database logic so direct upstream writes cannot bypass invalidation. Redis keys include merchant/cohort revisions, metric-definition version, and analytics-section id. Old revision keys expire after 30 days; invalidation does not scan or delete arbitrary keys. Redis values follow typed, versioned response schemas and contain aggregates only.
|
||||
|
||||
## Initial index policy
|
||||
|
||||
|
||||
@@ -9,11 +9,13 @@ The hackathon dataset is static after its one-time bootstrap. Continuously refre
|
||||
|
||||
## Decision
|
||||
|
||||
On the first relevant dashboard access, enqueue every approved analytics section for the authenticated merchant. A bounded on-demand worker pool calculates independent sections in parallel. This worker is event-driven, not scheduled.
|
||||
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.
|
||||
|
||||
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 configurable high TTL. Redis-backed idempotency/single-flight coordination prevents duplicate equivalent jobs. Old-revision keys expire naturally and are never treated as current.
|
||||
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.
|
||||
|
||||
Stream progress and section availability through an authenticated, merchant-scoped SSE connection. Emit 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -25,5 +27,5 @@ 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.
|
||||
- Bounded global/per-merchant concurrency, job timeouts, retry limits, SSE connection limits, and cache TTL still require explicit values.
|
||||
- Global/per-merchant concurrency and SSE connection/replay-retention limits still require explicit values.
|
||||
- Trigger/revision behavior, tenant isolation, job deduplication, partial failures, SSE reconnect, and cache data minimization require integration/security tests.
|
||||
|
||||
Reference in New Issue
Block a user