use processor::{
StreamProcessorBuilder, StreamProcessorConfig,
aggregation::CompositeAggregator,
};
use chrono::{Duration, Utc};
use tokio_stream::StreamExt;
use tracing::{info, Level};
use tracing_subscriber;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_max_level(Level::INFO)
.init();
info!("Starting stream processor demo");
let config = StreamProcessorConfig {
allow_late_events: true,
late_event_threshold: Duration::seconds(5),
watermark_interval: Duration::milliseconds(500),
window_retention: Duration::minutes(10),
max_windows_per_key: 100,
result_buffer_size: 1000,
};
let mut processor = StreamProcessorBuilder::new()
.with_config(config)
.build_tumbling(
Duration::seconds(10),
|key| {
info!("Creating aggregator for key: {}", key);
let mut agg = CompositeAggregator::new();
agg.add_count("request_count")
.add_avg("avg_latency")
.add_min("min_latency")
.add_max("max_latency")
.add_p50("p50_latency")
.add_p95("p95_latency")
.add_p99("p99_latency")
.add_stddev("stddev_latency");
agg
},
);
let mut results = processor.results().unwrap();
let results_task = tokio::spawn(async move {
info!("Waiting for results...");
while let Some(result) = results.next().await {
info!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
info!("Window Result:");
info!(" Window: {}", result.window);
info!(" Key: {}", result.key);
info!(" Events: {}", result.event_count);
info!(" Processed at: {}", result.processed_at);
if let Some(count) = result.results.get_count("request_count") {
info!(" Request Count: {}", count);
}
if let Some(avg) = result.results.get_value("avg_latency") {
info!(" Average Latency: {:.2}ms", avg);
}
if let Some(min) = result.results.get_value("min_latency") {
info!(" Min Latency: {:.2}ms", min);
}
if let Some(max) = result.results.get_value("max_latency") {
info!(" Max Latency: {:.2}ms", max);
}
if let Some(p50) = result.results.get_value("p50_latency") {
info!(" P50 Latency: {:.2}ms", p50);
}
if let Some(p95) = result.results.get_value("p95_latency") {
info!(" P95 Latency: {:.2}ms", p95);
}
if let Some(p99) = result.results.get_value("p99_latency") {
info!(" P99 Latency: {:.2}ms", p99);
}
if let Some(stddev) = result.results.get_value("stddev_latency") {
info!(" StdDev Latency: {:.2}ms", stddev);
}
info!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
}
info!("Results stream ended");
});
info!("Simulating streaming latency events...");
let base_time = Utc::now();
let mut event_time = base_time;
for window_idx in 0..3 {
info!("Processing window {} events...", window_idx + 1);
for i in 0..20 {
let latency = 100.0 + (i as f64 * 2.0) + (rand::random::<f64>() * 50.0);
processor.process_event(
"llm-service".to_string(),
event_time,
latency,
).await?;
event_time = event_time + Duration::milliseconds(500);
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
}
info!("Completed window {} with 20 events", window_idx + 1);
}
info!("All events processed");
let stats = processor.stats().await;
info!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
info!("Processor Statistics:");
info!(" Events Processed: {}", stats.events_processed);
info!(" Events Dropped: {}", stats.events_dropped);
info!(" Windows Created: {}", stats.windows_created);
info!(" Windows Fired: {}", stats.windows_fired);
info!(" Active Windows: {}", stats.active_windows);
info!(" Processing Latency P50: {:.2}ms", stats.latency_p50_ms);
info!(" Processing Latency P95: {:.2}ms", stats.latency_p95_ms);
info!(" Processing Latency P99: {:.2}ms", stats.latency_p99_ms);
if let Some(wm) = stats.current_watermark {
info!(" Current Watermark: {}", wm);
}
info!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
info!("Demo completed successfully!");
Ok(())
}