-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-10167: use the admin client to read end-offset #8876
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
Changes from 5 commits
b9a27a8
a322765
b4589e6
a1a4bb6
7f81699
2fd0ea5
67bf693
6934cd6
ebe86f9
097dbca
8d80cce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,9 @@ | |
| */ | ||
| package org.apache.kafka.streams.processor.internals; | ||
|
|
||
| import org.apache.kafka.clients.admin.Admin; | ||
| import org.apache.kafka.clients.admin.ListOffsetsResult; | ||
| import org.apache.kafka.clients.admin.OffsetSpec; | ||
| import org.apache.kafka.clients.consumer.Consumer; | ||
| import org.apache.kafka.clients.consumer.ConsumerRecord; | ||
| import org.apache.kafka.clients.consumer.ConsumerRecords; | ||
|
|
@@ -43,6 +46,8 @@ | |
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
| import java.util.concurrent.ExecutionException; | ||
| import java.util.function.Function; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| import static org.apache.kafka.streams.processor.internals.ClientUtils.fetchCommittedOffsets; | ||
|
|
@@ -199,6 +204,9 @@ int bufferedLimitIndex() { | |
| // to update offset limit for standby tasks; | ||
| private Consumer<byte[], byte[]> mainConsumer; | ||
|
|
||
| // the changelog reader needs the admin client to list end offsets | ||
| private final Admin adminClient; | ||
|
|
||
| private long lastUpdateOffsetTime; | ||
|
|
||
| void setMainConsumer(final Consumer<byte[], byte[]> consumer) { | ||
|
|
@@ -208,11 +216,13 @@ void setMainConsumer(final Consumer<byte[], byte[]> consumer) { | |
| public StoreChangelogReader(final Time time, | ||
| final StreamsConfig config, | ||
| final LogContext logContext, | ||
| final Admin adminClient, | ||
| final Consumer<byte[], byte[]> restoreConsumer, | ||
| final StateRestoreListener stateRestoreListener) { | ||
| this.time = time; | ||
| this.log = logContext.logger(StoreChangelogReader.class); | ||
| this.state = ChangelogReaderState.ACTIVE_RESTORING; | ||
| this.adminClient = adminClient; | ||
| this.restoreConsumer = restoreConsumer; | ||
| this.stateRestoreListener = stateRestoreListener; | ||
|
|
||
|
|
@@ -564,8 +574,16 @@ private Map<TopicPartition, Long> endOffsetForChangelogs(final Set<TopicPartitio | |
| return Collections.emptyMap(); | ||
|
|
||
| try { | ||
| return restoreConsumer.endOffsets(partitions); | ||
| } catch (final TimeoutException e) { | ||
| if (adminClient != null) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need this distinction? Seems we set
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also do not understand the distinction. When would we want to call
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. req: Could you add a test (or adapt an existing one) and verify whether during
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just a hack-around: I will always use admin-client by passing it via the constructor. I will add a unit test. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we remove this check then?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can remove this
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just saw that we kinda need to pass |
||
| final ListOffsetsResult result = adminClient.listOffsets(partitions.stream().collect( | ||
| Collectors.toMap(Function.identity(), tp -> OffsetSpec.latest()))); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we set
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, will do. |
||
| return result.all().get().entrySet().stream().collect( | ||
| Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().offset())); | ||
| } else { | ||
| // we only fall back to use restore consumer if admin client is not set in TTD | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this case ever apply? TTD should never restore any task from my understanding.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TTD would still call this function though... after some thinking I've decided to add a mock-admin to streams:test-utils (the existing mock is in clients:test and hence cannot be relied on). |
||
| return restoreConsumer.endOffsets(partitions); | ||
| } | ||
| } catch (final TimeoutException | InterruptedException | ExecutionException e) { | ||
| // if timeout exception gets thrown we just give up this time and retry in the next run loop | ||
| log.debug("Could not fetch all end offsets for {}, will retry in the next run loop", partitions); | ||
| return Collections.emptyMap(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.