Repository navigation
Fix #79: Fall back from SQLite WAL mode when the filesystem can't support it - #324
zwanski-start wants to merge 1 commit into
Conversation
…ort it SQLite accepted PRAGMA journal_mode=WAL on filesystems where the -shm shared-memory index can't be mmapped (WSL /mnt/c, Docker/VM shared folders, FUSE, eCryptfs), then failed the next query with 'disk I/O error', killing the orchestrator and MCP server. - Probe WAL on a throwaway database in the DB directory before using it - Detect more unsafe filesystems (9p, drvfs, virtiofs, fuse.*, ...) and match the most specific mount point instead of any string prefix - Convert a database left in WAL mode using EXCLUSIVE locking, which keeps the WAL index in heap memory, so it can be opened again - Add AUTOFORGE_SQLITE_JOURNAL_MODE override Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🔍 QA report: ✅ PassedTester: Dennis (cubefarm agent, in a fresh session) · Round: 1 · Author: Guido Reproduced issue #79 for real: on a FUSE direct_io mount (no shared mmap, like WSL /mnt/c or virtiofs), master's create_database() and the feature MCP server die with the exact
🧪 Commands run
No browser screenshots were taken in this round. Posted by cubefarm · merges automatically once GitHub's checks pass |
Closes #79
Root cause
The traceback has nothing to do with GLM.
create_database()runsPRAGMA journal_mode=WALonfeatures.db, and SQLite accepts it. On filesystems that can't memory-map the WAL-shmindex, the next statement (herePRAGMA table_info("features")insidecreate_all) then fails withsqlite3.OperationalError: disk I/O error. Examples are WSL/mnt/c(9p/drvfs), Docker or VM shared folders (virtiofs, vboxsf, grpcfuse), FUSE mounts and eCryptfs-encrypted homes. The orchestrator and the feature MCP server both die at startup, so features never start.The old check (
_is_network_path) only knew nfs, cifs, smbfs and sshfs. It also matched mount points by plain string prefix, so/mnt/nfsalso matched/mnt/nfs-local.Changes (
api/database.py)_probe_wal_support()tries it on a throwaway.wal_probe_<pid>.dbin the DB directory: write on one connection, read on a second one so the shm index is used, then clean up. If that fails, the code usesDELETEmode. The result is cached per directory./proc/mountsescapes, and adds 9p, drvfs, virtiofs, vboxsf, vmhgfs, prl_fs, ecryptfs, smb3, ceph, glusterfs, lustre, afs and allfuse.*types.fuseblk(local ntfs-3g) is left out on purpose.locking_mode=EXCLUSIVE, which keeps the WAL index in heap memory, then checkpointed and switched to the chosen mode. Data is kept.AUTOFORGE_SQLITE_JOURNAL_MODEenv var (WAL/DELETE/TRUNCATE/PERSIST) forces a mode. Invalid values are ignored with a warning, since the value goes into a PRAGMA. It is documented in.env.example.Verification
test_database_journal_mode.py(16 tests) covers mount matching (longest mount wins, whole path component, escaped spaces), unsafe-FS detection, the probe passing on local disk and failing on a simulateddisk I/O errorwith cleanup and caching, WAL on local disk, fallback to DELETE, the env override and invalid values, and converting an existing WAL database to DELETE with its rows kept.ruff check .andmypyon the changed files: clean.python test_security.py: all pass.pytest test_client.py test_dependency_resolver.py test_rate_limit_utils.py: pass.sqlite3.connectraisedisk I/O error.Assumptions
/home/william/...could be WSL, a container bind mount, or an encrypted home). That's why the main fix is a runtime probe rather than relying only on an FS-type list.🤖 Generated with Claude Code