Commit ca63adc
committed
fix(mirror): eliminate race conditions by using fresh factory inside task closures
Affected endpoints: apiMirrorsDrop, apiMirrorsUpdate.
Both endpoints shared the same architectural flaw as the previously fixed
publish, repos, and snapshot endpoints: operations were performed outside
the task lock, with stale DB state used inside the lock.
Issues Fixed:
1. apiMirrorsDrop - Collections created before task lock
Problem: mirrorCollection and snapshotCollection created before task lock.
Snapshot dependency check done with stale factory. Concurrent drops both
load pre-task state, both see same snapshot dependencies. If snapshots
created after pre-task check, can delete mirror used by snapshots.
Fix: Create fresh taskCollectionFactory inside task, fresh load of mirror
after lock acquired, fresh snapshot check with current factory, drop using
fresh collections.
2. apiMirrorsUpdate - Mirror loaded before task lock
Problem: remote loaded outside task, rename duplicate check with stale
factory. Concurrent updates both load pre-task state, long-running update
uses stale mirror reference. TOCTOU race: rename check passes, another
creates mirror with same name, update saves with stale data.
Fix: Create fresh taskCollectionFactory inside task, fresh load of mirror
after lock acquired, pre-task rename validation, fresh rename check inside
lock, use fresh mirror and collections for all operations.
Root cause analysis:
The fundamental issue is the split between pre-task work and task-protected
work. Collections and objects were being loaded before lock acquisition, then
stale copies used inside the lock.
Correct pattern (from fixed publish.go, repos.go, and snapshot.go):
1. HTTP Handler (before task lock):
- Shallow load for 404 check only
- Extract resource keys
- Submit task with resources
2. Task Closure (after lock acquired):
- Create fresh collectionFactory
- Fresh load of all objects
- LoadComplete on fresh copies
- All mutations on fresh state
- All checks atomic inside lock
- Save using fresh collections
This ensures:
- Concurrent operations are serialized by task queue
- No stale DB state used for mutations
- No lost updates from concurrent modifications
- No TOCTOU races on duplicate checks
- No loss of mirrors used by snapshots
- No stale data in long-running updates1 parent 70c2787 commit ca63adc
1 file changed
Lines changed: 38 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
216 | 216 | | |
217 | 217 | | |
218 | 218 | | |
| 219 | + | |
219 | 220 | | |
220 | 221 | | |
221 | | - | |
222 | 222 | | |
223 | 223 | | |
224 | 224 | | |
| |||
228 | 228 | | |
229 | 229 | | |
230 | 230 | | |
| 231 | + | |
231 | 232 | | |
232 | | - | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
233 | 245 | | |
234 | 246 | | |
235 | 247 | | |
236 | 248 | | |
237 | 249 | | |
238 | | - | |
| 250 | + | |
| 251 | + | |
239 | 252 | | |
240 | 253 | | |
241 | 254 | | |
242 | 255 | | |
243 | 256 | | |
244 | 257 | | |
245 | | - | |
| 258 | + | |
246 | 259 | | |
247 | 260 | | |
248 | 261 | | |
| |||
550 | 563 | | |
551 | 564 | | |
552 | 565 | | |
| 566 | + | |
553 | 567 | | |
554 | 568 | | |
555 | 569 | | |
| |||
566 | 580 | | |
567 | 581 | | |
568 | 582 | | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
569 | 600 | | |
570 | 601 | | |
571 | | - | |
| 602 | + | |
572 | 603 | | |
573 | 604 | | |
574 | 605 | | |
| |||
780 | 811 | | |
781 | 812 | | |
782 | 813 | | |
783 | | - | |
784 | | - | |
| 814 | + | |
| 815 | + | |
785 | 816 | | |
786 | 817 | | |
787 | 818 | | |
| |||
0 commit comments