切换到MassTransit.Marten后,Saga完成时未自动删除的问题排查
Saga状态机切换到Marten/Postgres后,Final状态实例未被自动删除
问题背景
首次实现Saga,此前用InMemory仓库测试一切正常,切换到Marten/Postgres后发现:触发超时事件将状态设为Final后,Saga实例并未被自动删除。由于和第三方系统交互无法获取其Guid关联ID,因此使用了对方的标识信息作为关联依据。
代码配置
Saga实例类
public class GroupSaga : SagaStateMachineInstance { public string Partition { get; set; } public Guid CorrelationId { get; set; } public string CurrentState { get; set; } }
超时事件类
class CommsTimeoutEvent : ICommsTimeout { readonly GroupSaga _instance; public CommsTimeoutEvent(GroupSaga instance) { _instance = instance; } public Guid SagaGuid => _instance.CorrelationId; }
状态机核心配置
Event(() => MyEventReceived, x => x.CorrelateBy(rsp => rsp.Partition, context => context.Message.Partition) .SelectId(_ => NewId.NextGuid())); Schedule(() => CommsTimedOut, x => x.ExpirationId, x => { x.Delay = TimeSpan.FromSeconds(10); x.Received = e => e.CorrelateById(context => context.Message.SagaGuid); }); Initially( When(MyEventReceived) .Then(context => { context.Saga.Partition = context.Message.Partition; }) .Activity(x => x.OfInstanceType<CustomActivity>()) .Schedule(CommsTimedOut, context => new CommsTimeoutEvent(context.Saga)) .TransitionTo(WaitingForResponse)); // 省略其他状态流转配置 DuringAny( When(CommsTimedOut.Received) .Finalize()); SetCompletedWhenFinalized();
Marten初始化代码
config.AddSagaStateMachine<GroupStateMachine, GroupSaga>() .MartenRepository(dbConnectionString);
问题现象
触发超时后,数据库表中Saga的CurrentState字段显示为Final,但实例从未被自动删除。再次触发启动事件MyEventReceived时会报错,因为初始事件无法在Final状态下处理。
解决方法
问题根源在于Marten Saga仓库的默认行为:InMemory仓库会自动删除Final状态实例,但Marten仓库默认不会主动清理,需要手动配置清理策略:
启用自动清理配置
在Marten仓库初始化时,添加CleanupRemovedSagas选项指定清理间隔:config.AddSagaStateMachine<GroupStateMachine, GroupSaga>() .MartenRepository(dbConnectionString, repoConfig => { // 配置每小时清理一次Final状态的Saga实例 repoConfig.CleanupRemovedSagas(TimeSpan.FromHours(1)); });确认状态机标记逻辑
确保SetCompletedWhenFinalized()已正确添加到状态机配置中,这个方法会将Final状态的Saga标记为待删除状态,供后续清理任务处理。手动触发清理(可选)
如果需要立即清理已标记的实例,可以在应用启动或定时任务中调用Marten的清理方法:using var session = documentStore.LightweightSession(); await session.DeleteExpiredSagasAsync(); session.SaveChanges();
补充说明
Marten不会实时删除Final状态的Saga,而是通过后台定时任务(默认每分钟检查一次,实际清理间隔由CleanupRemovedSagas参数决定)批量清理标记为已完成的实例。如果需要更及时的清理,可以缩短间隔,但需注意不要过于频繁导致数据库性能损耗。
内容的提问来源于stack exchange,提问作者Pete Roberts
相关产品推荐
相关产品推荐

