Rebus Saga处理LegalInfoAcquired事件时报ConcurrencyException的原因是什么?
Rebus Saga并发异常ConcurrencyException原因及解决方案
异常产生的核心原因
- 两个事件
LegalInfoAcquiredInFirstSystem和LegalInfoAcquiredInSecondSystem近乎同时投递到队列,被并行处理:两个处理线程同时从数据库加载了ID为95395c60-cf2e-48da-89ff-fdf192ce53b的同一份Saga数据,此时GotLegalInfoFromFirstSystem和GotLegalInfoFromSecondSystem均为false。两个线程分别修改了各自持有的Saga实例中对应的标记位,提交更新到数据库时,Rebus的乐观并发控制机制检测到数据版本已被另一个线程修改,因此抛出ConcurrencyException拒绝后提交的更新操作。 - 你的重试配置直接拦截了默认的并发异常自愈逻辑:当前配置中
SimpleRetryStrategy的maxDeliveryAttempts设为1,且关闭了二级重试,而Rebus默认会对并发类异常自动进行重试,重试后加载最新的Saga数据再次执行处理逻辑,绝大多数场景下一次重试即可解决冲突,你关闭重试后异常会直接抛出进入错误队列。 - 若你的服务是多实例部署,该类并发冲突的发生概率会更高:两个事件可能被分发到不同的服务实例处理,更易出现同时加载同一份Saga数据的情况。
可行的解决方案
- 调整重试策略:将
maxDeliveryAttempts调整为3~5次,根据需要开启二级重试,允许Rebus自动重试并发冲突的Saga更新操作,不需要额外修改业务逻辑即可解决99%以上的偶发并发问题。
示例修改配置:.Options(o => o.SimpleRetryStrategy(errorQueueAddress: settings.Settings.ErrorQueueName, maxDeliveryAttempts: 5, secondLevelRetriesEnabled: true)) - 若并发冲突频率极高,可以考虑引入Saga分布式锁,或者调整业务流程将两个事件的处理逻辑串行化,从根源上避免并行修改同一份Saga数据的场景。
- 额外检查Saga类的关联属性配置,确保两个事件的关联字段映射正确,避免因关联逻辑错误导致的意外并发修改问题。
内容的提问来源于stack exchange,提问作者Amour Rashid
相关产品推荐
相关产品推荐

