diff --git a/etc/config.sample.toml b/etc/config.sample.toml index 16f2df950ec..5202e474f55 100644 --- a/etc/config.sample.toml +++ b/etc/config.sample.toml @@ -330,6 +330,9 @@ reporting-disabled = false # batch-pending = 5 # number of batches that may be pending in memory # batch-timeout = "1s" # will flush at least this often even if we haven't hit buffer limit # read-buffer = 0 # UDP Read buffer size, 0 means OS default. UDP listener will fail if set above OS max. + + # set the expected UDP payload size; lower values tend to yield better performance, default is max UDP size 65536 + # udp-payload-size = 65536 ### ### [continuous_queries] diff --git a/services/udp/config.go b/services/udp/config.go index ad261906ab4..30b949c2196 100644 --- a/services/udp/config.go +++ b/services/udp/config.go @@ -31,6 +31,24 @@ const ( // Linux: sudo sysctl -w net.core.rmem_max= // BSD/Darwin: sudo sysctl -w kern.ipc.maxsockbuf= DefaultReadBuffer = 0 + + // DefaultUDPPayloadSize sets the default value of the incoming UDP packet + // to the spec max, i.e. 65536. That being said, this value should likely + // be tuned lower to match your udp_payload size if using tools like + // telegraf. + // + // https://en.wikipedia.org/wiki/User_Datagram_Protocol#Packet_structure + // + // Reading packets from a UDP socket in go actually only pulls + // one packet at a time, requiring a very fast reader to keep up with + // incoming data at scale. Reducing the overhead of the expected packet + // helps allocate memory faster (~10-25µs --> ~150ns with go1.5.2), thereby + // speeding up the processing of data coming in. + // + // NOTE: if you send a payload greater than the UDPPayloadSize, you will + // cause a buffer overflow...tune your application very carefully to match + // udp_payload for your metrics source + DefaultUDPPayloadSize = 65536 ) // Config holds various configuration settings for the UDP listener. @@ -44,6 +62,7 @@ type Config struct { BatchPending int `toml:"batch-pending"` ReadBuffer int `toml:"read-buffer"` BatchTimeout toml.Duration `toml:"batch-timeout"` + UDPPayloadSize int `toml:"udp-payload-size"` } // WithDefaults takes the given config and returns a new config with any required @@ -65,5 +84,8 @@ func (c *Config) WithDefaults() *Config { if d.ReadBuffer == 0 { d.ReadBuffer = DefaultReadBuffer } + if d.UDPPayloadSize == 0 { + d.UDPPayloadSize = DefaultUDPPayloadSize + } return &d } diff --git a/services/udp/config_test.go b/services/udp/config_test.go index aeeea14581b..71ac272ca5d 100644 --- a/services/udp/config_test.go +++ b/services/udp/config_test.go @@ -19,6 +19,7 @@ retention-policy = "awesomerp" batch-size = 100 batch-pending = 9 batch-timeout = "10ms" +udp-payload-size = 1500 `, &c); err != nil { t.Fatal(err) } @@ -38,5 +39,7 @@ batch-timeout = "10ms" t.Fatalf("unexpected batch pending: %d", c.BatchPending) } else if time.Duration(c.BatchTimeout) != (10 * time.Millisecond) { t.Fatalf("unexpected batch timeout: %v", c.BatchTimeout) + } else if c.UDPPayloadSize != 1500 { + t.Fatalf("unexpected udp-payload-size: %d", c.UDPPayloadSize) } } diff --git a/services/udp/service.go b/services/udp/service.go index 1cf34068a2a..1cf784124e4 100644 --- a/services/udp/service.go +++ b/services/udp/service.go @@ -18,10 +18,6 @@ import ( ) const ( - // UDPBufferSize is the maximum UDP packet size - // see https://en.wikipedia.org/wiki/User_Datagram_Protocol#Packet_structure - UDPBufferSize = 65536 - // Arbitrary, testing indicated that this doesn't typically get over 10 parserChanLen = 1000 ) @@ -163,7 +159,7 @@ func (s *Service) serve() { return default: // Keep processing. - buf := make([]byte, UDPBufferSize) + buf := make([]byte, s.config.UDPPayloadSize) n, _, err := s.conn.ReadFromUDP(buf) if err != nil { s.statMap.Add(statReadFail, 1)