radixdb-storage 1.1.0

Storage contracts and physical persistence engine for RadixDB
Documentation
use std::sync::{Arc, Weak};

use parking_lot::Mutex;

use crate::v6::{FormatError, FormatResult, StagingOwner};

use super::super::PhysicalGenerationSnapshot;
use super::model::{
    ActiveLeaseSet, ArtifactBuildKind, ArtifactBuildLease, BuildLeaseRecord, GenerationLeaseRecord,
    LeaseLimits, PhysicalGenerationLease,
};

#[derive(Debug)]
pub(crate) struct LeaseRegistry {
    limits: LeaseLimits,
    state: Mutex<LeaseRegistryState>,
}

#[derive(Debug, Default)]
struct LeaseRegistryState {
    generations: Vec<Weak<GenerationLeaseRecord>>,
    artifact_builds: Vec<Weak<BuildLeaseRecord>>,
}

impl LeaseRegistry {
    pub(crate) fn new(limits: LeaseLimits) -> Arc<Self> {
        Arc::new(Self {
            limits,
            state: Mutex::new(LeaseRegistryState::default()),
        })
    }

    pub(crate) fn register_generation(
        self: &Arc<Self>,
        snapshot: Arc<PhysicalGenerationSnapshot>,
    ) -> FormatResult<PhysicalGenerationLease> {
        let mut state = self.state.lock();
        let live = collect_live(&mut state.generations);
        if let Some(record) = live.iter().find(|record| {
            Arc::ptr_eq(record.snapshot(), &snapshot)
                || record.snapshot().control() == snapshot.control()
        }) {
            if record.snapshot().control() == snapshot.control()
                && !Arc::ptr_eq(record.snapshot(), &snapshot)
                && (record.snapshot().database_manifest() != snapshot.database_manifest()
                    || record.snapshot().table_manifests() != snapshot.table_manifests())
            {
                return invalid("one CONTROL identity resolves to different runtime graphs");
            }
            return Ok(PhysicalGenerationLease {
                record: Arc::clone(record),
                registry: Arc::clone(self),
            });
        }
        let actual = live.len().saturating_add(1);
        if actual > self.limits.max_generation_leases() {
            return Err(FormatError::LeaseLimitExceeded {
                field: "physical generations",
                actual,
                limit: self.limits.max_generation_leases(),
            });
        }
        let record = Arc::new(GenerationLeaseRecord::new(snapshot));
        state.generations.push(Arc::downgrade(&record));
        Ok(PhysicalGenerationLease {
            record,
            registry: Arc::clone(self),
        })
    }

    pub(crate) fn register_build(
        self: &Arc<Self>,
        kind: ArtifactBuildKind,
        source: &PhysicalGenerationLease,
        staging_owner: StagingOwner,
    ) -> FormatResult<ArtifactBuildLease> {
        if !Arc::ptr_eq(self, &source.registry) {
            return invalid("artifact build source belongs to another publisher");
        }
        validate_target_generation(kind, source, staging_owner)?;
        let mut state = self.state.lock();
        let live = collect_live(&mut state.artifact_builds);
        if live
            .iter()
            .any(|record| record.staging_owner() == staging_owner)
        {
            return invalid("staging owner already has an active artifact build");
        }
        let actual = live.len().saturating_add(1);
        if actual > self.limits.max_artifact_builds() {
            return Err(FormatError::LeaseLimitExceeded {
                field: "artifact builds",
                actual,
                limit: self.limits.max_artifact_builds(),
            });
        }
        let record = Arc::new(BuildLeaseRecord::new(kind, source.clone(), staging_owner));
        state.artifact_builds.push(Arc::downgrade(&record));
        Ok(ArtifactBuildLease { record })
    }

    pub(crate) fn active(self: &Arc<Self>) -> ActiveLeaseSet {
        let mut state = self.state.lock();
        let generations = collect_live(&mut state.generations)
            .into_iter()
            .map(|record| PhysicalGenerationLease {
                record,
                registry: Arc::clone(self),
            })
            .collect();
        let artifact_builds = collect_live(&mut state.artifact_builds)
            .into_iter()
            .map(|record| ArtifactBuildLease { record })
            .collect();
        ActiveLeaseSet::new(generations, artifact_builds)
    }
}

fn validate_target_generation(
    kind: ArtifactBuildKind,
    source: &PhysicalGenerationLease,
    owner: StagingOwner,
) -> FormatResult<()> {
    let expected = match kind {
        ArtifactBuildKind::Snapshot => source.database_generation(),
        ArtifactBuildKind::Seal
        | ArtifactBuildKind::DdlPublication
        | ArtifactBuildKind::Compaction
        | ArtifactBuildKind::IndexRebuild => source.database_generation().checked_next()?,
    };
    if owner.intended_generation() != expected {
        return invalid("staging owner targets the wrong database generation");
    }
    Ok(())
}

fn collect_live<T>(entries: &mut Vec<Weak<T>>) -> Vec<Arc<T>> {
    let live = entries.iter().filter_map(Weak::upgrade).collect::<Vec<_>>();
    entries.retain(|entry| entry.strong_count() > 0);
    live
}

fn invalid<T>(detail: &'static str) -> FormatResult<T> {
    Err(FormatError::InvalidLease { detail })
}