Repository navigation
test(bench): add enrichment completion harness and provider simulator (M0) - #98
Merged
Merged
Conversation
M0 baseline tooling for the Go/Python backend plan (issue #33): - scripts/bench/provider_simulator.py: deterministic aiohttp provider simulator with seeded fixed/uniform/lognormal latency, 5xx and 429 rates; outcomes are a pure function of (seed, key, attempt). - scripts/bench/enrichment_harness.py: creates N workbook rows with a declarative HTTP enrichment column pointing at the simulator, enqueues run_workbook on the durable queue, runs a real QueueService worker, and reports completions/sec, e2e and per-cell p50/p95/p99, errors, provider attempts/429s, CPU/RSS, plus the identity block (commit, hardware, Python, DB, worker config, workload). SQLite by default, PostgreSQL via BENCH_DATABASE_URL. - scripts/bench/_shim/sitecustomize.py: benchmark-process-only hooks that exempt exactly the loopback simulator origin from the SSRF guard (refuses non-loopback origins), register the generated manifest via the normal compile path, and record per-cell timings. - docs/operations/worker-concurrency.md: worker slot / concurrency settings, defaults, bounds, tuning, and harness usage. - docs/benchmarks/enrichment-baseline.md (+ raw JSON): first developer-laptop baseline, linked from the plan's M0 row/checklist. - tests/test_enrichment_bench_harness.py: simulator determinism, shim loopback-only guard, and a 5-row end-to-end smoke run (~3s). Signed-off-by: debpalash <debash67@outlook.in> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Part of #33. This covers the first M0 step: the harness, the simulator, the settings docs, and a first local baseline. It does not tick the M0 checkbox. That still needs a controlled-environment PostgreSQL baseline, API (k6) numbers, and the first job-type selection.
What's added
scripts/bench/provider_simulator.py: a deterministic local provider. Latency can be fixed, uniform, or lognormal, with a seed, plus configurable 5xx and 429 rates. Each outcome is a pure function of(seed, domain, attempt), so request scheduling does not change it./statsexposes request and status counts.scripts/bench/enrichment_harness.py: creates N workbook rows with a declarative HTTP provider column pointing at the simulator, then enqueuesrun_workbookwith the same payload shape asPOST /run. It runs a realQueueService: claim → killable job child → pebble provider worker → httpx → result commit. It reports JSON and a short summary: completions/sec, end-to-end and per-cell p50/p95/p99, errors, provider attempts/429s, CPU/RSS, and the identity block (commit, hardware, Python, DB, worker config, workload). SQLite is the default. SetBENCH_DATABASE_URLto use a disposable Postgres; the harness claims and deletes only its own tagged rows.check_url, so the guard is not changed. Benchmark processes loadscripts/bench/_shim/sitecustomize.pyviaPYTHONPATH. It exempts exactly the simulator's loopback origin (scheme+host+port) and refuses any non-loopback origin. It also registers the generated manifest through the normal compile/register path and records per-cell timings. Nothing ships in the app package.docs/operations/worker-concurrency.md: every worker slot and concurrency knob, with defaults, ranges, what it bounds, how to tune it, and how to run the harness.docs/benchmarks/enrichment-baseline.mdplus raw JSON, linked from the plan's M0 row and checklist.tests/test_enrichment_bench_harness.py: simulator determinism, the shim's loopback-only rule, and a 5-row end-to-end smoke run (about 3 s).Baseline (developer laptop, regression reference only, not capacity)
Identity: commit
9188fc4(app code) · Apple M2, 8 cores, 16 GB, macOS 26.4.1 arm64 · CPython 3.14.5 · SQLite 3.50.4 temp file / PostgreSQL 18.4 local (superuser, so RLS was bypassed) · Redis reachable · 1 worker process · defaultsWORKER_CONCURRENCY=1,WORKER_MAX_ACTIVE_PER_WORKSPACE=2,row_concurrency=12,provider_workers=8,provider_timeout=10,max_providers=8,retry_passes=1· 1 declarative HTTP column, verify off, cost 0 · simulator seed 42.Findings worth follow-up issues
row_concurrencyabove the DB pool size deadlocks a run. Withrow_concurrency=32on SQLite (the UI allows up to 64), the job child's event loop blocks on a SQLAlchemyQueuePoolcheckout (size 5 + overflow 10). A 300-row run finished 0 cells in 240 s. Postgres has the same failure mode above 30 connections.no_data. Error detail and rate-limit signal are lost.provider_workers<row_concurrencyand by batch-synchronous row execution. Matching them gave +40%.Tests
uv run pytest -q tests/test_enrichment_bench_harness.py tests/test_declarative_providers.py tests/test_declarative_array_projection.py tests/test_queue_claim.py tests/test_queue_concurrency.py tests/test_queue_load.py tests/test_job_process_runner.py tests/test_http_column.py→ 52 passed in 4.3suv run pytest -q→ 2218 passed, 139 skipped in 74s🤖 Generated with Claude Code