use serde::{Deserialize, Serialize};
use crate::columnar::ColumnType;
pub const KV_DEFAULT_INLINE_THRESHOLD: u16 = 64;
#[derive(
Debug,
Clone,
PartialEq,
Eq,
Serialize,
Deserialize,
zerompk::ToMessagePack,
zerompk::FromMessagePack,
)]
pub struct KvConfig {
pub schema: crate::columnar::StrictSchema,
pub ttl: Option<KvTtlPolicy>,
#[serde(default)]
pub capacity_hint: u32,
#[serde(default = "default_inline_threshold")]
pub inline_threshold: u16,
}
fn default_inline_threshold() -> u16 {
KV_DEFAULT_INLINE_THRESHOLD
}
impl KvConfig {
pub fn primary_key_column(&self) -> Option<&crate::columnar::ColumnDef> {
self.schema.columns.iter().find(|c| c.primary_key)
}
pub fn has_ttl(&self) -> bool {
self.ttl.is_some()
}
}
#[derive(
Debug,
Clone,
PartialEq,
Eq,
Serialize,
Deserialize,
zerompk::ToMessagePack,
zerompk::FromMessagePack,
)]
#[serde(tag = "kind")]
pub enum KvTtlPolicy {
FixedDuration {
duration_ms: u64,
},
FieldBased {
field: String,
offset_ms: u64,
},
}
const KV_VALID_KEY_TYPES: &[ColumnType] = &[
ColumnType::String, ColumnType::Uuid, ColumnType::Int64, ColumnType::Bytes, ColumnType::Timestamp, ];
pub fn is_valid_kv_key_type(ct: &ColumnType) -> bool {
KV_VALID_KEY_TYPES.contains(ct)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::columnar::{ColumnDef, ColumnType, StrictSchema};
#[test]
fn kv_config_primary_key() {
let schema = StrictSchema::new(vec![
ColumnDef::required("session_id", ColumnType::String).with_primary_key(),
ColumnDef::required("user_id", ColumnType::Uuid),
ColumnDef::nullable("payload", ColumnType::Bytes),
])
.unwrap();
let config = KvConfig {
schema,
ttl: None,
capacity_hint: 0,
inline_threshold: KV_DEFAULT_INLINE_THRESHOLD,
};
let pk = config.primary_key_column().unwrap();
assert_eq!(pk.name, "session_id");
assert_eq!(pk.column_type, ColumnType::String);
assert!(pk.primary_key);
}
#[test]
fn kv_valid_key_types() {
assert!(is_valid_kv_key_type(&ColumnType::String));
assert!(is_valid_kv_key_type(&ColumnType::Uuid));
assert!(is_valid_kv_key_type(&ColumnType::Int64));
assert!(is_valid_kv_key_type(&ColumnType::Bytes));
assert!(is_valid_kv_key_type(&ColumnType::Timestamp));
assert!(!is_valid_kv_key_type(&ColumnType::Float64));
assert!(!is_valid_kv_key_type(&ColumnType::Bool));
assert!(!is_valid_kv_key_type(&ColumnType::Geometry));
assert!(!is_valid_kv_key_type(&ColumnType::Vector(128)));
assert!(!is_valid_kv_key_type(&ColumnType::Decimal));
}
#[test]
fn kv_ttl_policy_serde() {
let policies = vec![
KvTtlPolicy::FixedDuration {
duration_ms: 60_000,
},
KvTtlPolicy::FieldBased {
field: "last_active".into(),
offset_ms: 3_600_000,
},
];
for p in policies {
let json = sonic_rs::to_string(&p).unwrap();
let back: KvTtlPolicy = sonic_rs::from_str(&json).unwrap();
assert_eq!(back, p);
}
}
}