Skip to main content

Tail

Struct Tail 

Source
pub struct Tail<'reader> { /* private fields */ }
Expand description

A live tail over the frames appended after a reader’s snapshot.

A tail is the mutable counterpart of a snapshot Scan: it begins after the blocks the reader already holds, and poll_next refreshes that reader and decodes one newly committed matching block per call. Polling is synchronous and never blocks: it sleeps on nothing, spawns no thread, and owns no timer, so a call that has nothing new returns Ok(None) immediately and the caller decides how long to wait before polling again. None means “pending now”, never a permanent end of stream, so a tail does not implement Iterator or FusedIterator.

None means only that, too. A block that prunes on its bounds or filters down to no rows is consumed inside the poll that reaches it, and the poll carries on to the next committed block, so a caller that waits on None is never waiting on data that has already arrived. Neither is a batch of zero rows ever reported in place of one.

A physically incomplete frame is never exposed: the tail leaves it undispatched and reports it through incomplete_tail, and a later poll sees it once a writer completes it.

Projection, reordered projection, empty projection, primary ranges, file order, and the treatment of a per-block decode error all match Scan, and the tail’s cumulative scan limits apply across its whole lifetime rather than per poll. Cancellation is dropping the tail or stopping the polls; dropping performs no I/O and leaks no thread or handle.

The tail borrows its reader, so the reader cannot be refreshed through Reader::refresh while one of its tails exists. That is the same snapshot safety boundary Scan already draws, kept on purpose rather than bypassed with interior mutation.

Implementations§

Source§

impl<'reader> Tail<'reader>

Source

pub fn project<I, S>(self, columns: I) -> Result<Self>
where I: IntoIterator<Item = S>, S: AsRef<str>,

Select columns by exact schema name, preserving the requested order.

This has exactly Scan::project’s semantics: the requested order becomes the batch column order, an empty list yields zero-column batches that still carry their row counts, an unknown or repeated name fails here rather than during polling, and calling this again replaces the whole projection.

Source

pub fn primary_range(self, range: PrimaryRange) -> Result<Self>

Configure a typed half-open primary range, [start, end).

This has exactly Scan::primary_range’s semantics: the range type must match the schema’s primary column and an empty range is legal. Blocks whose stored bounds cannot intersect the range are polled through without a batch, and no global primary ordering is ever inferred: a future block may overlap this range even when none of the current blocks do.

Source

pub fn file_order(self) -> Self

Keep committed block order and row order within each block explicit. This is currently the tail’s only ordering mode, matching Scan::file_order.

Source

pub fn poll_next(&mut self) -> Result<Option<RecordBatch>>

Synchronously discover and decode the next newly committed matching block, without sleeping or blocking.

A newly committed block is returned as Ok(Some(batch)). Ok(None) means the file currently holds no committed block this tail has not already dealt with — “pending now”, not the end of the stream, since polling again later may yield data. Blocks that pruning or range filtering removes are consumed on the way, so None is never returned with matching work still queued and a caller may safely wait on it.

A refresh or decode failure is returned as Err. A per-block decode failure consumes that block and the tail continues with the next committed block on a later poll, exactly as a snapshot Scan yields the damaged block as one error and keeps going. A refresh failure leaves the tail’s position untouched and is retried by the next poll.

Until a poll returns a decoded block it keeps refreshing the reader, which keeps the file extent and commit boundary current. Once undispatched blocks exist, polling decodes them first and only refreshes again when they are exhausted, so no block is skipped and none is decoded twice.

Source

pub fn metrics(&self) -> ScanMetrics

Return aggregate planning, stream, byte, and row counters collected across this tail’s lifetime.

ScanMetrics::bytes_read covers both halves of a tail’s work: the frames each refresh streamed to verify their commit trailers, and the bytes the decodes then read. A tail therefore reports more bytes than a Scan over the same blocks, because a scan never has to discover them. The row and decoded-byte allowances from Limits still bound decoding only; discovery is bounded by the file, not by the scan.

Trait Implementations§

Source§

impl<'reader> Debug for Tail<'reader>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'reader> !UnwindSafe for Tail<'reader>

§

impl<'reader> Freeze for Tail<'reader>

§

impl<'reader> RefUnwindSafe for Tail<'reader>

§

impl<'reader> Send for Tail<'reader>

§

impl<'reader> Sync for Tail<'reader>

§

impl<'reader> Unpin for Tail<'reader>

§

impl<'reader> UnsafeUnpin for Tail<'reader>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.