You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jira@kafka.apache.org by "kirktrue (via GitHub)" <gi...@apache.org> on 2025/06/17 13:42:49 UTC
[PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
kirktrue opened a new pull request, #19980:
URL: https://github.com/apache/kafka/pull/19980
There’s a difference in the two consumers’ `pollForFetches()` methods in this case: `ClassicKafkaConsumer` doesn't block waiting for data in the fetch buffer, but `AsyncKafkaConsumer` does.
In `ClassicKafkaConsumer.pollForFetches()`, after enqueuing the `FETCH` request, the consumer makes a call to `ConsumerNetworkClient.poll()`. In most cases `poll()` returns almost immediately because it successfully sent the `FETCH` request. So even when the `pollTimeout` value is, e.g. 3000, the call to `ConsumerNetworkClient.poll()` doesn't block that long waiting for a response.
After sending out a `FETCH` request, `AsyncKafkaConsumer` then calls `FetchBuffer.awaitNotEmpty()` and proceeds to block there for the full length of the timeout. In some cases, the response to the `FETCH` comes back with no results, which doesn't unblock `FetchBuffer.awaitNotEmpty()`. So because the application thread is still waiting for data in the buffer, it remains blocked, preventing any more `FETCH` requests from being sent, causing the long pauses in the console consumer.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "lianetm (via GitHub)" <gi...@apache.org>.
lianetm commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2316339959
##########
clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/PlaintextConsumerTest.java:
##########
@@ -1588,6 +1590,75 @@ private void sendCompressedMessages(int numRecords, TopicPartition tp) {
}
}
+ @ClusterTest
+ public void testClassicConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CLASSIC);
+ }
+
+ @ClusterTest
+ public void testAsyncConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CONSUMER);
+ }
+
+ /**
+ * This test is to prove that the intermittent stalling that has been experienced when using the asynchronous
+ * consumer, as filed under KAFKA-19259, have been fixed.
+ *
+ * <p/>
+ *
+ * The basic idea is to have one thread that produces a record every 500 ms. and the main thread that consumes
+ * records without pausing between polls for much more than the produce delay. In the test case filed in
+ * KAFKA-19259, the consumer sometimes pauses for up to 5-10 seconds despite records being produced every second.
+ */
+ private void testStallBetweenPoll(GroupProtocol groupProtocol) throws Exception {
+ var testTopic = "stall-test-topic";
+ var numPartitions = 6;
+ cluster.createTopic(testTopic, numPartitions, (short) BROKER_COUNT);
+
+ // The producer must produce slowly to tickle the scenario.
+ var produceDelay = 500;
+
+ var executor = Executors.newScheduledThreadPool(1);
+
+ try (var producer = cluster.producer()) {
+ // Start a thread running that produces records at a relative trickle.
+ executor.scheduleWithFixedDelay(
+ () -> producer.send(new ProducerRecord<>(testTopic, TestUtils.randomBytes(64))),
+ 0,
+ produceDelay,
+ TimeUnit.MILLISECONDS
+ );
+
+ Map<String, Object> consumerConfig = Map.of(GROUP_PROTOCOL_CONFIG, groupProtocol.name().toLowerCase(Locale.ROOT));
+
+ // Assign a tolerance for how much time is allowed to pass between Consumer.poll() calls given that there
+ // should be *at least* one record to read every second.
+ var pollDelayTolerance = 2000;
+
+ try (Consumer<byte[], byte[]> consumer = cluster.consumer(consumerConfig)) {
+ consumer.subscribe(List.of(testTopic));
+
+ // This is here to allow the consumer time to settle the group membership/assignment.
+ awaitNonEmptyRecords(consumer, new TopicPartition(testTopic, 0));
+
+ // Keep track of the last time the poll is invoked to ensure the deltas between invocations don't
+ // exceed the delay threshold defined above.
+ var beforePoll = System.currentTimeMillis();
+ consumer.poll(Duration.ofSeconds(5));
+ consumer.poll(Duration.ofSeconds(5));
+ var afterPoll = System.currentTimeMillis();
+ var pollDelay = afterPoll - beforePoll;
+
+ if (pollDelay > pollDelayTolerance)
+ fail("Detected a stall of " + pollDelay + " ms between Consumer.poll() invocations despite a Producer producing records every " + produceDelay + " ms");
Review Comment:
Adding a `consumer.unsubscribe()` before completing the test should help solve the flakiness @AndrewJSchofield reported.
I tried locally and got it too (around 30 iterations), and noticed it fails on the first poll to join the group and consume. This seemed to solve the flakiness on my side, up to 100 iterations, but please double check in case there is something else.
##########
clients/src/main/java/org/apache/kafka/clients/consumer/internals/FetchBuffer.java:
##########
@@ -98,7 +98,8 @@ void add(CompletedFetch completedFetch) {
try {
lock.lock();
completedFetches.add(completedFetch);
- notEmptyCondition.signalAll();
+ wokenup.set(true);
+ blockingCondition.signalAll();
Review Comment:
could we reuse the existing `addAll` in this implementation of `add`? Would avoid dup logic like this
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "lianetm (via GitHub)" <gi...@apache.org>.
lianetm commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2322471532
##########
clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/PlaintextConsumerTest.java:
##########
@@ -1588,6 +1590,75 @@ private void sendCompressedMessages(int numRecords, TopicPartition tp) {
}
}
+ @ClusterTest
+ public void testClassicConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CLASSIC);
+ }
+
+ @ClusterTest
+ public void testAsyncConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CONSUMER);
+ }
+
+ /**
+ * This test is to prove that the intermittent stalling that has been experienced when using the asynchronous
+ * consumer, as filed under KAFKA-19259, have been fixed.
+ *
+ * <p/>
+ *
+ * The basic idea is to have one thread that produces a record every 500 ms. and the main thread that consumes
+ * records without pausing between polls for much more than the produce delay. In the test case filed in
+ * KAFKA-19259, the consumer sometimes pauses for up to 5-10 seconds despite records being produced every second.
+ */
+ private void testStallBetweenPoll(GroupProtocol groupProtocol) throws Exception {
+ var testTopic = "stall-test-topic";
+ var numPartitions = 6;
+ cluster.createTopic(testTopic, numPartitions, (short) BROKER_COUNT);
+
+ // The producer must produce slowly to tickle the scenario.
+ var produceDelay = 500;
+
+ var executor = Executors.newScheduledThreadPool(1);
+
+ try (var producer = cluster.producer()) {
+ // Start a thread running that produces records at a relative trickle.
+ executor.scheduleWithFixedDelay(
+ () -> producer.send(new ProducerRecord<>(testTopic, TestUtils.randomBytes(64))),
+ 0,
+ produceDelay,
+ TimeUnit.MILLISECONDS
+ );
+
+ Map<String, Object> consumerConfig = Map.of(GROUP_PROTOCOL_CONFIG, groupProtocol.name().toLowerCase(Locale.ROOT));
+
+ // Assign a tolerance for how much time is allowed to pass between Consumer.poll() calls given that there
+ // should be *at least* one record to read every second.
+ var pollDelayTolerance = 2000;
+
+ try (Consumer<byte[], byte[]> consumer = cluster.consumer(consumerConfig)) {
+ consumer.subscribe(List.of(testTopic));
+
+ // This is here to allow the consumer time to settle the group membership/assignment.
+ awaitNonEmptyRecords(consumer, new TopicPartition(testTopic, 0));
+
+ // Keep track of the last time the poll is invoked to ensure the deltas between invocations don't
+ // exceed the delay threshold defined above.
+ var beforePoll = System.currentTimeMillis();
+ consumer.poll(Duration.ofSeconds(5));
+ consumer.poll(Duration.ofSeconds(5));
+ var afterPoll = System.currentTimeMillis();
+ var pollDelay = afterPoll - beforePoll;
+
+ if (pollDelay > pollDelayTolerance)
+ fail("Detected a stall of " + pollDelay + " ms between Consumer.poll() invocations despite a Producer producing records every " + produceDelay + " ms");
Review Comment:
I had the flakiness locally when Andrew reported it, running from the command line with:
> N=100; I=0; while [ $I -lt $N ] && ./gradlew clients:clients-integration-tests:test --tests org.apache.kafka.clients.consumer.PlaintextConsumerTest.testAsyncConsumerStallBetweenPoll --rerun --fail-fast; do (( I=$I+1 )); echo "Completed run: $I"; sleep 1; done
But agree it's not failing anymore, tried couple of times now (was it maybe the update to the test for simplifying it?). Anyways, seems stable now on my side and yours
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "kirktrue (via GitHub)" <gi...@apache.org>.
kirktrue commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2298971190
##########
clients/src/main/java/org/apache/kafka/clients/consumer/internals/AbstractFetch.java:
##########
@@ -222,6 +222,10 @@ protected void handleFetchSuccess(final Node fetchTarget,
fetchBuffer.add(completedFetch);
}
+ // "Wake" the fetch buffer on any response, even if it's empty, to allow the consumer to not block
+ // indefinitely waiting on the fetch buffer to get data.
+ fetchBuffer.wakeup();
Review Comment:
I've updated `FetchBuffer.add()` and `FetchBuffer.addAll()` to wakeup any threads that are blocked.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "AndrewJSchofield (via GitHub)" <gi...@apache.org>.
AndrewJSchofield commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2318806420
##########
clients/src/main/java/org/apache/kafka/clients/consumer/internals/AbstractFetch.java:
##########
@@ -222,6 +222,10 @@ protected void handleFetchSuccess(final Node fetchTarget,
fetchBuffer.add(completedFetch);
}
+ // "Wake" the fetch buffer on any response, even if it's empty, to allow the consumer to not block
+ // indefinitely waiting on the fetch buffer to get data.
+ fetchBuffer.wakeup();
Review Comment:
nit: Given that `fetchBuffer.add` already wakes, it seems unnecessary to called `fetchBuffer.wakeup` unless nothing was added to the buffer. This is harmless I think, but just an observation.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "kirktrue (via GitHub)" <gi...@apache.org>.
kirktrue commented on PR #19980:
URL: https://github.com/apache/kafka/pull/19980#issuecomment-3162066628
Here are some preliminary results:
| Metric | CLASSIC (trunk) | CONSUMER (trunk) | CONSUMER (branch) |
|:-------------------------------|---------------------:|---------------------:|---------------------:|
| bytes-consumed-rate | 137391120.171 | 47687420.962 | 49129271.338 |
| bytes-consumed-total | 52099996955.000 | 52099995913.000 | 52099039878.000 |
| fetch-latency-avg | 38.684 | 14.093 | 22.239 |
| fetch-latency-max | 364.000 | 328.000 | 489.000 |
| fetch-rate | 134.175 | 46.559 | 47.987 |
| fetch-size-avg | 1023939.386 | 1022509.584 | 1024085.929 |
| fetch-size-max | 1033664.000 | 1033664.000 | 1033664.000 |
| fetch-throttle-time-avg | 0.000 | 0.000 | 0.000 |
| fetch-throttle-time-max | 0.000 | 0.000 | 0.000 |
| fetch-total | 50876.000 | 50881.000 | 50881.000 |
| records-consumed-rate | 263706.565 | 91530.558 | 94298.026 |
| records-consumed-total | 100000004.000 | 100000002.000 | 99998167.000 |
| records-lag-max | 22377721.000 | 20091127.000 | 20177739.000 |
| records-lead-min | 13207586.000 | 15470459.000 | 15369182.000 |
| records-per-request-avg | 1965.335 | 1962.590 | 1965.616 |
| Average CPU load | 67.27% | 143.33% | 146.90% |
| P99 CPU load | 67.70% | 145.00% | 148.00% |
| Max CPU load | 67.70% | 145.00% | 148.00% |
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "kirktrue (via GitHub)" <gi...@apache.org>.
kirktrue commented on PR #19980:
URL: https://github.com/apache/kafka/pull/19980#issuecomment-3256971252
> Thanks @kirktrue ! Could you fix the checkstyle errors?
Yes. Done.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "lianetm (via GitHub)" <gi...@apache.org>.
lianetm commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2298653648
##########
clients/src/main/java/org/apache/kafka/clients/consumer/internals/AbstractFetch.java:
##########
@@ -222,6 +222,10 @@ protected void handleFetchSuccess(final Node fetchTarget,
fetchBuffer.add(completedFetch);
}
+ // "Wake" the fetch buffer on any response, even if it's empty, to allow the consumer to not block
+ // indefinitely waiting on the fetch buffer to get data.
+ fetchBuffer.wakeup();
Review Comment:
Should we notify the buffer as soon as we add the first completed batch to it, instead of waiting for all completed batches to be added?
Seems that will be closer to what the `ClassicConsumer` does by polling while `!fetcher.hasAvailableFetches()` (given that hasAvailable fetches will be true as soon as there is a first CompletedBatch added)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "kirktrue (via GitHub)" <gi...@apache.org>.
kirktrue commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2298969509
##########
clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/PlaintextConsumerTest.java:
##########
@@ -1588,6 +1593,103 @@ private void sendCompressedMessages(int numRecords, TopicPartition tp) {
}
}
+ // Override the default test timeout of 60 seconds since the core logic of the test allows up to 60 seconds to
+ // pass to detect the issue.
+ @Timeout(75)
+ @ClusterTest
+ public void testClassicConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CLASSIC);
+ }
+
+ // Override the default test timeout of 60 seconds since the core logic of the test allows up to 60 seconds to
+ // pass to detect the issue.
+ @Timeout(75)
+ @ClusterTest
+ public void testAsyncConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CONSUMER);
+ }
+
+ /**
+ * This test is to prove that the intermittent stalling that has been experienced when using the asynchronous
+ * consumer, as filed under KAFKA-19259, have been fixed.
+ *
+ * <p/>
+ *
+ * The basic idea is to have one thread that produces a record every 500 ms. and the main thread that consumes
+ * records without pausing between polls for much more than the produce delay. In the test case filed in
+ * KAFKA-19259, the consumer sometimes pauses for up to 5-10 seconds despite records being produced every
+ * quarter second.
+ */
+ private void testStallBetweenPoll(GroupProtocol groupProtocol) throws Exception {
+ var testTopic = "stutter-test-topic";
+ var numPartitions = 6;
+ cluster.createTopic(testTopic, numPartitions, (short) BROKER_COUNT);
+
+ // Give the test one minute to detect a stall.
+ var testTimeout = 60000;
+
+ // The producer must produce slowly to tickle the scenario.
+ var produceWait = 500;
+
+ // Assign a tolerance for how much time is allowed to pass between Consumer.poll() calls given that there
+ // should be *at least* one record to read every second.
+ var delayTolerance = produceWait * 2;
+
+ try (var producer = cluster.producer()) {
+ // Start a thread running that produces records at a relative trickle.
+ var producerThread = new Thread(() -> {
+ while (true) {
+ try {
+ Utils.sleep(produceWait);
+ producer.send(new ProducerRecord<>(testTopic, TestUtils.randomBytes(64))).get();
+ } catch (InterruptedException e) {
+ break;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ producerThread.start();
+
+ Map<String, Object> consumerConfig = Map.of(GROUP_PROTOCOL_CONFIG, groupProtocol.name().toLowerCase(Locale.ROOT));
+
+ try (Consumer<byte[], byte[]> consumer = cluster.consumer(consumerConfig)) {
+ consumer.subscribe(List.of(testTopic));
+
+ // This is just to wait until the group membership and assignment is in place.
+ awaitNonEmptyRecords(consumer, new TopicPartition(testTopic, 0));
+
+ var testTimer = Time.SYSTEM.timer(testTimeout);
+
+ // Keep track of the last time the poll is invoked to ensure the deltas between invocations don't
+ // exceed the delay threshold defined above.
+ var lastPoll = System.currentTimeMillis();
+
+ while (testTimer.notExpired()) {
Review Comment:
I refactored it to use just two calls to `Consumer.poll()`.
I temporarily removed the call to `FetchBuffer.wakeup()` from `AbstractFetch` and ran the test 100x and it fails consistently, as expected. I put the call to `wakeup()` back in and ran another 100x and the passes consistently.
Thanks for the suggestion!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "kirktrue (via GitHub)" <gi...@apache.org>.
kirktrue commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2320217963
##########
clients/src/main/java/org/apache/kafka/clients/consumer/internals/FetchBuffer.java:
##########
@@ -98,7 +98,8 @@ void add(CompletedFetch completedFetch) {
try {
lock.lock();
completedFetches.add(completedFetch);
- notEmptyCondition.signalAll();
+ wokenup.set(true);
+ blockingCondition.signalAll();
Review Comment:
Done.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "kirktrue (via GitHub)" <gi...@apache.org>.
kirktrue commented on PR #19980:
URL: https://github.com/apache/kafka/pull/19980#issuecomment-3256970902
> I had the flakiness locally when Andrew reported it, running from the command line with:
>
> > N=100; I=0; while [ $I -lt $N ] && ./gradlew clients:clients-integration-tests:test --tests org.apache.kafka.clients.consumer.PlaintextConsumerTest.testAsyncConsumerStallBetweenPoll --rerun --fail-fast; do (( I=$I+1 )); echo "Completed run: $I"; sleep 1; done
>
> But agree it's not failing anymore, tried couple of times now (was it maybe the update to the test for simplifying it?). Anyways, seems stable now on my side and yours
I wasn't able to replicate it locally using this method either 🤷♂️
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "kirktrue (via GitHub)" <gi...@apache.org>.
kirktrue commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2320185584
##########
clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/PlaintextConsumerTest.java:
##########
@@ -1588,6 +1590,75 @@ private void sendCompressedMessages(int numRecords, TopicPartition tp) {
}
}
+ @ClusterTest
+ public void testClassicConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CLASSIC);
+ }
+
+ @ClusterTest
+ public void testAsyncConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CONSUMER);
+ }
+
+ /**
+ * This test is to prove that the intermittent stalling that has been experienced when using the asynchronous
+ * consumer, as filed under KAFKA-19259, have been fixed.
+ *
+ * <p/>
+ *
+ * The basic idea is to have one thread that produces a record every 500 ms. and the main thread that consumes
+ * records without pausing between polls for much more than the produce delay. In the test case filed in
+ * KAFKA-19259, the consumer sometimes pauses for up to 5-10 seconds despite records being produced every second.
+ */
+ private void testStallBetweenPoll(GroupProtocol groupProtocol) throws Exception {
+ var testTopic = "stall-test-topic";
+ var numPartitions = 6;
+ cluster.createTopic(testTopic, numPartitions, (short) BROKER_COUNT);
+
+ // The producer must produce slowly to tickle the scenario.
+ var produceDelay = 500;
+
+ var executor = Executors.newScheduledThreadPool(1);
+
+ try (var producer = cluster.producer()) {
+ // Start a thread running that produces records at a relative trickle.
+ executor.scheduleWithFixedDelay(
+ () -> producer.send(new ProducerRecord<>(testTopic, TestUtils.randomBytes(64))),
+ 0,
+ produceDelay,
+ TimeUnit.MILLISECONDS
+ );
+
+ Map<String, Object> consumerConfig = Map.of(GROUP_PROTOCOL_CONFIG, groupProtocol.name().toLowerCase(Locale.ROOT));
+
+ // Assign a tolerance for how much time is allowed to pass between Consumer.poll() calls given that there
+ // should be *at least* one record to read every second.
+ var pollDelayTolerance = 2000;
+
+ try (Consumer<byte[], byte[]> consumer = cluster.consumer(consumerConfig)) {
+ consumer.subscribe(List.of(testTopic));
+
+ // This is here to allow the consumer time to settle the group membership/assignment.
+ awaitNonEmptyRecords(consumer, new TopicPartition(testTopic, 0));
+
+ // Keep track of the last time the poll is invoked to ensure the deltas between invocations don't
+ // exceed the delay threshold defined above.
+ var beforePoll = System.currentTimeMillis();
+ consumer.poll(Duration.ofSeconds(5));
+ consumer.poll(Duration.ofSeconds(5));
+ var afterPoll = System.currentTimeMillis();
+ var pollDelay = afterPoll - beforePoll;
+
+ if (pollDelay > pollDelayTolerance)
+ fail("Detected a stall of " + pollDelay + " ms between Consumer.poll() invocations despite a Producer producing records every " + produceDelay + " ms");
Review Comment:
I guess it depends on the mechanism used to execute the test repeatedly, but I'm using the JUnit runner inside IntelliJ and it appears that each test invocation is using a distinct coordinator. I'm still not understanding how unsubscribing would help since each test invocation is effectively sandboxed 🤔
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "kirktrue (via GitHub)" <gi...@apache.org>.
kirktrue commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2320189450
##########
clients/src/main/java/org/apache/kafka/clients/consumer/internals/AbstractFetch.java:
##########
@@ -222,6 +222,10 @@ protected void handleFetchSuccess(final Node fetchTarget,
fetchBuffer.add(completedFetch);
}
+ // "Wake" the fetch buffer on any response, even if it's empty, to allow the consumer to not block
+ // indefinitely waiting on the fetch buffer to get data.
+ fetchBuffer.wakeup();
Review Comment:
Yes, I should remove it. It adds unnecessary synchronization overhead.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "lianetm (via GitHub)" <gi...@apache.org>.
lianetm commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2298036689
##########
clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/PlaintextConsumerTest.java:
##########
@@ -1588,6 +1593,103 @@ private void sendCompressedMessages(int numRecords, TopicPartition tp) {
}
}
+ // Override the default test timeout of 60 seconds since the core logic of the test allows up to 60 seconds to
+ // pass to detect the issue.
+ @Timeout(75)
+ @ClusterTest
+ public void testClassicConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CLASSIC);
+ }
+
+ // Override the default test timeout of 60 seconds since the core logic of the test allows up to 60 seconds to
+ // pass to detect the issue.
+ @Timeout(75)
+ @ClusterTest
+ public void testAsyncConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CONSUMER);
+ }
+
+ /**
+ * This test is to prove that the intermittent stalling that has been experienced when using the asynchronous
+ * consumer, as filed under KAFKA-19259, have been fixed.
+ *
+ * <p/>
+ *
+ * The basic idea is to have one thread that produces a record every 500 ms. and the main thread that consumes
+ * records without pausing between polls for much more than the produce delay. In the test case filed in
+ * KAFKA-19259, the consumer sometimes pauses for up to 5-10 seconds despite records being produced every
+ * quarter second.
+ */
+ private void testStallBetweenPoll(GroupProtocol groupProtocol) throws Exception {
+ var testTopic = "stutter-test-topic";
+ var numPartitions = 6;
+ cluster.createTopic(testTopic, numPartitions, (short) BROKER_COUNT);
+
+ // Give the test one minute to detect a stall.
+ var testTimeout = 60000;
+
+ // The producer must produce slowly to tickle the scenario.
+ var produceWait = 500;
+
+ // Assign a tolerance for how much time is allowed to pass between Consumer.poll() calls given that there
+ // should be *at least* one record to read every second.
+ var delayTolerance = produceWait * 2;
+
+ try (var producer = cluster.producer()) {
+ // Start a thread running that produces records at a relative trickle.
+ var producerThread = new Thread(() -> {
+ while (true) {
+ try {
+ Utils.sleep(produceWait);
+ producer.send(new ProducerRecord<>(testTopic, TestUtils.randomBytes(64))).get();
+ } catch (InterruptedException e) {
+ break;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ producerThread.start();
+
+ Map<String, Object> consumerConfig = Map.of(GROUP_PROTOCOL_CONFIG, groupProtocol.name().toLowerCase(Locale.ROOT));
+
+ try (Consumer<byte[], byte[]> consumer = cluster.consumer(consumerConfig)) {
+ consumer.subscribe(List.of(testTopic));
+
+ // This is just to wait until the group membership and assignment is in place.
+ awaitNonEmptyRecords(consumer, new TopicPartition(testTopic, 0));
+
+ var testTimer = Time.SYSTEM.timer(testTimeout);
+
+ // Keep track of the last time the poll is invoked to ensure the deltas between invocations don't
+ // exceed the delay threshold defined above.
+ var lastPoll = System.currentTimeMillis();
+
+ while (testTimer.notExpired()) {
Review Comment:
this means that this test will run for the full 1min 15s (testTimeout=75s), but can't we simplify and detect the issue much sooner?
The faulty behaviour is that one poll runs fast, and the next one takes the full pollTimeout, right? So not if I'm missing something but wouldn't 2 polls be enough? (also considering that `awaitNonEmptyRecords` does poll before the actual call to consumer.poll on ln 1670).
##########
clients/src/main/java/org/apache/kafka/clients/consumer/internals/AbstractFetch.java:
##########
@@ -222,6 +222,10 @@ protected void handleFetchSuccess(final Node fetchTarget,
fetchBuffer.add(completedFetch);
}
+ // "Wake" the fetch buffer on any response, even if it's empty, to allow the consumer to not block
+ // indefinitely waiting on the fetch buffer to get data.
+ fetchBuffer.wakeup();
Review Comment:
Should we notify the buffer as soon as we add the first completed batch to it, instead of waiting for all completed batched to be added?
Seems that will be closer to what the `ClassicConsumer` does by polling while `!fetcher.hasAvailableFetches()` (given that hasAvailable fetches will be true as soon as there is a first CompletedBatch added)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "kirktrue (via GitHub)" <gi...@apache.org>.
kirktrue commented on PR #19980:
URL: https://github.com/apache/kafka/pull/19980#issuecomment-3165432167
# Results for August 7, 2025 testing
## Metrics
The metrics shown below come from three separate test runs:
1. Test run `CLASSIC` uses the `CLASSIC` group protocol from `trunk`
2. Test run `CONSUMER` uses the `CONSUMER` group protocol, also from `trunk`
3. Test run `CONSUMER (branch)` uses the `CONSUMER` group protocol from my current development branch (`KAFKA-19259-intermittent-fetch`)
Results:
| Metric | CLASSIC | CONSUMER (trunk) | CONSUMER (branch) |
|:--------------------------|--------------------------:|--------------------------:|--------------------------:|
| bytes-consumed-rate | 161948243.756 | 48869612.029 | 47844631.751 |
| bytes-consumed-total | 52099983328.000 | 52098998117.000 | 52098998117.000 |
| fetch-latency-avg | 30.181 | 10.375 | 9.097 |
| fetch-latency-max | 395.000 | 446.000 | 277.000 |
| fetch-rate | 158.164 | 47.708 | 46.735 |
| fetch-size-avg | 1024027.703 | 1024429.637 | 1023821.492 |
| fetch-size-max | 1033664.000 | 1033664.000 | 1033664.000 |
| fetch-throttle-time-avg | 0.000 | 0.000 | 0.000 |
| fetch-throttle-time-max | 0.000 | 0.000 | 0.000 |
| fetch-total | 50876.000 | 50881.000 | 50881.000 |
| records-consumed-rate | 310836.150 | 93798.193 | 91831.261 |
| records-consumed-total | 99999968.000 | 99998077.000 | 99998077.000 |
| records-lag-max | 28765980.000 | 26517085.000 | 26794519.000 |
| records-lead-min | 13050850.000 | 15613310.000 | 15053633.000 |
| records-per-request-avg | 1965.504 | 1966.276 | 1965.108 |
| commit-sync-time-ns-total | 0.000 | 0.000 | 0.000 |
| committed-time-ns-total | 0.000 | 0.000 | 0.000 |
| incoming-byte-rate | 162512956.322 | 49048728.884 | 48023787.626 |
| incoming-byte-total | 52302818435.000 | 52305011340.000 | 52306000040.000 |
| io-ratio | 0.238 | 0.093 | 0.091 |
| io-time-ns-avg | 3761.542 | 2267.901 | 2235.922 |
| io-time-ns-total | 77782691789.000 | 100466922476.000 | 97955040618.000 |
| io-wait-ratio | 0.313 | 0.522 | 0.524 |
| io-wait-time-ns-avg | 4946.433 | 12676.522 | 12873.410 |
| io-wait-time-ns-total | 111955075540.000 | 575715209235.000 | 565911944612.000 |
| last-poll-seconds-ago | 0.000 | 0.000 | 0.000 |
| network-io-rate | 2289.783 | 664.182 | 633.698 |
| network-io-total | 731587.000 | 698801.000 | 695812.000 |
| outgoing-byte-rate | 17636.502 | 5356.321 | 5244.190 |
| outgoing-byte-total | 5678537.000 | 5713940.000 | 5713348.000 |
| poll-idle-ratio-avg | NaN | NaN | NaN |
| request-rate | 158.645 | 48.113 | 47.119 |
| request-size-avg | 111.170 | 111.327 | 111.296 |
| request-size-max | 237.000 | 203.000 | 203.000 |
| request-total | 51080.000 | 51341.000 | 51337.000 |
| response-rate | 158.669 | 48.129 | 47.114 |
| response-total | 51076.000 | 51341.000 | 51337.000 |
| select-rate | 63253.029 | 41203.090 | 40724.821 |
| select-total | 20355063.000 | 43510072.000 | 44066630.000 |
| time-between-poll-avg | 0.016 | 0.053 | 0.054 |
| time-between-poll-max | 74.000 | 10.000 | 12.000 |
| Average CPU load | 77.34% | 145.66% | 146.67% |
| P99 CPU load | 77.50% | 148.00% | 148.00% |
| Max CPU load | 77.50% | 148.00% | 148.00% |
## Execution
The test is run on a single AWS instance targeting a six-node cluster. The data on the topic is pre-populated before any of the tests are run. A single “warm up” run of the performance test is executed before executing the three test runs mentioned above.
The command to execute the test is:
```bash
kafka-consumer-perf-test.sh \
--bootstrap-server $BOOTSTRAP_SERVER \
--topic $TOPIC_NAME \
--messages $NUM_MESSAGES \
--consumer.config conf/consumer-$GROUP_PROTOCOL.properties \
--show-detailed-stats \
--print-metrics
```
`NUM_MESSAGES` is set to `100000000` (100 million).
## Client configuration
The consumer configuration sets `max.poll.records=5`, but is otherwise mostly defaults apart from connectivity/authentication:
```
bootstrap.servers=$BOOTSTRAP_SERVER
sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required
username="$USER_ID"
password='$USER_PASSWORD' ;
security.protocol=SASL_SSL
sasl.mechanism=PLAIN
ssl.endpoint.identification.algorithm=
client.dns.lookup=use_all_dns_ips
# Key
max.poll.records=5
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "AndrewJSchofield (via GitHub)" <gi...@apache.org>.
AndrewJSchofield commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2318715441
##########
clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/PlaintextConsumerTest.java:
##########
@@ -1588,6 +1590,75 @@ private void sendCompressedMessages(int numRecords, TopicPartition tp) {
}
}
+ @ClusterTest
+ public void testClassicConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CLASSIC);
+ }
+
+ @ClusterTest
+ public void testAsyncConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CONSUMER);
+ }
+
+ /**
+ * This test is to prove that the intermittent stalling that has been experienced when using the asynchronous
+ * consumer, as filed under KAFKA-19259, have been fixed.
+ *
+ * <p/>
+ *
+ * The basic idea is to have one thread that produces a record every 500 ms. and the main thread that consumes
+ * records without pausing between polls for much more than the produce delay. In the test case filed in
+ * KAFKA-19259, the consumer sometimes pauses for up to 5-10 seconds despite records being produced every second.
+ */
+ private void testStallBetweenPoll(GroupProtocol groupProtocol) throws Exception {
+ var testTopic = "stall-test-topic";
+ var numPartitions = 6;
+ cluster.createTopic(testTopic, numPartitions, (short) BROKER_COUNT);
+
+ // The producer must produce slowly to tickle the scenario.
+ var produceDelay = 500;
+
+ var executor = Executors.newScheduledThreadPool(1);
+
+ try (var producer = cluster.producer()) {
+ // Start a thread running that produces records at a relative trickle.
+ executor.scheduleWithFixedDelay(
+ () -> producer.send(new ProducerRecord<>(testTopic, TestUtils.randomBytes(64))),
+ 0,
+ produceDelay,
+ TimeUnit.MILLISECONDS
+ );
+
+ Map<String, Object> consumerConfig = Map.of(GROUP_PROTOCOL_CONFIG, groupProtocol.name().toLowerCase(Locale.ROOT));
+
+ // Assign a tolerance for how much time is allowed to pass between Consumer.poll() calls given that there
+ // should be *at least* one record to read every second.
+ var pollDelayTolerance = 2000;
+
+ try (Consumer<byte[], byte[]> consumer = cluster.consumer(consumerConfig)) {
+ consumer.subscribe(List.of(testTopic));
+
+ // This is here to allow the consumer time to settle the group membership/assignment.
+ awaitNonEmptyRecords(consumer, new TopicPartition(testTopic, 0));
+
+ // Keep track of the last time the poll is invoked to ensure the deltas between invocations don't
+ // exceed the delay threshold defined above.
+ var beforePoll = System.currentTimeMillis();
+ consumer.poll(Duration.ofSeconds(5));
+ consumer.poll(Duration.ofSeconds(5));
+ var afterPoll = System.currentTimeMillis();
+ var pollDelay = afterPoll - beforePoll;
+
+ if (pollDelay > pollDelayTolerance)
+ fail("Detected a stall of " + pollDelay + " ms between Consumer.poll() invocations despite a Producer producing records every " + produceDelay + " ms");
Review Comment:
I don't see why adding `unsubscribe` would help. Today, I ran 150 iterations without errors before I got bored 🎉
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "kirktrue (via GitHub)" <gi...@apache.org>.
kirktrue commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2320220634
##########
clients/src/main/java/org/apache/kafka/clients/consumer/internals/AbstractFetch.java:
##########
@@ -222,6 +222,10 @@ protected void handleFetchSuccess(final Node fetchTarget,
fetchBuffer.add(completedFetch);
}
+ // "Wake" the fetch buffer on any response, even if it's empty, to allow the consumer to not block
+ // indefinitely waiting on the fetch buffer to get data.
+ fetchBuffer.wakeup();
Review Comment:
I have made the call to `FetchBuffer.wakeup()` conditional in `handleFetchSuccess()` so that it's invoked only when there were no `CompletedFetch`s added.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "lianetm (via GitHub)" <gi...@apache.org>.
lianetm commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2318963044
##########
clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/PlaintextConsumerTest.java:
##########
@@ -1588,6 +1590,75 @@ private void sendCompressedMessages(int numRecords, TopicPartition tp) {
}
}
+ @ClusterTest
+ public void testClassicConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CLASSIC);
+ }
+
+ @ClusterTest
+ public void testAsyncConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CONSUMER);
+ }
+
+ /**
+ * This test is to prove that the intermittent stalling that has been experienced when using the asynchronous
+ * consumer, as filed under KAFKA-19259, have been fixed.
+ *
+ * <p/>
+ *
+ * The basic idea is to have one thread that produces a record every 500 ms. and the main thread that consumes
+ * records without pausing between polls for much more than the produce delay. In the test case filed in
+ * KAFKA-19259, the consumer sometimes pauses for up to 5-10 seconds despite records being produced every second.
+ */
+ private void testStallBetweenPoll(GroupProtocol groupProtocol) throws Exception {
+ var testTopic = "stall-test-topic";
+ var numPartitions = 6;
+ cluster.createTopic(testTopic, numPartitions, (short) BROKER_COUNT);
+
+ // The producer must produce slowly to tickle the scenario.
+ var produceDelay = 500;
+
+ var executor = Executors.newScheduledThreadPool(1);
+
+ try (var producer = cluster.producer()) {
+ // Start a thread running that produces records at a relative trickle.
+ executor.scheduleWithFixedDelay(
+ () -> producer.send(new ProducerRecord<>(testTopic, TestUtils.randomBytes(64))),
+ 0,
+ produceDelay,
+ TimeUnit.MILLISECONDS
+ );
+
+ Map<String, Object> consumerConfig = Map.of(GROUP_PROTOCOL_CONFIG, groupProtocol.name().toLowerCase(Locale.ROOT));
+
+ // Assign a tolerance for how much time is allowed to pass between Consumer.poll() calls given that there
+ // should be *at least* one record to read every second.
+ var pollDelayTolerance = 2000;
+
+ try (Consumer<byte[], byte[]> consumer = cluster.consumer(consumerConfig)) {
+ consumer.subscribe(List.of(testTopic));
+
+ // This is here to allow the consumer time to settle the group membership/assignment.
+ awaitNonEmptyRecords(consumer, new TopicPartition(testTopic, 0));
+
+ // Keep track of the last time the poll is invoked to ensure the deltas between invocations don't
+ // exceed the delay threshold defined above.
+ var beforePoll = System.currentTimeMillis();
+ consumer.poll(Duration.ofSeconds(5));
+ consumer.poll(Duration.ofSeconds(5));
+ var afterPoll = System.currentTimeMillis();
+ var pollDelay = afterPoll - beforePoll;
+
+ if (pollDelay > pollDelayTolerance)
+ fail("Detected a stall of " + pollDelay + " ms between Consumer.poll() invocations despite a Producer producing records every " + produceDelay + " ms");
Review Comment:
I expect it's a matter of giving more time to the leave group request to complete before moving on (so that on the next run of the test the consumer can join and consume as expected).
When calling unsubscribe explicitly, we end up sending a leave group with the default api timeout (60s), vs when not calling it, it's the default consumer.close() that triggers the leave group, using the default close timeout (30s).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "kirktrue (via GitHub)" <gi...@apache.org>.
kirktrue commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2320188205
##########
clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/PlaintextConsumerTest.java:
##########
@@ -1588,6 +1590,75 @@ private void sendCompressedMessages(int numRecords, TopicPartition tp) {
}
}
+ @ClusterTest
+ public void testClassicConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CLASSIC);
+ }
+
+ @ClusterTest
+ public void testAsyncConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CONSUMER);
+ }
+
+ /**
+ * This test is to prove that the intermittent stalling that has been experienced when using the asynchronous
+ * consumer, as filed under KAFKA-19259, have been fixed.
+ *
+ * <p/>
+ *
+ * The basic idea is to have one thread that produces a record every 500 ms. and the main thread that consumes
+ * records without pausing between polls for much more than the produce delay. In the test case filed in
+ * KAFKA-19259, the consumer sometimes pauses for up to 5-10 seconds despite records being produced every second.
+ */
+ private void testStallBetweenPoll(GroupProtocol groupProtocol) throws Exception {
+ var testTopic = "stall-test-topic";
+ var numPartitions = 6;
+ cluster.createTopic(testTopic, numPartitions, (short) BROKER_COUNT);
+
+ // The producer must produce slowly to tickle the scenario.
+ var produceDelay = 500;
+
+ var executor = Executors.newScheduledThreadPool(1);
+
+ try (var producer = cluster.producer()) {
+ // Start a thread running that produces records at a relative trickle.
+ executor.scheduleWithFixedDelay(
+ () -> producer.send(new ProducerRecord<>(testTopic, TestUtils.randomBytes(64))),
+ 0,
+ produceDelay,
+ TimeUnit.MILLISECONDS
+ );
+
+ Map<String, Object> consumerConfig = Map.of(GROUP_PROTOCOL_CONFIG, groupProtocol.name().toLowerCase(Locale.ROOT));
+
+ // Assign a tolerance for how much time is allowed to pass between Consumer.poll() calls given that there
+ // should be *at least* one record to read every second.
+ var pollDelayTolerance = 2000;
+
+ try (Consumer<byte[], byte[]> consumer = cluster.consumer(consumerConfig)) {
+ consumer.subscribe(List.of(testTopic));
+
+ // This is here to allow the consumer time to settle the group membership/assignment.
+ awaitNonEmptyRecords(consumer, new TopicPartition(testTopic, 0));
+
+ // Keep track of the last time the poll is invoked to ensure the deltas between invocations don't
+ // exceed the delay threshold defined above.
+ var beforePoll = System.currentTimeMillis();
+ consumer.poll(Duration.ofSeconds(5));
+ consumer.poll(Duration.ofSeconds(5));
+ var afterPoll = System.currentTimeMillis();
+ var pollDelay = afterPoll - beforePoll;
+
+ if (pollDelay > pollDelayTolerance)
+ fail("Detected a stall of " + pollDelay + " ms between Consumer.poll() invocations despite a Producer producing records every " + produceDelay + " ms");
Review Comment:
Can you give me more detail about how you're running the tests repeatedly? I'm able to run it 100 times without issue (locally).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "lianetm (via GitHub)" <gi...@apache.org>.
lianetm merged PR #19980:
URL: https://github.com/apache/kafka/pull/19980
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "lianetm (via GitHub)" <gi...@apache.org>.
lianetm commented on code in PR #19980:
URL: https://github.com/apache/kafka/pull/19980#discussion_r2298036689
##########
clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/consumer/PlaintextConsumerTest.java:
##########
@@ -1588,6 +1593,103 @@ private void sendCompressedMessages(int numRecords, TopicPartition tp) {
}
}
+ // Override the default test timeout of 60 seconds since the core logic of the test allows up to 60 seconds to
+ // pass to detect the issue.
+ @Timeout(75)
+ @ClusterTest
+ public void testClassicConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CLASSIC);
+ }
+
+ // Override the default test timeout of 60 seconds since the core logic of the test allows up to 60 seconds to
+ // pass to detect the issue.
+ @Timeout(75)
+ @ClusterTest
+ public void testAsyncConsumerStallBetweenPoll() throws Exception {
+ testStallBetweenPoll(GroupProtocol.CONSUMER);
+ }
+
+ /**
+ * This test is to prove that the intermittent stalling that has been experienced when using the asynchronous
+ * consumer, as filed under KAFKA-19259, have been fixed.
+ *
+ * <p/>
+ *
+ * The basic idea is to have one thread that produces a record every 500 ms. and the main thread that consumes
+ * records without pausing between polls for much more than the produce delay. In the test case filed in
+ * KAFKA-19259, the consumer sometimes pauses for up to 5-10 seconds despite records being produced every
+ * quarter second.
+ */
+ private void testStallBetweenPoll(GroupProtocol groupProtocol) throws Exception {
+ var testTopic = "stutter-test-topic";
+ var numPartitions = 6;
+ cluster.createTopic(testTopic, numPartitions, (short) BROKER_COUNT);
+
+ // Give the test one minute to detect a stall.
+ var testTimeout = 60000;
+
+ // The producer must produce slowly to tickle the scenario.
+ var produceWait = 500;
+
+ // Assign a tolerance for how much time is allowed to pass between Consumer.poll() calls given that there
+ // should be *at least* one record to read every second.
+ var delayTolerance = produceWait * 2;
+
+ try (var producer = cluster.producer()) {
+ // Start a thread running that produces records at a relative trickle.
+ var producerThread = new Thread(() -> {
+ while (true) {
+ try {
+ Utils.sleep(produceWait);
+ producer.send(new ProducerRecord<>(testTopic, TestUtils.randomBytes(64))).get();
+ } catch (InterruptedException e) {
+ break;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ producerThread.start();
+
+ Map<String, Object> consumerConfig = Map.of(GROUP_PROTOCOL_CONFIG, groupProtocol.name().toLowerCase(Locale.ROOT));
+
+ try (Consumer<byte[], byte[]> consumer = cluster.consumer(consumerConfig)) {
+ consumer.subscribe(List.of(testTopic));
+
+ // This is just to wait until the group membership and assignment is in place.
+ awaitNonEmptyRecords(consumer, new TopicPartition(testTopic, 0));
+
+ var testTimer = Time.SYSTEM.timer(testTimeout);
+
+ // Keep track of the last time the poll is invoked to ensure the deltas between invocations don't
+ // exceed the delay threshold defined above.
+ var lastPoll = System.currentTimeMillis();
+
+ while (testTimer.notExpired()) {
Review Comment:
this means that this test will run for the full 1min 15s (testTimeout=75s), but can't we simplify and detect the issue much sooner?
The faulty behaviour is that one poll runs fast, and the next one takes the full pollTimeout, right? So maybe I'm missing something but wouldn't 2 polls be enough? (also considering that `awaitNonEmptyRecords` does poll before the actual call to consumer.poll on ln 1670).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] KAFKA-19259: Async consumer fetch intermittent delays on console consumer [kafka]
Posted by "lianetm (via GitHub)" <gi...@apache.org>.
lianetm commented on PR #19980:
URL: https://github.com/apache/kafka/pull/19980#issuecomment-3192715127
Thanks for the PR @kirktrue! Still going around this one, but high level question regarding the testing metrics shared above:
- what specific metric among the list are you looking at related to this issue? (I would expect the fetch rate one, but I don't see any movement with the PR, even though it does fix the behaviour we see on the command line tool). Or is it that we don't have a metric that shows the problem and validates the fix?
- it's mentioned that the tests runs with max.poll.records=5, is that the case? (or is it a mix with the CPU issue?)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: jira-unsubscribe@kafka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org