Mastering Spring Transaction Hooks: Async Kafka Logging After Commit
This article explains how to use Spring's TransactionSynchronizationManager to detect active transactions and reliably send Kafka messages either immediately or after transaction commit, illustrated with a payment‑system use case and complete code examples.
Introduction
After covering Spring AOP and transaction basics, we now introduce a powerful technique: Spring transaction hook functions.
1. Case Background
In a payment system, each account's fund flow must be recorded. The CTO requires that every transaction log be archived by publishing a message to Kafka, which a separate archiving service consumes and stores in a database that only the archiving service can write to.
The workflow is simple: the payment service writes the flow and pushes a Kafka message for archiving.
2. Solution Design
To avoid interfering with the main business, the library must:
Be provided as a Spring Boot starter.
Send messages to Kafka using a native Kafka producer, not Spring's KafkaTemplate, to prevent conflicts.
Offer a simple API for easy integration.
Support transactional sending, ensuring messages are only sent after the surrounding transaction commits.
The core idea is to check whether a transaction is active. If not, send the message asynchronously immediately. If a transaction is active, register a synchronization callback that sends the message after the transaction commits.
Implementation Sketch
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 message to Kafka
} catch (Exception e) {
// log exception, notify developers
}
});
return;
}
// Transaction is active, register a synchronization
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_COMMITTED) {
// After commit, send message asynchronously
executor.submit(() -> {
try {
// send message to Kafka
} catch (Exception e) {
// log exception, notify developers
}
});
}
}
});
}3. TransactionSynchronizationManager in Action
The class TransactionSynchronizationManager is a utility that holds a thread‑local Set<TransactionSynchronization>. When a transaction starts, Spring calls initSynchronization(), which creates the thread‑local set:
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
public static boolean isSynchronizationActive() {
return (synchronizations.get() != null);
}When a transaction begins, Spring invokes TransactionSynchronizationManager.initSynchronization() to activate synchronization:
public static void initSynchronization() throws IllegalStateException {
if (isSynchronizationActive()) {
throw new IllegalStateException("Cannot activate transaction synchronization - already active");
}
synchronizations.set(new LinkedHashSet<>());
}To execute custom logic after commit, we register a TransactionSynchronizationAdapter via registerSynchronization():
public static void registerSynchronization(TransactionSynchronization synchronization)
throws IllegalStateException {
Assert.notNull(synchronization, "TransactionSynchronization must not be null");
if (!isSynchronizationActive()) {
throw new IllegalStateException("Transaction synchronization is not active");
}
synchronizations.get().add(synchronization);
}The overridden afterCompletion method receives the transaction status, allowing different actions for commit or rollback.
4. Summary
Both transaction detection and hook registration rely on a thread‑local variable, so the code must run in the same thread as the transaction. Using TransactionSynchronizationManager enables safe, asynchronous Kafka logging without affecting the primary business flow.
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.
