Visitar URL original
feat(gateways): answer OffsetCommit and OffsetFetch by krishvishal · Pull Request #4415 · apache/iggy · GitHub
Skip to content

feat(gateways): answer OffsetCommit and OffsetFetch - #4415

Merged
krishvishal merged 3 commits into
masterfrom
kafka-offsets
Oct 8, 2026
Merged

krishvishal merged 3 commits into
masterfrom
kafka-offsets

Conversation

@krishvishal

@krishvishal krishvishal commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Closes #3542

With IGGY_KAFKA_BRIDGE_ENABLED=true, OffsetCommit (8) and OffsetFetch (9) now keep group offsets in Iggy, using the ExternalGroup kind from #4392. A group resumes from its last commit, also after a gateway restart, and kafka-console-consumer.sh --group commits and resumes.

How offsets are stored

  • key: the partition, plus Iggy consumer group kafka.cg.<group> on the mapped topic. The gateway creates that group on the first commit and never joins it
  • value: the Kafka offset as sent, no range check. 0 on an empty partition and one past the last message both work
  • a negative offset deletes the key. Metadata and leader epoch are dropped, OffsetFetch returns "" and -1
  • nothing deletes these groups on its own. OFFSET_STORAGE.md has the iggy consumer-group delete command for cleanup

Commit and fetch

  • OffsetCommit v2-v9 checks the committer the way Kafka's validateOffsetCommit does. After a gateway restart a member gets UNKNOWN_MEMBER_ID and rejoins. Admin tools commit with a negative generation, which works on an empty group
  • OffsetFetch v1-v9, batched groups from v8. A null topic list (Admin.listConsumerGroupOffsets) reads every topic that holds the group
  • no key, no group or no topic reads as -1 with no error. Any other failed read fails the whole group instead, because a consumer that gets -1 resets its position
  • retriable failures go back as 14, RequestTooOld too. Java fails offset calls on 6, and OffsetFetch on 7. A replay that already landed (RequestAlreadyApplied) counts as done
  • anything sent as -1 is logged at error! with the group and the cause

Iggy calls

  • offset calls run on 4 slots, and each slot has one Iggy client for commits and one for reads. Reads never wait behind a commit, and nothing waits behind Produce. The topic listing for a null topic list runs on a slot too
  • a request keeps at most one call queued per slot, so one big request (a null topic list over many topics, say) can't hold up the others. Inside that, its partitions, the groups of a v8 fetch and the topics of a null topic list run at once
  • each partition sticks to one slot, and a topic's partitions take the slots in turn. A commit holds its slot until it ends, 45s at most. Commits on a slot share one client that sends one call at a time, which keeps them in order even when a caller gives up. A read frees its slot at the deadline
  • a request gives up after half the min session timeout (3s by default). Heartbeats on the same connection wait behind it, so a request also gives up after half of what's left of the session of the member that heartbeats there. Partitions cut off by that answer 14 without a call

Server change

get_consumer_offset answered a read that timed out on the owning shard, or found the partition not loaded there, with an empty body. Over HTTP the not-loaded case was a 404. The SDK reads both as "no offset", and a consumer would reset. Both now answer TransientNotAccepted (503 over HTTP), which the Rust SDK retries. This is in core/server/src/dispatch/partition.rs and core/server/src/http/handlers.rs and applies to every client.

