feat(loadtest): 5-minute low-rate sweep workloads; portkey routing with features full

This commit is contained in:
narmstrong-berry 2026-11-03 17:55:38 +00:00
parent 2bb4905f5f
commit 5990e10ff8
No known key found for this signature in database
7 changed files with 35 additions and 15 deletions

View file

@ -16,8 +16,9 @@ machines.
| `s3stub` | In-memory path-style S3 (PUT, GET, ListObjectsV2) for the `s3_v2` logs of a full-feature run. Objects are stored compressed and listed from a sorted index, so millions of logs fit. |
Workloads live in `workloads/`: `smoke`, `realistic-ramp`, `long-streams`, `large-context`,
`key-churn` and `soak`. `sustained-10`, `-25`, `-50`, `-75`, `-100`, `-250`, `-500` and `-1000` hold one rate for
15 minutes with evenly spaced arrivals and smoke's request mix over every model group;
`key-churn` and `soak`. `sustained-100`, `sustained-250`, `sustained-500` and `sustained-1000` hold one rate for
15 minutes with evenly spaced arrivals and smoke's request mix over every model group (`sweep-10`, `-25`, `-50`,
`-75` and `-100` the same for 5 minutes each, for a low-rate sweep);
`sustained-check` runs the same mix at 100, 500 and 1,000 rps for 2 minutes each, to check that the
client, mock and stack hold 1,000 rps before the long runs.
@ -183,7 +184,7 @@ are only compared between the LiteLLM sides, since Bifrost's schema differs thro
## Portkey
A key set named `portkey` (`-key-sets ,portkey`, basic features only) is written as routing for the
A key set named `portkey` (`-key-sets ,portkey`; with `-features full` it is unchanged, since Portkey has none of those features) is written as routing for the
[open-source Portkey gateway](https://github.com/Portkey-AI/gateway) instead of key rows. That
gateway keeps no model list, keys or database: every request carries its routing in an
`x-portkey-config` header. So genconfig writes `portkey-headers.json`, one header per model group,

View file

@ -16,7 +16,7 @@
// on. S3 settings come from LOADTEST_S3_* environment variables.
//
// -key-sets ,portkey adds Portkey gateway routing (portkey.go): one x-portkey-config header per
// model group, in portkey-headers.json, for loadgen -model-headers. Basic features only.
// model group, in portkey-headers.json, for loadgen -model-headers. Features never change it.
//
// -key-sets python,rust writes one key set, with its own users and teams, per name, so each
// gateway's spend can be told apart in a shared database.
@ -142,9 +142,8 @@ general_settings:
sw.WriteString("-- Load-test virtual keys. Hash = sha256 hex of the key, as the proxy stores it.\n")
for _, set := range sets {
if set == portkeySet {
if full {
log.Fatal("the portkey key set needs -features basic: the open-source Portkey gateway has no keys, limits, budgets or logs to turn on")
}
// The same routing with -features full: the open-source Portkey gateway has no keys,
// limits, budgets or logs to turn on, so the other sides' features have no Portkey form.
writePortkey(bifrostDeps, portkeyKeys(*keys, *keySeed), *outDir)
continue
}

View file

@ -1,6 +1,6 @@
{
"name": "sustained 25 rps for 15 minutes, evenly spaced arrivals, smoke's request mix over every model group",
"stages": [{"duration_s": 900, "rps": 25}],
"name": "sustained 10 rps for 5 minutes, evenly spaced arrivals, smoke's request mix over every model group",
"stages": [{"duration_s": 300, "rps": 10}],
"arrivals": "constant",
"nonstream_max_s": 360,
"warmup_s": 60,

View file

@ -1,6 +1,6 @@
{
"name": "sustained 10 rps for 15 minutes, evenly spaced arrivals, smoke's request mix over every model group",
"stages": [{"duration_s": 900, "rps": 10}],
"name": "sustained 100 rps for 5 minutes, evenly spaced arrivals, smoke's request mix over every model group",
"stages": [{"duration_s": 300, "rps": 100}],
"arrivals": "constant",
"nonstream_max_s": 360,
"warmup_s": 60,

View file

@ -1,6 +1,6 @@
{
"name": "sustained 50 rps for 15 minutes, evenly spaced arrivals, smoke's request mix over every model group",
"stages": [{"duration_s": 900, "rps": 50}],
"name": "sustained 25 rps for 5 minutes, evenly spaced arrivals, smoke's request mix over every model group",
"stages": [{"duration_s": 300, "rps": 25}],
"arrivals": "constant",
"nonstream_max_s": 360,
"warmup_s": 60,

View file

@ -1,6 +1,6 @@
{
"name": "sustained 75 rps for 15 minutes, evenly spaced arrivals, smoke's request mix over every model group",
"stages": [{"duration_s": 900, "rps": 75}],
"name": "sustained 50 rps for 5 minutes, evenly spaced arrivals, smoke's request mix over every model group",
"stages": [{"duration_s": 300, "rps": 50}],
"arrivals": "constant",
"nonstream_max_s": 360,
"warmup_s": 60,

View file

@ -0,0 +1,20 @@
{
"name": "sustained 75 rps for 5 minutes, evenly spaced arrivals, smoke's request mix over every model group",
"stages": [{"duration_s": 300, "rps": 75}],
"arrivals": "constant",
"nonstream_max_s": 360,
"warmup_s": 60,
"max_in_flight": 30000,
"keys": {"file": "out/keys.txt", "zipf_s": 0},
"models": {"chat": {"prefix": "gpt-6-astra-", "count": 10}, "messages": {"prefix": "claude-opus-5-5-", "count": 10}},
"classes": [
{"name": "chat", "weight": 1, "endpoint": "chat", "stream_fraction": 0.5,
"context_tokens": {"dist": "loguniform", "min": 10000, "max": 50000},
"ttft_ms": {"dist": "uniform", "min": 200, "max": 800},
"duration_s": {"dist": "uniform", "min": 1, "max": 5}},
{"name": "messages", "weight": 1, "endpoint": "messages", "stream_fraction": 0.5,
"context_tokens": {"dist": "loguniform", "min": 10000, "max": 50000},
"ttft_ms": {"dist": "uniform", "min": 200, "max": 800},
"duration_s": {"dist": "uniform", "min": 1, "max": 5}}
]
}