A blocking `ALTER TABLE` locks 20M rows. Do expand/contract: add new columns nullable, have the code write both old and new (dual-write), backfill history in throttled batches + verify, switch reads to the new schema, then drop the old. Each step is independently deployable and reversible.
#data#postgresql#ci-cd
Storing scores as plain strings and sorting in the app is O(N log N) per read over millions of keys. Use a Sorted Set (ZSET): `ZADD` updates in O(log N), `ZREVRANGE 0 99` returns the top 100 already sorted, `ZREVRANK` gives a rank in O(log N). It stays ordered as you write, so reads are near-constant.
#data#redis#performance
Try the cheapest version first: add a read replica and move reporting there — that usually ends the locking. Adopt full CQRS only when query shapes diverge too much for one schema; sync the models event-driven with the outbox pattern, don't dual-write, and accept eventual consistency.
#data#architecture#elasticsearch
Don't reach for MongoDB just because attributes vary — that's the NoSQL trap. Keep the relational core (products, prices, orders) in PostgreSQL, put the variable attributes in a GIN-indexed `JSONB` column. Your reporting need alone argues against splitting into Mongo.
#data#architecture#postgresql
For money, keep the invariant in the DB. Use an atomic conditional `UPDATE ... SET balance = balance - 40 WHERE id = ? AND balance >= 40`, or `SELECT ... FOR UPDATE`. Reach for Redlock only for resources that aren't a single DB row; don't trust a distributed lock to protect a balance the DB can enforce.
#data#queue#redis
Bound the retries: set `tries`/`maxExceptions` + `backoff`, and on final failure route to `failed_jobs` instead of re-queueing — that's Laravel's built-in DLQ. Alert on it, replay with `queue:retry`. Never retry unbounded.
#data#queue#laravel
Liveness answers 'is the process wedged?' — keep it cheap and dependency-free (don't check the DB here). Readiness answers 'can I serve right now?' — check critical dependencies but cache the result for a few seconds. Liveness fail → restart, readiness fail → stop traffic. Don't let probes DDoS your DB.
#resilience#infrastructure#kubernetes
Use monthly RANGE declarative partitioning on `created_at`. For zero downtime, stand up the new partitioned parent, backfill history in batches while the app keeps writing, then swap names in a single transaction. UNIQUE/PK and FKs must include the partition key.
#database#postgresql#scaling
A deadlock is a lock-ordering cycle. Always acquire locks in the same global order (e.g. ascending PK), keep transactions short and narrow, and use targeted `FOR UPDATE` on the fewest rows. Expect them anyway: the DB aborts a victim, so make the operation idempotent and retry with backoff.
#resilience#database#postgresql
Put a middleware at the outermost edge of the chain that wraps each request in `defer recover()`, logs the stack + request id, returns a generic 500 and emits a metric. But recover only catches the same goroutine: if you spawn `go func()` inside a handler, that goroutine needs its own recover.
#resilience#go