Repository navigation
Fix #79: Fall back from SQLite WAL mode on 'disk I/O error' - #268
zwanski-start wants to merge 2 commits into
Conversation
WAL needs a shared-memory (-shm) mmap that WSL /mnt/* drives (9p/drvfs), VM/container host shares and FUSE mounts often don't support, so opening features.db failed with 'disk I/O error' and the orchestrator crashed. - Detect more unsafe filesystems (9p, drvfs, vboxsf, virtiofs, smb3, fuse.*, ...) and match mount points on path boundaries, longest match wins, with /proc/mounts octal escapes decoded - Probe the database after setting the journal mode; on disk I/O / locking protocol errors, convert the file to DELETE mode (via EXCLUSIVE locking so a stuck WAL database can still be opened) and continue - Add tests for mount detection and the fallback path Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🔍 QA report: ❌ FailedTester: Dennis (cubefarm agent, in a fresh session) · Round: 1 · Author: Barbara In a single process the fallback works: with SQLite's -shm mmap made to fail (reproducing the issue's 'disk I/O error'), a fresh or existing WAL features.db is converted to DELETE mode and stays usable, and lint, types and tests are clean. Two real cases still fail. (1) A features.db already in WAL mode on a filesystem the PR now detects as unsafe (e.g. WSL /mnt/c 9p) still crashes with 'disk I/O error', because the fallback only runs when the chosen mode was WAL. Master leaves exactly that file behind after hitting #79. (2) On a filesystem that isn't detected, every new process (each agent's MCP server, the orchestrator, the UI server) switches the file back to WAL and then falls back. Under concurrency the new process failed with 'database is locked' in 14 of 25 attempts, and an already-running process got 'disk I/O error' on every query from that point on in 2 of 4 runs. That is the parallel-mode setup from the issue.
🧪 Commands run
🔧 What needs fixingIn api/database.py create_database():
No browser screenshots were taken in this round. Posted by cubefarm · sent back to the developer for fixes |
… and probe WAL on a throwaway file - Run the WAL->DELETE recovery regardless of the chosen journal mode, and convert a features.db whose header says WAL before opening it in DELETE mode (e.g. one left behind on WSL /mnt/c by an older version). - Probe WAL support once per directory per process on a throwaway .wal-probe-<pid>.db instead of switching the shared features.db to WAL on every process start, which caused 'database is locked' and 'disk I/O error' for concurrent processes in parallel mode. - Add tests for both cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🔍 QA report: ✅ PassedTester: Ada (cubefarm agent, in a fresh session) · Round: 2 · Author: Ken Both round-1 failures are fixed. A WAL-mode features.db on a filesystem detected as unsafe is now converted to DELETE mode and opens with its data intact; master crashes with 'disk I/O error' on the same file. On an undetected filesystem where WAL fails, a one-time probe on a throwaway file means features.db is never switched to WAL, and a 25-process concurrency run against a live process had 0 errors in any process (round 1: 'database is locked' in 14/25 and 'disk I/O error' in already-running processes). Lint, tests, server and UI smoke tests are clean, and the requested tests are present and pass.
🧪 Commands run
No browser screenshots were taken in this round. Posted by cubefarm · merges automatically once GitHub's checks pass |
…' and probe WAL on a throwaway file Adaptations: the journal-mode choice and fallback are now public helpers in api/database.py (select_journal_mode, fall_back_to_rollback_journal, is_wal_io_error), used by create_database and by structured_logging._create_log_schema, so logs.db follows the same WAL rules as features.db (probe, stuck-WAL conversion, disk I/O fallback). Resolved the sqlalchemy import conflict with my-features' typed ORM imports. New tests for logs.db. Integrates AutoForgeAI#268 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeH26pcsqoxPb3CDs8g1VN
Problem
The traceback in #79 isn't caused by GLM or MCP. It's SQLite failing with
disk I/O erroron the first schema read offeatures.db(PRAGMA main.table_info("features")insidecreate_all). The orchestrator then crashes, so the coding phase can't start. The initializer had already written the features, which is why the feature count showed up.create_database()always turns on WAL unless the path is on NFS/CIFS/SMB/sshfs. WAL needs to mmap a shared-memory-shmfile. Many filesystems don't support that, so they fail this way: WSL/mnt/c(9p/drvfs), VirtualBox/VMware/Parallels shares, Docker virtiofs, and most FUSE mounts. The old mount check also matched plain string prefixes (/mnt/cmatched/mnt/code), ignored octal-escaped mount points, and returned on any match rather than the most specific mount.Changes (
api/database.py)_fs_type_for_path()matches mount points on path boundaries, picks the longest match and decodes\040escapes. The list of unsafe filesystems now also covers 9p, drvfs, vboxsf, vmhgfs, virtiofs, smb3, ceph, glusterfs and similar, plusfuse.*(but notfuseblk). Projects on these filesystems usejournal_mode=DELETEfrom the start._apply_journal_mode()readssqlite_master, which forces the WAL index to open. If that raisesdisk I/O errororlocking protocol, we log a warning, switch the file to DELETE mode and carry on. The switch opens the file inEXCLUSIVElocking mode, which keeps the WAL index in heap memory, so a database already stuck in WAL mode can still be opened. Any other error is re-raised as before.Verification
test_database_journal.py(30 tests): mount parsing (WSL drive, path boundary, longest match, escaped spaces), the filesystem lists, WAL on a local dir, the fallback path (simulateddisk I/O error, then the DB ends up in DELETE mode and works), converting a real WAL database, and other errors not being swallowed.ruff check .passes,mypy api/database.pyis clean,python test_security.pypasses,test_dependency_resolver.pypasses.Assumptions / notes
busy_timeoutandBEGIN IMMEDIATEhandling still apply, and this only affects filesystems where WAL doesn't work anyway.Closes #79
🤖 Generated with Claude Code
QA round 1 fixes
disk I/O errorfallback now runs no matter which journal mode was chosen..wal-probe-<pid>.db, and features.db is set to WAL only if that probe works. A per-process file name keeps concurrent probes from colliding. If the fallback triggers anyway, the cached result is set to "unsupported"._is_network_path=True+ first DELETE apply fails ends up in DELETE mode and usable; with the probe failing, an SQL trace shows noPRAGMA journal_mode=WALis ever issued against features.db.disk I/O error) means DELETE mode. This is the safe default.