Clamp an unlimited download queue's LIMIT for Postgres (#98)
With max_new_per_check at 0 and no per-subscription limit, the budget is usize::MAX, and pending() bound it `as i64`: -1. SQLite reads LIMIT -1 as no limit; Postgres refuses it, so a feed's downloads failed. The new test fails with "LIMIT must not be negative" on Postgres without the clamp and passes with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"permissions": {
|
|
||||||
"allow": [
|
|
||||||
"Bash(rtk grep *)",
|
|
||||||
"Bash(rtk read *)",
|
|
||||||
"Bash(rtk git *)"
|
|
||||||
],
|
|
||||||
"additionalDirectories": [
|
|
||||||
"/config/.claude/skills/security-audit",
|
|
||||||
"/config/security-audit-skill",
|
|
||||||
"/config/.cargo/registry"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- With no limit on new downloads per check (`max_new_per_check = 0`), downloads work on Postgres; they failed.
|
||||||
- Checking a feed with a long history is much quicker: items already stored are no longer written again on every check.
|
- Checking a feed with a long history is much quicker: items already stored are no longer written again on every check.
|
||||||
- A scan no longer asks a feed's website for its icon every time; it asks again when the artwork changes or you refresh the feed.
|
- A scan no longer asks a feed's website for its icon every time; it asks again when the artwork changes or you refresh the feed.
|
||||||
- The feed list loads several times faster: it was asking the database six questions per feed.
|
- The feed list loads several times faster: it was asking the database six questions per feed.
|
||||||
|
|||||||
15
src/db.rs
15
src/db.rs
@@ -635,7 +635,9 @@ impl Db {
|
|||||||
WHERE x.feed_id = $1 AND x.state = 'pending'
|
WHERE x.feed_id = $1 AND x.state = 'pending'
|
||||||
ORDER BY coalesce(e.published, e.first_seen) DESC, x.id DESC
|
ORDER BY coalesce(e.published, e.first_seen) DESC, x.id DESC
|
||||||
LIMIT $2",
|
LIMIT $2",
|
||||||
vec![feed_id.into(), (limit as i64).into()],
|
// Unlimited is usize::MAX, which `as i64` makes -1: SQLite reads LIMIT -1 as no
|
||||||
|
// limit, Postgres refuses it, and the feed's downloads failed (#98).
|
||||||
|
vec![feed_id.into(), (limit.min(i64::MAX as usize) as i64).into()],
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
.iter()
|
.iter()
|
||||||
@@ -1934,6 +1936,17 @@ mod tests {
|
|||||||
assert_eq!((list["f"].unread, list["g"].unread), (1, 1)); // a read, c hidden; sam's read is sam's
|
assert_eq!((list["f"].unread, list["g"].unread), (1, 1)); // a read, c hidden; sam's read is sam's
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn an_unlimited_queue_takes_everything_waiting() {
|
||||||
|
let db = Db::memory().await.unwrap();
|
||||||
|
db.exec_for_test(
|
||||||
|
"INSERT INTO entries (feed_id, guid, first_seen) VALUES ('f','a',0),('f','b',0);
|
||||||
|
INSERT INTO enclosures (id, feed_id, guid, url, state) VALUES (1,'f','a','u1','pending'),(2,'f','b','u2','pending');",
|
||||||
|
).await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(db.pending("f", usize::MAX).await.unwrap().len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn a_scan_knows_a_feeds_stored_items_and_files() {
|
async fn a_scan_knows_a_feeds_stored_items_and_files() {
|
||||||
let db = Db::memory().await.unwrap();
|
let db = Db::memory().await.unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user