Covers the previous patch ("MEDIUM: backend: report consistent-hash
LB bypass reason"):

  - balance-hash-bypass-maxconn.vtc extends the existing
    balance-hash-maxconn.vtc barrier-synchronized scenario (two
    concurrent requests hashing to the same saturated server under
    "hash-preserve-affinity maxconn") with assertions that the
    "lbbypass" counter increments exactly once on the bypassed
    server and on the backend, stays at 0 on the server that wasn't
    bypassed, and that "txn.lb_bypass_reason" reports
    "preserve-affinity-maxconn" on the bypassed request and an empty
    value on the non-bypassed one.

  - balance-hash-bypass-redispatch.vtc, modeled on
    reg-tests/proxy/retry_on.vtc, uses a two-server hash-based
    backend with one unreachable server and "option redispatch" to
    verify the simpler, single-client, deterministic redispatch
    case: "txn.lb_bypass_reason" reports "redispatch" and "lbbypass"
    is incremented on both the backend and the unreachable server.

Both were run against the patched build with a locally built vtest
and pass; the existing reg-tests/balance/ suite (6 runnable tests,
one pre-existing "broken"-tagged test skipped) and reg-tests/log/
plus reg-tests/proxy/retry_on.vtc (17 tests) still pass unmodified.
---
 .../balance/balance-hash-bypass-maxconn.vtc   | 101 ++++++++++++++++++
 .../balance-hash-bypass-redispatch.vtc        |  71 ++++++++++++
 2 files changed, 172 insertions(+)
 create mode 100644 reg-tests/balance/balance-hash-bypass-maxconn.vtc
 create mode 100644 reg-tests/balance/balance-hash-bypass-redispatch.vtc

diff --git a/reg-tests/balance/balance-hash-bypass-maxconn.vtc 
b/reg-tests/balance/balance-hash-bypass-maxconn.vtc
new file mode 100644
index 000000000..bdc922384
--- /dev/null
+++ b/reg-tests/balance/balance-hash-bypass-maxconn.vtc
@@ -0,0 +1,101 @@
+vtest "Test for lb_bypass observability (counter + txn.lb_bypass_reason) with 
hash-preserve-affinity maxconn"
+feature ignore_unknown_macro
+feature cmd "command -v socat"
+
+# Ensure c1 doesn't finish before c2
+barrier b1 cond 2
+
+# Ensure c2 only starts once c1's request is already in flight
+barrier b2 cond 2
+
+server s0 {
+    rxreq
+    barrier b2 sync
+    barrier b1 sync
+    txresp -hdr "Server: s0"
+} -start
+
+server s1 {
+    rxreq
+    barrier b1 sync
+    txresp -hdr "Server: s1"
+} -start
+
+haproxy h1 -arg "-L A" -conf {
+    global
+    .if feature(THREAD)
+        thread-groups 1
+    .endif
+        stats socket "${tmpdir}/h1/stats" level admin
+
+    defaults
+        mode http
+        timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+
+    listen px
+        bind "fd@${px}"
+        balance uri
+        hash-preserve-affinity maxconn
+        hash-type consistent
+        http-after-response set-header x-lb-bypass-reason 
"%[txn.lb_bypass_reason]"
+
+        server srv0 ${s0_addr}:${s0_port} maxconn 1
+        server srv1 ${s1_addr}:${s1_port} maxconn 1
+
+} -start
+
+client c1 -connect ${h1_px_sock} {
+    txreq -url "/test-url"
+    rxresp
+    expect resp.status == 200
+    expect resp.http.Server ~ s0
+    expect resp.http.x-lb-bypass-reason == ""
+}  -start
+
+barrier b2 sync
+
+# srv0 is saturated, request should be assigned to srv1, and the bypass
+# should be recorded as a hash-preserve-affinity maxconn event.
+client c2 -connect ${h1_px_sock} {
+    txreq -url "/test-url"
+    rxresp
+    expect resp.status == 200
+    expect resp.http.Server ~ s1
+    expect resp.http.x-lb-bypass-reason == "preserve-affinity-maxconn"
+} -start
+
+client c1 -wait
+client c2 -wait
+
+# c1's request was never bypassed (srv0 was the naive target and was free),
+# c2's request was bypassed exactly once, charged against srv0 (the naive
+# target it couldn't use) and against the "px" backend as a whole.
+shell {
+    stat=$(echo "show stat" | socat -t 5 "${tmpdir}/h1/stats" -)
+
+    be_bypass=$(echo "$stat" | awk -F, '
+        NR==1 { for (i=1; i<=NF; i++) if ($i=="lbbypass") c=i }
+        $1=="px" && $2=="BACKEND" { print $c }')
+    if [ "$be_bypass" != "1" ]; then
+        echo "expected 1 backend lbbypass, got '$be_bypass'"
+        exit 1
+    fi
+
+    srv0_bypass=$(echo "$stat" | awk -F, '
+        NR==1 { for (i=1; i<=NF; i++) if ($i=="lbbypass") c=i }
+        $1=="px" && $2=="srv0" { print $c }')
+    if [ "$srv0_bypass" != "1" ]; then
+        echo "expected 1 srv0 lbbypass, got '$srv0_bypass'"
+        exit 1
+    fi
+
+    srv1_bypass=$(echo "$stat" | awk -F, '
+        NR==1 { for (i=1; i<=NF; i++) if ($i=="lbbypass") c=i }
+        $1=="px" && $2=="srv1" { print $c }')
+    if [ "$srv1_bypass" != "0" ]; then
+        echo "expected 0 srv1 lbbypass, got '$srv1_bypass'"
+        exit 1
+    fi
+}
diff --git a/reg-tests/balance/balance-hash-bypass-redispatch.vtc 
b/reg-tests/balance/balance-hash-bypass-redispatch.vtc
new file mode 100644
index 000000000..d746f8c6e
--- /dev/null
+++ b/reg-tests/balance/balance-hash-bypass-redispatch.vtc
@@ -0,0 +1,71 @@
+vtest "Test for lb_bypass observability (counter + txn.lb_bypass_reason) on 
redispatch"
+feature ignore_unknown_macro
+feature cmd "command -v socat"
+
+# The hash-based backend only has two servers: "bad", which is unreachable,
+# and "good". Since the ring only has two candidates, a failed connect to
+# the naively-hashed "bad" server, followed by "option redispatch", must
+# land on "good" -- and the bypass must be reported as "redispatch".
+
+server s1 {
+       rxreq
+       txresp
+} -start
+
+haproxy h1 -conf {
+    global
+    .if feature(THREAD)
+        thread-groups 1
+    .endif
+        stats socket "${tmpdir}/h1/stats" level admin
+
+    defaults
+       mode http
+       timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+       timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+       timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+    frontend fe
+       bind "fd@${fe}"
+       default_backend be
+
+       http-after-response set-header x-reason  "%[txn.lb_bypass_reason]"
+       http-after-response set-header x-redisp  "%[txn.redispatched]"
+
+    backend be
+       timeout connect 200ms
+       balance uri
+       hash-type consistent
+       retries 1
+       option redispatch
+       server bad ${bad_backend}
+       server good ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+       txreq -url "/test-url"
+       rxresp
+       expect resp.status == 200
+       expect resp.http.x-redisp == "1"
+       expect resp.http.x-reason == "redispatch"
+} -run
+
+shell {
+    stat=$(echo "show stat" | socat -t 5 "${tmpdir}/h1/stats" -)
+
+    be_bypass=$(echo "$stat" | awk -F, '
+        NR==1 { for (i=1; i<=NF; i++) if ($i=="lbbypass") c=i }
+        $1=="be" && $2=="BACKEND" { print $c }')
+    if [ "$be_bypass" != "1" ]; then
+        echo "expected 1 backend lbbypass, got '$be_bypass'"
+        exit 1
+    fi
+
+    bad_bypass=$(echo "$stat" | awk -F, '
+        NR==1 { for (i=1; i<=NF; i++) if ($i=="lbbypass") c=i }
+        $1=="be" && $2=="bad" { print $c }')
+    if [ "$bad_bypass" != "1" ]; then
+        echo "expected 1 'bad' server lbbypass, got '$bad_bypass'"
+        exit 1
+    fi
+}
-- 
2.52.0



Reply via email to