事件分析
同一个任务,存在两份条件
场景很简单:为一场会议推荐场地,最初是 20人、预算3000元。查询甲受理后,系统可以先整理议程草稿,因为这件事不依赖场地返回;此时仍没有场地推荐,更没有完成预订。
等待期间,用户先把预算降到2000元,又把人数改成30人。这是对两个字段的更新,后一条不能覆盖前一条。接收盘先保存两条事件,调度器再把它们应用到当前条件。查询甲保留原来的20人、3000元快照,用来解释它为什么返回这批候选。
这两份条件都正确,只是回答的问题不同。若把新要求直接写进在途请求的旧记录,会抹掉请求真正执行过的条件;若一直沿用旧记录做推荐,又会忽略用户已经提出的变化。
一条完整的事件链
| 发生的事 | 需要保留或改变的状态 |
|---|---|
| 发起查询甲 | 登记待办,保存20人、3000元的发起快照 |
| 收到预算与人数更新 | 两条事件分别保留,尚未应用时仍显示待处理 |
| 应用两条更新 | 当前条件变为30人、2000元,查询甲快照保持原样 |
| 查询甲返回 | 核对请求归属,结束相应待办,再检查候选适用性 |
| 按新条件发起查询乙 | 创建新请求,不覆盖查询甲的身份和结果 |
| 查询乙返回并复核 | 竹厅32人、1900元进入合格候选,再形成推荐 |
演示的默认讲解按甲、乙顺序返回。使用“新结果先回,旧结果后回”预设,可以观察另一条合法顺序。到达顺序只能说明谁先返回,不能决定谁有权覆盖当前推荐。
旧结果是否可用,要看它提供了什么证据
查询甲返回松厅和云厅。按当前30人、2000元核对:松厅容量24人,不够;云厅费用2800元,超预算。这批结果不能成为当前推荐。查询乙返回的竹厅容量32人、费用1900元,两项都满足,才能进入候选槽。
但“发起时条件旧了”并不等于“结果必须丢弃”。如果旧查询返回的某个场地仍满足新条件,而且价格、可用性等信息仍然有效,就有复用价值。这里需要判断的是证据适用性,而不是简单比较时间戳。
本例还有一个容易忽略的限制:甲只返回松厅与云厅,它不是完整场地目录。两个候选不合格,只能得出“这批结果不满足要求”,不能推出“没有任何合适场地”。因此需要新查询,也不能从页面列出的固定资料里偷取一个尚未返回的场地给模型。
形成建议前,还应检查接收盘里是否存在尚未应用的要求。本页故意允许“只应用人数”,但只要预算事件仍待处理,推荐就会被阻止。这样,“符合当前条件”才不会变成“符合系统漏读之后的条件”。
工程取舍
异步任务里的四个设计选择
立即重查,还是先复核已有结果?
每次要求变化都取消并重发,实现起来直观,但会浪费仍可能有用的在途工作;始终复用旧结果,则容易漏掉新条件下才出现的候选。查询成本高、返回事实较稳定时,可以先复核再补查;库存、报价或权限变化快时,需要重新获取有效证据。即使复用了某个旧候选,也不代表已经获得新条件下的最优或完整结果。
何时应用更新?
逐条到达、逐条改动的好处是响应快,但后续步骤可能看到中间状态;在明确边界集中应用,比较容易得到一致的任务快照,代价是增加等待。本页把“收到”与“应用”拆开,是为了展示这个差别。真实系统可选择单任务串行处理事件,并在推荐或执行前核对条件版本,避免读到一半新、一半旧的状态。
这里的预算与人数更新属于不同字段,可以合并;若同一字段连续修改,应定义权威顺序,例如服务端确认的任务修订号。网络到达时间不一定代表用户意图的先后,不能不加区分地采用“最后到达覆盖一切”。
请求取消之后,还要等待什么?
发送取消请求,只说明系统表达了停止意图。本页将查询从“运行中”切到“取消请求中”,收到执行器确认后才标为“已取消”。如果结果在确认之前已经完成,取消确认不能追溯抹掉这次完成;确认后的晚到回执也不能重新激活请求。
对于查询,停止采用结果通常足以隔离影响;对于发邮件、预订或扣款,必须另外核对外部动作究竟有没有发生。停止等待、停止工作和撤销已发生的副作用需要分别处理。这个演示只推荐场地,不涉及真实预订。
重复投递与失败重试,需要两种身份
重复回执不应再次完成同一待办,也不应重复触发外部动作。重试则是同一逻辑请求下的一次新尝试:除了请求身份,还需要尝试身份,才能判断晚到回执来自旧执行还是新执行。本页查询乙失败后会保留失败记录,并生成新的句柄再重试。
如果操作可能产生副作用,还需要服务端认可的幂等约定:同一业务意图的网络重试应复用相应幂等键,不能因为换了尝试句柄就再订一次。同样,参数已经变化的新意图也不能随意复用旧幂等键。请求归属、尝试归属和副作用去重各自解决不同问题。
落到实现
让运行时持有事实,再把事实交给模型
这套机制可以归纳为三个值得在实现中持续检查的约束:
- 已发出的请求可追溯。身份、发起条件和终态有记录,新要求不改写旧请求的事实。
- 收到的事件有去处。要求更新明确为待处理或已应用,回执对应具体请求;无法归属的记录先隔离。
- 推荐绑定已核对的条件。模型获得最新任务快照和适用证据,条件再次变化时,旧建议需要复核。
本页的状态转移、容量与预算核对在浏览器内实际执行;场地资料、回执和模型建议使用固定样例。生产实现仍需补上持久化、故障恢复、并发更新控制和副作用约束。演示也没有开放任意旧重试句柄的回执注入,不能据此认为已经覆盖全部重试竞态。
异步的关键,是让仍在运行的工作、已经发生的事实和当前有效的要求保持可区分。这样,结果晚到时,系统仍知道它是谁的、能用来做什么,以及还缺什么才能继续。