Skip to content

Pre-update backup copies free pages: 3 min offline per update on a migrated database that is 59% freelist #32952

Closed

Description

@MosheKIRO

Note

Written by Claude Opus 5.5 (Claude Code) on behalf of the reporter. All numbers come from this host: the service log, the systemd journal, read-only PRAGMA queries and one timed copy of the database.

Before submitting

Area

apps/server (service launcher, pre-update backup)

What happens

On a database that was migrated from V1, most of the pre-update backup is copying empty pages, and it is done while the server is down.

  • statev2.sqlite: 13.55 GB, 3,308,587 pages of 4 KiB.
  • freelist_count 1,952,895 (59% of the file), auto_vacuum 0.
  • t3 doctor --dry-run: "Database: 12.62 GiB, 7.45 GiB reclaimable."

The launcher's backupDatabaseOnce uses NodeSqlite.backup, a page-level copy, so it copies the freelist too.

Update today, 0.1.8-nightly.20261104.4165 → 0.1.8-nightly.20261108.4367 (UTC+2):

15:21:11.752  Server update prepared; handing off to the service launcher.
15:21:14.289  Relay client stopped
              (nothing logged; server offline)
15:24:27      candidate `t3 serve` process started (ps lstart)
15:24:29.357  Migrations ran successfully  [80_ScheduledTaskThreadMode]
15:24:29.819  Listening on http://0.0.0.0:3773

That is 3 min 13 s offline. A one-step sqlite3 .backup of the same file on the same disk (ext4, NVMe, no reflink) took 173 s, so the backup accounts for almost all of it. The previous 8 updates on this host took between 1 min 20 s and 3 min 45 s from "update prepared" to "Listening", rising as the file grew.

Expected

An update on a database that is mostly free pages should not copy those free pages while the server is down.

Suggestions

  1. Back up without the freelist, for example VACUUM INTO for the pre-update snapshot when freelist_count is a large share of page_count. That would be about 5.6 GB here instead of 13.5 GB.
  2. Tell already-migrated installs to run the reclaim. fix(server): migrated databases can return freed space to disk #30066 only fixes new imports. Installs that are already on auto_vacuum = 0 keep a growing freelist, and nothing points them to t3 doctor. The server, or the update flow (which is offline anyway), could offer "Reclaim database space" when auto_vacuum = 0 and the freelist is above some threshold.

Side observation (likely #24225)

While the service was down, the macOS desktop's SSH launcher started its own t3 serve --host 127.0.0.1 --port 3773 --base-dir ~/.t3 (the previous runtime, 0.1.8-nightly.20261104.4165) about every 62 s: three times in the gap. A local guard killed them. They did not seem to delay this update, but each one is a second process on the same database while the launcher is mid-backup.

Version / environment

Workaround

Running t3 doctor --fix ("Reclaim database space") off-hours. I'll add the before/after sizes and how long it took once it has run.

Activity

  1. shivamhwp commented on Nov 8, 2026

    @shivamhwp
    Collaborator

    [gpt-6.1-sol][Codex] on behalf of shivam.

    After your planned off-hours t3 doctor --fix, could you add the before/after database size and freelist count, the next update's offline interval, and the launcher version used for that update? Those measurements would show whether reclaiming the free pages resolves the delay on your host.

    I tested compact snapshots on a separate 12.5 GB T3 database with 53% free pages. VACUUM INTO produced a smaller valid backup but took longer than the full copy in both runs, so I discarded that change. Skipping freelist pages directly also proved unsafe after an interrupted WAL checkpoint; a guarded WAL-free prototype gave no consistent benefit on fragmented layouts. No patch has been shipped, and this issue remains open.

    Benchmark operations on corex, Node 26.12.1 / SQLite 3.54.0, NVMe/XFS with shared disk load, against ade473018d32003658b7d38693e083ee2e314bd8. These do not reproduce your ext4 timings or the complete desktop update.

    Backup benchmark and investigation limits

  2. MosheKIRO commented on Nov 9, 2026

    @MosheKIRO
    Author

    Result: t3 doctor --fix ("Reclaim database space") ran on 09.11 at 01:00 local, with the service paused for it.

    • Doctor took 120 s (01:00:00 → 01:02:00); the service was healthy again at 01:02:05.
    • statev2.sqlite: 13.55 GB → 5.45 GB (3,308,587 → 1,329,514 pages). Doctor reported "12.62 GiB -> 5.07 GiB, 0 superseded events removed".
    • freelist_count 1,948,919 → 223 (0 six hours later), auto_vacuum 0 → 2.

    The pre-update backup should now copy about 40% of what it copied before.

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