use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::num::NonZeroU16;
use std::sync::Arc;
use std::sync::atomic::AtomicU64;
use std::time::Duration;
use crate::{AnyRunnable, Error, Result, RunContext, SharedState};
use super::{Scope, Scoped};
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize,
)]
#[serde(rename_all = "lowercase")]
pub enum Dispatch {
#[default]
Each,
One,
Pool,
}
impl Dispatch {
pub const fn is_controller_driven(self) -> bool {
matches!(self, Self::One | Self::Pool)
}
pub fn validate_policy(
self,
policy: Option<DispatchPolicy>,
) -> Result<()> {
if self.is_controller_driven() {
policy
.ok_or_else(|| {
Error::msg("dispatch one/pool requires dispatch_policy")
})?
.validate()?;
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct DispatchPolicy {
pub capacity: NonZeroU16,
#[serde(with = "humantime_serde")]
pub lease_ttl: Duration,
#[serde(with = "humantime_serde")]
pub renewal_interval: Duration,
}
impl DispatchPolicy {
pub fn validate(&self) -> Result<()> {
if self.lease_ttl.is_zero() {
return Err(Error::msg("dispatch lease_ttl must be nonzero"));
}
if self.renewal_interval.is_zero()
|| self.renewal_interval >= self.lease_ttl
{
return Err(Error::msg(
"dispatch renewal_interval must be nonzero and shorter than lease_ttl",
));
}
if tokio::time::Instant::now()
.checked_add(self.renewal_interval)
.is_none()
{
return Err(Error::msg(
"dispatch renewal_interval exceeds the runtime clock",
));
}
Ok(())
}
}
pub trait Dispatched: Scoped {
fn dispatch(&self) -> Dispatch;
fn dispatch_policy(&self) -> Option<DispatchPolicy> {
None
}
}
#[derive(Clone, Debug)]
pub struct InstancePlacement {
pub scope: Scope,
pub dispatch: Dispatch,
pub policy: Option<DispatchPolicy>,
}
impl Scoped for InstancePlacement {
fn scope(&self) -> Scope {
self.scope
}
}
impl Dispatched for InstancePlacement {
fn dispatch(&self) -> Dispatch {
self.dispatch
}
fn dispatch_policy(&self) -> Option<DispatchPolicy> {
self.policy
}
}
#[derive(Clone, Debug)]
pub struct DispatchRequest {
pub name: Arc<str>,
pub generation: u64,
pub scope: Scope,
pub dispatch: Dispatch,
pub policy: Option<DispatchPolicy>,
}
pub struct ServiceExecution<S> {
runnable: Arc<dyn AnyRunnable<S>>,
state: S,
active_executions: Arc<AtomicU64>,
}
impl<S: Clone> Clone for ServiceExecution<S> {
fn clone(&self) -> Self {
Self {
runnable: self.runnable.clone(),
state: self.state.clone(),
active_executions: self.active_executions.clone(),
}
}
}
impl<S: SharedState> ServiceExecution<S> {
pub fn state(&self) -> &S {
&self.state
}
pub(crate) fn new(
runnable: Arc<dyn AnyRunnable<S>>,
state: S,
active_executions: Arc<AtomicU64>,
) -> Self {
Self { runnable, state, active_executions }
}
pub async fn run(
&self,
context: RunContext,
) -> Result<()> {
let context = context.for_execution();
let _execution =
crate::service::ExecutionGuard::enter(&self.active_executions);
crate::service::run_owned(
context.clone(),
self.runnable.clone().run(self.state.clone(), context),
)
.await
}
}
#[async_trait]
pub trait Dispatcher<S>: Send + Sync + 'static {
fn validate(
&self,
request: &DispatchRequest,
state: &S,
) -> Result<()>;
async fn run(
&self,
request: DispatchRequest,
execution: ServiceExecution<S>,
context: RunContext,
) -> Result<()>;
}