[ 
https://issues.apache.org/jira/browse/FLINK-40543?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18114815#comment-18114815
 ] 

Qiu Yanjun commented on FLINK-40543:
------------------------------------

I would like to work on this issue. Could a committer please assign it to me?

> Race condition and ConcurrentModificationException in 
> MockSplitEnumeratorContext.getSentSourceEvent()
> -----------------------------------------------------------------------------------------------------
>
>                 Key: FLINK-40543
>                 URL: https://issues.apache.org/jira/browse/FLINK-40543
>             Project: Flink
>          Issue Type: Bug
>          Components: API / Core
>            Reporter: Sylwester Lachiewicz
>            Priority: Minor
>
> In `org.apache.flink.api.connector.source.mocks.MockSplitEnumeratorContext`:
> 1. `sendEventToSourceReader(int, SourceEvent)` modifies `sentSourceEvent`:
>    - Runs on `mainExecutor` (or current thread if already on the main thread):
>      `sentSourceEvent.computeIfAbsent(subtaskId, k -> new 
> ArrayList<>()).add(event)`
>    - `sentSourceEvent` is a plain `java.util.HashMap` containing standard 
> `java.util.ArrayList` instances.
> 2. `getSentSourceEvent()` reads and copies `sentSourceEvent`:
>    - Runs on `workerExecutor`:
>      `workerExecutor.submit(() -> new HashMap<>(sentSourceEvent)).get()`
>    - `workerExecutor` and `mainExecutor` are two distinct single-threaded 
> executors created in the constructor.
>    - Consequently, `new HashMap<>(sentSourceEvent)` executes concurrently 
> with `sentSourceEvent.computeIfAbsent(...)` on `mainExecutor` without 
> synchronization, occasionally throwing `ConcurrentModificationException` 
> (wrapped in `ExecutionException`).
>    - In addition, `new HashMap<>(sentSourceEvent)` is only a shallow copy: 
> the inner `List<SourceEvent>` instances are shared. Callers streaming or 
> iterating these lists (e.g. during test assertions or polling) experience 
> `ConcurrentModificationException` if `mainExecutor` concurrently appends new 
> events.
> ### Recommended Fix:
> - **Do NOT simply delegate the copy to `mainExecutor`**: Unit tests 
> frequently block or wait on the coordinator thread (`mainExecutor`) with 
> latches to assert intermediate states. Executing 
> `mainExecutor.submit(...).get()` inside `getSentSourceEvent()` would cause 
> test deadlocks in those scenarios.
> - **Use thread-safe / snapshot-isolated collections**:
>   - Store subtask event lists using a concurrent structure, e.g.:
>     `ConcurrentMap<Integer, CopyOnWriteArrayList<SourceEvent>> 
> sentSourceEvent = new ConcurrentHashMap<>();`
>     - Lock-free, wait-free reads; iterators operate on immutable array 
> snapshots (completely immune to `ConcurrentModificationException`).
>     - $O(N)$ write cost is negligible because unit tests only send a handful 
> of events ($N < 100$).
>   - Or use `ConcurrentHashMap<Integer, List<SourceEvent>>` and synchronize on 
> the individual subtask list when taking an `ArrayList` snapshot during 
> `getSentSourceEvent()`.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to