fix(playlists): every sync path locks the list before its items, and holds it to the write

The mirror refresh re-ingested inside its row lock, and that ingest's commit let go of the lock
before the replace: a remove made then was undone and the list read as synced. Push, re-pull and a
delete touched items before the list row, a deadlock beside the playlist-first route edits. A push
now re-plans from the items under the lock and derives dirty after its YouTube writes.
lock_playlists moves to the sync package. Tests pin both ids of a move's lock and the claim's
own connection.
This commit is contained in:
2026-09-27 21:56:23 +02:00
parent 2abd4ffcb6
commit 26697472cf
6 changed files with 329 additions and 47 deletions
+1 -1
View File
@@ -285,7 +285,7 @@ def test_no_add_can_land_in_the_target_while_a_move_holds_it(db, make_user):
_seed_videos(db, ["v1"])
src = _playlist(db, user, "Source", ["v1"])
dst = _playlist(db, user, "Target", [])
plroutes._lock_playlists(db, src.id, dst.id) # what the move holds until its commit
plroutes.lock_playlists(db, src.id, dst.id) # what the move holds until its commit
with SessionLocal() as other:
other.execute(text("SET LOCAL lock_timeout = '300ms'"))
other.add(PlaylistItem(playlist_id=dst.id, video_id="v1", position=0))