Bounded Workflow Progress Preparation: WSL2 Linux Evidence¶
This report records the required preparation-only evidence matrix for ADR-0005 and issue #140. The authoritative measurements are in the adjacent JSON artifact.
- Collected: 2026-07-21T13:24:05Z
- Source revision:
c42fb22634712e52d8aee74c86a62fea459da5e8 - Implementation digest:
2d32b2b2175706aaecd1a3348134f0e27ab61f4f489a8820c57c2fc3d8de3ce9 - JSON SHA-256:
8ea7b9e22182e9bbaf9962eedaea9dc4872893a34538f55adcbe9da23d3edf73 - JSON size: 27,200 bytes
- Result: all six required cases and the forced-termination cleanup control passed
Scope¶
The benchmark exercises the non-production SQLite spill prototype directly. Every case runs in a fresh child process, consumes topology and initial-detail generators once, prepares canonical V1 output, and removes its private workspace before the parent accepts the result.
The run did not start Ray, access a Django database, contact Kubernetes, change a runtime writer, or activate schema v3. It is evidence for the preparation contract, not for end-to-end workflow latency or production admission capacity.
Environment¶
| Field | Value |
|---|---|
| Platform | Linux-6.6.87.2-microsoft-standard-WSL2-x86_64-with-glibc2.39 |
| Python | CPython 3.12.3 |
| Django | 6.0.7 |
| SQLite | 3.45.1 |
| Workspace filesystem | ext4, 4 KiB allocation blocks |
| Filesystem identity | c6829391fef5d4dfb0f645124791a34b509c6c559bfb7029adaabd89e8684ab6 |
| RSS method | resource.getrusage(RUSAGE_SELF).ru_maxrss |
| RSS scope | Fresh child-process lifetime high-water; 10 ms sampler also recorded |
The checkout was created by Windows Git. The outer WSL command supplied
core.autocrlf=input and core.filemode=false through invocation-scoped GIT_CONFIG_*
variables so WSL evaluated the same index and working-tree content as Windows Git. This
did not rewrite the checkout or change repository configuration. Both source snapshots
recorded dirty=false, the same revision, and the same implementation digest.
Selected resource profile¶
| Setting | Effective value |
|---|---|
| SQLite page size | 4,096 bytes |
| SQLite cache | 8 MiB (cache_size=-8192) |
| Memory mapping | disabled (mmap_size=0) |
| Temporary storage | memory (temp_store=2) |
| Maximum database pages | 261,120 |
| Total workspace ceiling | 1 GiB, including a 4 MiB control reserve |
| Transaction batch | 256 items or 4 MiB decoded item bytes |
| Observed node / edge / detail ceilings | 1,000,000 / 4,000,000 / 1,000,000 |
| Journal / synchronous | off / 0 |
| Locking / foreign keys | exclusive / enabled |
| Trusted schema | disabled |
All cases reported the same effective settings and V1 output limits. The retained query plans were five direct scans or primary-key searches; none used a temporary B-tree or materialization.
Results¶
Memory and spill values use MiB. RSS delta subtracts the current RSS captured at the start of each fresh child process; the JSON also retains the process high-water baseline and sampler output.
| Observed nodes | Profile | Observed edges | Retained nodes / edges / detail | Wall s | CPU s | RSS peak | RSS delta | Python peak | Spill peak | Pages | Truncation reasons |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 25,000 | sparse | 24,999 | 25,000 / 24,999 / 25,000 | 67.39 | 71.46 | 202.0 | 154.3 | 64.8 | 14.5 | 196 | none |
| 25,000 | high-edge | 200,000 | 25,000 / 100,000 / 25,000 | 127.82 | 135.42 | 300.9 | 253.4 | 101.2 | 21.0 | 489 | edge_count_limit |
| 100,000 | sparse | 99,999 | 25,000 / 24,999 / 25,000 | 89.19 | 94.96 | 204.6 | 156.6 | 64.8 | 33.5 | 196 | node_count_limit |
| 100,000 | high-edge | 800,000 | 25,000 / 100,000 / 25,000 | 206.03 | 219.03 | 301.3 | 253.2 | 101.2 | 59.5 | 489 | edge_count_limit, node_count_limit |
| 250,000 | sparse | 249,999 | 25,000 / 24,999 / 25,000 | 153.15 | 163.46 | 204.1 | 156.6 | 64.8 | 71.4 | 196 | node_count_limit |
| 250,000 | high-edge | 2,000,000 | 25,000 / 100,000 / 25,000 | 355.16 | 378.49 | 301.0 | 252.9 | 101.2 | 136.6 | 489 | edge_count_limit, node_count_limit |
Interpretation¶
Once retained output reached the V1 caps, memory stayed effectively flat while observed input grew:
- sparse RSS delta was 154.3, 156.6, and 156.6 MiB, while tracemalloc peak stayed at 64.8 MiB;
- high-edge RSS delta was 253.4, 253.2, and 252.9 MiB, while tracemalloc peak stayed at 101.2 MiB; and
- retained output stayed at 25,000 nodes and detail rows, with either 24,999 sparse edges or the 100,000-edge V1 cap.
The external spill grew with observed input, from 14.5 to 71.4 MiB for sparse cases and from 21.0 to 136.6 MiB for high-edge cases. That is the intended trade: exact duplicate and reference state scales on bounded local disk rather than as Python identity collections. Wall time also grew with observed input, so these measurements establish a memory contract and decision baseline, not a latency SLO.
Source and cleanup controls¶
- Source snapshots before and after the matrix were identical and clean at revision
c42fb22634712e52d8aee74c86a62fea459da5e8. - Every normal case reported both worker-context and parent-watchdog cleanup as
removed; no scenario root remained. - The forced-termination child published its parent-owned readiness nonce while the
SQLite workspace was open, was killed with return code
-9, and produced no durable candidate. - The parent removed the forced child's workspace and scenario root, and the configured benchmark parent was empty in an immediate post-run filesystem check.
Decision and limitations¶
Together with the 76 focused canonical, failure, cancellation, and cleanup tests that passed on both Windows and Linux/WSL2 at the measured revision, this matrix satisfies ADR-0005's issue-#140 evidence gate for accepting the exact spill-backed preparation contract. It does not deliver or authorize production writer activation. Issue #141 still owns topology integration, issue #142 owns composite topology/detail lifetime and final production memory proof, and issue #79 owns live wire, mailbox, producer, concurrency, and node-local spill admission bounds.
This is one run on one WSL2 host and filesystem, without repetitions or concurrent preparers. Process RSS is a child-lifetime high-water measurement and includes Python startup; the recorded baseline makes that scope explicit. Production integration must repeat fault and capacity evidence for its configured spill root, janitor, aggregate quota, and concurrency policy.
Reproduction¶
The native-Linux form is documented in Performance. This WSL run
used the following checkout-local command; the GIT_CONFIG_* values are only needed
when a Windows-created checkout otherwise appears dirty to WSL Git.
cd /mnt/d/Repos/django-ray
mkdir -p /tmp/django-ray-issue140-benchmark
GIT_CONFIG_COUNT=2 \
GIT_CONFIG_KEY_0=core.autocrlf \
GIT_CONFIG_VALUE_0=input \
GIT_CONFIG_KEY_1=core.filemode \
GIT_CONFIG_VALUE_1=false \
PYTHONPATH=/mnt/d/Repos/django-ray/src:/mnt/d/Repos/django-ray \
/tmp/django-ray-issue140-venv/bin/python \
scripts/benchmark_workflow_progress_preparation.py \
--required-scale \
--timeout-seconds 7200 \
--workspace-parent /tmp/django-ray-issue140-benchmark \
--output \
docs/benchmarks/workflow-progress-preparation-sqlite-wsl2-linux-2026-07-21.json