Serialize startup prepared artifact reclamation

This commit is contained in:
ms2k 2026-07-22 03:56:44 +09:00
commit 7cd8a90af0

View file

@ -13,7 +13,9 @@ use lazaret_core::identity::{
DeviceId, HostPath, IdMapDigest, InodeId, PolicyId, PrepareOperationId, PreparedArtifactId,
SelectedManifestDigest, SourceIdentity,
};
use lazaret_core::records::{ArtifactState, PrepareOperation, PrepareStageState, PreparedArtifact};
use lazaret_core::records::{
ArtifactState, PrepareOperation, PrepareStageState, PreparedArtifact, StorageCatalog,
};
use lazaret_linux::filesystem::{
ExclusiveFileLock, SharedFileLock, try_exclusive_flock, try_shared_flock,
};
@ -522,12 +524,14 @@ impl PreparedLowerStore {
&self,
selected: &[PreparedArtifactId],
) -> PreparedLowerResult<Vec<PreparedArtifactGcResult>> {
self.gc_artifacts_inner(selected)
let physical_lock = self.physical_lock()?;
self.gc_artifacts_inner(selected, &physical_lock)
}
fn gc_artifacts_inner(
&self,
selected: &[PreparedArtifactId],
physical_lock: &ExclusiveFileLock,
) -> PreparedLowerResult<Vec<PreparedArtifactGcResult>> {
let mut unique = std::collections::HashSet::new();
if selected.iter().any(|id| !unique.insert(*id)) {
@ -539,6 +543,40 @@ impl PreparedLowerStore {
.catalog
.snapshot()
.map_err(PreparedLowerError::catalog)?;
self.gc_artifacts_from_snapshot(selected, &snapshot, physical_lock)
}
/// Reclaims the exact unleased artifacts observed while holding physical authority.
///
/// # Errors
/// Refuses catalog drift, leases, physical ownership mismatch, foreign paths, and
/// interrupted reclaim without exact durable evidence.
pub fn gc_unleased_artifacts(&self) -> PreparedLowerResult<Vec<PreparedArtifactGcResult>> {
let physical_lock = self.physical_lock()?;
let snapshot = self
.catalog
.snapshot()
.map_err(PreparedLowerError::catalog)?;
let selected = snapshot
.prepared_artifacts
.iter()
.filter(|artifact| {
!snapshot
.artifact_leases
.iter()
.any(|lease| lease.prepared_artifact_id == artifact.prepared_artifact_id)
})
.map(|artifact| artifact.prepared_artifact_id)
.collect::<Vec<_>>();
self.gc_artifacts_from_snapshot(&selected, &snapshot, &physical_lock)
}
fn gc_artifacts_from_snapshot(
&self,
selected: &[PreparedArtifactId],
snapshot: &StorageCatalog,
physical_lock: &ExclusiveFileLock,
) -> PreparedLowerResult<Vec<PreparedArtifactGcResult>> {
let mut results = Vec::with_capacity(selected.len());
for id in selected {
let artifact = snapshot
@ -548,8 +586,8 @@ impl PreparedLowerStore {
.context("prepared-artifact GC target is absent")
.map_err(PreparedLowerError::reclaim)?;
let outcome = match artifact.state {
ArtifactState::Available => self.reclaim(artifact)?,
ArtifactState::Reclaiming => self.resume_reclaim(artifact)?,
ArtifactState::Available => self.reclaim_inner(artifact, physical_lock)?,
ArtifactState::Reclaiming => self.resume_reclaim_locked(artifact, physical_lock)?,
};
results.push(PreparedArtifactGcResult {
prepared_artifact_id: *id,
@ -566,7 +604,15 @@ impl PreparedLowerStore {
/// Refuses leases, forged catalog rows, physical identity mismatch, foreign paths, and
/// filesystem or catalog failures.
pub fn reclaim(&self, expected: &PreparedArtifact) -> PreparedLowerResult<ReclaimOutcome> {
let _physical_lock = self.physical_lock()?;
let physical_lock = self.physical_lock()?;
self.reclaim_inner(expected, &physical_lock)
}
fn reclaim_inner(
&self,
expected: &PreparedArtifact,
_physical_lock: &ExclusiveFileLock,
) -> PreparedLowerResult<ReclaimOutcome> {
let artifact = self
.open_relative(&artifact_path(expected.prepared_artifact_id))
.map_err(PreparedLowerError::reclaim)?
@ -598,7 +644,15 @@ impl PreparedLowerStore {
&self,
reclaiming: &PreparedArtifact,
) -> PreparedLowerResult<ReclaimOutcome> {
let _physical_lock = self.physical_lock()?;
let physical_lock = self.physical_lock()?;
self.resume_reclaim_locked(reclaiming, &physical_lock)
}
fn resume_reclaim_locked(
&self,
reclaiming: &PreparedArtifact,
_physical_lock: &ExclusiveFileLock,
) -> PreparedLowerResult<ReclaimOutcome> {
let _claim = self
.lock_reclaiming_artifact(reclaiming)
.map_err(PreparedLowerError::reclaim)?;