Rebuild reliability core: SQLite store, durable queue, contained errors - #11
Merged
Conversation
4 tasks
miridius
force-pushed
the
fix-outage-class-errors
branch
from
July 16, 2026 06:10
c019d33 to
cbca4d8
Compare
Replaces the filesystem-as-a-database layer (job files, pending files, an info-cache dir with symlink aliases, title-named videos with .id sidecars) with one embedded bun:sqlite database — tables for jobs, parked confirmations, an identity-keyed blob index, and the URL info cache — so every multi-step state change is a transaction instead of a race-prone dance of renames and unlinks. Video bytes move to /storage/blobs, named by yt-dlp's stable identity (extractor:id:format, length-capped; salted filename fallback) and refcounted through the DB: bytes drop only when nothing references them and no telegram file_id makes them redundant. Dev and prod share the volume but get separate DB/blob stores (file_ids are per-bot). Message handlers now persist a job row and return; a worker pool downloads up to 3 in parallel with at-least-once recovery on boot, exponential-backoff retries for transient failures, and fail-fast classification of permanent ones — each attempt edits one status message instead of spawning threads. bot.catch contains per-update errors so the polling loop survives them, and a fatal polling crash exits deliberately for docker to restart. yt-dlp self-updates every 5 minutes (copy + --update + atomic rename, gated by a GitHub release-tag pre-check so an up-to-date tick costs one API call, not a 35MB copy). Cached scrape info expires after 6h — its signed media URLs rot — and is evicted on download failure so retries re-scrape. Long videos in groups ask before downloading; the real duration is probed once post-download and stored, so the gate holds even for metadata that lies and bytes long since uploaded and disposed. Private-chat message edits no longer re-send the same video (URL-level dedup per message). The first boot after the upgrade sweeps the dead pre-SQLite /storage layout. Tests mock only unowned boundaries (Telegram at fetch, GitHub releases): real SQLite, real filesystem, real child processes via PATH stubs, and a restart-recovery e2e over real persistence. The .claude workflow now enforces review deterministically: commit and merge gates verify a content-bound approval minted by fresh attestation agents, so an unreviewed or re-staged change cannot reach main. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
miridius
force-pushed
the
fix-outage-class-errors
branch
from
July 16, 2026 06:20
cbca4d8 to
8d74c8a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The bot's job queue, pending confirmations, and video cache were all coordinated through raw files under
/storage: job JSONs sorted by filename, pending-download files taken and put back, an info-cache directory with symlink aliases, and videos named by a title-based template with.idsidecar files. Coordinating concurrent access that way kept producing whole classes of user-visible failures:Fix
One embedded bun:sqlite database becomes the durable coordination store (jobs, parked confirmations, an identity-keyed blob index, a URL info cache), so every multi-step state change is a transaction instead of a rename/unlink dance.
extractor:id:format, with a URL-salted fallback for identity-less extractors). Downloads write into a private per-download staging dir and the bot takes whatever file actually appeared, so path prediction, and its whole failure class, is gone. Bytes are refcounted: dropped only when nothing references them, kept as afile_idcache after upload.Testing
Tests mock only unowned boundaries (Telegram at the fetch layer via a parity-checked MockBotApi, GitHub releases): real SQLite, real filesystem, real child processes via on-PATH stubs. 342 unit tests plus a network-free restart-recovery e2e;
./e2e.sh fullexercises real downloads (youtube, reddit, instagram) against the real yt-dlp, self-updated to the channel prod runs. Live QA against the dev bot covered the happy path, group confirmations, permanent/transient error classification, and mid-download/mid-upload SIGTERM.The
.claudeworkflow now enforces review deterministically: commit and merge gates verify content-bound approvals minted only by fresh attestation agents.🤖 Generated with Claude Code