use std::collections::VecDeque;
use parking_lot::{Mutex, RwLock};
use super::slowlog_entry::SlowLogEntry;
pub struct SlowLogContainer {
size: usize,
log_entries: Mutex<VecDeque<SlowLogEntry>>,
id: RwLock<i64>,
}
impl SlowLogContainer {
pub fn new(size: i32) -> Self {
Self {
size: size.max(0) as usize,
log_entries: Mutex::new(VecDeque::new()),
id: RwLock::new(0),
}
}
pub fn count(&self) -> i32 {
self.log_entries.lock().len() as i32
}
pub fn add(&self, mut entry: SlowLogEntry) {
entry.id = {
let mut id = self.id.write();
*id += 1;
(*id - 1) as i32
};
let mut entries = self.log_entries.lock();
entries.push_back(entry);
while entries.len() > self.size {
entries.pop_front();
}
}
pub fn clear(&self) {
self.log_entries.lock().clear();
}
pub fn get_entries(&self, count: i32) -> Vec<SlowLogEntry> {
let entries = self.log_entries.lock();
if count < 0 || count as usize >= entries.len() {
return entries.iter().cloned().collect();
}
entries
.iter()
.skip(entries.len() - count as usize)
.cloned()
.collect()
}
}
#[cfg(test)]
mod tests {
use super::{SlowLogContainer, SlowLogEntry};
use crate::types::RespCommand;
fn entry() -> SlowLogEntry {
SlowLogEntry {
id: 0,
timestamp: 1000,
duration: 42,
command: RespCommand::Get,
arguments: None,
client_ip_port: "127.0.0.1:1234".into(),
client_name: "cli".into(),
}
}
#[test]
fn add_assigns_ids_and_enforces_capacity() {
let log = SlowLogContainer::new(3);
for _ in 0..5 {
log.add(entry());
}
assert_eq!(log.count(), 3);
let entries = log.get_entries(-1);
assert_eq!(
entries.iter().map(|e| e.id).collect::<Vec<_>>(),
vec![2, 3, 4]
);
}
#[test]
fn get_entries_tail_snapshot() {
let log = SlowLogContainer::new(10);
for _ in 0..4 {
log.add(entry());
}
let latest_two = log.get_entries(2);
assert_eq!(
latest_two.iter().map(|e| e.id).collect::<Vec<_>>(),
vec![2, 3]
);
assert_eq!(log.get_entries(-1).len(), 4);
assert_eq!(log.get_entries(100).len(), 4);
}
#[test]
fn clear_empties_log() {
let log = SlowLogContainer::new(4);
log.add(entry());
log.clear();
assert_eq!(log.count(), 0);
}
}