Skip to content

Schedule urgent and batch requests on one service

Observe actual backend admission order while one existing request occupies capacity. This isolates the Gateway queue from engine scheduling and client completion timing.

Each rule update below merges the displayed values into the full baseline JSON from onboarding, then POSTs the complete service. Change fc_wake_policy to choose band dispatch, fc_tenant_wake for tenant turns inside a band, and fc_tenant_max_share_pct for the per-tenant inflight ceiling. The following requests show the actual key, objective header, JSON body and background PID.

Before you start

Finish the network exercises and remove their temporary bindings. Read each contention experiment before starting its held requests.

Focus of this page: Change the Gateway queue selection and tenant share, leaving the synthetic backend unchanged.

Topology and execution locations

flowchart TD
    A["Support namespace<br/>192.0.2.10"] -->|"sa to ga"| G["share-gateway<br/>inference 8080 / API 11111"]
    B["Research namespace<br/>198.51.100.10"] -->|"sb to gb"| G
    G -->|"ge to se"| E["Receipt backend namespace<br/>203.0.113.1:8080"]
    G -.->|"key and objective store"| P["share-pg<br/>Docker bridge / 5432"]
    O["Host Bash<br/>private LAB files"] -.->|"management via share-a"| G
    O -.->|"independent receipt via share-backend"| E
    style G fill:#e1f5fe,stroke:#0288d1,color:#1f2937
    style E fill:#e8f5e9,stroke:#43a047,color:#1f2937
    style P fill:#fff3e0,stroke:#fb8c00,color:#1f2937

One disposable Linux host contains two Gateway/DB containers and four network namespaces. Support reaches the Gateway on 192.0.2.254; research uses its 198.51.100.254 interface. The backend is a host Python process in share-backend, not an inference engine. PostgreSQL uses its inspected Docker-bridge address.

Where What happens here
Host root Bash Create namespaces, save LAB inputs, start/stop exact owned processes.
share-a / share-b Send inference as support/research; management curls use share-a.
share-gateway Apply policy and forward requests; management is port 11111, inference port 8080.
share-backend Read /receipt/ and /order directly, bypassing Gateway routing.
share-pg Keep keys/objectives; pause only in the explicit recovery experiment.

Walkthrough

Strict bands: urgency before waiting batch

Complete network exercises and confirm no bindings remain. This experiment leaves one running request untouched, queues batch first, then critical. Strict chooses the critical waiter when a permit returns. It can starve batch under sustained critical demand.

Step 1 — Raise the queue depth to 32

Send the displayed management request to /config/loadbalancer and retain its response. Require HTTP 200 for the saved management response before continuing.

set -euo pipefail
umask 077
source "${LAB:?set LAB to your private run directory}/env"
jq '.serviceArguments += {
  "fc_max_queue_depth": 32
}' \
  "$LAB/base-rule.json" > "$LAB/scheduling-rule-1.request.json"
status=$(ip netns exec share-a curl -sS --max-time 10 -X POST \
  -H 'Content-Type: application/json' --data-binary "@$LAB/scheduling-rule-1.request.json" \
  -D "$LAB/scheduling-rule-1.headers" -o "$LAB/scheduling-rule-1.response.json" \
  -w '%{http_code}' "$API/config/loadbalancer")
printf 'HTTP %s\n' "$status"
jq . "$LAB/scheduling-rule-1.response.json"
test "$status" = 200

Step 2 — Start the held strict-holder request

Write strict-holder.request.json with the displayed editable JSON input. Send strict-holder through the Gateway from the namespace shown below; retain its HTTP status and response separately. This client runs in the background; keep the same Bash session for the later release and wait steps.

cat > "$LAB/strict-holder.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "strict-holder"
    }
  ],
  "max_tokens": 4
}
JSON
ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/support.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: strict-holder' -H 'X-Request-Id: strict-holder' \
  -H 'X-Test-Hold: 1' \
  --data-binary "@$LAB/strict-holder.request.json" \
  -D "$LAB/strict-holder.headers" -o "$LAB/strict-holder.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/strict-holder.http" &
printf '%s\n' "$!" > "$LAB/strict-holder.pid"
arrived=false

Step 3 — Confirm the backend received strict-holder

Read strict-holder directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for strict-holder. Write strict-batch.request.json with the displayed editable JSON input.

for attempt in $(seq 1 80); do
  ip netns exec share-backend curl -fsS --max-time 3 \
    "http://127.0.0.1:8080/receipt/strict-holder" -o "$LAB/strict-holder.receipt.json"
  if jq -e '.count == 1' "$LAB/strict-holder.receipt.json" >/dev/null; then
    arrived=true
    break
  fi
  sleep 0.1
done
test "$arrived" = true
cat > "$LAB/strict-batch.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "strict-batch"
    }
  ],
  "max_tokens": 4
}
JSON

Step 4 — Queue strict-batch behind the holder

Send strict-batch through the Gateway from the namespace shown below; retain its HTTP status and response separately. This client runs in the background; keep the same Bash session for the later release and wait steps. Send the displayed management request to /config/loadbalancer/all and retain its response. Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state.

ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/research-batch.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: strict-batch' -H 'X-Request-Id: strict-batch' \
  -H 'X-Test-Hold: 1' \
  --data-binary "@$LAB/strict-batch.request.json" \
  -D "$LAB/strict-batch.headers" -o "$LAB/strict-batch.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/strict-batch.http" &
printf '%s\n' "$!" > "$LAB/strict-batch.pid"
reached=false
for attempt in $(seq 1 80); do
  ip netns exec share-a curl -fsS --max-time 5 "$API/config/loadbalancer/all" \
    -o "$LAB/scheduling-state-3.json"
  if jq -e --argjson port "$PORT" \
    '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective |
     .inflight == 1 and .queued == 1' "$LAB/scheduling-state-3.json" >/dev/null; then
    reached=true
    break
  fi
  sleep 0.1
