fix(archive): preserve prepared sessions across restarts

This commit is contained in:
DCCONSTRUCTIONS
2026-07-18 11:08:27 +03:00
parent aa3680948f
commit 574a494759
13 changed files with 398 additions and 132 deletions
+40 -8
View File
@@ -232,6 +232,26 @@ class SessionRecordingMaterializer:
with self._lock_for(session_id):
return self._load_cached_recording(session_id, _validate_source(command))
def restore_published(self, command: ReplayCommand) -> MaterializedRecording | None:
"""Restore an immutable published cache using bounded metadata checks.
Full source/output digests are proved before the cache is published.
On a later process start the private cache is restored only when its
schema, source stat identities and derived-file stat identity still
match the durable sidecar. This keeps a prepared recording durable
across restarts without rereading gigabytes or starting an exporter.
"""
session_id = _validate_command_shape(command)
if not self._has_compatible_cache_candidate(session_id):
return None
with self._lock_for(session_id):
return self._load_cached_recording(
session_id,
_validate_source(command),
verify_digests=False,
)
def get_cached_pinned(
self,
command: ReplayCommand,
@@ -430,6 +450,7 @@ class SessionRecordingMaterializer:
source: _ValidatedSource,
*,
pin: bool = False,
verify_digests: bool = True,
) -> MaterializedRecording | None:
# Cache validation and the optional lease are one transaction with
# eviction. This makes it safe for FileResponse to open the path after
@@ -451,6 +472,7 @@ class SessionRecordingMaterializer:
source=source,
recording_path=recording_path,
sidecar_path=sidecar_path,
verify_digests=verify_digests,
)
if cached is None:
return None
@@ -661,6 +683,7 @@ class SessionRecordingMaterializer:
source: _ValidatedSource,
recording_path: Path,
sidecar_path: Path,
verify_digests: bool = True,
) -> MaterializedRecording | None:
if recording_path.is_symlink() or sidecar_path.is_symlink():
return None
@@ -698,12 +721,19 @@ class SessionRecordingMaterializer:
source_sha256: str | None = None
for cached, artifact in zip(cached_artifacts, source.artifacts, strict=True):
digest = _sha256_prefix_stable(
artifact.path,
artifact.file_stat,
artifact.replay_byte_length,
digest = (
_sha256_prefix_stable(
artifact.path,
artifact.file_stat,
artifact.replay_byte_length,
)
if verify_digests
else cached["sha256"]
)
if artifact.expected_sha256 is not None and digest != artifact.expected_sha256:
if (
artifact.expected_sha256 is not None
and cached["sha256"] != artifact.expected_sha256
):
raise RecordingMaterializationError(
"recording artifact digest no longer matches catalog"
)
@@ -713,9 +743,11 @@ class SessionRecordingMaterializer:
source_sha256 = digest
if source_sha256 is None or source_sha256 != document["source_sha256"]:
return None
recording_sha256 = _sha256_stable(recording_path, recording_stat)
if recording_sha256 != document["recording_sha256"]:
return None
recording_sha256 = document["recording_sha256"]
if verify_digests:
recording_sha256 = _sha256_stable(recording_path, recording_stat)
if recording_sha256 != document["recording_sha256"]:
return None
_chmod_best_effort(recording_path, 0o600)
_chmod_best_effort(sidecar_path, 0o600)
if document["schema_version"] != CACHE_SCHEMA: