MassTransit状态机搭配MongoDB出现重复键异常求助
问题分析与解决方案
核心原因
你遇到的E11000重复键冲突,本质是Saga初始化与请求响应事件的并发写入竞争,结合MassTransit Saga的MongoDB存储逻辑和Ignore配置的行为导致:
Ignore的实际行为:在Initially块中添加Ignore(SomeField.Completed),会让MassTransit为处于初始状态(未持久化到MongoDB)的Saga订阅SomeField.Completed事件。- 并发写入竞争:当
StartingEvent触发后,Saga先执行请求发送逻辑,但此时Saga文档还未插入MongoDB;如果消费者响应极快(无延迟),SomeField.Completed事件会立即到达。此时MassTransit尝试处理该事件:因为Saga文档不存在,MongoDB存储会尝试插入新文档,与此同时StartingEvent的后续逻辑也在插入同一个CorrelationId的文档,最终触发重复键冲突。
为什么去掉Ignore或加延迟能解决
- 去掉
Ignore:初始状态的Saga不会订阅SomeField.Completed事件,只有当Saga完成初始插入并过渡到SomeFieldState后,才会处理该事件(默认自动处理请求完成来更新Saga状态),此时操作是更新已有文档,不会触发插入竞争。 - 添加
Task.Delay(1000):延迟让消费者响应变慢,给StartingEvent处理逻辑足够时间完成Saga文档的初始插入,当SomeField.Completed事件到达时,Saga已存在,执行的是更新操作,避免了重复插入。
正确的处理方式
不要在Initially块中处理请求完成事件,应该将Ignore(或处理逻辑)放到对应的目标状态中,示例代码如下:
public ChatStateMachine() { InstanceState(x => x.CurrentState); Event(() => StartingEvent, x => x.CorrelateById(y => y.Message.SomeId)); Request(() => SomeField, x => x.Timeout = TimeSpan.FromSeconds(10)); Initially( When(StartingEvent) .Request(SomeField, x => new SomeFieldRequest { SomeId = x.Saga.CorrelationId, SomeDefinitionId = x.Saga.SomeDefinitionId, FieldResults = x.Saga.FieldResults, Fields = x.Saga.Fields }) .TransitionTo(SomeFieldState)); // 在目标状态中忽略或处理请求完成事件 During(SomeFieldState, Ignore(SomeField.Completed) ); }
这样配置后,只有当Saga进入SomeFieldState(此时已完成初始持久化),才会订阅SomeField.Completed事件,避免了初始插入与事件处理的并发竞争。
内容的提问来源于stack exchange,提问作者hcvlg
相关产品推荐
相关产品推荐

