use rusteron_archive::testing::{find_unused_udp_port, EmbeddedArchiveMediaDriverProcess};
use rusteron_archive::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::thread::sleep;
use std::time::{Duration, Instant};
fn main() -> Result<(), Box<dyn std::error::Error>> {
EmbeddedArchiveMediaDriverProcess::kill_all_java_processes().ok();
let id = Aeron::nano_clock();
let aeron_dir = format!("target/aeron/{id}_example/shm");
let archive_dir = format!("target/aeron/{id}_example/archive");
let req_port = find_unused_udp_port(9000).expect("no free port");
let resp_port = find_unused_udp_port(req_port + 1).expect("no free port");
let events_port = find_unused_udp_port(resp_port + 1).expect("no free port");
let _process = EmbeddedArchiveMediaDriverProcess::build_and_start(
&aeron_dir,
&archive_dir,
&format!("aeron:udp?endpoint=localhost:{req_port}"),
&format!("aeron:udp?endpoint=localhost:{resp_port}"),
&format!("aeron:udp?endpoint=localhost:{events_port}"),
)?;
let aeron_context = AeronContext::new()?;
aeron_context.set_dir(&cformat!("{aeron_dir}"))?;
aeron_context.set_client_name(&cformat!("ps-example-{id}"))?;
let aeron = Aeron::new(&aeron_context)?;
aeron.start()?;
let archive_context = AeronArchiveContext::new()?;
archive_context.set_aeron(&aeron)?;
archive_context.set_control_request_channel(&cformat!("aeron:udp?endpoint=localhost:{req_port}"))?;
archive_context.set_control_response_channel(&cformat!("aeron:udp?endpoint=localhost:{resp_port}"))?;
archive_context.set_recording_events_channel(&cformat!("aeron:udp?endpoint=localhost:{events_port}"))?;
let archive =
AeronArchiveAsyncConnect::new_with_aeron(&archive_context, &aeron)?.poll_blocking(Duration::from_secs(20))?;
println!("connected to archive");
let live_channel = c"aeron:ipc";
let stream_id = 1001;
archive.start_recording(live_channel, stream_id, SOURCE_LOCATION_LOCAL, true)?;
let publication = aeron
.async_add_publication(live_channel, stream_id)?
.poll_blocking(Duration::from_secs(5))?;
while !publication.is_connected() {
sleep(Duration::from_millis(10));
}
for i in 0..10 {
let msg = format!("History-{i}");
while publication.offer(msg.as_bytes()).is_err() {
sleep(Duration::from_millis(1));
}
}
println!("seeded 10 historical messages");
let session_id = publication.get_constants()?.session_id;
let counters = aeron.counters_reader();
let counter_id = RecordingPos::find_counter_id_by_session(&counters, session_id);
let recording_id = RecordingPos::get_recording_id_block(&counters, counter_id, Duration::from_secs(5))?;
let published_position = publication.position();
let deadline = Instant::now() + Duration::from_secs(5);
while counters.get_counter_value(counter_id) < published_position && Instant::now() < deadline {
sleep(Duration::from_millis(10));
}
struct Listener {
joined: Arc<AtomicUsize>,
errors: Arc<Mutex<Vec<(i32, String)>>>,
}
impl PersistentSubscriptionListener for Listener {
fn on_live_joined(&self) {
self.joined.fetch_add(1, Ordering::SeqCst);
println!("on_live_joined: replay merged into the live stream");
}
fn on_live_left(&self) {
println!("on_live_left: fell back to replay");
}
fn on_error(&self, code: i32, msg: &str) {
self.errors.lock().unwrap().push((code, msg.into()));
}
}
let joined = Arc::new(AtomicUsize::new(0));
let errors: Arc<Mutex<Vec<(i32, String)>>> = Arc::new(Mutex::new(Vec::new()));
let ps = persistent_subscription_builder()?
.aeron(&aeron)?
.archive_context(&archive_context)?
.live_channel(live_channel.to_str()?)?
.live_stream_id(stream_id)?
.replay_channel("aeron:udp?endpoint=localhost:0")? .replay_stream_id(stream_id + 1)?
.start_from_beginning()?
.recording_id(recording_id)?
.listener(Listener {
joined: joined.clone(),
errors: errors.clone(),
})?
.build()?;
println!("persistent subscription created, replaying then joining live...");
let send_timestamp_supplier = Handler::new(|_frame: &mut [u8]| Aeron::nano_clock());
let deadline = Instant::now() + Duration::from_secs(30);
let mut live_sent = 0;
while !ps.is_live() && Instant::now() < deadline {
if ps.has_failed() {
let (code, msg) = ps.get_failure_reason().unwrap_or((-1, "unknown".into()));
return Err(format!("persistent subscription failed ({code}): {msg}").into());
}
live_sent += 1;
let msg = format!("Live-{live_sent}");
let _ = publication.offer_with_reserved_value(msg.as_bytes(), Some(&send_timestamp_supplier));
let fragments = ps.poll_fn(|buf, _hdr| println!(" fragment ({} bytes)", buf.len()), 100)?;
if fragments == 0 {
sleep(Duration::from_millis(1));
}
}
let joined_count = joined.load(Ordering::SeqCst);
println!("joined live {joined_count} time(s); sent {live_sent} live messages; done");
assert!(joined_count >= 1, "did not join live; errors: {:?}", errors);
Ok(())
}