RDBMS 事件存储:确保排序(单线程编写器)
RDBMS Event-Store: Ensure ordering (single threaded writer)
有关设置的简短说明:
我正在尝试使用 RDBMS(在我的例子中是 Postgres)实现一个 "basic" 事件存储/事件源应用程序。这些事件是通用事件,只有一些基本字段,如 eventtime
、location
、action
,格式为 XML。由于这种通用结构,现在可以以一种有用的方式对它们进行分区。事件通过 Java 应用程序捕获,该应用程序验证事件并将它们存储在事件 table 中。每个事件在被捕获时都会得到一个 uuid
和 recordtime
。
此外,还可以订阅外部应用程序,这应该会获取所有符合自定义条件的事件。当捕获到新的匹配事件时,应该将事件推送给订阅者。为了确保订阅者不会错过任何事件,我目前强制捕获过程是单线程的。当一个新事件进来时,设置一个锁,事件得到一个分配给当前时间的recordtime
,事件最终被插入到数据库中table(显式等待提交)。然后释放锁。例如,对于计划每 5 秒运行一次的订阅,我跟踪最后发送的事件的 recordtime
,并执行对新事件的查询,例如 where recordtime > subscription_recordtime
。当匹配的事件成功推送到订阅者时,subscription_recordtime
设置为事件最大值 recordtime
。
实际上一切正常,但正如您想象的那样,单线程捕获过程不能很好地扩展。因此,主要问题是:我如何优化它并允许并行多个捕获进程 运行?
我已经考虑过在插入时在数据库本身中设置 recordtime
,但是由于无法保证提交的顺序(JVM 暂停),我认为当两个捕获事务 运行几乎同时。当我了解数据库当前生成的时间戳时,它将在 实际提交之前设置 。因此,具有 recordtime
t2 的事务已经可以对订阅查询可见,尽管另一个具有 recordtime
t1[=46= 的事务] (t1 < t2),仍在进行中,因此尚未提交。订阅的 recordtime
将设置为 t2,因此事务 1 中的事件将丢失...
有没有办法保证数据库级别的顺序,以便事件按照捕获/提交的顺序可见?每个新可见事件的时间戳必须晚于之前的事件(严格单调递增)。我知道一个完整的 table 锁,但我认为,那样我将面临与以前相同的性能损失。
是否可以将 DB 设置为使用单线程编写器?然后每个捕获进程也将等待另一个写入 TX 完成,但在数据库级别,这比单个 instance/threaded 捕获应用程序要好得多。或者我可以使用不同的 field/id 来跟踪当前状态吗?正常的序列 ID 也会遇到同样的原因。
Is there a way to guarantee the order on a DB level, so that events are visible in the order they are captured/ committed?
您不应该关心事件的全局排序。您的活动应包含版本 属性。编写事件时,您应该始终为给定的 Aggregate/Stream ID 插入单调递增的版本号 。这确实是您插入时唯一重要的顺序。对于具有事件 1、2、3 和 4 的客户 ABC,您应该只编写事件 5。
数据库事务可以使用上述规则确保流中的正确顺序。
For a subscription which runs scheduled for example every 5 seconds, I track the recordtime of the last sent event, and execute a query for new events like where recordtime > subscription_recordtime.
阅读活动的情况略有不同。首先,您可能会有一个序列列来唯一标识事件。这将为您提供排序并允许您确定是否已阅读所有事件。当您从存储中读取事件时,如果您检测到序列中存在间隙。如果当您阅读最新事件时插入正在进行中,就会发生这种情况。在这种情况下,只需重新读取数据并查看间隙是否消失。这需要您的订阅保持其在索引中的位置。或者或另外,您可以读取至少 N 毫秒之前的事件,其中 N 是一个阈值,足以补偿事务中的延迟(例如 500 或 1000)。
另外,请记住,您可以在流程中使用或利用开源 RDBMS 事件存储。
马丁: http://jasperfx.github.io/marten/documentation/events/
SqlStreamStore: https://github.com/SQLStreamStore/SQLStreamStore
有关设置的简短说明:
我正在尝试使用 RDBMS(在我的例子中是 Postgres)实现一个 "basic" 事件存储/事件源应用程序。这些事件是通用事件,只有一些基本字段,如 eventtime
、location
、action
,格式为 XML。由于这种通用结构,现在可以以一种有用的方式对它们进行分区。事件通过 Java 应用程序捕获,该应用程序验证事件并将它们存储在事件 table 中。每个事件在被捕获时都会得到一个 uuid
和 recordtime
。
此外,还可以订阅外部应用程序,这应该会获取所有符合自定义条件的事件。当捕获到新的匹配事件时,应该将事件推送给订阅者。为了确保订阅者不会错过任何事件,我目前强制捕获过程是单线程的。当一个新事件进来时,设置一个锁,事件得到一个分配给当前时间的recordtime
,事件最终被插入到数据库中table(显式等待提交)。然后释放锁。例如,对于计划每 5 秒运行一次的订阅,我跟踪最后发送的事件的 recordtime
,并执行对新事件的查询,例如 where recordtime > subscription_recordtime
。当匹配的事件成功推送到订阅者时,subscription_recordtime
设置为事件最大值 recordtime
。
实际上一切正常,但正如您想象的那样,单线程捕获过程不能很好地扩展。因此,主要问题是:我如何优化它并允许并行多个捕获进程 运行?
我已经考虑过在插入时在数据库本身中设置 recordtime
,但是由于无法保证提交的顺序(JVM 暂停),我认为当两个捕获事务 运行几乎同时。当我了解数据库当前生成的时间戳时,它将在 实际提交之前设置 。因此,具有 recordtime
t2 的事务已经可以对订阅查询可见,尽管另一个具有 recordtime
t1[=46= 的事务] (t1 < t2),仍在进行中,因此尚未提交。订阅的 recordtime
将设置为 t2,因此事务 1 中的事件将丢失...
有没有办法保证数据库级别的顺序,以便事件按照捕获/提交的顺序可见?每个新可见事件的时间戳必须晚于之前的事件(严格单调递增)。我知道一个完整的 table 锁,但我认为,那样我将面临与以前相同的性能损失。
是否可以将 DB 设置为使用单线程编写器?然后每个捕获进程也将等待另一个写入 TX 完成,但在数据库级别,这比单个 instance/threaded 捕获应用程序要好得多。或者我可以使用不同的 field/id 来跟踪当前状态吗?正常的序列 ID 也会遇到同样的原因。
Is there a way to guarantee the order on a DB level, so that events are visible in the order they are captured/ committed?
您不应该关心事件的全局排序。您的活动应包含版本 属性。编写事件时,您应该始终为给定的 Aggregate/Stream ID 插入单调递增的版本号 。这确实是您插入时唯一重要的顺序。对于具有事件 1、2、3 和 4 的客户 ABC,您应该只编写事件 5。
数据库事务可以使用上述规则确保流中的正确顺序。
For a subscription which runs scheduled for example every 5 seconds, I track the recordtime of the last sent event, and execute a query for new events like where recordtime > subscription_recordtime.
阅读活动的情况略有不同。首先,您可能会有一个序列列来唯一标识事件。这将为您提供排序并允许您确定是否已阅读所有事件。当您从存储中读取事件时,如果您检测到序列中存在间隙。如果当您阅读最新事件时插入正在进行中,就会发生这种情况。在这种情况下,只需重新读取数据并查看间隙是否消失。这需要您的订阅保持其在索引中的位置。或者或另外,您可以读取至少 N 毫秒之前的事件,其中 N 是一个阈值,足以补偿事务中的延迟(例如 500 或 1000)。
另外,请记住,您可以在流程中使用或利用开源 RDBMS 事件存储。
马丁: http://jasperfx.github.io/marten/documentation/events/
SqlStreamStore: https://github.com/SQLStreamStore/SQLStreamStore