done
test "$reached" = true

Step 5 — Queue strict-urgent as an interactive request

Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state. Write strict-urgent.request.json with the displayed editable JSON input. Send strict-urgent through the Gateway from the namespace shown below; retain its HTTP status and response separately.

jq --argjson port "$PORT" \
  '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective' \
  "$LAB/scheduling-state-3.json"
cat > "$LAB/strict-urgent.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "strict-urgent"
    }
  ],
  "max_tokens": 4
}
JSON
ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/support.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: strict-urgent' -H 'X-Request-Id: strict-urgent' \
  -H 'X-Test-Hold: 1' \
  -H x-llm-d-inference-objective:interactive \
  --data-binary "@$LAB/strict-urgent.request.json" \
  -D "$LAB/strict-urgent.headers" -o "$LAB/strict-urgent.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/strict-urgent.http" &

Step 6 — Wait for two waiters, then release strict-holder

Send the displayed management request to /config/loadbalancer/all and retain its response. Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state. Read strict-batch directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 0 for strict-batch. Release the held backend request strict-holder so its capacity can return to the Gateway.

printf '%s\n' "$!" > "$LAB/strict-urgent.pid"
reached=false
for attempt in $(seq 1 80); do
  ip netns exec share-a curl -fsS --max-time 5 "$API/config/loadbalancer/all" \
    -o "$LAB/scheduling-state-4.json"
  if jq -e --argjson port "$PORT" \
    '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective |
     .inflight == 1 and .queued == 2' "$LAB/scheduling-state-4.json" >/dev/null; then
    reached=true
    break
  fi
  sleep 0.1
done
test "$reached" = true
jq --argjson port "$PORT" \
  '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective' \
  "$LAB/scheduling-state-4.json"
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/strict-batch" -o "$LAB/strict-batch.receipt.json"
jq -e '.count == 0' "$LAB/strict-batch.receipt.json"
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/release/strict-holder" | jq .
arrived=false

Step 7 — Confirm strict-urgent dispatches before strict-batch

Read strict-urgent directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for strict-urgent. Send the displayed management request to /config/loadbalancer/all and retain its response. Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state.

for attempt in $(seq 1 80); do
  ip netns exec share-backend curl -fsS --max-time 3 \
    "http://127.0.0.1:8080/receipt/strict-urgent" -o "$LAB/strict-urgent.receipt.json"
  if jq -e '.count == 1' "$LAB/strict-urgent.receipt.json" >/dev/null; then
    arrived=true
    break
  fi
  sleep 0.1
done
test "$arrived" = true
reached=false
for attempt in $(seq 1 80); do
  ip netns exec share-a curl -fsS --max-time 5 "$API/config/loadbalancer/all" \
    -o "$LAB/scheduling-state-5.json"
  if jq -e --argjson port "$PORT" \
    '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective |
     .inflight == 1 and .queued == 1' "$LAB/scheduling-state-5.json" >/dev/null; then
    reached=true
    break
  fi
  sleep 0.1
done
test "$reached" = true

Step 8 — Release strict-urgent and confirm strict-batch dispatch

Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state. Read strict-batch directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 0 for strict-batch. Release the held backend request strict-urgent so its capacity can return to the Gateway. Require backend receipt count 1 for strict-batch. Release the held backend request strict-batch so its capacity can return to the Gateway. Wait for the saved client PID in this same parent Bash before checking its final response.

jq --argjson port "$PORT" \
  '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective' \
  "$LAB/scheduling-state-5.json"
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/strict-batch" -o "$LAB/strict-batch.receipt.json"
jq -e '.count == 0' "$LAB/strict-batch.receipt.json"
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/release/strict-urgent" | jq .
arrived=false
for attempt in $(seq 1 80); do
  ip netns exec share-backend curl -fsS --max-time 3 \
    "http://127.0.0.1:8080/receipt/strict-batch" -o "$LAB/strict-batch.receipt.json"
  if jq -e '.count == 1' "$LAB/strict-batch.receipt.json" >/dev/null; then
    arrived=true
    break
  fi
  sleep 0.1
done
test "$arrived" = true
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/release/strict-batch" | jq .
if test -f "$LAB/strict-holder.pid"; then
  wait "$(cat "$LAB/strict-holder.pid")"
fi

Step 9 — Verify HTTP 200 and one receipt for each strict request

Require saved HTTP 200 for strict-holder. Read strict-holder directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for strict-holder. Wait for the saved client PID in this same parent Bash before checking its final response. Require saved HTTP 200 for strict-urgent. Read strict-urgent directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for strict-urgent. Require saved HTTP 200 for strict-batch. Read strict-batch directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for strict-batch.

test "$(cat "$LAB/strict-holder.http")" = 200
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/strict-holder" -o "$LAB/strict-holder.receipt.json"
jq -e '.count == 1' "$LAB/strict-holder.receipt.json"
if test -f "$LAB/strict-urgent.pid"; then
  wait "$(cat "$LAB/strict-urgent.pid")"
fi
test "$(cat "$LAB/strict-urgent.http")" = 200
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/strict-urgent" -o "$LAB/strict-urgent.receipt.json"
jq -e '.count == 1' "$LAB/strict-urgent.receipt.json"
if test -f "$LAB/strict-batch.pid"; then
  wait "$(cat "$LAB/strict-batch.pid")"
fi
test "$(cat "$LAB/strict-batch.http")" = 200
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/strict-batch" -o "$LAB/strict-batch.receipt.json"
jq -e '.count == 1' "$LAB/strict-batch.receipt.json"
jq -e '((.["arrival"] > 0))' "$LAB/strict-batch.receipt.json"
reached=false

Step 10 — Wait for the queue to drain to zero

Send the displayed management request to /config/loadbalancer/all and retain its response. Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state.

for attempt in $(seq 1 80); do
  ip netns exec share-a curl -fsS --max-time 5 "$API/config/loadbalancer/all" \
    -o "$LAB/scheduling-state-6.json"
  if jq -e --argjson port "$PORT" \
    '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective |
     .inflight == 0 and .queued == 0' "$LAB/scheduling-state-6.json" >/dev/null; then
    reached=true
    break
  fi
  sleep 0.1
done
test "$reached" = true
jq --argjson port "$PORT" \
  '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective' \
  "$LAB/scheduling-state-6.json"

Both reads of strict-batch before urgent release must show count 0. fc_effective must reach inflight 1/queued 2 before release; otherwise you have not tested contention. Running work is never preempted.

Tenant RR within the standard band

FIFO preserves global arrival order. Change to RR to give research a turn despite support's earlier burst; support's own pending work retains FIFO order. RR counts resolved tenant identities, not keys, connections, prompt tokens or GPU milliseconds. It rotates tenant slots from the current cursor. The first turn below relies on this fresh lab's slot-allocation history (support in slot 0) and cursor reset; an existing pool can start with another tenant. Preserve per-tenant FIFO and rotation when adapting the check, rather than assuming support always starts.

Step 1 — Switch tenant wake to round robin

Send the displayed management request to /config/loadbalancer and retain its response. Require HTTP 200 for the saved management response before continuing.

set -euo pipefail
umask 077
source "${LAB:?set LAB to your private run directory}/env"
jq '.serviceArguments += {
  "fc_tenant_wake": "rr"
}' \
  "$LAB/base-rule.json" > "$LAB/scheduling-rule-7.request.json"
status=$(ip netns exec share-a curl -sS --max-time 10 -X POST \
  -H 'Content-Type: application/json' --data-binary "@$LAB/scheduling-rule-7.request.json" \
  -D "$LAB/scheduling-rule-7.headers" -o "$LAB/scheduling-rule-7.response.json" \
  -w '%{http_code}' "$API/config/loadbalancer")
printf 'HTTP %s\n' "$status"
jq . "$LAB/scheduling-rule-7.response.json"
test "$status" = 200

Step 2 — Start the held rr-holder request

Write rr-holder.request.json with the displayed editable JSON input. Send rr-holder through the Gateway from the namespace shown below; retain its HTTP status and response separately. This client runs in the background; keep the same Bash session for the later release and wait steps.

cat > "$LAB/rr-holder.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "rr-holder"
    }
  ],
  "max_tokens": 4
}
JSON
ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/support.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: rr-holder' -H 'X-Request-Id: rr-holder' \
  -H 'X-Test-Hold: 1' \
  --data-binary "@$LAB/rr-holder.request.json" \
  -D "$LAB/rr-holder.headers" -o "$LAB/rr-holder.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/rr-holder.http" &
printf '%s\n' "$!" > "$LAB/rr-holder.pid"
arrived=false

Step 3 — Confirm the backend received rr-holder

Read rr-holder directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for rr-holder. Write rr-support-1.request.json with the displayed editable JSON input.

for attempt in $(seq 1 80); do
  ip netns exec share-backend curl -fsS --max-time 3 \
    "http://127.0.0.1:8080/receipt/rr-holder" -o "$LAB/rr-holder.receipt.json"
  if jq -e '.count == 1' "$LAB/rr-holder.receipt.json" >/dev/null; then
    arrived=true
    break
  fi
  sleep 0.1
done
test "$arrived" = true
cat > "$LAB/rr-support-1.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "rr-support-1"
    }
  ],
  "max_tokens": 4
}
JSON

Step 4 — Queue rr-support-1 behind the holder

Send rr-support-1 through the Gateway from the namespace shown below; retain its HTTP status and response separately. This client runs in the background; keep the same Bash session for the later release and wait steps. Send the displayed management request to /config/loadbalancer/all and retain its response. Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state.

ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/support.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: rr-support-1' -H 'X-Request-Id: rr-support-1' \
  -H 'X-Test-Hold: 1' \
  --data-binary "@$LAB/rr-support-1.request.json" \
  -D "$LAB/rr-support-1.headers" -o "$LAB/rr-support-1.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/rr-support-1.http" &
printf '%s\n' "$!" > "$LAB/rr-support-1.pid"
reached=false
for attempt in $(seq 1 80); do
  ip netns exec share-a curl -fsS --max-time 5 "$API/config/loadbalancer/all" \
    -o "$LAB/scheduling-state-9.json"
  if jq -e --argjson port "$PORT" \
    '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective |
     .inflight == 1 and .queued == 1' "$LAB/scheduling-state-9.json" >/dev/null; then
    reached=true
    break
  fi
  sleep 0.1
done
test "$reached" = true

Step 5 — Queue rr-support-2 from the same tenant

Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state. Write rr-support-2.request.json with the displayed editable JSON input. Send rr-support-2 through the Gateway from the namespace shown below; retain its HTTP status and response separately. This client runs in the background; keep the same Bash session for the later release and wait steps.

jq --argjson port "$PORT" \
  '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective' \
  "$LAB/scheduling-state-9.json"
cat > "$LAB/rr-support-2.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "rr-support-2"
    }
  ],
  "max_tokens": 4
}
JSON
ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/support.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: rr-support-2' -H 'X-Request-Id: rr-support-2' \
  -H 'X-Test-Hold: 1' \
  --data-binary "@$LAB/rr-support-2.request.json" \
  -D "$LAB/rr-support-2.headers" -o "$LAB/rr-support-2.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/rr-support-2.http" &
printf '%s\n' "$!" > "$LAB/rr-support-2.pid"

Step 6 — Wait for two queued support requests

Send the displayed management request to /config/loadbalancer/all and retain its response. Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state.

