题目重点:从 durable task schema 到到期分发、lease、重试和热点分片,回答队列内部怎样运作。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练目标是一次性延时任务,允许任务至少一次投递;若面试要求 cron、严格顺序或恰好一次外部副作用,须增加契约。
- 任务执行者可能崩溃或网络断联;外部效果是否幂等取决于业务,调度器不能凭空保证。
解题路线
- 定义 durable task:taskId、tenant、payloadRef、runAt、priority、status、attempt、maxAttempts、leaseOwner、leaseUntil、idempotencyKey、createdAt 与规则版本。大 payload 存对象存储,只在队列传引用。
- 持久层以 `(shard,runAt,taskId)` 建到期索引。调度器扫描到期任务并用条件更新从 scheduled→leased,记录 lease;工作节点只执行自己仍持有的版本。
- 队列可以是 DB 到期扫描+Ready Queue,或用 broker 承接就绪任务;DB 是真相源,broker 消息重复与丢失要靠状态版本和重放修复。
- 执行成功以 compare-and-set ack 为 completed;失败按指数退避重新 scheduled,到达重试上限进 dead-letter。Worker 定期 heartbeat 延长 lease,超时任务由恢复器再次入队。
- 按 taskId/tenant 分片扫描与 worker 池扩容,限制单租户并发和配额以防热点;观测调度延迟 `startedAt−runAt`、积压、重试率、lease 过期率、外部副作用重复率。
手动走一遍
任务 T 在 10:00 到期,worker A 取得 30 秒 lease,调用外部 API 成功但 ack 前崩溃。10:00:30 后 T 会被重新分配给 B;如果外部 API 没有 idempotency key,效果可能重复。调度器最多能保证重投与状态恢复,不能从本地 completed 状态推断外部调用恰好一次。
规模与取舍
到期索引查询约 O(log Q+k) 取 k 条到期任务,按任务 ID 的状态更新依存储实现通常 O(log Q) 或平均 O(1);持久状态 O(Q)。吞吐随分片/worker 增长,但同租户顺序、全局优先级和热点限制会削弱线性扩容。
易错点与追问
容易失分的边界
- 只画一个内存 priority queue,进程重启后任务消失。
- worker 拿到任务就删除,执行崩溃时无法重试。
- 声称 ack 和外部副作用构成跨系统原子事务。
- 多调度器同时扫描,没有 CAS/lease 而重复发放。
- 只加 worker 不分片到期索引,数据库扫描成为瓶颈。
继续深挖
- 如何支持每周本地时间的 cron 并处理夏令时?
- 若必须同一用户任务顺序执行,分片键和并发度怎么改?
- 任务排到 10⁹ 条时怎样避免全表轮询?
- 外部服务无幂等接口时如何降低重复副作用风险?
