-
Notifications
You must be signed in to change notification settings - Fork 305
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 /proc/net/udp parsing especially for the tx_queue and rx_queue lengths #235
Add /proc/net/udp parsing especially for the tx_queue and rx_queue lengths #235
Conversation
I would update prometheus/node_exporter#1503 as soon as this PR is merged. |
Nice. It would also be good to include /proc/net/udp6 as well. |
|
||
// newNetUDP creates a new NetUDP from the contents of the given file. | ||
func newNetUDP(file string) (*NetUDP, error) { | ||
f, err := os.Open(file) |
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're trying to make use of the procfs utli.ReadFileNoStat()
, in order to read this data more atomically from the kernel.
But, look at this file, it seems like it could be very big in a real-world environment. Do you have any data on what size this file can grow to?
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 have a hard limit on our hosts of 1300 concurrent connections. I measured locally that one line is about 150 Byte. So, 1300x150 Byte = 19.5 KByte =~ 19 Kibibyte.
If we play a bit with the numbers and say we will use the number of possible ports (and forget about other limits like open files), which is limited to 16 bits for the source port field in a UDPv4 header, we could have 2^16 = 65536 lines for a server listening on a single IP.
--> So, with 150 Byte per line we theoretically have a filesize of 65536 * 150 Byte ~ 10 MByte
But lets have a look at some numbers available at the internet:
- Whatsapp (published 2012: https://web.archive.org/web/20140501234954/https://blog.whatsapp.com/196/1-million-is-so-2011) they were able to handle 2M TCP connections at this time. Just for the fun lets say 2M UDP connections will then create a /proc/net/udp file with size of 300 MByte
- MigratoryData WebSocket Server (published: 2013 https://mrotaru.wordpress.com/2013/06/20/12-million-concurrent-connections-with-migratorydata-websocket-server/) they can handle 12M concurrent connections. Even this is not UDP related and more socket descriptor magic, let's use this number and we have a filesize of 1.8 GByte...
Means, we have a filesize range from more realistic KBytes to unusual GBytes. As an engineer I would choose the middle and estimate a maximum of multiple MByte per IP.
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.
Thanks for the followup, good stuff.
I guess we should stick to this streaming reader for large file safety. What do you think @mdlayher?
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.
I think it's most reasonable to stick to a streaming read but would also encourage using an io.LimitReader when initializing the scanner and set some sane upper bound like 4GiB of file.
net_udp.go
Outdated
) | ||
|
||
// NetUDP returns kernel/networking statistics for udp datagrams read from /proc/net/udp. | ||
func (fs FS) NetUDP() (*NetUDP, error) { |
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.
Since this is a more specialty parser than the typical procfs reader, I might suggest we have a name like NetUDPSummary()
, or NetUDPStats()
. What do you think @pgier?
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.
If I am allowed to add my 2 cents: The name should represents the location of the file in the proc filesystem. If someone else needs to extract some other data from this file, she/he/it could extend the function/struct instead of creating another file/reader. From my perspective, there is also not much more you can get from the content of the lines.
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.
Most of our other parsers dump the raw contents of the file into a struct, rather than parse and produce some derived metrics. I agree, that the function should be named after the filepath, but since this isn't a raw dump, we should either have it be a different function name, or have a struct method to summarize.
So, the NetUDP type struct is a full contents of the file, and then you would call netUdp.Summary()
to get TxQueueLength, etc.
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.
+1. We should provide Go representations of the raw data and allow the caller to determine how the data is used. The convenience method could be reasonable if the logic is nontrivial.
net_udp.go
Outdated
@@ -0,0 +1,94 @@ | |||
// Copyright 2018 The Prometheus Authors |
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.
2019
net_udp.go
Outdated
type ( | ||
// NetUDPLine is a line parsed from /proc/net/udp | ||
// For the proc file format details, see https://linux.die.net/man/5/proc | ||
NetUDPLine struct { |
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.
Unexport this type and add a doc comment to NetUDP.
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.
Actually if we decide to expose this directly it should remain exported.
net_udp.go
Outdated
) | ||
|
||
// NetUDP returns kernel/networking statistics for udp datagrams read from /proc/net/udp. | ||
func (fs FS) NetUDP() (*NetUDP, error) { |
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.
+1. We should provide Go representations of the raw data and allow the caller to determine how the data is used. The convenience method could be reasonable if the logic is nontrivial.
|
||
// newNetUDP creates a new NetUDP from the contents of the given file. | ||
func newNetUDP(file string) (*NetUDP, error) { | ||
f, err := os.Open(file) |
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.
I think it's most reasonable to stick to a streaming read but would also encourage using an io.LimitReader when initializing the scanner and set some sane upper bound like 4GiB of file.
net_udp.go
Outdated
) | ||
} | ||
q := strings.Split(fields[4], ":") | ||
if len(q) < 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.
Based on what I'm seeing on my machine, I think this should be != 2
.
net_udp_test.go
Outdated
) | ||
|
||
func Test_parseNetUDPLine(t *testing.T) { | ||
type args struct { |
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.
I'm not sure this extra struct is necessary since there's only one field.
net_udp_test.go
Outdated
@@ -0,0 +1,131 @@ | |||
// Copyright 2018 The Prometheus Authors |
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.
2019
net_udp_test.go
Outdated
} | ||
|
||
func Test_newNetUDP(t *testing.T) { | ||
type args struct { |
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.
Same
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.
I would also like to see udp6 parsed as well. It appears much of the logic can be reused and we can add NetUDP6 method that returns a NetUDP type (the fields are identical yeah?).
I don't like the UDP/UDP6 convention personally but I guess we should do what the kernel does.
You can see how I handled sockstat/sockstat6 in #237. |
…ngths. @pgier this belongs to the requested of @discordianfish in prometheus/node_exporter#1503. Signed-off-by: Peter Bueschel <peter.bueschel@logmein.com>
Add UDPv6 support. Limit file read to 4GiB. Signed-off-by: Peter Bueschel <peter.bueschel@logmein.com>
as discussed UDP6 support and a read limitation were added. Also the NetUDP represents now the lines parsed from the /proc/net/udp{,6} file, while the summary struct contains the total lengths. A user can decide if he/she/it wants to collect directly the queue lengths without storing the parsed lines or if he/she/it needs all lines for further calculations. |
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.
LGTM, just had a couple of suggestions related to the comments
net_udp.go
Outdated
) | ||
|
||
const ( | ||
readLimit = 4294967296 // Bytes |
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.
I think it would be helpful to add a comment with some info for future reference about why we need the limit and how the number was chosen.
net_udp.go
Outdated
return netUDP, nil | ||
} | ||
|
||
// newNetUDP creates a new NetUDP{,6} from the contents of the given file. |
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.
Comment should be updated to newNetUDPSummary
to match the function name.
Signed-off-by: Peter Bueschel <peter.bueschel@logmein.com>
Hi @pgier,
this belongs to the requested of @discordianfish in prometheus/node_exporter#1503.
Signed-off-by: Peter Bueschel peter.bueschel@logmein.com