use crate::checkpoint::load_namespace_manifest_envelope;
use crate::error::MetadataProjectionLoadError;
use crate::error::{CoreError, Result};
use crate::namespace::basis::{read_head_and_metadata_basis, resolve_retention_floor_seq};
use crate::wal::{count_visible_wal_tail_segments, WalChainLoadRequest};
use loonfs_api::wire::control::NamespaceState;
use loonfs_api::{ChangeSeq, ManifestId, NamespaceId};
use loonfs_objectstore::ObjectStore;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NamespaceHeadSummary {
pub namespace_id: NamespaceId,
pub head_seq: ChangeSeq,
pub current_manifest_id: Option<ManifestId>,
pub wal_tail_segments: u64,
pub retention_floor_seq: ChangeSeq,
}
pub async fn load_namespace_head_summary<S: ObjectStore + ?Sized>(
store: &S,
expected_namespace_id: &NamespaceId,
) -> Result<NamespaceHeadSummary> {
let loaded = read_head_and_metadata_basis(store, expected_namespace_id)
.await
.map_err(|error| {
CoreError::MetadataProjection(MetadataProjectionLoadError::LoadHead(error))
})?;
let head = loaded.head.envelope.state;
if head.state == NamespaceState::Deleted {
return Err(CoreError::NamespaceDeleted {
namespace_id: expected_namespace_id.clone(),
});
}
let (current_manifest_id, basis_head_seq) = match loaded.basis.manifest() {
Some(basis) => {
let manifest = load_namespace_manifest_envelope(
store,
&basis.owner_namespace_id,
&basis.manifest_object_id,
)
.await
.map_err(|error| {
CoreError::MetadataProjection(MetadataProjectionLoadError::ManifestLoad(error))
})?;
let own_manifest_id = loaded
.basis
.is_owned_by(expected_namespace_id)
.then_some(basis.manifest_id);
(own_manifest_id, manifest.payload.head_seq)
}
None => (None, ChangeSeq(0)),
};
let wal_tail_segments = count_visible_wal_tail_segments(
store,
WalChainLoadRequest {
namespace_id: expected_namespace_id,
chain_base_seq: basis_head_seq,
head_seq: head.seq,
visible_tip: head.visible_wal_tip.clone(),
stop_after_seq: None,
recent_segments: &head.recent_segments,
},
)
.await
.map_err(|error| {
CoreError::MetadataProjection(MetadataProjectionLoadError::WalChainLoad(error))
})?;
let retention_floor_seq = resolve_retention_floor_seq(store, &head)
.await
.map_err(|error| {
CoreError::MetadataProjection(MetadataProjectionLoadError::LoadHead(error))
})?;
Ok(NamespaceHeadSummary {
namespace_id: head.namespace_id,
head_seq: head.seq,
current_manifest_id,
wal_tail_segments,
retention_floor_seq,
})
}
pub async fn load_deleted_namespace_head_summary<S: ObjectStore + ?Sized>(
store: &S,
expected_namespace_id: &NamespaceId,
) -> Result<NamespaceHeadSummary> {
let head = crate::namespace::control::read_head_object(store, expected_namespace_id)
.await
.map_err(|error| {
CoreError::MetadataProjection(MetadataProjectionLoadError::LoadHead(error))
})?
.envelope
.state;
if head.state != NamespaceState::Deleted {
return Err(CoreError::Internal(format!(
"namespace `{expected_namespace_id}` is not deleted; the live head summary serves it"
)));
}
let retention_floor_seq = resolve_retention_floor_seq(store, &head)
.await
.map_err(|error| {
CoreError::MetadataProjection(MetadataProjectionLoadError::LoadHead(error))
})?;
Ok(NamespaceHeadSummary {
namespace_id: head.namespace_id,
head_seq: head.seq,
current_manifest_id: None,
wal_tail_segments: 0,
retention_floor_seq,
})
}