从 DeadlineHandler 中已使用序列的 EventStoreException 中恢复

Recovering from an EventStoreException where sequence is already in use within DeadlineHandler

有时会发生 EventStoreException,指出事件无法存储,因为它与聚合中的另一个事件具有相同的序列。

当 EventA 和 EventB 几乎具有相同的时间戳时会发生这种情况。
CommandA 由控制器发送,CommandB 由 DeadlineHandler 中的 Saga 发送。

所以最后期限的处理失败,记录了 EventStoreException,但没有重试。
如果我们用 PropagatingErrorHandler 配置 Saga 会有帮助吗?

事件table:

timestamp                      | aggregate_id                         | seq | type
                               |                                      |     |
2020-11-30T15:14:51.345541552Z | b02a5364-ee34-431a-ab1a-6c59bb937845 | 0   | MyAggregate
2020-11-30T15:14:52.06794746Z  | b02a5364-ee34-431a-ab1a-6c59bb937845 | 1   | MyAggregate

异常详情:

org.axonframework.eventsourcing.eventstore.EventStoreException: An event for aggregate [b02a5364-ee34-431a-ab1a-6c59bb937845] at sequence [1] was already inserted

java.sql.BatchUpdateException: Batch entry 0 INSERT INTO events 
(event_id, aggregate_id, sequence_number, type, timestamp, payload_type, payload_revision, payload, metadata) 
VALUES 
('d5be369e-5fd0-475e-b5b6-e12449a4ed04',
'b02a5364-ee34-431a-ab1a -6c59bb937845',
1,
'MyAggregate',
'2020-11-30T15:14:52.067871723Z',
'MyEvent',
NULL,
'{"payload":"payload"}',
'{"metaData":"metaData"}') 
was aborted: ERROR: duplicate key value violates unique constraint "uk_aggregate_identifier_sequence_number"
  Detail: Key (aggregate_id, sequence_number)=(b02a5364-ee34-431a-ab1a-6c59bb937845, 1) already exists.

如您所见,事件的时间戳几乎相同:
EventA: 2020-11-30T15:14:52.06794746Z 对比 EventB: 2020-11-30T15:14:52.067871723Z

首先回答您的问题,配置 PropagatingErrorHandler 没有帮助,因为 TrackingEventProcessor 不会重试 DeadlineMessage。它仅适用于重试真实事件,但 DeadlineMessage 并非如此,因为它不是事件。

现在针对你的问题,我们假设你的 Saga 有一个 DeadlineHandler 并且这个组件正在向你的 Aggregate 调度一个 Command 同时另一个组件也在调度一个 Command 到相同的 Aggregate。这样,Aggregate 就无法处理第二个 Command。 基于此,我们可以给你2个建议:

  • 有一个一致的 Routing Strategy,它被 CommandBus 的分布式实现使用。简而言之,它将为您提供以下内容:

Two commands with the same routing key will always be routed to the same segment.

  • 在您的 CommandGateway 上配置 RetryScheduler。您可以阅读更多相关信息 here

The RetryScheduler is capable of scheduling retries when command execution has failed.