系统设计 E01:协同编辑的收敛删除与光标
两个人离线在同一个字符前输入,重新联网后看到相同文本,只能证明副本收敛;它不能证明插入位置符合各自意图,也不能证明光标不会跳到另一段。多人编辑需要分别设计文本合并、删除语义与交互定位。 本篇讨论小型纯文本协同编辑,支持离线插入、删除和再次同步;不实现富文本、表格公式、权限撤销后的离线内容回收或生产级编辑器。关键不变量是同一有效操作集合产生相同可见文本、重复操作不重复插字、已删除字符不会因迟到插入消息复活。体验目标另外定义:本地键入立即回显,在线操作传播拟定 p99 小于 300 ms,离线恢复后明确显示同步状态。 从位置冲突理解 OT 与 CRDT 假设文档是 AB,两名作者都以整数偏移 1 插入 X、Y。一端先 X 后 Y,另一端先 Y 后 X,如果直接执行相同偏移,结果可能为 AYXB 与 AXYB。冲突不在网络丢消息,而在操作引用的“位置 1”依赖各端当时的文档版本。 操作转换 OT 可以根据已经应用的并发操作转换位置;常见集中式安排由服务器确定顺序,客户端对尚未确认的本地操作执行相应转换。正确性依赖转换函数、上下文和协议,不是简单把远端位置加一。CRDT 则把合并所需身...
系统设计 35:三场限时设计与约束改变复盘
短链、派单和票务都可以从单库开始,但十倍流量触发的修改不同。短链需要处理热读与撤销确认,派单需要拆开空间候选与司机归属,票务需要同时核算提交预算和外部扣款协议。复盘应留下旧方案在哪条约束下失效,以及新方案付出的代价。 这三场练习由 AI 执行者各在一个真实持续至少四十五分钟的窗口内完成。记录包含阶段稿、运行源码、正反例输出和约束注入时刻,原稿没有事后替换成最终答案。它们证明书面设计与代码核对过程;真人口述、实际支付渠道和生产吞吐尚未运行。 短链:热读不能绕开撤销 初始范围是创建、重定向、过期与撤销。创建响应丢失后,同一业务幂等键重试应返回同一个 code;同键换 target 要拒绝。撤销确认后开始的新读取不能再返回目标,已经在途的响应和复制出去的 URL 不在此保证内。 基准读取 2000 request/s、创建 20 request/s。按每条元数据 400 B、三十天保留、三份副本,正文为 20×86400×400×30×3=62.208 GB,索引和备份另算。读 p99 100 ms、创建 p99 300 ms 是教学目标,实验没有证明这些性能。 初稿采用单个 SQL...
系统设计 34:内容平台跨模块保证的四条路径
发布按钮返回成功、时间线出现一张卡片、搜索可以找到正文、好友收到通知,是四个不同的确认。把上传、时间线、搜索、消息和通知组合到同一张图上,并不会使这些确认自动同步。本篇用四条跨模块路径检查它们在哪里可以延迟,在哪里必须拒绝继续。 教学平台支持图片与短文本发布、关注时间线、公开内容搜索、私信和站内通知。排除广告竞价、推荐模型训练、视频转码和真实外部推送。核心不变量是未提交的上传不可见、已确认删除的内容不通过新读取泄露、客户端重连不会重复展示同一消息。搜索和通知允许延迟,但延迟不能变成权限旁路。 为每种状态确定权威来源 元数据采用 posts(id, owner, object_key, state, version, visibility),状态依次为 UPLOADING、LIVE、DELETED。对象上传与数据库无法仅凭两次成功响应成为原子事务,因此只有对象校验完成、元数据进入 LIVE 后才允许读者看到。未引用对象由扫描任务延迟回收,回收前检查上传会话和引用,避免删除仍在提交的对象。 发布接口 POST /posts 携带创建幂等键;上传会话另有 POST /uploads ...
系统设计 33:回填期间更新怎样安全迁移
迁移失败不一定发生在切流那一秒。旧库的一条 v1 记录被回填器读走,业务紧接着写入 v2,新库也已接收 v2;几秒后回填器把 v1 覆盖回去。两边都有记录、总行数相同、服务没有报错,用户却看到了旧值。 本篇把分享元数据从旧表迁到增加访问权限字段的新表。讨论在线回填、兼容读取与可回退范围,排除真实跨数据库复制的实现。本次本机实验使用 SQLite 3.51.3 执行版本条件 UPSERT;跨库原子性、CDC 完整性和线上性能不在实验结论内。 先确定迁移后必须相同的东西 业务不变量是同一分享的较旧版本不能覆盖较新版本,撤销墓碑不能被回填复活;同一个创建幂等键仍指向同一分享。迁移成功不是两个库行数接近,而是指定切换位点之前的业务状态可解释一致。读取目标拟定为 p99 200 ms、写入 p99 300 ms、回填每天不超过原存储可用 IO 的二成,这些是计划预算,不是本实验测出的能力。 旧模型为 shares(id, target, version, deleted),新模型增加 owner_id, visibility, schema_version。更新接口 PATCH /sha...
系统设计 32:用观测与成本证伪架构假设
架构图写着“p99 小于 200 ms”,监控却只收成功请求的延迟;十个请求里有一个超时,图表仍然可能全部变绿。可观测性首先是为设计结论提供反例渠道,其次才是把组件指标画成仪表盘。 本篇沿内容分享的发布、读取和异步通知路径定义测量契约。讨论对象是候选后端方案,不涉及监控产品选型或真实生产成本报价。关键不变量是撤销后不能返回内容;服务目标是读取三十天有效请求成功率 99.9%、服务端 p99 小于 200 ms、通知入队后 99% 在五秒内完成。目标和实验观察分别记录。 五类指标对应五种反驳 Google SRE 的监控章节提出延迟、流量、错误和饱和度四类信号,并区分成功与失败延迟。本题还需要业务正确性:返回了已撤销内容的请求可能是 HTTP 200,低延迟、高可用,却仍违反设计。 设计主张 测量位置与分母 能推翻它的观测 读取足够快 网关收到请求至写完响应,超时单列 p99 超目标或超时比例上升 通知不会持续积压 入队至最终处理,包含仍未完成者 最老任务年龄增长 故障不被重试放大 同一逻辑请求下游尝试总数 尝试数增长而成功数不增 撤销后不可见 撤销确认...
系统设计 31:多地域切换与数据恢复的不同边界
地域切换解决的是服务入口和写入权限的变化;数据恢复解决的是哪些已确认记录仍然存在。DNS 已切到另一座城市,既不能证明旧地域停止写入,也不能证明最后一笔订单没有丢失。多地域设计必须分别给出这三个答案。 本文把教学分享服务扩展到两个地域,采用每个账户只有一个写入归属地域的候选方案。它不描述任何公司的内部架构,也不实现跨地域共识协议。正常情况下归属地域提交元数据,另一地域异步接收日志;只有公开且允许陈旧的统计可以就近读取。 从业务损失反推复制确认 需求卡先区分内容和权限:普通草稿允许灾难后丢失最近五秒更新,撤销权限的成功确认不允许在恢复后被当作从未发生。前者可以接受有界的数据损失目标,后者需要更强的提交条件,或在无法确认撤销状态时拒绝访问。不能用“系统 RPO 五秒”抹平两个业务对象之间的差异。 RPO 是恢复点目标,用时间表达可接受的最近数据损失范围;RTO 是恢复时间目标,从业务中断开始,直到指定业务功能恢复。目标必须说明故障范围:本题只讨论单地域失效、备用地域及控制面仍可用;两个地域同时永久丢失、账号密钥泄露和逻辑误删不由地域复制自动处理。Google SRE 的数据完整性章...
系统设计 30:任务租约和过期持有者栅栏
一个日报任务在 10 秒到期,工作者 A 取走后停止响应;16 秒工作者 B 重领并完成。A 恰好又恢复,拿着旧结果写入数据库。如果系统只检查“任务 ID 相同”,最后保存的可能是 A 的过期数据。延时队列解决“什么时候可领”,租约和栅栏解决“谁的完成确认还有效”,两者不是同一个问题。 每个周期是独立的一次执行 教学任务 report 的周期实例键是 (task_id,slot),其中 slot 来自固定时区下的计划触发时间,不由工作者的当前时钟临时生成。同一键至多保留一个有效结果;过期工作者可以消耗计算资源,不能覆盖已被后继租约提交的结果。任务执行并不保证恰好一次;若副作用发生在邮件、账务等外部系统,仍需要那些系统支持幂等键或结果核对。误触发、超时和未执行都必须可查询。 教学 SLO:未来 30 天 99% 已到期实例在调度时间后 10 秒内被领取(只对调度器可用且下游有容量的实例,测量计划时间与数据库租约时间);99.9% 到期实例在 5 分钟内达到成功或明确的死信状态。禁止用“已经领到”替代“业务动作完成”。跨时区夏令时规则、需要人工签核的任务及任意第三方 HTTP 的恰...
系统设计 29:排行榜的迟到修正与热点代价
如果一场比赛的第 60–120 秒排行榜在第 120 秒立刻封榜,一条第 69 秒发生、但第 121 秒才入站的得分应该去哪?忽略它能让榜单快速稳定,却会让账本与排行榜对不上。排行榜设计首先需要决定:读的是实时暂定榜,还是可纠错的最终榜。 本题假设每个被验证的得分事件 event_id 对同一窗口只计一次;窗口采用事件时间半开区间 [60,120) 秒。相同分数采用并列密集排名,展示顺序再按玩家 ID 字典序稳定排序。删除作弊得分也是修正事件,不是默默改计数。只有权限通过的已确认事件进入榜单,未验证事件不参与计算。 先把榜单合同写全 教学 SLO:暂定榜近 30 天 99% 请求在入口 200 毫秒内返回,且带 computed_through 位点;已受理得分在 5 秒内进入暂定榜(处理完成时间减本地持久化时间的 99%)。终榜在窗口结束后等待 2 分钟迟到宽限,再按相同事件集批量重算并公布版本。以上都是目标,不是实验测出的性能。更晚到达的事件放到修正队列,由业务规则决定是否发新版本。跨赛季汇总、奖金发放和反作弊算法不在本题范围内;有奖金时不能把该暂定榜当财务最终排名。 假设...
系统设计 28:订单支付与邮件收据的三种确认
支付接口没回包,数据库写着 PENDING,用户却看到扣款:客服该查哪一个真相?订单库不是支付机构的账本,邮件“已接收”也不是收件人“已收到”。本篇处理支付受理后回包丢失、重复与乱序回调,并用合成收据状态表示本地接收与退信;邮件是旁路通知,退信不能回滚已确认的付款。 先钉住不可逆的动作 请求键 request-1 只对应一个本地订单 o1,订单金额为 2500 分;一次订单只有一个稳定的网关扣款键 charge-o1。只有确认网关对应扣款成功,订单才从 PENDING 单向转到 PAID;乱序到达的 pending 不能把 PAID 降回去。CANCELLED 只允许在确认没有成功扣款时进入;退款是独立状态机而非把历史付款改为未发生。收据意图与本地订单状态在同一事务提交;本地记录 PAID 不等于用户收件箱已出现邮件。 教学 SLO:入口合法请求 99.9% 在 2 秒内返回订单 ID 和当前状态(30 天窗口,入口接收到响应发出);待核查支付 99% 在 10 分钟内被网关查询或人工队列接管(只测本地状态变化,排除外部长期不可用)。不承诺“2 秒内扣款”或邮件投递成功。税务发...
系统设计 27:通知系统的逻辑去重与送达边界
用户提交订单两次刷新页面,只应该看到一条“订单已受理”的逻辑通知;短信渠道返回超时,却无法据此判断短信没有发出。前一句是自己数据库能控制的去重,后一句是跨渠道的确认缺口。把两句话合成“通知恰好送达一次”,会让重试策略无法落地。 这里设计的是本地假渠道支持的教学方案,不代表任何邮件、短信平台的实际送达语义。业务事件先落地,再根据收件人偏好、安静时段与渠道错误决定投递时机。 需求从收件人开始 输入为 event_id, recipient_id, template_version。教学契约规定同一事件、收件人、模板版本最多生成一条逻辑通知;渠道改变不是新事件,但一次明确的用户主动重发要带新请求键。偏好以创建通知时的版本作为路由快照;取消订阅则是更强的安全约束,投递前还需重新查最新授权。营销退订不能因旧的排队任务而失效。 假设目标:事件受理成功响应意味着事件及通知意图已经持久化,不意味着外部送达;近 30 天的 99.9% 合法事件在受理后 60 秒内进入终态(渠道接受、永久拒绝或待人工核查),计时从入口收到请求至本地状态记录,按自然月窗口统计。安静时段里的营销通知不计入这一目标,应...















