-
Notifications
You must be signed in to change notification settings - Fork 544
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
kafka replay speed: upstream concurrent fetchers #9452
Merged
dimitarvdimitrov
merged 6 commits into
main
from
dimitar/ingest/replay-speed/concurrent-fetchers
Sep 27, 2024
Merged
kafka replay speed: upstream concurrent fetchers #9452
dimitarvdimitrov
merged 6 commits into
main
from
dimitar/ingest/replay-speed/concurrent-fetchers
Sep 27, 2024
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is the first of series of PRs to upstream the code for improving Kafka replay speed in the ingester. In this PR I'm upstreaming a tiny change related to partitionOffsetReader. We need caching in the reader so that we can check the start offset of the partition. We don't need that to be very exact because we use it to find out if we're trying to consume from before the start. Signed-off-by: Dimitar Dimitrov <[email protected]>
dimitarvdimitrov
requested review from
stevesg,
grafanabot,
a team and
tacole02
as code owners
September 27, 2024 13:50
dimitarvdimitrov
force-pushed
the
dimitar/ingest/replay-speed/concurrent-fetchers
branch
from
September 27, 2024 13:52
a595a91
to
2938fa9
Compare
This is the second of series of PRs to upstream the code for improving Kafka replay speed in the ingester. In this PR I'm upstreaming the fetching code. The core of the change is in `concurrentFetchers`. # `concurrentFetchers` Overview * **Segmentation (fetchWant):** The fetcher divides the work into segments called fetchWants. Each fetchWant represents a range of records to be fetched, defined by a start and end offset. This segmentation allows for concurrent fetching of different parts of the topic partition. * **Concurrent Fetching:** Multiple goroutines (defined by the concurrency parameter) work on fetching these segments simultaneously. Each goroutine runs the `run` method, which processes fetchWants from a channel. * **Fetching Process:** For each fetchWant, the fetcher attempts to retrieve the records through the `fetchSingle` method. This method: * Finds the leader for the partition * Builds a fetch request * Sends the request to the Kafka broker * Parses the response * **Multiple Attempts:** If a fetch attempt fails or doesn't retrieve all requested records, the fetcher will retry. It uses an error backoff mechanism to avoid overwhelming the system with rapid retries. The fetcher updates the start offset of the fetchWant based on the last successfully fetched record and continues until all requested records are retrieved or the context is cancelled. * **Overfetching Risk:** The system risks overfetching because it might retrieve records that have already been processed. This is handled by: * Tracking the last returned record offset (`lastReturnedRecord`) * Using `recordIndexAfterOffset` to find the first new record in each fetch result * Discarding any duplicate records before passing them to the consumer * **Ordering:** The fetcher ensures that segments are processed in order by: * Using a linked list (`pendingResults`) to keep track of fetch results in the order they were requested * Buffering results in `bufferedResult` and only sending them to `orderedFetches` channel when they're next in sequence * The `PollFetches` method, which consumers call to get records, receives from the `orderedFetches` channel, ensuring records are always returned in the correct order * **Adaptive Fetching:** The system adapts the size of fetch requests based on previous results. It estimates the bytes per record and adjusts the `MaxBytes` parameter of fetch requests accordingly, trying to optimize the amount of data fetched in each request. Signed-off-by: Dimitar Dimitrov <[email protected]> Co-authored-by: gotjosh <[email protected]> Signed-off-by: Dimitar Dimitrov <[email protected]>
dimitarvdimitrov
force-pushed
the
dimitar/ingest/replay-speed/concurrent-fetchers
branch
from
September 27, 2024 13:53
2938fa9
to
c33dc72
Compare
Signed-off-by: Dimitar Dimitrov <[email protected]>
Signed-off-by: Dimitar Dimitrov <[email protected]>
gotjosh
approved these changes
Sep 27, 2024
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.
LGTM
My comments are all nits, I don't need to see this again and you can merge after you've decided which ones you want to pick.
Co-authored-by: gotjosh <[email protected]>
Signed-off-by: Dimitar Dimitrov <[email protected]>
dimitarvdimitrov
deleted the
dimitar/ingest/replay-speed/concurrent-fetchers
branch
September 27, 2024 15:49
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This is the second of series of PRs to upstream the code for improving Kafka replay speed in the ingester.
In this PR I'm upstreaming the fetching code. The core of the change is in
concurrentFetchers
.I'm submitting the PR, but the work was done jointly by @gotjosh and myself
franz-go fork
We need the changes in this PR twmb/franz-go#803, so for now I'm running a fork
concurrentFetchers
OverviewSegmentation (fetchWant): The fetcher divides the work into segments called fetchWants. Each fetchWant represents a range of records to be fetched, defined by a start and end offset. This segmentation allows for concurrent fetching of different parts of the topic partition.
Concurrent Fetching: Multiple goroutines (defined by the concurrency parameter) work on fetching these segments simultaneously. Each goroutine runs the
run
method, which processes fetchWants from a channel. The results (fetchResult
) of the fetch are send to another channel:fetchWant.result
.startup-fetch-concurrency & .startup-records-per-fetch
and.ongoing-fetch-concurrency & .ongoing-records-per-fetch
Multiple Attempts: If a fetch attempt fails or doesn't retrieve all requested records, the fetcher will retry. The fetcher updates the start offset of the fetchWant based on the last successfully fetched record and continues until all requested records are retrieved.
Overfetching Risk: The system risks overfetching due to limitations in the Kafka protocol:
lastReturnedRecord
). We uselastReturnedRecord
to remove any records from afetchResult
that may have already been processed.Ordering: The fetcher ensures that segments are processed in order by:
pendingResults
) to keep track of fetch results channels in the order they were requestedbufferedResult
and only sending them toorderedFetches
channel when they're next in sequencePollFetches
method, which consumers call to get records, receives from theorderedFetches
channel, ensuring records are always returned in the correct orderAdaptive Fetching: The system adapts the size of fetch requests based on previous results. It estimates the bytes per record and adjusts the
MaxBytes
parameter of fetch requests accordingly, trying to optimize the amount of data fetched in each request.Changes to existing code
Most of this PR introduces new code. However, there are some changes to existing behaviours
processNextFetchesUntilTargetOrMaxLagHonored
: the second attempt is now allowed more time. The reason for this is that the catchup speed when during stable state-consumption is slower. For example, this means that we can't reduce lag from 15s to 2s within 15s. In those cases leaving 30s is enough to catch up to below 2s lag.Testing
concurrentFetchers
is now the default in unit tests inpkg/storage/ingest
. This increases the test coverage significantly and didn't require us to write so many tests.Future work
concurrentFetchers
- this would unblock making concurrent fetchers the default (some e2e tests fail if we can't resolve the topic the first time)