#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeployMode {
Local,
Replicate,
Cluster,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Topology {
pub mode: DeployMode,
pub shards: u32,
pub replication: u32,
pub node_id: u64,
pub query: String,
}
impl Topology {
#[must_use]
pub fn from_env() -> Self {
let mode = deploy_mode_from_env();
Self {
mode,
shards: shards_from_env(mode),
replication: replication_from_env(),
node_id: node_id_from_env(),
query: query_from_env(mode),
}
}
#[must_use]
pub fn local_default() -> Self {
Self {
mode: DeployMode::Local,
shards: 1,
replication: 1,
node_id: 1,
query: "kv".into(),
}
}
#[must_use]
pub fn is_cluster(&self) -> bool {
self.mode == DeployMode::Cluster
}
#[must_use]
pub fn bootstrap_leader(&self) -> bool {
self.is_cluster() && self.node_id == 1
}
#[must_use]
pub fn mode_label(&self) -> &'static str {
match self.mode {
DeployMode::Local => "local",
DeployMode::Replicate => "replicate",
DeployMode::Cluster => "cluster",
}
}
}
pub fn deploy_mode_from_env() -> DeployMode {
match std::env::var("NAUTALID_AETHERDB_MODE")
.ok()
.as_deref()
.map(str::trim)
.map(str::to_ascii_lowercase)
.as_deref()
{
Some("cluster") => DeployMode::Cluster,
Some("replicate") | Some("replay") => DeployMode::Replicate,
Some("local") | Some("embedded") => DeployMode::Local,
_ => DeployMode::Replicate,
}
}
pub fn host_mode_from_env() -> DeployMode {
match std::env::var("AETHERDB_HOST_MODE")
.ok()
.as_deref()
.map(str::trim)
.map(str::to_ascii_lowercase)
.as_deref()
{
Some("cluster") => DeployMode::Cluster,
Some("replicate") | Some("replay") => DeployMode::Replicate,
_ => deploy_mode_from_env(),
}
}
fn shards_from_env(mode: DeployMode) -> u32 {
std::env::var("NAUTALID_AETHERDB_SHARDS")
.ok()
.or_else(|| std::env::var("AETHERDB_HOST_SHARDS").ok())
.and_then(|s| s.parse().ok())
.unwrap_or(match mode {
DeployMode::Cluster => 16,
DeployMode::Replicate | DeployMode::Local => 1,
})
.max(1)
}
fn replication_from_env() -> u32 {
std::env::var("NAUTALID_AETHERDB_REPLICATION")
.ok()
.or_else(|| std::env::var("AETHERDB_HOST_REPLICATION").ok())
.and_then(|s| s.parse().ok())
.unwrap_or(3)
.max(1)
}
fn node_id_from_env() -> u64 {
std::env::var("NAUTALID_AETHERDB_NODE_ID")
.ok()
.or_else(|| std::env::var("AETHERDB_HOST_NODE_ID").ok())
.and_then(|s| s.parse().ok())
.unwrap_or(1)
.max(1)
}
fn query_from_env(mode: DeployMode) -> String {
std::env::var("NAUTALID_AETHERDB_QUERY")
.ok()
.or_else(|| std::env::var("AETHERDB_HOST_QUERY").ok())
.filter(|s| !s.trim().is_empty())
.unwrap_or_else(|| match mode {
DeployMode::Cluster => "sql".into(),
DeployMode::Replicate | DeployMode::Local => "kv".into(),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cluster_defaults_sixteen_shards() {
unsafe {
std::env::set_var("NAUTALID_AETHERDB_MODE", "cluster");
std::env::remove_var("NAUTALID_AETHERDB_SHARDS");
}
let topo = Topology::from_env();
assert_eq!(topo.mode, DeployMode::Cluster);
assert_eq!(topo.shards, 16);
assert_eq!(topo.query, "sql");
unsafe {
std::env::remove_var("NAUTALID_AETHERDB_MODE");
}
}
#[test]
fn replicate_defaults_one_shard() {
unsafe {
std::env::set_var("NAUTALID_AETHERDB_MODE", "replicate");
std::env::remove_var("NAUTALID_AETHERDB_SHARDS");
}
let topo = Topology::from_env();
assert_eq!(topo.shards, 1);
unsafe {
std::env::remove_var("NAUTALID_AETHERDB_MODE");
}
}
}