Skip to content

Add parallel kafka consumer - #5

Merged
alishazaee merged 1 commit into
masterfrom
feature/add-parallel-kafka-consumer
Jul 30, 2026
Merged

Add parallel kafka consumer#5
alishazaee merged 1 commit into
masterfrom
feature/add-parallel-kafka-consumer

Conversation

@alishazaee

@alishazaee alishazaee commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added a parallel Kafka consumer that separates message retrieval from processing, supports bounded queues, acknowledgements, offset tracking, and graceful shutdown.
    • Added configurable consumer settings, including topics, queue sizes, polling timeouts, and Kafka properties.
    • Added reliable per-partition offset tracking and commits after contiguous message processing.
    • Improved log generator integration with camera log protobuf support.
  • Tests

    • Added coverage for message consumption, acknowledgements, queue backpressure, and offset tracking.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new Kafka parallel-consumer module with background polling, bounded record queues, acknowledgement-based offset commits, partition tracking, builder validation, and Testcontainers integration tests. It also updates log-generator APIs, dependencies, and Gradle project linkage.

Changes

Parallel Kafka Consumer

Layer / File(s) Summary
Log generator contract and module setup
common-libs/log-generator/..., .idea/gradle.xml
Exposes the camera protobuf dependency, renames the log builder method, restricts random data helpers, and links the new Gradle module.
Partition-aware offset tracking
common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/PartitionTracker.java, Tracker.java
Tracks contiguous completed offsets per partition and issues asynchronous or synchronous Kafka commits during completion and rebalance handling.
Consumer runtime and builder
common-libs/parallel-consumer/build.gradle, common-libs/parallel-consumer/src/main/java/.../KafkaParallelConsumer.java
Adds configurable Kafka polling, record and acknowledgement queues, rebalance processing, shutdown handling, and builder validation.
Kafka integration validation
common-libs/parallel-consumer/src/test/..., common-libs/test-extensions/build.gradle
Tests two-record and queue-pressure consumption scenarios with Kafka Testcontainers and exposes Testcontainers dependencies to downstream consumers.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Application
  participant KafkaParallelConsumer
  participant KafkaConsumer
  participant Tracker
  Application->>KafkaParallelConsumer: start()
  KafkaParallelConsumer->>KafkaConsumer: subscribe(topic)
  KafkaParallelConsumer->>KafkaConsumer: poll(pollTimeout)
  KafkaConsumer-->>KafkaParallelConsumer: records
  KafkaParallelConsumer->>Tracker: track(records)
  KafkaParallelConsumer-->>Application: poll() returns record
  Application->>KafkaParallelConsumer: ack(record)
  KafkaParallelConsumer->>Tracker: complete(record)
  Tracker->>KafkaConsumer: commitAsync(offset + 1)
Loading

Possibly related PRs

  • alishazaee/PathLens#2: Touches the same CameraLogGenerator utility and related method and visibility declarations.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: introducing a parallel Kafka consumer implementation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/add-parallel-kafka-consumer

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java (2)

15-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use Awaitility’s public package.

These imports couple the test to Testcontainers’ shaded implementation. Use org.awaitility.Awaitility from the declared Awaitility dependency instead; shaded packages are not stable APIs.

