mt-tenants campaign tooling: awsbench product-surface MT mode + feeds… #354
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
| name: ci | |
| on: | |
| push: | |
| branches: [main, slate] | |
| pull_request: | |
| schedule: | |
| # SR2-5: the noisy-neighbor campaign runs NIGHTLY (03:17 UTC), not | |
| # as a one-time recorded receipt. Locked thresholds live in the | |
| # script; a regression fails this workflow. | |
| - cron: "17 3 * * *" | |
| # Product-surface spec appendix §7: the required CI topology. The | |
| # dual-surface equivalence corpus, DST scenarios, and product | |
| # conformance run inside `cargo test --release` (dst::dst_tests); | |
| # request-cost budgets are asserted by the postings/planner tests in | |
| # the same suite. The upstream Durable Streams server suite runs as | |
| # its own job against a fresh namespace. | |
| jobs: | |
| # Job: rust build + full suite (product conformance, dual-surface | |
| # equivalence corpus, DST safety/liveness, request-cost budgets). | |
| rust: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Check | |
| run: cargo check --all-targets | |
| # SR-6: the two multitenancy gates run as NAMED steps so a | |
| # failure is legible at a glance. The audit is the fingerprint | |
| # inventory (identity-neutral categories, baseline-diffed); the | |
| # syn lint is the zero-unmarked-residual security gate and also | |
| # runs inside the full suite — here it prints the reviewed | |
| # exemption-marker inventory into the CI log for certification. | |
| - name: Multitenancy audit (bare, never piped) | |
| run: bash scripts/multitenancy-audit.sh | |
| - name: Multitenancy identity lint (exemption inventory) | |
| run: cargo test --release --bin streams-slate multitenancy_identity_lint -- --nocapture | |
| - name: Test (product conformance + dual-surface + DST + cost budgets) | |
| run: | | |
| cargo test --release 2>&1 | tee /tmp/suite.log | |
| grep -E "^test result" /tmp/suite.log | |
| if grep -E "^test result" /tmp/suite.log | grep -qv " 0 failed"; then | |
| echo "suite has failures"; exit 1 | |
| fi | |
| # Job: the REAL enforce-mode noisy-neighbor campaign (SR2-5), | |
| # nightly. Reduced herd for the 4-core runner (24 noisy, 100 idle) | |
| # with the SAME locked thresholds; the contract-scale 48/200 run is | |
| # part of RC promotion (scripts/promote-rc.sh). | |
| noisy-campaign: | |
| if: github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| - name: Noisy-neighbor campaign (locked thresholds) | |
| run: NOISY=24 IDLE=100 WINDOW_SECS=20 node scripts/mt-noisy-campaign.mjs | |
| # Job: the platform end-to-end battery (docs/CONTROL-PLANE- | |
| # INTEGRATION.md §15) — the TS emulator mints real credentials, | |
| # tokens and feed files; a REAL streams-slate consumes them under | |
| # the full release posture (workload identity, no static token); | |
| # the SDK exchanges through tokenProvider. Phase-A scenarios. | |
| platform-e2e: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| - name: Build SDK dist | |
| run: cd sdk && npm ci && npm run build | |
| - name: Platform end-to-end battery | |
| run: node scripts/platform-e2e.mjs | |
| # Job: the shared-cell certification smoke at CONTRACT scale | |
| # (docs/MULTITENANCY.md §18 Stage 8 / SR-6). The suite's default run | |
| # exercises the same test at 128 projects; this job runs the | |
| # 1,000-project posture (48 noisy, shared workspaces, same-name | |
| # streams, suspension/revocation cutoffs, reconciliation, journaled | |
| # denials) on every push so the at-scale posture cannot rot. | |
| mt-cert-1000: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Certification smoke at 1,000 projects | |
| run: MT_CERT_PROJECTS=1000 cargo test --release --bin streams-slate shared_cell_certification_smoke -- --nocapture | |
| # Job: the SDK ships as an npm package, so CI validates the PACKAGE, | |
| # not the source tree: build, pack, install the tarball into a clean | |
| # project, and run the end-to-end smoke through the package specifier | |
| # on the oldest supported runtime (engines: >=18) and on current Node. | |
| sdk-package: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| - name: Typecheck + build + pack | |
| run: | | |
| cd sdk | |
| npm ci | |
| npx tsc --noEmit -p tsconfig.json | |
| npm run build | |
| # npm >=10 does not create the destination directory; without | |
| # it, pack dies with ENOENT on the tarball path. | |
| mkdir -p /tmp/pkg | |
| npm pack --pack-destination /tmp/pkg | |
| - name: Install the tarball into a clean project | |
| run: | | |
| mkdir -p /tmp/consumer && cd /tmp/consumer | |
| npm init -y >/dev/null | |
| npm install /tmp/pkg/prisma-streams-*.tgz | |
| node -e "require('node:assert').ok(require('node:fs').existsSync('node_modules/@prisma/streams/dist/index.js'))" | |
| - name: Build server | |
| run: cargo build --release --bin s3lite --bin streams-slate | |
| - name: Smoke through the package (current Node) | |
| run: | | |
| ./target/release/s3lite --listen 127.0.0.1:9500 --latency-ms 2 & | |
| sleep 1 | |
| ./target/release/streams-slate --listen 127.0.0.1:8090 \ | |
| --s3-endpoint http://127.0.0.1:9500 --bucket sdk22-$RANDOM \ | |
| --max-unflushed-bytes 67108864 & | |
| sleep 2 | |
| cd /tmp/consumer | |
| STREAMS_SDK=@prisma/streams node $GITHUB_WORKSPACE/sdk/smoke.mjs http://127.0.0.1:8090 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 18 | |
| - name: Smoke through the package (Node 18, the engines floor) | |
| run: | | |
| ./target/release/streams-slate --listen 127.0.0.1:8091 \ | |
| --s3-endpoint http://127.0.0.1:9500 --bucket sdk18-$RANDOM \ | |
| --max-unflushed-bytes 67108864 & | |
| sleep 2 | |
| cd /tmp/consumer | |
| STREAMS_SDK=@prisma/streams node $GITHUB_WORKSPACE/sdk/smoke.mjs http://127.0.0.1:8091 | |
| # The SDK claims Bun and Deno too, so gate them rather than assert | |
| # them. Both run the same package through the same smoke. | |
| - uses: oven-sh/setup-bun@v2 | |
| - name: Smoke through the package (Bun) | |
| run: | | |
| ./target/release/streams-slate --listen 127.0.0.1:8092 \ | |
| --s3-endpoint http://127.0.0.1:9500 --bucket sdkbun-$RANDOM \ | |
| --max-unflushed-bytes 67108864 & | |
| sleep 2 | |
| cd /tmp/consumer | |
| STREAMS_SDK=@prisma/streams bun $GITHUB_WORKSPACE/sdk/smoke.mjs http://127.0.0.1:8092 | |
| - uses: denoland/setup-deno@v2 | |
| with: | |
| deno-version: v2.x | |
| - name: Smoke through the package (Deno) | |
| run: | | |
| ./target/release/streams-slate --listen 127.0.0.1:8093 \ | |
| --s3-endpoint http://127.0.0.1:9500 --bucket sdkdeno-$RANDOM \ | |
| --max-unflushed-bytes 67108864 & | |
| sleep 2 | |
| cd /tmp/consumer | |
| STREAMS_SDK=@prisma/streams deno run -A --node-modules-dir \ | |
| $GITHUB_WORKSPACE/sdk/smoke.mjs http://127.0.0.1:8093 | |
| # Job: the pinned upstream Durable Streams server conformance suite, | |
| # UNMODIFIED, against a fresh namespace (spec appendix §7). The | |
| # version here must match src/protocol_pin.rs. | |
| durable-streams-server-conformance: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| - name: Build server | |
| run: cargo build --release --bin s3lite --bin streams-slate --bin streams-keys | |
| - name: Run pinned suite (fresh namespace) | |
| run: | | |
| ./target/release/s3lite --listen 127.0.0.1:9500 --latency-ms 2 & | |
| sleep 1 | |
| KEY=$(./target/release/streams-keys generate) | |
| # Group commit is required: the suite's property tests run | |
| # ~240 sequential appends inside vitest's 5 s budget, and the | |
| # default flush cadence makes them latency-marginal. This is | |
| # what field deployments run anyway (see CONFORMANCE.md). | |
| ./target/release/streams-slate --listen 127.0.0.1:8090 \ | |
| --s3-endpoint http://127.0.0.1:9500 --bucket conf-$RANDOM \ | |
| --max-unflushed-bytes 67108864 \ | |
| --flush-interval-ms 1 --wal-flush-gap-ms 2 \ | |
| --conformance-default-key "$KEY" & | |
| sleep 2 | |
| # The runner lives in conformance/ with a committed lock and an | |
| # exact expected outcome. It does NOT use the package's npx | |
| # CLI, whose include glob misses its own runner and exits 0 | |
| # having run nothing. | |
| cd conformance | |
| npm ci | |
| CONFORMANCE_TEST_URL=http://127.0.0.1:8090 npm test | |
| # Job: the product field corpus (scripts/field-gate.mjs) — the same | |
| # 20 checks the cloud campaign runs, against a local server with | |
| # aggressive split thresholds so a real scaler split fires inside CI. | |
| # Negative auth, raw create replay, product seal replay, fork replay, | |
| # a real automatic split, and raw/product default-key isolation. | |
| product-field-gate: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| - name: Build server | |
| run: cargo build --release --bin s3lite --bin streams-slate | |
| - name: Run the field gate (auth enabled, deterministic split) | |
| run: | | |
| ./target/release/s3lite --listen 127.0.0.1:9500 --latency-ms 2 & | |
| sleep 1 | |
| # Low, deterministic scaler thresholds: one hot evaluation | |
| # over a 10 s window splits, so the "load drove a real split" | |
| # check fires within the gate's own load phase instead of | |
| # racing production cadence. | |
| SCALE_EVAL_SECS=5 SCALE_RATE_WINDOW_SECS=10 \ | |
| SCALE_HOT_PCT=1 SCALE_HOT_EVALS=1 SCALE_COOLDOWN_SECS=5 \ | |
| ./target/release/streams-slate --listen 127.0.0.1:8092 \ | |
| --s3-endpoint http://127.0.0.1:9500 --bucket fg-$RANDOM \ | |
| --max-unflushed-bytes 67108864 \ | |
| --flush-interval-ms 1 --wal-flush-gap-ms 2 \ | |
| --auth-token ci-gate-token & | |
| sleep 2 | |
| STREAMS_TOKEN=ci-gate-token node scripts/field-gate.mjs http://127.0.0.1:8092 |