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

Fixes infinite loop triggered by oversized metrics #1

Closed
wants to merge 2 commits into from

Conversation

qxjit
Copy link
Owner

@qxjit qxjit commented May 14, 2019

Adds a length check to send that checks whether the metric being sent
is longer than the max buffer size. An exception is raised to the caller
(not in the reaper thread) if the metric is too large. In order to
check the length before adding the item to the reaper thread, the UTF8
building had to move to be done in the calling thread rather than the
background.

Prior to this, if a chunk larger than the max buffer size made it into
builderAction it would cause an infinite loop. It would try to flush
existing chunks to make room for the new one, but would then get stuck
because the oversize chunk was still too big. The exact same thing
would happen on the next builderAction iteration (and so forth).

Fixes iand675#32.

Adds a length check to `send` that checks whether the metric being sent
is longer than the max buffer size. An exception is raised to the caller
(*not* in the reaper thread) if the metric is too large. In order to
check the length before adding the item to the reaper thread, the UTF8
building had to move to be done in the calling thread rather than the
background.

Prior to this, if a chunk larger than the max buffer size made it into
`builderAction` it would cause an infinite loop. It would try to flush
existing chunks to make room for the new one, but would then get stuck
because the oversize chunk was still too big. The exact same thing
would happen on the next `builderAction` iteration (and so forth).

Fixes iand675#32.
then (finalizeChunks chunks, s)
else go newSize (bs : chunks) rest
(bs Seq.:< rest) ->
let newChuckSize = B.length bs

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chuck -> Chunk?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

(bs Seq.:< rest) ->
let newChuckSize = B.length bs
newTotalSize = newChuckSize + accum
in if newChuckSize > maxBufSize

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this need to be newChuckSize + 1 > maxBufSize to account for the newline that is added on line 452 in the send function?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because the newline is already part of the chunk at this point.

@@ -52,3 +55,39 @@ spec = describe "StatsD spec" $ do
case val of
Just _ -> pure ()
Nothing -> expectationFailure "Did not receive DogStatsD event"
it "does not go into an infinite loop when trying sending a metric larger than a UPD packet" $ do
let longText = Text.replicate 65507 "x"
Copy link

@exodrifter exodrifter May 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to get the 65507 value from the settings when you create it on line 61 with dogStatsSettingsBufferSize, in case this hard-coded value is changed in the future.

Copy link
Owner Author

@qxjit qxjit May 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was a bit torn on that since the value is really related to the max size of a UDP packet, which isn't going to change. I'll think through it and revise it somehow before I post the PR upstream.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. Something else you might want to consider is this line from the datadog documentation:

In some scenarios, however, you may wish to send smaller packets.

Although, since you are using the default configuration, that might not be a factor for this unit test.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultimately I decided to do nothing here, because the matching the actual rendered chunk length in statsd format to the configured buffered size is delicate, and could easily be broken in the future by changes to renderEvent.

Leaving the value at the maximum possible UDP packet data size guarantees that this test will produce a "too large" packet no matter what (realistic) default value is configured in the library in the future.

@qxjit
Copy link
Owner Author

qxjit commented May 15, 2019

I'm going to close this, squash the commits on a new branch, and then submit the PR upstream.

@qxjit qxjit closed this May 15, 2019
@qxjit qxjit deleted the fix_oversized_chunk_infinite_loop branch May 15, 2019 18:24
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.

Sending an item larger than the buffer size leads to infinite loop
3 participants