Proposed import change
-import org.testcontainers.shaded.org.awaitility.Awaitility;
-import org.testcontainers.shaded.org.awaitility.core.ThrowingRunnable;
+import org.awaitility.Awaitility;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java`
around lines 15 - 16, Update the imports in KafkaParallelConsumerTest to use
Awaitility and ThrowingRunnable from the declared org.awaitility public package
instead of org.testcontainers.shaded.org.awaitility, while leaving the test
behavior unchanged.

47-59: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

Wait for asynchronous acknowledgements to be committed.

ack() only enqueues the record, but the test closes the consumer as soon as two records are observed. The tracking thread may not drain the acknowledgement queue, so this test can pass without validating offset commits. Wait for a commit-visible condition, such as restarting the same consumer group and asserting no redelivery, before closing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java`
around lines 47 - 59, Update the asynchronous test flow around
consumer.ack(record) and consumer.close() so it waits for acknowledgements to be
committed, not merely for both records to be observed. Before closing the
consumer, verify commit visibility by restarting or reusing the same consumer
group and asserting the acknowledged records are not redelivered, while
preserving the existing log-content assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java`:
- Around line 123-156: Update Builder.build() to default the Kafka consumer
property enable.auto.commit to false and reject configurations that explicitly
set it to true before constructing KafkaParallelConsumer. Preserve the existing
validation and ensure acknowledgement-driven offset handling cannot be used with
auto-commit enabled.
- Around line 78-84: The close() method must stop both workers and release the
Kafka client. After setting running false, wake and interrupt the poll and
tracking threads, join each non-null thread so trackingThread cannot remain
blocked in ackedRecordsQueue.take(), then call consumer.close() once the poll
loop has exited.
- Around line 106-111: Update handleUnackedMessages() to enqueue acknowledged
records for processing by the poll-owning thread instead of calling
tracker.track(record) directly. Ensure that thread invokes
Tracker.complete(record), including commitAsync(), while preserving the existing
running-loop and queue handling behavior.

In
`@common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java`:
- Line 29: Update KafkaParallelConsumerTest so sendRecord waits for each
asynchronous producer send to complete and propagates failures before returning.
Ensure test cleanup always closes both the consumer and kafkaProducer, including
failure paths, by moving cleanup into finally or an `@AfterEach` lifecycle method.

---

Nitpick comments:
In
`@common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java`:
- Around line 15-16: Update the imports in KafkaParallelConsumerTest to use
Awaitility and ThrowingRunnable from the declared org.awaitility public package
instead of org.testcontainers.shaded.org.awaitility, while leaving the test
behavior unchanged.
- Around line 47-59: Update the asynchronous test flow around
consumer.ack(record) and consumer.close() so it waits for acknowledgements to be
committed, not merely for both records to be observed. Before closing the
consumer, verify commit visibility by restarting or reusing the same consumer
group and asserting the acknowledged records are not redelivered, while
preserving the existing log-content assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3a5805a8-9c82-4058-ac18-8590dc4dfc5f

📥 Commits

Reviewing files that changed from the base of the PR and between 96ef385 and a0db72d.

📒 Files selected for processing (10)
  • .idea/gradle.xml
  • common-libs/log-generator/build.gradle
  • common-libs/log-generator/src/main/java/ir/pathlens/generator/CameraLogGenerator.java
  • common-libs/parallel-consumer/build.gradle
  • common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java
  • common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/PartitionTracker.java
  • common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/Tracker.java
  • common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java
  • common-libs/parallel-consumer/src/test/resources/application.yml
  • common-libs/test-extensions/build.gradle

Comment on lines +78 to +84
public void close() throws Exception {
running = false;
consumer.wakeup();
if (pollThread != null) {
pollThread.interrupt();
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
fd -a 'KafkaParallelConsumer\.java$' . || true

echo "== target outline =="
file="$(fd 'KafkaParallelConsumer\.java$' . | head -n1 || true)"
if [ -n "${file:-}" ]; then
  echo "FILE=$file"
  ast-grep outline "$file" --view expanded || true
  echo "== target lines 1-140 =="
  sed -n '1,140p' "$file" | nl -ba
fi

echo "== queue/thread references =="
rg -n "trackingThread|pollThread|ackedRecordsQueue|close\\(|consumer\\.close\\(|start\\(|join\\(\\)|interrupt\\(\\)" "$file" || true

Repository: alishazaee/PathLens

Length of output: 1243


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="$(fd 'KafkaParallelConsumer\.java$' . | head -n1 || true)"
echo "FILE=$file"

echo "== lines 1-140 =="
sed -n '1,140p' "$file"

echo "== queue/thread/close references =="
rg -n "trackingThread|pollThread|ackedRecordsQueue|consumer\\.|close\\(|start\\(|join\\(\\)|interrupt\\(\\)" "$file" || true

echo "== Java version availability =="
command -v java || true
command -v javac || true
</file>

Repository: alishazaee/PathLens

Length of output: 6685


🌐 Web query:

Apache Kafka KafkaConsumer close releases network connections and leaves consumer group

💡 Result:

Yes, calling close on a KafkaConsumer is required to properly release network connections and leave the consumer group [1][2][3]. When close is invoked, the consumer performs the following actions: 1. It attempts to commit any pending offsets (if auto-commit is enabled) [2]. 2. It sends a request to the group coordinator to gracefully leave the consumer group, which triggers an immediate rebalance [3]. This allows other members of the group to take over the partitions previously assigned to the closed consumer without waiting for the group coordinator to detect a timeout due to missing heartbeats [3]. 3. It closes the network connections (sockets) to the Kafka brokers to prevent resource leaks [1][3]. The close method has a default timeout (typically 30 seconds) to allow these cleanup tasks to complete [1][2][4]. If the consumer cannot gracefully leave the group or commit offsets within the specified timeout, it will be force-closed [2]. You should always invoke close after you are finished with a consumer instance to ensure resources are freed and the group rebalances promptly [1][3].

Citations:


Finish both workers and close the Kafka client.

The current close() only wakes/interrupts the poll thread and leaves trackingThread stuck in ackedRecordsQueue.take() when no ack is queued. It also never calls consumer.close(), so broker connections and consumer-group membership are left to timeout. Join both worker threads and close the consumer after the poll loop exits.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java`
around lines 78 - 84, The close() method must stop both workers and release the
Kafka client. After setting running false, wake and interrupt the poll and
tracking threads, join each non-null thread so trackingThread cannot remain
blocked in ackedRecordsQueue.take(), then call consumer.close() once the poll
loop has exited.