reached=false
for attempt in $(seq 1 80); do
  ip netns exec share-a curl -fsS --max-time 5 "$API/config/loadbalancer/all" \
    -o "$LAB/scheduling-state-10.json"
  if jq -e --argjson port "$PORT" \
    '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective |
     .inflight == 1 and .queued == 2' "$LAB/scheduling-state-10.json" >/dev/null; then
    reached=true
    break
  fi
  sleep 0.1
done
test "$reached" = true
jq --argjson port "$PORT" \
  '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective' \
  "$LAB/scheduling-state-10.json"

Step 7 — Queue rr-research from the research tenant

Write rr-research.request.json with the displayed editable JSON input. Send rr-research through the Gateway from the namespace shown below; retain its HTTP status and response separately. This client runs in the background; keep the same Bash session for the later release and wait steps.

cat > "$LAB/rr-research.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "rr-research"
    }
  ],
  "max_tokens": 4
}
JSON
ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/research.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: rr-research' -H 'X-Request-Id: rr-research' \
  -H 'X-Test-Hold: 1' \
  --data-binary "@$LAB/rr-research.request.json" \
  -D "$LAB/rr-research.headers" -o "$LAB/rr-research.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/rr-research.http" &
printf '%s\n' "$!" > "$LAB/rr-research.pid"
reached=false

Step 8 — Wait for three waiters, then release rr-holder

Send the displayed management request to /config/loadbalancer/all and retain its response. Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state. Release the held backend request rr-holder so its capacity can return to the Gateway.

for attempt in $(seq 1 80); do
  ip netns exec share-a curl -fsS --max-time 5 "$API/config/loadbalancer/all" \
    -o "$LAB/scheduling-state-11.json"
  if jq -e --argjson port "$PORT" \
    '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective |
     .inflight == 1 and .queued == 3' "$LAB/scheduling-state-11.json" >/dev/null; then
    reached=true
    break
  fi
  sleep 0.1
done
test "$reached" = true
jq --argjson port "$PORT" \
  '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective' \
  "$LAB/scheduling-state-11.json"
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/release/rr-holder" | jq .
arrived=false

Step 9 — Release rr-support-1 and confirm rr-research dispatches next

Read rr-support-1 directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for rr-support-1. Release the held backend request rr-support-1 so its capacity can return to the Gateway. Read rr-research directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for rr-research.

for attempt in $(seq 1 80); do
  ip netns exec share-backend curl -fsS --max-time 3 \
    "http://127.0.0.1:8080/receipt/rr-support-1" -o "$LAB/rr-support-1.receipt.json"
  if jq -e '.count == 1' "$LAB/rr-support-1.receipt.json" >/dev/null; then
    arrived=true
    break
  fi
  sleep 0.1
done
test "$arrived" = true
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/release/rr-support-1" | jq .
arrived=false
for attempt in $(seq 1 80); do
  ip netns exec share-backend curl -fsS --max-time 3 \
    "http://127.0.0.1:8080/receipt/rr-research" -o "$LAB/rr-research.receipt.json"
  if jq -e '.count == 1' "$LAB/rr-research.receipt.json" >/dev/null; then
    arrived=true
    break
  fi
  sleep 0.1
done
test "$arrived" = true

Step 10 — Release rr-research and confirm rr-support-2 dispatches last

Read rr-support-2 directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 0 for rr-support-2. Release the held backend request rr-research so its capacity can return to the Gateway. Require backend receipt count 1 for rr-support-2. Release the held backend request rr-support-2 so its capacity can return to the Gateway. Wait for the saved client PID in this same parent Bash before checking its final response. Require saved HTTP 200 for rr-holder. Read rr-holder directly from the backend receipt endpoint; this observation bypasses the Gateway.

ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/rr-support-2" -o "$LAB/rr-support-2.receipt.json"
jq -e '.count == 0' "$LAB/rr-support-2.receipt.json"
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/release/rr-research" | jq .
arrived=false
for attempt in $(seq 1 80); do
  ip netns exec share-backend curl -fsS --max-time 3 \
    "http://127.0.0.1:8080/receipt/rr-support-2" -o "$LAB/rr-support-2.receipt.json"
  if jq -e '.count == 1' "$LAB/rr-support-2.receipt.json" >/dev/null; then
    arrived=true
    break
  fi
  sleep 0.1
done
test "$arrived" = true
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/release/rr-support-2" | jq .
if test -f "$LAB/rr-holder.pid"; then
  wait "$(cat "$LAB/rr-holder.pid")"
fi
test "$(cat "$LAB/rr-holder.http")" = 200
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/rr-holder" -o "$LAB/rr-holder.receipt.json"

Step 11 — Verify HTTP 200 and one receipt for each RR request

Require backend receipt count 1 for rr-holder. Wait for the saved client PID in this same parent Bash before checking its final response. Require saved HTTP 200 for rr-support-1. Read rr-support-1 directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for rr-support-1. Require saved HTTP 200 for rr-research. Read rr-research directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for rr-research. Require saved HTTP 200 for rr-support-2. Read rr-support-2 directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for rr-support-2.

jq -e '.count == 1' "$LAB/rr-holder.receipt.json"
if test -f "$LAB/rr-support-1.pid"; then
  wait "$(cat "$LAB/rr-support-1.pid")"
fi
test "$(cat "$LAB/rr-support-1.http")" = 200
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/rr-support-1" -o "$LAB/rr-support-1.receipt.json"
jq -e '.count == 1' "$LAB/rr-support-1.receipt.json"
if test -f "$LAB/rr-research.pid"; then
  wait "$(cat "$LAB/rr-research.pid")"
fi
test "$(cat "$LAB/rr-research.http")" = 200
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/rr-research" -o "$LAB/rr-research.receipt.json"
jq -e '.count == 1' "$LAB/rr-research.receipt.json"
if test -f "$LAB/rr-support-2.pid"; then
  wait "$(cat "$LAB/rr-support-2.pid")"
fi
test "$(cat "$LAB/rr-support-2.http")" = 200
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/rr-support-2" -o "$LAB/rr-support-2.receipt.json"
jq -e '.count == 1' "$LAB/rr-support-2.receipt.json"
reached=false

