题目重点:依据 Netflix VO 给出的 300M monthly subscription billing 规模,从账期事件到支付意图、不可变账本、失败重试与财务对账推导可扩容架构。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版账户每月一个账期,可能有不同地区、计划、税与优惠;金额以最小货币单位整数与币种表示。题目未明确定价或付款方式。
- 账期在账户所在时区/约定日生成,含月底日期回退规则;单个账期只能有一个最终成功扣费,但可能有多次付款尝试。
- 外部支付网关可能超时、重复回调、延迟成功和乱序;系统采用至少一次消息与幂等状态机,不能假定一次 RPC 完成可靠计费。
解题路线
- 先定边界:订阅状态、账期、invoice、payment_intent、payment_attempt、ledger_entry 分开建模。账期生成时冻结当期 plan/discount/tax/currency 的价格快照,避免后续改价使历史账单漂移。
- 按 `(account_id,billing_period)` 建唯一键生成 invoice;消息重复只读回已存在账单。付款意图有稳定幂等键,向支付网关发送与重试时复用,回调按 gateway event id 去重;状态机只允许合法转移,ACK 丢失时先查付款结果再考虑重发。
- 不可变账本记录应收、成功收款、退款与冲正,账单视图由事件投影。每天与网关 settlement、银行到账和内部账本做对账,发现孤儿成功、重复收款、金额差异与跨币种错误,人工/自动修复需审计轨迹。
- 300M/月平均约 116 个账户/秒,但集中账单日可高很多倍。按账户分片账期任务,队列削峰、按区域/支付供应商限速;付款敏感状态放事务库,账单查询可读副本/缓存,网关故障时延迟重试与退避,不把用户访问权无条件立即撤销。
手动走一遍
训练例 U 的 10 月账期生成 invoice I1,金额 1299 美分;付款网关已扣款但响应超时。worker 重试前以相同 payment_intent 幂等键查询或重发,网关返回同一结果,账本只登记一次成功。若回调又重复到达,也按 event id 去重。
规模与取舍
账户数 A、账期数 B,账期生成和付款处理至少 O(B) 事件量,分片与队列决定峰值吞吐。300M/月只是平均容量起点,集中账单日、失败重试、回调和对账扫描会放大读写;持久账本存储随历史账期增长。强一致扣款边界与高吞吐查询链路应分开扩容。
易错点与追问
容易失分的边界
- 只用 cron 逐用户直接扣卡,无 invoice/attempt/账本区分。
- 支付超时就换一个新幂等键重试,可能重复扣款。
- 计划改价后重算历史账单,失去当期金额快照。
- 只看平均 116/s,不估月初/月底热点与供应商限流。
- 用支付网关成功响应代替日终财务对账。
- 把训练账户时区、税务和宽限期当成面经给定政策。
继续深挖
- 月底 31 日订阅如何映射到二月账期?
- 支付失败多次后的宽限期、降级与客户通知怎样设计?
- 退款、部分退款与拒付如何写入账本而不改历史记录?
- 跨国多币种汇率和税率版本如何保证可追溯?
