use segment_buffer::{FlushPolicy, SegmentBuffer, SegmentConfig};
use serde::{Deserialize, Serialize};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::thread;
use tempfile::tempdir;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
struct Job {
writer: u64,
seq: u64,
}
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
let dir = tempdir()?;
let config = SegmentConfig::builder()
.flush_policy(FlushPolicy::Manual)
.compression_level(3)
.build();
let buf: Arc<SegmentBuffer<Job>> = Arc::new(SegmentBuffer::open(dir.path(), config)?);
const WRITERS: u64 = 4;
const WRITES_PER_WRITER: u64 = 1_000;
let total_writes = WRITERS * WRITES_PER_WRITER;
let total_appended = Arc::new(AtomicU64::new(0));
let mut handles = Vec::new();
for writer_id in 0..WRITERS {
let buf = buf.clone();
let counter = total_appended.clone();
handles.push(thread::spawn(move || {
for seq in 0..WRITES_PER_WRITER {
let _assigned_seq = buf
.append(Job {
writer: writer_id,
seq,
})
.expect("append must not fail under backpressure (Manual flush, no cap)");
counter.fetch_add(1, Ordering::Relaxed);
}
}));
}
for h in handles {
h.join().unwrap();
}
println!(
"[writers] {} writers x {} appends = {} total (verified: {})",
WRITERS,
WRITES_PER_WRITER,
total_writes,
total_appended.load(Ordering::Relaxed)
);
buf.flush()?;
println!(
"[after flush] pending_count={} latest_sequence={}",
buf.pending_count(),
buf.latest_sequence()
);
let mut next_to_read: u64 = 0;
let mut total_read: u64 = 0;
let mut per_writer_counts = [0u64; WRITERS as usize];
while next_to_read < total_writes {
let batch = buf.read_from(next_to_read, 100)?;
if batch.is_empty() {
break;
}
for job in &batch {
per_writer_counts[job.writer as usize] += 1;
}
let last_buffer_seq = next_to_read + batch.len() as u64 - 1;
let _deleted = buf.delete_acked(last_buffer_seq)?;
next_to_read = last_buffer_seq + 1;
total_read += batch.len() as u64;
}
println!(
"[reader] read {} jobs across {} batches",
total_read,
total_read / 100
);
for (writer, count) in per_writer_counts.iter().enumerate() {
println!(" writer {}: {} jobs read back", writer, count);
}
assert_eq!(total_read, total_writes, "every appended job must be read");
for count in per_writer_counts {
assert_eq!(
count, WRITES_PER_WRITER,
"every writer's jobs must be fully drained"
);
}
println!();
println!("TAKEAWAY: parking_lot::Mutex made this correct with zero ceremony.");
println!("Lock contention is the only serialization — no reads block writes for long.");
Ok(())
}