wedb_embed 0.1.2

Embedded database engine providing Redis-like APIs, built on fjall / 嵌入式数据库引擎,提供类似 Redis 的接口,底层基于 fjall 开发
Documentation
use std::sync::Arc;

use fjall::{
  CompressionType, Database, Keyspace as FjallKeyspace, KeyspaceCreateOptions, KvSeparationOptions,
  config::{
    BlockSizePolicy, CompressionPolicy, HashRatioPolicy, PinningPolicy, RestartIntervalPolicy,
  },
};

use crate::{
  conf::{Conf, DbConfig},
  error::{Error, Result},
};

pub const DATA: &str = "data";
pub const META: &str = "meta";
pub const DATA_NS: &str = "data_ns";
pub const META_NS: &str = "meta_ns";

/// 统一管理与初始化 Fjall 存储引擎的双轨制核心 Keyspace
#[derive(Clone)]
pub struct Keyspace {
  /// 默认命名空间业务数据(String 裸值、复杂结构子键等,0 字节额外前缀)
  pub data: FjallKeyspace,
  /// 默认命名空间复合数据结构元数据
  pub meta: FjallKeyspace,
  /// 多租户命名空间业务数据(带租户隔离前缀)
  pub data_ns: FjallKeyspace,
  /// 多租户命名空间复合元数据、Catalog 目录与 ID 映射
  pub meta_ns: FjallKeyspace,
}

impl Keyspace {
  /// 构造通用 Keyspace 配置
  #[inline]
  fn make_options(
    comp_policy: CompressionPolicy,
    manual_persist: bool,
    memtable_size: u64,
    kv_separation: Option<usize>,
    block_size: u32,
    hash_ratio: f32,
    expect_point_hits: bool,
  ) -> KeyspaceCreateOptions {
    let mut opts = KeyspaceCreateOptions::default()
      .data_block_size_policy(BlockSizePolicy::all(block_size))
      .data_block_compression_policy(comp_policy)
      .data_block_hash_ratio_policy(HashRatioPolicy::all(hash_ratio))
      .data_block_restart_interval_policy(RestartIntervalPolicy::new([8, 16]))
      .index_block_pinning_policy(PinningPolicy::all(true))
      .filter_block_pinning_policy(PinningPolicy::all(true))
      .expect_point_read_hits(expect_point_hits)
      .max_memtable_size(memtable_size)
      .manual_journal_persist(manual_persist);

    if let Some(threshold) = kv_separation {
      opts = opts.with_kv_separation(Some(
        KvSeparationOptions::default()
          .separation_threshold(threshold as u32)
          .compression(CompressionType::Lz4),
      ));
    }

    opts
  }

  /// 统一打开并初始化双轨制 Keyspace,支持配置项列表 conf_li
  pub fn open<I>(db: &Arc<Database>, conf_li: I) -> Result<Self>
  where
    I: IntoIterator<Item = Conf>,
  {
    let cfg = DbConfig::from_conf_li(conf_li);
    Self::open_with_cfg(db, &cfg)
  }

  /// 内部通过 DbConfig 初始化双轨制 Keyspace
  pub fn open_with_cfg(db: &Arc<Database>, cfg: &DbConfig) -> Result<Self> {
    let comp_type: CompressionType = cfg.compression.into();
    let data_comp_policy = match comp_type {
      CompressionType::None => CompressionPolicy::disabled(),
      CompressionType::Lz4 => CompressionPolicy::new([
        CompressionType::None,
        CompressionType::None,
        CompressionType::Lz4,
      ]),
    };

    let manual_persist = cfg.manual_journal_persist;
    let data_block_size = cfg.data_block_size as u32;
    let meta_block_size = cfg.meta_block_size as u32;
    let data_memtable_size = cfg.data_memtable_size as u64;
    let meta_memtable_size = cfg.meta_memtable_size as u64;
    let data_kv_sep = cfg.kv_separation_threshold;
    let data_hash_ratio = cfg.data_hash_ratio;
    let expect_point_hits = cfg.expect_point_read_hits;

    let p1 = data_comp_policy.clone();
    let data = db
      .keyspace(DATA, move || {
        Self::make_options(
          p1,
          manual_persist,
          data_memtable_size,
          data_kv_sep,
          data_block_size,
          data_hash_ratio,
          expect_point_hits,
        )
      })
      .map_err(|e| Error::internal_with_source("Failed to open data keyspace", e))?;

    let p2 = data_comp_policy.clone();
    let meta = db
      .keyspace(META, move || {
        Self::make_options(
          p2,
          manual_persist,
          meta_memtable_size,
          None,
          meta_block_size,
          0.0,
          expect_point_hits,
        )
      })
      .map_err(|e| Error::internal_with_source("Failed to open meta keyspace", e))?;

    let p3 = data_comp_policy.clone();
    let data_ns = db
      .keyspace(DATA_NS, move || {
        Self::make_options(
          p3,
          manual_persist,
          data_memtable_size,
          data_kv_sep,
          data_block_size,
          data_hash_ratio,
          expect_point_hits,
        )
      })
      .map_err(|e| Error::internal_with_source("Failed to open data_ns keyspace", e))?;

    let meta_ns = db
      .keyspace(META_NS, move || {
        Self::make_options(
          data_comp_policy,
          manual_persist,
          meta_memtable_size,
          None,
          meta_block_size,
          0.0,
          expect_point_hits,
        )
      })
      .map_err(|e| Error::internal_with_source("Failed to open meta_ns keyspace", e))?;

    Ok(Self {
      data,
      meta,
      data_ns,
      meta_ns,
    })
  }
}