Skip to content

Instantly share code, notes, and snippets.

@rponte
Last active February 13, 2026 08:40
Show Gist options
  • Select an option

  • Save rponte/9477858e619d8b986e17771c8be7827f to your computer and use it in GitHub Desktop.

Select an option

Save rponte/9477858e619d8b986e17771c8be7827f to your computer and use it in GitHub Desktop.
THEORY: Distributed Transactions and why you should avoid them (2 Phase Commit , Saga Pattern, TCC, Idempotency etc)

Distributed Transactions and why you should avoid them

  1. Modern technologies won't support it (RabbitMQ, Kafka, etc.);
  2. This is a form of using Inter-Process Communication in a synchronized way and this reduces availability;
  3. All participants of the distributed transaction need to be avaiable for a distributed commit, again: reduces availability.

Implementing business transactions that span multiple services is not straightforward. Distributed transactions are best avoided because of the CAP theorem. Moreover, many modern (NoSQL) databases don’t support them. The best solution is to use the Saga Pattern.

[...]

One of the most well-known patterns for distributed transactions is called Saga. The first paper about it was published back in 1987 and has it been a popular solution since then.

There are a couple of different ways to implement a saga transaction, but the two most popular are:

  • Events/Choreography: When there is no central coordination, each service produces and listen to other service’s events and decides if an action should be taken or not;
  • Command/Orchestration: when a coordinator service is responsible for centralizing the saga’s decision making and sequencing business logic;
@rponte
Copy link
Author

rponte commented Sep 8, 2025

@rponte
Copy link
Author

rponte commented Sep 30, 2025

@rponte
Copy link
Author

rponte commented Oct 16, 2025

Microservices, clearing up the definitions -- by Andras Gerlits

For a software to be predictable, we need to make sure that single events from the client’s perspective are reflected as such throughout the whole system.

This means that if the client asks for a change, all its facets need to be accepted or rejected by the system as a single package, we can’t pick and choose which aspects to do or not to do, unless we have an intuitive way to prompt the user about what we have failed to achieve and that fault is translated back to the client. It’s easy to see that if we allow for such failures, we must design the corresponding 'translation' to the end user, and that this means understanding our client’s competence level with regards to that system. In other words, we can shift some of the responsibility on the end user, but only the ones we can expect them to manage appropriately and by providing them the right tools.

@rponte
Copy link
Author

rponte commented Oct 20, 2025

⭐️ Spring Outbox | Github repository | LinkedIn Annoucement

Spring Outbox is a minimal-configuration Spring Boot library for reliably publishing domain events using the Outbox Pattern.

It works out of the box: you just add the dependency, enable the outbox, and provide a OutboxRecordProcessor bean. The library handles storing, processing, and retrying events automatically, so you can focus on your business logic instead of wiring infrastructure.

@rponte
Copy link
Author

rponte commented Nov 18, 2025

@rponte
Copy link
Author

rponte commented Nov 24, 2025

@rponte
Copy link
Author

rponte commented Nov 24, 2025

@rponte
Copy link
Author

rponte commented Nov 24, 2025

@rponte
Copy link
Author

rponte commented Dec 1, 2025

@rponte
Copy link
Author

rponte commented Dec 3, 2025

A good description on the difference between Deduplication and Idempotency

🔗 Post on Linkedin by Henri Maxime DemoulinHenri Maxime Demoulin

Many engineers confuse these two: deduplication keys vs idempotency keys. They look similar but solve two completely different problems.

Deduplication keys are used to detect that a unit of work has already been executed and skip the execution entirely.

Their goal is to prevent the work from running twice.

Idempotency keys are used to ensure that even if a unit of work runs twice (or more), the final state is correct and equivalent.

One prevents execution, the other effects.

Deduplication keys have been around for a while, e.g., SQS uses them to ensure only once delivery within 5 minute windows.

Before you ship your next API or background worker, ask yourself: do you need idempotency, deduplication... or both?

image

@rponte
Copy link
Author

rponte commented Dec 30, 2025

Idempotence comes in different shapes - by Dominik Tornow

Idempotence comes in different shapes

Idempotence is the guarantee that repeating a request yields the same outcome (or, more formally, does not change the state of the system beyond the initial application)

