The moment you give each service its own database, the comfortable guarantee of a single ACID transaction disappears — and "how do you keep data consistent across services?" becomes one of the hardest and most-asked microservices interview questions. It pulls in two-phase commit, sagas, CAP, and eventual consistency all at once. This page covers the distributed-transaction questions and the follow-ups that separate memorized answers from real understanding.
Why can't a normal ACID transaction span microservices?
Because each service owns a separate database with no shared transaction manager, so nothing can atomically commit or roll back all of them together — ACID guarantees stop at a single database's boundary. A @Transactional method covers one database, not three services.
This is the root of the entire topic. Database-per-service buys independent evolution and scaling, and the price is that cross-service consistency must be engineered rather than assumed. Every pattern below is a different way to pay that price.
What is two-phase commit, and why do most teams avoid it?
2PC uses a coordinator: phase one asks every participant to prepare (locking resources and promising it can commit); phase two tells them all to commit or abort. It gives atomicity but holds locks across all services for the whole transaction, scales poorly, and if the coordinator dies after prepare, participants are blocked holding locks.
The blocking failure mode is the killer. A participant that has promised to commit cannot unilaterally proceed or abort, so a coordinator crash can freeze resources across the system. That, plus the cross-service locking, is why high-scale microservices architectures almost always choose sagas instead.
Interview note: Follow-up: "is 2PC ever the right choice?" Occasionally, for a small number of services needing strict atomicity with low throughput, sometimes via XA transactions. But it is the exception, and you should say why it does not scale.
How does a saga solve this instead?
A saga replaces one distributed transaction with a sequence of local transactions, each committing independently; if a later step fails, compensating transactions semantically undo the earlier ones. No cross-service locks, so the system stays available — at the cost of eventual rather than immediate consistency.
The saga is the practical default. You accept that the system passes through temporarily inconsistent states and design compensations (refund, release, cancel) to recover from failures. Interviewers want you to connect the abstract need — consistency without ACID — to this concrete mechanism.
Instead of one atomic transaction across services:
BEGIN; debit(A); credit(B); COMMIT; // impossible across services
A saga does:
local: debit(A) -> event
local: credit(B) -> event
on failure of credit(B): compensate -> refund(A) // eventual consistency
What is eventual consistency, and how do you make it safe?
Eventual consistency means parts of the system may briefly disagree after an update but converge to a consistent state given time and no further updates. You make it safe with idempotency (so retries and duplicates do not corrupt state), versioning (to detect stale data), and UX that sets the right expectation (an order shown as "processing").
The senior nuance is that eventual consistency is a deliberate choice, not sloppiness. For a product catalog, a few seconds of staleness is invisible and worth the availability. For an account balance shown as final, you may need read-your-writes or stronger guarantees. Matching the consistency model to the business requirement is the skill being tested.
How does the CAP theorem force the decision?
Under a network partition you can guarantee consistency or availability, not both — so distributed transactions become a concrete CAP choice. 2PC leans CP (consistent but unavailable during partitions/coordinator failure); sagas lean AP (available, eventually consistent).
Apply CAP rather than recite it. The strong answer names a feature and justifies the choice: "for placing an order we favor availability and reconcile with events; for the final payment capture we favor consistency because a double charge is unacceptable." That per-use-case reasoning is what interviewers reward.
How do you make a cross-service operation safe to retry?
Attach an idempotency key to the request so the service recognizes a duplicate and returns the original result instead of applying the effect twice. Networks retry, and without idempotency a retried payment charges the customer twice.
public PaymentResult charge(ChargeRequest req) {
return existing.findByKey(req.idempotencyKey()) // seen this key before?
.orElseGet(() -> {
PaymentResult r = processCharge(req);
existing.save(req.idempotencyKey(), r); // record for future duplicates
return r;
});
}
Idempotency keys are the standard way real payment systems make "charge once" survive an unreliable network. Naming this pattern signals production experience.
How do you publish an event and commit a database change reliably?
Use the transactional outbox pattern: write the event to an outbox table inside the same local transaction as the business change, and have a relay publish it to the broker afterward. This solves the dual-write problem where writing to the DB and the broker as two steps can lose or duplicate events on a crash.
The outbox is the glue between the local transaction and the event-driven mechanics a saga relies on. Without it, the "publish an event on success" step is itself unreliable, undermining the whole consistency story.
How do you reconcile when something still goes wrong?
Run reconciliation jobs and audits that compare state across services and repair drift, and use a dead-letter queue plus alerting for messages and compensations that cannot complete automatically. Eventual consistency in the real world includes a safety net of reconciliation and, occasionally, human intervention.
Acknowledging this is a strength — it shows you know distributed consistency is an operational property maintained over time, not a one-shot guarantee.
What interviewers really test
Distributed transaction questions test whether you understand that consistency across services is engineered, not free — and whether you can choose the right model for the stakes. The candidates who pass reject 2PC for scale reasons, reach for sagas and eventual consistency, and volunteer idempotency and the outbox as the supporting mechanics. Be ready to apply CAP to a specific feature rather than quote it.
This topic is the theory behind the saga pattern questions and depends on the delivery mechanics in the event-driven architecture question set — pair this page with both. Build the foundation with the microservices learning path, and practice defending a consistency choice under follow-up in a mock interview.
Frequently Asked Questions
Why can't you use a normal ACID transaction across microservices?
What is two-phase commit and why is it avoided?
What is eventual consistency?
How does the CAP theorem apply to distributed transactions?
What is an idempotency key?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — Explore the Java Full Stack program

