Skip to content

fix: close sqlite3 staging connections before os.replace (Windows WinError 32) - #452

Open
julingwu wants to merge 5 commits into
douglasmonsky:mainfrom
julingwu:fix/windows-sqlite-close
Open

julingwu wants to merge 5 commits into
douglasmonsky:mainfrom
julingwu:fix/windows-sqlite-close

Conversation

@julingwu

Copy link
Copy Markdown

Fixes #418.

with sqlite3.connect(staging) as connection: commits but never closes the
connection. sqlite3.Connection sits in a reference cycle, so its file handle
outlives the block, and the following os.replace(staging, target) then fails on
Windows with:

PermissionError: [WinError 32] The process cannot access the file because it is
being used by another process: '.../.codex-usage-kernel-v1.sqlite3.building-<hash>'

POSIX allows renaming an open file, which is why Linux/macOS CI never caught this.
On Windows both setup and refresh fail deterministically and leave a partial
.building-* file behind each time.

Minimal standalone reproduction (no project code involved):

import os, sqlite3
staging, target = ".a.sqlite3.building", "a.sqlite3"
with sqlite3.connect(staging) as connection:
    connection.execute("create table t(x)")
os.replace(staging, target)     # Windows: WinError 32 ; POSIX: succeeds

Change

12 explicit close() calls across 5 files, each placed after the with block and
before the staging file is renamed/copied:

file why
kernel/database.py analytical init (os.replace) + validator / schema-header readers
kernel/operational.py operational sidecar init (os.replace)
kernel/content.py content DB init (os.replace)
kernel/ingest.py staging clone path returned for later install
kernel/application/jobs.py reader helper (leaked handle under long-running service)

contextlib.closing(...) would also work, but it drops the implicit
commit-on-exit that the surrounding code relies on, so an explicit close() is
the smaller change.

Verification (Windows 11, Python 3.12, 0.28.0)

  • codex-usage-tracker setup{"cache_root": "...", "state": "absent"}, no leftovers
  • codex-usage-tracker refresh{"disposition": "started", ...}
  • codex-usage-tracker statusstate: active, generation: 1,
    history_coverage: 28 sources / 11.8 MB / preset recent_30d
  • MCP handshake (codex-usage-tracker _mcp): initialize
    serverInfo {name: codex-usage-tracker, version: 0.28.0}; tools/list → 6 tools

Suggested guardrail

A regression test that builds a staging DB through the same helper and asserts the
file is then renameable passes on Linux both before and after the fix, so it would
not catch this class. Consider a windows-latest job (or at least an
os.name == "nt"-marked variant).

…uglasmonsky#418)

`with sqlite3.connect(...)` commits but does not close; the leaked handle makes
the following os.replace fail with WinError 32 on Windows (POSIX is unaffected,
so CI never caught it). Explicit close() before the rename/copy.
…uglasmonsky#418)

`with sqlite3.connect(...)` commits but does not close; the leaked handle makes
the following os.replace fail with WinError 32 on Windows (POSIX is unaffected,
so CI never caught it). Explicit close() before the rename/copy.
…uglasmonsky#418)

`with sqlite3.connect(...)` commits but does not close; the leaked handle makes
the following os.replace fail with WinError 32 on Windows (POSIX is unaffected,
so CI never caught it). Explicit close() before the rename/copy.
…uglasmonsky#418)

`with sqlite3.connect(...)` commits but does not close; the leaked handle makes
the following os.replace fail with WinError 32 on Windows (POSIX is unaffected,
so CI never caught it). Explicit close() before the rename/copy.
…uglasmonsky#418)

`with sqlite3.connect(...)` commits but does not close; the leaked handle makes
the following os.replace fail with WinError 32 on Windows (POSIX is unaffected,
so CI never caught it). Explicit close() before the rename/copy.

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.

Windows: SQLite staging database handle remains open during setup and refresh

1 participant