Count only subscribers' read marks and pins when freeing disk space (#156)
The reaper counted read marks and pins in entry_state from anyone and compared them with the feed's subscribers. Unsubscribing leaves your rows behind, and the flags endpoint writes them for feeds you do not subscribe to as well. So an episode read by someone who had left and by one subscriber of two counted as heard by everyone, and went first under the quota though the other had not played it; and a pin of a person who had left kept a file, and its item, for good, which can leave the quota impossible to meet. Both queries, the reaper's candidates and prune_entries, now count only the marks of people who subscribe to the item's feed. The old prune test's pin had no subscriber behind it; it has one now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
13
src/db.rs
13
src/db.rs
@@ -782,7 +782,9 @@ impl Db {
|
||||
/// One file serves every subscriber, so both tests are about all of them: **anyone**
|
||||
/// starring it keeps it, and it only counts as read when **everyone** subscribed has
|
||||
/// read it. A file whose feed nobody subscribes to has no one left to keep it, so it
|
||||
/// sorts with the read ones.
|
||||
/// sorts with the read ones. Only the marks of people who subscribe count: unsubscribing
|
||||
/// leaves yours behind, and counted, a reader who had left made an episode someone had not
|
||||
/// played look finished, and a pin of theirs kept a file for good (#156).
|
||||
///
|
||||
/// (The Python intended `read = 1 AND flagged = 0` but never achieved it -- a missing
|
||||
/// plistlib import and an `EntreiesData` typo meant the filter always threw.)
|
||||
@@ -796,13 +798,15 @@ impl Db {
|
||||
LEFT JOIN (SELECT feed_id, count(*) AS n FROM subscriptions GROUP BY feed_id) subs
|
||||
ON subs.feed_id = e.feed_id
|
||||
-- Someone an item is hidden from is as done with it as someone who read it.
|
||||
LEFT JOIN (SELECT feed_id, guid, count(*) AS n
|
||||
LEFT JOIN (SELECT done.feed_id, done.guid, count(*) AS n
|
||||
FROM (SELECT user_id, feed_id, guid FROM entry_state WHERE read
|
||||
UNION SELECT user_id, feed_id, guid FROM hidden) done
|
||||
GROUP BY feed_id, guid) readers
|
||||
JOIN subscriptions u ON u.user_id = done.user_id AND u.feed_id = done.feed_id
|
||||
GROUP BY done.feed_id, done.guid) readers
|
||||
ON readers.feed_id = e.feed_id AND readers.guid = e.guid
|
||||
WHERE e.path IS NOT NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM entry_state s
|
||||
JOIN subscriptions u ON u.user_id = s.user_id AND u.feed_id = s.feed_id
|
||||
WHERE s.feed_id = e.feed_id AND s.guid = e.guid AND s.flagged)
|
||||
ORDER BY 6 DESC, coalesce(e.downloaded_at, 0) ASC, e.id ASC",
|
||||
vec![],
|
||||
@@ -941,9 +945,10 @@ impl Db {
|
||||
SELECT 1 FROM enclosures e
|
||||
WHERE e.feed_id = entries.feed_id AND e.guid = entries.guid
|
||||
AND e.path IS NOT NULL)
|
||||
-- Starred by anyone keeps it, the same rule the reaper follows.
|
||||
-- Starred by anyone subscribed keeps it, the same rule the reaper follows.
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM entry_state s
|
||||
JOIN subscriptions u ON u.user_id = s.user_id AND u.feed_id = s.feed_id
|
||||
WHERE s.feed_id = entries.feed_id AND s.guid = entries.guid
|
||||
AND s.flagged)",
|
||||
vec![older_than.into()],
|
||||
|
||||
Reference in New Issue
Block a user