chathistory: GC conversation keys idle past chathistory_maxage (default 7d) — the key set never shrank
This commit is contained in:
parent
5522e36125
commit
fad66eaac2
2 changed files with 60 additions and 0 deletions
|
|
@ -9,6 +9,7 @@ use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
use crate::channels::RANK_HALFOP;
|
use crate::channels::RANK_HALFOP;
|
||||||
use crate::command::{CmdResult, Command};
|
use crate::command::{CmdResult, Command};
|
||||||
|
use crate::module::Module;
|
||||||
use crate::server::{iso_time, now, parse_iso, Server};
|
use crate::server::{iso_time, now, parse_iso, Server};
|
||||||
use crate::Uid;
|
use crate::Uid;
|
||||||
|
|
||||||
|
|
@ -91,6 +92,30 @@ pub fn commands() -> Vec<Box<dyn Command>> {
|
||||||
vec![Box::new(ChatHistory), Box::new(Redact)]
|
vec![Box::new(ChatHistory), Box::new(Redact)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Garbage-collects the history ring. Each conversation's buffer is count-capped, but
|
||||||
|
/// the SET of conversation keys (every channel + DM pair that ever spoke) was never
|
||||||
|
/// pruned — a slow leak over the process lifetime. On the tick, drop any conversation
|
||||||
|
/// whose newest message is older than `chathistory_maxage` (default 7d; 0 disables).
|
||||||
|
pub struct ChatHistoryGc;
|
||||||
|
impl Module for ChatHistoryGc {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"chathistory"
|
||||||
|
}
|
||||||
|
fn on_tick(&mut self, s: &mut Server) {
|
||||||
|
let maxage = s.conf_num("chathistory_maxage", 604800u64);
|
||||||
|
if maxage == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let now = now();
|
||||||
|
if let Some(h) = s.ext.get_mut::<History>() {
|
||||||
|
h.0.retain(|_, buf| {
|
||||||
|
buf.back()
|
||||||
|
.is_some_and(|m| now.saturating_sub(m.ts) < maxage)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// CHATHISTORY — replay recent messages (draft/chathistory), leveraging BATCH.
|
/// CHATHISTORY — replay recent messages (draft/chathistory), leveraging BATCH.
|
||||||
/// `CHATHISTORY <LATEST|BEFORE|AFTER|AROUND|BETWEEN> <#chan|nick> <selector..>
|
/// `CHATHISTORY <LATEST|BEFORE|AFTER|AROUND|BETWEEN> <#chan|nick> <selector..>
|
||||||
/// <limit>`; a `<selector>` is `*`, `timestamp=<iso>` or `msgid=<id>`. Channel
|
/// <limit>`; a `<selector>` is `*`, `timestamp=<iso>` or `msgid=<id>`. Channel
|
||||||
|
|
@ -389,3 +414,37 @@ impl Command for Redact {
|
||||||
CmdResult::Ok
|
CmdResult::Ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::config::Config;
|
||||||
|
use std::sync::atomic::AtomicU64;
|
||||||
|
use std::sync::{mpsc, Arc};
|
||||||
|
|
||||||
|
// The GC drops conversation keys whose newest message aged past chathistory_maxage
|
||||||
|
// (default 7d), so the key set can't grow forever with every distinct DM/channel.
|
||||||
|
#[test]
|
||||||
|
fn gc_drops_stale_conversation_keys() {
|
||||||
|
let (tx, _rx) = mpsc::channel();
|
||||||
|
let mut s = Server::new(Config::default(), tx, Arc::new(AtomicU64::new(1)));
|
||||||
|
let n = now();
|
||||||
|
let msg = |ts| HistMsg {
|
||||||
|
ts,
|
||||||
|
msgid: "m".into(),
|
||||||
|
prefix: "p!u@h".into(),
|
||||||
|
verb: "PRIVMSG",
|
||||||
|
target: "#c".into(),
|
||||||
|
text: "hi".into(),
|
||||||
|
};
|
||||||
|
{
|
||||||
|
let h = s.ext.get_or_insert_with::<History>(History::default);
|
||||||
|
h.0.insert("#fresh".into(), VecDeque::from([msg(n)]));
|
||||||
|
h.0.insert("#stale".into(), VecDeque::from([msg(n.saturating_sub(700_000))]));
|
||||||
|
}
|
||||||
|
ChatHistoryGc.on_tick(&mut s);
|
||||||
|
let h = s.ext.get::<History>().unwrap();
|
||||||
|
assert!(h.0.contains_key("#fresh"), "recent conversation kept");
|
||||||
|
assert!(!h.0.contains_key("#stale"), "stale conversation GC'd");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ pub fn default_modules() -> Vec<Box<dyn Module>> {
|
||||||
Box::new(autodrop::AutoDrop),
|
Box::new(autodrop::AutoDrop),
|
||||||
Box::new(operprefix::OperPrefix),
|
Box::new(operprefix::OperPrefix),
|
||||||
Box::new(permchannels::PermChannels::default()),
|
Box::new(permchannels::PermChannels::default()),
|
||||||
|
Box::new(chathistory::ChatHistoryGc),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue