Skip to content
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

fix(pkg/stanza/input/file/reader): skip building fingerprint in case of configuration change #10485

Merged
merged 4 commits into from
Jun 13, 2022

Conversation

sumo-drosiek
Copy link
Member

Description:

If customer changes fingerprint_size while offset is behind the value,
it is going to panic as it is unable to rebuild fingerprint

Reproduction:
logs/file.txt

this is some file for testing purposes

use this configuration

extensions:
  file_storage:
    directory: ./
    timeout: 10s
receivers:
  filelog:
    include:
      - logs/file.txt
    fingerprint_size: 16
    start_at: beginning
exporters:
  logging:
    logLevel: debug
service:
  extensions:
    file_storage
  pipelines:
    logs:
      receivers:
        - filelog
      exporters:
        - logging

run again with ioncreased fingerprint_size

extensions:
  file_storage:
    directory: ./
    timeout: 10s
receivers:
  filelog:
    include:
      - logs/file.txt
    fingerprint_size: 17
    start_at: beginning
exporters:
  logging:
    logLevel: debug
service:
  extensions:
    file_storage
  pipelines:
    logs:
      receivers:
        - filelog
      exporters:
        - logging

The error occures:

2022-06-01T17:19:54.955+0200    info    file/file.go:174        Started watching file   {"kind": "receiver", "name": "filelog", "operator_id": "file_input", "operator_type": "file_input", "path": "logs/file.txt"}
panic: runtime error: slice bounds out of range [:39] with capacity 18

goroutine 141 [running]:
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/input/file.(*Reader).Read(0xc000aa8a20, {0xc000cee000, 0x27?, 0x4000})
        github.com/open-telemetry/opentelemetry-collector-contrib/pkg/[email protected]/operator/input/file/reader.go:234 +0x190
bufio.(*Scanner).Scan(0xc000bc4f80)
        bufio/scan.go:215 +0x865
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/input/file.(*Reader).ReadToEnd(0xc000aa8a20, {0x53ba918, 0xc000c68680})
        github.com/open-telemetry/opentelemetry-collector-contrib/pkg/[email protected]/operator/input/file/reader.go:134 +0x216
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/input/file.(*InputOperator).poll.func1(0xc00043cf90?)
        github.com/open-telemetry/opentelemetry-collector-contrib/pkg/[email protected]/operator/input/file/file.go:155 +0x5c
created by github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/input/file.(*InputOperator).poll
        github.com/open-telemetry/opentelemetry-collector-contrib/pkg/[email protected]/operator/input/file/file.go:153 +0x198

Link to tracking Issue: N/A

Testing: ToDo

Documentation: in code comments

@sumo-drosiek
Copy link
Member Author

This is draft as I want to add some unit tests to the PR

@sumo-drosiek sumo-drosiek force-pushed the drosiek-fix-filelog branch from 7282b95 to df0f936 Compare June 2, 2022 06:29
@sumo-drosiek
Copy link
Member Author

Not sure, how this change affects this code:

// StartsWith returns true if the fingerprints are the same
// or if the new fingerprint starts with the old one
// This is important functionality for tracking new files,
// since their initial size is typically less than that of
// a fingerprint. As the file grows, its fingerprint is updated
// until it reaches a maximum size, as configured on the operator
func (f Fingerprint) StartsWith(old *Fingerprint) bool {
l0 := len(old.FirstBytes)
if l0 == 0 {
return false
}
l1 := len(f.FirstBytes)
if l0 > l1 {
return false
}
return bytes.Equal(old.FirstBytes[:l0], f.FirstBytes[:l0])
}

There shouldn't be an issue for increasing the fingerprint size. I will test it for decreasing the fingerprint size, but I consider PR ready to review

@sumo-drosiek sumo-drosiek marked this pull request as ready for review June 2, 2022 06:41
@sumo-drosiek sumo-drosiek requested a review from a team June 2, 2022 06:41
@sumo-drosiek sumo-drosiek requested a review from djaglowski as a code owner June 2, 2022 06:41
@sumo-drosiek
Copy link
Member Author

for decreasing the fingerprint_size, file is going to be read again, which is not a new behavior and I would rather resolve it in separate issue

@djaglowski WDYT?

@sumo-drosiek sumo-drosiek force-pushed the drosiek-fix-filelog branch 2 times, most recently from 205b671 to df0f936 Compare June 2, 2022 09:48
@djaglowski
Copy link
Member

Am I correct in thinking that this only affects fingerprints that have been loaded from a storage extension? If so, I wonder if we could detect this and normalize the fingerprints during startup. This would seem to be preferable to adding complexity to the ongoing read loop.

@sumo-drosiek
Copy link
Member Author

sumo-drosiek commented Jun 7, 2022

Am I correct in thinking that this only affects fingerprints that have been loaded from a storage extension?

