Skip to content

Publishing Distributed Events as Transactional #6126

@gentledepp

Description

@gentledepp

Issue: abp.io seems to create a two-phase commit distributed transaction when using distributed events

Abp.io latest source code sends messages (domainevents which should be called integrationevents in DDD I guess) when committing a transaction.

Thus, it can happen, that

  1. the EF transaction succeeds
  2. but the message bus is not reachable (for a longer time so that even using Polly does not help)

In that case, the events will never be sent!.

If I understand your code base correctly, you do send the distributed events within the EF Core transaction:

var result = await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
await EntityChangeEventHelper.TriggerEventsAsync(changeReport);

At least, you send it to the message bus and if that fails, the transaction is also not committed.
However, isn't that exactly what causes a two phase commit?

await TriggerEventsInternalAsync(changeReport);
if (changeReport.IsEmpty() || UnitOfWorkManager.Current == null)
{
return;
}
await UnitOfWorkManager.Current.SaveChangesAsync();

Shouldn't this be avoided?

Solution: The outbox pattern

The solution is using the outbox pattern:
Instead of directly sending the events to the event bus (e.g. RabbitMQ), they are

  • serialized to the database (-> within the same transaction)
  • later consumed by a polling consumer (polling consumer pattern)

Note: There is someone who wrote a 3 series blog article about this very problem and in part 3 he even developed a sample solution which is available on github: https://github.com/mizrael/DDD-School/

Please have a look!

(Here are part 1 and part 2 for reference)

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions