L5 SDE · System Design / Bar Raiser

Amazon System Design:Kindle 离线阅读进度如何跨设备同步

设计 Kindle Reader 的 API 和数据结构,支持离线阅读及同一账户跨设备同步阅读进度,处理并发覆盖、冲突解决、幂等与一致性。

题目重点:设计 Kindle Reader 的 API 和数据结构,支持离线阅读及同一账户跨设备同步阅读进度,处理并发覆盖、冲突解决、幂等与一致性。

题意与假设

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

  • 本文聚焦个人阅读进度,购书授权、内容分发和 DRM 仅划定接口边界;不把未公开的实际 Kindle实现当答案。
  • 阅读位置用 book_id+edition_id+稳定内容定位符;重排版页码不是跨设备通用位置。
  • 区分 current cursor(继续阅读位置)、furthest read(累计最远位置)和书签;用户允许向前翻阅或向后重读。
  • 训练合同离线操作落设备持久队列,联网至少一次重试,服务端幂等去重;同时离线写入可能产生需要显式解决的并发版本。

解题路线

  1. 接口包括 GET /books/{edition}/progress 与 POST progress operations。操作携 op_id、device_id、device_sequence、base_revision、location、intent;客户端时间仅作展示线索,不作为可信全局排序。ACK返回服务端revision及冲突状态,重试同op_id返回同处理结果。
  2. schema把 user/book/edition 的汇总与操作日志分开。汇总保存 current、furthest、revision;日志保存操作来源、父版本与幂等结果。同步按账户书本键分区,用事务或条件写比较 base_revision,并把操作落盘与汇总变更原子提交。
  3. 若 base_revision 匹配,提交新 cursor并提升 revision;furthest 可按稳定内容顺序单调合并。若设备重连带旧 base,先识别其是否已处理或能沿已知因果链续接,真正并发才进入冲突,而非把所有旧revision当错误。
  4. 冲突政策以体验决定:显示‘手机读到位置B,阅读器读到位置A,继续哪一个’,或在明确产品合同下自动选最近服务端接受的一项并保留可恢复分支。不能把 max(location)当 current:用户回读第20页,另一设备第80页旧值会把它推回80页。
  5. 离线读内容走本地缓存与已授权资源;进度写持久排队,合并同一连续阅读会话的更新降低频率,但‘跳转/重读/书签’应保留意图。网络失败不阻塞阅读;同步状态和冲突提示可见,避免看起来已保存却丢失。
  6. 区域故障时权威写分区迁移需防双主,通过租约/epoch拒绝旧主;可缓存读取,但带revision和stale说明。如果要求全球强一致,解释跨区写延迟与分区不可写代价,再核对阅读进度是否值得这个合同。

手动走一遍

训练例服务器revision=10、位置40。设备A离线继续到80,设备B离线回读到20,两者都基于10。A提交产生11;B提交发现并发,不用设备时间或最大页码直接覆盖,而保留位置20分支并提供恢复选择。重发A的同op_id返回11,不再产生12。furthest可以合并到80,但current由明确冲突政策决定。

规模与取舍

进度操作量按活跃阅读会话与上报频率估,不按每翻一页必写云端。训练假设百万同时阅读用户每30秒上报约3.3万写/秒;日志保留期、合并与快照决定存储。单书进度条件更新 O(1),多设备分支量按设备/未同步操作数增长。

易错点与追问

容易失分的边界

  • 以屏幕页码作为不同字体设备统一位置。
  • 最大位置会阻止重读,latest客户端时间受时钟漂移影响。
  • 重复请求只去重写入却返回不同结果。
  • 通知同步成功前没有落设备队列,崩溃丢进度。
  • 强一致只喊口号,不解释网络分区的不可写代价。

继续深挖

  • 书籍更新版内容移动了,旧位置怎样映射?
  • 用户关闭某设备同步后,旧离线队列重连怎么处理?
  • 冲突全部自动处理时,你选择哪种可解释政策?
  • 日志压缩后如何仍判断重试和并发来源?
✓已复制微信号!