事件溯源可作为AI大模型的创意工具

banq

事件溯源是一种架构,它将系统中的每次变更都存储在不可变的事件日志中,而不仅仅是捕获最新状态,而是存储数据变更的意图。它不仅仅是记录过去的操作,更是保存数据的完整叙述。每一次创建、更新或删除都会成为事件历史记录中有意义的条目。通过按照事件在系统中发生的顺序重放这些事件,您可以轻松重建应用程序在任何时间点的状态,就像在无缝地浏览系统的故事一样。在这篇文章中,我将尝试阐述事件溯源的巨大潜力,而目前人们对事件溯源的理解非常狭隘,这可以理解。

大多数开发人员将事件溯源视为安全网,主要用于灾难恢复、调试复杂的生产问题、重建损坏的读取模型、通过详细的审计线索维护合规性,或管理大型关键系统中具有挑战性的模式迁移等场景。通常,重放的使用并不频繁,例如在中断后恢复支付账本、纠正金融交易不一致,或在软件部署出现问题后恢复用户数据。在这些情况下,重放风险很高,需要谨慎对待,因为替代方案更糟糕。

这种事件源观点具有极大的局限性。

可重玩性
事件溯源的每一个可能性都应该从一个简单的超能力开始:重放的能力

重放通常被视为危险、脆弱,或者只有高级工程师才应该接触的东西。说实话,这很公平。在大多数实现中,这很困难。这是因为重放通常是事后添加的。事件是在应用程序逻辑运行后发出的。你的 API 处理请求,更新数据库,然后才将事件作为副作用发布。事件不是真相的来源。它只是一条消息,表明发生了某事。

这会产生各种重放风险。由于事件本身就不是为了重放而设计的,处理它们的逻辑可能并非幂等。你可能会面临数据重复处理的风险。你必须仔细地设置版本控制。你必须确保你的数据库能够承受重写。而且,为了安全地做到这一点,你不得不编写大量的自定义基础设施。

所以,把重播当作最后的手段也是合情合理的。它很脆弱,也很可怕。除非别无选择,否则你不会动用它。

但事实并非必须如此。

如果翻转流程会怎样? - 用例 1
如果事件不是在应用程序逻辑运行后 发出事件,而是作为起点,会怎么样?

用户点击按钮:客户端不会向您的 API发送请求,而是直接向事件源发送请求。该事件会被不可变地附加到事件源,并立即成为事件发生的真实情况。只有这样,它才会传递给您的 API 进行验证、处理,并写入数据库。

现在,你的 API 变成了一个转换层,而不是权威层。你的数据库变成了一个读取模型,  一个缓存,而不是事实来源。真正的记录是不可变的事件日志。这样,你就遵循了 CQRS 方法论。

重放不再是一项危险的操作。这只是……系统运作的方式。更新逻辑?删除数据库。重放事件。系统会自行恢复到新状态。无需停机。无需迁移。无需回填。无需复杂的脚本或批处理作业。只需一键重置,  即可升级行为。

当事件流成为你的数据源时,应用程序的每个部分都可以安全地进行改进。你可以重构数据库,重写处理程序,更改应用程序的行为方式,并重新回到全新、一致、正确的状态。

这种架构不仅能增强系统的弹性,还能解决软件开发中最古老、最顽固的难题之一:事后更改数据模型。

自从我们构建应用程序以来,我们就一直害怕架构变更、迁移、数据损坏以及破坏我们不完全理解的东西。我们编写过脆弱的一次性脚本,在部署窗口期熬夜,也曾祈祷在生产环境中运行 ALTER TABLE ;_;

动态导出 – 用例 2
使用重放,您无需事先了解理想的模式。您确实不需要漫长的设计阶段。无论您对新功能、报告、集成的需求如何变化,甚至仅仅是探索一个想法,您都可以构建新的读取模型。需要以不同的方式对事件进行分组?跟踪新字段?展平嵌套结构?只需编写新的逻辑并重放即可。您的原始事件保持不变。但您对数据的理解和形状可能会随时发生变化。

这与脆弱的数据管道相反,这是一种有韧性的探索。

