-
Notifications
You must be signed in to change notification settings - Fork 131
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
add a TCP listener #56
Conversation
Hi @teepark thanks for this PR. It looks like it needs a rebase after a recent merge and there's a bit of code to review but otherwise at a high level I'm 👍 with this. |
@teepark, wanted to check-in and see if you wanted a stab at rebasing this against master. Have not gotten to test the TCP functionality yet but it seems with these changes the first set of values don't get sent out.
|
hi @markrechler, I won't have a chance to for little while. on that failure, shouldn't there be newlines between the values? |
There are:
Added some debugging:
Looks like rest keeps the newlines. Will dig in some more later, probably something with the parsing as it only happens for the first set of values, works fine after that first error. |
Only seems to affect gauges:
|
This is making more sense now. We queue up counter metrics in the library we use and send gauges one at a time. This is related to line parsing. In master we are able to take in one value at a time, or a few values at a time seperated by newlines (https://github.com/etsy/statsd/blob/master/docs/metric_types.md#multi-metric-packets). |
I do actually have a chance to look at this. This branch is also capable of reading multi-metric packets, and that's actually the paradigm that it uses for long-lived TCP connections: it always pulls in each metric by reading up to a newline. You don't have a test case that illustrates this branch's problem with gauges do you? |
Was actually just looking at this, if you submit a metric or two via This manifested in the form of gauges for us because we were sending them as one-offs, we send other stuff as multi-metric off the bat. Adding another condition to lineFrom
Sends all metrics with the caveat that Next will never return a false. |
I'm merging changes with current master right now, give me a minute to get that pushed. I mostly mean in statsdaemon_test.go -- I'd love a real failing test :) |
Will see if I can come up with anything, the buffer effectively becomes "deploys.test.myservice:2|cdeploys.test.myservice:1|c" so it's a bit funkier to test for. |
with newlines though right? I can stick that in the tests |
It has newlines when sending multi-metric, or single with newline, but when sending one-offs w/out newlines, the buffer turns into essentially an invalid mult-metric. Breaks:
Works:
Works:
|
To give some more context: and with the current/master parsing: Even without a newline, there is still potentially a valid line |
the code in those two links are equivalent. if '\n' doesn't appear in the string then bytes.Split(n []byte{'\n'}) returns a [][]byte of length 1. |
Right, was getting caught up in the wrong part of buffer handling, apologies, so one-off metrics work fine with the tcp-handler. UDP being treated as a stream breaks the parsing since one-offs just keep getting bunched together in the buffer. It might be better to have separate UDP parsing that treats each packet as a complete message. |
I get it now, sorry I've been so dense. Yeah the parser here is set up to read from any io.Reader including what comes out of net.ListenUDP. But in that case I don't want to be joining together all reads into a single buffer. OK, I'll build a test case around this and then work from there. |
ok, give that a shot now. flipping the boolean here causes the test to pass or fail, so it seems to be doing what it should. |
) | ||
|
||
if strings.HasPrefix(typeCode, "c") { | ||
split = bytes.SplitN([]byte(typeCode), []byte("|@"), 2) |
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.
Timers also support sampling rates, may want to generalize this.
Test wise, could add this to TestParseLineTimer:
d = []byte("glork:320|ms|@0.1")
packet = parseLine(d)
assert.NotEqual(t, packet, nil)
assert.Equal(t, "glork", packet.Bucket)
assert.Equal(t, uint64(320), packet.Value.(uint64))
assert.Equal(t, "ms", packet.Modifier)
assert.Equal(t, float32(0.1), packet.Sampling)
Thanks for adding the UDP test. Preliminary testing against a real data set is looking good. |
var ( | ||
serviceAddress = flag.String("address", ":8125", "UDP service address") | ||
tcpServiceAddress = flag.String("tcpaddr", "", "TCP service address, if set") | ||
maxUdpPacketSize = flag.Int64("max-udp-packet-size", 1472, "Maximum UDP packet size") |
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.
We can remove the maxUdpPacketSize flag since it's no longer being used.
Sorry for the delayed reply, everything looks good. @teepark could you rebase/squash down the commits to prep for a fast-forward merge whenever you get a chance? |
I think this suffered from a messy rebase. try |
Because individual sends via TCP can end up split across packet boundaries, the parsing code had to be reworked to handle that case. It now takes io.ReadCloser rather than []byte. It is used this way in the UDP case as well, although there it disallows single metrics spanning multiple read()s.
Because individual sends via TCP can end up split across packet
boundaries, the parsing code had to be reworked to handle that case.
It now takes io.ReadCloser rather than []byte.
It is used this way in the UDP case as well.