wedb_embed 0.1.1

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

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

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

pub const DATA: &str = "data";
pub const META: &str = "meta";

/// 统一管理与初始化 Fjall 存储引擎的核心 Keyspace
#[derive(Clone)]
pub struct Keyspace {
    /// 用户业务数据(String 裸值、各类复杂结构的子键字段等)
    pub data: FjallKeyspace,
    /// 统一复合数据结构元数据(Hash/List/ZSet/Set 等的 KeyMeta、TTL、计数等)
    pub meta: FjallKeyspace,
}

impl Keyspace {
    /// 统一打开并初始化所有 Keyspace
    pub fn open(db: &Arc<Database>, conf: &Conf) -> Result<Self> {
        let comp_type = parse_compression_type(conf.compression.as_deref(), CompressionType::Lz4);
        let data_comp_policy = match comp_type {
            CompressionType::None => CompressionPolicy::disabled(),
            CompressionType::Lz4 => CompressionPolicy::new([
                CompressionType::None,
                CompressionType::None,
                CompressionType::Lz4,
            ]),
        };

        let manual_persist = conf.manual_journal_persist.unwrap_or(false);

        // 1. Data Keyspace:4KB Block + 压缩策略 + Data Block Hash 索引 + Index/Filter 常驻 Pinning + KV 分离 (Blob Storage) + 64MB MemTable
        let data_policy = data_comp_policy.clone();
        let data = db
            .keyspace(DATA, move || {
                KeyspaceCreateOptions::default()
                    .data_block_size_policy(BlockSizePolicy::all(4 * 1024))
                    .data_block_compression_policy(data_policy)
                    .data_block_hash_ratio_policy(HashRatioPolicy::all(0.75))
                    .data_block_restart_interval_policy(RestartIntervalPolicy::new([8, 16]))
                    .index_block_pinning_policy(PinningPolicy::all(true))
                    .filter_block_pinning_policy(PinningPolicy::all(true))
                    .max_memtable_size(64 * 1024 * 1024)
                    .manual_journal_persist(manual_persist)
                    .with_kv_separation(Some(
                        KvSeparationOptions::default()
                            .separation_threshold(4096)
                            .compression(CompressionType::Lz4),
                    ))
            })
            .map_err(|e| Error::internal_with_source("Failed to open data keyspace", e))?;

        // 2. Meta Keyspace:4KB Block + 压缩策略 + Data Block Hash 索引 + Index/Filter 内存常驻 Pinning + 32MB MemTable
        let meta = db
            .keyspace(META, move || {
                KeyspaceCreateOptions::default()
                    .data_block_size_policy(BlockSizePolicy::all(4 * 1024))
                    .data_block_compression_policy(data_comp_policy)
                    .data_block_hash_ratio_policy(HashRatioPolicy::all(0.75))
                    .data_block_restart_interval_policy(RestartIntervalPolicy::new([8, 16]))
                    .index_block_pinning_policy(PinningPolicy::all(true))
                    .filter_block_pinning_policy(PinningPolicy::all(true))
                    .max_memtable_size(32 * 1024 * 1024)
                    .manual_journal_persist(manual_persist)
            })
            .map_err(|e| Error::internal_with_source("Failed to open meta keyspace", e))?;

        Ok(Self { data, meta })
    }
}