[Bug]: 'Close Directory and Delete Cache' Fails on Windows #27
Labels
No labels
Kind/Bug
Kind/Feature
Priority/High
Priority/Medium
Reviewed/Confirmed
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ai-collab/bulk-image-organizer#27
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
App version
0.1.1
Platform
Windows
Steps to reproduce
Close Directory and Delete CachebuttonExpected behavior
The working directory is closed and the cache database is deleted from the disk for that directory
Actual behavior
'Delete Cache' dialog appears with error stating the process cannot access the file because it is being used by another process [DB_FILE_PATH]
Additional context
No response
Pushed a fix on branch
0.1.37/issue-27-34-close-directory(not yet merged):mmap_size. On Windows, a memory-mapped WAL file can stay briefly locked after its connection closes — the same reasoning the share-sync pragmas already accounted for (_apply_share_file_pragmassetsmmap_size = 0).mmap_sizeis now0on Windows for the local cache too (it's a read-perf optimization only).checkpoint_and_release(), called right before "Close Directory and Delete Cache" attempts to unlink the file: it opens one last short-lived connection, runsPRAGMA wal_checkpoint(TRUNCATE), and switches back to a rollback journal so no-wal/-shmcompanions are left to interfere with deletion.Covered by a new unit test (
test_checkpoint_and_release_removes_wal_companions). I wasn't able to verify on actual Windows hardware, so I'd appreciate confirmation once this lands in a build — leaving the issue open until then.Update — found the actual root cause. The mmap/WAL-checkpoint mitigation in my previous comment was a reasonable defense-in-depth measure but not the real bug. Thanks for the
handle64.exedump (run right after the timeout error) — it showed ~25 simultaneously open handles to the same.bulk_image_organizer.dbfile (plus ~16-waland a couple-shm), still open well past the 10s retry window, plus handles toconfig.db/config.db-wal/config.db-shmtoo. That's a genuine handle leak, not a transient Windows lock.Root cause: every
WorkingDBandGlobalConfigDBread/write opened a freshsqlite3.Connectionvia a barewith self._connect() as conn:.sqlite3.Connection's own context manager only commits/rolls back the transaction — it never calls.close(). The code relied on CPython reference-counting to release the connection once it went out of scope. I reproduced this locally: looping 200 short-lived connections opened that way leaked ~100 file descriptors even after an explicitgc.collect(), while the same loop with an explicitconn.close()stayed flat. So every scan, thumbnail read, pref lookup, etc. across a session was leaking a handle to the cache file — which is exactly why the file was still "in use" no matter how long the retry loop waited.Fix (pushed to the same branch,
0.1.37/issue-27-34-close-directory): both DB classes already had a properly-closingtransaction()context manager; every bare_connect()call site now routes through it instead. Added a regression test in each (test_repeated_reads_do_not_leak_connection_handles,test_repeated_pref_reads_do_not_leak_connection_handles) that fails against the old pattern (confirmed: 62 leaked fds) and passes against the fix.This is a much more confident fix than the earlier mmap mitigation, though I still can't verify on real Windows hardware — would appreciate a retest once it's in a build.
Confirmed fixed and validated. Resolved on branch
0.1.37/issue-27-34-close-directory(PR tomainincoming) by closing every SQLite connection explicitly instead of relying on garbage collection — see the previous comment for the root-cause writeup. Closing.