fix(sync): R5 S2 — sync guards
- never prune subscriptions on an empty fetch: one anomalous empty-but-200 response wiped every subscription (and its deep_requested flag) in one run - push_playlist: stop after 5 consecutive write failures — a quota-dead or scope-revoked account otherwise burns 50 units per remaining item - RSS: raise RssError instead of returning [] on a transport/HTTP failure, so a failed poll no longer stamps last_rss_at and a 404 feed stops looking healthy - worker: exit when the schema never appears instead of "continuing" into a crash one call later
This commit is contained in:
@@ -108,17 +108,29 @@ def import_subscriptions(db: Session, user: User) -> dict:
|
||||
sub_row.yt_subscription_id = sub.get("yt_subscription_id")
|
||||
db.flush()
|
||||
|
||||
# Prune subscriptions removed on YouTube.
|
||||
# Prune subscriptions removed on YouTube — but NEVER on an empty fetch. The API can answer
|
||||
# 200 with no items (a transient blip, a scope/token edge), and pruning against that wipes
|
||||
# every subscription the user has, taking each row's `deep_requested` flag with it, in one
|
||||
# run. Someone who genuinely unsubscribed from everything simply keeps their rows until a
|
||||
# sync that returns something.
|
||||
removed = 0
|
||||
existing = (
|
||||
db.execute(select(Subscription).where(Subscription.user_id == user.id))
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
for sub_row in existing:
|
||||
if sub_row.channel_id not in fetched:
|
||||
db.delete(sub_row)
|
||||
removed += 1
|
||||
prune_skipped = bool(existing) and not fetched
|
||||
if prune_skipped:
|
||||
log.warning(
|
||||
"Subscription import (user %s): YouTube returned 0 subscriptions while %d exist "
|
||||
"locally — skipping the prune (treating it as a transient empty response)",
|
||||
user.id, len(existing),
|
||||
)
|
||||
else:
|
||||
for sub_row in existing:
|
||||
if sub_row.channel_id not in fetched:
|
||||
db.delete(sub_row)
|
||||
removed += 1
|
||||
db.commit()
|
||||
|
||||
# Fetch details for channels that don't have them yet.
|
||||
@@ -144,6 +156,7 @@ def import_subscriptions(db: Session, user: User) -> dict:
|
||||
"channels_new": new_channels,
|
||||
"channels_detailed": detailed,
|
||||
"removed_stale": removed,
|
||||
"prune_skipped": prune_skipped,
|
||||
}
|
||||
log.info("Subscription import (user %s): %s", user.id, result)
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user