Skip to content

Cap output.db by size with FIFO eviction — the 30-day prune never reclaims disk space #124

Description

@AThraen

Reported from live use (2026-09-08): output.db has grown to 1.32 GB.

Retention already exists — and it cannot control size

AppSettings.OutputRetentionDays (default 30) is applied at startup by MainWindow.OnLoadedSearchService.PruneOldOutputAsync. So this is not a missing feature; it's a policy that cannot do the job, for two independent reasons.

1. The prune deletes rows but never reclaims the file

cmd.CommandText = "DELETE FROM session_output WHERE ts < $cutoff";

That's the whole of PruneOldOutputAsync (Services/SearchService.cs:296-305). SQLite marks the pages free for reuse; it does not shrink the file. The only VACUUM in the codebase is in ClearAllOutputAsync, which is the nuclear "wipe everything" path.

So the on-disk figure only ever ratchets upward. A user watching output.db grow past a gigabyte sees the retention setting doing literally nothing, because the observable quantity — file size — is not what the policy controls.

2. Age is the wrong axis

30 days is unbounded in bytes. It depends entirely on how much output was produced, which at 47 concurrent Claude sessions is a lot. Two users with identical settings can differ by an order of magnitude, and neither can predict or cap their footprint. What a user actually wants to say is "use at most N GB".

Proposal

Add a size cap with FIFO eviction, keeping the age policy as a separate, complementary axis.

Setting: AppSettings.MaxOutputDbSizeMb (0 = unlimited). Suggest defaulting to something like 512 MB, alongside the existing OutputRetentionDays.

Eviction: oldest-first by ts — the FIFO the reporter asked for — deleting in batches until the projected size is under the cap. Because SQLite won't report a smaller size until a VACUUM, the loop should evict against a row/page estimate (page_count * page_size via PRAGMA, plus freelist_count to know what's already reclaimable) rather than re-stating the file each pass.

Reclaim: the missing half. Options, cheapest first:

  • PRAGMA auto_vacuum=INCREMENTAL + periodic PRAGMA incremental_vacuum(N) — reclaims progressively without a full rewrite. Note this must be set before tables are created, so an existing DB needs a one-time full VACUUM to convert.
  • A full VACUUM after a large eviction. Rewrites the whole file — at 1.3 GB that is slow and needs free disk equal to the DB size, so it should not run on the UI thread or block startup.

Where it runs: not synchronously at startup. Startup is already the app's worst latency path (#82), and a VACUUM on a gigabyte-scale file would add seconds to it. Off-thread, after restore, or on idle.

FTS5: session_output is an FTS5 table, so its index shadow tables carry much of the bulk. Verify eviction shrinks those too and that optimize / rebuild isn't needed to actually recover the space.

Also worth surfacing

The user had no way to see the problem coming. Settings should show current output.db size next to the retention controls, with the manual "clear output" action that already exists nearby.

Acceptance

  • MaxOutputDbSizeMb setting, enforced by oldest-first eviction
  • Freed space is actually returned to the filesystem, not just marked reusable
  • Neither eviction nor reclaim blocks startup or the UI thread
  • Current DB size visible in Settings
  • Existing OutputRetentionDays keeps working as the age axis

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions