Skip to main content

## MIGRATION_023

# Constant MIGRATION_023

Source

    const MIGRATION_023: &str = r#"
    ALTER TABLE downloads ADD COLUMN expires_at TEXT;

    -- Reclaim scans filter on expiry among 'auto' rows; index both so the sweep
    -- stays cheap as the cache tier grows.
    CREATE INDEX IF NOT EXISTS idx_downloads_expiry
        ON downloads(download_source, expires_at);
    "#;

Expand description

Give temporary downloads a life limit.

A cache entry is not a different kind of object from a download — it is a download with a shorter life. Modelling it as one `downloads` row with an expiry (rather than a parallel cache store) means there is a single storage accounting, a single eviction path, and no way for a cache and a download library to disagree about what is on disk.

`expires_at` is NULL for permanent rows, which is every row that exists today: `download_source` defaults to `'user'`, and a user’s own download never expires. Only `'auto'` rows get a timestamp, and they are reclaimed by whichever comes first — the expiry passing, or LRU eviction under space pressure (DR-126).

TRACES: UR-071 | DR-127