Using Spring Transaction Hooks to Send Kafka Messages After Transaction Commit
This article explains how to leverage Spring's TransactionSynchronizationManager to detect active transactions and register synchronization callbacks so that Kafka messages are sent asynchronously only after a transaction successfully commits, illustrated with a Spring Boot starter example.
After reviewing Spring AOP and transaction basics, the article introduces the concept of Spring transaction hook functions and demonstrates their practical use in a payment system scenario where each account's financial ledger must be archived by sending messages to Kafka.
Case Background : The payment system needs to record every debit and credit operation, and the CTO requires that each ledger entry be pushed as a Kafka message for a separate archiving service that has exclusive write access to the archive database.
The proposed solution is to create a second‑party library (starter) that encapsulates Kafka message production and integrates with Spring transactions, ensuring that message sending does not interfere with the main business flow.
Key Requirements :
Provide the library as a Spring Boot starter.
Implement Kafka producer directly instead of using Spring's KafkaTemplate to avoid conflicts.
Offer a simple API for easy adoption.
Support transactional behavior: if the surrounding method runs inside a transaction, the message should be sent only after the transaction commits.
Solution Overview : Use TransactionSynchronizationManager to check for an active transaction and register a synchronization callback that triggers the Kafka send after commit.
private final ExecutorService executor = Executors.newSingleThreadExecutor();
public void sendLog() {
// Check if a transaction is active
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
// No transaction – send asynchronously immediately
executor.submit(() -> {
try {
// send message to Kafka
} catch (Exception e) {
// log exception, notify developers
}
});
return;
}
// Transaction active – register a synchronization callback
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_COMMITTED) {
// After commit, send asynchronously
executor.submit(() -> {
try {
// send message to Kafka
} catch (Exception e) {
// log exception, notify developers
}
});
}
}
});
}The method TransactionSynchronizationManager.isSynchronizationActive() simply checks whether a thread‑local ThreadLocal<Set> synchronizations is non‑null, which is set by the transaction manager when a transaction begins via initSynchronization():
public static void initSynchronization() throws IllegalStateException {
if (isSynchronizationActive()) {
throw new IllegalStateException("Cannot activate transaction synchronization - already active");
}
synchronizations.set(new LinkedHashSet<>());
}When a transaction is active, registerSynchronization() adds a TransactionSynchronization implementation to the thread‑local set, allowing Spring to invoke its callbacks at various transaction phases. By overriding afterCompletion(int status), the library can react specifically to the STATUS_COMMITTED case and send the Kafka message only after a successful commit.
Finally, the article warns that because the synchronization state is stored in a thread‑local variable, the code must not switch threads between the transaction and the hook execution; otherwise the hook would not fire.
Conclusion : Using TransactionSynchronizationManager provides a reliable way to determine transaction presence and to execute custom logic—such as asynchronous Kafka publishing—exactly after a transaction commits, without impacting the primary business flow.
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
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.
