-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(admin): add KIP-396 list/alter offsets APIs #3419
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
edad338 to
11e34c1
Compare
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
admin_offsets.go
Outdated
|
|
||
| // OffsetSpec specifies which offset to look up for a partition. | ||
| type OffsetSpec struct { | ||
| timestamp int64 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not sure why we need a ton of constructors, when we can simply export this value.
After all, given the functionality provided, we can already arbitrarily mutate any given OffsetSpec and access the field at will:
offspec := OffsetSpecLatest()
offspec = OffsetSpecForTimestamp(arbitraryTimestamp)
go func() {
offspec = OffsetSpecForTimestamp(OffsetNewest)
}()
ts := offspec.Timestamp() // write-read race conditionThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not sure why we need a ton of constructors, when we can simply export this value.
After all, given the functionality provided, we can already arbitrarily mutate any given
OffsetSpecand access the field at will:offspec := OffsetSpecLatest() offspec = OffsetSpecForTimestamp(arbitraryTimestamp) go func() { offspec = OffsetSpecForTimestamp(OffsetNewest) }() ts := offspec.Timestamp() // write-read race condition
Agree that we should not mix two different input styles for the same concept (as noted in #3419 (comment)).
To stay consistent with existing Sarama APIs (e.g. Client.GetOffset(topic, partition, time int64) which uses OffsetOldest/OffsetNewest), I removed OffsetSpec and made ListOffsets take int64 directly (pass OffsetOldest/OffsetNewest or a millisecond timestamp).
Done in commit f3e40c7.
admin_offsets.go
Outdated
| close(results) | ||
|
|
||
| allResults := make(map[TopicPartitionID]*ListOffsetsResult, len(partitions)) | ||
| var firstErr error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var errs []error
for res := range results {
if res.err != nil {
errs = append(errs, res.err)
}
...
}
return allResults errors.Join(errs...)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var errs []error for res := range results { if res.err != nil { errs = append(errs, res.err) } ... } return allResults errors.Join(errs...)
Addressed in commit bce2339 by aggregating all errors with errors.Join.
admin_offsets.go
Outdated
|
|
||
| for _, req := range requests { | ||
| wg.Add(1) | ||
| go func(req *brokerOffsetRequest) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How many requests are we likely to be spinning off here?
It can happen that spinning off too many goroutines will actually be performance-degrading rather than performance-enhancing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How many requests are we likely to be spinning off here?
It can happen that spinning off too many goroutines will actually be performance-degrading rather than performance-enhancing.
Thanks for the note. The Java AdminClient doesn’t appear to impose an explicit concurrency cap either — it groups by broker and issues one in‑flight request per broker via the admin I/O loop.
Key entry point (no concurrency limiting logic):
In practice, the Java I/O loop is equivalent to spawning goroutines in Go: both drive concurrent in‑flight requests without a per‑broker cap.
Given typical Kafka clusters are well below 1,000 brokers, I believe even 10,000 concurrent in‑flight requests should be relatively easy for modern hardware to handle.
admin.go
Outdated
| ListOffsets(partitions map[TopicPartitionID]OffsetSpec, options *ListOffsetsOptions) (map[TopicPartitionID]*ListOffsetsResult, error) | ||
|
|
||
| // AlterConsumerGroupOffsets alters offsets for the specified group by committing the provided offsets and metadata. | ||
| // The request targets the group's coordinator and returns per-partition results in the response. | ||
| // This operation is not transactional so it may succeed for some partitions while fail for others. | ||
| AlterConsumerGroupOffsets(group string, offsets map[TopicPartitionID]OffsetAndMetadata, options *AlterConsumerGroupOffsetsOptions) (*OffsetCommitResponse, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our current design uses map[string]map[int32]V adding this TopicPartitionID might have been nice if we had started with it, but we haven’t really. https://pkg.go.dev/github.com/IBM/sarama#AlterPartitionReassignmentsResponse
Providing two different ways to do something is likely to increase confusion over any benefits of flattening the maps.
It also reduces the ability to iterate over topics individually, without needing to then iterate over all Topic × Partition combinations and select for Topic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our current design uses
map[string]map[int32]Vadding thisTopicPartitionIDmight have been nice if we had started with it, but we haven’t really. https://pkg.go.dev/github.com/IBM/sarama#AlterPartitionReassignmentsResponseProviding two different ways to do something is likely to increase confusion over any benefits of flattening the maps.
It also reduces the ability to iterate over topics individually, without needing to then iterate over all
Topic×Partitioncombinations and select forTopic.
Thanks for the point about keeping the API shape consistent. I reverted to the existing map[string]map[int32]V style so callers can iterate by topic without scanning all partitions.
This removes TopicPartitionID from ListOffsets and aligns the input/return maps with the rest of Sarama’s admin APIs.
Done in commit be65797.
admin_offsets.go
Outdated
| } | ||
|
|
||
| // ListOffsetsResult contains the response for a single topic partition. | ||
| type ListOffsetsResult struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not sure this is an Offsets result? As it seems to be single offset result?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed ListOffsetsResult to OffsetResult to reflect the single-partition result; done in commit f9150fe.
admin_offsets.go
Outdated
| type brokerOffsetRequest struct { | ||
| broker *Broker | ||
| request *OffsetRequest | ||
| partitions []TopicPartitionID | ||
| } | ||
|
|
||
| type brokerOffsetResult struct { | ||
| result map[TopicPartitionID]*ListOffsetsResult | ||
| err error | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also isolate/scope these types into the ListOffsets right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also isolate/scope these types into the
ListOffsetsright?
Updated in commit 243445e (scoped the helper types inside ListOffsets).
admin_offsets.go
Outdated
| req = &brokerOffsetRequest{ | ||
| broker: broker, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not sure why we double track the broker? We’re using it as the key, and as a field in the value?
We could just for broker, req := range requests { … } below, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not sure why we double track the
broker? We’re using it as the key, and as a field in the value?We could just
for broker, req := range requests { … }below, right?
Addressed in commit ea32b6e by removing the redundant broker field and passing the broker via the map key in the loop.
2e502be to
ad782bd
Compare
Co-authored-by: Cassondra Foesch <puellanivis@users.noreply.github.com> Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Co-authored-by: Cassondra Foesch <puellanivis@users.noreply.github.com> Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Co-authored-by: Cassondra Foesch <puellanivis@users.noreply.github.com> Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Signed-off-by: DCjanus <DCjanus@dcjanus.com>
Summary
ListOffsetsandAlterConsumerGroupOffsetsto support bulk offset queries and group offset commits.OffsetSpec,OffsetAndMetadata).Key changes
ListOffsetsandAlterConsumerGroupOffsetstoClusterAdminand wire protocol requests.Constraints / tradeoffs
OffsetSpecis a new type instead of reusingGetOffset(int64)becauseGetOffsetrelies on magicint64values (earliest/latest) whileListOffsetsneeds an explicit spec (earliest/latest/timestamp); this matches the Java AdminClient surface defined in KIP-396 and keeps room for future spec variants without breaking changes.Notes