Step 12 — Wait for the RR queue to drain to zero

Send the displayed management request to /config/loadbalancer/all and retain its response. Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state.

for attempt in $(seq 1 80); do
  ip netns exec share-a curl -fsS --max-time 5 "$API/config/loadbalancer/all" \
    -o "$LAB/scheduling-state-12.json"
  if jq -e --argjson port "$PORT" \
    '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective |
     .inflight == 0 and .queued == 0' "$LAB/scheduling-state-12.json" >/dev/null; then
    reached=true
    break
  fi
  sleep 0.1
done
test "$reached" = true
jq --argjson port "$PORT" \
  '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective' \
  "$LAB/scheduling-state-12.json"

Expected dispatch: support-1, research, support-2. If it differs, save the receipt arrival times and effective policy; do not infer RR from three eventual HTTP 200s.

Weighted bands: bounded progress under mixed backlog

Use weights 8:3:1 with no floors or reserve, so the wake algorithm alone determines turns. The shell commands below hold one permit, queue 10 critical, 5 standard and 3 sheddable requests, and release one dispatched request at a time. Change the three case rows to alter the backlog or credentials; change fc_band_weights in the rule to choose the allocation. Keep each band runnable for at least one complete weighted cycle. Every request, response, receipt and dispatch turn is visible below.

Step 1 — Apply 8:3:1 band weights and read back the config

Send the displayed management request to /config/loadbalancer and retain its response. Require HTTP 200 for the saved management response before continuing. Send the displayed management request to /config/loadbalancer/all and retain its response.

set -euo pipefail
umask 077
source "${LAB:?set LAB to your private run directory}/env"
jq '.serviceArguments += {
  "fc_wake_policy": "weighted",
  "fc_band_weights": "8:3:1",
  "fc_max_queue_depth": 64,
  "fc_max_queue_wait_ms": 60000
}' \
  "$LAB/base-rule.json" > "$LAB/scheduling-rule-13.request.json"
status=$(ip netns exec share-a curl -sS --max-time 10 -X POST \
  -H 'Content-Type: application/json' --data-binary "@$LAB/scheduling-rule-13.request.json" \
  -D "$LAB/scheduling-rule-13.headers" -o "$LAB/scheduling-rule-13.response.json" \
  -w '%{http_code}' "$API/config/loadbalancer")
printf 'HTTP %s\n' "$status"
jq . "$LAB/scheduling-rule-13.response.json"
test "$status" = 200
status=$(ip netns exec share-a curl -sS --max-time 10 -X GET \
  -D "$LAB/scheduling-config-15.headers" -o "$LAB/scheduling-config-15.response.json" \
  -w '%{http_code}' "$API/config/loadbalancer/all")
printf 'HTTP %s\n' "$status"
jq . "$LAB/scheduling-config-15.response.json"
test "$status" = 200

Step 2 — Start the held weighted-holder request

Write weighted-holder.request.json with the displayed editable JSON input. Send weighted-holder through the Gateway from the namespace shown below; retain its HTTP status and response separately. This client runs in the background; keep the same Bash session for the later release and wait steps.

cat > "$LAB/weighted-holder.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "weighted-holder"
    }
  ],
  "max_tokens": 4
}
JSON
ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/support.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: weighted-holder' -H 'X-Request-Id: weighted-holder' \
  -H 'X-Test-Hold: 1' \
  --data-binary "@$LAB/weighted-holder.request.json" \
  -D "$LAB/weighted-holder.headers" -o "$LAB/weighted-holder.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/weighted-holder.http" &
printf '%s\n' "$!" > "$LAB/weighted-holder.pid"
arrived=false

Step 3 — Confirm the backend received weighted-holder

Read weighted-holder directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for weighted-holder.

for attempt in $(seq 1 80); do
  ip netns exec share-backend curl -fsS --max-time 3 \
    "http://127.0.0.1:8080/receipt/weighted-holder" -o "$LAB/weighted-holder.receipt.json"
  if jq -e '.count == 1' "$LAB/weighted-holder.receipt.json" >/dev/null; then
    arrived=true
    break
  fi
  sleep 0.1
done
test "$arrived" = true
# Every row describes a band, its backlog size and the credential to use.
: > "$LAB/weighted-pending.txt"

Step 4 — Queue 10 critical, 5 standard and 3 sheddable requests

This client runs in the background; keep the same Bash session for the later release and wait steps.

for band in critical standard sheddable; do
  case "$band" in
    critical) count=10; identity=support; objective=interactive ;;
    standard) count=5; identity=research; objective= ;;
    sheddable) count=3; identity=support-batch; objective= ;;
  esac
  for number in $(seq 1 "$count"); do
    name="weighted-$band-$number"
    jq -n --arg model "$MODEL" --arg text "$name" \
      '{model:$model,messages:[{role:"user",content:$text}],max_tokens:4}' \
      > "$LAB/$name.request.json"
    # An empty optional header is omitted; it does not choose another class.
    extra_headers=()
    if test -n "$objective"; then
      extra_headers=(-H "x-llm-d-inference-objective: $objective")
    fi
    ip netns exec share-a curl --http1.1 -sS --max-time 55 \
      --config "$LAB/keys/$identity.curl" -H 'Content-Type: application/json' \
      -H "X-Test-Nonce: $name" -H "X-Request-Id: $name" -H 'X-Test-Hold: 1' \
      "${extra_headers[@]}" --data-binary "@$LAB/$name.request.json" \
      -D "$LAB/$name.headers" -o "$LAB/$name.response.json" \
      -w '%{http_code}\n' "$INFER" > "$LAB/$name.http" &
    printf '%s\n' "$!" > "$LAB/$name.pid"
    printf '%s\n' "$name" >> "$LAB/weighted-pending.txt"
  done
done

Step 5 — Wait for 18 waiters, then release weighted-holder

Send the displayed management request to /config/loadbalancer/all and retain its response. Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state. Release the held backend request weighted-holder so its capacity can return to the Gateway.

reached=false
for attempt in $(seq 1 80); do
  ip netns exec share-a curl -fsS --max-time 5 "$API/config/loadbalancer/all" \
    -o "$LAB/weighted-state-101.json"
  if jq -e --argjson port "$PORT" \
    '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective |
     .inflight == 1 and .queued == 18' "$LAB/weighted-state-101.json" >/dev/null; then
    reached=true
    break
  fi
  sleep 0.1
done
test "$reached" = true
jq --argjson port "$PORT" \
  '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective' \
  "$LAB/weighted-state-101.json"
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/release/weighted-holder" | jq .
: > "$LAB/weighted-order.tsv"

Step 6 — Release each dispatched request and record its band

Read $name directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for $name. Release the held backend request $arrived so its capacity can return to the Gateway.

for turn in $(seq 1 18); do
  arrived=''
  for attempt in $(seq 1 100); do
    while read -r name; do
      ip netns exec share-backend curl -fsS --max-time 3 \
        "http://127.0.0.1:8080/receipt/$name" -o "$LAB/$name.receipt.json"
      if jq -e '.count == 1' "$LAB/$name.receipt.json" >/dev/null; then
        arrived="$name"
        break
      fi
    done < "$LAB/weighted-pending.txt"
    test -n "$arrived" && break
    sleep 0.05
  done
  test -n "$arrived"
  band=${arrived#weighted-}
  band=${band%-*}
  printf '%s\t%s\n' "$arrived" "$band" >> "$LAB/weighted-order.tsv"
  ip netns exec share-backend curl -fsS --max-time 3 \
    "http://127.0.0.1:8080/release/$arrived" > "$LAB/$arrived.release.json"
  # grep returns 1 when the last pending row has been removed.
  grep -Fxv "$arrived" "$LAB/weighted-pending.txt" > "$LAB/weighted-pending.tmp" || test "$turn" = 18
  mv "$LAB/weighted-pending.tmp" "$LAB/weighted-pending.txt"
done

Step 7 — Check every response and the first-12 band split

Wait for the saved client PID in this same parent Bash before checking its final response. Require saved HTTP 200 for $name. Read $name directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for $name.

for file in "$LAB"/weighted-*.pid; do
  name=$(basename "$file" .pid)
  wait "$(cat "$file")"
  test "$(cat "$LAB/$name.http")" = 200
  ip netns exec share-backend curl -fsS --max-time 3 \
    "http://127.0.0.1:8080/receipt/$name" -o "$LAB/$name.receipt.json"
  jq -e '.count == 1' "$LAB/$name.receipt.json" >/dev/null
done
cat "$LAB/weighted-order.tsv"
head -n 12 "$LAB/weighted-order.tsv" | cut -f2 | sort | uniq -c
test "$(head -n 12 "$LAB/weighted-order.tsv" | cut -f2 | grep -c '^critical$')" = 8
test "$(head -n 12 "$LAB/weighted-order.tsv" | cut -f2 | grep -c '^standard$')" = 3
test "$(head -n 12 "$LAB/weighted-order.tsv" | cut -f2 | grep -c '^sheddable$')" = 1
reached=false

Step 8 — Wait for the weighted queue to drain to zero

Send the displayed management request to /config/loadbalancer/all and retain its response. Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state.

for attempt in $(seq 1 80); do
  ip netns exec share-a curl -fsS --max-time 5 "$API/config/loadbalancer/all" \
    -o "$LAB/scheduling-state-16.json"
  if jq -e --argjson port "$PORT" \
    '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective |
     .inflight == 0 and .queued == 0' "$LAB/scheduling-state-16.json" >/dev/null; then
    reached=true
    break
  fi
  sleep 0.1
done
test "$reached" = true
jq --argjson port "$PORT" \
  '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective' \
  "$LAB/scheduling-state-16.json"

Expected first 12 dispatches, while all bands have runnable backlog: critical 8, standard 3, sheddable 1. Remaining work drains as bands empty; do not require the same ratio after a band runs out. This is a bounded dispatch experiment, not a long-term latency or compute-share guarantee. An invalid weight configuration must leave the accepted policy usable:

Step 1 — Reject the invalid 8:0:1 weights with HTTP 400

Send the displayed management request to /config/loadbalancer and retain its response. Require HTTP 400 for the saved management response before continuing.

set -euo pipefail
umask 077
source "${LAB:?set LAB to your private run directory}/env"
jq '.serviceArguments += {
  "fc_wake_policy": "weighted",
  "fc_band_weights": "8:0:1"
}' \
  "$LAB/base-rule.json" > "$LAB/scheduling-rule-17.request.json"
status=$(ip netns exec share-a curl -sS --max-time 10 -X POST \
  -H 'Content-Type: application/json' --data-binary "@$LAB/scheduling-rule-17.request.json" \
  -D "$LAB/scheduling-rule-17.headers" -o "$LAB/scheduling-rule-17.response.json" \
  -w '%{http_code}' "$API/config/loadbalancer")
printf 'HTTP %s\n' "$status"
jq . "$LAB/scheduling-rule-17.response.json"
test "$status" = 400

Step 2 — Send after-invalid-weight and expect HTTP 200

Write after-invalid-weight.request.json with the displayed editable JSON input. Send after-invalid-weight through the Gateway from the namespace shown below; retain its HTTP status and response separately. Wait for the saved client PID in this same parent Bash before checking its final response. Require saved HTTP 200 for after-invalid-weight.

cat > "$LAB/after-invalid-weight.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "after-invalid-weight"
    }
  ],
  "max_tokens": 4
}
JSON
ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/support.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: after-invalid-weight' -H 'X-Request-Id: after-invalid-weight' \
  --data-binary "@$LAB/after-invalid-weight.request.json" \
  -D "$LAB/after-invalid-weight.headers" -o "$LAB/after-invalid-weight.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/after-invalid-weight.http"
