Skip to content

feat(perf): benchmark and load-test tooling with real FIO game data - #49

Merged
jplacht merged 2 commits into
mainfrom
perf/tooling
Sep 27, 2026
Merged

jplacht merged 2 commits into
mainfrom
perf/tooling

Conversation

@jplacht

@jplacht jplacht commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a perf suite for the backend and seeds it with real game data from FIO.

perf/run.sh starts a throwaway Postgres + Redis in Docker (in memory, own ports), migrates and seeds it, then:

  1. Benchmarks (manage.py perf_bench): in-process requests to the main endpoints as a power user, recording SQL query count, response size, and cold/warm latency.
  2. Load test (full mode): gunicorn with the production config, hit by Locust.
  3. Report (perf/report.py): report.md plus regressions against the previous run of the same scale, mode and game data source.

Game data comes from a FIO snapshot (manage.py perf_snapshot, gitignored at perf/snapshot/), downloaded once with sequential requests and validated with the FIO schemas. seed_perf --source auto|snapshot|fake loads it through the FIO importers. The fake users, plans and empires use real planet ids, building tickers and recipe ids. After seeding, the analytics aggregations run, so the planet insight and material insight endpoints return real data. --no-snapshot forces fake game data. Docs are in perf/README.md.

Review focus: refactor(gamedata) commit

This commit touches production code. Each import_all_* in gamedata/fio/importers.py is split into a fetch plus a save_* function, and the CXPC task now builds its rows with cxpc_objects. Behaviour should be unchanged. Please review this commit on its own; the rest is perf-only code under core/management/commands/ and perf/.

First results (small scale, real data, local machine)

No N+1 queries: every endpoint runs 1 to 4 queries. One finding to follow up separately, with no change in this PR: GET /data/planets/ takes 2.3 s uncached / 36 ms cached for 6.0 MB with the 4,576 real planets. It still runs 4 queries, so the time is Python-side. The likely cause is serializing 205,920 production-fee rows (45 per planet); it hasn't been profiled yet.

Checks

  • uv run ruff check, uv run ruff format --check: pass
  • uv run ty check --exclude "**/migrations/*.py": pass
  • uv run pytest: 317 passed. 24 of them are new perf tests; none hit the network (pytest-httpx and a small fixture snapshot)
  • No migrations, no typing.Any
  • perf/run.sh --mode quick ran end to end locally with the FIO snapshot; --mode full last ran before the snapshot changes (fake data, 0 failures)

No frontend change and no API change.

🤖 Generated with Claude Code

jplacht and others added 2 commits September 27, 2026 13:45
Each import_all_* now fetches from FIO and hands the validated data to a
save_* function (materials, buildings, recipes, planets, exchanges), and
the CXPC task builds its rows with cxpc_objects. Behaviour is unchanged;
the split lets the perf seed load a saved FIO snapshot through the same
field mapping.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
perf/run.sh seeds a throwaway Postgres + Redis in Docker, benchmarks the
main endpoints in-process (query counts, cold and warm latency, size),
optionally load-tests gunicorn with Locust, and reports regressions
against the previous run of the same scale, mode and game data source.

- perf_snapshot downloads the public FIO game data once (sequential,
  validated with the FIO schemas) into perf/snapshot/gamedata.json.gz;
  --cxpc-sample adds real price history for a few tickers.
- seed_perf --source auto|snapshot|fake loads the snapshot through the
  FIO importers; fake users, plans and empires use real planets,
  buildings and recipe ids, and the analytics aggregations run after
  seeding so the analytics endpoints serve real data.
- run.sh downloads the snapshot when missing (--no-snapshot for fake
  data) and records the source; report.py only compares like with like.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@codacy-production

codacy-production Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 4 high · 10 medium

Alerts:
⚠ 14 issues (≤ 0 issues of at least minor severity)

Results:
14 new issues

Category Results
Security 10 medium
4 high

View in Codacy

🟢 Metrics 284 complexity · 0 duplication

Metric Results
Complexity 284
Duplication 0

View in Codacy

🟢 Coverage 97.13% diff coverage · +3.38% coverage variation

Metric Results
Coverage variation ✅ +3.38% coverage variation (-1.00%)
Diff coverage ✅ 97.13% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (a0333a9) 3322 3008 90.55%
Head commit (3dbd1b2) 3836 (+514) 3603 (+595) 93.93% (+3.38%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#49) 522 507 97.13%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@jplacht
jplacht merged commit 962f334 into main Sep 27, 2026
5 of 6 checks passed
@jplacht
jplacht deleted the perf/tooling branch September 27, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant