Repository navigation
Data: Shapefile cache releases unlock explicitly, fixing flaky cache tests (#687) - #704
Conversation
A flock is released only when every descriptor sharing its open file description is closed. A process spawned on another thread (the cache child-process tests, GDAL, DuckDB) holds duplicates of all descriptors until it execs, so dropping a lease or the directory lock left it held for that window: a release then deferred its cleanup, a sweep counted the entry as pinned, and a re-reservation met WouldBlock. Lock files are now wrapped in a guard that unlocks on drop; unlocking acts on the open file description, duplicates included. A regression test holds a duplicate descriptor across each release.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
Code reviewI found no bugs or security issues in this PR. It adds a I read the changed code against the lock call sites in Bugs
Security
Performance
Quality
AGENTS.md
Change sizeCompared: the whole PR against main (merge base 94bfaeb to PR head e7bfa42)
|
📖 Docs preview
GitHub Pages takes a minute or two to publish a new commit. |
Root cause
The Shapefile index cache (#668) guards entries and its directory with
flock. Aflockbelongs to the open file description and is released only when every descriptor sharing it is closed. When another thread spawns a process (the cache child-process tests here; GDAL, DuckDB or a plugin in the app), the child holds duplicates of all of the parent's descriptors until it execs. Dropping aFileduring that window left the lease or directory lock held, so:cache_concurrent_builder_uses_private_index_without_replacing_reader),maintain(root, 0)left bytes behind (damaged_shapefiles_are_errors_or_skips_not_panics,cache_shared_readers_and_oversized_indexes_are_protected_until_release,cache_lru_touches_reopened_indexes_and_evicts_oldest_idle),reserve_indexdropped a lease lock and immediately re-took it withtry_lock, which metWouldBlock(readyat tests.rs:49 incache_unix_release_does_not_mutate_a_root_that_became_unsafeand others).The retry worker and the thread-local test root were not the cause: the worker only maintains roots it is handed and serializes on the directory lock, and failures vanished when the test ran alone.
Fix
Lock files are wrapped in a small
Flockguard that callsunlock()on drop. Unlocking acts on the open file description, so it releases the lock even while a spawned child still holds a duplicate, and it is a no-op for a lock never taken. Leases, the directory lock and the sweep's transient locks all use it. No test assertions were loosened.A regression test,
cache_releases_unlock_even_while_a_spawned_process_holds_a_duplicate, holds atry_cloneduplicate (what a forked child holds) across a temporary release, a ready-index release followed by re-reservation, and a sweep. It fails on main and passes with the fix.Failure rates (same machine, before = main, after = this branch)
source::on 2 CPUs, 80 runsdamaged_shapefiles...)source::shp::cache::on 2 CPUs, 32 threads, 300 runssource::shp::cache::on 1 CPU, 16 threads, 200 runsreadyWouldBlock)Fixes #687
Summary by CodeRabbit