use std::collections::{BTreeMap, BTreeSet, VecDeque};
use serde::{Deserialize, Serialize};
use super::ClusterNodeId;
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize,
)]
pub struct LogicalTime(u64);
impl LogicalTime {
pub const fn from_millis(millis: u64) -> Self {
Self(millis)
}
pub const fn as_millis(self) -> u64 {
self.0
}
pub const fn saturating_add(self, duration: LogicalDuration) -> Self {
Self(self.0.saturating_add(duration.as_millis()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct LogicalDuration(u64);
impl LogicalDuration {
pub const fn from_millis(millis: u64) -> Self {
Self(millis)
}
pub const fn as_millis(self) -> u64 {
self.0
}
}
impl Default for LogicalDuration {
fn default() -> Self {
Self(1_000)
}
}
pub trait ClusterClock {
fn now(&self) -> LogicalTime;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct ManualClusterClock {
now: LogicalTime,
}
impl ManualClusterClock {
pub const fn new(now: LogicalTime) -> Self {
Self { now }
}
pub fn set(&mut self, now: LogicalTime) {
self.now = now;
}
pub fn advance(&mut self, duration: LogicalDuration) {
self.now = self.now.saturating_add(duration);
}
}
impl ClusterClock for ManualClusterClock {
fn now(&self) -> LogicalTime {
self.now
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ClientOp {
Put { key: String, value: Vec<u8> },
Get { key: String },
Invalidate { key: String },
Flush,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ClientAck {
Accepted { sequence: u64 },
PendingStorage { request_id: u64 },
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ClusterNodeMessage {
Heartbeat { at: LogicalTime, sequence: u64 },
ReplicatePut {
key: String,
value: Vec<u8>,
sequence: u64,
},
ReplicateInvalidate { key: String, sequence: u64 },
ReplicateFlush { sequence: u64 },
Ack { sequence: u64 },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OutboundClusterMessage {
pub from: ClusterNodeId,
pub to: ClusterNodeId,
pub message: ClusterNodeMessage,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum StorageOpKind {
Read { key: String },
Write { key: String, value: Vec<u8> },
Delete { key: String },
Flush,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StorageOp {
pub request_id: u64,
pub kind: StorageOpKind,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StorageResult {
pub request_id: u64,
pub value: Option<Vec<u8>>,
}
pub trait ClusterStorage {
fn apply(&mut self, op: StorageOp) -> StorageResult;
}
#[derive(Debug, Clone, Default)]
pub struct InMemoryClusterStorage {
values: BTreeMap<String, Vec<u8>>,
}
impl InMemoryClusterStorage {
pub fn get(&self, key: &str) -> Option<&[u8]> {
self.values.get(key).map(Vec::as_slice)
}
}
impl ClusterStorage for InMemoryClusterStorage {
fn apply(&mut self, op: StorageOp) -> StorageResult {
let value = match op.kind {
StorageOpKind::Read { key } => self.values.get(&key).cloned(),
StorageOpKind::Write { key, value } => {
self.values.insert(key, value);
None
}
StorageOpKind::Delete { key } => {
self.values.remove(&key);
None
}
StorageOpKind::Flush => {
self.values.clear();
None
}
};
StorageResult {
request_id: op.request_id,
value,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClusterNodeConfig {
pub node_id: ClusterNodeId,
pub peers: Vec<ClusterNodeId>,
pub heartbeat_interval: LogicalDuration,
}
impl ClusterNodeConfig {
pub fn new(node_id: impl Into<ClusterNodeId>, peers: Vec<ClusterNodeId>) -> Self {
let node_id = node_id.into();
let peers = peers
.into_iter()
.filter(|peer| peer != &node_id)
.collect::<BTreeSet<_>>()
.into_iter()
.collect();
Self {
node_id,
peers,
heartbeat_interval: LogicalDuration::default(),
}
}
pub fn heartbeat_interval(mut self, interval: LogicalDuration) -> Self {
self.heartbeat_interval = interval;
self
}
}
#[derive(Debug, Clone)]
pub struct ClusterNode {
config: ClusterNodeConfig,
next_sequence: u64,
next_storage_request: u64,
last_heartbeat_at: Option<LogicalTime>,
outbound: VecDeque<OutboundClusterMessage>,
storage_requests: VecDeque<StorageOp>,
storage_results: BTreeMap<u64, StorageResult>,
}
impl ClusterNode {
pub fn new(config: ClusterNodeConfig) -> Self {
Self {
config,
next_sequence: 1,
next_storage_request: 1,
last_heartbeat_at: None,
outbound: VecDeque::new(),
storage_requests: VecDeque::new(),
storage_results: BTreeMap::new(),
}
}
pub fn node_id(&self) -> &ClusterNodeId {
&self.config.node_id
}
pub fn peers(&self) -> &[ClusterNodeId] {
&self.config.peers
}
pub fn tick(&mut self, now: LogicalTime) {
let should_heartbeat = self
.last_heartbeat_at
.map(|last| {
now.as_millis().saturating_sub(last.as_millis())
>= self.config.heartbeat_interval.as_millis()
})
.unwrap_or(true);
if should_heartbeat {
self.last_heartbeat_at = Some(now);
let sequence = self.next_sequence();
self.broadcast(ClusterNodeMessage::Heartbeat { at: now, sequence });
}
}
pub fn handle_message(&mut self, from: ClusterNodeId, message: ClusterNodeMessage) {
match message {
ClusterNodeMessage::Heartbeat { sequence, .. } => {
self.enqueue_outbound(from, ClusterNodeMessage::Ack { sequence });
}
ClusterNodeMessage::Ack { .. } => {}
ClusterNodeMessage::ReplicatePut {
key,
value,
sequence,
} => {
self.enqueue_storage(StorageOpKind::Write { key, value });
self.enqueue_outbound(from, ClusterNodeMessage::Ack { sequence });
}
ClusterNodeMessage::ReplicateInvalidate { key, sequence } => {
self.enqueue_storage(StorageOpKind::Delete { key });
self.enqueue_outbound(from, ClusterNodeMessage::Ack { sequence });
}
ClusterNodeMessage::ReplicateFlush { sequence } => {
self.enqueue_storage(StorageOpKind::Flush);
self.enqueue_outbound(from, ClusterNodeMessage::Ack { sequence });
}
}
}
pub fn handle_client(&mut self, op: ClientOp) -> ClientAck {
match op {
ClientOp::Put { key, value } => {
let sequence = self.next_sequence();
self.enqueue_storage(StorageOpKind::Write {
key: key.clone(),
value: value.clone(),
});
self.broadcast(ClusterNodeMessage::ReplicatePut {
key,
value,
sequence,
});
ClientAck::Accepted { sequence }
}
ClientOp::Get { key } => {
let request_id = self.enqueue_storage(StorageOpKind::Read { key });
ClientAck::PendingStorage { request_id }
}
ClientOp::Invalidate { key } => {
let sequence = self.next_sequence();
self.enqueue_storage(StorageOpKind::Delete { key: key.clone() });
self.broadcast(ClusterNodeMessage::ReplicateInvalidate { key, sequence });
ClientAck::Accepted { sequence }
}
ClientOp::Flush => {
let sequence = self.next_sequence();
self.enqueue_storage(StorageOpKind::Flush);
self.broadcast(ClusterNodeMessage::ReplicateFlush { sequence });
ClientAck::Accepted { sequence }
}
}
}
pub fn take_outbound(&mut self) -> Vec<OutboundClusterMessage> {
self.outbound.drain(..).collect()
}
pub fn storage_requests(&mut self) -> Vec<StorageOp> {
self.storage_requests.drain(..).collect()
}
pub fn apply_storage_result(&mut self, result: StorageResult) {
self.storage_results.insert(result.request_id, result);
}
pub fn storage_result(&self, request_id: u64) -> Option<&StorageResult> {
self.storage_results.get(&request_id)
}
fn next_sequence(&mut self) -> u64 {
let sequence = self.next_sequence;
self.next_sequence = self.next_sequence.saturating_add(1);
sequence
}
fn enqueue_storage(&mut self, kind: StorageOpKind) -> u64 {
let request_id = self.next_storage_request;
self.next_storage_request = self.next_storage_request.saturating_add(1);
self.storage_requests
.push_back(StorageOp { request_id, kind });
request_id
}
fn broadcast(&mut self, message: ClusterNodeMessage) {
for peer in self.config.peers.clone() {
self.enqueue_outbound(peer, message.clone());
}
}
fn enqueue_outbound(&mut self, to: ClusterNodeId, message: ClusterNodeMessage) {
self.outbound.push_back(OutboundClusterMessage {
from: self.config.node_id.clone(),
to,
message,
});
}
}