micronaut-projects / micronaut-projects/micronaut-data
Use copy-on-write list for TransactionOperations synchronizations
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 482
- Forks
- 229
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 32
Description
Expected Behavior
A beforeCommit transaction synchronization can register an afterCommit one.
Actual Behaviour
java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1096)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1050)
at io.micronaut.transaction.impl.AbstractInternalTransaction.triggerBeforeCommit(AbstractInternalTransaction.java:78)
at io.micronaut.transaction.support.AbstractTransactionOperations.commitInternal(AbstractTransactionOperations.java:358)
at io.micronaut.transaction.support.AbstractTransactionOperations.executeTransactional(AbstractTransactionOperations.java:327)
at io.micronaut.transaction.support.AbstractTransactionOperations.doExecute(AbstractTransactionOperations.java:126)
at io.micronaut.transaction.support.AbstractPropagatedStatusTransactionOperations.execute(AbstractPropagatedStatusTransactionOperations.java:64)
at io.micronaut.transaction.interceptor.TransactionalInterceptor.intercept(TransactionalInterceptor.java:142)
at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:142)
at com.example.$HomeController$Definition$Intercepted.getUserCount(Unknown Source)
Small demo application: demo.zip
Steps To Reproduce
- Have a transaction synchronization to register another in any of the overridden methods
Small demo application: demo.zip
Environment Information
- Linux
- JDK 25
Example Application
Version
5.1.3
Suggestion
May I suggesting using CopyOnWriteArrayList? Here is the entire diff, because it is small:
diff --git a/data-tx/src/main/java/io/micronaut/transaction/impl/AbstractInternalTransaction.java b/data-tx/src/main/java/io/micronaut/transaction/impl/AbstractInternalTransaction.java
index cf79855071..b4eedf6885 100644
--- a/data-tx/src/main/java/io/micronaut/transaction/impl/AbstractInternalTransaction.java
+++ b/data-tx/src/main/java/io/micronaut/transaction/impl/AbstractInternalTransaction.java
@@ -23,7 +23,7 @@ import io.micronaut.transaction.support.TransactionSynchronization;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
-import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.List;
/**
@@ -142,7 +142,7 @@ public abstract class AbstractInternalTransaction<C> implements InternalTransact
@Override
public void registerInvocationSynchronization(@NonNull TransactionSynchronization synchronization) {
if (synchronizations == null) {
- synchronizations = new ArrayList<>(5);
+ synchronizations = new CopyOnWriteArrayList<>();
}
synchronizations.add(synchronization);
OrderUtil.sort(synchronizations);
In a way this is a continuation of issue #3983
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with data-tx/src/main/java/io/micronaut/transaction/impl/AbstractInternalTransaction.java and inspect how synchronizations are stored, iterated, and sorted. Reproduce the registration of an afterCommit synchronization from beforeCommit, then run the relevant data-tx tests or demo; done means registration during a callback no longer causes ConcurrentModificationException while synchronization ordering remains intact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- database
- Issue type
- Bug
- Difficulty
- 1/5
- Estimated time
- Under an hour
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100