- The queue keeps a done job by when it became done (ready_at), not by its last write: renaming, keeping or moving an old file, or the GC's expiry warning, no longer brings it back as just finished. - GC expiry and eviction re-read each file under the asset→job locks the user routes take, and commit one file at a time: a delete, a re-download or a keep made while the GC runs is honoured, never a second release that frees a file another user still holds. - A file records the origin it was placed under (download_jobs.source_key, migration 0105): an edit whose parent expired still goes back to its folder, not to Uploads. - A folder a file is being put in is held (FOR SHARE) until it lands; a delete of it waits and takes that file home too, instead of failing on the foreign key. - The hash route reads at most two files at once (429 past that) and holds no connection meanwhile. - 0104's backfill dates old files by their request date only: no notice prod wrote names its job. - The old DOWNLOAD_ROOT still finds the files; a files root that is not a mounted volume stops the worker and is logged by the API; the volume-copy recipe looks the project-prefixed names up. - Stale comments about the /downloads mount and the Downloads page.
39 lines
1.3 KiB
Python
39 lines
1.3 KiB
Python
"""A file's "Added" is when it became done
|
|
|
|
Revision ID: 0104_job_ready_at
|
|
Revises: 0103_files_sha256
|
|
Create Date: 2026-10-12
|
|
|
|
`download_jobs.ready_at` is when a job became done, and the Files library sorts and dates by it: a
|
|
download that failed for days and then went through used to sit days down the list, under its
|
|
request date. Every path to done stamps it, held by `ck_download_jobs_ready_at`.
|
|
|
|
A done job from before has no record of that moment (its "ready to watch" notice never named the
|
|
job, so it cannot be matched back), so it takes its request date, which is where it already sat.
|
|
"""
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "0104_job_ready_at"
|
|
down_revision: str | None = "0103_files_sha256"
|
|
branch_labels: str | None = None
|
|
depends_on: str | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column("download_jobs", sa.Column("ready_at", sa.DateTime(timezone=True)))
|
|
op.execute(
|
|
"""
|
|
UPDATE download_jobs SET ready_at = created_at WHERE status = 'done'
|
|
"""
|
|
)
|
|
op.create_check_constraint(
|
|
"ck_download_jobs_ready_at", "download_jobs", "status <> 'done' OR ready_at IS NOT NULL"
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_constraint("ck_download_jobs_ready_at", "download_jobs", type_="check")
|
|
op.drop_column("download_jobs", "ready_at")
|