#![allow(dead_code)]
use std::future::{Future, ready};
use std::marker::PhantomData;
use std::num::NonZeroUsize;
use std::sync::Arc;
use std::time::Duration;
use futures::{Stream, StreamExt, stream};
use ruststream::codec::CodecError;
use ruststream::memory::{
ConnectedMemoryBroker, Discarding, LogMode, MemoryError, MemoryMessage, MemorySeeker,
MemorySubscriber, Retaining,
};
use ruststream::runtime::{Input, IntoSource, SoloCarried};
use ruststream::{
AckError, AddressedCopies, BatchSubscriber, BuildBatchContext, Carries, CarriesBatch, CopyPath,
Field, HeaderMap, IncomingMessage, RedeliveryAddress, RedeliveryAddressed, Seekable, Subscribe,
Subscriber, SubscriptionSource,
};
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
use tokio::task::yield_now;
#[derive(Debug, Clone, PartialEq, Eq, schemars::JsonSchema)]
pub(crate) struct Row {
pub(crate) id: u64,
pub(crate) name: String,
}
impl Row {
pub(crate) fn new(id: u64, name: &str) -> Self {
Self {
id,
name: name.to_owned(),
}
}
pub(crate) fn payload(&self) -> String {
format!("{}:{}", self.id, self.name)
}
fn read(payload: &[u8]) -> Option<Self> {
let text = std::str::from_utf8(payload).ok()?;
let (id, name) = text.split_once(':')?;
Some(Self::new(id.parse().ok()?, name))
}
}
impl Input for Row {
type Axis = SoloCarried<Self>;
}
pub(crate) const UNREADABLE: &str = "unreadable";
pub(crate) const UNREADABLE_REASON: &str =
"the column `amount` holds text, the field is an integer";
pub(crate) fn reported(payload: &[u8]) -> Option<CodecError> {
payload
.windows(UNREADABLE.len())
.any(|word| word == UNREADABLE.as_bytes())
.then(|| CodecError::Decode(UNREADABLE_REASON.into()))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(crate) enum Fault {
#[default]
None,
StaleValue,
PlaceholderForGone,
ReversedPage,
ExtraRow,
GoneFirst,
EmptyPayload,
OnePerPage,
NoRequeue,
EmptyPages,
OneInFlight,
}
#[derive(Debug, Clone)]
pub(crate) struct Rows<Copies = AddressedCopies> {
name: String,
fault: Fault,
copies: PhantomData<fn() -> Copies>,
}
impl Rows {
pub(crate) fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
fault: Fault::None,
copies: PhantomData,
}
}
}
impl<Copies> Rows<Copies> {
pub(crate) fn faulty(self, fault: Fault) -> Self {
Self { fault, ..self }
}
pub(crate) fn copy_path<Path: CopyPath>(self) -> Rows<Path> {
Rows {
name: self.name,
fault: self.fault,
copies: PhantomData,
}
}
}
impl<Copies> IntoSource for Rows<Copies> {
type Source = Self;
fn into_source(self) -> Self {
self
}
}
impl<Log: LogMode, Copies: CopyPath> SubscriptionSource<ConnectedMemoryBroker<Log>>
for Rows<Copies>
{
type Subscriber = RowSubscriber<Log>;
type Copies = Copies;
fn name(&self) -> &str {
&self.name
}
async fn subscribe(
self,
connected: &ConnectedMemoryBroker<Log>,
) -> Result<RowSubscriber<Log>, MemoryError> {
Ok(RowSubscriber {
inner: Subscribe::subscribe(connected, &self.name).await?,
fault: self.fault,
first: None,
})
}
}
impl<Log: LogMode> RedeliveryAddressed<ConnectedMemoryBroker<Log>> for Rows {
fn redelivery_address(
&self,
_connected: &ConnectedMemoryBroker<Log>,
) -> impl Future<Output = Result<RedeliveryAddress, MemoryError>> + Send {
ready(Ok(RedeliveryAddress::new(self.name.clone())))
}
}
pub(crate) struct RowSubscriber<Log = Discarding> {
inner: MemorySubscriber<Log>,
fault: Fault,
first: Option<Row>,
}
impl<Log: LogMode> RowSubscriber<Log> {
fn read(fault: Fault, first: &mut Option<Row>, inner: MemoryMessage<Log>) -> RowDelivery<Log> {
let row = match (fault, Row::read(inner.payload())) {
(Fault::StaleValue, Some(row)) => Some(first.get_or_insert(row).clone()),
(Fault::PlaceholderForGone, None) => Some(Row::new(0, "placeholder")),
(_, row) => row,
};
RowDelivery {
error: reported(inner.payload()),
inner,
row,
fault,
slot: None,
}
}
}
impl<Log: LogMode> Subscriber for RowSubscriber<Log> {
type Message = RowDelivery<Log>;
type Error = std::convert::Infallible;
fn stream(&mut self) -> impl Stream<Item = Result<RowDelivery<Log>, Self::Error>> + Send + '_ {
let fault = self.fault;
let first = &mut self.first;
self.inner
.stream()
.map(move |delivery| delivery.map(|inner| Self::read(fault, first, inner)))
}
}
impl<Log: LogMode> BatchSubscriber for RowSubscriber<Log> {
type Batch = RowBatch<Log>;
fn batches(
&mut self,
size: NonZeroUsize,
) -> impl Stream<Item = Result<RowBatch<Log>, Self::Error>> + Send + '_ {
let fault = self.fault;
if fault == Fault::EmptyPages {
return stream::repeat(())
.then(async move |()| {
yield_now().await;
Ok(RowBatch {
rows: Vec::new(),
deliveries: Vec::new(),
})
})
.boxed();
}
if fault == Fault::OneInFlight {
let pages = self.inner.batches(NonZeroUsize::MIN).boxed();
let in_flight = Arc::new(Semaphore::new(1));
return stream::unfold((pages, in_flight), async move |(mut pages, in_flight)| {
let slot = Arc::clone(&in_flight).acquire_owned().await.ok()?;
let page = pages.next().await?;
let page = page.map(|page| RowBatch::read(page, Fault::OneInFlight).holding(slot));
Some((page, (pages, in_flight)))
})
.boxed();
}
let size = if fault == Fault::OnePerPage {
NonZeroUsize::MIN
} else {
size
};
self.inner
.batches(size)
.map(move |batch| batch.map(|page| RowBatch::read(page, fault)))
.boxed()
}
}
impl Seekable for RowSubscriber<Retaining> {
type Seeker = MemorySeeker;
fn seeker(&self) -> MemorySeeker {
self.inner.seeker()
}
}
pub(crate) struct RowDelivery<Log = Discarding> {
inner: MemoryMessage<Log>,
row: Option<Row>,
fault: Fault,
slot: Option<OwnedSemaphorePermit>,
error: Option<CodecError>,
}
impl<Log> Carries<Row> for RowDelivery<Log> {
fn carried(&self) -> Option<&Row> {
self.row.as_ref()
}
}
impl<Log: LogMode> IncomingMessage for RowDelivery<Log> {
fn payload(&self) -> &[u8] {
if self.fault == Fault::EmptyPayload {
return &[];
}
self.inner.payload()
}
fn headers(&self) -> &HeaderMap {
self.inner.headers()
}
fn decode_error(&self) -> Option<&CodecError> {
self.error.as_ref()
}
fn redelivery_count(&self) -> Option<u64> {
self.inner.redelivery_count()
}
fn ack(self) -> impl Future<Output = Result<(), AckError>> + Send {
let settled = self.inner.ack();
let slot = self.slot;
async move {
let settled = settled.await;
drop(slot);
settled
}
}
fn nack(self, requeue: bool) -> impl Future<Output = Result<(), AckError>> + Send {
let unsupported = requeue && self.fault == Fault::NoRequeue;
let settled = self.inner.nack(requeue && !unsupported);
let slot = self.slot;
async move {
let settled = settled.await;
drop(slot);
settled?;
if unsupported {
return Err(AckError::Unsupported);
}
Ok(())
}
}
fn supports_nack_after(&self) -> bool {
self.inner.supports_nack_after()
}
fn nack_after(self, delay: Duration) -> impl Future<Output = Result<(), AckError>> + Send {
let settled = self.inner.nack_after(delay);
let slot = self.slot;
async move {
let settled = settled.await;
drop(slot);
settled
}
}
}
pub(crate) struct RowBatch<Log = Discarding> {
rows: Vec<Row>,
deliveries: Vec<RowDelivery<Log>>,
}
impl<Log: LogMode> RowBatch<Log> {
fn read(page: Vec<MemoryMessage<Log>>, fault: Fault) -> Self {
let mut rows = Vec::with_capacity(page.len());
let mut deliveries = Vec::with_capacity(page.len());
let mut gone = Vec::new();
for inner in page {
let error = reported(inner.payload());
let row = if error.is_some() {
None
} else {
Row::read(inner.payload())
};
match row {
Some(row) => {
rows.push(row);
deliveries.push(RowDelivery {
inner,
row: None,
fault,
slot: None,
error,
});
}
None if fault == Fault::GoneFirst => {
deliveries.push(RowDelivery {
inner,
row: None,
fault,
slot: None,
error,
});
}
None => gone.push(RowDelivery {
inner,
row: None,
fault,
slot: None,
error,
}),
}
}
if fault == Fault::PlaceholderForGone {
rows.extend(gone.iter().map(|_| Row::new(0, "placeholder")));
}
deliveries.extend(gone);
match fault {
Fault::ReversedPage => rows.reverse(),
Fault::ExtraRow => rows.push(Row::new(0, "extra")),
_ => {}
}
Self { rows, deliveries }
}
fn holding(mut self, slot: OwnedSemaphorePermit) -> Self {
if let Some(delivery) = self.deliveries.first_mut() {
delivery.slot = Some(slot);
}
self
}
}
impl<Log> CarriesBatch<Row> for RowBatch<Log> {
fn carried(&self) -> &[Row] {
&self.rows
}
}
impl<Log> IntoIterator for RowBatch<Log> {
type Item = RowDelivery<Log>;
type IntoIter = std::vec::IntoIter<RowDelivery<Log>>;
fn into_iter(self) -> Self::IntoIter {
self.deliveries.into_iter()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct PageContext {
pub(crate) rows: usize,
}
impl<Log> BuildBatchContext<RowBatch<Log>> for PageContext {
fn build(page: &RowBatch<Log>) -> Self {
Self {
rows: page.rows.len(),
}
}
}
impl<Log> BuildBatchContext<RowDelivery<Log>> for PageContext {
fn build(_one: &RowDelivery<Log>) -> Self {
Self { rows: 1 }
}
}
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct PageRows;
impl Field<PageContext> for PageRows {
type Value<'a> = usize;
fn get(self, src: &PageContext) -> usize {
src.rows
}
}