feat: target the prod auth gateway from the capacity benchmark - #327
Open
tvinagre wants to merge 16 commits into
Open
feat: target the prod auth gateway from the capacity benchmark#327tvinagre wants to merge 16 commits into
tvinagre wants to merge 16 commits into
Conversation
Queue wait growing while solve time stays flat is the leading indicator of worker saturation. Records worker_pool_queue_wait_seconds (histogram) and worker_pool_queue_depth (gauge), both labeled by pool, at the point each worker picks a task off the queue. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The TODO to make worker_id a string with the pool name is obsoleted by the pool_name field added directly below it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Threads an encoding: bool through SwapRequest::to_quote_params_with_encoding, ParallelizationMode::run, and its three strategy functions so load tests can attach EncodingOptions (0.5% slippage, TransferFrom) and exercise the full quote+encode server path. Exposed as --encoding on the load subcommand; scale keeps the flag off. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wires the ladder+SLO evaluation types from capacity_report.rs into a new `capacity` CLI subcommand: measures an unloaded sequential baseline, steps an RPS ladder with rate-based traffic until a step breaches the SLO, and prints the resulting report as JSON after a marker line so in-cluster Jobs can pull it from pod logs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Task 6's in-cluster capacity Job runs fynd-benchmark from the published image, so both build stages now compile the crate and the runtime stage copies the binary to /usr/local/bin. The second build stage copies tools/benchmark/src/ instead of stubbing it, since pairs.json and trades_sample.json are embedded via include_str! and must be present when the real binary is compiled. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The human-readable "Capacity: N rps" trailer was printed after the marker + JSON block, so the documented log-retrieval capture (sed marker-to-EOF, drop the marker line) appended prose after the closing brace and produced invalid JSON. Print the summary first so marker-to-EOF minus the marker line is exactly the JSON. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The orchestration test used SloPolicy::default(), whose 1.2x p95 multiplier sits inside OS scheduling jitter with a ~1ms mock baseline. Worse, round-trips are measured in whole milliseconds, so an undelayed local mock truncates the baseline p95 to 0ms — making the SLO limit (baseline * multiplier) zero and failing every step regardless of the multiplier. Give the mock a 20ms response delay so the baseline is nonzero, and use a 100x multiplier since SLO pass/fail math is already unit-tested in capacity_report; this test covers orchestration only. Verified 10 consecutive passing runs locally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
capacity_report.rs: floor the baseline p95 at 1ms before scaling by the SLO multiplier. Sub-millisecond baselines against fast mocks/solvers truncate p95 to 0ms, making the SLO limit 0 and failing every step regardless of actual latency. capacity_report.rs: replace the manual hex-encoding loop in sha256_hex with alloy::hex::encode, matching the pattern already used elsewhere in the tools workspace. capacity.rs: add ladder_stops_at_first_failing_step, pinning that the ladder orchestration breaks on the first failing step instead of continuing. README.md: note that request intervals are whole milliseconds, so offered rates quantize above ~30 rps, and recommend reading a step's achieved_rps over its target_rps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The second RUN block's touch list covered every real-source main.rs except tools/benchmark/src/main.rs. On a fresh clone, cargo's mtime freshness check could still treat the dep-layer's stub fynd-benchmark binary as up to date, shipping a no-op binary in the image. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A GitHub pre-release currently behaves like a full release: it moves the floating latest image tag, auto-promotes the tag to the staging environment, and attempts crates.io/npm publishes. Guard all three on github.event.release.prerelease so a pre-release builds and pushes only its own version-tagged Docker image, deployed deliberately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 10k aggregator trade dataset (and embedded sample, as a defensive measure) can contain templates where token_in == token_out. The server rejects these with 400 BadRequest, which the capacity subcommand counts as an HTTP error, tripping the 0.1% SLO error-rate threshold on every ladder step regardless of latency. Filter same-token templates (case-insensitive) in load_request_templates and load_embedded_trades, logging the skipped count via tracing::warn! so a filtered dataset is never silently smaller than it looks. Error out if filtering leaves zero templates. load_all_templates_from_file already delegates to load_request_templates; load_all_embedded_templates gets the same filtering and logging. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Staging measurement showed a heterogeneous 10k-trade dataset's unsolved rate fluctuates ±2pp between step samples (binomial noise), so the hardcoded 0.001 excess-unsolved tolerance fails healthy capacity steps. Add --max-http-error-rate and --max-excess-unsolved-rate flags so operators can tune both SloPolicy thresholds per dataset, matching the existing --slo-multiplier flag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Generate a per-chain synthetic swap-request dataset for capacity load testing, covering chains the Ethereum-only download-trades set does not. Adds fynd_rpc::protocols::fetch_token_pool_stats, which reuses the Tycho pool-count query behind derive-connector-tokens to return per-token pool counts and decimals. The new subcommand samples token pairs weighted by pool count (seeded StdRng, no self-pairs), log-spaces amounts around one whole token, and writes a --requests-file-compatible JSON array. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FyndClientBuilder gains with_api_key (sent verbatim as the Authorization header, no Bearer prefix, matching the auth gateway) and with_quote_path / with_health_path so a chain-scoped base like https://host/v1/base joins to /v1/base/quote. HTTP 429 responses map to a new non-retryable FyndError::RateLimited carrying the Retry-After hint, so a rate-limited request is not retried into extra load. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wire FYND_API_KEY, QUOTE_PATH, and HEALTH_PATH into the capacity and load subcommands so they can drive a solver behind the auth gateway at https://fynd-api.propellerheads.xyz/v1/<chain>. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the client and benchmark support needed to run the capacity/load
benchmark against the prod auth gateway (
https://fynd-api.propellerheads.xyz/v1/<chain>).Client (
fynd-client)FyndClientBuilder::with_api_keysends the key verbatim as theAuthorizationheader (noBearerprefix), matching the gateway'sauth.lua, which compares the raw header value against its key store.FyndClientBuilder::with_quote_path/with_health_pathoverride the request path appended to the base URL (defaults/v1/quote,/v1/health). A chain-scoped base such ashttps://host/v1/basewith/quotejoins to/v1/base/quote, which the gateway rewrites to upstream/v1/quote.FyndError::RateLimited { retry_after }. Previously a 429 body failed to parse as a FyndErrorResponseand surfaced as a retryableHttperror, so the client retried a rate-limited request three times, amplifying load and distorting latency stats.Benchmark (
fynd-benchmark)capacityandloadsubcommands acceptFYND_API_KEY,QUOTE_PATH, andHEALTH_PATH(also--api-key,--quote-path,--health-path) and pass them to the client. Gateway 429s count toward the report'shttp_error_raterather than being retried.Tests
RateLimitedand is not retried (expect(1)against a 3-attempt retry policy);RateLimitedis not retryable.capacityparses the three gateway flags and defaults/v1/quoteand/v1/health.Note: overlaps PR #325
This branch is based on the release branch (
tl/release-0.90.0-rc) rather thanmain, because thecapacity/generate-requestssubcommands it wires up are not yet onmain. Its client-side auth (FyndClientBuilder::with_api_key+with_quote_path/with_health_pathoverrides, and the non-retryableFyndError::RateLimited429 variant) overlaps with PR #325, which adds hosted-gateway auth (with_api_key/with_chain) to the sameclient.rs. Both PRs touchclient.rsand will conflict on merge. Once #325 lands with the raw-Authorizationheader fix (the deployed gateway matches the raw token; aBearerprefix returns 401), this client change should be reconciled with — or superseded by — #325's implementation. The benchmark wiring and manifests here run against the built rc image and are independent of which client API wins.🤖 Generated with Claude Code