What Is an XA Transaction?

An XA transaction is a distributed transaction that coordinates a single unit of work across two or more independent resources — such as a relational database, a message broker, and a cache — so that all of them either commit together or roll back together. It takes the all-or-nothing guarantee of a local transaction and extends it across systems that would otherwise commit independently.

The name comes from the X/Open XA specification, the industry standard for distributed transaction processing. XA defines the contract between a central transaction manager, which coordinates the overall transaction, and the resource managers — the databases, queues, and other services that each hold part of the data being changed.

How an XA Transaction Works

XA relies on a protocol called two-phase commit (2PC), which splits the commit into a vote and a decision:

  1. Prepare phase. The transaction manager asks every enlisted resource to prepare to commit. Each resource does the work, persists enough to guarantee it could commit, and then votes — "ready" or "abort."
  2. Commit phase. If every resource voted ready, the transaction manager tells them all to commit. If even one voted abort (or failed to answer), it tells them all to roll back.

Because no resource finalizes its changes until every participant has agreed in the prepare phase, the group stays consistent: you never end up with the database committed but the message never sent, or the cache updated but the database unchanged.

XA Transactions in Java

In Java, XA is exposed through the Jakarta Transactions API (JTA), formerly the Java Transaction API. A JTA transaction manager coordinates resources that implement the XAResource interface. Application servers and standalone transaction managers (for example, Narayana or Atomikos) drive the two-phase commit, while each participating resource contributes its own XAResource. You enlist each resource in a global transaction, perform your work, and let the transaction manager commit or roll back the whole set.

When to Use XA — and the Trade-Offs

XA transactions are the right tool when a single business operation spans multiple systems and partial completion is unacceptable: debiting a database while publishing an event, or updating a cache and a system of record as one atomic step.

That guarantee has a cost. Two-phase commit is synchronous and blocking — resources hold locks between the prepare and commit phases — which adds latency and reduces throughput. A failure of the coordinator can leave transactions "in doubt" until recovery completes. For these reasons, many high-scale systems prefer looser patterns such as the saga or outbox pattern, and reserve XA for the cases where strict cross-resource atomicity genuinely matters.

XA Transactions with Valkey and Redis

By default, Valkey and Redis are not part of a distributed transaction — their native transactions are limited to commands on a single server and offer no rollback after execution. Redisson, the Valkey and Redis Java client, closes that gap by providing an RXAResource implementation, so a Valkey or Redis operation can participate as a full resource in a JTA-managed XA transaction alongside a database or message queue. You obtain the resource with redisson.getXAResource(), enlist it in the global transaction, and let the transaction manager coordinate the two-phase commit across every system involved.

This capability is part of Redisson PRO. For a hands-on walkthrough with code, see How to Manage Transactions in Valkey and Redis on Java.