-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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 discarded logs on chunk upload failure #2724
Fix discarded logs on chunk upload failure #2724
Conversation
@patrick-east I think I have a basic idea on how to test this. Something like:
I'll try my best to get a basic test following the above steps done this weekend - if you have any pointers on that strategy that's much appreciated. |
The fix looks good to me! For the test that sounds like a good plan. I'm not sure why checking the buffer length isn't working, a quick look through the code makes it seem like it should work as expected. Was the buffer maybe flushed between calling |
Not sure either - this always fails for me as the func TestPluginRequeSomeMore(t *testing.T) {
ctx := context.Background()
fixture := newTestFixture(t)
defer fixture.server.stop()
fixture.server.ch = make(chan []EventV1, 1)
var input interface{} = map[string]interface{}{"method": "GET"}
var result1 interface{} = false
fixture.plugin.Log(ctx, &server.Info{
DecisionID: "abc",
Path: "data.foo.bar",
Input: &input,
Results: &result1,
RemoteAddr: "test",
Timestamp: time.Now().UTC(),
})
bufLen := fixture.plugin.buffer.Len()
if bufLen != 1 {
t.Fatalf("expected 1 log in the buffer, found %v", bufLen)
}
} |
2ed61e2
to
4d234ca
Compare
@patrick-east alright - not sure why the |
1ace20f
to
6a81f4a
Compare
Oh, the size reported there was even before gzip encoding, so how that would differ between platforms is even more mysterious. I just upped the |
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.
Looks good! Just one comment for the fixture config
plugins/logs/plugin_test.go
Outdated
@@ -791,7 +840,7 @@ type testFixture struct { | |||
server *testServer | |||
} | |||
|
|||
func newTestFixture(t *testing.T) testFixture { | |||
func newTestFixture(t *testing.T, pluginConfigRaw string) testFixture { |
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.
nit: we might want to change this option to be more of a like overlay or additional config. Making the callers provide {"service": "example"}
is kind of of hassle, especially when that service is being defined automatically in this helper. Any change to it may require the callers to update.
A pattern we use a bunch in OPA is to allow for optionally passing in config functions like:
type testPluginCustomizer func(c *Config)
and then to customize the config for a test have something like:
func newTestFixture(t *testing.T, options ...testPluginCustomizer) testFixture {
...
config, _ := ParseConfig([]byte(pluginConfig), manager.Services(), nil)
for _, option := range options {
option(config)
}
...
}
called with something like:
fixture := newTestFixture(t, func(c *Config) {
c.Reporting.UploadSizeLimitBytes = 300
})
All the other callers can skip providing anything so they stay as newTestFixture(t)
The only potential downside of that approach is that it skips the validateAndInjectDefaults
step, but I'm guessing thats OK for unit tests where the test author is setting very explicit options.
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.
Oh, nice! I was reaching for something like optional/default parameters for this exactly but came up blank - I've never done much golang except for some small fixes here and there, so learning patterns like this is awesome. I'll update the PR with that once I get a few moments to spare sometime this week. Thanks @patrick-east 👍
Pushed a new commit addressing the optional config as suggested by @patrick-east. Also modified the chunk upload failure handling to stop the upload attempts at first failure and just preserve whatever is in the buffer. We'll instead let the caller decide if a retry should be done immediately or if it can wait until the next configured interval has passed. Thanks for your input on that @srenatus! |
Changes LGTM, go ahead and squash the commits then we should be ready to merge 👍 |
If the size of the decision logs buffered exceeeds that of `upload_size_limit_bytes`, the upload will be split into chunks. If one of the attempted uploads return with an error the chunk is stored in the "new" buffer and will be re-attempted at the next invocation of the `oneShot` method. However, once that is done the function returns, leaving any decisions left in the buffer to be discarded. This PR fixes that, preserving all failed chunk uploads to the buffer. Signed-off-by: Anders Eknert <[email protected]>
94bed31
to
ad380b4
Compare
If the size of the decision logs buffered exceeeds that of
upload_size_limit_bytes
, the upload will be split into chunks. If one of the attempted uploads return with an error the chunk is stored in the "new" buffer and will be re-attempted at the next invocation of theoneShot
method. However, once that is done the function returns, leaving any decisions left in the buffer to be discarded.I've reproduced this by queuing up decisions in OPA above
upload_size_limit_bytes
and having an external mock server on the receiving end which randomly responds with non-OK status codes. Not having written too much golang (yet!) I could however use some guidance in porting these to the OPA test suite.As for the fix itself - an alternative (and perhaps more sensible?) approach would be to stop uploading at first error but to push all remaining chunks in the old buffer into the new one to be preserved for next invocation.
Signed-off-by: Anders Eknert [email protected]