-
-
Notifications
You must be signed in to change notification settings - Fork 1
Performance and Load Baseline
This page characterizes the login path's behavior under load and concurrency - the RC-readiness question of #742. The login path carries three load-oriented controls; each is pinned by regression tests that double as the behavioral demonstration, and a machine-independent latency baseline is recorded separately (see Latency).
The OpenID authorize-state store bounds outstanding challenges at a global cap
(OidcStateStore.DefaultMaxEntries = 100,000) and reclaims expired entries with
a sweep throttled to at most once per minute - design in
Security Model § In-flight login capacity.
The behavior under a challenge flood is a regression test, not a construction
claim (SSO-Auth.Tests/Oidc/OidcStateStoreTests):
-
At the cap a fresh challenge is refused, never an in-flight state evicted -
TryAdd_AtCap_RefusesANewKeyAndKeepsTheInFlightState,TryAdd_DistinctKeys_FillToGlobalCap_ThenRefuseNotEvict. The refusal surfaces to the user as the cleanCould not start login; please retryresponse (a 500), never an out-of-memory. -
Per-client isolation - a flood from one client key cannot refuse a
different key's challenge (
TryAdd_FloodFromOneKey_DoesNotRefuseADifferentKey); each client is additionally held to a 1% share of the cap (up to 1,000 concurrent in-flight logins per client). -
The capacity warning is throttled to once per interval, so a flood cannot
amplify into unbounded logging
(
TryAdd_Refused_SignalsTheCapacityWarningOncePerInterval). -
Under concurrency the store stays bounded and the sweep never corrupts it -
TryAdd_UnderContention_StaysBoundedAndRejectsSome,PruneExpired_ConcurrentWithAdds_DoesNotThrowAndKeepsFreshEntries. -
The sweep runs at most once per minute, keeping its O(n) scan off the
anonymous hot path
(
PruneExpired_WithinTheInterval_IsThrottled_AndTheUnsweptEntryIsStillRejected).
The edge rate limiter throttles a hammering client with a 429 + Retry-After
while a normal multi-stage login (including an MFA retry) never trips it - design
in Security Model § Rate limiting. Pinned
by the SsoRateLimiter and PerClientBudgetLimiter test suites, with the
429/Retry-After emission covered through the login status mapper.
The optional avatar fetch is bounded by a deadline (and the SSRF allow-list) so a
slow or hostile IdP-hosted image cannot stall the login; the AvatarService
tests pin the deadline and abort behavior.
A machine-independent latency baseline (challenge + callback round-trip, nominal and concurrent) is a characterization, not a CI gate. It must be captured on a controlled runner (CI, or a representative server) rather than a developer laptop so the numbers are comparable release-over-release. The behavioral guarantees above hold regardless of the absolute latency.
Captured by the SSO-Auth.Bench harness on a GitHub-hosted runner through the
Performance baseline (login latency) workflow, which archives the raw output
as the login-latency-baseline artifact of its run.
| scenario | stage | n | p50 ms | p95 ms | p99 ms | max ms |
|---|---|---|---|---|---|---|
| nominal | challenge | 2000 | 0.469 | 0.776 | 0.839 | 21.532 |
| nominal | callback | 2000 | 0.369 | 0.584 | 0.630 | 27.548 |
| concurrent | challenge | 2000 | 0.264 | 0.329 | 0.493 | 27.132 |
| concurrent | callback | 2000 | 0.138 | 0.204 | 0.286 | 18.677 |
Concurrent throughput: 2104.4 round-trips/s, 2000 round-trips over 0.950 s of wall clock, 16 concurrent callers.
Provenance, which is what makes the next release's numbers comparable to these:
- runner label
ubuntu-24.04, runner imageubuntu24 20260720.247.2, 4 processors - .NET SDK 10.0.302; the harness reported runtime 9.0.18 X64
- plugin commit
43488c88ce6d85d0943c77af09c543d1e6c88c2c - parameters
--iterations 2000 --warmup 200 --concurrency 16 - run 31369550693, scheduled trigger
Read them for what the harness measures and no further. The identity provider is served in process, Jellyfin's services are the mocks the unit tests use, and the measured legs stop at the callback, so a real deployment adds the IdP round-trip and session minting on top of these figures.
The max column is a property of a shared runner more than of the plugin: it
sits between 25 and 66 times p99 across the four rows, which is what a noisy neighbour plus
a JIT or GC pause looks like. Compare p50 and p95 release over release, and treat
a moved max as a reason to re-run rather than as a regression. Nothing here
gates: the workflow records, it does not fail on a slower number, and it is not a
required check.
- Cap / sweep / in-flight under concurrency (acceptance criterion 2) → the
OidcStateStoreregression tests above. - Rate-limit throttle vs. a normal login (criterion 3) → the rate-limiter test suites above.
- Baseline latency numbers (criterion 1) → recorded from a controlled run (Latency).
Repository · Issues · Releases · Security policy - report vulnerabilities privately, never in a public issue. Pages describe what is implemented today; if the wiki disagrees with the code, the code wins.
Getting started
- Installation
- Provider Setup
- Hardening & Options Reference
- Migrating from 9p4
- Troubleshooting
- Rollback
How it works
Security
- Security Model
- Security Conformance (ASVS / RFC 9700)
- SSO-Only Login - design record
- Single Logout - design record
Standards & process (internal / maintainer)