19aece1fc4
Routine subscription polls walked the entire post history every tick
even when nothing had changed, because gallery-dl's default `skip: True`
continues iterating archived posts. A creator with ~550 archived posts
(Knuxy patreon) saturates the 870s wall-clock cap before completing,
even with zero downloads needed. Plus, a tier-limited run that
downloaded hundreds of files but ran out the clock should be a
warning, not an error.
Two coupled changes, both operator-flagged 2026-06-01:
* **Tick mode (default, cron polls).** New `TICK_SKIP_VALUE = "exit:20"`
asks gallery-dl to exit after 20 contiguous archived items. Fresh
subscriptions + new-content cases still walk normally; established
subscription with zero new content exits in ~30s of HEAD requests
instead of pegging the timeout. 20 (not 5) gives headroom against
paywall warnings interleaving with archived items.
* **Backfill mode (explicit, operator-triggered).** Sticky for N runs
via new `Source.backfill_runs_remaining` (alembic 0031). While > 0,
downloads use `skip: True` + 1800s timeout. Auto-decrements per run
with early-reset to 0 when a clean run finds zero files (queue
drained). N defaults to 3 — multiple runs give the system enough
budget to finish a deep walk across timeout boundaries. New
`POST /api/sources/{id}/backfill` arms the source; "Deep scan"
button on each SourceRow (chip shows remaining count) wires it.
Plus partial-success classifier: non-zero gallery-dl exit + ≥1 file
downloaded + no source-level error fires `ErrorType.PARTIAL`, which
download_service maps to `status=\"ok\"`. The run did real work; the
next tick continues via gallery-dl's archive. No more red events for
"timed out mid-walk after downloading 300 files."
Retires `SourceConfig.skip_existing` — skip value is now derived from
the source state and passed as a separate `skip_value` parameter
through download() / _build_config_for_source(). `GD_DEFAULTS` drops
the now-dead key (was inert data after this refactor).
Tests cover:
* tick + backfill skip-value emission in _build_config_for_source
* PARTIAL classifier branch + TIER_LIMITED-wins-over-PARTIAL ordering
* SourceService.set_backfill_runs validation + persistence
* /api/sources/{id}/backfill 200/400/404 paths
* download_service auto-decrement / auto-reset / tick-mode-no-touch
* PARTIAL → status=ok in the orchestrator (no consecutive_failures bump)
46 lines
1.3 KiB
Python
46 lines
1.3 KiB
Python
"""source.backfill_runs_remaining: sticky deep-scan mode
|
|
|
|
Revision ID: 0031
|
|
Revises: 0030
|
|
Create Date: 2026-06-01
|
|
|
|
Tick vs backfill mode for subscription downloads. When
|
|
`backfill_runs_remaining > 0`, the next N download runs use
|
|
`skip: True` + 30-min timeout (walk full history). When 0, runs use
|
|
`skip: "exit:20"` + 14.5-min timeout (catch-up mode, exits early once
|
|
20 contiguous archived items are seen).
|
|
|
|
Operator-flagged 2026-06-01 (Knuxy run #38887): a creator with ~550
|
|
archived posts saturates the 870s catch-up timeout even when there is
|
|
no new content, because gallery-dl's default `skip: True` keeps walking.
|
|
Tick mode short-circuits that; backfill mode is the explicit opt-in for
|
|
deep history scans.
|
|
|
|
Default 0 (all existing subscriptions start in tick mode).
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "0031"
|
|
down_revision: Union[str, None] = "0030"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column(
|
|
"source",
|
|
sa.Column(
|
|
"backfill_runs_remaining",
|
|
sa.Integer,
|
|
nullable=False,
|
|
server_default="0",
|
|
),
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_column("source", "backfill_runs_remaining")
|