use std::collections::VecDeque;
use crate::cluster::{ClusterGeneration, PartitionId};
use crate::invalidation_bus::CacheInvalidation;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidationEvent {
pub sequence: u64,
pub partition: PartitionId,
pub invalidation: CacheInvalidation,
pub source_generation: Option<ClusterGeneration>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReplayResult {
Range(Vec<InvalidationEvent>),
FellBehind { clear_partition: PartitionId },
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct InvalidationRingMetrics {
pub invalidation_ring_depth: u64,
pub invalidation_replayed_total: u64,
pub invalidation_fell_behind_total: u64,
pub invalidation_ring_overrun_total: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidationRing {
partition: PartitionId,
capacity: usize,
head_seq: u64,
next_seq: u64,
events: VecDeque<InvalidationEvent>,
metrics: InvalidationRingMetrics,
}
impl InvalidationRing {
pub fn new(partition: PartitionId, capacity: usize) -> Self {
Self {
partition,
capacity: capacity.max(1),
head_seq: 0,
next_seq: 0,
events: VecDeque::new(),
metrics: InvalidationRingMetrics::default(),
}
}
pub fn partition(&self) -> PartitionId {
self.partition
}
pub fn head_seq(&self) -> u64 {
self.head_seq
}
pub fn next_seq(&self) -> u64 {
self.next_seq
}
pub fn metrics(&self) -> InvalidationRingMetrics {
let mut metrics = self.metrics;
metrics.invalidation_ring_depth = self.events.len() as u64;
metrics
}
pub fn publish(&mut self, invalidation: CacheInvalidation) -> u64 {
self.publish_with_generation(invalidation, None)
}
pub fn publish_with_generation(
&mut self,
invalidation: CacheInvalidation,
source_generation: Option<ClusterGeneration>,
) -> u64 {
let sequence = self.next_seq;
self.next_seq = self.next_seq.saturating_add(1);
if self.events.len() == self.capacity {
self.events.pop_front();
self.metrics.invalidation_ring_overrun_total = self
.metrics
.invalidation_ring_overrun_total
.saturating_add(1);
}
self.events.push_back(InvalidationEvent {
sequence,
partition: self.partition,
invalidation,
source_generation,
});
self.head_seq = self
.events
.front()
.map(|event| event.sequence)
.unwrap_or(self.next_seq);
sequence
}
pub fn replay_from(&mut self, last_seen: u64) -> ReplayResult {
if self.events.is_empty() {
return ReplayResult::Range(Vec::new());
}
if last_seen.saturating_add(1) < self.head_seq {
self.metrics.invalidation_fell_behind_total = self
.metrics
.invalidation_fell_behind_total
.saturating_add(1);
return ReplayResult::FellBehind {
clear_partition: self.partition,
};
}
let range = self
.events
.iter()
.filter(|event| event.sequence > last_seen)
.cloned()
.collect::<Vec<_>>();
self.metrics.invalidation_replayed_total = self
.metrics
.invalidation_replayed_total
.saturating_add(range.len() as u64);
ReplayResult::Range(range)
}
pub fn snapshot(&self) -> InvalidationRingSnapshot {
InvalidationRingSnapshot {
partition: self.partition,
capacity: self.capacity,
head_seq: self.head_seq,
next_seq: self.next_seq,
events: self.events.iter().cloned().collect(),
}
}
pub fn restore(snapshot: InvalidationRingSnapshot) -> Self {
Self {
partition: snapshot.partition,
capacity: snapshot.capacity.max(1),
head_seq: snapshot.head_seq,
next_seq: snapshot.next_seq,
events: snapshot.events.into(),
metrics: InvalidationRingMetrics::default(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidationRingSnapshot {
pub partition: PartitionId,
pub capacity: usize,
pub head_seq: u64,
pub next_seq: u64,
pub events: Vec<InvalidationEvent>,
}