…nect drain
MAJOR-1: the initial POST dispatch marked the first child RUNNING with a plain
ORM write committed only AFTER the blocking dispatch_to_agent round-trip, while
the group+children were already committed PENDING. A WS (re)connect firing in
that window found the row PENDING, won claim_pending_run, and sent a SECOND
{"type":"run"} for the same run. Now the initial dispatch claims the child
ATOMICALLY (claim_pending_run, group-guarded) and PERSISTS the claim BEFORE the
send round-trip, and reverts on send failure -- so initial-dispatch and
connect-drain are mutually exclusive on the row; the loser skips.
MAJOR-2: the connect-drain guarded agent-wide while _dispatch_next_group_child
claimed next-in-group, so on a run_completed+reconnect interleave the two paths
could claim different sibling rows and put two children of one group RUNNING.
claim_pending_run now takes group_id and adds a NOT-EXISTS group-sequential
guard (refuse if any sibling is RUNNING); _dispatch_next_group_child uses it, and
the connect-drain routes grouped runs through _dispatch_next_group_child -- one
serialization point, so two siblings can never both be RUNNING.
MINOR-3: add deterministic state-level tests for the group guard (two siblings
can't both be RUNNING), the MAJOR-1 initial-vs-drain claim race, and async tests
for the drain's claim -> send_command_to_agent -> group PENDING->RUNNING path and
the release_claimed_run rollback on send failure.
MINOR-4: pre-existing WS-identity weakness (drain auto-sends a run config to any
JWT socket when BENCHMARK_AGENT_AUTH_REQUIRED is off) left for a separate issue --
a matching-agent_id guard would reject the flag-off built-in agent (whose token
legitimately carries no agent_id claim), so it is not a safe one-liner here.
Claude-Session: https://claude.ai/code/session_01UCsZXDxBsWV2s4kT47DwDW
Summary
This PR resolves external routability and port mapping issues when benchmarking non-BNK proxies (HAProxy and NGINX) from external benchmark agents, and introduces multi-cluster fleet filtering and cluster badging across the Benchmarks UI.
Key Changes
_resolve_service_external_urlinProxyDeployServiceto inspect Kubernetes NodePort allocations and worker node routable VPC IPs (InternalIP/ExternalIP/Hostname), storing the reachable URL inProxyDeployment.external_url.trigger_benchmark_runand scenario dispatches to prioritizedeploy.external_urlover internal cluster DNS.BenchmarkTargetService.validate_targetto verify backing services and running pods for internal cluster URLs._values_haproxyinProxyDeployServiceto setservice.ports.httpandcontainerPorts.httpto 10080 (matchinghaproxy.cfgbind port), eliminating connection refused errors on NodePort forwards.cluster_nameproperty toBenchmarkTarget,BenchmarkRun, andBenchmarkRunGroupmodels and response schemas.cluster_idquery filtering and eager loading to benchmark listing APIs.ClusterPickerintoBenchmarks.tsxheader for fleet-level aggregate or cluster-scoped views.Verification
test_proxy_deploy_resolve_url.py,test_validate_target.py,test_benchmark_cluster_info.py,test_proxy_deploy_new_proxies.py).BenchmarkTargetsTab.test.tsx).bnk-singapore: executed Run E2E: Dev/QA — performance & validation (benchmark run & compare) #60 against HAProxy with 250/250 successful requests (100% success rate).