Known limits

  • every Iggy server needs a build with kind 3 (feat(server): store offsets of groups managed outside Iggy #4392) before the first commit. Nothing enforces that yet, and in a partly upgraded cluster one commit can stop a partition from committing
  • in a cluster an offset read can land on a node that lags. Right after a group's first commit that node answers -1, and a consumer on auto.offset.reset=latest skips records. Reads from the primary are Serve consumer offset reads from the partition primary #4409
  • with more than one gateway, a commit with no generation on a gateway without the group's members overwrites the live offsets, and a commit a gateway gave up on can land after a newer one from another gateway. Both are in OFFSET_STORAGE.md
  • delivery stays at-least-once

@github-actions github-actions Bot added the S-waiting-on-review PR is waiting on a reviewer label Oct 6, 2026
@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.96970% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.47%. Comparing base (af706e6) to head (e1e52e6).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
core/server/src/dispatch/partition.rs 97.95% 3 Missing ⚠️
core/server/src/http/handlers.rs 88.88% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #4415      +/-   ##
============================================
+ Coverage     87.82%   88.47%   +0.65%     
- Complexity     1607     1608       +1     
============================================
  Files          1302     1302              
  Lines        251971   252088     +117     
  Branches     212264   212382     +118     
============================================
+ Hits         221299   223043    +1744     
+ Misses        25386    23658    -1728     
- Partials       5286     5387     +101     
Components Coverage Δ
Rust Core 89.60% <96.96%> (-0.03%) ⬇️
Java SDK 68.82% <ø> (+0.01%) ⬆️
C# SDK 77.83% <ø> (+16.39%) ⬆️
Python SDK 91.25% <ø> (ø)
PHP SDK 85.67% <ø> (ø)
Node SDK 96.65% <ø> (+1.66%) ⬆️
Go SDK 70.32% <ø> (+0.10%) ⬆️
C++ SDK 86.60% <ø> (ø)
Files with missing lines Coverage Δ
core/server/src/http/handlers.rs 78.65% <88.88%> (-1.49%) ⬇️
core/server/src/dispatch/partition.rs 95.99% <97.95%> (+0.05%) ⬆️

... and 96 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ryerraguntla ryerraguntla left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Follower lag that returns -1 stays pre-existing (partition.rs:678, documented, #4409). It is not a second defect on top of the NotFound hole.

Comment thread core/server/src/dispatch/partition.rs Outdated
Comment thread gateways/kafka/src/bridge/iggy_bridge/group_offsets.rs Outdated
Comment thread gateways/kafka/src/protocol/handlers/offset_fetch.rs Outdated
Comment thread gateways/kafka/src/protocol/handlers/offset_fetch.rs Outdated
Comment thread gateways/kafka/src/protocol/handlers/offset_fetch.rs Outdated
Comment thread gateways/kafka/src/protocol/handlers/offset_commit.rs
Comment thread gateways/kafka/src/bridge/iggy_bridge/group_offsets.rs Outdated
Comment thread gateways/kafka/docs/kafka_api_keys_reference.md Outdated
Comment thread gateways/kafka/README.md
Comment thread gateways/kafka/tests/offsets_real_bridge_tests.rs Outdated
@ryerraguntla

Copy link
Copy Markdown
Contributor

/author

@github-actions github-actions Bot added S-waiting-on-author PR is waiting on author response and removed S-waiting-on-review PR is waiting on a reviewer labels Oct 7, 2026
@krishvishal
krishvishal force-pushed the kafka-offsets branch 3 times, most recently from 2950cd0 to 35968df Compare October 7, 2026 12:21
@krishvishal
krishvishal added this pull request to stack #4432 October 7, 2026 13:30

@hubcio hubcio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

most of this is about the offset slots - a stuck commit blocks reads on its slot, and the permit frees at SLOT_LIMIT while the SDK call still runs. two notes outside the diff:

warning: core/server/src/http/handlers.rs:1373 still answers 404 for PartitionReadReply::NotFound, which the HTTP SDK turns into Ok(None). map it to TransientNotAccepted like TCP now does, and fix the doc at line 1331 that still says TCP sends an empty body.

nit: the get_consumer_offset doc in core/common/src/traits/consumer_offset_client.rs:45 doesn't list TransientNotAccepted, which the server now sends for an unanswered read. add the line the delete_consumer_offset doc already has.

Comment thread gateways/kafka/src/bridge/iggy_bridge/group_offsets.rs Outdated
Comment thread gateways/kafka/src/bridge/iggy_bridge/group_offsets.rs Outdated
Comment thread gateways/kafka/src/protocol/handlers/offset_fetch.rs
Comment thread gateways/kafka/src/bridge/iggy_bridge/group_offsets.rs Outdated
Comment thread gateways/kafka/src/bridge/iggy_bridge/group_offsets.rs Outdated
Comment thread gateways/kafka/docs/CONSUMER_GROUPS.md Outdated
Comment thread core/server/src/dispatch/partition.rs
Comment thread gateways/kafka/src/bridge/iggy_bridge/group_offsets.rs Outdated
Comment thread gateways/kafka/src/bridge/iggy_bridge/group_offsets.rs Outdated
Comment thread gateways/kafka/src/group/state.rs
@krishvishal

Copy link
Copy Markdown
Member Author

the two outside the diff are in a85d90f:

  • http answers 503 TransientNotAccepted for NotFound now, like tcp. doc fixed, small test added
  • get_consumer_offset doc has the TransientNotAccepted line now

@krishvishal

Copy link
Copy Markdown
Member Author

/ready

@github-actions github-actions Bot added S-waiting-on-review PR is waiting on a reviewer and removed S-waiting-on-author PR is waiting on author response labels Oct 8, 2026
@krishvishal
krishvishal dismissed ryerraguntla’s stale review October 8, 2026 12:52

Addressed review comments.

@krishvishal
krishvishal merged commit 789c2e0 into master Oct 8, 2026
83 checks passed
@krishvishal
krishvishal deleted the kafka-offsets branch October 8, 2026 12:53
krishvishal added a commit that referenced this pull request Oct 8, 2026
Closes #3544

Stacked on #4415, base is `kafka-offsets`. I'll rebase onto master after
#4415 merges. CI doesn't run on PRs into other branches, it will once
this targets master.

Two tests #3544 still needed, both with real Java clients through the
bridge. The `--group` resume test in #4415 covers the rest.

### Tests

-
`given_a_group_that_committed_when_the_gateway_restarts_should_resume_after_it`:
the group commits, the gateway stops and a new one starts on the same
`iggy-server`. The group only gets the record written after its commit.
G10 in `MANUAL_TESTING.md`
-
`given_two_members_of_one_group_when_one_leaves_should_let_the_other_resume_its_partition`:
two `kafka-verifiable-consumer.sh` members split a 2-partition topic.
One leaves, the other picks up its partition at the leaver's commit,
nothing read twice or skipped. new G11
- it waits on the consumer's JSON events, no sleeps

### Harness

- `start_bridged_gateway` returns a handle that can stop the gateway,
`spawn_bridged_gateway` uses it
- `produce_to_partition` takes a topic and partition
- `RunningClient` keeps a container running and streams its output. the
container is named so a failed test still removes it, and `timeout`
inside caps it
- `serde_json` dev-dependency for the consumer's events
@github-actions github-actions Bot removed the S-waiting-on-review PR is waiting on a reviewer label Oct 8, 2026
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.

Kafka gateway: Kafka OffsetCommit and OffsetFetch with persisted consumer offsets

4 participants