wrolpi/modules/videos
Roland 06ac0d2055 review: ffprobe out of the write transaction, and three tests that were watching nothing
All three bugs confirmed; the two suggestions taken.

[bug] `video_modeler` awaited ffprobe -- a subprocess per video, up to 20
per batch -- inside its `commit=True` session.  With IMMEDIATE as the
default that holds the write lock for the whole batch, which is the same
starvation this branch is curing in `video_cleanup`.  (It was already
partly true before: the batch's first `session.flush([video])` took the
lock ahead of the remaining ffprobes.)  The batch is now read, ffprobed
with no transaction open, then written.  `get_or_create_ffprobe_json` in
modules/videos/common.py is the path-only entry point that makes that
possible; `Video.get_ffprobe_json` delegates to it and keeps appending
the cache file to its FileGroup.  `validate()` -- and the poster
generation it can trigger -- is still inside the write transaction, and
now says so.

[bug] `test_download_dispatched_outside_immediate_transaction` probed
`getattr(_immediate_txn, 'active', False)`, which a ContextVar never has,
so it passed unconditionally.  It now probes the actual write lock from a
competing connection under `production_like_sessions`.  Mutation-checked:
moving the dispatch back inside the claim transaction fails it.

[bug] `asyncio.create_task` snapshots the current Context, so a flag held
for a write session's lifetime was inherited by every task spawned inside
it -- and the parent's `reset()` cannot reach a child's copy, leaving
that task issuing BEGIN IMMEDIATE for its *read* sessions.
`create_downloads` spawns `dispatch_downloads` exactly that way, so every
user-created download hit it.  The flag now lives only across the
session's opening `connection()` call, which also means the write lock is
taken when the session opens rather than at the caller's first statement.

[suggestion] `renew_recurring_downloads` scanned every recurring Download
under the write lock, including the usual cycle where nothing is due.  It
reads first now and takes the lock only for the rows that change,
re-checking status since the dispatcher may have claimed one in between.

[nit] Parenthesized the directory OR in the claim join.

`probe_write_lock_is_held` moved to conftest alongside
`write_lock_held_briefly`; three suites use it now.

Suite: 1712 passed, 5 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 20:15:50 -06:00
..
channel Postgres -> SQLite hard cutover: the database moves into the media directory. 2026-07-12 14:09:37 -06:00
test review: ffprobe out of the write transaction, and three tests that were watching nothing 2026-08-07 20:15:50 -06:00
video SQLite: make the browse pages index-only. 2026-07-14 07:46:29 -06:00
__init__.py review: ffprobe out of the write transaction, and three tests that were watching nothing 2026-08-07 20:15:50 -06:00
api.py Unifying video download settings so that global/channel/rss/video 2026-03-05 17:40:17 -07:00
common.py review: ffprobe out of the write transaction, and three tests that were watching nothing 2026-08-07 20:15:50 -06:00
conftest.py Search: fast ABC mode by default, opt-in deep search 2026-07-03 10:32:16 -06:00
cookies.py Using shared context to sync cookies between processes. 2026-02-22 14:32:27 -07:00
downloader.py fix: a video download requests one format, not every matching format 2026-08-05 17:25:19 -06:00
errors.py Adding toggle to Channel page to stop downloading comments for certain channels. 2025-03-01 18:23:29 -07:00
lib.py Postgres -> SQLite hard cutover: the database moves into the media directory. 2026-07-12 14:09:37 -06:00
models.py review: ffprobe out of the write transaction, and three tests that were watching nothing 2026-08-07 20:15:50 -06:00
normalize_video_url.py A video that is uploaded is attached to its Channel. Saving Channels config with both download url and frequency in case downloads config is corrupted. 2024-10-16 18:56:11 -06:00
schema.py Search: fast ABC mode by default, opt-in deep search 2026-07-03 10:32:16 -06:00