pub mod error;
pub mod gates;
pub use error::HiveError;
pub use gates::{BackpressureGate, CircuitState, ClusterCircuitBreaker, ClusterSecurityGate};
pub use crate::colony::common::{
ActivateServletRequest, ActivateServletResponse, ClusterCommand, ClusterCommandResponse, ClusterStatus,
HeartbeatParams, HeartbeatResult, HiveManagementRequest, HiveManagementResponse, InstanceMetrics, LeastLoaded,
ListServletsParams, ListServletsResult, LoadBalancer, MessageRouter, MessageValidator, PowerOfTwoChoices,
RegisterHiveRequest, RegisterHiveResponse, RoundRobin, ScalingDecision, ScalingMetrics, ServletAddressUpdate,
ServletAddressUpdateResponse, ServletInfo, ServletScaleConf, SpawnServletParams, SpawnServletResult,
StopServletParams, StopServletResult, TypeBasedRouter,
};
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::{sync::Arc, vec::Vec};
#[cfg(feature = "std")]
use std::collections::HashMap;
#[cfg(feature = "std")]
use std::sync::Arc;
use core::future::Future;
use core::pin::Pin;
use core::time::Duration;
use crate::constants::DEFAULT_BACKPRESSURE_THRESHOLD_BPS;
use crate::trace::TraceCollector;
use crate::transport::policy::CoreRetryPolicy;
use crate::transport::Protocol;
use crate::utils::BasisPoints;
use crate::TightBeamError;
#[cfg(feature = "x509")]
pub use crate::crypto::x509::store::CertificateTrust;
pub type SpawnerFn = Arc<
dyn Fn(Arc<TraceCollector>) -> Pin<Box<dyn Future<Output = Result<Box<dyn ServletBox>, TightBeamError>> + Send>>
+ Send
+ Sync,
>;
pub trait ServletBox: Send + Sync {
fn addr_bytes(&self) -> Vec<u8>;
fn stop_boxed(self: Box<Self>);
fn utilization(&self) -> Option<BasisPoints> {
None
}
fn is_healthy(&self) -> bool {
true
}
}
pub struct ServletRegistration {
pub servlet: Box<dyn ServletBox>,
pub spawner: SpawnerFn,
pub servlet_type: &'static str,
}
pub trait ServletRegistry: Send + Sync {
fn insert(&self, key: Vec<u8>, registration: ServletRegistration) -> Result<(), TightBeamError>;
fn remove(&self, key: &[u8]) -> Option<ServletRegistration>;
fn with_get<F, R>(&self, key: &[u8], f: F) -> Option<R>
where
F: FnOnce(&ServletRegistration) -> R;
fn for_each<F>(&self, f: F)
where
F: FnMut(&Vec<u8>, &ServletRegistration);
fn for_each_by_type<F>(&self, prefix: &[u8], f: F)
where
F: FnMut(&Vec<u8>, &ServletRegistration);
fn count(&self) -> usize;
fn addresses(&self) -> Vec<(&'static str, Vec<u8>)>;
fn find_type_by_prefix(&self, prefix: &[u8]) -> Option<&'static str>;
fn drain_all(&self) -> Vec<(Vec<u8>, ServletRegistration)>;
fn keys(&self) -> Vec<Vec<u8>>;
}
pub struct HashMapRegistry {
inner: std::sync::Mutex<HashMap<Vec<u8>, ServletRegistration>>,
}
impl Default for HashMapRegistry {
fn default() -> Self {
Self { inner: std::sync::Mutex::new(HashMap::new()) }
}
}
impl ServletRegistry for HashMapRegistry {
fn insert(&self, key: Vec<u8>, registration: ServletRegistration) -> Result<(), TightBeamError> {
self.inner
.lock()
.map_err(|_| TightBeamError::LockPoisoned)?
.insert(key, registration);
Ok(())
}
fn remove(&self, key: &[u8]) -> Option<ServletRegistration> {
self.inner.lock().ok()?.remove(key)
}
fn with_get<F, R>(&self, key: &[u8], f: F) -> Option<R>
where
F: FnOnce(&ServletRegistration) -> R,
{
let guard = self.inner.lock().ok()?;
guard.get(key).map(f)
}
fn for_each<F>(&self, mut f: F)
where
F: FnMut(&Vec<u8>, &ServletRegistration),
{
if let Ok(guard) = self.inner.lock() {
guard.iter().for_each(|(k, v)| f(k, v));
}
}
fn for_each_by_type<F>(&self, prefix: &[u8], mut f: F)
where
F: FnMut(&Vec<u8>, &ServletRegistration),
{
if let Ok(guard) = self.inner.lock() {
guard.iter().filter(|(k, _)| k.starts_with(prefix)).for_each(|(k, v)| f(k, v));
}
}
fn count(&self) -> usize {
self.inner.lock().map(|g| g.len()).unwrap_or(0)
}
fn addresses(&self) -> Vec<(&'static str, Vec<u8>)> {
self.inner
.lock()
.map(|guard| guard.values().map(|reg| (reg.servlet_type, reg.servlet.addr_bytes())).collect())
.unwrap_or_default()
}
fn find_type_by_prefix(&self, prefix: &[u8]) -> Option<&'static str> {
self.inner.lock().ok().and_then(|guard| {
guard
.iter()
.find(|(k, _)| k.starts_with(prefix))
.map(|(_, reg)| reg.servlet_type)
})
}
fn drain_all(&self) -> Vec<(Vec<u8>, ServletRegistration)> {
self.inner.lock().map(|mut guard| guard.drain().collect()).unwrap_or_default()
}
fn keys(&self) -> Vec<Vec<u8>> {
self.inner
.lock()
.map(|guard| guard.keys().cloned().collect())
.unwrap_or_default()
}
}
pub trait Hive: Sized + Send + Sync {
type Protocol: Protocol;
type Address;
fn new(config: Option<HiveConf>) -> Result<Self, TightBeamError>;
fn register<S, F, Fut>(&mut self, name: &'static str, servlet: S, spawner: F) -> Result<(), TightBeamError>
where
S: ServletBox + 'static,
F: Fn(Arc<TraceCollector>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<S, TightBeamError>> + Send + 'static;
fn establish(&mut self, trace: Arc<TraceCollector>) -> impl Future<Output = Result<(), TightBeamError>> + Send;
fn addr(&self) -> Self::Address;
fn servlet_addresses(&self) -> Vec<(&'static str, Vec<u8>)>;
fn stop(self);
fn join(self) -> impl Future<Output = Result<(), TightBeamError>> + Send;
fn register_with_cluster(
&self,
cluster_addr: <Self::Protocol as Protocol>::Address,
) -> impl Future<Output = Result<RegisterHiveResponse, TightBeamError>> + Send;
fn drain(&self) -> impl Future<Output = Result<(), TightBeamError>> + Send;
fn is_draining(&self) -> bool;
}
#[cfg(feature = "x509")]
pub struct HiveTlsConfig {
pub certificate: crate::crypto::x509::CertificateSpec,
pub key: Arc<dyn crate::crypto::key::SigningKeyProvider>,
pub validators: Vec<Arc<dyn crate::crypto::x509::policy::CertificateValidation>>,
}
#[cfg(feature = "x509")]
impl core::fmt::Debug for HiveTlsConfig {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("HiveTlsConfig")
.field("certificate", &self.certificate)
.field("key", &"<KeyProvider>")
.field("validators", &format!("[{} validators]", self.validators.len()))
.finish()
}
}
pub type CallFuture<'a> = Pin<Box<dyn core::future::Future<Output = Result<Vec<u8>, TightBeamError>> + Send + 'a>>;
pub trait HiveContext: Send + Sync {
fn call<'a>(&'a self, servlet_type: &'a [u8], request: Vec<u8>) -> CallFuture<'a>;
}
pub fn call_typed<'a, Req, Resp>(
ctx: &'a (dyn HiveContext + Sync),
servlet_type: &'a [u8],
request: &Req,
) -> Pin<Box<dyn Future<Output = Result<Resp, TightBeamError>> + Send + 'a>>
where
Req: der::Encode,
Resp: for<'de> der::Decode<'de> + Send + 'a,
{
let req_result = crate::encode(request);
Box::pin(async move {
let req_bytes = req_result?;
let resp_bytes = ctx.call(servlet_type, req_bytes).await?;
crate::decode(&resp_bytes)
})
}
#[derive(Clone)]
pub struct HiveConf<L: LoadBalancer = LeastLoaded, R: MessageRouter = TypeBasedRouter> {
pub load_balancer: L,
pub router: R,
pub default_scale: ServletScaleConf,
pub servlet_overrides: HashMap<Vec<u8>, ServletScaleConf>,
pub cooldown: Duration,
pub queue_capacity: u32,
pub backpressure_threshold: BasisPoints,
pub circuit_breaker_threshold: u8,
pub circuit_breaker_cooldown_ms: u64,
pub servlet_pool_size: usize,
pub servlet_pool_idle_timeout: Option<Duration>,
pub drain_timeout: Duration,
#[cfg(feature = "std")]
pub cluster_notify_retry: Arc<dyn CoreRetryPolicy + Send + Sync>,
#[cfg(feature = "x509")]
pub trust_store: Option<Arc<dyn CertificateTrust>>,
#[cfg(feature = "x509")]
pub hive_tls: Option<Arc<HiveTlsConfig>>,
}
impl<L: LoadBalancer + core::fmt::Debug, R: MessageRouter + core::fmt::Debug> core::fmt::Debug for HiveConf<L, R> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut d = f.debug_struct("HiveConf");
d.field("load_balancer", &self.load_balancer)
.field("router", &self.router)
.field("default_scale", &self.default_scale)
.field("servlet_overrides", &self.servlet_overrides)
.field("cooldown", &self.cooldown)
.field("queue_capacity", &self.queue_capacity)
.field("backpressure_threshold", &self.backpressure_threshold)
.field("circuit_breaker_threshold", &self.circuit_breaker_threshold)
.field("circuit_breaker_cooldown_ms", &self.circuit_breaker_cooldown_ms)
.field("servlet_pool_size", &self.servlet_pool_size)
.field("servlet_pool_idle_timeout", &self.servlet_pool_idle_timeout)
.field("drain_timeout", &self.drain_timeout);
#[cfg(feature = "std")]
d.field("cluster_notify_retry", &"<RetryPolicy>");
#[cfg(feature = "x509")]
d.field("trust_store", &self.trust_store.as_ref().map(|_| "<CertificateTrust>"));
#[cfg(feature = "x509")]
d.field("hive_tls", &self.hive_tls);
d.finish()
}
}
impl Default for HiveConf {
fn default() -> Self {
Self {
load_balancer: LeastLoaded,
router: TypeBasedRouter,
default_scale: ServletScaleConf::default(),
servlet_overrides: HashMap::new(),
cooldown: Duration::from_secs(5),
queue_capacity: 100,
backpressure_threshold: BasisPoints::new(DEFAULT_BACKPRESSURE_THRESHOLD_BPS),
circuit_breaker_threshold: 3,
circuit_breaker_cooldown_ms: 30_000,
servlet_pool_size: 8,
servlet_pool_idle_timeout: Some(Duration::from_secs(30)),
drain_timeout: Duration::from_secs(30),
#[cfg(feature = "std")]
cluster_notify_retry: Arc::new(crate::transport::policy::RestartExponentialBackoff {
max_attempts: 3,
scale_factor: 500,
jitter: Some(Box::new(crate::transport::policy::DecorrelatedJitter)),
}),
#[cfg(feature = "x509")]
trust_store: None,
#[cfg(feature = "x509")]
hive_tls: None,
}
}
}
#[path = "macros.rs"]
mod macros_impl;