use spate_core::record::PartitionId;
use std::sync::Arc;
use std::sync::atomic::{AtomicI64, AtomicU32, Ordering};
pub(crate) const LANE_HANDOFF_CHUNKS: usize = 4;
pub(crate) type LaneLabel = Arc<str>;
const UNKNOWN: i64 = i64::MIN;
const CLOSED: u32 = 1 << 31;
#[derive(Debug)]
pub(crate) struct SplitTracker {
terminal: AtomicI64,
objects_done: AtomicU32,
}
impl SplitTracker {
pub(crate) fn new() -> SplitTracker {
SplitTracker {
terminal: AtomicI64::new(UNKNOWN),
objects_done: AtomicU32::new(0),
}
}
pub(crate) fn set_terminal(&self, watermark: i64) {
debug_assert_ne!(watermark, UNKNOWN, "terminal watermark is a real offset");
let prev = self.terminal.swap(watermark, Ordering::Release);
debug_assert!(
prev == UNKNOWN || prev == watermark,
"a lane decides end-of-input once; conflicting terminals {prev} vs {watermark}"
);
}
pub(crate) fn terminal(&self) -> Option<i64> {
match self.terminal.load(Ordering::Acquire) {
UNKNOWN => None,
w => Some(w),
}
}
pub(crate) fn object_done(&self) -> bool {
self.objects_done.fetch_add(1, Ordering::Relaxed) & CLOSED == 0
}
pub(crate) fn close(&self) -> u32 {
self.objects_done.fetch_or(CLOSED, Ordering::Relaxed) & !CLOSED
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum PoisonKind {
NotFound,
EtagDrift,
Undecodable,
RetriesExhausted,
}
impl PoisonKind {
pub(crate) fn reason_label(self) -> &'static str {
match self {
PoisonKind::NotFound => "not_found",
PoisonKind::EtagDrift => "etag_drift",
PoisonKind::Undecodable => "undecodable",
PoisonKind::RetriesExhausted => "retries_exhausted",
}
}
}
#[derive(Debug)]
pub(crate) struct PoisonReport {
pub(crate) partition: PartitionId,
pub(crate) kind: PoisonKind,
pub(crate) reason: String,
pub(crate) overwrite: Option<Box<crate::fetch::Overwrite>>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tracker_is_unknown_until_set_and_stable_after() {
let t = SplitTracker::new();
assert_eq!(t.terminal(), None);
t.set_terminal(42);
assert_eq!(t.terminal(), Some(42));
t.set_terminal(42);
assert_eq!(t.terminal(), Some(42));
}
#[test]
fn tracker_carries_zero_watermarks_and_counts_objects() {
let t = SplitTracker::new();
t.set_terminal(0);
assert_eq!(t.terminal(), Some(0));
assert!(t.object_done());
assert!(t.object_done());
assert_eq!(t.close(), 2);
assert!(!t.object_done(), "a closed split's lane takes nothing back");
}
}