Using Spring Transaction Hooks with TransactionSynchronizationManager for Asynchronous Kafka Messaging
This article explains how to leverage Spring's TransactionSynchronizationManager to detect active transactions and register synchronization callbacks that asynchronously send Kafka messages after transaction commit, illustrated with a practical payment‑system example and complete Java code snippets.
In this tutorial, a senior architect introduces a practical use case of Spring transaction hook functions for a payment system that needs to archive account flow records by publishing messages to Kafka.
The requirements include building a second‑party library (starter) that sends Kafka messages without interfering with the main business logic, supports asynchronous execution, and works correctly whether a transaction is active or not.
The core solution relies on TransactionSynchronizationManager , a static utility that tracks transaction synchronization via a thread‑local variable. By checking TransactionSynchronizationManager.isSynchronizationActive() , the code determines if a transaction is present.
If no transaction is active, the message is sent asynchronously immediately. If a transaction is active, a TransactionSynchronizationAdapter is registered via TransactionSynchronizationManager.registerSynchronization() , overriding afterCompletion to send the Kafka message only after the transaction commits, thus avoiding side effects on the main business flow.
private final ExecutorService executor = Executors.newSingleThreadExecutor();
public void sendLog() {
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
executor.submit(() -> {
try {
// send message to Kafka
} catch (Exception e) {
// handle exception
}
});
return;
}
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_COMMITTED) {
executor.submit(() -> {
try {
// send message to Kafka
} catch (Exception e) {
// handle exception
}
});
}
}
});
}The article also shows the internal implementation of isSynchronizationActive() and initSynchronization() , explaining that the synchronization flag is set when a transaction begins, and cleared when it ends.
Finally, the author warns that the synchronization logic depends on thread‑local storage, so developers must avoid switching threads between the transaction and the hook to ensure the callback executes correctly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.