[9.5](backport #51801) filestream: fix duplicate harvesters on growing fingerprint migration#51826
Merged
Conversation
…#51801) With file_identity.fingerprint.growing enabled, the registry key migration that follows a file's fingerprint growth raced with asynchronously starting harvesters. Start reserves the source id synchronously, but the harvester goroutine only registers and locks its resource when it gets to run. A migration landing in that window ignored the reserved-but-unregistered harvester, so the follow-up Start of the new key spawned a second harvester for the same file, while the pending one registered under the stale key, locked a fresh offset-0 resource, and re-read the whole file. Every line was ingested once per extra harvester (2x-3x observed) and the migrated-away keys were resurrected in the registry with cursor data. The reader bookkeeping now stores a handle from reservation on and the whole identity chain moves atomically: - reserve() returns a *reader handle; the goroutine upgrades it via register(), which follows any re-keying that happened in the meantime and fails if the reservation was removed, so a Stop that arrives before the harvester starts now sticks. - HarvesterGroup.Migrate re-keys the registry entry (UpdateKey) and any registration in one critical section, and derives the new key from the Source so it always matches what a follow-up Start reserves. - Harvesters read their (key, resource) pair atomically; because UpdateKey re-keys resources in place, the pair stays live across migrations and a stale key can no longer mint a fresh resource. - UpdateKey rolls back its in-memory swap when persisting fails, so a failed migration leaves a consistent old-keyed world; the prospector keeps the file flowing under its old identity and retries on a later scan, pruning index entries whose registry key is gone (ErrKeyGone). - Stop is synchronous so a queued Stop can never cancel a newer Start's reservation, and stopping is no longer delayed behind the harvester_limit semaphore. (cherry picked from commit ba9c83b)
Contributor
🤖 GitHub commentsJust comment with:
|
|
Pinging @elastic/elastic-agent-data-plane (Team:Elastic-Agent-Data-Plane) |
orestisfl
approved these changes
Jul 9, 2026
This file contains hidden or 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
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.
Proposed commit message
Checklist
[ ] I have made corresponding changes to the documentation[ ] I have made corresponding change to the default configuration filesstresstest.shscript to run them under stress conditions and race detector to verify their stability.[ ] I have added an entry in(skip-changelog: the growing fingerprint feature is unreleased)./changelog/fragmentsusing the changelog tool.Disruptive User Impact
None.
How to test this PR locally
Unit and integration tests:
Reproduce with
harvester_limitwhich keeps starting harvesters queued on the task group semaphore.Create ~30 files with ~600 bytes (below the 1024-byte fingerprint threshold), start filebeat, then append lines in two waves so each file first grows below the threshold and then crosses it. Group the output docs by
(log.file.path, log.offset): before this fix files that were queued behind the limit get every line 2-3 times (andfilebeat.harvester.startedexceeds the file count); after it every pair appears exactly once.This is an automatic backport of pull request #51801 done by [Mergify](https://mergify.com).