v1.5.0: entry-dedup in één query + conditional get (ETag/Last-Modified)

- filter_entries: één SELECT per feed i.p.v. per-entry EXISTS (1082 queries → 20)
- feed.etag/last_modified (migratie v9); parse_feed stuurt If-None-Match/If-Modified-Since, 304 → None
- _poll_once telt unchanged (304-feeds); summary-regel uitgebreid met unchanged
- _poll_feeds re-raised CancelledError (reload-stapeling duplicaten) + handler-cleanup
This commit is contained in:
Eddy de Vink 2026-09-06 00:11:22 +02:00
commit a03f76ca83
4 changed files with 59 additions and 25 deletions

View file

@ -64,6 +64,8 @@ class Feed:
next_retry: int = 0
error_count: int = 0
etag: str = ""
last_modified: str = ""
subscriptions: list[Subscription] = attr.ib(factory=lambda: [])
@ -119,7 +121,7 @@ class DBManager:
async def get_feeds(self) -> list[Feed]:
q = """
SELECT id, url, title, subtitle, link, next_retry, error_count,
SELECT id, url, title, subtitle, link, next_retry, error_count, etag, last_modified,
room_id, user_id, notification_template, send_notice
FROM feed INNER JOIN subscription ON feed.id = subscription.feed_id
"""
@ -135,25 +137,19 @@ class DBManager:
async def get_feeds_by_room(self, room_id: RoomID) -> list[tuple[Feed, UserID]]:
q = """
SELECT id, url, title, subtitle, link, next_retry, error_count, user_id FROM feed
SELECT id, url, title, subtitle, link, next_retry, error_count, etag, last_modified,
user_id FROM feed
INNER JOIN subscription ON feed.id = subscription.feed_id AND subscription.room_id = $1
"""
rows = await self.db.fetch(q, room_id)
return [(Feed.from_row(row), row["user_id"]) for row in rows]
async def filter_entries(self, feed_id: int, entries: dict[str, Entry]) -> None:
entry_keys = list(entries.keys())
if not entry_keys:
if not entries:
return
async with self.db.acquire() as conn:
for key in entry_keys:
exists = await conn.fetchval(
"SELECT EXISTS(SELECT 1 FROM entry WHERE feed_id = $1 AND id = $2)",
feed_id,
key,
)
if exists:
del entries[key]
existing = await self.db.fetch("SELECT id FROM entry WHERE feed_id = $1", feed_id)
for row in existing:
entries.pop(row["id"], None)
async def add_entries(self, entries: list[Entry], override_feed_id: int | None = None) -> None:
if not entries:
@ -174,14 +170,14 @@ class DBManager:
await conn.executemany(q, records)
async def get_feed_by_url(self, url: str) -> Feed | None:
q = "SELECT id, url, title, subtitle, link, next_retry, error_count FROM feed WHERE url=$1"
q = "SELECT id, url, title, subtitle, link, next_retry, error_count, etag, last_modified FROM feed WHERE url=$1"
return Feed.from_row(await self.db.fetchrow(q, url))
async def get_subscription(
self, feed_id: int, room_id: RoomID
) -> tuple[Subscription | None, Feed | None]:
q = """
SELECT id, url, title, subtitle, link, next_retry, error_count,
SELECT id, url, title, subtitle, link, next_retry, error_count, etag, last_modified,
room_id, user_id, notification_template, send_notice
FROM feed LEFT JOIN subscription ON feed.id = subscription.feed_id AND room_id = $2
WHERE feed.id = $1
@ -206,6 +202,10 @@ class DBManager:
q = "UPDATE feed SET error_count = $2, next_retry = $3 WHERE id = $1"
await self.db.execute(q, info.id, error_count, next_retry)
async def set_feed_headers(self, info: Feed, etag: str, last_modified: str) -> None:
q = "UPDATE feed SET etag = $2, last_modified = $3 WHERE id = $1"
await self.db.execute(q, info.id, etag, last_modified)
async def subscribe(
self,
feed_id: int,