use std::fmt;
use std::sync::Arc;
use std::vec;
use ruststream::CarriesBatch;
#[cfg(feature = "testing")]
use super::broker::Shared;
use super::database::QueueDatabase;
use super::delivery::{Hold, InboxDelivery};
use super::engine::{Claimed, Events};
use super::form::advisory::LockHold;
use super::form::lease::{LeaseBook, Slot};
use super::form::row_lock::BatchTx;
use super::headers::HeaderCell;
use super::queue::Queue;
use super::subscriber::{InboxSubscriber, Taken};
use super::{Lane, PayloadLane, PayloadRow, QueueRow, RowLane};
use crate::inbox::pools::ServicePool;
#[doc(hidden)]
pub trait BatchLane<DB: QueueDatabase, Row: Events<DB>>: Lane<Row> {
type Batch: IntoIterator<Item = InboxDelivery<DB, Row>> + Send;
fn batch(claim: BatchClaim<'_, DB, Row>) -> Self::Batch;
}
#[doc(hidden)]
pub struct BatchClaim<'s, DB: QueueDatabase, Row: Events<DB>> {
subscriber: &'s mut InboxSubscriber<DB, Row>,
taken: Taken<DB, Row>,
count: usize,
}
impl<'s, DB: QueueDatabase, Row: Events<DB>> BatchClaim<'s, DB, Row> {
pub(crate) const fn new(
subscriber: &'s mut InboxSubscriber<DB, Row>,
taken: Taken<DB, Row>,
count: usize,
) -> Self {
Self {
subscriber,
taken,
count,
}
}
}
impl<DB: QueueDatabase, Row: Events<DB>> fmt::Debug for BatchClaim<'_, DB, Row> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BatchClaim")
.field("subscription", &self.subscriber.queue().name)
.field("count", &self.count)
.finish_non_exhaustive()
}
}
impl<DB, Row> BatchLane<DB, Row> for PayloadLane
where
DB: QueueDatabase,
Row: Events<DB> + PayloadRow,
{
type Batch = Vec<InboxDelivery<DB, Row>>;
fn batch(claim: BatchClaim<'_, DB, Row>) -> Self::Batch {
let BatchClaim {
subscriber,
taken,
count,
} = claim;
let (queue, pool) = (subscriber.queue(), subscriber.pool());
#[cfg(feature = "testing")]
let shared = Arc::clone(&subscriber.shared);
let on = |delivery: InboxDelivery<DB, Row>| {
#[cfg(feature = "testing")]
let delivery = delivery.on(&shared);
delivery
};
match taken {
Taken::Locked(tx) => {
let batch = BatchTx::new(tx, count);
subscriber
.take_rows()
.map(|claimed| {
on(InboxDelivery::batched(
claimed,
Arc::clone(&batch),
queue,
pool,
))
})
.collect()
}
Taken::Leased(book, lease) => subscriber
.take_rows()
.map(|claimed| {
on(InboxDelivery::leased(
claimed, book, lease, None, queue, pool,
))
})
.collect(),
Taken::Advised => subscriber
.take_advised()
.map(|(claimed, hold)| on(InboxDelivery::advised(claimed, hold, queue, pool)))
.collect(),
}
}
}
impl<DB, Row> BatchLane<DB, Row> for RowLane
where
DB: QueueDatabase,
Row: Events<DB> + QueueRow<Lane = Self>,
{
type Batch = RowBatch<DB, Row>;
fn batch(claim: BatchClaim<'_, DB, Row>) -> Self::Batch {
RowBatch::new(claim)
}
}
pub struct RowBatch<DB, Row>
where
DB: QueueDatabase,
Row: Events<DB> + QueueRow<Lane = RowLane>,
{
deliveries: RowDeliveries<DB, Row>,
}
#[doc(hidden)]
pub struct RowDeliveries<DB, Row>
where
DB: QueueDatabase,
Row: Events<DB> + QueueRow<Lane = RowLane>,
{
rows: vec::IntoIter<Row>,
headers: vec::IntoIter<Row::Headers>,
claims: Claims<DB, Row>,
gone: vec::IntoIter<InboxDelivery<DB, Row>>,
queue: &'static Queue,
pool: &'static ServicePool<DB>,
#[cfg(feature = "testing")]
shared: Arc<Shared<DB>>,
}
enum Claims<DB: QueueDatabase, Row: Events<DB>> {
Locked(Arc<BatchTx<DB>>),
Leased(&'static LeaseBook<DB, Row>, vec::IntoIter<Slot>),
Advised(vec::IntoIter<LockHold<DB>>),
}
impl<DB, Row> RowBatch<DB, Row>
where
DB: QueueDatabase,
Row: Events<DB> + QueueRow<Lane = RowLane>,
{
fn new(claim: BatchClaim<'_, DB, Row>) -> Self {
let BatchClaim {
subscriber,
taken,
count,
} = claim;
let (queue, pool) = (subscriber.queue(), subscriber.pool());
#[cfg(feature = "testing")]
let shared = Arc::clone(&subscriber.shared);
let on = |delivery: InboxDelivery<DB, Row>| {
#[cfg(feature = "testing")]
let delivery = delivery.on(&shared);
delivery
};
let mut rows = Vec::with_capacity(count);
let mut headers = Vec::new();
let mut gone = Vec::new();
let claims = match taken {
Taken::Locked(tx) => {
let batch = BatchTx::new(tx, count);
for claimed in subscriber.take_rows() {
match claimed {
Claimed::Row(row) => take(row, &mut rows, &mut headers, count),
claimed => gone.push(on(InboxDelivery::batched(
claimed,
Arc::clone(&batch),
queue,
pool,
))),
}
}
Claims::Locked(batch)
}
Taken::Leased(book, lease) => {
let mut slots = Vec::with_capacity(count);
for claimed in subscriber.take_rows() {
match claimed {
Claimed::Row(row) => {
slots.push(book.enter(Row::id(&row), lease));
take(row, &mut rows, &mut headers, count);
}
claimed => gone.push(on(InboxDelivery::leased(
claimed, book, lease, None, queue, pool,
))),
}
}
Claims::Leased(book, slots.into_iter())
}
Taken::Advised => {
let mut holds = Vec::with_capacity(count);
for (claimed, hold) in subscriber.take_advised() {
match claimed {
Claimed::Row(row) => {
holds.push(hold);
take(row, &mut rows, &mut headers, count);
}
claimed => {
gone.push(on(InboxDelivery::advised(claimed, hold, queue, pool)));
}
}
}
Claims::Advised(holds.into_iter())
}
};
Self {
deliveries: RowDeliveries {
rows: rows.into_iter(),
headers: headers.into_iter(),
claims,
gone: gone.into_iter(),
queue,
pool,
#[cfg(feature = "testing")]
shared,
},
}
}
}
fn take<DB: QueueDatabase, Row: Events<DB>>(
mut row: Row,
rows: &mut Vec<Row>,
headers: &mut Vec<Row::Headers>,
count: usize,
) {
let taken = Row::Headers::take(&mut row);
if !headers.is_empty() || !taken.is_unset() {
if headers.is_empty() {
headers.reserve_exact(count);
headers.resize_with(rows.len(), Row::Headers::default);
}
headers.push(taken);
}
rows.push(row);
}
impl<DB, Row> CarriesBatch<Row> for RowBatch<DB, Row>
where
DB: QueueDatabase,
Row: Events<DB> + QueueRow<Lane = RowLane>,
{
fn carried(&self) -> &[Row] {
self.deliveries.rows.as_slice()
}
}
impl<DB, Row> IntoIterator for RowBatch<DB, Row>
where
DB: QueueDatabase,
Row: Events<DB> + QueueRow<Lane = RowLane>,
{
type Item = InboxDelivery<DB, Row>;
type IntoIter = RowDeliveries<DB, Row>;
fn into_iter(self) -> Self::IntoIter {
self.deliveries
}
}
impl<DB, Row> Iterator for RowDeliveries<DB, Row>
where
DB: QueueDatabase,
Row: Events<DB> + QueueRow<Lane = RowLane>,
{
type Item = InboxDelivery<DB, Row>;
fn next(&mut self) -> Option<Self::Item> {
if self.rows.len() == 0 {
return self.gone.next();
}
let hold = match &mut self.claims {
Claims::Locked(batch) => Hold::Batch(Arc::clone(batch)),
Claims::Leased(book, slots) => Hold::Lease {
book: *book,
slot: slots.next()?,
tx: None,
},
Claims::Advised(holds) => Hold::Advisory(holds.next()?),
};
let row = self.rows.next()?;
let headers = self.headers.next().unwrap_or_default();
let delivery = InboxDelivery::of_batch(row, headers, hold, self.queue, self.pool);
#[cfg(feature = "testing")]
let delivery = delivery.on(&self.shared);
Some(delivery)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.rows.len() + self.gone.len();
(len, Some(len))
}
}
impl<DB, Row> ExactSizeIterator for RowDeliveries<DB, Row>
where
DB: QueueDatabase,
Row: Events<DB> + QueueRow<Lane = RowLane>,
{
}
impl<DB, Row> Drop for RowDeliveries<DB, Row>
where
DB: QueueDatabase,
Row: Events<DB> + QueueRow<Lane = RowLane>,
{
fn drop(&mut self) {
self.for_each(drop);
}
}
impl<DB, Row> fmt::Debug for RowBatch<DB, Row>
where
DB: QueueDatabase,
Row: Events<DB> + QueueRow<Lane = RowLane>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RowBatch")
.field("subscription", &self.deliveries.queue.name)
.field("rows", &self.deliveries.rows.len())
.field("gone", &self.deliveries.gone.len())
.finish_non_exhaustive()
}
}
impl<DB, Row> fmt::Debug for RowDeliveries<DB, Row>
where
DB: QueueDatabase,
Row: Events<DB> + QueueRow<Lane = RowLane>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RowDeliveries")
.field("subscription", &self.queue.name)
.field("left", &self.len())
.finish_non_exhaustive()
}
}