- downloads/quota.py: per-user limits (DownloadQuota row or sysconfig defaults; unlimited bypass), footprint = distinct ready-asset bytes the user holds, check_enqueue (max_jobs + max_bytes hard at enqueue), at_concurrency_limit (worker-side max_concurrent), usage snapshot - downloads/gc.py: run_download_gc — pre-expiry warning (once, gc_notified flag), TTL deletion (files + row; FK SET NULL orphans holders' jobs -> 'expired'), LRU eviction over a total-cache cap; notifies holders on each event - migration 0038: media_assets.gc_notified - config/sysconfig: download_total_max_bytes (0=unlimited) in the downloads group - service.enqueue enforces quota; worker claim skips users at their concurrency limit - scheduler: download_gc job registered (default 360 min, admin-tunable) Verified on localdev: footprint accounting, max_jobs block, pre-expiry warning (notifies all holders), TTL deletion (asset+files gone, dirs pruned, jobs orphaned, holders notified).
29 lines
870 B
Python
29 lines
870 B
Python
"""media_assets.gc_notified flag for one-shot pre-expiry warnings
|
|
|
|
Revision ID: 0038_asset_gc_notified
|
|
Revises: 0037_dl_builtin_profiles
|
|
Create Date: 2026-07-03
|
|
|
|
The download GC warns each holder once, a grace window before an asset's TTL expiry; this flag
|
|
records that so subsequent GC runs don't re-notify until the asset is deleted.
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "0038_asset_gc_notified"
|
|
down_revision: Union[str, None] = "0037_dl_builtin_profiles"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column(
|
|
"media_assets",
|
|
sa.Column("gc_notified", sa.Boolean(), server_default="false", nullable=False),
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_column("media_assets", "gc_notified")
|