Skip to content

Decide a replay is too short before registering its match - #132

Open
bill-rich wants to merge 1 commit into
f4hy:mainfrom
bill-rich:pr/validate-before-register
Open

Decide a replay is too short before registering its match#132
bill-rich wants to merge 1 commit into
f4hy:mainfrom
bill-rich:pr/validate-before-register

Conversation

@bill-rich

Copy link
Copy Markdown
Contributor

What

upload_replay registered the match, computed its composition and invalidated the caches, and only then asked match_from_replay whether the replay was long enough to count, raising 422 when it wasn't:

_, created = replay_manager.register_match(db_match)
replay_manager.compute_and_save_composition(db_match.match_id)
invalidate_match_caches()

match_info = matches.match_from_replay(result.replay)
if match_info is None:
    raise HTTPException(status_code=422, detail="Replay is too short or invalid")

The writes had already happened by the time we decided the upload was invalid, so a short game landed half-present: reachable through /api/details and /api/replays, absent from /api/matches/by_date (get_match_infos lists with a 2.0 minute floor), and reported to the uploading client as a flat failure.

Match 866608453 from 2026-08-21 is a live example: a 42 second game someone quit out of. cncstats holds stats for it, radarvan answers /api/details/866608453, and it shows up in no date listing. It also makes "did every match land?" reconciliation between the two servers lie.

Change

Run the validation before any write, and register nothing when it fails. The parsed .rep and .json are still persisted by upload_and_parse, so the raw artifact survives either way; only the match row becomes conditional, which is what every read path already assumes.

Testing

New tests/test_upload_replay.py pins the ordering: a short replay must leave register_match / compute_and_save_composition uncalled, and a normal one must still register. Verified the first test fails against the old ordering and passes with the fix.

Full suite: 226 passed, 5 skipped. ruff check radarvan/ and mypy radarvan/ clean.

upload_replay registered the match, computed its composition and
invalidated the caches, and only then asked match_from_replay whether the
replay was long enough to count, raising 422 when it wasn't. The writes
had already happened, so a short game ended up half-present: reachable
through /api/details and /api/replays, absent from /api/matches/by_date
(get_match_infos lists with a 2.0 minute floor), and reported to the
uploading client as a plain failure.

Match 866608453 from 2026-08-21 is one of these, a 42 second game someone
quit out of. cncstats holds stats for it, radarvan answers /api/details
for it, and it appears in no date listing.

Run the validation first and register nothing when it fails. The parsed
.rep and .json are still persisted by upload_and_parse, so the artifact
survives either way; only the match row is now conditional, which matches
what every read path already assumes.
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