为了处理大量遥测数据并仍然能够对数据执行快速查询,我采用了使用 Azure Functions 和 Azure Cosmos DB 的事件溯源/CQRS 模式。
在我的架构中,入站遥测流存储在充当事件存储的 Cosmos DB 集合中。
为了创建原始遥测数据的物化 View ,我使用了另一个带有 Cosmos DB 触发器的 Azure 函数,该函数对存储在事件存储中的所有新文档激活,并对这些文档执行转换。
在每个文档的基础上进行工作非常容易。 棘手的是,当我需要引用其他文档来计算我的物化 View 时。
例如,当接收到的遥测事件包含相对计数器值(例如特定操作中使用的能量)时。在我的物化 View 中,我希望有一个包含所有能源消耗总和的文档。
现在,一个简单的实现是在我的物化 View 中查看该文档的当前状态,然后将该值增加新收到的值。
使用这种方法可能遇到的问题是,当我必须重新计算我的物化 View 时,因为在未来的版本中我需要生成一些额外的 View 。
对于重新计算,我只需触摸事件存储中要重新计算的所有相关文档,触发 Azure 函数再次计算物化 View 。这将导致文档进入之前已处理过的此 Azure 函数。
当重新计算时,如果我简单地增加总和,我的计数器现在将不再准确,因为已经属于总和一部分的文档将再次添加。
解决这个重新计算场景(我想到的)的方法是:
- 跟踪所有属于总和一部分的源文档并忽略 已经属于总和的文档的事件
- 跟踪已经属于总和的最新遥测事件的序列号,并在重新计算时忽略序列号低于已属于总和的序列号的事件。
您能给我一些关于如何正确解决此类情况的建议吗?
最佳答案
因此,总结@Mikhail 和@RomanEremin 的评论以及我的想法,这将是处理这些场景的方法:
如果重新计算 View :
- 删除现有聚合并从头开始构建,重播 事件存储中的事件。
如果事件总线提供“至少一次”(Azure 与 CosmosDb 触发器一起运行的方式是底层 ChangeFeedProcessor 的结果):
- 版本 1:跟踪属于聚合文档中聚合的一部分的事件 ID(文档 ID),并忽略已经属于聚合的事件。
- 版本 2:提供源事件的顺序版本(序列号),并将聚合所基于的版本存储在聚合文档中。计算聚合时,请对照事件的序列号检查此序列号。如果事件的序列号低于聚合文档:忽略,否则重新计算聚合并更新聚合所基于的序列号。
关于azure-functions - 具有事件溯源和 CQRS 的聚合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50897331/