Skip to content

Release every lock on every exit path - #74

Merged
Mistium merged 7 commits into
mainfrom
fix-remaining-locks
Nov 1, 2026
Merged

Mistium merged 7 commits into
mainfrom
fix-remaining-locks

Conversation

@Mistium

@Mistium Mistium commented Nov 1, 2026

Copy link
Copy Markdown
Member

Stacked on the save-bug fixes (fix-project-save-bugs).

About 100 more places took a lock and unlocked it by hand on each return path, so any thrown error left the lock held until the server restarted. They are spread across projects, git, history, storage, community, classroom, admin, games, Rotur payments and webhooks, and auth. Every one now uses runLocked or sync.lock with a deferred unlock.

  • Mid-function unlocks: where work used to continue after an unlock (notifications, indexing, audits), the locked part moved into its own *Locked function, so it covers exactly what it covered before.
  • Lock order: nested order is unchanged: purchase start then purchase keys, quota then backpack, classroom plan then user ids, inventory sync then index.

Also fixed:

  • Blocked extension URLs: blocking an extension URL in admin never saved it, because the list was copied and never written back. It is saved now, under its own lock.
  • Bans file: renaming or purging a user now updates the bans file under the bans lock.

Verification

  • Unit tests: osl test passes 59. New tests make a locked step throw and then take the lock again, for pull numbering, comment counts, merges, branch changes, the nested backpack add and quota. They fail without the defers.
  • Smoke diff: against the save-bug branch, the only difference is the stated extension policy fix.
  • Route dump: identical to main.
  • Review: an independent review checked every conversion against its previous version: coverage, early returns, lock order, re-entrancy and locks held across network calls. It found no must-fix or should-fix issues, and its two nits (bans and extension policy writes without a lock) are fixed here.

There is no visible change.

…exit path

Every sync.lock in these domains is now followed by a deferred unlock,
so an error can't leave the lock held until restart. Where work carried
on after the unlock, the locked part moved into its own function, so
each lock still covers the same steps and nested locks keep their order.
The bans file helpers take the lock in their callers instead of handing
it from lockBans to saveAndUnlockBans.
Blocking a URL appended it to a copy of the policy's list and never
wrote the list back, so the block was never saved. The list is now
written back before the policy is saved.
Every remaining lock in these domains is now followed by a deferred
unlock, so an error can't leave it held until restart. Where work
carried on after the unlock, the locked part moved into its own
function, so each lock still covers the same steps: pull numbering,
branch changes, pull comment counts and fast-forward merges, the
backpack (still taking the quota lock before the backpack lock),
extension records, staged assets, the storage ledger queues and the R2
inventory. The remix and reaction wrappers use runLocked.

Tests make a locked step throw in pulls, the backpack and the upload
quota, and check the lock can be taken again.
Adds pull comments, commit co-author credits, branch renames and
deletes, reactions, staged assets, the backpack and the admin
extension policies, with random comment and backpack ids normalized.
Renaming and purging a user rewrote the bans file without the lock the
ban and sync code take, so a concurrent ban could be lost. Nothing else
is locked while the bans lock is held, so the order can't invert.
Blocking or unblocking a URL reads, edits and saves the policy file
under one lock, so two admins changing it at once can't lose a change.
The remixer's project limit and storage allowance come from their plan,
which can mean asking Rotur. They are now looked up before the parent
project's save lock is taken and passed to forkProjectFor, so a slow
Rotur doesn't hold up saves and history writes of the original.
@Mistium
Mistium force-pushed the fix-remaining-locks branch from 7b8cae4 to c254b14 Compare November 1, 2026 09:32
@Mistium
Mistium changed the base branch from fix-project-save-bugs to main November 1, 2026 09:32
@Mistium
Mistium merged commit ab7957d into main Nov 1, 2026
1 check passed
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