Comment on lines +123 to +156
public Builder<K, V> withProperties(Properties properties) {
if (properties == null) {
throw new IllegalArgumentException("properties must not be null");
}
this.properties = properties;
return this;
}

public Builder<K, V> withQueueSize(int queueSize) {
if (queueSize <= 0) {
throw new IllegalArgumentException("queueSize must be positive, got: " + queueSize);
}
this.queueSize = queueSize;
return this;
}

public Builder<K, V> withTopic(String topic) {
this.topic = topic;
return this;
}

public Builder<K, V> withPollTimeout(Duration pollTimeout) {
if (pollTimeout == null || pollTimeout.isNegative()) {
throw new IllegalArgumentException("pollTimeout must be non-negative");
}
this.pollTimeout = pollTimeout;
return this;
}

public KafkaParallelConsumer<K, V> build() {
Validate.notNull(properties, "properties must not be null");
Validate.notNull(topic, "topic can not be null");
Validate.isTrue(queueSize > 0, "queueSize must be positive");
return new KafkaParallelConsumer<>(this);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file =="
fd -a 'KafkaParallelConsumer.java' . || true

echo "== relevant outline =="
ast-grep outline common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java --view expanded || true

echo "== file section 1-260 =="
cat -n common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java | sed -n '1,260p'

echo "== search enable.auto.commit and build usages =="
rg -n "enable.auto.commit|auto.commit|withProperties|build\\(|ack\\(" common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer || true

Repository: alishazaee/PathLens

Length of output: 9614


🌐 Web query:

Apache Kafka 4.1 consumer config enable.auto.commit default true documentation

💡 Result:

The default value for the enable.auto.commit configuration in Apache Kafka 4.1 is true [1]. When set to true, the consumer's offset is periodically committed in the background [2][3], with the frequency of these commits controlled by the auto.commit.interval.ms configuration [2][3]. This behavior has remained consistent across Apache Kafka versions [1].

Citations:


Disable Kafka auto-commit for this acknowledgement-driven consumer.

enable.auto.commit defaults to true, so Kafka can commit offsets for polled records before the caller stores them in polledRecords and eventually calls ack(). Default it to false and reject an explicit true value during build().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java`
around lines 123 - 156, Update Builder.build() to default the Kafka consumer
property enable.auto.commit to false and reject configurations that explicitly
set it to true before constructing KafkaParallelConsumer. Preserve the existing
validation and ensure acknowledgement-driven offset handling cannot be used with
auto-commit enabled.

@alishazaee
alishazaee force-pushed the feature/add-parallel-kafka-consumer branch from a0db72d to 29d13d1 Compare July 28, 2026 20:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java`:
- Around line 52-62: Update the rebalance callbacks so onPartitionsRevoked
commits the revoked partitions synchronously, then removes those same partitions
from tracker state via tracker.removePartitions(partitions). Do not remove
partitions in onPartitionsAssigned; preserve assignment tracking there so late
acknowledgements for revoked partitions are discarded.
- Around line 97-105: Update the poll loop in KafkaParallelConsumer so
drainCommittedMessages() executes once on every poll iteration, including when
ConsumerRecords is empty. Move or add the drain call outside the records loop
while preserving the existing per-record tracking and queue-offer behavior.
- Around line 49-67: Update KafkaParallelConsumer.start() to enforce a one-shot
lifecycle transition, preventing repeated calls from creating multiple poll
threads or polling the same consumer concurrently. Update close() to close the
consumer directly when start() was never completed, while preserving the
existing poll-thread shutdown and join behavior for started consumers.

In
`@common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/Tracker.java`:
- Around line 30-35: Tracker.complete must reject or discard acknowledgements
for offsets that were never registered or have already been completed, so
unknown offsets cannot advance commits. In
common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/Tracker.java
lines 30-35, retain delivered/in-flight offsets and complete each tracked offset
only once. In
common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/PartitionTrackerTest.java
lines 17-24, register offsets 102 and 103 for the out-of-order completion test
and add coverage proving an unregistered offset cannot advance the commit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8ffe3df2-1365-4dbb-8ca9-10591dbaab48

📥 Commits

Reviewing files that changed from the base of the PR and between a0db72d and 29d13d1.

📒 Files selected for processing (11)
  • .idea/gradle.xml
  • common-libs/log-generator/build.gradle
  • common-libs/log-generator/src/main/java/ir/pathlens/generator/CameraLogGenerator.java
  • common-libs/parallel-consumer/build.gradle
  • common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java
  • common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/PartitionTracker.java
  • common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/Tracker.java
  • common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java
  • common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/PartitionTrackerTest.java
  • common-libs/parallel-consumer/src/test/resources/application.yml
  • common-libs/test-extensions/build.gradle
🚧 Files skipped from review as they are similar to previous changes (7)
  • common-libs/log-generator/build.gradle
  • common-libs/parallel-consumer/src/test/resources/application.yml
  • .idea/gradle.xml
  • common-libs/parallel-consumer/build.gradle
  • common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/PartitionTracker.java
  • common-libs/test-extensions/build.gradle
  • common-libs/log-generator/src/main/java/ir/pathlens/generator/CameraLogGenerator.java

@alishazaee
alishazaee force-pushed the feature/add-parallel-kafka-consumer branch from 29d13d1 to cade1b2 Compare July 30, 2026 07:50
@alishazaee
alishazaee merged commit 720a264 into master Jul 30, 2026
1 check was pending

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java (1)

24-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Declare Awaitility as a direct test dependency.

KafkaParallelConsumerTest calls Awaitility.await(), but parallel-consumer only declares Testcontainers test dependencies, so the awaited import relies on a shaded implementation detail rather than a public API. Add an explicit org.awaitility test dependency and import org.awaitility.Awaitility.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java`
at line 24, Update KafkaParallelConsumerTest to import org.awaitility.Awaitility
instead of the Testcontainers-shaded Awaitility class, and add org.awaitility as
a direct test dependency for parallel-consumer. Keep the existing Awaitility
usage unchanged.

Source: MCP tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@common-libs/log-generator/src/main/java/ir/pathlens/generator/CameraLogGenerator.java`:
- Line 109: Restore backwards-compatible public access in CameraLogGenerator by
reintroducing generateLogProto(), randomIpv4(), randomIpv6(), and
randomPhoneNumber() as deprecated adapters that delegate to the current
implementations, preserving their prior behavior and signatures where possible.
Do not leave these API changes undocumented or inaccessible to external
consumers.

In
`@common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java`:
- Around line 99-106: Update the polling loop in KafkaParallelConsumer so
backpressure from polledRecords does not prevent consumer.poll() from running
beyond max.poll.interval.ms. Preserve delivery queue backpressure while
pausing/resuming assigned partitions, limiting each fetch to available capacity,
or using bounded enqueue waits that continue polling; ensure committed offsets
remain valid across rebalances.
- Around line 137-143: Update Builder.withProperties to preserve a
caller-provided ConsumerConfig.AUTO_OFFSET_RESET_CONFIG value by using
putIfAbsent instead of overwriting it, while retaining "earliest" only as the
default. Document that earliest is applied when no offset-reset policy is
supplied.

In
`@common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java`:
- Around line 76-90: Update the test around the generated records and Awaitility
assertion to retain the 50 original CameraLogProto.Log instances, then compare
them with the consumed logs rather than asserting only logs.size(). Use
multiset-style comparison if consumer ordering is not guaranteed, while
preserving the existing count and acknowledgment flow.

---

Nitpick comments:
In
`@common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java`:
- Line 24: Update KafkaParallelConsumerTest to import org.awaitility.Awaitility
instead of the Testcontainers-shaded Awaitility class, and add org.awaitility as
a direct test dependency for parallel-consumer. Keep the existing Awaitility
usage unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a1ba1e43-1b53-4837-b4a6-7a44aa318061

📥 Commits

Reviewing files that changed from the base of the PR and between 29d13d1 and cade1b2.

📒 Files selected for processing (10)
  • .idea/gradle.xml
  • common-libs/log-generator/build.gradle
  • common-libs/log-generator/src/main/java/ir/pathlens/generator/CameraLogGenerator.java
  • common-libs/parallel-consumer/build.gradle
  • common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java
  • common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/PartitionTracker.java
  • common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/Tracker.java
  • common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java
  • common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/PartitionTrackerTest.java
  • common-libs/test-extensions/build.gradle
🚧 Files skipped from review as they are similar to previous changes (3)
  • common-libs/test-extensions/build.gradle
  • .idea/gradle.xml
  • common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/PartitionTrackerTest.java

}

