Screening read benchmark¶
The opt-in ProjectScreeningReadBenchmark compares the existing broad FullStats calculation
against the production materialized screening adapter on the same deterministic corpus in a
single-node MongoDB replica set. It reuses the Phase 0 dataset generator, percentile sampler and
JSON results format. The ordinary test lanes skip it unless SYRF_STATS_DATASET is set.
The authoritative side is deliberately the broad GetFullProjectStatsAsync facet query, because
that is what ProjectScreeningStatisticsQueryAdapter falls back to on main today. A
screening-only source query is a separate, currently unmerged change, so no variant here measures
one.
Run¶
From the PR worktree, with Docker available:
SYRF_STATS_DATASET=PS-DS-02 SYRF_STATS_ITERATIONS=100 SYRF_STATS_WARMUP=10 \
SYRF_STATS_RESULTS_DIR=/tmp/feat024-screening-read-results \
dotnet test src/libs/project-management/SyRF.ProjectManagement.Mongo.Data.Tests/SyRF.ProjectManagement.Mongo.Data.Tests.csproj \
--configuration Release --filter FullyQualifiedName~ProjectScreeningReadBenchmark
Use PS-DS-01 and ten iterations for a harness smoke run; that is not a performance gate sample.
Run the named datasets from the capacity baseline in
separate processes to compare representative shapes. Record the commit, command and machine/load
conditions alongside each emitted JSON artifact. Do not enable flags in a deployed environment to
run this test: its source data, controls, rebuilds and injected Stale state live only in Testcontainers.
What it proves¶
| Variant | Expected authoritative Study aggregations per read | Result |
|---|---|---|
| Broad FullStats | 1 | Unchanged independent screening oracle |
| Missing projection | 1 | Reader refusal reason Missing, then authoritative fallback |
| Fresh projection | 0 | Real snapshot reader and current permissions; lazy delegate never invoked |
| Serving disabled | 1 | Flag-off authoritative fallback; mandatory source checks still apply |
| Stale projection | 1 | Exact fallback, with no stale materialized response |
Correctness and aggregation counts fail the run when violated: every variant's complete scalar and tally output is compared against the broad-facet oracle, and the Fresh read additionally asserts that the lazy authoritative delegate is never evaluated. Timing does not fail ordinary tests: the artifact records p50/p95 and whether the observed Fresh read p95 improves at least 20% over the broad read. Variants run sequentially; host contention and sample ordering remain possible influences. The artifact includes the corpus fingerprint, server/runtime details and current-row BSON sizes.
The command recorder measures authoritative source commands on the instrumented client only, not every MongoDB round trip. Its zero on a Fresh read means zero authoritative Study aggregates, not zero MongoDB commands: the real materialized reader still loads its controls, authorization, guards, fences and rows inside one snapshot on the uninstrumented context. The latency sample does include that reader work. Rebuild and initial history construction occur outside timed reads.
Activation evidence still required¶
The committed artifact records PS-DS-02 scale 1: 5,000 target studies, ten warmups and 100 measured reads per variant, Release .NET 10, MongoDB 8.0.28, on a shared 48-processor development host without container resource caps. Exact parity and the expected aggregation counts passed. This run measured:
| Variant | p50 (ms) | p95 (ms) |
|---|---|---|
| Broad FullStats | 932.15 | 1077.01 |
| Missing fallback | 912.79 | 969.71 |
| Fresh materialized | 10.14 | 11.97 |
| Serving disabled | 900.63 | 1132.36 |
| Stale fallback | 962.16 | 1516.02 |
The Fresh read is the only variant that avoids the broad aggregation, and its p95 is 98.9% below the broad baseline. Read the other three as one group, not as a ranking: each is the broad query plus a reader refusal, so they scatter around the broad baseline — here from 969.71 to 1516.02 ms p95, some above it and one below — and that spread is uncontrolled shared-host noise around the same aggregation rather than a measured difference between fallback reasons. That is the expected shape: the projection helps only when it is servable, and refusing costs little enough to disappear into host noise.
The corpus fingerprint D0D56518026AAE4659B141C95E6F7D5380FA4084E20A2A618F565BA41F4A56EA is
identical to the Phase 0 PS-DS-02 baselines, so this run measured the same generated corpus.
The recorded aggregation reduction and p95 improvement are both derived from the run's own samples and round-trip counts, not asserted: this run recorded one authoritative aggregate for the broad read and zero for the Fresh read. The current projection was one 2,252-byte BSON row. That excludes history, indexes and source storage, so it is not a capacity acceptance result. Sequential variants and uncontrolled host contention limit the timing comparison; the raw artifact preserves the environment and corpus.
This is development evidence for repository/adapter reads. It does not measure HTTP middleware or
the endpoint's outer authorization, the existing reviewer request, Project Overview polling, or the
legacy SignalR producer's broad aggregation. Those costs must be measured in a controlled endpoint
and page run before claiming an overall workload reduction. In particular the full-stats endpoint
on main is equality-gated — the flag-on path still runs the authoritative aggregation and serves
the stored section only when every value matches — so this read-path improvement is not available
to that endpoint today (follow-up #3311).
The required activation gates remain: at least 20% lower read p95 and 80% fewer authoritative aggregations for the actual consumer workload, less than 10% source-write p95 regression, ½/5/10-reviewer conflict/retry evidence, bounded history/index/storage and rollback proof, followed by separately authorized staging activation and at least seven actual days of soak. A passing correctness run or an observed read improvement here does not close those gates.