Armazene as tarefas em uma fila de prioridade / dividida em buckets de tempo indexada pelo horário de vencimento, faça poll apenas das tarefas vencidas agora e as entregue a workers idempotentes com entrega at-least-once. Nunca escaneie 10M de linhas a cada tick — indexe pelo horário de disparo para que cada poll toque apenas o que está vencido.
Arquitetura
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
