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

Add /proc/net/udp parsing especially for the tx_queue and rx_queue lengths #235

Merged
merged 3 commits into from
Dec 9, 2019
Merged

Add /proc/net/udp parsing especially for the tx_queue and rx_queue lengths #235

merged 3 commits into from
Dec 9, 2019

Conversation

peterbueschel
Copy link

Hi @pgier,

this belongs to the requested of @discordianfish in prometheus/node_exporter#1503.

Signed-off-by: Peter Bueschel peter.bueschel@logmein.com

@peterbueschel
Copy link
Author

I would update prometheus/node_exporter#1503 as soon as this PR is merged.

@SuperQ SuperQ requested a review from pgier November 19, 2019 14:00
@SuperQ
Copy link
Member

SuperQ commented Nov 19, 2019

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)
Copy link
Member

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?

Copy link
Author

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:

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.

Copy link
Member

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?

Copy link
Contributor

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) {
Copy link
Member

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?

Copy link
Author

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.

Copy link
Member

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.

Copy link
Contributor

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
Copy link
Contributor

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 {
Copy link
Contributor

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.

Copy link
Contributor

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) {
Copy link
Contributor

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)
Copy link
Contributor

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 {
Copy link
Contributor

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 {
Copy link
Contributor

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
Copy link
Contributor

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 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same

Copy link
Contributor

@mdlayher mdlayher left a 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.

@mdlayher
Copy link
Contributor

You can see how I handled sockstat/sockstat6 in #237.

Peter Bueschel added 2 commits November 26, 2019 17:59
…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>
@peterbueschel
Copy link
Author

Hi @SuperQ , Hi @mdlayher ,

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.

Copy link
Collaborator

@pgier pgier left a 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
Copy link
Collaborator

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.
Copy link
Collaborator

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>
@pgier pgier merged commit fa4d6ce into prometheus:master Dec 9, 2019
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.

None yet

4 participants