use super::Error;
use futures::{stream, Stream, StreamExt as _};
use std::{future::Future, num::NonZeroUsize, ops::Range};
use tracing::warn;
mod blobs;
mod checkpoint;
pub mod fixed;
mod metrics;
pub mod variable;
#[cfg(test)]
mod tests;
fn batch_count_to_blob_boundary(position: u64, remaining: usize, items_per_blob: u64) -> usize {
let pos_in_blob = position % items_per_blob;
let remaining_space = items_per_blob - pos_in_blob;
remaining_space.min(remaining as u64) as usize
}
const fn position_to_blob(position: u64, items_per_blob: u64) -> u64 {
position / items_per_blob
}
fn blob_first_position(blob: u64, items_per_blob: u64) -> Result<u64, Error> {
blob.checked_mul(items_per_blob)
.ok_or(Error::OffsetOverflow)
}
const fn blob_end_position(blob: u64, items_per_blob: u64, end: u64) -> u64 {
if end == 0 {
return 0;
}
let end_blob = (end - 1) / items_per_blob;
if blob >= end_blob {
return end;
}
(blob + 1) * items_per_blob
}
type ReplayBatch<S> = Option<(Vec<Result<(u64, <S as ReplayBatchState>::Item), Error>>, S)>;
trait ReplayBatchState: Sized {
type Item;
fn next_batch(self) -> impl Future<Output = ReplayBatch<Self>> + Send;
}
struct ReplayStreamState<S: ReplayBatchState> {
states: std::vec::IntoIter<S>,
current: Option<S>,
done: bool,
}
impl<S: ReplayBatchState + Send> ReplayStreamState<S>
where
S::Item: Send,
{
async fn next(mut self) -> Option<(Vec<Result<(u64, S::Item), Error>>, Self)> {
loop {
if self.done {
return None;
}
let state = match self.current.take().or_else(|| self.states.next()) {
Some(state) => state,
None => return None,
};
match state.next_batch().await {
Some((batch, state)) => {
if batch.iter().any(Result::is_err) {
self.done = true;
self.current = None;
} else {
self.current = Some(state);
}
return Some((batch, self));
}
None => {
self.current = None;
}
}
}
}
}
fn replay_stream_from_states<S>(
states: Vec<S>,
) -> impl Stream<Item = Result<(u64, S::Item), Error>> + Send
where
S: ReplayBatchState + Send,
S::Item: Send,
{
stream::unfold(
ReplayStreamState {
states: states.into_iter(),
current: None,
done: false,
},
ReplayStreamState::next,
)
.flat_map(stream::iter)
}
pub trait Contiguous: Send + Sync {
type Item: Send;
fn bounds(&self) -> Range<u64>;
fn read(&self, position: u64) -> impl Future<Output = Result<Self::Item, Error>> + Send + Sync;
fn read_many(
&self,
positions: &[u64],
) -> impl Future<Output = Result<Vec<Self::Item>, Error>> + Send
where
Self::Item: Send;
fn try_read_sync(&self, position: u64) -> Option<Self::Item>;
fn try_read_many_sync(&self, positions: &[u64]) -> Vec<Option<Self::Item>>;
fn replay(
&self,
start_pos: u64,
buffer: NonZeroUsize,
) -> impl Future<
Output = Result<impl Stream<Item = Result<(u64, Self::Item), Error>> + Send, Error>,
> + Send;
}
pub enum Many<'a, T> {
Flat(&'a [T]),
Nested(&'a [&'a [T]]),
}
impl<T> Many<'_, T> {
pub fn len(&self) -> usize {
match self {
Self::Flat(items) => items.len(),
Self::Nested(nested_items) => nested_items.iter().map(|items| items.len()).sum(),
}
}
pub fn is_empty(&self) -> bool {
match self {
Self::Flat(items) => items.is_empty(),
Self::Nested(nested_items) => nested_items.iter().all(|items| items.is_empty()),
}
}
}
pub trait Mutable: Contiguous + Send + Sync {
fn append(
&mut self,
item: &Self::Item,
) -> impl std::future::Future<Output = Result<u64, Error>> + Send;
fn append_many<'a>(
&'a mut self,
items: Many<'a, Self::Item>,
) -> impl std::future::Future<Output = Result<u64, Error>> + Send + 'a
where
Self::Item: Sync;
fn prune(
&mut self,
min_position: u64,
) -> impl std::future::Future<Output = Result<bool, Error>> + Send;
fn rewind(&mut self, size: u64) -> impl std::future::Future<Output = Result<(), Error>> + Send;
fn commit(&mut self) -> impl std::future::Future<Output = Result<(), Error>> + Send;
fn sync(&mut self) -> impl std::future::Future<Output = Result<(), Error>> + Send;
fn destroy(self) -> impl std::future::Future<Output = Result<(), Error>> + Send
where
Self: Sized;
fn rewind_to<'a, P>(
&'a mut self,
mut predicate: P,
) -> impl std::future::Future<Output = Result<u64, Error>> + Send + 'a
where
P: FnMut(&Self::Item) -> bool + Send + 'a,
{
async move {
let bounds = self.bounds();
let mut rewind_size = bounds.end;
while rewind_size > bounds.start {
let item = self.read(rewind_size - 1).await?;
if predicate(&item) {
break;
}
rewind_size -= 1;
}
if rewind_size != bounds.end {
let rewound_items = bounds.end - rewind_size;
warn!(
journal_size = bounds.end,
rewound_items, "rewinding journal items"
);
self.rewind(rewind_size).await?;
}
Ok(rewind_size)
}
}
}