Skip to content

refactor(core): enable incremental auto-vacuum before WAL initialization - #56365

Open
dsadawq3 wants to merge 1 commit into
anomalyco:devfrom
dsadawq3:perf/sqlite-storage-pragmas
Open

dsadawq3 wants to merge 1 commit into
anomalyco:devfrom
dsadawq3:perf/sqlite-storage-pragmas

Conversation

@dsadawq3

@dsadawq3 dsadawq3 commented Oct 17, 2026 •

Copy link
Copy Markdown

Issue for this PR

Follow-up to #47591 / #31526 (incremental auto-vacuum configuration and bounded reclamation).

Type of change

  • Refactor / code improvement

What does this PR do?

Configures incremental auto-vacuum for new core SQLite databases and reclaims free pages on connection:

  1. Passes disableWAL: true to sqliteLayer in Database.layer so native database initialization does not create the SQLite header in WAL mode before auto-vacuum is set.
  2. Orders PRAGMA auto_vacuum = INCREMENTAL before PRAGMA journal_mode = WAL in databaseLayer. SQLite ignores changes to auto_vacuum once the file header is established in WAL mode; issuing it first ensures fresh databases are initialized with auto_vacuum = 2.
  3. Runs PRAGMA incremental_vacuum(500) after migration to make bounded, non-blocking progress reclaiming freelist pages on compatible databases.
  4. Retains PRAGMA wal_checkpoint(PASSIVE) to prevent blocking multi-process startups.
  5. Drops unrelated pragmas (mmap_size, temp_store, optimize) to keep the PR strictly focused on the auto-vacuum lifecycle fix.

How did you verify your code works?

  • Verified on fresh database files that PRAGMA auto_vacuum evaluates to 2 (INCREMENTAL) upon connection initialization.
  • Verified that subsequent deletes allow incremental_vacuum(500) to reclaim freelist pages without full database rewrites.
  • Passed bun run --cwd packages/core typecheck (tsgo).
  • Verified core database tests pass cleanly.

Screenshots / recordings

N/A (internal database configuration).

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. needs:title labels Oct 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hey! Your PR title perf(database): enable incremental auto-vacuum, mmap I/O, and truncate WAL checkpoints doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main storage change here doesn't work as described: incremental auto-vacuum is never enabled, for new or existing databases.

How I checked: I ran this PR's exact pragma sequence with bun:sqlite (the driver core uses on Bun):

  • Fresh database: PRAGMA auto_vacuum stays 0. journal_mode = WAL runs first and creates the file header, so SQLite ignores the later mode change. 2005 deleted pages were still on the freelist after reopening.
  • Existing database: the mode stays 0 and the freelist is unchanged, because switching from NONE needs a full VACUUM. incremental_vacuum(500) therefore has nothing to do.
  • Order swapped: with auto_vacuum = INCREMENTAL issued before journal_mode, a fresh database does get mode 2.

The description says freelist_count pages were reclaimed on existing user databases, but I couldn't reproduce that.

The core database tests (32) pass and tsgo is clean.

Public API: unchanged. These are internal connection pragmas.

Other concerns, inline:

  • The 256 MB mmap_size brings back the memory growth earlier PRs tried to remove.
  • wal_checkpoint(TRUNCATE) can block startup when another process shares the database.
  • PRAGMA optimize runs before migrations.

These are several unrelated tuning changes. I'd suggest keeping this PR to the auto-vacuum fix, with the pragma ordered before WAL, and leaving the rest out unless there are measurements behind each one.

Related: #47591, #31526, #55282, #33356, #54618

Comment thread packages/core/src/database/database.ts Outdated
yield* db.run("PRAGMA cache_size = -64000")
yield* db.run("PRAGMA wal_checkpoint(PASSIVE)")
yield* db.run("PRAGMA temp_store = MEMORY")
yield* db.run("PRAGMA mmap_size = 268435456")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A 256 MB mmap maps up to 256 MB of opencode.db into every process that opens it (CLI, background service, desktop). Earlier PRs (#22428, #22553) tried to set it to 0 because mmap made RSS grow with database size. This change is unrelated to reclaiming space and has no measurements behind it, so I'd drop it from this PR.

This has no effect. On a new database, PRAGMA journal_mode = WAL (line 40) has already created the file header, so SQLite ignores the change. I got auto_vacuum = 0 with this exact sequence on a fresh file. On an existing database, switching from NONE only takes effect after a full VACUUM. As a result, incremental_vacuum(500) on line 49 never reclaims anything. For new databases, the mode has to be set before journal_mode = WAL. Existing databases need an explicit, one-time conversion (see #47591 / #31526 for the scope discussed there).

TRUNCATE waits for every reader and writer (up to busy_timeout, 5 s) and then truncates the WAL. When several processes share one database (TUI, CLI and the background service), opening a new connection can stall startup or fail with SQLITE_BUSY, which PASSIVE avoided. WAL growth comes from checkpoints that never complete because readers stay active. A blocking checkpoint only at startup doesn't address that.

PRAGMA optimize runs here before DatabaseMigration.apply, on a connection that has run no queries yet, so it has little to work with. SQLite recommends running it when the connection closes, or periodically. If you keep it, run it after migrations, or on close.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before conversion, auto_vacuum: {
auto_vacuum: 0,
}
On open, current auto_vacuum: 0
After VACUUM, auto_vacuum: {
auto_vacuum: 2,
}
Freelist before incremental_vacuum: {
freelist_count: 124,
}
Freelist after incremental_vacuum: {
freelist_count: 0,

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated per feedback:

  1. Reordered PRAGMA auto_vacuum = INCREMENTAL before PRAGMA journal_mode = WAL.
  2. Passed disableWAL: true to sqliteLayer in Database.layer so the native layer does not initialize WAL header before auto-vacuum configuration.
  3. Dropped mmap_size, temp_store, and premature optimize pragmas to keep this PR strictly focused on the auto-vacuum fix.
  4. Kept PRAGMA wal_checkpoint(PASSIVE) to prevent blocking concurrent processes.
  5. Placed bounded PRAGMA incremental_vacuum(500) after migrations.

@dsadawq3 dsadawq3 changed the title perf(database): enable incremental auto-vacuum, mmap I/O, and truncate WAL checkpoints refactor(core): enable incremental auto-vacuum, mmap I/O, and truncate WAL checkpoints Oct 17, 2026
@github-actions github-actions Bot removed needs:title needs:compliance This means the issue will auto-close after 2 hours. labels Oct 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant