#[cfg(feature = "ha")]
pub use crate::ha::HAConfig;
pub use crate::time_series::TimeSeriesConfig;
use crate::types::TableDef;
use core::mem::size_of;
#[derive(Clone, Debug)]
pub struct ModelWorkerConfig {
pub enabled: bool,
pub cpu_cores: usize,
pub memory_limit_mb: usize,
pub max_models: usize,
pub request_timeout_ms: u64,
pub restart_on_failure: bool,
pub max_restart_attempts: u32,
}
impl Default for ModelWorkerConfig {
fn default() -> Self {
Self::DEFAULT
}
}
impl ModelWorkerConfig {
pub const DEFAULT: Self = Self {
enabled: true,
cpu_cores: 2,
memory_limit_mb: 2048,
max_models: 10,
request_timeout_ms: 5000,
restart_on_failure: true,
max_restart_attempts: 3,
};
pub fn validate(&self) -> bool {
if self.cpu_cores == 0 || self.cpu_cores > 64 {
return false;
}
if self.memory_limit_mb < 256 || self.memory_limit_mb > 65536 {
return false;
}
if self.max_models == 0 || self.max_models > 100 {
return false;
}
if self.request_timeout_ms < 100 || self.request_timeout_ms > 60000 {
return false;
}
if self.max_restart_attempts > 10 {
return false;
}
true
}
}
pub struct DefaultMemoryAllocator;
impl MemoryAllocator for DefaultMemoryAllocator {
fn allocate(&self, size: usize) -> Option<core::ptr::NonNull<u8>> {
#[cfg(feature = "std")]
{
let mut vec = Vec::with_capacity(size);
vec.resize(size, 0);
let ptr = vec.as_mut_ptr();
std::mem::forget(vec);
Some(unsafe { core::ptr::NonNull::new_unchecked(ptr) })
}
#[cfg(not(feature = "std"))]
{
None
}
}
fn deallocate(&self, ptr: core::ptr::NonNull<u8>, size: usize) {
#[cfg(feature = "std")]
{
unsafe {
let vec = Vec::from_raw_parts(ptr.as_ptr(), size, size);
drop(vec);
}
}
}
}
pub trait MemoryAllocator: Sync {
fn allocate(&self, size: usize) -> Option<core::ptr::NonNull<u8>>;
fn deallocate(&self, ptr: core::ptr::NonNull<u8>, size: usize);
}
#[derive(Copy, Clone, PartialEq)]
pub enum LogMode {
Sync,
Async,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum WALCompressionType {
None,
LZ4,
ZSTD,
}
pub struct WALConfig {
pub log_path: &'static str,
pub log_mode: LogMode,
pub checkpoint_interval_ms: u64,
pub log_file_size_limit: usize,
pub log_prealloc_size: usize,
pub log_segment_size: usize,
pub retained_checkpoints: usize,
pub max_consecutive_invalid: u32,
pub skip_threshold: u32,
pub skip_block_size: usize,
pub max_skip_attempts: u32,
pub compression_type: WALCompressionType,
pub compression_level: u8,
}
pub struct DbConfig {
pub tables: Vec<TableDef>,
pub total_memory: usize,
pub low_power_mode_supported: bool,
pub low_power_max_records: Option<usize>,
pub default_max_records: usize,
pub memory_allocator: &'static dyn MemoryAllocator,
pub wal_config: WALConfig,
pub time_series_defaults: TimeSeriesConfig,
#[cfg(feature = "pubsub")]
pub pubsub_config: Option<crate::pubsub::PubSubConfig>,
#[cfg(feature = "ha")]
pub ha_config: Option<HAConfig>,
pub model_worker_config: ModelWorkerConfig,
}
pub fn validate_config(config: &DbConfig) -> bool {
if config.tables.len() > 32 {
return false;
}
if let Some(low_power_max) = config.low_power_max_records {
if low_power_max > 100000 {
return false;
}
}
if config.default_max_records > 500000 {
return false;
}
if config.wal_config.checkpoint_interval_ms > 3600000 {
return false;
}
if config.wal_config.log_file_size_limit < 1024 * 1024 {
return false;
}
if config.wal_config.log_prealloc_size > config.wal_config.log_file_size_limit {
return false;
}
if config.wal_config.log_segment_size < 1024 * 1024 {
return false;
}
if config.wal_config.retained_checkpoints > 10 {
return false;
}
if config.wal_config.compression_level < 1 || config.wal_config.compression_level > 9 {
return false;
}
#[cfg(not(feature = "wal-compression-lz4"))]
{
if matches!(config.wal_config.compression_type, WALCompressionType::LZ4) {
return false;
}
}
#[cfg(not(feature = "wal-compression-zstd"))]
{
if matches!(config.wal_config.compression_type, WALCompressionType::ZSTD) {
return false;
}
}
#[cfg(feature = "ha")]
{
if let Some(ha_config) = &config.ha_config {
if ha_config.heartbeat_interval_ms < 100 {
return false;
}
if ha_config.heartbeat_interval_ms > 60000 {
return false;
}
if ha_config.failure_detection_ms < ha_config.heartbeat_interval_ms {
return false;
}
if ha_config.failure_detection_ms > 300000 {
return false;
}
if ha_config.sync_timeout_ms < 100 {
return false;
}
if ha_config.sync_timeout_ms > 10000 {
return false;
}
}
}
{
if !config.model_worker_config.validate() {
return false;
}
}
for table in &config.tables {
if table.record_size > 512 {
return false;
}
if table.max_records > 500000 {
return false;
}
for &pk_index in &table.primary_key {
if pk_index >= table.fields.len() {
return false;
}
}
if let Some(secondary_index) = &table.secondary_index {
for &index in secondary_index {
if index >= table.fields.len() {
return false;
}
}
}
}
true
}
pub fn table_memory_usage(table: &TableDef) -> usize {
let record_memory = table.record_size * table.max_records;
let index_memory = table.max_records * size_of::<u32>();
let secondary_index_memory = if table.secondary_index.is_some() {
match table.secondary_index_type {
crate::types::IndexType::SortedArray => {
let primary_key_field = &table.fields[table.primary_key[0]];
table.max_records * (primary_key_field.size + size_of::<u16>())
}
crate::types::IndexType::BTree => {
const BTREE_NODE_SIZE: usize = 1 + 1 + (64 * 4) + ((size_of::<usize>() * 5) / 8);
let max_nodes = table.max_records / 2;
max_nodes * BTREE_NODE_SIZE
}
crate::types::IndexType::TTree => {
const TTREE_NODE_SIZE: usize = 1 + (64 * 3) + (size_of::<usize>() * 3);
let max_nodes = table.max_records / 2;
max_nodes * TTREE_NODE_SIZE
}
_ => {
let primary_key_field = &table.fields[table.primary_key[0]];
table.max_records * (primary_key_field.size + size_of::<u16>())
}
}
} else {
0
};
record_memory + index_memory + secondary_index_memory
}
pub fn total_memory_usage(config: &DbConfig) -> usize {
let mut total = 0;
let mut i = 0;
while i < config.tables.len() {
total += table_memory_usage(&config.tables[i]);
i += 1;
}
total
}