Repository navigation
Conversation
Before opening with mode='w' (which truncates the file at the OS level), check that the existing file is accessible by opening it in read mode. If it is locked by another process, raise an error immediately without corrupting the file. h5py.File(path, mode='w') truncates the file before acquiring the HDF5 lock, so if the lock fails the file is already corrupted. This pre-check eliminates that window of corruption. Fixes ilastik#3235
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3244 +/- ##
==========================================
+ Coverage 62.51% 62.53% +0.01%
==========================================
Files 537 537
Lines 63363 63369 +6
==========================================
+ Hits 39614 39628 +14
+ Misses 23749 23741 -8 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| # If the file already exists, check that it is not locked before attempting to overwrite. | ||
| # Opening with mode="w" truncates the file at the OS level BEFORE acquiring the HDF5 lock, | ||
| # so if the file is locked by another process, the file would be corrupted even though | ||
| # the subsequent OSError is caught. See https://github.com/ilastik/ilastik/issues/3235 | ||
| if os.path.exists(projectFilePath): | ||
| try: | ||
| with h5py.File(projectFilePath, mode="r"): | ||
| pass | ||
| except OSError as e: | ||
| raise OSError( | ||
| f"Cannot overwrite project file because it is locked by another process: {projectFilePath}" | ||
| ) from e | ||
|
|
There was a problem hiding this comment.
- comment could be shorter
- not sure every OSError would indicate the file is locked. What matters is that it gets re-raised, but the message phrasing should maybe offer locking only as one possible reason
| shell._loadProject.assert_not_called() | ||
|
|
||
|
|
||
| def test_createBlankProjectFile_does_not_corrupt_locked_file(): |
There was a problem hiding this comment.
This test fails on main only due to technicalities (main doesn't raise the specific error string; and writes a different version string in createBlankProjectFile). With those two expectations removed, the test passes fine, even though the corruption issue exists.
It should actually reproduce the file corruption and make sure the fix prevents it.
Also use tmp_path instead of tempfile to stay in-pattern
|
The test works out both simpler and effective at actually testing the bug it's supposed to catch if you have it lock the file with Or do you see a specific reason to avoid genuinely reproducing the problem? |
That's a fair point. My concern with a genuine subprocess lock is that file locking behavior is platform-dependent:
The mock approach tests the specific invariant we care about: that Would you be okay with keeping the mock-based test as a unit test and relying on the existing manual reproduction steps (from the issue) for integration validation? Or would you prefer I attempt a cross-platform subprocess approach with platform-specific lock detection? |
|
Sure, maybe there are platform-specific behaviours, but all the test needs to do is replicate the relevant production situation: process 1 has used If the corruption issue is specific to Windows, then the test will currently only fail on main for Windows, but the test correctly expresses that we need to not corrupt the file on any platform when this situation occurs. |
…corruption prevention - Shortened the pre-check comment - Changed error message to not assume locking is the only reason - Rewrote test to prove that mode='w' is never called when mode='r' fails, which is the actual mechanism that prevents file corruption - Used tmp_path fixture instead of tempfile.TemporaryDirectory()
f2c0ce6 to
b9067bf
Compare
Summary
Fixes #3235
When trying to create a new project file overwriting an existing file that is locked by another ilastik instance, the file gets corrupted (truncated to 0 bytes) even though the error is caught.
Root cause
h5py.File(path, mode="w")truncates the file at the OS level before acquiring the HDF5 file lock. On Windows,CreateFilewithCREATE_ALWAYStruncates the file, then HDF5 tries to lock it. If locking fails (file open in another instance), theOSErroris caught inilastikShell.py, but the file is already corrupted.Fix
In
ProjectManager.createBlankProjectFile(), before opening withmode="w"(which truncates), check if the existing file is accessible by opening it in read mode ("r"). If that fails withOSError(locked by another process), raise immediately without ever attempting the truncating"w"open.Test
Added
test_createBlankProjectFile_does_not_corrupt_locked_fileregression test that:h5py.Fileto raiseOSErrorwhen opened in read mode (simulating a lock)createBlankProjectFileraises an error