Split Series from Bookmark, keeping the wire format flat (#21) #29

Merged
sulthan merged 1 commits from feat/db-change into main 2026-08-08 07:19:55 +07:00
Owner

Implements #21 (blocked-by #20, already merged as #28).

Series becomes a shared row keyed (site, series_id) owning title, cover, canonical URL, kind, latest chapter, last-checked time (ADR-0003). Bookmarks keep only progress, favourite, lifecycle bucket, updated_at.

  • Migration 0002 backfills series from existing bookmarks; verified by TestMigration0002BackfillsExistingBookmarks.
  • Store.Upsert decomposes one flat body across both tables in one tx; client title/series_url/cover apply only when the series row is new, then only the poll may change them (security boundary — shared row, scraped values).
  • Flat wire format preserved: TestFlatWireFieldSet asserts the exact 15-field key set on PUT and GET (ADR-0004).
  • Poller walks Series: one fetch per shared series per cycle, due queue ordered reader_count DESC then latest_checked_at ASC, orphan series never due and never deleted, row stamped before fetch. Batch/stagger/interval unchanged.
  • Poller's write is now a single-column SetLatestChapter — the old Get+Upsert stale-re-read race is gone.

go vet clean, full suite green. Reviewed on both axes (standards + spec), zero actionable findings.

Implements #21 (blocked-by #20, already merged as #28). Series becomes a shared row keyed (site, series_id) owning title, cover, canonical URL, kind, latest chapter, last-checked time (ADR-0003). Bookmarks keep only progress, favourite, lifecycle bucket, updated_at. - Migration 0002 backfills series from existing bookmarks; verified by TestMigration0002BackfillsExistingBookmarks. - Store.Upsert decomposes one flat body across both tables in one tx; client title/series_url/cover apply only when the series row is new, then only the poll may change them (security boundary — shared row, scraped values). - Flat wire format preserved: TestFlatWireFieldSet asserts the exact 15-field key set on PUT and GET (ADR-0004). - Poller walks Series: one fetch per shared series per cycle, due queue ordered reader_count DESC then latest_checked_at ASC, orphan series never due and never deleted, row stamped before fetch. Batch/stagger/interval unchanged. - Poller's write is now a single-column SetLatestChapter — the old Get+Upsert stale-re-read race is gone. go vet clean, full suite green. Reviewed on both axes (standards + spec), zero actionable findings.
sulthan added 1 commit 2026-08-08 07:19:45 +07:00
Series becomes a shared row keyed (site, series_id) owning title, cover,
canonical URL, kind, latest chapter and last-checked time (ADR-0003).
A bookmark keeps only progress, favourite, lifecycle bucket, updated_at.

Store.Upsert decomposes one flat body across both tables in one
transaction: client title/series_url/cover apply only when the series row
is new, then only the poll may change them (security boundary — the row
is shared and the values are scraped page content). Reads join series
back in, so GET/PUT emit and accept exactly the flat field set they did
before (ADR-0004), asserted by TestFlatWireFieldSet.

The poller walks Series instead of Bookmarks: one fetch per shared
series per due cycle, due queue ordered reader_count DESC then
latest_checked_at ASC, orphaned series never due and never deleted, row
still stamped before the fetch. Batch/stagger/interval unchanged.

Migration 0002 backfills series from existing bookmarks; verified by
TestMigration0002BackfillsExistingBookmarks.
sulthan force-pushed feat/db-change from 84291c3635 to a48aba67b8 2026-08-08 07:19:45 +07:00 Compare
sulthan merged commit 984965ed9f into main 2026-08-08 07:19:55 +07:00
Sign in to join this conversation.