use oxigraph::model::*;
use rsp_rs::RSPEngine;
use std::thread;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== RSP-RS Streaming Lifecycle Example ===\n");
let query = r#"
PREFIX ex: <https://rsp.rs/>
REGISTER RStream <output> AS
SELECT ?s ?p ?o
FROM NAMED WINDOW ex:w1 ON STREAM ex:stream1 [RANGE 10000 STEP 2000]
WHERE {
WINDOW ex:w1 { ?s ?p ?o }
}
"#;
println!("Query Configuration:");
println!(" - Window RANGE: 10000ms (10 seconds)");
println!(" - Window STEP: 2000ms (2 seconds)");
println!(" - Stream: https://rsp.rs/stream1\n");
let mut rsp_engine = RSPEngine::new(query.to_string());
rsp_engine.initialize()?;
let stream = rsp_engine
.get_stream("https://rsp.rs/stream1")
.expect("Stream should exist after initialization");
println!("Stream obtained: {}\n", stream.name);
if let Some(window) = rsp_engine.get_window("https://rsp.rs/w1") {
let window_lock = window.lock().unwrap();
drop(window_lock); }
let result_receiver = rsp_engine.start_processing();
let result_thread = thread::spawn(move || {
let mut count = 0;
println!("--- Starting Result Collection ---\n");
loop {
match result_receiver.recv_timeout(Duration::from_millis(500)) {
Ok(result) => {
count += 1;
println!(
"[x] Result #{}: Window [{}, {})",
count, result.timestamp_from, result.timestamp_to
);
println!(" Bindings: {}\n", result.bindings);
}
Err(_) => {
thread::sleep(Duration::from_millis(100));
if result_receiver
.recv_timeout(Duration::from_millis(100))
.is_err()
{
break;
}
}
}
}
println!("--- Result Collection Complete ---");
println!("Total results received: {}\n", count);
});
thread::sleep(Duration::from_millis(100));
println!("=== Adding Events to Stream ===\n");
println!("NOTE: The 't=' values below are EVENT TIMESTAMPS, not wall-clock time!");
println!("We could add all these events instantly - the system only cares about");
println!("the timestamp parameter we pass to add_quads().\n");
println!("timestamp=0: Adding event (subject_0)");
let quad0 = Quad::new(
NamedNode::new("https://rsp.rs/subject_0")?,
NamedNode::new("https://rsp.rs/predicate")?,
NamedNode::new("https://rsp.rs/object")?,
GraphName::DefaultGraph,
);
stream.add_quads(vec![quad0], 0)?; thread::sleep(Duration::from_millis(50));
println!("timestamp=500: Adding event (subject_1)");
let quad1 = Quad::new(
NamedNode::new("https://rsp.rs/subject_1")?,
NamedNode::new("https://rsp.rs/predicate")?,
NamedNode::new("https://rsp.rs/object")?,
GraphName::DefaultGraph,
);
stream.add_quads(vec![quad1], 500)?; thread::sleep(Duration::from_millis(50));
if let Some(window) = rsp_engine.get_window("https://rsp.rs/w1") {
let window_lock = window.lock().unwrap();
println!("\nWindow State Inspection (after 2 events, before t=2000):");
println!(
" Active windows: {}",
window_lock.get_active_window_count()
);
println!(" Window ranges:");
for (start, end) in window_lock.get_active_window_ranges() {
println!(" [{}, {})", start, end);
}
println!(" Note: Windows are open but haven't emitted yet!");
println!(" They're waiting for an event with timestamp >= 2000 to trigger closure!\n");
}
println!("timestamp=1000: Adding event (subject_2)");
let quad2 = Quad::new(
NamedNode::new("https://rsp.rs/subject_2")?,
NamedNode::new("https://rsp.rs/predicate")?,
NamedNode::new("https://rsp.rs/object")?,
GraphName::DefaultGraph,
);
stream.add_quads(vec![quad2], 1000)?; thread::sleep(Duration::from_millis(50));
println!("timestamp=1500: Adding event (subject_3)");
let quad3 = Quad::new(
NamedNode::new("https://rsp.rs/subject_3")?,
NamedNode::new("https://rsp.rs/predicate")?,
NamedNode::new("https://rsp.rs/object")?,
GraphName::DefaultGraph,
);
stream.add_quads(vec![quad3], 1500)?; thread::sleep(Duration::from_millis(100));
println!("\nWARNING: Still no results! Windows are open but not closed yet.");
println!(" Need an event with timestamp >= 2000 to trigger closure!\n");
println!("timestamp=2000: Adding event (subject_4)");
println!(" -> This should CLOSE window [-8000, 2000) and emit first result!");
let quad4 = Quad::new(
NamedNode::new("https://rsp.rs/subject_4")?,
NamedNode::new("https://rsp.rs/predicate")?,
NamedNode::new("https://rsp.rs/object")?,
GraphName::DefaultGraph,
);
stream.add_quads(vec![quad4], 2000)?; thread::sleep(Duration::from_millis(100));
println!("\ntimestamp=4000: Adding event (subject_5)");
println!(" -> This should CLOSE window [-6000, 4000) and emit second result!");
let quad5 = Quad::new(
NamedNode::new("https://rsp.rs/subject_5")?,
NamedNode::new("https://rsp.rs/predicate")?,
NamedNode::new("https://rsp.rs/object")?,
GraphName::DefaultGraph,
);
stream.add_quads(vec![quad5], 4000)?; thread::sleep(Duration::from_millis(100));
println!("\ntimestamp=6000: Adding event (subject_6)");
println!(" -> This should CLOSE window [-4000, 6000) and emit third result!");
let quad6 = Quad::new(
NamedNode::new("https://rsp.rs/subject_6")?,
NamedNode::new("https://rsp.rs/predicate")?,
NamedNode::new("https://rsp.rs/object")?,
GraphName::DefaultGraph,
);
stream.add_quads(vec![quad6], 6000)?; thread::sleep(Duration::from_millis(100));
println!("\ntimestamp=7000: Adding final event (subject_7)");
println!(" This is our LAST event.");
let quad7 = Quad::new(
NamedNode::new("https://rsp.rs/subject_7")?,
NamedNode::new("https://rsp.rs/predicate")?,
NamedNode::new("https://rsp.rs/object")?,
GraphName::DefaultGraph,
);
stream.add_quads(vec![quad7], 7000)?; thread::sleep(Duration::from_millis(200));
println!("\nWARNING: Without close_stream(), remaining windows won't emit!");
println!(" We need an event with a higher timestamp to trigger closure.");
if let Some(window) = rsp_engine.get_window("https://rsp.rs/w1") {
let window_lock = window.lock().unwrap();
println!("\nWindow State Before close_stream():");
println!(
" Active windows: {}",
window_lock.get_active_window_count()
);
println!(" Window ranges:");
for (start, end) in window_lock.get_active_window_ranges() {
println!(" [{}, {})", start, end);
}
println!(" These windows have data but haven't emitted because they're still open!\n");
}
println!("=== Calling close_stream() ===");
println!("This adds a sentinel event with timestamp=20000 to close all remaining windows.");
println!("Remember: it's the TIMESTAMP that matters, not wall-clock time!\n");
rsp_engine.close_stream("https://rsp.rs/stream1", 20000)?;
thread::sleep(Duration::from_millis(500));
drop(stream);
result_thread.join().unwrap();
println!("\n=== Key Takeaways ===");
println!("1. Windows emit results when they CLOSE, not when events arrive");
println!("2. Window closure is TIMESTAMP-driven, not wall-clock-driven!");
println!("3. A window closes when a new event with timestamp > window.end arrives");
println!("4. You can add all events instantly - only the timestamp parameter matters");
println!("5. Always call close_stream() at the end to emit final results");
println!("6. Use get_active_window_count() and get_active_window_ranges() for debugging");
println!("7. Enable debug_mode for detailed window lifecycle logging");
println!("\n=== Example Complete ===");
Ok(())
}