8b0976d0

By: Michael Lynch <git@mtlynch.io>

Bound media work to request and shutdown lifecycles

Media conversion previously ran synchronously inside HTTP handlers without a single owner. Request cancellation stopped at the handler boundary, ffmpeg, ffprobe, S3, and the final SQLite insert used detached operations, and a timed-out shutdown could close SQLite while an upload was still finishing. That combination could waste CPU after a disconnect and race successful media work against a closed database.

This follows the lifecycle model in Litestream v0.5.14 in files store.go and db.go: the component that starts background or expensive work owns its context, cancellation, concurrency bound, and wait group, and Close cancels and joins that work before releasing downstream resources. Litestream v0.5.14 in file cmd/litestream/replicate.go also uses context-bound child processes and preserves an explicit final-sync phase during shutdown.

Add a bounded medialifecycle owner and run validation, conversion, variant writes, and the final media insert inside it. Thread context through upload and Tinybeans conversion APIs, local and S3 writes, ExecContext, and exec.CommandContext for ffmpeg and ffprobe. Shutdown now rejects and cancels media work, drains HTTP, joins admitted jobs, and closes SQLite only after both drains complete, preserving the clean child exit that gives Litestream its configured final-sync window.

The main implementation difficulty was that uploads and Tinybeans share converter and writer signatures, so context propagation required a complete in-tree API migration rather than compatibility wrappers. Pure Go image operations cannot be interrupted internally, so they check cancellation between bounded stages. If draining times out, the process deliberately avoids explicitly closing SQLite instead of racing active handlers; process exit releases the descriptor and still lets Litestream observe a normal child exit.

No external blockers were encountered. Focused tests, the full Go checks, and every flake target passed before the amendment.

Suite timing

Time to Start Worker time Duration Time to finish Idle
Config 22s 3s 3s 26s 22s
Eval 41s 1m51s 1m51s 2m33s 15s
Build 2m07s 6m57s 2m32s 4m39s 0s
Suite 22s 8m53s 4m17s 4m39s 37s

Timeline

0s1m1m20s1m40s2m2m20s2m40s3m3m20s3m40s4m4m20s