storage: keep the stories people saved, and prune more than once a deploy
Maintenance deleted every story older than 30 days and then swept up any user_story_state row pointing at a story that no longer existed. A bookmark is one of those rows, so every saved story quietly vanished a month after Pete first saw it, which is exactly what saving it was meant to prevent. A bookmarked story is now exempt from the prune; read markers on pruned stories still go. Maintenance also only ran at startup, on a service that can go weeks between deploys, so nothing was pruned for weeks and then everything was at once. It runs at startup and then daily. Pruning daily makes one old hole steady-state: the stories row is Pete's only memory of a GUID, so a slow feed still listing month-old items would meet them again as new the day after their rows went. The poller now skips items published before the retention window. Undated items can't be judged and still pass.
This commit is contained in:
@@ -124,3 +124,46 @@ func TestUserStoryState_ScopedBySub(t *testing.T) {
|
||||
t.Fatalf("user-b count = %d, want 0", n)
|
||||
}
|
||||
}
|
||||
|
||||
// Maintenance prunes stories older than the retention window, and then sweeps
|
||||
// away user rows pointing at them. A bookmark is someone asking for a story to
|
||||
// be kept, so a bookmarked story must survive the prune, and its bookmark with
|
||||
// it. Read markers on pruned stories are fair game.
|
||||
func TestMaintenanceKeepsBookmarkedStories(t *testing.T) {
|
||||
setupTestDB(t)
|
||||
saved := seedStory(t, "saved") // seedStory stamps 2023, far past retention
|
||||
readOnly := seedStory(t, "read") // read, not saved
|
||||
seedStory(t, "untouched")
|
||||
const sub = "ak-sub-keep"
|
||||
|
||||
if err := SetBookmark(sub, saved, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := SetRead(sub, readOnly, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
RunMaintenance()
|
||||
|
||||
var n int
|
||||
if err := Get().QueryRow(`SELECT COUNT(*) FROM stories`).Scan(&n); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != 1 {
|
||||
t.Errorf("stories left after maintenance = %d, want 1 (the bookmarked one)", n)
|
||||
}
|
||||
marks, err := ListBookmarks(sub, 10, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(marks) != 1 || marks[0].GUID != "saved" {
|
||||
t.Fatalf("bookmarks after maintenance = %+v, want just the saved story", marks)
|
||||
}
|
||||
read, _, err := UserStoryState(sub, []int64{readOnly})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if read[readOnly] {
|
||||
t.Error("read marker on a pruned story survived the orphan sweep")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user