8000 Use transaction instead of thread · eXist-db/exist@5eef0e2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5eef0e2

Browse files
committed
Use transaction instead of thread
1 parent 16b072b commit 5eef0e2

File tree

2 files changed

+48
-22
lines changed

2 files changed

+48
-22
lines changed

exist-core/src/main/java/org/exist/collections/triggers/TriggerStatePerThread.java

+38-13
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
*/
2222
package org.exist.collections.triggers;
2323

24+
import org.exist.storage.txn.Txn;
25+
import org.exist.storage.txn.TxnListener;
2426
import org.exist.xmldb.XmldbURI;
2527

2628
import javax.annotation.Nullable;
@@ -30,6 +32,8 @@
3032
import java.util.Objects;
3133
import java.util.concurrent.ConcurrentHashMap;
3234
import java.util.concurrent.ConcurrentMap;
35+
import java.util.function.BiConsumer;
36+
import java.util.function.Consumer;
3337

3438
/**
3539
* Avoid infinite recursions in Triggers by preventing the same trigger
@@ -39,10 +43,10 @@
3943
*/
4044
public class TriggerStatePerThread {
4145

42-
private static final ConcurrentMap<Thread, Deque<TriggerState>> THREAD_LOCAL_STATES = new ConcurrentHashMap<>();
46+
private static final ConcurrentMap<Txn, Deque<TriggerState>> TRIGGER_STATES = new ConcurrentHashMap<>();
4347

44-
public static void setAndTest(final Trigger trigger, final TriggerPhase triggerPhase, final TriggerEvent triggerEvent, final XmldbURI src, final @Nullable XmldbURI dst) throws CyclicTriggerException {
45-
final Deque<TriggerState> states = getStates();
48+
public static void setAndTest(final Txn txn, final Trigger trigger, final TriggerPhase triggerPhase, final TriggerEvent triggerEvent, final XmldbURI src, final @Nullable XmldbURI dst) throws CyclicTriggerException {
49+
final Deque<TriggerState> states = getStates(txn);
4650

4751
if (states.isEmpty()) {
4852
if (triggerPhase != TriggerPhase.BEFORE) {
@@ -115,11 +119,11 @@ public CyclicTriggerException(final String message) {
115119
}
116120
}
117121

118-
public static void clearIfFinished(final TriggerPhase phase) {
122+
public static void clearIfFinished(final Txn txn, final TriggerPhase phase) {
119123
if (phase == TriggerPhase.AFTER) {
120124

121125
int depth = 0;
122-
final Deque<TriggerState> states = getStates();
126+
final Deque<TriggerState> states = getStates(txn);
123127
for (final Iterator<TriggerState> it = states.descendingIterator(); it.hasNext(); ) {
124128
final TriggerState state = it.next();
125129
switch (state.triggerPhase) {
@@ -135,24 +139,45 @@ public static void clearIfFinished(final TriggerPhase phase) {
135139
}
136140

137141
if (depth == 0) {
138-
clear();
142+
clear(txn);
139143
}
140144
}
141145
}
142146

143-
public static void clear() {
144-
THREAD_LOCAL_STATES.remove(Thread.currentThread());
147+
public static void clear(final Txn txn) {
148+
TRIGGER_STATES.remove(txn);
145149
}
146150

147-
public static boolean isEmpty() {
148-
return getStates().isEmpty();
151+
public static boolean isEmpty(final Txn txn) {
152+
return getStates(txn).isEmpty();
149153
}
150154

151-
private static Deque<TriggerState> getStates() {
152-
return THREAD_LOCAL_STATES.computeIfAbsent(Thread.currentThread(), thread -> new ArrayDeque<>());
155+
public static void forEach(BiConsumer<Txn, Deque<TriggerState>> action) {
156+
TRIGGER_STATES.forEach(action);
153157
}
154158

155-
record TriggerState(Trigger trigger, TriggerPhase triggerPhase, TriggerEvent triggerEvent, XmldbURI src,
159+
private static Deque<TriggerState> getStates(final Txn txn) {
160+
return TRIGGER_STATES.computeIfAbsent(txn, TriggerStatePerThread::initStates);
161+
}
162+
163+
private static Deque<TriggerState> initStates(final Txn txn) {
164+
txn.registerListener(new TransactionCleanUp(txn, TriggerStatePerThread::clear));
165+
return new ArrayDeque<>();
166+
}
167+
168+
public record TransactionCleanUp(Txn txn, Consumer<Txn> consumer) implements TxnListener {
169+
@Override
170+
public void commit() {
171+
consumer.accept(txn);
172+
}
173+
174+
@Override
175+
public void abort() {
176+
consumer.accept(txn);
177+
}
178+
}
179+
180+
public record TriggerState(Trigger trigger, TriggerPhase triggerPhase, TriggerEvent triggerEvent, XmldbURI src,
156181
@Nullable XmldbURI dst, boolean possiblyCyclic) {
157182

158183
@Override

exist-core/src/main/java/org/exist/collections/triggers/XQueryTrigger.java

+10-9
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ private void prepare(final TriggerEvent event, final DBBroker broker, final Txn
227227

228228
// avoid infinite recursion
229229
try {
230-
TriggerStatePerThread.setAndTest(this, TriggerPhase.BEFORE, event, src, dst);
230+
TriggerStatePerThread.setAndTest(transaction,this, TriggerPhase.BEFORE, event, src, dst);
231231
} catch (final TriggerStatePerThread.CyclicTriggerException e) {
232232
LOG.warn(e.getMessage());
233233
return;
@@ -241,7 +241,7 @@ private void prepare(final TriggerEvent event, final DBBroker broker, final Txn
241241
declareExternalVariables(context, TriggerPhase.BEFORE, event, src, dst, isCollection);
242242

243243
} catch (final XPathException | IOException | PermissionDeniedException e) {
244-
TriggerStatePerThread.clear();
244+
TriggerStatePerThread.clear(transaction);
245245
throw new TriggerException(PREPARE_EXCEPTION_MESSAGE, e);
246246
}
247247

@@ -255,7 +255,7 @@ private void prepare(final TriggerEvent event, final DBBroker broker, final Txn
255255
LOG.debug("Trigger fired for prepare");
256256
}
257257
} catch (final XPathException | PermissionDeniedException e) {
258-
TriggerStatePerThread.clear();
258+
TriggerStatePerThread.clear(transaction);
259259
throw new TriggerException(PREPARE_EXCEPTION_MESSAGE, e);
260260
} finally {
261261
context.runCleanupTasks();
@@ -271,7 +271,7 @@ private void finish(final TriggerEvent event, final DBBroker broker, final Txn t
271271

272272
// avoid infinite recursion
273273
try {
274-
TriggerStatePerThread.setAndTest(this, TriggerPhase.AFTER, event, src, dst);
274+
TriggerStatePerThread.setAndTest(transaction,this, TriggerPhase.AFTER, event, src, dst);
275275
} catch (final TriggerStatePerThread.CyclicTriggerException e) {
276276
LOG.warn(e.getMessage());
277277
return;
@@ -305,7 +305,7 @@ private void finish(final TriggerEvent event, final DBBroker broker, final Txn t
305305
context.runCleanupTasks();
306306
}
307307

308-
TriggerStatePerThread.clearIfFinished(TriggerPhase.AFTER);
308+
TriggerStatePerThread.clearIfFinished(transaction, TriggerPhase.AFTER);
309309

310310
if (LOG.isDebugEnabled()) {
311311
LOG.debug("Trigger fired for finish");
@@ -393,10 +393,11 @@ private CompiledXQuery getScript(final DBBroker broker, final Txn transaction) t
393393
}
394394

395395
private void execute(final TriggerPhase phase, final TriggerEvent event, final DBBroker broker, final Txn transaction, final QName functionName, final XmldbURI src, final XmldbURI dst) throws TriggerException {
396+
System.err.format("phase: %s, event: %s, tx: %s, thread: %s", phase, event, transaction, Thread.currentThread()).println();
396397

397398
// avoid infinite recursion
398399
try {
399-
TriggerStatePerThread.setAndTest(this, phase, event, src, dst);
400+
TriggerStatePerThread.setAndTest(transaction, this, phase, event, src, dst);
400401
} catch (final TriggerStatePerThread.CyclicTriggerException e) {
401402
LOG.warn("Skipping Trigger: {}", e.getMessage());
402403
return;
@@ -414,7 +415,7 @@ private void execute(final TriggerPhase phase, final TriggerEvent event, final D
414415
return;
415416
}
416417
} catch (final TriggerException e) {
417-
TriggerStatePerThread.clear();
418+
TriggerStatePerThread.clear(transaction);
418419
throw e;
419420
}
420421

@@ -454,14 +455,14 @@ private void execute(final TriggerPhase phase, final TriggerEvent event, final D
454455
}
455456
}
456457

457-
TriggerStatePerThread.clear();
458+
TriggerStatePerThread.clear(transaction);
458459
throw new TriggerException(PREPARE_EXCEPTION_MESSAGE, e);
459460
} finally {
460461
compiledQuery.reset();
461462
context.runCleanupTasks();
462463
}
463464

464-
TriggerStatePerThread.clearIfFinished(phase);
465+
TriggerStatePerThread.clearIfFinished(transaction, phase);
465466

466467
if (LOG.isDebugEnabled()) {
467468
if (phase == TriggerPhase.AFTER) {

0 commit comments

Comments
 (0)
0