This is an automated email from the ASF dual-hosted git repository.
nic-6443 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new a177234f6e test(prometheus): wait for stream session metrics to reach
the exposition (#14092)
a177234f6e is described below
commit a177234f6e8c28bfbd55c07ddcad78319cec1196
Author: Nic <[email protected]>
AuthorDate: Mon Oct 26 18:02:16 2026 +0800
test(prometheus): wait for stream session metrics to reach the exposition
(#14092)
---
t/stream-plugin/prometheus-metrics-service.t | 24 +++--
t/stream-plugin/prometheus-metrics.t | 130 +++++++++++++++++++++++----
2 files changed, 129 insertions(+), 25 deletions(-)
diff --git a/t/stream-plugin/prometheus-metrics-service.t
b/t/stream-plugin/prometheus-metrics-service.t
index 5de47102a4..9a9c46e6b0 100644
--- a/t/stream-plugin/prometheus-metrics-service.t
+++ b/t/stream-plugin/prometheus-metrics-service.t
@@ -557,14 +557,26 @@ between two names across the metrics.
assert(sock:close())
- local body, err = scrape()
- if not body then
- ngx.say(err)
- return
- end
-
local status =
'code="200",listen_addr="0.0.0.0:1985",service="%s",'
.. 'service_id="svc-b",node="127.0.0.1:1993"'
+
+ -- the status is counted when the session ends and shows up only
+ -- once its worker has synced it, every second, and the cache has
+ -- been refilled after that, which can take longer than one scrape
+ local body, err
+ for _ = 1, 5 do
+ body, err = scrape()
+ if not body then
+ ngx.say(err)
+ return
+ end
+
+ if series_value(body, "apisix_stream_status",
status:format("Billing"))
+ ~= "no-series" then
+ break
+ end
+ end
+
ngx.say("status under the first name: ",
series_value(body, "apisix_stream_status",
status:format("Billing")))
ngx.say("status under the new name: ",
diff --git a/t/stream-plugin/prometheus-metrics.t
b/t/stream-plugin/prometheus-metrics.t
index f398cf80eb..53ae033ca4 100644
--- a/t/stream-plugin/prometheus-metrics.t
+++ b/t/stream-plugin/prometheus-metrics.t
@@ -71,9 +71,59 @@ _EOC_
# while the exposition is built, so a block generated without a stream
# block has nothing to read -- and the reload into it drops the zone, which
# takes the running totals with it.
- if ($block->request =~ m{/apisix/prometheus/metrics}) {
+ if ($block->request =~ m{/apisix/prometheus/metrics|/scrape}) {
$block->set_value("stream_enable", 1);
}
+
+ # A session's status and connection count reach the exposition only after
+ # its worker has synced them into the shared dict -- every second, or when
+ # the worker exits on the reload into the next block -- and the privileged
+ # agent has refilled the cache after that. On a reload the new privileged
+ # agent fills the cache as soon as it starts, racing the flush of the old
+ # workers, and the next refill is a whole refresh_interval later. So a
+ # scrape right after the block that ran the session can still miss it, and
+ # has to wait for the series to show up.
+ my $extra_init_by_lua = <<_EOC_;
+ function _G.scrape_until(pattern)
+ local body
+ local deadline = ngx.now() + 5
+ repeat
+ local sock = ngx.socket.tcp()
+ local ok, err = sock:connect("127.0.0.1", 1984)
+ if not ok then
+ ngx.say("scrape connect: ", err)
+ return
+ end
+
+ ok, err = sock:send("GET /apisix/prometheus/metrics HTTP/1.0\\r\\n"
+ .. "Host: 127.0.0.1\\r\\n\\r\\n")
+ if not ok then
+ ngx.say("scrape send: ", err)
+ return
+ end
+
+ local rerr, partial
+ body, rerr, partial = sock:receive("*a")
+ sock:close()
+ body = body or partial
+ if not body then
+ ngx.say("scrape: ", rerr)
+ return
+ end
+
+ if ngx.re.find(body, pattern, "jom") then
+ ngx.say("matched")
+ return
+ end
+
+ ngx.sleep(0.1)
+ until ngx.now() > deadline
+
+ ngx.print(body)
+ end
+_EOC_
+
+ $block->set_value("extra_init_by_lua", $extra_init_by_lua);
});
run_tests;
@@ -136,10 +186,17 @@ hello world
=== TEST 3: the session is counted as a normal close, not as an error
+--- config
+ location /scrape {
+ content_by_lua_block {
+
scrape_until([[apisix_stream_status\{code="200",listen_addr="[^"]+",service="",service_id="",node="127\.0\.0\.1:1995"\}
1$]])
+ }
+ }
--- request
-GET /apisix/prometheus/metrics
---- response_body eval
-qr/apisix_stream_status\{code="200",listen_addr="[^"]+",service="",service_id="",node="127.0.0.1:1995"\}
1$/m
+GET /scrape
+--- timeout: 10
+--- response_body
+matched
@@ -360,10 +417,17 @@ connect() failed
=== TEST 12: the failing node is still reported on a 502
+--- config
+ location /scrape {
+ content_by_lua_block {
+
scrape_until([[apisix_stream_status\{code="502",listen_addr="[^"]+",service="",service_id="",node="127\.0\.0\.1:1979"\}
1$]])
+ }
+ }
--- request
-GET /apisix/prometheus/metrics
---- response_body eval
-qr/apisix_stream_status\{code="502",listen_addr="[^"]+",service="",service_id="",node="127.0.0.1:1979"\}
1$/m
+GET /scrape
+--- timeout: 10
+--- response_body
+matched
@@ -428,10 +492,17 @@ This is the point of the feature. nginx reports $status
200 for every failure
after the upstream connection is up -- apisix-nginx-module's t/stream/metrics.t
pins that for the same case -- so an idle timeout has to reach the metric as
502 through $stream_session_reason, not as a success.
+--- config
+ location /scrape {
+ content_by_lua_block {
+
scrape_until([[apisix_stream_status\{code="502",listen_addr="0\.0\.0\.0:1985",service="",service_id="",node="127\.0\.0\.1:1993"\}]])
+ }
+ }
--- request
-GET /apisix/prometheus/metrics
---- response_body_like eval
-qr/apisix_stream_status\{code="502",listen_addr="0\.0\.0\.0:1985",service="",service_id="",node="127\.0\.0\.1:1993"\}/
+GET /scrape
+--- timeout: 10
+--- response_body
+matched
--- no_error_log
[error]
@@ -482,10 +553,17 @@ hello world
=== TEST 18: the termination status carries the service
+--- config
+ location /scrape {
+ content_by_lua_block {
+
scrape_until([[apisix_stream_status\{code="200",listen_addr="0\.0\.0\.0:1985",service="svc-a",service_id="svc-a",node="127\.0\.0\.1:1995"\}
1$]])
+ }
+ }
--- request
-GET /apisix/prometheus/metrics
---- response_body_like eval
-qr/apisix_stream_status\{code="200",listen_addr="0\.0\.0\.0:1985",service="svc-a",service_id="svc-a",node="127\.0\.0\.1:1995"\}
1$/m
+GET /scrape
+--- timeout: 10
+--- response_body
+matched
@@ -546,10 +624,17 @@ hello world
=== TEST 22: with prefer_name, service carries the name and service_id the id
The same rule as the http metrics.
+--- config
+ location /scrape {
+ content_by_lua_block {
+
scrape_until([[apisix_stream_status\{code="200",listen_addr="0\.0\.0\.0:1985",service="Order
TCP",service_id="svc-a",node="127\.0\.0\.1:1995"\} 1$]])
+ }
+ }
--- request
-GET /apisix/prometheus/metrics
---- response_body_like eval
-qr/apisix_stream_status\{code="200",listen_addr="0\.0\.0\.0:1985",service="Order
TCP",service_id="svc-a",node="127\.0\.0\.1:1995"\} 1$/m
+GET /scrape
+--- timeout: 10
+--- response_body
+matched
@@ -617,10 +702,17 @@ hello world
=== TEST 25: the session carries the service of the route
+--- config
+ location /scrape {
+ content_by_lua_block {
+
scrape_until([[apisix_stream_status\{code="200",listen_addr="0\.0\.0\.0:1985",service="Billing",service_id="svc-b",node="127\.0\.0\.1:1995"\}
1$]])
+ }
+ }
--- request
-GET /apisix/prometheus/metrics
---- response_body_like eval
-qr/apisix_stream_status\{code="200",listen_addr="0\.0\.0\.0:1985",service="Billing",service_id="svc-b",node="127\.0\.0\.1:1995"\}
1$/m
+GET /scrape
+--- timeout: 10
+--- response_body
+matched