use processor::{
EventKey, EventTimeExtractor, KeyExtractor,
StreamPipelineBuilder, StreamExecutor,
StateBackend,
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Debug, Clone, Serialize, Deserialize)]
struct MetricEvent {
name: String,
value: f64,
timestamp: DateTime<Utc>,
source: String,
tags: Vec<String>,
}
impl MetricEvent {
fn new(name: impl Into<String>, value: f64, source: impl Into<String>) -> Self {
Self {
name: name.into(),
value,
timestamp: Utc::now(),
source: source.into(),
tags: Vec::new(),
}
}
fn with_timestamp(mut self, timestamp: DateTime<Utc>) -> Self {
self.timestamp = timestamp;
self
}
fn with_tag(mut self, tag: impl Into<String>) -> Self {
self.tags.push(tag.into());
self
}
}
impl EventTimeExtractor<MetricEvent> for MetricEvent {
fn extract_event_time(&self, event: &MetricEvent) -> DateTime<Utc> {
event.timestamp
}
}
impl KeyExtractor<MetricEvent> for MetricEvent {
fn extract_key(&self, event: &MetricEvent) -> EventKey {
EventKey::Session(event.source.clone())
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Stream Processing Pipeline Example ===\n");
println!("Example 1: Basic Pipeline with Operators");
basic_pipeline_example().await?;
println!("\nExample 2: Windowed Aggregation");
windowed_aggregation_example().await?;
println!("\nExample 3: Complex Pipeline");
complex_pipeline_example().await?;
Ok(())
}
async fn basic_pipeline_example() -> Result<(), Box<dyn std::error::Error>> {
let pipeline = StreamPipelineBuilder::new()
.with_name("basic-pipeline")
.with_description("A basic example pipeline")
.with_parallelism(2)
.with_buffer_size(1000)
.build()?;
println!("Created pipeline: {}", pipeline.name());
println!("Description: {}", pipeline.description().unwrap_or("None"));
let executor: StreamExecutor<MetricEvent> = pipeline.create_executor();
let events = vec![
MetricEvent::new("latency", 100.0, "service-a"),
MetricEvent::new("latency", 150.0, "service-b"),
MetricEvent::new("latency", 75.0, "service-a"),
];
for event in events {
executor.ingest(event).await?;
}
let stats = executor.stats().await;
println!("Events processed: {}", stats.events_processed);
println!("Events dropped: {}", stats.events_dropped);
Ok(())
}
async fn windowed_aggregation_example() -> Result<(), Box<dyn std::error::Error>> {
let pipeline = StreamPipelineBuilder::new()
.with_name("windowed-aggregator")
.with_description("Aggregates metrics in 1-minute windows")
.with_tumbling_window(60_000) .with_watermark_delay(Duration::from_secs(5))
.with_state_backend(StateBackend::Memory)
.with_allowed_lateness(10_000) .build()?;
println!("Created windowed pipeline: {}", pipeline.name());
println!("Window type: Tumbling (60s)");
let executor: StreamExecutor<MetricEvent> = pipeline.create_executor();
let base_time = Utc::now();
let events = vec![
MetricEvent::new("requests", 100.0, "api-server")
.with_timestamp(base_time)
.with_tag("http"),
MetricEvent::new("requests", 150.0, "api-server")
.with_timestamp(base_time + chrono::Duration::seconds(10))
.with_tag("http"),
MetricEvent::new("requests", 120.0, "api-server")
.with_timestamp(base_time + chrono::Duration::seconds(20))
.with_tag("http"),
];
for event in events {
println!("Ingesting event: {} @ {}", event.name, event.timestamp);
executor.ingest(event).await?;
}
let stats = executor.stats().await;
println!("\nPipeline Statistics:");
println!(" Events processed: {}", stats.events_processed);
println!(" Windows created: {}", stats.windows_created);
println!(" Active windows: {}", stats.active_windows);
println!(" Current watermark: {}", stats.current_watermark);
Ok(())
}
async fn complex_pipeline_example() -> Result<(), Box<dyn std::error::Error>> {
let pipeline = StreamPipelineBuilder::new()
.with_name("complex-pipeline")
.with_description("Multi-stage processing with operators")
.with_parallelism(4)
.with_buffer_size(10_000)
.with_sliding_window(300_000, 60_000) .with_watermark_delay(Duration::from_secs(10))
.with_state_backend(StateBackend::Memory)
.with_max_state_size(100_000_000) .with_percentiles(vec![50, 90, 95, 99])
.with_outlier_removal(3.0) .with_tag("production")
.with_tag("metrics")
.build()?;
println!("Created complex pipeline: {}", pipeline.name());
println!("Tags: {:?}", pipeline.tags());
println!("Parallelism: {}", pipeline.config().processor.parallelism);
println!("Buffer size: {}", pipeline.config().processor.buffer_size);
let executor: StreamExecutor<MetricEvent> = pipeline.create_executor();
let base_time = Utc::now();
let mut events = Vec::new();
for i in 0..10 {
events.push(
MetricEvent::new("cpu_usage", 50.0 + (i as f64 * 2.0), "server-1")
.with_timestamp(base_time + chrono::Duration::seconds(i * 10))
.with_tag("cpu")
.with_tag("monitoring"),
);
events.push(
MetricEvent::new("memory_usage", 75.0 + (i as f64), "server-1")
.with_timestamp(base_time + chrono::Duration::seconds(i * 10))
.with_tag("memory")
.with_tag("monitoring"),
);
}
println!("\nIngesting {} events...", events.len());
executor.ingest_batch(events).await?;
let stats = executor.stats().await;
println!("\nFinal Statistics:");
println!(" Total events: {}", stats.events_processed);
println!(" Dropped events: {}", stats.events_dropped);
println!(" Windows created: {}", stats.windows_created);
println!(" Windows triggered: {}", stats.windows_triggered);
println!(" Active windows: {}", stats.active_windows);
println!(" Aggregations computed: {}", stats.aggregations_computed);
println!(" Bytes processed: {} bytes", stats.bytes_processed);
println!(" Errors: {}", stats.errors);
let elapsed = 1.0; println!(" Throughput: {:.2} events/sec", stats.events_per_second(elapsed));
println!(" Throughput: {:.2} MB/s", stats.throughput_mbps(elapsed));
Ok(())
}