use std::sync::Arc;
use bytes::Bytes;
use crate::config::RetentionPolicy;
use crate::error::DurableError;
use crate::ids::{ExecutionId, IdempotencyKey, JournalSeq, PromiseId, TimerId};
use crate::journal::{ExecutionStatus, Journal, JournalEntry};
use crate::promise::PromiseRecord;
use crate::waiters::NotifyRegistry;
pub mod execution_lock;
pub mod local;
pub use execution_lock::ExecutionLock;
pub use local::{CancelOutcome, LocalBackend};
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
pub struct ExecutionSummary {
pub execution_id: ExecutionId,
pub kind: String,
pub status: ExecutionStatus,
pub created_at_ms: i64,
pub updated_at_ms: i64,
pub finalized_at_ms: Option<i64>,
pub step_count: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
pub struct RedactedEntry {
pub seq: i64,
pub step_id: crate::ids::StepId,
pub entry_kind: String,
pub effect_class: Option<String>,
pub idem_key_prefix: Option<String>,
pub payload_len: u64,
pub created_at_ms: i64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BackendCapabilities {
pub parallel_steps: bool,
pub cross_process: bool,
pub max_payload: usize,
}
pub trait ExecutionBackend: Journal + Send + Sync + crate::sealed::Sealed {
fn capabilities(&self) -> BackendCapabilities;
fn lookup_committed_result(
&self,
id: ExecutionId,
idem_key: IdempotencyKey,
) -> impl std::future::Future<Output = Result<Option<JournalEntry>, DurableError>> + Send;
}
#[derive(Debug)]
#[non_exhaustive]
pub enum DurableBackendEnum {
Local(Arc<LocalBackend>),
}
impl crate::sealed::Sealed for DurableBackendEnum {}
impl Journal for DurableBackendEnum {
async fn append(&self, entry: JournalEntry) -> Result<JournalSeq, DurableError> {
match self {
Self::Local(backend) => backend.append(entry).await,
}
}
async fn read_execution(&self, id: ExecutionId) -> Result<Vec<JournalEntry>, DurableError> {
match self {
Self::Local(backend) => backend.read_execution(id).await,
}
}
async fn read_execution_range(
&self,
id: ExecutionId,
from_step_id: u32,
limit: usize,
) -> Result<Vec<JournalEntry>, DurableError> {
match self {
Self::Local(backend) => backend.read_execution_range(id, from_step_id, limit).await,
}
}
async fn finalize(&self, id: ExecutionId, status: ExecutionStatus) -> Result<(), DurableError> {
match self {
Self::Local(backend) => backend.finalize(id, status).await,
}
}
async fn prune(&self, policy: &RetentionPolicy) -> Result<u64, DurableError> {
match self {
Self::Local(backend) => backend.prune(policy).await,
}
}
async fn sweep_orphans(&self, policy: &RetentionPolicy) -> Result<u64, DurableError> {
match self {
Self::Local(backend) => backend.sweep_orphans(policy).await,
}
}
}
impl ExecutionBackend for DurableBackendEnum {
fn capabilities(&self) -> BackendCapabilities {
match self {
Self::Local(backend) => backend.capabilities(),
}
}
async fn lookup_committed_result(
&self,
id: ExecutionId,
idem_key: IdempotencyKey,
) -> Result<Option<JournalEntry>, DurableError> {
match self {
Self::Local(backend) => backend.lookup_committed_result(id, idem_key).await,
}
}
}
impl DurableBackendEnum {
pub async fn cancel_execution(&self, id: ExecutionId) -> Result<CancelOutcome, DurableError> {
match self {
Self::Local(backend) => backend.cancel_execution(id).await,
}
}
pub(crate) async fn insert_promise(
&self,
id: PromiseId,
execution_id: ExecutionId,
resolver_token_hash: [u8; 32],
created_at_ms: i64,
) -> Result<(), DurableError> {
match self {
Self::Local(backend) => {
backend
.insert_promise(id, execution_id, resolver_token_hash, created_at_ms)
.await
}
}
}
pub(crate) async fn promise_state(
&self,
id: PromiseId,
) -> Result<Option<PromiseRecord>, DurableError> {
match self {
Self::Local(backend) => backend.promise_state(id).await,
}
}
pub(crate) async fn resolve_promise(
&self,
id: PromiseId,
execution_id: ExecutionId,
value_plaintext: &[u8],
resolved_at_ms: i64,
) -> Result<bool, DurableError> {
match self {
Self::Local(backend) => {
backend
.resolve_promise(id, execution_id, value_plaintext, resolved_at_ms)
.await
}
}
}
pub(crate) async fn claim_promise_notification(
&self,
id: PromiseId,
notified_at_ms: i64,
) -> Result<bool, DurableError> {
match self {
Self::Local(backend) => backend.claim_promise_notification(id, notified_at_ms).await,
}
}
pub(crate) fn open_promise_payload(
&self,
id: PromiseId,
execution_id: ExecutionId,
sealed: &[u8],
) -> Result<Bytes, DurableError> {
match self {
Self::Local(backend) => backend.open_promise_payload(id, execution_id, sealed),
}
}
pub(crate) fn promise_waiters(&self) -> &NotifyRegistry {
match self {
Self::Local(backend) => backend.promise_waiters(),
}
}
pub(crate) async fn arm_timer(
&self,
id: TimerId,
execution_id: ExecutionId,
due_at_ms: i64,
created_at_ms: i64,
) -> Result<(), DurableError> {
match self {
Self::Local(backend) => {
backend
.arm_timer(id, execution_id, due_at_ms, created_at_ms)
.await
}
}
}
pub(crate) async fn timer_state(
&self,
id: TimerId,
) -> Result<Option<(i64, bool)>, DurableError> {
match self {
Self::Local(backend) => backend.timer_state(id).await,
}
}
pub(crate) async fn due_timers(&self, now_ms: i64) -> Result<Vec<TimerId>, DurableError> {
match self {
Self::Local(backend) => backend.due_timers(now_ms).await,
}
}
pub(crate) async fn mark_timer_fired(&self, id: TimerId) -> Result<bool, DurableError> {
match self {
Self::Local(backend) => backend.mark_timer_fired(id).await,
}
}
pub(crate) fn timer_waiters(&self) -> &NotifyRegistry {
match self {
Self::Local(backend) => backend.timer_waiters(),
}
}
pub(crate) async fn checkpoint_fold(
&self,
execution_id: ExecutionId,
up_to_step: u32,
) -> Result<u64, DurableError> {
match self {
Self::Local(backend) => backend.checkpoint_fold(execution_id, up_to_step).await,
}
}
pub(crate) async fn read_checkpoints(
&self,
execution_id: ExecutionId,
) -> Result<Vec<JournalEntry>, DurableError> {
match self {
Self::Local(backend) => backend.read_checkpoints(execution_id).await,
}
}
}