Skip to content

Commit 100be4c

Browse files
fix(datadog): cap coalesced DogStatsD datagram size, fall back to per-metric sends (#13665)
1 parent 2f0898c commit 100be4c

2 files changed

Lines changed: 99 additions & 4 deletions

File tree

apisix/plugins/datadog.lua

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ local defaults = {
3636
constant_tags = {"source:apisix"}
3737
}
3838

39+
-- DogStatsD agent's default receive buffer (dogstatsd_buffer_size); datagrams
40+
-- larger than this are silently truncated, so cap coalescing here
41+
local MAX_DATAGRAM_SIZE = 8192
42+
3943
local batch_processor_manager = bp_manager_mod.new(plugin_name)
4044

4145
-- Shared schema for individual tag strings.
@@ -142,7 +146,7 @@ local function generate_tag(entry, const_tags)
142146
end
143147

144148

145-
-- Build all DogStatsD metrics for one entry as a single newline-delimited packet.
149+
-- Build the DogStatsD metric lines for one entry.
146150
local function build_metrics(entry, metadata)
147151
-- Generate prefix & suffix according dogstatsd udp data format.
148152
local suffix = generate_tag(entry, metadata.value.constant_tags)
@@ -168,7 +172,7 @@ local function build_metrics(entry, metadata)
168172
core.table.insert(metrics, format("%s:%s|%s%s", prefix .. "egress.size",
169173
entry.response.size, "ms", suffix))
170174

171-
return concat(metrics, "\n")
175+
return metrics
172176
end
173177

174178

@@ -197,8 +201,27 @@ local function push_metrics(entries)
197201

198202
local err_msg, first_fail
199203
for i = 1, #entries do
200-
-- coalesce the per-request metrics into one datagram
201-
local send_ok, send_err = sock:send(build_metrics(entries[i], metadata))
204+
local lines = build_metrics(entries[i], metadata)
205+
local payload = concat(lines, "\n")
206+
207+
local send_ok, send_err
208+
if #payload <= MAX_DATAGRAM_SIZE then
209+
-- coalesce the entry's metrics into one datagram
210+
send_ok, send_err = sock:send(payload)
211+
else
212+
-- too large to coalesce safely: one datagram per metric
213+
for j = 1, #lines do
214+
if #lines[j] > MAX_DATAGRAM_SIZE then
215+
core.log.warn("dogstatsd metric line exceeds ", MAX_DATAGRAM_SIZE,
216+
" bytes, agent may truncate it")
217+
end
218+
send_ok, send_err = sock:send(lines[j])
219+
if not send_ok then
220+
break
221+
end
222+
end
223+
end
224+
202225
if not send_ok then
203226
err_msg = "failed to send metrics to dogstatsd server: host[" .. host
204227
.. "] port[" .. tostring(port) .. "] err: " .. send_err

t/plugin/datadog.t

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,3 +599,75 @@ GET /articles/12345/comments?foo=bar
599599
--- wait: 0.5
600600
--- error_log
601601
property "constant_tags" validation failed
602+
603+
604+
605+
=== TEST 13: oversized coalesced payload falls back to per-metric datagrams
606+
--- apisix_yaml
607+
routes:
608+
- uri: /opentracing
609+
name: datadog
610+
upstream:
611+
nodes:
612+
"127.0.0.1:1982": 1
613+
plugins:
614+
datadog:
615+
batch_max_size: 1
616+
max_retry_count: 0
617+
constant_tags:
618+
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
619+
- "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
620+
- "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
621+
- "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
622+
- "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
623+
- "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
624+
- "gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg"
625+
- "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
626+
- "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"
627+
- "jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj"
628+
#END
629+
--- extra_stream_config
630+
server {
631+
listen 8125 udp;
632+
content_by_lua_block {
633+
local core = require("apisix.core")
634+
local sock = ngx.req.socket()
635+
while true do
636+
local data, err = sock:receive()
637+
if not data then
638+
if err and err ~= "no more data" then
639+
core.log.error("socket error: ", err)
640+
end
641+
return
642+
end
643+
644+
local count = 0
645+
for line in data:gmatch("[^\n]+") do
646+
count = count + 1
647+
core.log.warn("message received: ", line)
648+
end
649+
core.log.warn("datagram carried ", count, " metric line(s)")
650+
end
651+
}
652+
}
653+
--- request
654+
GET /opentracing
655+
--- response_body
656+
opentracing
657+
--- wait: 0.5
658+
--- grep_error_log eval
659+
qr/message received: apisix\.[\w.]+?(?=:)|datagram carried \d+ metric line/
660+
--- grep_error_log_out eval
661+
qr/message received: apisix\.request\.counter
662+
datagram carried 1 metric line
663+
message received: apisix\.request\.latency
664+
datagram carried 1 metric line
665+
message received: apisix\.upstream\.latency
666+
datagram carried 1 metric line
667+
message received: apisix\.apisix\.latency
668+
datagram carried 1 metric line
669+
message received: apisix\.ingress\.size
670+
datagram carried 1 metric line
671+
message received: apisix\.egress\.size
672+
datagram carried 1 metric line
673+
/

0 commit comments

Comments
 (0)