Mastering Spring Transaction Hooks: Safely Sync Kafka Messages After Commit
This article explains how to use Spring's TransactionSynchronizationManager to detect active transactions and register callbacks that asynchronously send Kafka messages only after a transaction successfully commits, illustrated with a payment‑system case study and complete code examples.
Introduction
After summarizing Spring AOP and transactions, the article introduces Spring transaction hook functions and demonstrates their proper usage through a real‑world case.
1. Case Background
In a payment system, each account's fund flow must be recorded and archived. The CTO requires that every flow be pushed as a message to Kafka, where an archival service consumes it and writes to a dedicated database.
The process is simple, and the team decides to build a second‑party library that encapsulates Kafka message sending.
2. Determining the Solution
Use Spring Boot and provide the library as a starter.
Implement Kafka producer directly instead of Spring’s KafkaTemplate to avoid conflicts.
Offer a simple API with low integration cost.
Ensure message sending participates in the surrounding transaction without affecting the main business.
The key challenge is to detect an active transaction and trigger the message after the transaction commits.
3. TransactionSynchronizationManager in Action
The class is a static utility that holds thread‑local transaction synchronizations. The following pseudo‑code shows how to check for an active transaction and register a synchronization that sends the Kafka message after commit.
private final ExecutorService executor = Executors.newSingleThreadExecutor();
public void sendLog() {
// No transaction: send asynchronously
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
executor.submit(() -> {
// send to Kafka
});
return;
}
// Transaction active: register callback
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_COMMITTED) {
executor.submit(() -> {
// send to Kafka
});
}
}
});
}The method TransactionSynchronizationManager.isSynchronizationActive() checks a ThreadLocal set that is populated by TransactionSynchronizationManager.initSynchronization() when a transaction begins.
Registering a TransactionSynchronizationAdapter adds a callback to the thread‑local set, allowing Spring to invoke afterCompletion after the transaction finishes.
3.1 Detecting an Active Transaction
Source code of isSynchronizationActive() simply returns whether the thread‑local synchronizations is non‑null.
3.2 Triggering Custom Logic After Commit
By registering a synchronization, the library can execute custom code in afterCompletion, distinguishing between commit and rollback via the status parameter.
4. Summary
Both transaction detection and hook registration rely on thread‑local variables, so the library must avoid thread switching to ensure the callbacks fire correctly.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
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.
