feat: add load-testing/stress-testing suite#370
Merged
Conversation
Add eventcore-stress, a binary crate for sustained concurrent load testing against all EventStore backends. Unlike the existing Criterion micro-benchmarks (eventcore-bench) which measure single-operation latency, this suite measures system behavior under concurrent load: throughput, tail latencies (p50/p95/p99), retry rates, and correctness. Five scenarios exercise different contention patterns: - contention: N tasks deposit to one shared stream (version conflicts) - throughput: per-task streams measuring raw store throughput - transfers: multi-stream atomic transfers with conservation checks - projection: batch-mode projection catch-up after concurrent writes - pool-saturation: postgres-only pool exhaustion under high concurrency The binary is never discovered by cargo nextest/test (no #[test] fns), compiles cleanly under cargo clippy --workspace, and is excluded from mutation testing. Run via: cargo run -p eventcore-stress -- <scenario> Also bumps rustls-webpki 0.103.10 -> 0.103.12 to resolve RUSTSEC-2026-0098 and RUSTSEC-2026-0099.
Bump rand to 0.10 across eventcore, eventcore-testing, and eventcore-stress. The Rng trait methods (random_range, random_bool) moved to RngExt in 0.10, so imports are updated accordingly. Bump mutants dev-dependency to 0.0.4 in eventcore-postgres (used only for #[mutants::skip] attributes). rusqlite remains at 0.32: upgrading to 0.39 is blocked by a libsqlite3-sys links conflict between rusqlite and sqlx-sqlite, which both require different major versions of the sys crate.
The projection stress test scenario reported Correctness: FAIL on PostgreSQL because the database contained stale events from contract tests and previous runs. The first batch of events (LIMIT 1000) was entirely non-stress-test events that failed deserialization silently. Add clean_postgres_database() that truncates eventcore_events and eventcore_subscription_versions before each stress test run when using the postgres backend. TRUNCATE is used instead of DELETE to bypass the row-level delete-prevention trigger. Closes #371
The projection scenario needs a clean database for its correctness check (expects exactly N streams). Previous scenarios in run-all (contention, throughput, transfers) fill the table with unrelated BankAccountEvents. Batch-mode projection reads only the first 1000 events which were all from the contention scenario, finding 1 stream instead of 50. Add a truncation step before the projection scenario within run-all so it starts from a clean slate, matching the behavior of in-memory and SQLite backends which create fresh stores per scenario.
jwilger
approved these changes
Apr 15, 2026
Merged
jwilger-ai-bot
pushed a commit
that referenced
this pull request
Apr 17, 2026
## 🤖 New release * `eventcore-macros`: 0.7.0 -> 0.7.1 * `eventcore-types`: 0.7.0 -> 0.7.1 (✓ API compatible changes) * `eventcore-postgres`: 0.7.0 -> 0.7.1 (✓ API compatible changes) * `eventcore-sqlite`: 0.7.0 -> 0.7.1 (✓ API compatible changes) * `eventcore`: 0.7.0 -> 0.7.1 (✓ API compatible changes) * `eventcore-memory`: 0.7.0 -> 0.7.1 (✓ API compatible changes) * `eventcore-testing`: 0.7.0 -> 0.7.1 (✓ API compatible changes) * `eventcore-examples`: 0.7.0 -> 0.7.1 <details><summary><i><b>Changelog</b></i></summary><p> ## `eventcore-macros` <blockquote> ## [0.7.0](eventcore-macros-v0.6.0...eventcore-macros-v0.7.0) - 2026-04-13 ### Bug Fixes - improve error message consistency, context, and safety across all crates ([#352](#352)) ### Features - enhance require! macro to accept typed error values ([#335](#335)) - add required event_type_name() to Event trait for stable storage ([#344](#344)) ### Miscellaneous Tasks - adopt han plugins, blueprints, and project conventions ([#330](#330)) - consolidate workspace lints and enforce strict lint policy ([#351](#351)) ### Refactoring - expose projection config via free function API, then reduce public surface ([#357](#357)) </blockquote> ## `eventcore-types` <blockquote> ## [0.7.1](eventcore-types-v0.7.0...eventcore-types-v0.7.1) - 2026-04-15 ### Bug Fixes - filter read_events by event_type to prevent projection stalls ([#373](#373)) </blockquote> ## `eventcore-postgres` <blockquote> ## [0.7.1](eventcore-postgres-v0.7.0...eventcore-postgres-v0.7.1) - 2026-04-15 ### Bug Fixes - filter read_events by event_type to prevent projection stalls ([#373](#373)) ### Features - add load-testing/stress-testing suite ([#370](#370)) </blockquote> ## `eventcore-sqlite` <blockquote> ## [0.7.1](eventcore-sqlite-v0.7.0...eventcore-sqlite-v0.7.1) - 2026-04-15 ### Bug Fixes - filter read_events by event_type to prevent projection stalls ([#373](#373)) </blockquote> ## `eventcore` <blockquote> ## [0.7.1](eventcore-v0.7.0...eventcore-v0.7.1) - 2026-04-15 ### Bug Fixes - filter read_events by event_type to prevent projection stalls ([#373](#373)) ### Features - add load-testing/stress-testing suite ([#370](#370)) </blockquote> ## `eventcore-memory` <blockquote> ## [0.7.1](eventcore-memory-v0.7.0...eventcore-memory-v0.7.1) - 2026-04-15 ### Bug Fixes - filter read_events by event_type to prevent projection stalls ([#373](#373)) </blockquote> ## `eventcore-testing` <blockquote> ## [0.7.1](eventcore-testing-v0.7.0...eventcore-testing-v0.7.1) - 2026-04-15 ### Features - add load-testing/stress-testing suite ([#370](#370)) </blockquote> ## `eventcore-examples` <blockquote> ## [0.7.0](eventcore-examples-v0.6.0...eventcore-examples-v0.7.0) - 2026-04-13 ### Bug Fixes - improve error message consistency, context, and safety across all crates ([#352](#352)) ### Features - add required event_type_name() to Event trait for stable storage ([#344](#344)) - add TestScenario GWT testing helpers to eventcore-testing ([#346](#346)) ### Miscellaneous Tasks - consolidate workspace lints and enforce strict lint policy ([#351](#351)) ### Refactoring - replace into_inner() with into() for nutype domain types ([#334](#334)) - expose projection config via free function API, then reduce public surface ([#357](#357)) </blockquote> </p></details> --- This PR was generated with [release-plz](https://github.com/release-plz/release-plz/).
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
eventcore-stressbinary crate with 5 stress test scenarios (contention, throughput, transfers, projection, pool-saturation) measuring sustained concurrent load against all EventStore backendsrand0.9→0.10 andmutants0.0.3→0.0.4; bumprustls-webpkito resolve RUSTSEC-2026-0098/0099#[test]fns), mutation testing, andcargo nextest run --workspaceTest plan
cargo clippy --workspace --all-targets --all-featurespassescargo nextest run --workspace— 193 tests pass, stress tests not discoveredcargo run -p eventcore-stress -- run-all --backend memory— all scenarios PASS correctnesscargo run -p eventcore-stress -- contention --backend sqlite— SQLite backend workscargo audit— no vulnerabilities🤖 Generated with Claude Code