You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Use primitive int for concurrency in factory (consistent with phase field)
- Remove unnecessary `getConcurrency()` getter (only used in trivial tests)
- Use `HashMap` instead of `ConcurrentHashMap` in metrics() (already inside lock)
- Use `CompletableFuture.allOf()` for cleaner shutdown coordination
- Remove debug logging from tests (unnecessary noise in CI/CD)
- Remove thread tracking from concurrency tests (over-complicates assertions)
Clarify documentation based on KIP-932 specifications:
- Add explicit note that concurrency is additive across application instances
- Replace high-level distribution description with precise KIP-932 details
- Document pull-based model, acquisition locks, and batch behavior
- Explain `max.poll.records` as soft limit with complete batch preference
- Set accurate expectations about broker-controlled record distribution
Signed-off-by: Soby Chacko <[email protected]>
Copy file name to clipboardExpand all lines: spring-kafka-docs/src/main/antora/modules/ROOT/pages/kafka/kafka-queues.adoc
+37-10Lines changed: 37 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -160,6 +160,23 @@ Each thread runs its own `ShareConsumer` instance that participates in the same
160
160
Unlike traditional consumer groups where concurrency involves partition distribution, share consumers leverage Kafka's record-level distribution at the broker.
161
161
This means multiple consumer threads in the same container work together as part of the share group, with the Kafka broker distributing records across all consumer instances.
162
162
163
+
[IMPORTANT]
164
+
====
165
+
**Concurrency is Additive Across Application Instances**
166
+
167
+
From the share group's perspective, each `ShareConsumer` instance is an independent member, regardless of where it runs.
168
+
Setting `concurrency=3` in a single container creates 3 share group members.
169
+
If you run multiple application instances with the same share group ID, all their consumer threads combine into one pool.
170
+
171
+
For example:
172
+
* Application Instance 1: `concurrency=3` → 3 share group members
173
+
* Application Instance 2: `concurrency=3` → 3 share group members
174
+
* **Total**: 6 share group members available for the broker to distribute records to
175
+
176
+
This means setting `concurrency=5` in a single container is operationally equivalent to running 5 separate application instances with `concurrency=1` each (all using the same `group.id`).
177
+
The Kafka broker treats all consumer instances equally and distributes records across the entire pool.
178
+
====
179
+
163
180
==== Configuring Concurrency Programmatically
164
181
165
182
[source,java]
@@ -269,21 +286,31 @@ public void processOrder(ConsumerRecord<String, String> record, ShareAcknowledgm
269
286
270
287
[NOTE]
271
288
====
272
-
**Work Distribution Behavior:**
289
+
**Record Acquisition and Distribution Behavior:**
290
+
291
+
Share consumers use a pull-based model where each consumer thread calls `poll()` to fetch records from the broker.
292
+
When a consumer polls, the broker's share-partition leader:
293
+
294
+
* Selects records in "Available" state
295
+
* Moves them to "Acquired" state with a time-limited acquisition lock (default 30 seconds, configurable via `group.share.record.lock.duration.ms`)
296
+
* Prefers to return complete record batches for efficiency
297
+
* Applies `max.poll.records` as a soft limit, meaning complete record batches will be acquired even if it exceeds this value
273
298
274
-
With share consumers, record distribution is controlled by Kafka's share group coordinator at the broker level, not by Spring for Apache Kafka.
275
-
The broker may assign all records to a single consumer thread at any given time, especially when:
299
+
While records are acquired by one consumer, they are not available to other consumers.
300
+
When the acquisition lock expires, unacknowledged records automatically return to "Available" state and can be delivered to another consumer.
276
301
277
-
* The topic has a single partition
278
-
* There's low message volume
279
-
* The broker's distribution algorithm favors certain consumers
302
+
The broker limits the number of records that can be acquired per partition using `group.share.partition.max.record.locks`.
303
+
Once this limit is reached, subsequent polls temporarily return no records until locks expire.
280
304
281
-
This is normal behavior. The key benefit of concurrency is having multiple consumer threads *available* to the share group coordinator for distribution.
282
-
As message volume increases or over time, you should see distribution across multiple threads.
305
+
**Implications for Concurrency:**
283
306
284
-
This differs from traditional `ConcurrentMessageListenerContainer` where Spring explicitly distributes partitions across threads.
307
+
* Each consumer thread independently polls and may acquire different numbers of records per poll
308
+
* Record distribution across threads depends on polling timing and batch availability
309
+
* Multiple threads increase the pool of consumers available to acquire records
310
+
* With low message volume or single partitions, records may concentrate on fewer threads
311
+
* For long-running workloads, distribution tends to be more even
285
312
286
-
When using concurrency with share consumers:
313
+
**Configuration:**
287
314
288
315
* Each thread polls and processes records independently
289
316
* Acknowledgment constraints apply per-thread (one thread's unacknowledged records don't block other threads)
Copy file name to clipboardExpand all lines: spring-kafka/src/test/java/org/springframework/kafka/listener/ShareKafkaMessageListenerContainerIntegrationTests.java
Copy file name to clipboardExpand all lines: spring-kafka/src/test/java/org/springframework/kafka/listener/ShareKafkaMessageListenerContainerUnitTests.java
0 commit comments