Commit 394bf92c authored by Matija Obreza's avatar Matija Obreza

Using a ThreadLocal stack for audit logs entries for individual (sub) transactions

parent dcb5da8a
...@@ -28,8 +28,8 @@ import java.util.Date; ...@@ -28,8 +28,8 @@ import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Stack;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
...@@ -115,18 +115,12 @@ public class AuditTrailInterceptor extends EmptyInterceptor implements Initializ ...@@ -115,18 +115,12 @@ public class AuditTrailInterceptor extends EmptyInterceptor implements Initializ
/** The time formatter. */ /** The time formatter. */
private Format timeFormatter; private Format timeFormatter;
/** Place to store audit logs before storing them to db */
private static final ThreadLocal<Set<TransactionAuditLog>> auditLogs = new ThreadLocal<Set<TransactionAuditLog>>() {
@Override
protected Set<TransactionAuditLog> initialValue() {
return new HashSet<>();
};
};
private static final ThreadLocal<AtomicLong> transactionStack = new ThreadLocal<AtomicLong>() { /** Place to store audit logs before storing them to db */
private static final ThreadLocal<Stack<Set<TransactionAuditLog>>> auditLogStack = new ThreadLocal<Stack<Set<TransactionAuditLog>>>() {
@Override @Override
protected AtomicLong initialValue() { protected Stack<Set<TransactionAuditLog>> initialValue() {
return new AtomicLong(0l); return new Stack<Set<TransactionAuditLog>>();
}; };
}; };
...@@ -599,12 +593,12 @@ public class AuditTrailInterceptor extends EmptyInterceptor implements Initializ ...@@ -599,12 +593,12 @@ public class AuditTrailInterceptor extends EmptyInterceptor implements Initializ
return; return;
} }
TransactionAuditLog change = auditTrailService.auditLogEntry(AuditAction.UPDATE, entity, id, propertyName, previousState, currentState, referencedEntity); TransactionAuditLog change = auditTrailService.auditLogEntry(AuditAction.UPDATE, entity, id, propertyName, previousState, currentState, referencedEntity);
if (auditLogs.get().remove(change)) { if (auditLogStack.get().peek().remove(change)) {
LOG.trace("Replacing exising changelog {}", change); LOG.trace("Replacing exising changelog {}", change);
} else { } else {
LOG.trace("Adding new changelog {}", change); LOG.trace("Adding new changelog {}", change);
} }
auditLogs.get().add(change); auditLogStack.get().peek().add(change);
} }
/** /**
...@@ -619,12 +613,12 @@ public class AuditTrailInterceptor extends EmptyInterceptor implements Initializ ...@@ -619,12 +613,12 @@ public class AuditTrailInterceptor extends EmptyInterceptor implements Initializ
*/ */
private void recordDelete(final Object entity, final Long id, final String propertyName, final String state, final Class<?> referencedEntity) { private void recordDelete(final Object entity, final Long id, final String propertyName, final String state, final Class<?> referencedEntity) {
TransactionAuditLog delete = auditTrailService.auditLogEntry(AuditAction.DELETE, entity, id, propertyName, state, null, referencedEntity); TransactionAuditLog delete = auditTrailService.auditLogEntry(AuditAction.DELETE, entity, id, propertyName, state, null, referencedEntity);
if (auditLogs.get().remove(delete)) { if (auditLogStack.get().peek().remove(delete)) {
LOG.trace("Replacing exising changelog {}", delete); LOG.trace("Replacing exising changelog {}", delete);
} else { } else {
LOG.trace("Adding new changelog {}", delete); LOG.trace("Adding new changelog {}", delete);
} }
auditLogs.get().add(delete); auditLogStack.get().peek().add(delete);
} }
/** /**
...@@ -684,8 +678,10 @@ public class AuditTrailInterceptor extends EmptyInterceptor implements Initializ ...@@ -684,8 +678,10 @@ public class AuditTrailInterceptor extends EmptyInterceptor implements Initializ
@Override @Override
public void afterTransactionBegin(final Transaction tx) { public void afterTransactionBegin(final Transaction tx) {
transactionStack.get().getAndIncrement(); // Push new auditLogs to the stack
LOG.trace("Starting transaction level={}", transactionStack.get().get()); auditLogStack.get().push(new HashSet<>());
LOG.trace("Starting transaction level={}", auditLogStack.get().size());
// tx.registerSynchronization(new Synchronization() { // tx.registerSynchronization(new Synchronization() {
// //
...@@ -705,19 +701,25 @@ public class AuditTrailInterceptor extends EmptyInterceptor implements Initializ ...@@ -705,19 +701,25 @@ public class AuditTrailInterceptor extends EmptyInterceptor implements Initializ
// Called before a transaction is committed (but not before rollback). // Called before a transaction is committed (but not before rollback).
@Override @Override
public void beforeTransactionCompletion(final Transaction tx) { public void beforeTransactionCompletion(final Transaction tx) {
final long level = transactionStack.get().get();
LOG.trace("beforeTransactionCompletion transaction level={} auditlogs={}", level, auditLogs.get().size()); final long level = auditLogStack.get().size();
Set<TransactionAuditLog> currentAuditLogs = auditLogStack.get().pop(); // pop
LOG.trace("beforeTransactionCompletion transaction level={} auditlogs={}", level, currentAuditLogs.size());
// LOG.trace("Before transaction completion status={} tx={}", // LOG.trace("Before transaction completion status={} tx={}",
// tx.getLocalStatus(), tx); // tx.getLocalStatus(), tx);
if (level == 1 && auditLogs.get().size() > 0) { if (currentAuditLogs.size() > 0) {
LOG.trace("We have {} auditlogs", auditLogs.get().size()); LOG.trace("We have {} auditlogs", currentAuditLogs.size());
auditLogs.get().stream().forEach(auditLog -> { currentAuditLogs.stream().forEach(auditLog -> {
LOG.debug("Audit log to save: {}", auditLog); LOG.debug("Audit log to save: {}", auditLog);
}); });
this.auditTrailService.addAuditLogs(auditLogs.get()); if (tx.wasRolledBack()) {
auditLogs.get().clear(); LOG.warn("Transaction was rolled back. Audit logs likely won't be persisted");
}
this.auditTrailService.addAuditLogs(currentAuditLogs);
currentAuditLogs.clear(); // not required anymore
} }
super.beforeTransactionCompletion(tx); super.beforeTransactionCompletion(tx);
...@@ -737,18 +739,15 @@ public class AuditTrailInterceptor extends EmptyInterceptor implements Initializ ...@@ -737,18 +739,15 @@ public class AuditTrailInterceptor extends EmptyInterceptor implements Initializ
// Called after a transaction is committed or rolled back. // Called after a transaction is committed or rolled back.
@Override @Override
public void afterTransactionCompletion(Transaction tx) { public void afterTransactionCompletion(Transaction tx) {
final long level = transactionStack.get().decrementAndGet();
LOG.trace("afterTransactionCompletion transaction level={} auditlogs={}", level, auditLogs.get().size()); final long level = auditLogStack.get().size();
LOG.trace("afterTransactionCompletion transaction level={}", level);
if (tx.wasCommitted()) { if (tx.wasCommitted()) {
LOG.trace("Transaction was committed, have {} audit logs", auditLogs.get().size()); LOG.trace("Transaction was committed, level={}", level);
} else if (tx.wasRolledBack()) { } else if (tx.wasRolledBack()) {
LOG.trace("Transaction was rolled back, have {} audit logs", auditLogs.get().size()); LOG.trace("Transaction was rolled back, level={}", level);
} }
if (level == 0) {
// clear pending logs if back to level 0
auditLogs.get().clear();
}
super.afterTransactionCompletion(tx); super.afterTransactionCompletion(tx);
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment