Skip to content

GH-4273: Fix ConcurrentModificationException in `getAssignedPartiti…#4274

Merged
artembilan merged 1 commit into
spring-projects:mainfrom
sobychacko:gh-4273
Feb 10, 2026
Merged

GH-4273: Fix ConcurrentModificationException in `getAssignedPartiti…#4274
artembilan merged 1 commit into
spring-projects:mainfrom
sobychacko:gh-4273

Conversation

@sobychacko

Copy link
Copy Markdown
Contributor

…ons()`

Fixes #4273

The previous fix (GH-3726) wrapped definedPartitions and assignedPartitions with Collections.synchronizedMap/Set, but this only synchronizes individual operations. Per Java documentation, manual synchronization is required when iterating over synchronized collection views.

The method was returning unmodifiable views of these collections, which callers would then iterate outside any synchronization. When partitions were reassigned concurrently (e.g., during rebalance), the iteration would fail with ConcurrentModificationException.

The fix synchronizes on the underlying collection while creating a snapshot copy. This ensures the iteration (during copy) is protected, and callers receive an independent snapshot safe to use without holding any locks.

Additionally, wrapUp() now explicitly passes an empty collection to onPartitionsRevoked() during shutdown. Previously, this accidentally worked because the mutable view would become empty after unsubscribe() cleared the underlying collection. Since getAssignedPartitions() now returns a snapshot, we must explicitly pass an empty collection to preserve the shutdown signal semantics. The actual partition cleanup already occurs via the rebalance listener during unsubscribe().

…AssignedPartitions()`

Fixes spring-projects#4273

The previous fix (spring-projectsGH-3726) wrapped `definedPartitions` and `assignedPartitions`
with `Collections.synchronizedMap/Set`, but this only synchronizes individual
operations. Per Java documentation, manual synchronization is required when
iterating over synchronized collection views.

The method was returning unmodifiable views of these collections, which
callers would then iterate outside any synchronization. When partitions
were reassigned concurrently (e.g., during rebalance), the iteration would
fail with `ConcurrentModificationException`.

The fix synchronizes on the underlying collection while creating a snapshot
copy. This ensures the iteration (during copy) is protected, and callers
receive an independent snapshot safe to use without holding any locks.

Additionally, `wrapUp()` now explicitly passes an empty collection to
`onPartitionsRevoked()` during shutdown. Previously, this accidentally
worked because the mutable view would become empty after `unsubscribe()`
cleared the underlying collection. Since `getAssignedPartitions()` now
returns a snapshot, we must explicitly pass an empty collection to preserve
the shutdown signal semantics. The actual partition cleanup already occurs
via the rebalance listener during `unsubscribe()`.

Signed-off-by: Soby Chacko <soby.chacko@broadcom.com>
@artembilan artembilan merged commit 84a49eb into spring-projects:main Feb 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KafkaMessageListenerContainer.getAssignedPartitions can throw ConcurrentModificationException

2 participants