if test -f "$LAB/after-invalid-weight.pid"; then
  wait "$(cat "$LAB/after-invalid-weight.pid")"
fi
test "$(cat "$LAB/after-invalid-weight.http")" = 200

Step 3 — Confirm the backend received after-invalid-weight

Read after-invalid-weight directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for after-invalid-weight.

ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/after-invalid-weight" -o "$LAB/after-invalid-weight.receipt.json"
jq -e '.count == 1' "$LAB/after-invalid-weight.receipt.json"

One tenant cannot multiply its inflight share with keys

At ceiling 4 and tenant share 50%, support can hold 2 permits. A second interactive-capable key for the same department cannot obtain a third. Research still proceeds. For this experiment fc_max_queue_depth: 0 resets to the process default. Setup explicitly sets LLB_FC_MAX_QUEUE_DEPTH=0, so the effective depth is zero and the third support request receives an immediate 429. If your process default is nonzero, a rule value of zero inherits that queue; check the effective configuration before expecting immediate refusal.

Step 1 — Create the support-secondary API key

Write support-secondary.key-request.json with the displayed editable JSON input. Send the displayed management request to /config/ai/apikey and retain its response. Require HTTP 201 for the saved management response before continuing.

set -euo pipefail
umask 077
source "${LAB:?set LAB to your private run directory}/env"
cat > "$LAB/support-secondary.key-request.json" <<'JSON'
{
  "tenant_id": "support",
  "name": "support-secondary",
  "allowed_models": [
    "shared-model"
  ],
  "enabled": true,
  "rate_limit_rps": 10000,
  "burst_size": 10000,
  "priority_class": "critical"
}
JSON
status=$(ip netns exec share-a curl -sS --max-time 10 -X POST \
  -H 'Content-Type: application/json' --data-binary "@$LAB/support-secondary.key-request.json" \
  -o "$LAB/keys/support-secondary.json" -w '%{http_code}' "$API/config/ai/apikey")
test "$status" = 201
jq -er '"header = \"X-Api-Key: " + .raw_key + "\""' \
  "$LAB/keys/support-secondary.json" > "$LAB/keys/support-secondary.curl"
chmod 600 "$LAB/keys/support-secondary.json" "$LAB/keys/support-secondary.curl"
jq '{key_id,tenant_id,allowed_models,priority_class}' "$LAB/keys/support-secondary.json"

Step 2 — Set ceiling 4 with a 50% tenant share

Send the displayed management request to /config/loadbalancer and retain its response. Require HTTP 200 for the saved management response before continuing.

jq '.serviceArguments += {
  "fc_max_outstanding": 4,
  "fc_max_queue_depth": 0,
  "fc_tenant_max_share_pct": 50,
  "fc_shed_queue": "off"
}' \
  "$LAB/base-rule.json" > "$LAB/scheduling-rule-19.request.json"
status=$(ip netns exec share-a curl -sS --max-time 10 -X POST \
  -H 'Content-Type: application/json' --data-binary "@$LAB/scheduling-rule-19.request.json" \
  -D "$LAB/scheduling-rule-19.headers" -o "$LAB/scheduling-rule-19.response.json" \
  -w '%{http_code}' "$API/config/loadbalancer")
printf 'HTTP %s\n' "$status"
jq . "$LAB/scheduling-rule-19.response.json"
test "$status" = 200

Step 3 — Start the held share-support-1 request

Write share-support-1.request.json with the displayed editable JSON input. Send share-support-1 through the Gateway from the namespace shown below; retain its HTTP status and response separately. This client runs in the background; keep the same Bash session for the later release and wait steps.

cat > "$LAB/share-support-1.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "share-support-1"
    }
  ],
  "max_tokens": 4
}
JSON
ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/support.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: share-support-1' -H 'X-Request-Id: share-support-1' \
  -H 'X-Test-Hold: 1' \
  --data-binary "@$LAB/share-support-1.request.json" \
  -D "$LAB/share-support-1.headers" -o "$LAB/share-support-1.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/share-support-1.http" &
printf '%s\n' "$!" > "$LAB/share-support-1.pid"
arrived=false

Step 4 — Confirm the backend received share-support-1

Read share-support-1 directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for share-support-1. Write share-support-2.request.json with the displayed editable JSON input.

for attempt in $(seq 1 80); do
  ip netns exec share-backend curl -fsS --max-time 3 \
    "http://127.0.0.1:8080/receipt/share-support-1" -o "$LAB/share-support-1.receipt.json"
  if jq -e '.count == 1' "$LAB/share-support-1.receipt.json" >/dev/null; then
    arrived=true
    break
  fi
  sleep 0.1
done
test "$arrived" = true
cat > "$LAB/share-support-2.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "share-support-2"
    }
  ],
  "max_tokens": 4
}
JSON

Step 5 — Start share-support-2 and confirm its receipt

Send share-support-2 through the Gateway from the namespace shown below; retain its HTTP status and response separately. This client runs in the background; keep the same Bash session for the later release and wait steps. Read share-support-2 directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for share-support-2.

ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/support.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: share-support-2' -H 'X-Request-Id: share-support-2' \
  -H 'X-Test-Hold: 1' \
  --data-binary "@$LAB/share-support-2.request.json" \
  -D "$LAB/share-support-2.headers" -o "$LAB/share-support-2.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/share-support-2.http" &
printf '%s\n' "$!" > "$LAB/share-support-2.pid"
arrived=false
for attempt in $(seq 1 80); do
  ip netns exec share-backend curl -fsS --max-time 3 \
    "http://127.0.0.1:8080/receipt/share-support-2" -o "$LAB/share-support-2.receipt.json"
  if jq -e '.count == 1' "$LAB/share-support-2.receipt.json" >/dev/null; then
    arrived=true
    break
  fi
  sleep 0.1
