并发 – 事件源:同时创建冲突事件

我正在尝试使用Kafka实现事件采购系统,并遇到了以下问题.在新用户注册期间,我想检查用户提供的用户名是否已被占用.但是,请考虑2个用户同时尝试注册以提供相同用户名的情况.

在我对ES工作原理的理解中,处理注册请求的控制器将检查请求是否有效,然后它将向Kafka发送一个新事件(例如NewUser),最后该事件将由另一个控制器接收.将它保持在物化视图中(例如Postgres DB).问题是请求的验证是针对物化视图完成的,但实际的持久性发生在以后.因此,由于2个请求是并行处理的(由不同的服务实例),它们可能都会通过验证,从而产生2个NewUser消息.但是,当第二个控制器尝试在数据库中保留这2个NewUser消息时,由于违反了用户名的唯一性约束,第二个事件将失败.

关于如何解决这个问题的任何想法?

谢谢.

更新:

特别是,我想验证以下问题是否已被接受:

>使用用户名作为userId(限制性)
>将事件发送到由用户名分区的主题以及验证时
已完成将事件发送到另一个主题

最佳答案 在您有约束的大多数情况下,针对物化视图的初始验证是不够的.总有一些相关事件尚未实现.有两种主要的 concurrency control方法可确保生成正确的结果:

1.悲观的方法:
如果要在发布事件之前验证约束,则需要锁定相关资源(实体,聚合或数据集).锁定意味着您的服务必须无法在这些资源上发布事件.在此之后,要获取数据的当前状态:

>您可以等到锁定前发布的所有事件都实现.
>您可以从数据库中读取当前状态,并在单独的进程中对其应用事件.

2.乐观的方法:
在此方法中,您在发布事件后执行验证.为此,您需要实现反馈机制.使用事件和执行验证的过程应该能够发布验证结果.您可以在可能的情况下在内存中执行验证.否则,您可以依赖物化数据存储.

Martin Kleppman谈到了完全相同的问题here和his book的两步解决方案.在这个解决方案中,有两个主题:“索赔”和“注册”.首先,您发布声明以获取用户名,然后尝试将其写入数据库,最后将结果发布到注册主题.在概念层面,它遵循您提到的第二种方法中的相同步骤.在验证步骤中,它避免了实现验证逻辑并通过依赖数据库将二级索引保留在内存中.

点赞