Mastering Spring Transaction Hooks: Send Kafka Messages Only After Commit
This article explains how to use Spring's TransactionSynchronizationManager to detect active transactions and register callbacks that asynchronously send Kafka messages after a transaction successfully commits, ensuring data consistency in payment‑system archiving scenarios.
After previous summaries of Spring AOP and transactions, the article introduces Spring transaction hook functions to handle a payment‑system scenario where each fund flow must be archived by sending a message to Kafka after the transaction commits.
Case Background
In a payment system, every account's fund flow needs to be recorded. The CTO requires that each flow be archived by pushing a message to Kafka, which a separate archiving service consumes and stores in a database that only the archiving service can write to.
To support multiple business units, the team decides to develop a secondary library (a Spring Boot starter) that sends messages to Kafka, avoiding conflicts with existing KafkaTemplate usage.
Design Considerations
Provide the library as a Spring Boot starter.
Implement the Kafka producer directly instead of using Spring's KafkaTemplate to prevent integration conflicts.
Expose a simple API so business code can adopt it with minimal learning cost.
Ensure message sending is transactional and does not affect the main business flow.
Transaction Hook Implementation
The core solution uses TransactionSynchronizationManager to determine whether a transaction is active and to register a synchronization callback that runs after the transaction commits.
private final ExecutorService executor = Executors.newSingleThreadExecutor();
public void sendLog() {
// Check if a transaction is active
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
// No transaction: send message asynchronously
executor.submit(() -> {
try {
// send to Kafka
} catch (Exception e) {
// log 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 error
}
});
}
}
});
}The method TransactionSynchronizationManager.isSynchronizationActive() checks a thread‑local ThreadLocal<Set<TransactionSynchronization>> that Spring populates when a transaction begins via initSynchronization(). If it returns true, a transaction is in progress.
To execute custom logic after commit, the code registers a TransactionSynchronizationAdapter via registerSynchronization(). Spring invokes afterCompletion with the transaction status, allowing the message to be sent only when the transaction is successfully committed.
Important Notes
The synchronization relies on thread‑local storage; switching threads before the callback runs will cause the hook to be ineffective.
Asynchronous execution ensures the main business flow is not blocked.
Both detection and callback registration are encapsulated inside the secondary library, keeping integration simple for consumers.
Conclusion
By leveraging TransactionSynchronizationManager, developers can safely send Kafka messages only after a Spring transaction commits, preserving data consistency without impacting primary business logic. The approach must be used within the same thread context to avoid missed callbacks.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
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.
