题目重点:Uber Senior Onsite 系统设计题强调 payout correctness、accuracy 与 no double spending。用余额预留、不可变账本、外部支付幂等键、未知结果恢复及对账串成闭环。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 系统把已确认的司机应付收入按某次 payout request 支付到已验证收款账户;金额用最小货币单位整数表示,币种、手续费、税费由上游明确计算并冻结。
- 本文假设一笔应付收入只能被一个未撤销的 payout reservation 消耗;题目未明确具体司机收入构成或监管规则,不把示例当 Uber 真实支付流程。
- 外部支付提供方的请求可能超时、重复 webhook 或先返回 UNKNOWN;系统不能仅凭客户端超时就重新生成一个新的业务支付 ID。
解题路线
- 在内部双分录账本里把每笔可支付收入记到司机 payable 余额;生成 payout 时用稳定业务键做唯一约束,在事务中原子地从 available 转到 reserved,确保同一余额不会被两个并发请求同时领取。
- Outbox 记录待调用外部支付指令;worker 以 payoutId 派生稳定的 provider idempotency key。外部调用成功、失败或结果未知分别进入明确状态,UNKNOWN 先查询提供方/等 webhook,不重新生成另一笔支付。
- 验签并去重 webhook,按 provider transaction ID 关联 payout。最终成功将 reserved 转 settled;确定失败才释放回 available。每日对账内部账本、外部支付流水和银行/提供方结算,差异进人工队列并可追溯。
手动走一遍
司机有 100 单位可支付余额,两次并发 payout 请求都要付 100。唯一 requestId + 余额预留事务只允许一笔占用这 100;外部调用超时进入 UNKNOWN,重试仍使用同一 payoutId/幂等键。后到的成功 webhook 只落账一次,不能再生成第二笔 100。
规模与取舍
若每笔 payout 有 P 条收入明细,冻结与账本写入至少 O(P)(或先按已结算余额聚合后 O(1) 扣减),外部 API 调用与对账吞吐由业务量决定。账本/状态记录按交易数线性增长;查询可建 driverId+status 与 payoutId 索引。
易错点与追问
容易失分的边界
- 只靠‘请求已经发出’布尔值不能保证钱未重复付:外部超时后仍可能成功,必须查提供方最终状态并使用稳定幂等键。
- 以浮点数累计司机收入会引入金额舍入误差;币种、费率版本、调整项与结算快照要可审计。
- 扣 available 与创建 payout 分开提交会在宕机/并发时丢款或超付;使用同一原子事务与 outbox 恢复。
继续深挖
- 若支持跨币种,先固定汇率来源、报价时间、舍入规则和目标币种金额,并把换汇视作可审计的独立账本事件。
- 监控重复支付拒绝率、UNKNOWN 持续时长、对账差异金额、应付到到账延迟;对账发现差异时先冻结相关收入/人工调查,不自动反向修改历史账本。
