Axon Sagas在将事件重放到新数据库时复制事件存储中的事件
我们有一个存储新订单的Axon应用程序。对于每个订单状态更改(OrderStateChangedEvent),它计划了两个任务。这些任务是由另一个传奇故事触发和进行的(TaskSaga-超出问题范围) 当我删除投影数据库,但离开事件存储区,然后再次运行应用程序时,事件将被重放(正确),但任务将被复制 我想这是因为每次Axon Sagas在将事件重放到新数据库时复制事件存储中的事件,axon,Axon,我们有一个存储新订单的Axon应用程序。对于每个订单状态更改(OrderStateChangedEvent),它计划了两个任务。这些任务是由另一个传奇故事触发和进行的(TaskSaga-超出问题范围) 当我删除投影数据库,但离开事件存储区,然后再次运行应用程序时,事件将被重放(正确),但任务将被复制 我想这是因为每次OrderStateChangedEvent都会触发一组新的ScheduleTaskCommand 因为我是轴突方面的新手,我不知道如何避免这种重复 在AxonServer上运行的事
OrderStateChangedEvent
都会触发一组新的ScheduleTaskCommand
因为我是轴突方面的新手,我不知道如何避免这种重复
在AxonServer上运行的事件存储
Spring启动应用程序自动配置axon内容
投影数据库包含投影表和轴突表:
令牌输入
传奇故事
关联\u值\u条目
我假设所有事件都被重放,因为通过重新创建数据库,Axon表消失了(因此没有关于上次应用事件的记录)
我错过什么了吗
- token_条目/saga_条目/association_value_条目表是否应该是每个应用程序节点上投影表的DB的一部分
- 我认为事件存储可以随时重放到新应用程序节点的数据库中,而不必更改事件历史记录,这样我就可以运行任意数量的节点。或者,我可以随时删除投影数据库并运行应用程序,这会导致事件再次投影到新的数据库。或者这不是真的
- 一般来说,我的问题是一个事件产生命令,导致产生新事件(重复)。我是否应该避免事件的“链接”以避免重复
@Configuration
public class AxonConfig {
@Bean
public EventSourcingRepository<ApplicationAggregate> applicationEventSourcingRepository(EventStore eventStore) {
return EventSourcingRepository.builder(ApplicationAggregate.class)
.eventStore(eventStore)
.build();
}
@Bean
public SagaStore sagaStore(EntityManager entityManager) {
return JpaSagaStore.builder().entityManagerProvider(new SimpleEntityManagerProvider(entityManager)).build();
}
}
private Map tasks=new HashMap();
私有OrderState-OrderState;
@斯塔萨加
@SagaEventHandler(associationProperty=“orderId”)
(OrderStateChangeEvent事件)上的公共无效{
orderState=event.getNewState();
List tasksByState=tasksservice.getTasksByState(orderState);
if(tasksByState.isEmpty()){
finishSaga(event.getOrderId());
}
tasksByState.stream()
.map(任务->ScheduleTaskCommand.builder()
.orderId(事件.getOrderId())
.taskId(IdentifierFactory.getInstance().generateIdentifier())
.targetState(订单状态)
.taskName(task.getTaskName())
.build())
.peek(command->tasks.put(command.getTaskId(),SCHEDULED))
.forEach(命令->命令网关.send(命令));
}
在这种情况下,我想我可以帮你。
因此,发生这种情况是因为将所有事件提供给Saga实例的TrackingEventProcessor
使用的TrackingToken
被初始化到事件流的开头。因此,TrackingEventProcessor
将从时间开始启动,从而第二次调度所有命令
你可以做一些事情来解决这个问题
TrackingEventProcessor
的initialTrackingToken
配置为从事件流的头部而不是尾部开始TrackingEventProcessor配置来实例化TrackingEventProcessor
:
EventProcessingConfigurer configurer;
TrackingEventProcessorConfiguration trackingProcessorConfig =
TrackingEventProcessorConfiguration.forSingleThreadedProcessing()
.andInitialTrackingToken(StreamableMessageSource::createHeadToken);
configurer.registerTrackingEventProcessor("{class-name-of-saga}Processor",
Configuration::eventStore,
c -> trackingProcessorConfig);
因此,您需要为您的传奇创建所需的配置,并调用和initialTrackingToken()
函数,确保创建的头令牌不存在令牌
我希望这能帮到你,汤姆 史蒂文的解决方案很有魅力,但只适用于传奇故事。对于那些想要达到相同效果但在经典的@EventHandler
(在重播时跳过执行)中的人,有一种方法。首先,您必须了解跟踪事件处理器的名称-我在AxonDashboard(运行AxonServer的8024端口)中找到了它-通常它是带有@EventHandler
注释(确切地说是包名)的组件的位置。然后添加Steven在回答中指出的配置
@Autowired
public void customConfig(EventProcessingConfigurer configurer) {
// This prevents from replaying some events in @EventHandler
var trackingProcessorConfig = TrackingEventProcessorConfiguration
.forSingleThreadedProcessing()
.andInitialTrackingToken(StreamableMessageSource::createHeadToken);
configurer.registerTrackingEventProcessor("com.domain.notreplayable",
org.axonframework.config.Configuration::eventStore,
c -> trackingProcessorConfig);
}
顺便说一句,我刚刚创建了一个问题来讨论将要切换的默认设置,特别是对于传奇。我已经多次遇到了这个问题,我觉得这应该在框架中进行调整。有没有任何答案解决了你的问题@TomášMika?如果你能分享你的行动方针,或者如果答案解决了你的问题,对其他读者会有好处。
private Map<String, TaskStatus> tasks = new HashMap<>();
private OrderState orderState;
@StartSaga
@SagaEventHandler(associationProperty = "orderId")
public void on(OrderStateChangedEvent event) {
orderState = event.getNewState();
List<OrderStateAwareTaskDefinition> tasksByState = taskService.getTasksByState(orderState);
if (tasksByState.isEmpty()) {
finishSaga(event.getOrderId());
}
tasksByState.stream()
.map(task -> ScheduleTaskCommand.builder()
.orderId(event.getOrderId())
.taskId(IdentifierFactory.getInstance().generateIdentifier())
.targetState(orderState)
.taskName(task.getTaskName())
.build())
.peek(command -> tasks.put(command.getTaskId(), SCHEDULED))
.forEach(command -> commandGateway.send(command));
}
EventProcessingConfigurer configurer;
TrackingEventProcessorConfiguration trackingProcessorConfig =
TrackingEventProcessorConfiguration.forSingleThreadedProcessing()
.andInitialTrackingToken(StreamableMessageSource::createHeadToken);
configurer.registerTrackingEventProcessor("{class-name-of-saga}Processor",
Configuration::eventStore,
c -> trackingProcessorConfig);
@Autowired
public void customConfig(EventProcessingConfigurer configurer) {
// This prevents from replaying some events in @EventHandler
var trackingProcessorConfig = TrackingEventProcessorConfiguration
.forSingleThreadedProcessing()
.andInitialTrackingToken(StreamableMessageSource::createHeadToken);
configurer.registerTrackingEventProcessor("com.domain.notreplayable",
org.axonframework.config.Configuration::eventStore,
c -> trackingProcessorConfig);
}