Memorizza i task in una coda a bucket temporali / a priorità indicizzata per orario di scadenza, esegui il polling solo dei task in scadenza adesso e passali a worker idempotenti con consegna at-least-once. Non fare mai lo scan di 10M di righe a ogni tick: indicizza per orario di attivazione così che ogni polling tocchi solo ciò che è scaduto.
Architettura
API ─▶ Task store (DB/Redis ZSET, score = due_ts) ── 10M tasks, sharded
│
Poller (leader per partition) ── ZPOPMIN due<=now ──▶ Ready queue (Kafka)
│ leader election (etcd/ZK) │
▼ ▼
other pollers idle/standby Worker pool (idempotent)
└─ ack ✔ / retry ✖ ─▶ DLQ
