hawdb-storage 0.9.0

An embedded Rust graph database for local data-plane workloads
Documentation
// Copyright 2026 Nowledge
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::{
    RelationalRowPagePhysicalGeneration, RelationalRowPagePublicationError,
    RelationalRowPageRootReader,
};
use hawdb_core::RuntimeTaskContext;
use std::num::NonZeroU64;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RelationalRowPageRewriteConfig {
    pub max_live_ratio_percent: u8,
    pub max_scan_pages: NonZeroU64,
    pub max_rewrite_bytes: NonZeroU64,
}

impl Default for RelationalRowPageRewriteConfig {
    fn default() -> Self {
        Self {
            max_live_ratio_percent: 50,
            max_scan_pages: NonZeroU64::new(1_000_000).unwrap(),
            max_rewrite_bytes: NonZeroU64::new(128 * 1024 * 1024 * 1024).unwrap(),
        }
    }
}

impl RelationalRowPageRewriteConfig {
    pub fn validate(self) -> Result<(), RelationalRowPagePublicationError> {
        if !(1..=100).contains(&self.max_live_ratio_percent) {
            return Err(RelationalRowPagePublicationError::Admission(
                "row-page rewrite live ratio must be in 1..=100 percent".to_string(),
            ));
        }
        Ok(())
    }
}

#[derive(Clone, Copy)]
pub(super) struct RowPageRewriteControls<'a> {
    pub config: RelationalRowPageRewriteConfig,
    pub task: &'a RuntimeTaskContext,
}

impl RowPageRewriteControls<'_> {
    pub(super) fn validate(
        self,
        base: Option<&RelationalRowPageRootReader>,
    ) -> Result<(), RelationalRowPagePublicationError> {
        self.checkpoint()?;
        self.config.validate()?;
        if base
            .is_some_and(|base| base.manifest().root_page_count > self.config.max_scan_pages.get())
        {
            return Err(RelationalRowPagePublicationError::Admission(
                "row-page rewrite exceeds its descriptor scan limit".to_string(),
            ));
        }
        Ok(())
    }

    pub(super) fn checkpoint(self) -> Result<(), RelationalRowPagePublicationError> {
        self.task.checkpoint().map_err(|reason| {
            RelationalRowPagePublicationError::Admission(format!(
                "row-page rewrite stopped: {reason}"
            ))
        })
    }

    pub(super) fn selects(self, entry: &RelationalRowPagePhysicalGeneration) -> bool {
        u128::from(entry.live_pages) * 100
            <= u128::from(entry.allocated_pages) * u128::from(self.config.max_live_ratio_percent)
    }
}