use core::fmt;
use core::num::NonZeroUsize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct EventId(u64);
impl EventId {
pub const WATERMARK: Self = Self(0);
#[must_use]
pub const fn new(value: u64) -> Self {
Self(value)
}
#[must_use]
pub const fn get(self) -> u64 {
self.0
}
#[must_use]
pub const fn is_watermark(self) -> bool {
self.0 == 0
}
}
impl fmt::Display for EventId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StreamEvent<T> {
id: EventId,
payload: T,
}
impl<T> StreamEvent<T> {
#[must_use]
pub const fn new(id: EventId, payload: T) -> Self {
Self { id, payload }
}
#[must_use]
pub const fn id(&self) -> EventId {
self.id
}
#[must_use]
pub const fn payload(&self) -> &T {
&self.payload
}
#[must_use]
pub fn into_parts(self) -> (EventId, T) {
(self.id, self.payload)
}
#[must_use]
pub const fn payload_mut(&mut self) -> &mut T {
&mut self.payload
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Replay<'a, T> {
UpToDate {
last: EventId,
},
Buffered {
events: &'a [StreamEvent<T>],
last: EventId,
},
Gap {
requested: EventId,
evicted_through: EventId,
last: EventId,
},
Unknown {
requested: EventId,
last: EventId,
},
}
impl<T> Replay<'_, T> {
#[must_use]
pub const fn is_complete(&self) -> bool {
matches!(self, Self::UpToDate { .. } | Self::Buffered { .. })
}
#[must_use]
pub const fn events(&self) -> &[StreamEvent<T>] {
match self {
Self::Buffered { events, .. } => events,
Self::UpToDate { .. } | Self::Gap { .. } | Self::Unknown { .. } => &[],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RecommendedCapacity(NonZeroUsize);
impl RecommendedCapacity {
#[must_use]
pub const fn get(self) -> usize {
self.0.get()
}
}
impl From<RecommendedCapacity> for NonZeroUsize {
fn from(value: RecommendedCapacity) -> Self {
value.0
}
}
pub const RECOMMENDED_CAPACITY: RecommendedCapacity =
RecommendedCapacity(match NonZeroUsize::new(1024) {
Some(value) => value,
None => NonZeroUsize::MIN,
});
#[derive(Debug)]
pub struct EventRing<T> {
capacity: NonZeroUsize,
buffer: Vec<StreamEvent<T>>,
start: usize,
last: EventId,
evicted_through: Option<EventId>,
retired_through: Option<EventId>,
}
impl<T> EventRing<T> {
#[must_use]
pub fn new(capacity: NonZeroUsize) -> Self {
Self {
capacity,
buffer: Vec::with_capacity(capacity.get()),
start: 0,
last: EventId::WATERMARK,
evicted_through: None,
retired_through: None,
}
}
#[must_use]
pub fn with_recommended_capacity() -> Self {
Self::new(RECOMMENDED_CAPACITY.into())
}
#[must_use]
pub const fn capacity(&self) -> usize {
self.capacity.get()
}
#[must_use]
pub const fn last(&self) -> EventId {
self.last
}
#[must_use]
pub const fn len(&self) -> usize {
self.buffer.len() - self.start
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub const fn evicted_through(&self) -> Option<EventId> {
self.evicted_through
}
pub fn push(&mut self, payload: T) -> EventId {
if self.len() == self.capacity.get() {
self.evict_one();
}
self.last = EventId(self.last.get().saturating_add(1));
self.buffer.push(StreamEvent::new(self.last, payload));
self.last
}
fn window(&self) -> &[StreamEvent<T>] {
self.buffer.get(self.start..).unwrap_or(&[])
}
fn evict_one(&mut self) {
if let Some(dropped) = self.buffer.get(self.start) {
self.evicted_through = Some(dropped.id());
}
self.start += 1;
if self.start == self.buffer.len() {
self.buffer.clear();
self.start = 0;
} else if self.start >= self.capacity.get() {
self.buffer.drain(..self.start);
self.start = 0;
}
}
#[must_use]
pub fn replay_from(&self, after: Option<EventId>) -> Replay<'_, T> {
let last = self.last;
let Some(requested) = after else {
if self.is_empty() {
return Replay::UpToDate { last };
}
return Replay::Buffered {
events: self.window(),
last,
};
};
if requested == last {
return Replay::UpToDate { last };
}
if requested > last {
return Replay::Unknown { requested, last };
}
if let Some(retired) = self.retired_through
&& requested <= retired
{
return Replay::Unknown { requested, last };
}
if let Some(evicted_through) = self.evicted_through
&& requested < evicted_through
{
return Replay::Gap {
requested,
evicted_through,
last,
};
}
let offset = usize::try_from(last.get() - requested.get())
.ok()
.and_then(|behind| self.len().checked_sub(behind))
.map(|offset| self.start + offset);
offset
.and_then(|start| self.buffer.get(start..))
.map_or_else(
|| Replay::Gap {
requested,
evicted_through: self.evicted_through.unwrap_or(EventId::WATERMARK),
last,
},
|events| Replay::Buffered { events, last },
)
}
pub fn clear(&mut self) {
self.buffer.clear();
self.start = 0;
self.retired_through = Some(self.last);
self.evicted_through = None;
}
}
impl<T> Default for EventRing<T> {
fn default() -> Self {
Self::with_recommended_capacity()
}
}
#[cfg(test)]
mod tests {
use super::{EventId, EventRing, RECOMMENDED_CAPACITY, Replay, StreamEvent};
use core::num::NonZeroUsize;
use std::sync::Arc;
use std::thread;
fn ring<T>(capacity: usize) -> Result<EventRing<T>, Failure> {
let capacity = NonZeroUsize::new(capacity).ok_or(Failure::ZeroCapacity)?;
Ok(EventRing::new(capacity))
}
#[derive(Debug, PartialEq, Eq)]
enum Failure {
ZeroCapacity,
WrongIds(Vec<u64>),
WrongPayloads(Vec<String>),
Outcome(&'static str),
Concurrent,
}
fn payloads<T: Clone + core::fmt::Debug>(replay: &Replay<'_, T>) -> Vec<String> {
replay
.events()
.iter()
.map(|event| format!("{:?}", event.payload()))
.collect()
}
fn ids<T>(replay: &Replay<'_, T>) -> Vec<u64> {
replay
.events()
.iter()
.map(|event| event.id().get())
.collect()
}
#[test]
fn first_continuation_returns_everything_in_order() -> Result<(), Failure> {
let mut ring = ring(8)?;
let mut assigned = Vec::new();
for payload in ["a", "b", "c"] {
assigned.push(ring.push(payload).get());
}
let replay = ring.replay_from(None);
if !replay.is_complete() {
return Err(Failure::Outcome("a fresh client must be served whole"));
}
if ids(&replay) != assigned {
return Err(Failure::WrongIds(assigned));
}
if payloads(&replay) != vec!["\"a\"", "\"b\"", "\"c\""] {
return Err(Failure::WrongPayloads(payloads(&replay)));
}
Ok(())
}
#[test]
fn resume_sends_only_what_follows() -> Result<(), Failure> {
let mut ring = ring(8)?;
let first = ring.push("a");
let second = ring.push("b");
let replay = ring.replay_from(Some(first));
if ids(&replay) != vec![second.get()] {
return Err(Failure::WrongIds(ids(&replay)));
}
if payloads(&replay) != vec!["\"b\""] {
return Err(Failure::WrongPayloads(payloads(&replay)));
}
Ok(())
}
#[test]
fn a_current_client_is_told_it_is_current() -> Result<(), Failure> {
let mut ring = ring(8)?;
ring.push("a");
match ring.replay_from(Some(ring.last())) {
Replay::UpToDate { last } if last.get() == 1 => Ok(()),
other => Err(Failure::Outcome(match other {
Replay::UpToDate { .. } => "wrong last id",
Replay::Buffered { .. } => "a current client was sent events",
Replay::Gap { .. } => "reported a gap with nothing dropped",
Replay::Unknown { .. } => "reported an unknown id for a current client",
})),
}
}
#[test]
fn replay_from_a_forgotten_event_is_a_named_gap_not_an_empty_success() -> Result<(), Failure> {
let mut ring = ring(3)?;
let first = ring.push("a");
ring.push("b");
ring.push("c");
let fourth = ring.push("d");
let asked = EventId::WATERMARK;
let _ = first;
match ring.replay_from(Some(asked)) {
Replay::Gap {
requested,
evicted_through,
last,
} => {
if requested != asked {
return Err(Failure::WrongIds(vec![requested.get()]));
}
if last != fourth {
return Err(Failure::WrongIds(vec![last.get()]));
}
if evicted_through.get() <= requested.get() {
return Err(Failure::WrongIds(vec![evicted_through.get()]));
}
if evicted_through.get() > last.get() {
return Err(Failure::WrongIds(vec![evicted_through.get()]));
}
Ok(())
}
Replay::UpToDate { .. } => Err(Failure::Outcome(
"a dropped event was reported as up to date",
)),
Replay::Buffered { .. } => {
Err(Failure::Outcome("a dropped event was reported as buffered"))
}
Replay::Unknown { .. } => Err(Failure::Outcome(
"a dropped event was reported as an unknown id",
)),
}
}
#[test]
fn resume_from_the_dropped_event_is_still_contiguous() -> Result<(), Failure> {
let mut ring = ring(2)?;
ring.push("a");
let second = ring.push("b");
let third = ring.push("c");
let replay = ring.replay_from(Some(second));
if !replay.is_complete() {
return Err(Failure::Outcome(
"resuming at the drop point should be complete",
));
}
if ids(&replay) != vec![third.get()] {
return Err(Failure::WrongIds(ids(&replay)));
}
Ok(())
}
#[test]
fn an_id_from_the_future_is_unknown_not_a_gap() -> Result<(), Failure> {
let mut ring = ring(4)?;
ring.push("a");
match ring.replay_from(Some(EventId::new(99))) {
Replay::Unknown { requested, last } => {
if requested.get() != 99 || last.get() != 1 {
return Err(Failure::WrongIds(vec![requested.get(), last.get()]));
}
Ok(())
}
_ => Err(Failure::Outcome("a future id was not reported as unknown")),
}
}
#[test]
fn a_ring_that_fits_everything_never_reports_a_gap() -> Result<(), Failure> {
let mut ring = EventRing::new(NonZeroUsize::new(16).ok_or(Failure::ZeroCapacity)?);
let mut last = EventId::WATERMARK;
for index in 0..15 {
last = ring.push(index);
}
for watermark in [EventId::WATERMARK, EventId::new(1), EventId::new(7), last] {
if !ring.replay_from(Some(watermark)).is_complete() {
return Err(Failure::Outcome("a ring holding everything reported a gap"));
}
}
Ok(())
}
#[test]
fn ids_stay_monotonic_across_eviction() -> Result<(), Failure> {
let mut ring = ring(2)?;
let mut assigned = Vec::new();
for index in 0..10 {
assigned.push(ring.push(index).get());
}
let expected: Vec<u64> = (1..=10).collect();
if assigned != expected {
return Err(Failure::WrongIds(assigned));
}
if ring.last().get() != 10 {
return Err(Failure::WrongIds(vec![ring.last().get()]));
}
if ring.len() != 2 {
return Err(Failure::WrongIds(vec![
ring.len().try_into().unwrap_or(u64::MAX),
]));
}
Ok(())
}
#[test]
fn compaction_keeps_the_buffer_from_growing() -> Result<(), Failure> {
let mut ring = ring(4)?;
for index in 0..1_000 {
ring.push(index);
}
if ring.len() != 4 {
return Err(Failure::WrongIds(vec![
ring.len().try_into().unwrap_or(u64::MAX),
]));
}
let allocated = ring.buffer.len();
if allocated > ring.capacity() * 2 {
return Err(Failure::WrongIds(vec![
allocated.try_into().unwrap_or(u64::MAX),
]));
}
Ok(())
}
#[test]
fn recommended_capacity_covers_a_reconnect() -> Result<(), Failure> {
const RECONNECT_SECONDS: usize = 1_020;
if RECOMMENDED_CAPACITY.get() < RECONNECT_SECONDS {
return Err(Failure::WrongIds(vec![
RECOMMENDED_CAPACITY.get().try_into().unwrap_or(u64::MAX),
]));
}
if RECOMMENDED_CAPACITY.get() > 65_536 {
return Err(Failure::WrongIds(vec![
RECOMMENDED_CAPACITY.get().try_into().unwrap_or(u64::MAX),
]));
}
Ok(())
}
#[test]
fn clear_starts_a_new_stream_and_refuses_the_old_ids() -> Result<(), Failure> {
let mut ring = ring(4)?;
let before = ring.push("a");
ring.push("b");
ring.clear();
if !ring.is_empty() {
return Err(Failure::Outcome("clear left events behind"));
}
if !matches!(ring.replay_from(Some(before)), Replay::Unknown { .. }) {
return Err(Failure::Outcome("a cleared stream served a pre-reset id"));
}
let after = ring.push("c");
if after.get() != 3 {
return Err(Failure::WrongIds(vec![after.get()]));
}
Ok(())
}
#[test]
fn quoting_a_dropped_id_is_servable_but_older_is_not() -> Result<(), Failure> {
let mut ring = ring(3)?;
let first = ring.push("a");
ring.push("b");
ring.push("c");
ring.push("d");
let held = match ring.replay_from(Some(first)) {
Replay::Buffered { events, .. } => events,
Replay::UpToDate { .. } => {
return Err(Failure::Outcome("a held id was reported as up to date"));
}
Replay::Gap { .. } => {
return Err(Failure::Outcome(
"a client holding the dropped id was told it was behind",
));
}
Replay::Unknown { .. } => {
return Err(Failure::Outcome(
"a client holding the dropped id was told it was unknown",
));
}
};
let ids: Vec<u64> = held.iter().map(|event| event.id().get()).collect();
if ids != vec![2, 3, 4] {
return Err(Failure::WrongIds(ids));
}
if !matches!(
ring.replay_from(Some(EventId::WATERMARK)),
Replay::Gap { .. }
) {
return Err(Failure::Outcome("a cursor below the drop was not a gap"));
}
Ok(())
}
#[test]
fn a_single_slot_ring_still_reports_eviction() -> Result<(), Failure> {
let mut ring = ring(1)?;
let first = ring.push("a");
ring.push("b");
if !matches!(
ring.replay_from(Some(EventId::WATERMARK)),
Replay::Gap { .. }
) {
return Err(Failure::Outcome(
"a one-slot ring did not report its eviction",
));
}
if !matches!(ring.replay_from(Some(first)), Replay::Buffered { .. }) {
return Err(Failure::Outcome(
"a client holding the dropped id was refused its own successor",
));
}
Ok(())
}
#[test]
fn an_empty_ring_has_no_gap() -> Result<(), Failure> {
let ring = ring::<&str>(4)?;
if !matches!(ring.replay_from(None), Replay::UpToDate { .. }) {
return Err(Failure::Outcome("an empty ring refused a fresh client"));
}
if !matches!(
ring.replay_from(Some(EventId::WATERMARK)),
Replay::UpToDate { .. }
) {
return Err(Failure::Outcome("an empty ring refused the watermark"));
}
if !matches!(
ring.replay_from(Some(EventId::new(1))),
Replay::Unknown { .. }
) {
return Err(Failure::Outcome("an empty ring invented an event"));
}
Ok(())
}
#[test]
fn completeness_agrees_with_the_arm() -> Result<(), Failure> {
let mut ring = ring(2)?;
ring.push("a");
ring.push("b");
ring.push("c");
let cases = [
(ring.replay_from(None), true),
(ring.replay_from(Some(ring.last())), true),
(ring.replay_from(Some(EventId::WATERMARK)), false),
(ring.replay_from(Some(EventId::new(77))), false),
];
for (replay, expected) in cases {
if replay.is_complete() != expected {
return Err(Failure::Outcome("is_complete disagreed with the arm"));
}
}
Ok(())
}
#[test]
fn a_replay_borrows_rather_than_copying() -> Result<(), Failure> {
let mut ring = ring::<Arc<str>>(4)?;
ring.push(Arc::<str>::from("a"));
ring.push(Arc::<str>::from("b"));
let replay = ring.replay_from(None);
let borrowed: &[StreamEvent<Arc<str>>] = replay.events();
if ring.len() != 2 {
return Err(Failure::WrongIds(vec![
ring.len().try_into().unwrap_or(u64::MAX),
]));
}
let first = borrowed
.first()
.ok_or(Failure::Outcome("no event borrowed"))?;
if Arc::strong_count(first.payload()) < 1 {
return Err(Failure::Outcome("payload was cloned rather than borrowed"));
}
Ok(())
}
#[test]
fn is_shareable_across_threads() -> Result<(), Failure> {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<EventRing<u64>>();
assert_send_sync::<Replay<'static, u64>>();
let ring = Arc::new(std::sync::Mutex::new(EventRing::new(
NonZeroUsize::new(64).ok_or(Failure::ZeroCapacity)?,
)));
let writer = {
let ring = Arc::clone(&ring);
thread::spawn(move || {
let mut held = ring.lock().map_err(|_| Failure::Concurrent)?;
for value in 0..1_000_u64 {
held.push(value);
}
drop(held);
Ok::<(), Failure>(())
})
};
let reader = {
let ring = Arc::clone(&ring);
thread::spawn(move || {
let mut watermark = EventId::WATERMARK;
for _ in 0..1_000 {
let held = ring.lock().map_err(|_| Failure::Concurrent)?;
let replay = held.replay_from(Some(watermark));
if replay.is_complete()
&& watermark.get() < held.last().get()
&& replay.events().is_empty()
{
return Err(Failure::Outcome("a complete replay was empty"));
}
for event in replay.events() {
if event.id().get() <= watermark.get() {
return Err(Failure::Outcome("an event was replayed twice"));
}
watermark = event.id();
}
drop(held);
}
Ok::<(), Failure>(())
})
};
if writer.join().map_err(|_| Failure::Concurrent)? != Ok(()) {
return Err(Failure::Outcome("the writer thread failed"));
}
if reader.join().map_err(|_| Failure::Concurrent)? != Ok(()) {
return Err(Failure::Outcome("the reader thread failed"));
}
Ok(())
}
}