phenotypic.sdk_.promote_store#
- phenotypic.sdk_.promote_store(part: Path, final: Path, *, fsync: bool, commit_guard: Callable[[], AbstractContextManager[None]] | None = None) Path[source]#
Atomically promote a fully written
.partdirectory to final.The caller is responsible for the write order inside part: all arrays and chunks first, then
OME/zarr.json, then the rootzarr.jsonlast. An interrupted store therefore has no valid root and reads as absent. This function does not write the rootzarr.jsonitself.The move-aside is mandatory, not an optimization:
os.replaceonto a non-empty directory raisesOSError(ENOTEMPTY) on POSIX, and on WindowsMoveFileEx’sMOVEFILE_REPLACE_EXISTINGcannot name a directory at all.The whole
exists -> move-aside -> replacesequence sits inside one retry loop and re-evaluates existence on every attempt. That is what makes duplicate execution benign: a uuid.partprevents two writers interleaving chunks, but it does nothing for the promote itself, where a check-then-act done once lets writer B skip the move-aside because A had not yet renamed, then hitENOTEMPTYon a now-non-empty target.On failure after a successful move-aside, that attempt’s trash is reconciled before retrying or raising. The previous store is rolled back when final is absent; if a concurrent writer has already published a new final, that winner remains authoritative and only the attempt’s superseded trash is removed. Every retry uses a fresh UUID trash path, so no attempt can collide with its predecessor’s move-aside directory.
Known weakening versus the single-file rename: the two renames are still not one atomic step, so a crash between them (as opposed to a raised error) leaves the image absent plus an orphaned
.trash. Both are recoverable – absence reclassifies to the rebuilding stage, andsweep_orphan_parts()clears the leftovers.- Parameters:
part (Path) – Fully written
.partdirectory.final (Path) – Target store path.
fsync (bool) – Whether to flush part before renaming (see
durable_writes_enabled()).commit_guard (Callable[[], AbstractContextManager[None]] | None)
- Returns:
final.
- Return type: