test infra: backend suite speedup (Phase A1) — Python 3.12 + sysmon coverage - #1767
Conversation
…orksteal
Two changes that together produced a 4x speedup on the hot test files in
local A/B measurement (19:34 -> 4:50 on the WebSocket / extract /
structured-response cluster; see plan §6). Targets the two highest-ROI
findings from a CI runtime audit of the ~47-min backend job:
A1. Bump test/runtime Python image from 3.11.15 to 3.12.7 and set
COVERAGE_CORE=sysmon in the test env. Coverage on 3.11 was measured at
+156% over the no-cov baseline on the hot files; sys.monitoring is
~5-10x cheaper. Codecov receives the same coverage.xml — only the
collection path changes. Dropped django_coverage_plugin from setup.cfg
because Coverage 7.x silently falls back to the C-trace path whenever
any file-tracer plugin is configured, which would defeat sysmon (the
project owns 7 templates total, all 4xx/5xx pages plus a couple of
admin views — a trivial coverage slice for a ~150% wall-time tax).
B1. Switch the pytest CI invocation from --dist loadscope to
--dist worksteal so large classes like TestStructuredResponseAPI
(843 s / 27 tests) can fan out across all workers instead of pinning
to one. Conftest gains an auto-tag that pins django.test.TestCase
subclasses with class-scoped state (setUpTestData overridden or
fixtures set) to a per-class xdist_group, so worksteal still respects
that binding where it matters. TransactionTestCase subclasses are
deliberately not pinned — they rebuild per-test regardless, so they
benefit from free redistribution.
Plan also covers Phase A2 (drop redundant migrate step), A3 (ghcr image
cache), A4 (explicit -n), B2 (slim 17 MB fixture), B3 (class-once fixture
load), B4 (TransactionTestCase audit). Tracked in
docs/refactor_plans/2026-05-23-test-suite-speedup-A1-B1.md so we can
re-measure after this lands and decide which further phases are needed.
Code ReviewOverviewThis PR ships two independent CI speedup levers — A1 (Python 3.12 + sysmon coverage) and B1 (worksteal + auto-tag class-bound state) — and is well-motivated by empirical benchmark data. The changes are minimal, clearly scoped, and the plan doc is a strong artifact. A few items worth addressing before merge. Bugs / Correctness
The auto-tag logic in Under
Style / ConventionsCLAUDE.md still documents
After this PR lands, developers following CLAUDE.md will use Plan doc credits Claude Code (violates project rule) Line 3 of CLAUDE.md explicitly says: "Never credit Claude or Claude Code in commit messages, PR messages, comments, or any other artifacts." This includes docs checked into the repo. Scope ConcernProduction Dockerfile is changed in a "test infra" PR Both Positive Highlights
SummaryApprove after: (1) removing the Claude Code credit from the plan doc, and (2) updating CLAUDE.md's local-run examples from |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
The combined PR #1767 ran in ~31:53 (vs 41-min baseline, −22%) but failed with 39 tests broken — psycopg2 UniqueViolation on the `admin` user across plain TestCase subclasses (UserTypePrivacyTestCase, TestSearchAgentsForMention, TestOpenContractsAnalyzers, etc). Root cause: worksteal interleaved tests from these classes with tests from other classes on the same worker, exposing state leaks that loadscope's class-pinning was hiding. The auto-tag only pinned TestCase subclasses with setUpTestData/fixtures, leaving plain TestCase classes vulnerable to interleave-on-worker pollution from sibling classes' uncommitted state. This commit: - Reverts .github/workflows/backend.yml to `--dist loadscope`. - Reverts conftest.py::pytest_collection_modifyitems to its prior shape (serial marker handling only). - Keeps the Phase A1 changes (Python 3.12.7 image, COVERAGE_CORE=sysmon, dropped django_coverage_plugin) — they ran cleanly and account for the ~22% wall-time reduction independently. - Updates the plan doc (§10 post-mortem) with what we learned: the local A/B 4× speedup was specific to the 3 hottest files (~30% of CI time), not a sound extrapolation to the full suite. Coverage cost was a multiplier on the per-test 17 MB fixture-reload base cost, not the base itself. After A1, that fixture reload is the binding constraint and Phase B3 is the right next lever. Worksteal can be re-attempted only after a dedicated test-isolation sweep that finds and fixes the admin-user collision pattern (probably in factories + Celery-eager + migration seeds).
Code Review — PR #1767: Backend Suite Speedup (Phase A1)OverviewThis PR upgrades the Django image from Python 3.11.15 → 3.12.7, enables ✅ What's done well
🔍 Issues / Suggestions1. Misleading comment in +# Kept in sync with compose/local/django/Dockerfile. 3.12 is required for the
+# ``sys.monitoring``-backed coverage path used in CI; see that file's header.
ARG PYTHON_VERSION=3.12.7-slim-bookworm
2. If someone builds the Django image locally against an older base (e.g. by hand from a different tag) and runs The existing comment mentions the requirement, so this is very minor. 3. Plan document version discrepancy (cosmetic) §4 (Phase A design) says to bump to 4. No validation of Python 3.12 wheel compatibility in The PR description relies on "CI's linter job has been running on Python 3.12 for weeks" as the wheel-compatibility proof. The linter job likely covers a subset of the full dependency tree (no psycopg2 native compile, no ML libs like numpy/torch/opencv). This is a tracked risk in §7 of the plan doc — just want to confirm: has the test image been rebuilt against 3.12 in CI for this PR, or will that happen on merge? If not yet validated in a real build, noting it explicitly in the PR checklist would add confidence. 5. Template coverage loss is permanent until The 7 Django templates (4xx/5xx + admin views) are low-risk, but dropping their coverage is a one-way door until upstream SecurityNo security implications. Pure CI/infrastructure change. PerformanceThe stated goal is validated with local A/B data. The §6 post-mortem honestly accounts for why the local 4× speedup didn't extrapolate linearly to the full suite (coverage was a multiplier on fixture-load cost, not additive). The projected ~22% reduction on CI is credible. Test CoverageNo product code changed; no test additions/removals required. The CI workflow itself is the test vehicle. VerdictApprove with minor suggestions. The scope is correct (A1 only), the documentation is thorough, and the revert of B1 was the right engineering judgment. The production Dockerfile comment (#1 above) is the only change worth making before merge. Everything else is cosmetic or tracking items. |
Python 3.12's mimetypes module returns ``text/javascript`` for ``.js`` files (per RFC 9239, which deprecates ``application/javascript``). The test was hardcoded to the legacy value and broke when CI bumped the django image to Python 3.12 in this PR. Both forms are RFC-valid and both browsers serve them identically — relax the assertion so the test passes on either runtime.
Signed-off-by: JSIV <5049984+JSv4@users.noreply.github.com>
Code Review — PR #1767: Backend Test Suite Speedup (Phase A1)OverviewThis PR upgrades the Django container from Python 3.11.15 to 3.12.7, enables The approach is conservative and well-evidenced. The empirical data (156% coverage overhead on Python 3.11, measured 22% CI reduction) makes the rationale solid. Below are specific observations. What's working well
Issues and Suggestions1. Production Dockerfile comment is misleading (minor)
The version bump itself is correct — keeping local/production/test images on the same major version avoids subtle divergence bugs. The justification just needs to be production-appropriate. 2.
|
Summary
Phase A1 only — Python 3.12 +
COVERAGE_CORE=sysmon+ droppeddjango_coverage_plugin. Phase B1 (--dist worksteal+ auto-tag) was trialled in the first commit and reverted (commit399d555a7); see the plan doc §10 post-mortem.What landed (A1)
compose/{local,production}/django/Dockerfile:python:3.11.15-slim-bookworm→python:3.12.7-slim-bookworm..envs/.test/.django:COVERAGE_CORE=sysmon— switches coverage.py tosys.monitoring-backed instrumentation (SysMonitortracer class), ~5-10× cheaper than the legacy C trace function used on 3.11.setup.cfg: droppeddjango_coverage_plugin. Coverage 7.x silently falls back to the legacy C trace path whenever any file-tracer plugin is configured, which would defeat sysmon. The project owns 7 Django templates total (4xx/5xx + a couple of admin views) — a trivial coverage slice for a ~150% wall-time tax.coverage.xmlcontent, only the collection path changes.What was reverted (B1)
--dist workstealand thexdist_groupauto-tag inconftest.py. They exposed 39 pre-existing test-isolation bugs in plainTestCasesubclasses that loadscope's class-pinning had been hiding (mostlyUniqueViolationon theadminuser from migration-seeded users colliding withUserFactory).Measured CI delta
First commit (A1 + B1, with failures): 41 min → 31:53 (−22%). The failures themselves didn't dominate wall-time; the modest gain reflects that coverage cost was a multiplier on the per-test 17 MB fixture-reload base cost (not the base itself). After A1 lands cleanly, expect a similar ~22% reduction without failures.
Next phase (separately tracked)
A follow-up issue will cover Phase B3 (class-once fixture load for
WebsocketFixtureBaseTestCase+ per-test TRUNCATE-restore) and the test-isolation sweep needed before worksteal can be re-attempted. See plan doc §10 for the detailed analysis.Test plan
coverage.xmland project/patch numbers are within usual noise.