Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ See the full technical specs: [backend](dev-docs/SPEC-backend.md) | [frontend](d
- Python 3.12+ with [uv](https://docs.astral.sh/uv/)
- [just](https://github.com/casey/just) (task runner)
- [Bun](https://bun.sh/) (frontend package manager, only needed for frontend dev)
- [Cargo](https://doc.rust-lang.org/cargo/) with [`inferno`](https://github.com/jonhoo/inferno) installed (`cargo install inferno`) if you want to run benchmarks

## Quick start

Expand Down
15 changes: 15 additions & 0 deletions dev-docs/SPEC-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,21 @@ The workflow triggers on:

This keeps backend CI scoped to builder and shared Python dependency changes.

### Benchmarking

End-to-end build benchmarks live under `builders/server/benchmarks/`. They measure wall-clock time for a full build request through the server (HTTP handler → dependency resolution → subprocess spawns → DB insert) using a testcontainer postgres so as not to pollute the real database.

Two profiling modes:

- **`just bench`** — runs `pytest-benchmark` over a 90-day `mock-ohlc` build, 3 rounds, outputs a timing table (mean/stddev/min/max).
- **`just bench-profile [DAYS]`** — wraps the standalone `benchmarks/bench_build.py` script with `py-spy record --subprocesses`, producing `bench-flamegraph.svg`. The `--subprocesses` flag captures the builder subprocess spawns, which dominate build time for simple datasets.

The benchmark test uses `benchmark.pedantic(rounds=3, warmup_rounds=0)` — explicit rounds because each round is expensive, no warmup because every round must do real work (the DB is truncated between rounds by the `clean_db` fixture).

The standalone `__main__` script does the same testcontainer setup without pytest, so it can be wrapped directly by `py-spy`. Module patching is done via direct attribute assignment rather than `monkeypatch`.

The flame graph should reveal the breakdown between: subprocess spawn overhead (`subprocess.Popen` + interpreter startup), JSON serialization (stdin/stdout IPC), calendar/timestamp generation, and DB operations (`get_existing_timestamps` + `insert_rows`).

## Containers

- The service, Postgres database, and Caddy reverse proxy each run in their own Docker containers.
Expand Down