@danhachuel/thunderbolt 0.7.15 → 0.7.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hermes_ui/storage.py +25 -3
- package/package.json +1 -1
package/hermes_ui/storage.py
CHANGED
|
@@ -450,6 +450,7 @@ _LOCK_TIMEOUT_SECONDS = 30.0
|
|
|
450
450
|
_LOCK_POLL_SECONDS = 0.05
|
|
451
451
|
_LOCK_STALE_SECONDS = 120
|
|
452
452
|
_PROTECTED_STATE_FILES = {"channels.json", "tasks.json", "batches.json", "queues.json", "uploads.json"}
|
|
453
|
+
_REPLACE_RETRY_DELAYS_SECONDS = (0.05, 0.1, 0.2, 0.4, 0.8, 1.0, 1.0, 1.0)
|
|
453
454
|
|
|
454
455
|
|
|
455
456
|
class StorageIntegrityError(RuntimeError):
|
|
@@ -533,10 +534,31 @@ def _atomic_write_unlocked(path: Path, data: Any) -> None:
|
|
|
533
534
|
handle.write("\n")
|
|
534
535
|
handle.flush()
|
|
535
536
|
os.fsync(handle.fileno())
|
|
536
|
-
|
|
537
|
+
replace_error: PermissionError | None = None
|
|
538
|
+
for delay in (0.0, *_REPLACE_RETRY_DELAYS_SECONDS):
|
|
539
|
+
if delay:
|
|
540
|
+
time.sleep(delay)
|
|
541
|
+
try:
|
|
542
|
+
os.replace(temp_name, path)
|
|
543
|
+
replace_error = None
|
|
544
|
+
break
|
|
545
|
+
except PermissionError as exc:
|
|
546
|
+
# Windows can briefly keep the destination open while Streamlit,
|
|
547
|
+
# an antivirus scanner, or the previous reader releases it.
|
|
548
|
+
# Keep the atomic replace, but give that transient handle time
|
|
549
|
+
# to disappear instead of failing the pipeline at its next
|
|
550
|
+
# progress checkpoint.
|
|
551
|
+
replace_error = exc
|
|
552
|
+
if replace_error is not None:
|
|
553
|
+
raise replace_error
|
|
537
554
|
finally:
|
|
538
|
-
|
|
539
|
-
os.
|
|
555
|
+
try:
|
|
556
|
+
if os.path.exists(temp_name):
|
|
557
|
+
os.unlink(temp_name)
|
|
558
|
+
except OSError:
|
|
559
|
+
# A scanner may also briefly hold the temporary file. The next
|
|
560
|
+
# write can clean it up; never hide the replace error with cleanup.
|
|
561
|
+
pass
|
|
540
562
|
|
|
541
563
|
|
|
542
564
|
def atomic_write(path: Path, data: Any) -> None:
|
package/package.json
CHANGED