Yes, it affects only fingerprints which have been loaded from storage

If so, I wonder if we could detect this and normalize the fingerprints during startup.

So, as I understand flow correctly, you propose to add logic to this function:

func (f *Input) loadLastPollFiles(ctx context.Context) error {
encoded, err := f.persister.Get(ctx, knownFilesKey)
if err != nil {
return err
}
if encoded == nil {
f.knownFiles = make([]*Reader, 0, 10)
return nil
}
dec := json.NewDecoder(bytes.NewReader(encoded))
// Decode the number of entries
var knownFileCount int
if err := dec.Decode(&knownFileCount); err != nil {
return fmt.Errorf("decoding file count: %w", err)
}
// Decode each of the known files
f.knownFiles = make([]*Reader, 0, knownFileCount)
for i := 0; i < knownFileCount; i++ {
splitter, err := f.getMultiline()
if err != nil {
return err
}
newReader, err := f.NewReader("", nil, nil, splitter)
if err != nil {
return err
}
if err = dec.Decode(newReader); err != nil {
return err
}
f.knownFiles = append(f.knownFiles, newReader)
}
return nil
}

@djaglowski
Copy link
Member

I think in theory this case would be better handled in loadLastPollFiles, but having refreshed myself on the code somewhat, it's going to take some substantial refactoring or duplication to make that possible. I think what you have makes sense.

Comparing benchmarks before and after the shows no notable change:

## main

pkg: github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/input/file
BenchmarkFileInput/Single-10         	  913302	      1138 ns/op	     672 B/op	       9 allocs/op
BenchmarkFileInput/Glob-10           	  245481	      4946 ns/op	    2693 B/op	      36 allocs/op
BenchmarkFileInput/MultiGlob-10      	  245552	      4919 ns/op	    2693 B/op	      36 allocs/op
BenchmarkFileInput/MaxConcurrent-10  	  140121	      7163 ns/op	    2690 B/op	      36 allocs/op
BenchmarkFileInput/FngrPrntLarge-10  	  924232	      1122 ns/op	     672 B/op	       9 allocs/op
BenchmarkFileInput/FngrPrntSmall-10  	  915962	      1138 ns/op	     672 B/op	       9 allocs/op
PASS
coverage: 71.8% of statements
ok  	github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/input/file	52.988s


## This branch

pkg: github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/input/file
BenchmarkFileInput/Single-10         	  913993	      1136 ns/op	     672 B/op	       9 allocs/op
BenchmarkFileInput/Glob-10           	  241573	      4992 ns/op	    2693 B/op	      36 allocs/op
BenchmarkFileInput/MultiGlob-10      	  239896	      4404 ns/op	    2692 B/op	      36 allocs/op
BenchmarkFileInput/MaxConcurrent-10  	  140404	      7155 ns/op	    2690 B/op	      36 allocs/op
BenchmarkFileInput/FngrPrntLarge-10  	  919200	      1134 ns/op	     672 B/op	       9 allocs/op
BenchmarkFileInput/FngrPrntSmall-10  	 1161418	      1087 ns/op	     672 B/op	       9 allocs/op
PASS
coverage: 72.0% of statements
ok  	github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/input/file	52.935s

Only thing I would ask is that new test cases be added rather than adding to the existing "GrowAndStop" test.

@sumo-drosiek
Copy link
Member Author

Only thing I would ask is that new test cases be added rather than adding to the existing "GrowAndStop" test.

Sure, I was considering that, but it will be mostly copy of the GrowAndStop

Dominik Rosiek added 3 commits June 8, 2022 07:27
…of configuration change

If customer changes fingerprint_size while offset is behind the value,
it is going to panic as it is unable to rebuild fingerprint

Signed-off-by: Dominik Rosiek <[email protected]>
Signed-off-by: Dominik Rosiek <[email protected]>
@sumo-drosiek sumo-drosiek force-pushed the drosiek-fix-filelog branch from df0f936 to 251c4f0 Compare June 8, 2022 05:28
@djaglowski djaglowski merged commit 1db76fe into open-telemetry:main Jun 13, 2022
@sumo-drosiek sumo-drosiek deleted the drosiek-fix-filelog branch June 14, 2022 05:15
kentquirk pushed a commit to McSick/opentelemetry-collector-contrib that referenced this pull request Jun 14, 2022
…of configuration change (open-telemetry#10485)

* fix(pkg/stanza/input/file/reader): skip building fingerprint in case of configuration change

If customer changes fingerprint_size while offset is behind the value,
it is going to panic as it is unable to rebuild fingerprint

Signed-off-by: Dominik Rosiek <[email protected]>

* chore: changelog

Signed-off-by: Dominik Rosiek <[email protected]>

* feat(tests): extract fingerprint size change as separate test

Signed-off-by: Dominik Rosiek <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants