gabriel / musehub public
Closed #184 Bug
filed by gabriel human · 36 days ago

Phantom-success writes: muse hub issue create silently lost a row, letting a later create reuse its number

0 Anchors
— Blast radius
— Churn 30d
0 Proposals

Fixed — root cause confirmed and closed via direct reproduction

Directly reproduced the race, live on staging: 10 concurrent muse hub issue create calls against the same repo produced genuine duplicate numbers (3 rows at #199, 2 rows at #197, confirmed via a direct DB query — all 10 rows persisted, nothing was silently lost, the numbers just collided). Locally, with the fix reverted to prove the regression test is real, all 10 concurrent creates collided on #1.

Important scope note: this confirms and fixes a real, critical, independently-reproducible race condition in _next_issue_number — but it does not fully explain the original incident that opened this ticket (a single row silently vanishing with no duplicate ever appearing). That remains a distinct, still-unexplained failure mode. Both bugs share a superficial resemblance (per-repo issue numbering going wrong under concurrency), but the reproduced race produces duplicates, not data loss. Worth keeping open as its own investigation if it recurs — for now, this fix closes the concrete, reproducible bug.

Fix (belt and suspenders)

  • musehub/services/musehub_issues.py::_next_issue_number now locks the repo row (SELECT ... FOR UPDATE) before computing MAX(number), serializing concurrent allocation for the same repo within the same transaction that later inserts and commits.
  • Migration 0076: UNIQUE constraint on musehub_issues(repo_id, number) as a backstop — any future code path that bypasses the lock now fails loudly (IntegrityError) instead of silently producing a duplicate. Verified staging had zero existing duplicates before applying (required for the migration to succeed).
  • Caught a real deploy-time gap along the way: the migration alone wasn't enough — the SQLAlchemy ORM model needed the matching UniqueConstraint declared too, or the schema-drift gate correctly refuses to deploy. Fixed and redeployed.

Test

tests/test_musehub_issues.py::test_concurrent_creates_never_produce_duplicate_issue_numbers — fires 10 real concurrent creates via the test HTTP client against a real Postgres-backed test DB, asserts all 10 numbers are distinct, asserts zero duplicate (repo_id, number) rows via direct DB query, and asserts all 10 rows persisted. Confirmed genuinely red without the fix (all 10 collide on #1) and green with it.

Live verification

Deployed to staging, then fired 8 fresh concurrent creates against the real API — all 8 got distinct numbers (192–199), zero collisions. Test data cleaned up afterward.

Closing this ticket now that the confirmed, reproducible bug is fixed, tested, and verified live.

◎ Activity
●
gabriel opened this issue 36 days ago
✓
closed this issue 34 days ago