use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Instant;
use mongreldb_types::hlc::HlcTimestamp;
use mongreldb_types::ids::TransactionId;
use crate::envelope::{CommandEnvelope, EnvelopeError};
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub struct LogPosition {
pub term: u64,
pub index: u64,
}
impl LogPosition {
pub const ZERO: Self = Self { term: 0, index: 0 };
}
#[derive(Debug, Clone)]
pub struct CommittedEntry {
pub position: LogPosition,
pub commit_ts: HlcTimestamp,
pub envelope: CommandEnvelope,
}
#[derive(Debug, Clone)]
pub struct LogSnapshot {
pub position: LogPosition,
pub commit_ts: HlcTimestamp,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum DurabilityLevel {
GroupCommit,
LeaderDisk,
Quorum,
}
#[derive(Debug, Clone)]
pub struct CommitReceipt {
pub transaction_id: TransactionId,
pub commit_ts: HlcTimestamp,
pub log_position: LogPosition,
pub durability: DurabilityLevel,
}
#[derive(Debug, Clone, Default)]
pub struct ExecutionControl {
pub deadline: Option<Instant>,
pub cancellation: Option<Arc<AtomicBool>>,
}
impl ExecutionControl {
pub fn check(&self) -> Result<(), LogError> {
if let Some(flag) = &self.cancellation {
if flag.load(Ordering::Relaxed) {
return Err(LogError::Cancelled);
}
}
if let Some(deadline) = self.deadline {
if Instant::now() >= deadline {
return Err(LogError::DeadlineExceeded);
}
}
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum LogError {
#[error(transparent)]
Envelope(#[from] EnvelopeError),
#[error("operation cancelled")]
Cancelled,
#[error("deadline exceeded")]
DeadlineExceeded,
#[error("commit log is closed")]
Closed,
#[error("not the leader (current leader: {leader_hint:?})")]
NotLeader {
leader_hint: Option<String>,
},
#[error("unsupported operation: {0}")]
Unsupported(&'static str),
#[error("commit log failure: {0}")]
Internal(String),
}
pub trait CommitLog: Send + Sync {
fn propose(
&self,
command: CommandEnvelope,
control: &ExecutionControl,
) -> Result<CommitReceipt, LogError>;
fn read_committed(
&self,
after: LogPosition,
limit: usize,
) -> Result<Vec<CommittedEntry>, LogError>;
fn applied_position(&self) -> LogPosition;
fn create_snapshot(&self) -> Result<LogSnapshot, LogError>;
fn install_snapshot(&self, snapshot: LogSnapshot) -> Result<(), LogError>;
}