done
test "$arrived" = true

Step 6 — Expect HTTP 429 for share-same-tenant

Write share-same-tenant.request.json with the displayed editable JSON input. Send share-same-tenant through the Gateway from the namespace shown below; retain its HTTP status and response separately. Wait for the saved client PID in this same parent Bash before checking its final response. Require saved HTTP 429 for share-same-tenant.

cat > "$LAB/share-same-tenant.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "share-same-tenant"
    }
  ],
  "max_tokens": 4
}
JSON
ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/support-secondary.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: share-same-tenant' -H 'X-Request-Id: share-same-tenant' \
  --data-binary "@$LAB/share-same-tenant.request.json" \
  -D "$LAB/share-same-tenant.headers" -o "$LAB/share-same-tenant.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/share-same-tenant.http"
if test -f "$LAB/share-same-tenant.pid"; then
  wait "$(cat "$LAB/share-same-tenant.pid")"
fi
test "$(cat "$LAB/share-same-tenant.http")" = 429

Step 7 — Check share-same-tenant was refused, then send share-other-tenant

Read share-same-tenant directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 0 for share-same-tenant. Write share-other-tenant.request.json with the displayed editable JSON input. Send share-other-tenant through the Gateway from the namespace shown below; retain its HTTP status and response separately.

ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/share-same-tenant" -o "$LAB/share-same-tenant.receipt.json"
jq -e '.count == 0' "$LAB/share-same-tenant.receipt.json"
jq -e '((.["error"] == "admission_tenant_share"))' "$LAB/share-same-tenant.response.json"
cat > "$LAB/share-other-tenant.request.json" <<'JSON'
{
  "model": "shared-model",
  "messages": [
    {
      "role": "user",
      "content": "share-other-tenant"
    }
  ],
  "max_tokens": 4
}
JSON
ip netns exec share-a curl --http1.1 -sS --max-time 55 \
  --config "$LAB/keys/research.curl" \
  -H 'Content-Type: application/json' \
  -H 'X-Test-Nonce: share-other-tenant' -H 'X-Request-Id: share-other-tenant' \
  --data-binary "@$LAB/share-other-tenant.request.json" \
  -D "$LAB/share-other-tenant.headers" -o "$LAB/share-other-tenant.response.json" \
  -w '%{http_code}\n' "$INFER" > "$LAB/share-other-tenant.http"

Step 8 — Confirm share-other-tenant succeeds and release support holds

Wait for the saved client PID in this same parent Bash before checking its final response. Require saved HTTP 200 for share-other-tenant. Read share-other-tenant directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for share-other-tenant. Release the held backend request share-support-1 so its capacity can return to the Gateway. Release the held backend request share-support-2 so its capacity can return to the Gateway. Require saved HTTP 200 for share-support-1. Read share-support-1 directly from the backend receipt endpoint; this observation bypasses the Gateway. Require backend receipt count 1 for share-support-1. Require saved HTTP 200 for share-support-2. Read share-support-2 directly from the backend receipt endpoint; this observation bypasses the Gateway.

if test -f "$LAB/share-other-tenant.pid"; then
  wait "$(cat "$LAB/share-other-tenant.pid")"
fi
test "$(cat "$LAB/share-other-tenant.http")" = 200
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/share-other-tenant" -o "$LAB/share-other-tenant.receipt.json"
jq -e '.count == 1' "$LAB/share-other-tenant.receipt.json"
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/release/share-support-1" | jq .
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/release/share-support-2" | jq .
if test -f "$LAB/share-support-1.pid"; then
  wait "$(cat "$LAB/share-support-1.pid")"
fi
test "$(cat "$LAB/share-support-1.http")" = 200
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/share-support-1" -o "$LAB/share-support-1.receipt.json"
jq -e '.count == 1' "$LAB/share-support-1.receipt.json"
if test -f "$LAB/share-support-2.pid"; then
  wait "$(cat "$LAB/share-support-2.pid")"
fi
test "$(cat "$LAB/share-support-2.http")" = 200
ip netns exec share-backend curl -fsS --max-time 3 \
  "http://127.0.0.1:8080/receipt/share-support-2" -o "$LAB/share-support-2.receipt.json"

Step 9 — Wait for the pool to return to idle

Require backend receipt count 1 for share-support-2. Send the displayed management request to /config/loadbalancer/all and retain its response. Inspect the displayed effective inflight/queue condition; a successful configuration response alone does not prove this state.

jq -e '.count == 1' "$LAB/share-support-2.receipt.json"
reached=false
for attempt in $(seq 1 80); do
  ip netns exec share-a curl -fsS --max-time 5 "$API/config/loadbalancer/all" \
    -o "$LAB/scheduling-state-21.json"
  if jq -e --argjson port "$PORT" \
    '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective |
     .inflight == 0 and .queued == 0' "$LAB/scheduling-state-21.json" >/dev/null; then
    reached=true
    break
  fi
  sleep 0.1
done
test "$reached" = true
jq --argjson port "$PORT" \
  '.lbAttr[] | select(.serviceArguments.port == $port) | .serviceArguments.fc_effective' \
  "$LAB/scheduling-state-21.json"

Tenant share is local to this pool. Cross-node limits need a separate contract. Continue to floor and reserve.

Check your result

Prove a backlog and actual backend admission order; eventual completion order is not dispatch order.

Keep the configured policy, HTTP status, resolved identity, and independent backend receipt together. A rejected request must have receipt 0; a successful request must have receipt 1. A missing receipt response is a failed observation, never a zero.

Next steps and cleanup

Continue in the documented order with the same LAB. Remove an experiment's binding or temporary rule before the next experiment; stop the shared lab only at the final cleanup chapter. Read the lab starting point if required values are missing. Use cleanup and recovery when ending the run.