public CameraLogProto.Log.Builder generateLogProto() {
public CameraLogProto.Log.Builder generateLogBuilder() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP '\b(generateLogProto|randomIpv4|randomIpv6|randomPhoneNumber)\s*\(' --glob '*.java'

Repository: alishazaee/PathLens

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -e

# Locate and inspect the relevant file and signatures.
fd -a 'CameraLogGenerator\.java$' . | sed 's#^\./##'
file="$(fd 'CameraLogGenerator\.java$' . | head -n 1)"
if [ -n "$file" ]; then
  wc -l "$file"
  ast-grep outline "$file" --view expanded || true
  echo '--- relevant sections ---'
  sed -n '80,180p' "$file" | cat -n
fi

echo '--- search identifiers with surrounding context ---'
rg -nP '\bgenerateLogProto\b|\brandomIpv4\b|\brandomIpv6\b|\brandomPhoneNumber\b|\bgenerateLogBuilder\b|CameraLogGenerator\b' --glob '*.java' .

Repository: alishazaee/PathLens

Length of output: 8508


Preserve backwards compatibility for the public CameraLogGenerator API.

CameraLogGenerator.generateLogProto() is no longer available, and randomIpv4(), randomIpv6(), and randomPhoneNumber() are now private static, so external consumers cannot call them. Add deprecated compatibility adapters or explicitly document/version this breaking change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@common-libs/log-generator/src/main/java/ir/pathlens/generator/CameraLogGenerator.java`
at line 109, Restore backwards-compatible public access in CameraLogGenerator by
reintroducing generateLogProto(), randomIpv4(), randomIpv6(), and
randomPhoneNumber() as deprecated adapters that delegate to the current
implementations, preserving their prior behavior and signatures where possible.
Do not leave these API changes undocumented or inaccessible to external
consumers.

Comment on lines +99 to +106
ConsumerRecords<K, V> records = consumer.poll(pollTimeout);
for (ConsumerRecord<K, V> record : records) {
tracker.track(record);
drainCommittedMessages();
while (!polledRecords.offer(record)) {
drainCommittedMessages();
Thread.sleep(1);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C2 'max\.poll\.(interval|records)|while \(!polledRecords\.offer|consumer\.(pause|resume|poll)' \
  common-libs/parallel-consumer

Repository: alishazaee/PathLens

Length of output: 3255


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file outline =="
ast-grep outline common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java || true

echo "== relevant source =="
cat -n common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java

echo "== config/package hints =="
rg -n "KafkaParallelConsumer|max.poll|max.poll.records|max.poll.interval|polledRecords|pause|resume|commit" common-libs/parallel-consumer/common-libs common-libs/parallel-consumer || true

Repository: alishazaee/PathLens

Length of output: 12634


🌐 Web query:

Apache Kafka KafkaConsumer poll max.poll.interval.ms consumer liveness documentation

💡 Result:

In Apache Kafka, max.poll.interval.ms is a configuration setting that defines the maximum allowed delay between consecutive invocations of the poll method when using consumer group management [1][2]. It serves as a liveness detection mechanism to prevent a consumer from holding onto assigned partitions indefinitely if it has stalled or is stuck in a livelock [3]. Key aspects of max.poll.interval.ms include: Mechanism: The KafkaConsumer proactively tracks the time elapsed between subsequent calls to poll. If this time exceeds the configured max.poll.interval.ms, the consumer considers itself failed, stops sending heartbeats, and voluntarily leaves the consumer group [3][4][5]. This triggers a rebalance so that the partitions can be reassigned to other healthy consumers [1][3]. Purpose and Liveness: This timeout is distinct from the session timeout (session.timeout.ms). While the session timeout monitors consumer health via background heartbeats to detect crashes or network issues, max.poll.interval.ms monitors the application's ability to process the records it has fetched [3][2][5]. It is specifically designed to handle scenarios where the consumer is technically alive (heartbeating in the background) but is not making progress in processing records [3]. Default Behavior and Tuning: - Default Value: The default is 300,000 milliseconds (5 minutes) [1][6][7]. - Triggering: Hitting this timeout typically indicates that your application code takes longer to process a batch of records than the allotted interval [4][5]. - Mitigation: To address frequent timeouts, you can increase max.poll.interval.ms to accommodate longer processing times or decrease max.poll.records to reduce the number of records returned in each poll, thereby shortening the time required to complete the poll loop [4][2][5]. Special Case for Static Membership: For consumers configured with a non-null group.instance.id (static members), reaching the max.poll.interval.ms timeout does not trigger an immediate partition reassignment [1]. Instead, the consumer stops sending heartbeats and the partitions will only be reassigned after the session timeout expires [1][4].

Citations:


Keep poll() running while the delivery queue fills up.

The inner while (!polledRecords.offer(record)) loop can block for max.poll.interval.ms without calling consumer.poll() again, causing the consumer group to leave the assignment and making async commit offsets stale on rebalance. Pause/resume the assigned partitions while accepting backpressure, limit fetched batches to available queue capacity, or use bounded blocking enqueue behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java`
around lines 99 - 106, Update the polling loop in KafkaParallelConsumer so
backpressure from polledRecords does not prevent consumer.poll() from running
beyond max.poll.interval.ms. Preserve delivery queue backpressure while
pausing/resuming assigned partitions, limiting each fetch to available capacity,
or using bounded enqueue waits that continue polling; ensure committed offsets
remain valid across rebalances.

Comment on lines +137 to +143
public Builder<K, V> withProperties(Properties properties) {
if (properties == null) {
throw new IllegalArgumentException("properties must not be null");
}
this.properties = properties;
this.properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");;
return this;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C2 'AUTO_OFFSET_RESET_CONFIG|auto\.offset\.reset' common-libs

Repository: alishazaee/PathLens

Length of output: 1776


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,190p' common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java

Repository: alishazaee/PathLens

Length of output: 6370


Respect the caller’s offset-reset configuration.

withProperties() currently overwrites any explicit latest/none policy with earliest, so a new group can unexpectedly replay retained records. Use putIfAbsent(...) and document the default.

Proposed fix
-            this.properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");;
+            this.properties.putIfAbsent(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public Builder<K, V> withProperties(Properties properties) {
if (properties == null) {
throw new IllegalArgumentException("properties must not be null");
}
this.properties = properties;
this.properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");;
return this;
public Builder<K, V> withProperties(Properties properties) {
if (properties == null) {
throw new IllegalArgumentException("properties must not be null");
}
this.properties = properties;
this.properties.putIfAbsent(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
return this;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@common-libs/parallel-consumer/src/main/java/ir/pathlens/parallelconsumer/KafkaParallelConsumer.java`
around lines 137 - 143, Update Builder.withProperties to preserve a
caller-provided ConsumerConfig.AUTO_OFFSET_RESET_CONFIG value by using
putIfAbsent instead of overwriting it, while retaining "earliest" only as the
default. Document that earliest is applied when no offset-reset policy is
supplied.

Comment on lines +76 to +90
for (int i = 0; i < 50; i++) {
CameraLogProto.Log log = CameraLogGenerator.randomLog().generateLogBuilder().build();
sendRecord(new ProducerRecord<>("test-2", log.toByteArray()));
}
consumer.start();
List<CameraLogProto.Log> logs = new ArrayList<>();
Awaitility.await()
.atMost(Duration.ofSeconds(10))
.untilAsserted(() -> {
ConsumerRecord<byte[], byte[]> record = consumer.poll();
if (record != null) {
logs.add(CameraLogProto.Log.parseFrom(record.value()));
consumer.ack(record);
}
assertEquals(50, logs.size());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Assert the consumed records, not only their count.

Retain the 50 generated logs and compare them with the consumed logs (as a multiset if ordering is not guaranteed). The current assertion passes even when records are duplicated, stale, or otherwise incorrect.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@common-libs/parallel-consumer/src/test/java/ir/pathlens/parallelconsumer/KafkaParallelConsumerTest.java`
around lines 76 - 90, Update the test around the generated records and
Awaitility assertion to retain the 50 original CameraLogProto.Log instances,
then compare them with the consumed logs rather than asserting only logs.size().
Use multiset-style comparison if consumer ordering is not guaranteed, while
preserving the existing count and acknowledgment flow.

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.

1 participant