use std::collections::VecDeque;
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use serde_json::Value;
pub const MAX_EVENTS: usize = 512;
pub const MAX_BYTES: usize = 4 * 1024 * 1024;
pub const MAX_WAIT_MS: u32 = 25_000;
const REPLY_EVENTS: usize = 256;
const REPLY_BYTES: usize = 8 * 1024 * 1024;
struct Entry {
seq: u64,
topic: String,
payload: Arc<Value>,
size: usize,
}
struct State {
entries: VecDeque<Entry>,
bytes: usize,
next_seq: u64,
closed: bool,
}
pub struct Polled {
pub seq: u64,
pub topic: String,
pub payload: Arc<Value>,
}
pub struct PollResult {
pub events: Vec<Polled>,
pub next: u64,
pub dropped: bool,
}
pub struct EventLog {
state: Mutex<State>,
wake: Condvar,
}
impl Default for EventLog {
fn default() -> EventLog {
let start = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_micros() as u64)
.unwrap_or(0);
EventLog {
state: Mutex::new(State {
entries: VecDeque::new(),
bytes: 0,
next_seq: start.saturating_add(1),
closed: false,
}),
wake: Condvar::new(),
}
}
}
impl EventLog {
fn lock(&self) -> MutexGuard<'_, State> {
self.state.lock().unwrap_or_else(|e| e.into_inner())
}
pub fn push(&self, topic: &str, payload: &Value) {
let mut size = serde_json::to_vec(payload).map(|v| v.len()).unwrap_or(0);
let oversized = size > MAX_BYTES;
let marker = serde_json::json!({ "truncated": true, "bytes": size });
let payload = if oversized { &marker } else { payload };
if oversized {
size = serde_json::to_vec(payload).map(|v| v.len()).unwrap_or(0);
}
let mut state = self.lock();
let seq = state.next_seq;
state.next_seq = seq.saturating_add(1);
state.bytes += size;
state.entries.push_back(Entry {
seq,
topic: topic.to_string(),
payload: Arc::new(payload.clone()),
size,
});
while state.entries.len() > 1
&& (state.entries.len() > MAX_EVENTS || state.bytes > MAX_BYTES)
{
if let Some(old) = state.entries.pop_front() {
state.bytes -= old.size;
}
}
drop(state);
self.wake.notify_all();
}
pub fn close(&self) {
self.lock().closed = true;
self.wake.notify_all();
}
pub fn poll(&self, after: u64, topics: &[String], wait: Duration) -> PollResult {
let deadline = Instant::now() + wait;
let mut state = self.lock();
loop {
let last = state.next_seq - 1;
let oldest = state.entries.front().map_or(state.next_seq, |e| e.seq);
if after > last || after.saturating_add(1) < oldest {
return PollResult {
events: Vec::new(),
next: last,
dropped: true,
};
}
let start = ((after + 1 - oldest) as usize).min(state.entries.len());
let mut events = Vec::new();
let mut bytes = 0usize;
let mut truncated = false;
for entry in state.entries.range(start..) {
if !topics.is_empty() && !topics.contains(&entry.topic) {
continue;
}
if !events.is_empty()
&& (events.len() >= REPLY_EVENTS || bytes + entry.size > REPLY_BYTES)
{
truncated = true;
break;
}
bytes += entry.size;
events.push(Polled {
seq: entry.seq,
topic: entry.topic.clone(),
payload: Arc::clone(&entry.payload),
});
}
if !events.is_empty() {
let next = if truncated {
events.last().map_or(last, |e| e.seq)
} else {
last
};
return PollResult {
events,
next,
dropped: false,
};
}
let now = Instant::now();
if state.closed || now >= deadline {
return PollResult {
events,
next: last,
dropped: false,
};
}
state = self
.wake
.wait_timeout(state, deadline - now)
.unwrap_or_else(|e| e.into_inner())
.0;
}
}
}