chadtmiller.com/knotless
git that lives in your Atmosphere account
Spaces are listed one at a time for each account
MergedThis adds a per-account async mutex, App::discover_lock, and takes it at the top of spaces::owned. That serializes the listing that runs space::forget, then the joined checks and read_by/unread_by writes. A check that started before a removal can no longer finish after a newer listing's result.
The diff matches the problem you describe. It follows the existing orgs lock pattern. The std Mutex guarding the map is released before the .await, so it isn't held across an await. Only listings for the same account wait. The throttle check in discover still runs before the lock, so a listing that has to wait doesn't start a second one.
Two minor points, neither blocking:
- The lock map gains one entry per account and never shrinks.
orgsdoes the same, so this is consistent with it. docs/design/access.mdcovers spaces. It may be worth one line saying listings are serialized per account, if the page describes discovery.
I did not run the tests. As you say, whether private_repositories_indexed_here stops failing will show in CI.
8 passed, 2 skipped
What changes
An account's spaces are listed one at a time (
spaces::discover), behind a lock per account onApp. A second listing waits for the first one to finish instead of running beside it.Why
Each listing deletes the account's kept space credentials, then asks each space's authority whether the account is still a member. When two listings run at once, a check that started before a removal can return after it. It then writes back a credential the authority issued before the removal, or marks the repository readable again after the newer check took it away. So a member who was removed can still find the repository until the next listing. This is the race behind
private_repositories_indexed_here, the test that failed most often in recenttestruns: that test clears the throttle on every look, so it starts many listings at once. In prod, the once-a-minute throttle makes overlaps rare, but a slow listing can still overlap the next one.A lock per account is the smallest fix. It costs no extra calls to the authority, and only listings for the same account ever wait.
How it was checked
Left to CI. The test is still quarantined until 2026-11-21. The failure report in private_repositories_indexed_here says why when it waits past its time will show whether anything else keeps it failing.
Shortcomings
A page read by the member that starts before the removal could still store a credential issued before it. That credential works until it expires, at most a few minutes. The next listing deletes it.
Changeset: none
🤖 Generated with Claude Code