Backend Development 12 min read

Using Spring TransactionSynchronizationManager for Transaction Hooks and Asynchronous Kafka Messaging

This article explains how to leverage Spring's TransactionSynchronizationManager to detect active transactions, register synchronization callbacks, and asynchronously send Kafka messages after transaction commit, providing a starter library design for Java backend services that need reliable transaction‑aware messaging.

Top Architect
Top Architect
Top Architect
Using Spring TransactionSynchronizationManager for Transaction Hooks and Asynchronous Kafka Messaging

In this tutorial the author, a senior architect, introduces a practical technique for handling transaction‑aware messaging in Spring Boot applications. The focus is on using Spring's TransactionSynchronizationManager to determine whether a transaction is active and to register a synchronization callback that sends a Kafka message only after the transaction successfully commits.

The business scenario involves a payment system that must archive account fund flow records. The requirement is to push these records to Kafka as messages, which a separate archiving service consumes and persists, while ensuring the main service's write operations remain unaffected.

To meet this, the author proposes a lightweight second‑party library (starter) that provides a simple API for sending logs. The library must:

Be packaged as a Spring Boot starter.

Create its own Kafka producer instead of using the application's KafkaTemplate to avoid conflicts.

Offer an easy‑to‑use API with minimal integration effort.

Support transactional sending so that messages are dispatched only after the surrounding transaction commits.

The core implementation uses an ExecutorService for asynchronous execution and checks transaction status with TransactionSynchronizationManager.isSynchronizationActive() . If no transaction is present, the message is sent immediately. If a transaction exists, a TransactionSynchronizationAdapter is registered, overriding afterCompletion to send the message after a successful commit:

private final ExecutorService executor = Executors.newSingleThreadExecutor();

public void sendLog() {
    // Determine if a transaction is active
    if (!TransactionSynchronizationManager.isSynchronizationActive()) {
        // No transaction – send asynchronously
        executor.submit(() -> {
            try {
                // send to Kafka
            } catch (Exception e) {
                // log/notify error
            }
        });
        return;
    }
    // Transaction active – register a synchronization callback
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
        @Override
        public void afterCompletion(int status) {
            if (status == TransactionSynchronization.STATUS_COMMITTED) {
                executor.submit(() -> {
                    try {
                        // send to Kafka
                    } catch (Exception e) {
                        // log/notify error
                    }
                });
            }
        }
    });
}

The article then dives into the internals of TransactionSynchronizationManager , showing how it uses a ThreadLocal<Set<TransactionSynchronization>> to track synchronizations. The method isSynchronizationActive() simply checks whether this thread‑local set is non‑null, indicating an active transaction. The synchronization is initialized by the transaction manager via TransactionSynchronizationManager.initSynchronization() , which sets a new LinkedHashSet for the current thread.

By registering a custom TransactionSynchronizationAdapter , developers can execute arbitrary logic after transaction completion, distinguishing between commit and rollback using the status code passed to afterCompletion . This enables reliable, asynchronous Kafka publishing without affecting the primary business flow.

Finally, the author warns that the callback relies on thread‑local state, so developers must avoid switching threads between the transaction and the callback execution, otherwise the synchronization will not be triggered.

backendJavatransactionSpringAsynchronousKafkaTransactionSynchronizationManager
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.