AI 优化的衍生读取模型 – 用例 3
大语言模型不需要事务表。它们需要清晰度、上下文和形状。

当您的事件存储意图而不仅仅是状态时,您可以将它们重放到针对语义搜索、代理工作流或自然语言界面优化的读取模型中。

如果您需要构建一个 AI 界面来回答“去年哪些城市的新业务增长最多?”,
您无需查询事务数据库。
您可以重放到一个专为推理量身定制的新表中。

更棒的是:AI 还能帮你决定表格应该是什么样子。通过查看事件源日志。没错,不是开玩笑。

无需重写的基础设施 – 用例 4
遗留系统数据满满?没有事件?没问题。
只需将数据提取到事件存储中一次。之后,您可以根据用例需要,将数据回放到任何结构中。
想要迁移系统?在其上构建新产品?插入分析功能?
您无需完全重写。您只需要一个良好的事件流。
回放将成为您的集成层——一个您可以掌控的层。

改进事件源 – 用例 5
重放最容易被忽视的超能力之一就是,您不会被永远锁定在原始事件流中。
您可以将一个事件源重放为一个具有改进结构、丰富字段或清理语义的 新事件源。

假设你的早期事件比较原始。它们可能缺少字段、格式不一致或数据噪声较大。
与其一直纠结于这些问题,不如编写一个转换器来清理它们,并将其重放到一个新的、结构良好的事件日志中。

现在,您的新事件源将成为未来流程的基础,更清晰、更易于使用,并与您当前对领域的理解保持一致。

它是针对您的数据意图的版本控制,而不仅仅是您的模型。

网友:
1、你的做法和我的 ES 之旅非常相似,我的 ES 之旅始于阅读 LMAX 和帧同步视频游戏模式。这种方法在热门的 ES 文章中被称为“命令源”,他们认为这种方法很糟糕,应该在触发事件之前先运行逻辑。我和你一样,认为他们漏掉了一个技巧。
如果你想让重放变得轻松,事件必须在API之前到达事件源。首先,因为你想存储用户的原始意图,而不是业务逻辑的副作用。但更重要的是,这才是重放默认可访问的原因。无需特殊脚本,也无需脆弱的补丁工作。只需更新逻辑并重放即可。这是大多数人容易忽略的技巧。

2、如果失败了,存储意图有什么意义?如果逻辑发生变化,存储意图又有什么意义?
如果某个事件在 API 级别失败,您必须在事件源中存储一个相反的事件,以确保历史记录的准确性。因此,如果我们在事件日志中收到一个用户创建的事件,而该事件在业务逻辑级别失败,那么发送一个用户已删除的事件来维护事件的正确历史记录就非常重要。否则,当您下次重放该事件时,该事件可能不会失败,而用户会被创建,即使实际上该用户并不存在,因为它失败了。

当逻辑更新时,通过重放相同的意图事件,更新后的逻辑将 像处理事件一样处理这些事件,并应用新的约束。因此,存储意图的目的是能够正确地重建应用程序的状态,只不过是以新的或更新的方式。

3、我非常欣赏这篇文章,也很喜欢事件溯源架构的想法。我只见过它实现过一次,虽然事件溯源的承诺与我们在 2018 年所了解的完全一致,但重放本身一直都是一场噩梦。如果它运行良好,并且我们能够持续快速地重放事件,那么也许我们就能体会到它的一些好处。在一个相当新的系统中,重放总是一个缓慢而痛苦的过程,而且只有几十万个事件需要花费数小时才能重放。也许从那时起这项技术已经有所改进,但从那以后我就没有设计任何使用它的系统了。

4、这种开发方式不仅仅关乎韧性和持续改进,它还意味着您的数据工程师能够访问丰富的、按时间顺序排列的意图流,而不是零散的快照。这意味着能够可视化数据的演变过程,清晰地追踪根本原因,并设计基于上下文而非仅仅基于结构的 MCP 服务器模型。事件历史记录将成为洞察的画布,而不仅仅是备份计划。

5、Replay 的强大之处在于,它以最原始的形式存储数据,换句话说,你存储的是承载着意图的事件。以这种方式存储数据,你可以从中获取结构,而不是预先构建数据,否则你就会被锁定在这种结构中。