将事件重播到新数据库时,Axon Sagas复制事件存储中的事件

时间:2019-03-07 09:52:04

标签: axon

我们有Axon应用程序可以存储新订单。对于每个订单状态更改(OrderStateChangedEvent),它计划几个任务。任务由另一个Saga触发并执行(TaskSaga-超出问题范围)

当我删除投影数据库,但是离开事件存储,然后再次运行应用程序时,将重播事件(正确的事件),但是任务是重复的。

我认为这是因为OrderStateChangedEvent每次都会触发一组新的ScheduleTaskCommand

由于我是Axon的新手,所以无法弄清楚如何避免这种重复。

在AxonServer上运行的事件存储

Spring启动应用程序会自动配置轴突内容

投影数据库包含投影表和轴突表: token_entry saga_entry association_value_entry

我想重播所有事件,因为通过重新创建数据库,Axon表消失了(因此没有关于上一次应用事件的记录)

我想念什么吗?

  • token_entry / saga_entry / association_value_entry表是否应该是每个应用程序节点上的投影表的数据库的一部分?
  • 我认为事件存储可以在不更改事件历史记录的情况下随时重播到新的应用程序节点的数据库中,因此我可以根据需要运行任意数量的节点。或者我可以随时删除投影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();
    }
}
  1. 按订单汇总收到的CreateOrderCommand(fromCommand方法仅将1:1命令映射到事件)
    @CommandHandler
    public OrderAggregate(CreateOrderCommand cmd) {
        apply(OrderCreatedEvent.fromCommand(cmd))
                .andThenApply(() -> OrderStateChangedEvent.builder()
                        .applicationId(cmd.getOrderId())
                        .newState(OrderState.NEW)
                        .build());
    }
  1. 订单汇总设置属性
    @EventSourcingHandler
    protected void on(OrderCreatedEvent event) {
        id = event.getOrderId();

        // ... additional properties set

    }

    @EventSourcingHandler
    protected void on(OrderStateChangedEvent cmd) {
        this.state = cmd.getNewState();
    }
    Saga监听了
  1. OrderStateChangedEvent,该事件为特定状态的顺序安排了一些任务
    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));
    }

2 个答案:

答案 0 :(得分:1)

Steven的解决方案就像一个魅力,但仅在Sagas中起作用。对于那些想要达到相同效果但在经典@EventHandler中(跳过重播执行)的人,有一种方法。首先,您必须找出跟踪事件处理器的命名方式-我在AxonDashboard中找到了它(正在运行AxonServer的8024端口)-通常它是带有@EventHandler批注的组件的位置(精确的包名称)。然后按照史蒂文在回答中指出的那样添加配置。

    @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);
    }

答案 1 :(得分:0)

在这种情况下,我想可以为您提供帮助。 因此,发生这种情况是因为将所有事件提供给Saga实例的TrackingToken使用的TrackingEventProcessor已初始化为事件流的开头。因此,TrackingEventProcessor将从时间开始开始,从而第二次分发所有命令。

您可以采取一些措施来解决此问题。

  1. 您可以擦拭投影表并完整保留令牌表,而不是擦除整个数据库。
  2. 您可以将initialTrackingToken的{​​{1}}配置为从事件流的开头而不是结尾开始。

选项1可以解决问题,但是从操作角度来说需要一些委派。选项2交给开发人员,可能比其他解决方案安全一些。

要调整令牌从头开始,可以用TrackingEventProcessor实例化TrackingEventProcessor

TrackingEventProcessorConfiguration

因此,您将为Saga创建所需的配置并调用 EventProcessingConfigurer configurer; TrackingEventProcessorConfiguration trackingProcessorConfig = TrackingEventProcessorConfiguration.forSingleThreadedProcessing() .andInitialTrackingToken(StreamableMessageSource::createHeadToken); configurer.registerTrackingEventProcessor("{class-name-of-saga}Processor", Configuration::eventStore, c -> trackingProcessorConfig); 函数,并确保创建不存在令牌的头部令牌。

我希望这可以帮助您解决Tomáš!