1mod cast;
3mod duration_str;
4mod event_time;
5mod filter;
6mod watermark;
7
8pub use cast::{cast_to_millis_array, CastError};
9pub use duration_str::parse_duration_str;
10pub use event_time::{EventTimeError, EventTimeExtractor, ExtractionMode, TimestampField};
11
12pub use filter::{filter_batch_by_timestamp, FilterError, ThresholdOp};
13
14pub use watermark::{
15 AscendingTimestampsGenerator, BoundedOutOfOrdernessGenerator, PeriodicGenerator,
16 ProcessingTimeGenerator, PunctuatedGenerator, SourceProvidedGenerator, WatermarkGenerator,
17 WatermarkRestoreError, WatermarkTracker, DEFAULT_MAX_FUTURE_SKEW_MS,
18};
19
20use smallvec::SmallVec;
21use std::cmp::Ordering;
22use std::collections::BinaryHeap;
23use std::time::{SystemTime, UNIX_EPOCH};
24
25#[must_use]
27pub fn now_unix_millis() -> i64 {
28 SystemTime::now()
29 .duration_since(UNIX_EPOCH)
30 .map_or(0, |d| i64::try_from(d.as_millis()).unwrap_or(i64::MAX))
31}
32
33pub type TimerKey = SmallVec<[u8; 16]>;
38
39pub type FiredTimersVec = SmallVec<[TimerRegistration; 8]>;
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
49pub struct Watermark(pub i64);
50
51impl Watermark {
52 #[inline]
54 #[must_use]
55 pub fn new(timestamp: i64) -> Self {
56 Self(timestamp)
57 }
58
59 #[inline]
61 #[must_use]
62 pub fn timestamp(&self) -> i64 {
63 self.0
64 }
65
66 #[inline]
71 #[must_use]
72 pub fn is_late(&self, event_time: i64) -> bool {
73 event_time < self.0
74 }
75
76 #[must_use]
78 pub fn min(self, other: Self) -> Self {
79 Self(self.0.min(other.0))
80 }
81
82 #[must_use]
84 pub fn max(self, other: Self) -> Self {
85 Self(self.0.max(other.0))
86 }
87}
88
89impl Default for Watermark {
90 fn default() -> Self {
91 Self(i64::MIN)
92 }
93}
94
95impl From<i64> for Watermark {
96 fn from(timestamp: i64) -> Self {
97 Self(timestamp)
98 }
99}
100
101impl From<Watermark> for i64 {
102 fn from(watermark: Watermark) -> Self {
103 watermark.0
104 }
105}
106
107#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct TimerRegistration {
113 pub id: u64,
115 pub timestamp: i64,
117 pub key: Option<TimerKey>,
120 pub operator_index: Option<usize>,
122}
123
124impl Ord for TimerRegistration {
125 fn cmp(&self, other: &Self) -> Ordering {
126 match other.timestamp.cmp(&self.timestamp) {
128 Ordering::Equal => other.id.cmp(&self.id),
129 ord => ord,
130 }
131 }
132}
133
134impl PartialOrd for TimerRegistration {
135 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
136 Some(self.cmp(other))
137 }
138}
139
140const TIMER_WARN_THRESHOLD: usize = 100_000;
143
144pub struct TimerService {
150 timers: BinaryHeap<TimerRegistration>,
151 next_timer_id: u64,
152}
153
154impl TimerService {
155 #[must_use]
157 pub fn new() -> Self {
158 Self {
159 timers: BinaryHeap::new(),
160 next_timer_id: 0,
161 }
162 }
163
164 #[must_use]
166 pub fn with_capacity(capacity: usize) -> Self {
167 Self {
168 timers: BinaryHeap::with_capacity(capacity),
169 next_timer_id: 0,
170 }
171 }
172
173 pub fn register_timer(
183 &mut self,
184 timestamp: i64,
185 key: Option<TimerKey>,
186 operator_index: Option<usize>,
187 ) -> u64 {
188 let id = self.next_timer_id;
189 self.next_timer_id += 1;
190
191 self.timers.push(TimerRegistration {
192 id,
193 timestamp,
194 key,
195 operator_index,
196 });
197
198 if self.timers.len() == TIMER_WARN_THRESHOLD {
199 tracing::warn!(
200 pending = self.timers.len(),
201 "Timer heap reached {} pending timers — watermark may be stalled",
202 TIMER_WARN_THRESHOLD,
203 );
204 }
205
206 id
207 }
208
209 #[inline]
219 pub fn poll_timers(&mut self, current_time: i64) -> FiredTimersVec {
220 let mut fired = FiredTimersVec::new();
221
222 while let Some(timer) = self.timers.peek() {
223 if timer.timestamp <= current_time {
224 fired.push(self.timers.pop().expect("heap should not be empty"));
226 } else {
227 break;
228 }
229 }
230
231 fired
232 }
233
234 pub fn cancel_timer(&mut self, id: u64) -> bool {
238 let count_before = self.timers.len();
239 self.timers.retain(|t| t.id != id);
240 self.timers.len() < count_before
241 }
242
243 #[must_use]
245 pub fn pending_count(&self) -> usize {
246 self.timers.len()
247 }
248
249 #[must_use]
251 pub fn next_timer_timestamp(&self) -> Option<i64> {
252 self.timers.peek().map(|t| t.timestamp)
253 }
254
255 pub fn clear(&mut self) {
257 self.timers.clear();
258 }
259}
260
261impl Default for TimerService {
262 fn default() -> Self {
263 Self::new()
264 }
265}
266
267#[derive(Debug, thiserror::Error)]
269pub enum TimeError {
270 #[error("Invalid timestamp: {0}")]
272 InvalidTimestamp(i64),
273
274 #[error("Timer not found: {0}")]
276 TimerNotFound(u64),
277
278 #[error("Watermark regression: current={current}, new={new}")]
280 WatermarkRegression {
281 current: i64,
283 new: i64,
285 },
286}
287
288#[cfg(test)]
289mod tests;