Commit Graph
19 Commits
Author SHA1 Message Date
Dmitriy Kovalenko 9e77fb4ad5 Make SQLite schema migrations safe for concurrent startup (#59015)
## Why

SQLx's SQLite migration lock is a no-op, allowing concurrent database openers to race between schema version checks and migration changes.

## What changed

Run schema checks and migrations in one transaction, retrying SQLite busy errors from a fresh snapshot. Databases with a current schema need only a read lock, including on NFS. Include the error cause chain when the app server reports SQLite initialization failures.

## Testing

Add regression tests for eight concurrent database openers applying each migration once and for reopening a current schema while another connection holds a writer reservation with NFS settings.

GitOrigin-RevId: 38004d58a5c637ac1b2be53d07fd9a4c8efec7ab
2026-11-03 00:04:40 +00:00
Dmitriy Kovalenko ca8b7a0737 Allow NFS database migration with shared file permissions (#57226)
## Why

NFS database migration rejected files with group or other access. Allow accessible databases to migrate while making the effects on file access explicit.

## What changed

- Preserve file mode bits during migration while requiring owner read and write permissions on replacement files.
- Before CLI consent, disclose that replacements belong to the current user and do not copy the original owner, group, or extended ACLs. Require explicit confirmation for files with permissions other than `0600`.
- Warn at Linux TUI startup when NFS databases need migration and permission confirmation. Inspect journal mode without accessing WAL shared memory or releasing another connection's locks.
- Allow five seconds to acquire the exclusive database lock during migration.

## Testing

Add CLI coverage for consent, access disclosures, and migration of `0644` files. Extend state tests for permission preservation, rejection of missing owner access, and interrupted migration recovery; snapshot the startup warning.

GitOrigin-RevId: 2d9c3663172c3e631a787dc1dc76db95c39e6aa1
2026-10-26 20:16:30 +00:00
Dmitriy Kovalenko 480ab2d006 Add managed enforcement of DELETE journaling on NFS (#57225)
## Why

Existing databases retain their journal mode, so selecting `DELETE` for new NFS databases leaves existing WAL databases unchanged.

## What changed

- Add the managed `unsafe_enforce_sqlite_journal_mode` requirement to migrate existing NFS databases before opening writable pools and require `DELETE` on each connection.
- Share migration code between startup and `codex migrate-nfs-db`, preserving WAL backups and resuming interrupted replacements, including lazily opened databases.
- Hold the migration lock through pool initialization and keep migration failures out of automatic corruption recovery.
- Preserve local logs behavior and skip legacy NFS logs cleanup when enforcement is enabled.

## Testing

Add coverage for requirement precedence, database settings, data and backup preservation across restarts, interrupted replacement recovery, refusal of active WAL databases, migration lock waiting, and local logs remaining in WAL mode.

GitOrigin-RevId: da41ec1b58b1ee8092591cdf8b358280928ff9d4
2026-10-26 20:10:45 +00:00
Dmitriy Kovalenko 0d7f3ea03b Initialize SQLite databases with DELETE journaling on NFS (#56552)
## What changed

Choose `DELETE` journaling for new SQLite databases on NFS and `WAL` elsewhere, using the database's own directory to detect the filesystem. Probe a separately configured logs directory independently of the state directory.

Preserve journal modes for databases with an existing schema, and apply `synchronous = EXTRA` when initialization results in `DELETE` mode.

## Testing

Extend SQLite tests to cover NFS and non-NFS initialization, interrupted initialization, lazy pool connections, separate logs directories, and preservation of existing settings under a write lock.

GitOrigin-RevId: bde35861e517881b9ce23b28b199475cee70bd76
2026-10-23 18:30:44 +00:00
Dmitriy Kovalenko 17a0e6f63f Route SQLite logs to an explicitly configured log_dir (#55978)
## What changed

- Store `logs_2.sqlite` in `log_dir` when explicitly configured, unless a managed `sqlite_home` requirement applies. Keep other databases in `sqlite_home` and preserve the existing SQLite log location when `log_dir` is unset.
- Initialize and lock the selected log directory only when opening its database, so an unavailable directory does not prevent authentication commands from loading configuration. Share the cleanup lock across configuration clones and allow failed initialization to be retried.
- Leave existing logs in place. Read legacy logs for diagnostic submissions when the current query returns no logs, attaching them as `codex-logs-legacy.log`. Continue retention cleanup and page reclamation for both locations, with independent maintenance locks and filesystem checks.

## Testing

Add coverage for configuration routing and managed requirements, deferred initialization and lock lifetimes, authentication with an unavailable log directory, legacy log attachments, and cleanup of current and legacy databases.

GitOrigin-RevId: 48216694596d738eff1b3c5eef6c4e7ada6d0137
2026-10-21 19:53:00 +00:00
Dmitriy Kovalenko 9c84e89ce0 Skip SQLite page reclamation on Linux NFS mounts (#54417)
## Why

SQLite page reclamation on NFS creates I/O load and contention.

## What changed

Detect whether `sqlite_home` is on NFS on Linux and skip reclamation before opening a maintenance connection. Cache successful filesystem probes across `SqliteConfig` clones while allowing failed probes to retry.

## Testing

Add tests for shared probe caching and recovery after a missing directory is created. Update reclamation coverage to expect no reclaimed pages or file shrinkage on NFS.

GitOrigin-RevId: 4f627cabc6ad712171654c3f8b8659d2f632bbba
2026-10-15 21:07:40 +00:00
Dmitriy Kovalenko 1ef30bcbe7 Preserve existing SQLite journal modes on startup (#54389)
## Why

Opening an initialized database in `DELETE` journal mode previously attempted to convert it to `WAL`, which could fail while another connection held a write lock.

## What changed

- Initialize `WAL` and incremental auto-vacuum only for databases without a schema, including files left by interrupted initialization.
- Preserve journal and vacuum modes for initialized databases, use `synchronous = EXTRA` for `DELETE` connections, and retain `NORMAL` for `WAL`.
- Close failed initialization pools before retrying and restrict background page reclamation to `WAL` databases.

## Testing

Extend SQLite tests to cover interrupted initialization, opening `DELETE` databases under a write lock across all pool connections, persistence after reopening, and lock release after failed initialization.

GitOrigin-RevId: 23acecfe472618dde8545815e9a1058a51591235
2026-10-15 19:15:04 +00:00
willwang-openai 7ae8319917 Retry SQLite operations on transient writer contention (#54369)
## Why

Competing SQLite writers can cause database opening and writes to fail with `SQLITE_BUSY`. Deferred write transactions can also encounter contention after taking a read snapshot.

## What changed

- Add up to two retries with 100 ms and 200 ms delays for SQLite busy errors during pool opening, writer admission, and standalone writes.
- Use a shared `BEGIN IMMEDIATE` helper across state, thread history, and agent message board writes to acquire the writer slot before reading.
- Preserve bound arguments when retrying standalone writes, and commit writes using `RETURNING` before exposing their results. Retry transaction admission without replaying an already-started transaction.

## Testing

Add tests using real SQLite writer contention to verify recovery after a competing writer releases its lock. Queue tests cover enqueue, update, and delete, checking preserved payloads, single revision increments, and no duplicate messages or unintended deletions.

GitOrigin-RevId: ac5bd31a87c67cc47fb385bf3fa5435a54ff70f0
2026-10-15 18:06:57 +00:00
Dmitriy Kovalenko 3620b2caf8 Detect SQLite corruption during startup and preserve recovery backups (#49701)
## Why

A database can open successfully while containing corruption that startup previously missed. Detecting it before runtime initialization completes allows recoverable databases to be rebuilt while preserving the damaged files.

## What changed

- Run `PRAGMA quick_check(1)` when opening writable databases, with a 100 ms scan budget. Share attempts across cloned `SqliteConfig` instances by file identity so repeated opens skip validation and replacement files are checked again.
- Back up and rebuild runtime databases that support recovery. Report corruption without rebuilding thread history or other databases without an automatic recovery policy. Treat interrupted or locked checks as incomplete rather than confirmed corruption.
- Collect startup backups across recovery retries and include their locations in the app server warning. Explain that saved conversations can restore the thread list and history, while some database-only metadata may be unavailable.
- Record confirmed corruption in `codex.sqlite.corruption.count`.

## Testing

Add tests for recovery policies, backup preservation, validation caching and concurrency, cancelled and interrupted checks, and locked databases. Add app server coverage for recovery warnings and saved conversation restoration, a TUI warning snapshot, and message board post preservation.

GitOrigin-RevId: 43e26328888e0301def642d69bd324acee867566
2026-09-30 18:29:46 +00:00
Benjamin Carlsson c2d2f422e6 Preserve SQLite vacuum modes and surface pool initialization errors (#49102)
## Why

Changing an existing database from `FULL` to `INCREMENTAL` auto-vacuum requires a writer lock and can block pool initialization. SQLx also retries connection initialization failures, masking the original error as `PoolTimedOut`.

## What changed

Preserve existing auto-vacuum modes when opening writable pools, while continuing to initialize empty databases with incremental auto-vacuum. Return the first connection initialization error directly to the caller, retaining SQLx's normal error handling for later lazy connections.

## Testing

Add regression tests for fresh database settings, opening an existing `FULL` auto-vacuum database under a write lock, and preserving the WAL conversion lock error while allowing a subsequent open to succeed after the lock is released.

GitOrigin-RevId: b586c68e43a5a0d3b47268378716919d01b749cf
2026-09-28 23:44:39 +00:00
Dmitriy Kovalenko 33a0f766a6 Reclaim unused SQLite log database pages in the background (#49069)
## Why

Deleting logs leaves free SQLite pages available for reuse without shrinking the database file. Reclaim excess free space while preserving a reserve for future writes.

## What changed

- Start a background incremental-vacuum worker with `StateRuntime`, enabling reclamation only for the logs database. Use a per-home file lock to select one worker and wait for it to finish during shutdown.
- Reclaim pages when at least 64 MiB and 25% of the database are free, preserving a reserve of at least 16 MiB or 10% of the database.
- Use short transactions with page and cooperative time budgets, defer work on contention, and reduce batch sizes with exponential backoff when interrupted passes make no progress.
- Record reclamation counts, durations, and reclaimed pages in telemetry.

## Testing

Add a regression test that interrupts a vacuum commit, verifies foreground writes can resume without data loss, then confirms a resumed pass shrinks the database and passes `PRAGMA integrity_check`.

GitOrigin-RevId: 8421f87b0aeef6540eda5690c84216906673c72e
2026-09-28 20:19:10 +00:00
jif 46fdd5ef39 Avoid SQLite stalls from connection setup and stderr span logging (#49032)
## Why

Setting `auto_vacuum` on every connection can wait for a writer lock even when the mode is unchanged. Logging every span entry and exit can also block SQLx workers on undrained stderr while they hold a write transaction.

## What changed

- Set incremental vacuum only for empty databases or databases using full auto-vacuum, before enabling WAL. Preserve existing nonempty databases with auto-vacuum disabled.
- Emit span creation and closure events in app-server text and JSON stderr logs, preserving busy/idle timings and explicit events while omitting span entry and exit records.

## Testing

Add regression tests for opening and expanding writable pools while another connection holds a writer lock, and for transaction completion with undrained stderr in both log formats. Verify vacuum modes and retained log diagnostics.

GitOrigin-RevId: 40d4ef32c7efc49f4240b987277ba45551259335
2026-09-28 17:00:25 +00:00
jif 3f76e88a48 Add configurable memory versions with isolated storage (#43797)
## What changed

- Add `memories.version` with `v1` as the default and `v2` as an opt-in selection.
- Route memory generation, summaries, and retrieval tools through the selected version. Store `v2` artifacts in `memories_v2` and lazily create a separate SQLite database, keeping jobs and outputs isolated while sharing the thread catalog.
- Preserve the initial memory version across extension config updates so summaries and retrieval tools use the same namespace.
- Clear both versions on memory reset and remove thread memory from both stores on thread deletion. Recognize both artifact roots in shell usage telemetry.

## Testing

Add regression coverage for version defaults and validation, independent job claims and outputs, deletion and reset across versions, and shell usage classification for both memory roots.

GitOrigin-RevId: aa799bf87ab6ec10c8f5668213931e57547d080b
2026-09-08 10:55:48 +00:00
chess 32ce703a80 Bound SQLite integrity checks for diagnostic attachments (#40688)
## Why

Diagnostic uploads skip the doctor attachment if report generation exceeds its
overall timeout. SQLite integrity scans can be delayed by large databases or
lock contention.

## What changed

- Give each database integrity scan a one-second deadline when collecting a
  diagnostic attachment.
- Interrupt scans and lock waits at the deadline, preserve any corruption rows
  already returned, and report an incomplete clean scan as a warning.
- Keep direct `codex doctor` integrity checks unbounded.

## Testing

- Cover interrupted scans, lock waits, retries without a deadline, and
  corruption results returned before interruption.

GitOrigin-RevId: 4817fb459ac53953669a9ba5f03397a6fc82c72e
2026-08-25 20:09:55 +00:00
efrazer-oai b87981a510 Add durable per-thread user submission queues (#36952)
## What changed

- Add a storage-neutral `QueueStore` interface backed by a dedicated SQLite
  database for ordered, thread-scoped user submissions.
- Support enqueueing, paginated listing, editing, deletion, and atomic
  reordering, with a limit of 100 pending items per thread.
- Remove queued submissions when their thread is deleted.

## Testing

- Cover concurrent inserts, ordering and pagination, edits, deletion,
  cross-thread isolation, queue limits, and thread cleanup.

GitOrigin-RevId: 0a8e95ac71137afc6f3a039d4b5fc8603110c891
2026-08-04 19:23:29 +00:00
Adam Perry @ OpenAI 50a7328f50 Enforce centralized SQLite connection creation (#35828)
## Why

Direct SQLx constructors can bypass the shared SQLite configuration in
`codex-state`.

## What changed

- Deny SQLx pool, connection, and database creation methods through the
  workspace Clippy configuration for both Cargo and Bazel builds.
- Exempt `codex-rs/state/src/sqlite.rs`, the centralized connection shim, from
  the lint.
- Document that the deny list must be audited when upgrading SQLx.

GitOrigin-RevId: e20d7e83095727ac446347157782175062a100fc
2026-07-28 20:49:22 +00:00
Adam Perry @ OpenAI 946ed315a4 Centralize SQLite connection configuration (#34808)
## What changed

- Add `SqliteConfig` to own the resolved SQLite home, runtime database paths,
  and shared read/write and read-only pool settings.
- Pass the configuration through state and local thread-store consumers,
  replacing standalone path helpers and duplicated connection setup.

GitOrigin-RevId: 7351b08da94b5c8b6c0bbe492f86aeeca0699d3c
2026-07-22 18:45:47 +00:00
Adam Perry @ OpenAI 81e89fa5af Require absolute paths for test SQLite configuration (#34411)
## Why

`SqliteConfig` stores its home as an `AbsolutePathBuf`, but its test constructor
previously accepted a `PathBuf` and checked the absolute-path invariant at
runtime.

## What changed

- Make `SqliteConfig::new_for_testing` accept an `AbsolutePathBuf` directly,
  removing its fallible conversion and `expect`.
- Update SQLite test setup to convert temporary directory paths with
  `PathExt::abs` at each call site.

GitOrigin-RevId: 93585b9aea805e2449b3465ac20eeb39417ed555
2026-07-20 20:54:08 +00:00
Adam Perry @ OpenAI 6bd3f5e3db Centralize SQLite connection configuration (#33938)
## What changed

- Add `SqliteConfig` as the shared entry point for read-write and read-only SQLite pools.
- Apply consistent WAL, synchronization, auto-vacuum, busy-timeout, logging, and pool-size settings to writable Codex databases.
- Route state runtime, audit, CLI, and test database connections through the shared configuration.

## Testing

- Run migration tests against temporary on-disk databases opened through `SqliteConfig`, including the concurrent-writer repair case.

GitOrigin-RevId: e3946b98bde04c47574532ac8b1a7bb2b03edd97
2026-07-18 02:06:13 +00:00