SWE / Infrastructure · System Design

十亿日事件Webhook:持久受理、重试隔离和幂等

eventId唯一对应callback,但一次投递可能发生多次网络尝试。

题目重点:eventId唯一对应callback,但一次投递可能发生多次网络尝试。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 每个eventId→一个callback,触发有独立event_instance_id;每天10亿事件。训练至少一次投递,收件方用instance ID幂等,不承诺网络层恰好一次。
  • 受理ACK表示事件已持久记录,不表示收件方已执行业务。注册callback有tenant归属、签名密钥和版本,回调修改是否影响积压事件先定义,训练固定触发时endpoint版本。

解题路线

  1. 注册表以eventId索引,事件受理写Event(instance,eventId,payload_ref,endpoint_version)+outbox;队列按tenant/endpoint分区,delivery attempt带attempt_id、超时和下次重试时间。
  2. worker发HTTP,响应2xx记delivered;timeout、连接断开和可重试状态进入指数退避+jitter。4xx是否永久失败按明确合同而非一概重试;超过预算进DLQ供重放。receiver处理成功却回包丢失仍会重试,同instance ID支持去重。
  3. 10亿/86400约11,574事件/秒平均,10倍峰约115,740/秒。100ms响应、平均发送率11574/s时在途约1157个,超时3s会显著增加占用;慢endpoint需独立并发/速率预算,不绑住全部worker。
  4. payload HMAC签名覆盖原始bytes、timestamp和event_instance_id,密钥支持版本轮换。callback注册/发送校验目标地址,限制本机/私网/metadata并在DNS解析及重定向后重新检查,避免URL服务访问内部资源。
  5. attempt只用lease做排他不足以防重复发送:worker超时失联后旧worker可能还在发。外部HTTP没有fencing接口,所以仍靠receiver幂等;内部状态CAS attempt generation防旧worker覆盖新状态。

手动走一遍

回调执行业务后2xx丢失,第二次同instance到达:receiver查幂等记录返回成功,业务不再执行。某endpoint持续500,退避和租户配额隔离它,其他租户仍正常。每天1KiB payload约1TB/day未含副本/attempt;保留7天需按约7TB原始payload预算。

规模与取舍

事件/attempt持久写按数量线性;每事件一次查注册和入队平均O(1),总HTTP尝试受重试倍率放大。队列积压、老化时间、endpoint失败率比单总QPS更能解释容量。

易错点与追问

容易失分的边界

  • 把eventId当每次触发幂等ID会把合法多次触发吞掉。
  • 队列exactly once消费不能保证HTTP外部副作用exactly once。
  • 无限立即重试制造雪崩且占满线程。

继续深挖

  • 要求同endpoint严格顺序时,失败事件是否阻塞后续?
  • 注册URL更新与重放旧事件,endpoint版本和签名密钥如何保留?
✓已复制微信号!