Skip to main content

oxirs_stream/processing/
processor.rs

1//! Main event processor implementation
2//!
3//! This module provides the core event processing functionality including:
4//! - Event processor management
5//! - Window lifecycle management
6//! - Watermark handling
7//! - Late event processing
8
9use super::window::{EventWindow, Watermark, WindowConfig, WindowResult};
10use crate::StreamEvent;
11use anyhow::{anyhow, Result};
12use chrono::{DateTime, Duration as ChronoDuration, Utc};
13use serde::{Deserialize, Serialize};
14use std::collections::{HashMap, VecDeque};
15use tracing::{debug, info, warn};
16
17/// Processor configuration
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ProcessorConfig {
20    /// Maximum number of windows to maintain
21    pub max_windows: usize,
22    /// Maximum late event buffer size
23    pub max_late_events: usize,
24    /// Watermark advancement interval
25    pub watermark_interval: ChronoDuration,
26    /// Enable statistics collection
27    pub enable_stats: bool,
28    /// Memory limit for event storage (bytes)
29    pub memory_limit: Option<usize>,
30}
31
32impl Default for ProcessorConfig {
33    fn default() -> Self {
34        Self {
35            max_windows: 1000,
36            max_late_events: 10000,
37            watermark_interval: ChronoDuration::seconds(1),
38            enable_stats: true,
39            memory_limit: Some(1024 * 1024 * 100), // 100MB
40        }
41    }
42}
43
44/// Processing statistics
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct ProcessorStats {
47    /// Total events processed
48    pub events_processed: u64,
49    /// Total windows created
50    pub windows_created: u64,
51    /// Total windows triggered
52    pub windows_triggered: u64,
53    /// Late events received
54    pub late_events: u64,
55    /// Dropped events (due to memory limits)
56    pub dropped_events: u64,
57    /// Processing start time
58    pub start_time: DateTime<Utc>,
59    /// Last processing time
60    pub last_processing_time: DateTime<Utc>,
61    /// Average processing latency (milliseconds)
62    pub avg_latency_ms: f64,
63    /// Peak memory usage (bytes)
64    pub peak_memory_usage: usize,
65}
66
67impl Default for ProcessorStats {
68    fn default() -> Self {
69        let now = Utc::now();
70        Self {
71            events_processed: 0,
72            windows_created: 0,
73            windows_triggered: 0,
74            late_events: 0,
75            dropped_events: 0,
76            start_time: now,
77            last_processing_time: now,
78            avg_latency_ms: 0.0,
79            peak_memory_usage: 0,
80        }
81    }
82}
83
84/// Advanced event processor with windowing and aggregations
85pub struct EventProcessor {
86    windows: HashMap<String, EventWindow>,
87    watermark: DateTime<Utc>,
88    late_events: VecDeque<(StreamEvent, DateTime<Utc>)>,
89    stats: ProcessorStats,
90    config: ProcessorConfig,
91    watermark_manager: Watermark,
92}
93
94impl EventProcessor {
95    /// Create a new event processor
96    pub fn new(config: ProcessorConfig) -> Self {
97        Self {
98            windows: HashMap::new(),
99            watermark: Utc::now(),
100            late_events: VecDeque::new(),
101            stats: ProcessorStats::default(),
102            config,
103            watermark_manager: Watermark::default(),
104        }
105    }
106
107    /// Create a new window with the given configuration
108    pub fn create_window(&mut self, config: WindowConfig) -> Result<String> {
109        let window = EventWindow::new(config);
110        let window_id = window.id().to_string();
111
112        // Check memory limits
113        if let Some(limit) = self.config.memory_limit {
114            if self.estimate_memory_usage() > limit {
115                return Err(anyhow!("Memory limit exceeded, cannot create new window"));
116            }
117        }
118
119        // Check window count limits
120        if self.windows.len() >= self.config.max_windows {
121            warn!("Maximum number of windows reached, removing oldest window");
122            self.remove_oldest_window();
123        }
124
125        self.windows.insert(window_id.clone(), window);
126        self.stats.windows_created += 1;
127
128        info!("Created new window: {}", window_id);
129        Ok(window_id)
130    }
131
132    /// Process an event through all windows
133    pub fn process_event(&mut self, event: StreamEvent) -> Result<Vec<WindowResult>> {
134        let start_time = std::time::Instant::now();
135        let mut results = Vec::new();
136
137        // Update watermark
138        self.update_watermark(&event)?;
139
140        // Check if event is late
141        if self.is_late_event(&event) {
142            self.handle_late_event(event)?;
143            return Ok(results);
144        }
145
146        // Process event through all windows
147        let mut windows_to_trigger = Vec::new();
148
149        for (window_id, window) in &mut self.windows {
150            if let Err(e) = window.add_event(event.clone()) {
151                warn!("Failed to add event to window {}: {}", window_id, e);
152                continue;
153            }
154
155            // Check if window should trigger
156            if window.should_trigger(self.watermark) {
157                windows_to_trigger.push(window_id.clone());
158            }
159        }
160
161        // Trigger windows that need to be triggered
162        for window_id in windows_to_trigger {
163            let result = self.trigger_window(&window_id)?;
164            results.push(result);
165        }
166
167        // Update statistics
168        self.update_stats(start_time);
169
170        Ok(results)
171    }
172
173    /// Trigger a window and produce results
174    fn trigger_window(&mut self, window_id: &str) -> Result<WindowResult> {
175        let window = self
176            .windows
177            .get(window_id)
178            .ok_or_else(|| anyhow!("Window not found: {}", window_id))?;
179
180        // Calculate aggregations from the window that is actually triggering.
181        // Each window maintains its own aggregation state (populated as events
182        // are added), so results reflect the events that fell into this window.
183        let aggregations = window.aggregation_results()?;
184
185        let result = WindowResult {
186            window_id: window_id.to_string(),
187            window_start: window
188                .config()
189                .window_type
190                .start_time()
191                .unwrap_or(Utc::now()),
192            window_end: Utc::now(),
193            event_count: window.event_count(),
194            aggregations,
195            trigger_reason: "Window trigger condition met".to_string(),
196            processing_time: Utc::now(),
197        };
198
199        self.stats.windows_triggered += 1;
200        info!("Triggered window: {}", window_id);
201
202        Ok(result)
203    }
204
205    /// Update watermark based on event
206    fn update_watermark(&mut self, event: &StreamEvent) -> Result<()> {
207        let event_time = event.timestamp();
208        self.watermark_manager.update(event_time);
209        self.watermark = self.watermark_manager.current();
210        Ok(())
211    }
212
213    /// Check if event is late
214    fn is_late_event(&self, event: &StreamEvent) -> bool {
215        let event_time = event.timestamp();
216        let allowed_lateness = self.watermark_manager.allowed_lateness;
217        event_time < self.watermark - allowed_lateness
218    }
219
220    /// Handle late events
221    fn handle_late_event(&mut self, event: StreamEvent) -> Result<()> {
222        if self.late_events.len() >= self.config.max_late_events {
223            self.late_events.pop_front();
224            self.stats.dropped_events += 1;
225        }
226
227        self.late_events.push_back((event, Utc::now()));
228        self.stats.late_events += 1;
229
230        Ok(())
231    }
232
233    /// Remove oldest window to make room for new ones
234    fn remove_oldest_window(&mut self) {
235        if let Some((oldest_id, _)) = self.windows.iter().min_by_key(|(_, window)| {
236            window
237                .config()
238                .window_type
239                .start_time()
240                .unwrap_or(Utc::now())
241        }) {
242            let oldest_id = oldest_id.clone();
243            self.windows.remove(&oldest_id);
244            debug!("Removed oldest window: {}", oldest_id);
245        }
246    }
247
248    /// Estimate current memory usage
249    fn estimate_memory_usage(&self) -> usize {
250        // Rough estimation of memory usage
251        let window_size = std::mem::size_of::<EventWindow>();
252        let late_event_size = std::mem::size_of::<(StreamEvent, DateTime<Utc>)>();
253
254        self.windows.len() * window_size + self.late_events.len() * late_event_size
255    }
256
257    /// Update processing statistics
258    fn update_stats(&mut self, start_time: std::time::Instant) {
259        self.stats.events_processed += 1;
260        self.stats.last_processing_time = Utc::now();
261
262        let elapsed = start_time.elapsed();
263        let latency_ms = elapsed.as_secs_f64() * 1000.0;
264
265        // Update average latency (exponential moving average)
266        let alpha = 0.1;
267        self.stats.avg_latency_ms = alpha * latency_ms + (1.0 - alpha) * self.stats.avg_latency_ms;
268
269        // Update peak memory usage
270        let current_memory = self.estimate_memory_usage();
271        if current_memory > self.stats.peak_memory_usage {
272            self.stats.peak_memory_usage = current_memory;
273        }
274    }
275
276    /// Get processing statistics
277    pub fn stats(&self) -> &ProcessorStats {
278        &self.stats
279    }
280
281    /// Get active windows
282    pub fn active_windows(&self) -> Vec<String> {
283        self.windows.keys().cloned().collect()
284    }
285
286    /// Get window by ID
287    pub fn get_window(&self, window_id: &str) -> Option<&EventWindow> {
288        self.windows.get(window_id)
289    }
290
291    /// Remove window by ID
292    pub fn remove_window(&mut self, window_id: &str) -> Result<()> {
293        if self.windows.remove(window_id).is_some() {
294            info!("Removed window: {}", window_id);
295            Ok(())
296        } else {
297            Err(anyhow!("Window not found: {}", window_id))
298        }
299    }
300
301    /// Clear all windows
302    pub fn clear_windows(&mut self) {
303        self.windows.clear();
304        info!("Cleared all windows");
305    }
306
307    /// Get current watermark
308    pub fn current_watermark(&self) -> DateTime<Utc> {
309        self.watermark
310    }
311
312    /// Get late events
313    pub fn late_events(&self) -> &VecDeque<(StreamEvent, DateTime<Utc>)> {
314        &self.late_events
315    }
316}
317
318impl Default for EventProcessor {
319    fn default() -> Self {
320        Self::new(ProcessorConfig::default())
321    }
322}
323
324// Helper trait for window types
325trait WindowTypeExt {
326    fn start_time(&self) -> Option<DateTime<Utc>>;
327}
328
329impl WindowTypeExt for super::window::WindowType {
330    fn start_time(&self) -> Option<DateTime<Utc>> {
331        // This would need to be implemented based on the actual window type
332        Some(Utc::now())
333    }
334}