In practice, idempotence comes in a few variants, most notably positive and negative idempotence

Positive

Positive idempotence denotes that the system has accepted the request in the past:

I have accepted this request in the past, I will accept the request again, I will apply this request again, nothing changes

-or-

I have accepted this request in the past, I will accept the request again, I will not apply this request again

Negative

Negative idempotence denotes that the system has rejected the request in the past

I have rejected this request in the past, I will reject this request again

Negative idempotence is often harder to guarantee:

When a system accepts a request, the system state changes, the new state is evidence of the past acceptance of the request

When a system rejects a request, the system state may not change, there is no evidence of the past rejection of the request

@rponte
Copy link
Author

rponte commented Dec 31, 2025

@rafaelpontezup
Copy link

rafaelpontezup commented Feb 4, 2026

Linkedin post: Stop using the "Outbox pattern". It's 2026: you can use Durable Execution instead -- by Henri Maxime Demoulin

Stop using the "Outbox pattern". It's 2026: you can use Durable Execution instead.

want to write to system A and B atomically. But I can't write:

  • WriteA()
  • WriteB()

If the process crash between the two, WriteB() is lost forever.

The outbox pattern says:

  • Write to A and an intent to write to B atomically (e.g., in the same transaction or the same document)
  • Let another system, a "message relay", eventually send to B.

Eventually, the message relay will pick up on the message and A and B will be in sync.

But consider the downsides:

  • You need an external polling system
  • You need to move all the compensation logic (for atomicity) in the message relay

i.e., your code becomes a living nightmare and even Claude will struggle to get it right.

With durable execution, you are guaranteed that your program will eventually complete.

So you can finally write:

  • WriteA()
  • WriteB()

And if the process crash between A and B, the durable execution engine will resume execution where it left of, that is, A, and proceed > to writing to B.

Of course, external systems need to be idempotent, because you get at-least-once execution (like the outbox pattern.)

Who's going to defend the outbox pattern here? :)

image

I liked this comment. Something to think about:

image

@rafaelpontezup
Copy link

rafaelpontezup commented Feb 4, 2026

Linkedin post: There are at least 7 ways to achieve idempotency -- by Yves Goeleven

A functional core must be idempotent

Idempotence is the property of certain operations whereby the operation can be applied multiple times > without changing the result.

There are at least 7 ways to achieve it

Natural idempotency

Many operations can be designed in a naturally idempotent way, 'Turn On The Light' is a command which > is idempotent by default, it will have the same effect no matter if the lights were on or off to > begin with.

Keep track of prior decisions

Prior to applying a state change to the system, keep track of the decision that you are going to > apply the state change using event sourcing (Light Turned On), so that any subsequent requests to > make the same state change can be derived from prior recorded decisions and ignored.

Side effect checks

Some times a dangerous approach, but often very useful in the real world, is to check for indirect > side effects of a command to determine if a it needs to be performed or not. When the temperature of > the light is over 65C, there is no need to 'Turn On The Light', you've probably done that already (or > your house may be on fire)

Versioning

A special case of the side effect check, applicable when working directly with state, is to add > versioning information to them (like timestamps or sequence numbers) while at the same time also > adding expected version information to any command that intends to alter the target. Whenever the > version information on the target exceeds the version information in the incoming command, you can > discard the command.

Identify and deduplicate

A fourth option is to identify each command with a unique identifier, and keep track of a list of > recently performed commands (command sourcing). If the identifier is on the list, you can discard it.

Partner state machines

Partner state machines are an approach introduced in the legendary paper 'Life Beyond Distributed > Transactions' by Pat Helland. In a conversation between multiple partners, using messaging, each > partner can maintain a state machine for it's communication with any other partner. The state machine > represents the progression of the relationship between them and avoids conflicts by allowing only for > valid state transitions. By checking the state of the conversation, it is possible to ensure each > command is executed only once.

Accept uncertainty

And finally, there is always the option, at least from a business perspective, to live with some > uncertainty and deal with duplicates in a business sense. Very often there are business processes in > place which can correct these scenarios. For example when you performed a duplicate payment, than you > can dedupe it by issuing a credit nota.

image

I tried to argue with him about the use of transactions as a way to achieve idempotency (comment link):

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment