feat: support staged, spike, and arrival-rate load profiles - #46
Merged
Conversation
…elease v1.7.0 Flux previously only modeled load as a fixed concurrency plus optional ramp_up, so it could not express a stepped load, a hold period, a spike, or a target request-arrival rate. This adds a `load_profile` configuration that replaces `concurrency`/`ramp_up` when set: - config: a `LoadProfile` enum with `stages` (a sequence of duration/target_concurrency steps) and `arrival_rate` (target_rps/duration/max_concurrency) variants, validated and environment-variable-expandable like the rest of the config; - executor: a staged scheduler spawns every worker any stage could need up front and gates each on the currently active stage's target concurrency, so transitions happen on schedule without respawning tasks; an arrival-rate pacer paces request starts with a tokio interval and bounds in-flight requests with a semaphore, counting a pacing tick that finds no free permit as saturation instead of queuing unbounded work; - metrics/reporter: a per-stage breakdown and configured-vs-achieved rate summary are attributed as requests complete and included in the terminal summary, JSON report and HTML report; a plain fixed-concurrency run carries no load_profile and an empty stages list, so existing reports are unaffected; - cancellation, retries, think time and scenarios reuse the same execution paths as fixed concurrency, so they behave identically under a load profile. Also releases v1.7.0 and documents stage/spike/arrival-rate examples in the README and samples/. Co-authored-by: Claude <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.
Summary
Closes #33. Flux previously modeled load only as a fixed
concurrency(with an optionalramp_up), which cannot express a stepped load, a hold period, a spike, or a target request-arrival rate. This adds aload_profileconfiguration section that replacesconcurrency/duration/ramp_upwhen configured:Existing configs with no
load_profileare completely unaffected — same fields, same execution path, same behavior.Implementation
LoadProfileenum (stages/arrival_rate), mutually exclusive withramp_upsince a profile fully replaces the warm-up model. Stage/arrival-rate durations support${VAR}expansion like every other duration field.--concurrency/--durationCLI overrides are rejected whenload_profileis set, so timing always comes from one place.target_concurrencyidles (50ms poll) instead of sending requests. This makes transitions land on schedule with no worker-startup latency mid-run, while reusing the same worker-loop code path as fixed concurrency, so retries, think time, scenarios, and cancellation behave identically.tokio::time::interval, independent of how long each request takes. In-flight requests are bounded by aSemaphoresized tomax_concurrency; a pacing tick that finds no free permit is counted as saturated and skipped rather than queued, so an overloaded target shows up as a number instead of unbounded task creation.load_profileand an emptystageslist.Testing
cargo test— 138 tests pass, including new coverage for profile validation (empty/zero-duration stages, non-positivetarget_rps, zeromax_concurrency, theramp_up/load_profileconflict, env-var expansion), stage transition timing, arrival-rate pacing and saturation tracking, and cancellation mid-stage / mid-pacing.cargo clippy --all-targetsandcargo fmt --check— clean.--releasebuilds) confirmed staged concurrency transitions on schedule, arrival-rate pacing tracks the target rate (97/100 req/s achieved in a release build against a fast local target), and per-stage request counts sum exactly to the run total.samples/*.yamlconfigs added.Version
Bumped
vars/versionandCargo.tomlfromv1.6.0tov1.7.0(minor), per this repo's existing release convention.Test plan
cargo testcargo clippy --all-targetscargo fmt --checkGenerated by Claude Code