-
Notifications
You must be signed in to change notification settings - Fork 537
fix: Compute signatures from files so that loki.source.file can handle atomic writes #5143
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
Merged
Merged
Changes from 23 commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
fdf8d19
fix: add signature for tail.File so we can handle atomic writes
kalleep 5687a9b
fix: if generated signature is empty we always asume they are not equal
kalleep 3e7c421
smarter signature updates
kalleep cbb17d8
use 1024 as ideal identity size
kalleep 85ac6ab
we don't have to check offset because we already caches the lastOffset
kalleep 6585c27
Handle edgecase where a file gets parialy truncated
kalleep b62379d
fix: close file in case of errors.
kalleep 57d8d12
recompute once max size is reached
kalleep e2e8106
address comments
kalleep 3b2aa76
add tests
kalleep 6fe1a0e
Close file if error occurs in NewFile
kalleep 7bc73f7
Add more test
kalleep 687114f
fix test
kalleep 44a9281
Merge branch 'main' into kalleep/tailer-signature
kalleep 8ad1e28
Change to use ReadAt, this will not affact file position
kalleep 50ec656
close file if we cannot create reader and ignore ErrClosed
kalleep 29e707e
revert ignore
kalleep fad38c0
fix comment
kalleep aedbd61
Merge branch 'main' into kalleep/tailer-signature
kalleep 40f72dd
fix: use atomic package to perform writes in test
kalleep ba8bbcb
Use ReplaceFileW instead of atomic package for windows atomic writes.
kalleep c8723d9
Update comments
kalleep da29c49
fix comment
kalleep f997287
move to test only func
kalleep 77209ea
rename file
kalleep d395d2e
fix type
kalleep f680e17
fix assert
kalleep ce9b8cd
remove remove call, the file is move during FileRenameW
kalleep 7c4e710
Update internal/component/loki/source/file/internal/tail/signature.go
kalleep 18a8f08
fix function name
kalleep File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
78 changes: 78 additions & 0 deletions
78
internal/component/loki/source/file/internal/tail/signature.go
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| package tail | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "errors" | ||
| "fmt" | ||
| "io" | ||
| "os" | ||
| ) | ||
|
|
||
| // signature represents the first N bytes of a file, used to detect atomic writes | ||
| // where a file is replaced but may have the same initial content. | ||
| type signature struct { | ||
| d []byte | ||
| } | ||
|
|
||
| // signatureSize is the target size for a complete signature. | ||
| const signatureSize = 1024 | ||
kalleep marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // signatureThresholds defines the byte offsets at which we should recompute the signature | ||
| // as the file grows. This allows us to progressively build a more complete signature. | ||
| var signatureThresholds = []int{64, 128, 256, 512, signatureSize} | ||
|
|
||
| // newSignatureFromFile reads up to signatureSize bytes from the beginning of the file | ||
| // to create a signature. If the file is smaller, the signature will be incomplete. | ||
| func newSignatureFromFile(f *os.File) (*signature, error) { | ||
| buf := make([]byte, signatureSize) | ||
| n, err := f.ReadAt(buf, 0) | ||
| if err != nil && !errors.Is(err, io.EOF) { | ||
| return nil, fmt.Errorf("failed to compute signature for file: %w", err) | ||
| } | ||
| return &signature{ | ||
| d: buf[:n], | ||
| }, nil | ||
| } | ||
|
|
||
| // completed returns true if the signature has reached the target size. | ||
| func (s *signature) completed() bool { | ||
| return len(s.d) == signatureSize | ||
| } | ||
|
|
||
| // equal compares two signatures. For incomplete signatures, it compares only | ||
| // the overlapping bytes. For complete signatures, both must be the same length and content. | ||
| // If signature has a lenght of 0 equal will always return false. | ||
kalleep marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| func (s *signature) equal(other *signature) bool { | ||
| len1 := len(s.d) | ||
| if len1 == 0 { | ||
| return false | ||
| } | ||
|
|
||
| len2 := len(other.d) | ||
kalleep marked this conversation as resolved.
Show resolved
Hide resolved
kalleep marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if !s.completed() { | ||
| if len1 > len2 { | ||
| return false | ||
| } | ||
| return bytes.Equal(s.d[:len1], other.d[:len1]) | ||
| } | ||
|
|
||
| return len1 == len2 && bytes.Equal(s.d, other.d) | ||
| } | ||
|
|
||
| // shouldRecompute returns true if we have read past a signature threshold and | ||
| // the current signature is smaller than that threshold. This allows us to | ||
| // progressively update the signature as the file grows. | ||
| func (s *signature) shouldRecompute(at int64) bool { | ||
| if s.completed() { | ||
| return false | ||
| } | ||
|
|
||
| currentSize := len(s.d) | ||
| for _, threshold := range signatureThresholds { | ||
| if at >= int64(threshold) && currentSize < threshold { | ||
| return true | ||
| } | ||
| } | ||
|
|
||
| return false | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.