mirror of
https://github.com/TheFunny/TelegramTwitterMediaBot.git
synced 2026-09-29 00:12:12 +00:00
fix: prune persisted expired prompts
This commit is contained in:
@@ -178,7 +178,7 @@ impl ChatStore {
|
|||||||
// what the eviction below is for. The pruning itself re-reads and
|
// what the eviction below is for. The pruning itself re-reads and
|
||||||
// writes under the per-chat lock below; taking no lock here means a
|
// writes under the per-chat lock below; taking no lock here means a
|
||||||
// chat appearing later is simply picked up by the next sweep.
|
// chat appearing later is simply picked up by the next sweep.
|
||||||
let candidates: Vec<i64> = {
|
let mut candidates: Vec<i64> = {
|
||||||
let cache = self.cache.lock();
|
let cache = self.cache.lock();
|
||||||
cache
|
cache
|
||||||
.iter()
|
.iter()
|
||||||
@@ -192,6 +192,48 @@ impl ChatStore {
|
|||||||
.map(|(chat_id, _)| *chat_id)
|
.map(|(chat_id, _)| *chat_id)
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
let persisted = self
|
||||||
|
.pool
|
||||||
|
.with_conn_or(
|
||||||
|
log::Level::Warn,
|
||||||
|
"expired prompt scan failed",
|
||||||
|
Vec::<i64>::new(),
|
||||||
|
move |conn| {
|
||||||
|
let mut stmt = conn.prepare("SELECT chat_id, payload FROM chat_state")?;
|
||||||
|
let rows = stmt.query_map([], |row| {
|
||||||
|
let id: String = row.get(0)?;
|
||||||
|
let id: i64 = id.parse().map_err(|e| {
|
||||||
|
rusqlite::Error::FromSqlConversionFailure(
|
||||||
|
0,
|
||||||
|
rusqlite::types::Type::Text,
|
||||||
|
Box::new(e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let payload: String = row.get(1)?;
|
||||||
|
let data: ChatData = serde_json::from_str(&payload).map_err(|e| {
|
||||||
|
rusqlite::Error::FromSqlConversionFailure(
|
||||||
|
0,
|
||||||
|
rusqlite::types::Type::Text,
|
||||||
|
Box::new(e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok((id, data))
|
||||||
|
})?;
|
||||||
|
Ok(rows
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.filter(|(_, data)| {
|
||||||
|
data.edit_message
|
||||||
|
.values()
|
||||||
|
.any(|entry| entry.created_at + ttl_secs <= now)
|
||||||
|
})
|
||||||
|
.map(|(id, _)| id)
|
||||||
|
.collect())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
candidates.extend(persisted);
|
||||||
|
candidates.sort_unstable();
|
||||||
|
candidates.dedup();
|
||||||
let mut removed = Vec::new();
|
let mut removed = Vec::new();
|
||||||
let mut evicted_chats = Vec::new();
|
let mut evicted_chats = Vec::new();
|
||||||
for chat_id in candidates {
|
for chat_id in candidates {
|
||||||
@@ -320,6 +362,31 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn persisted_expired_prompts_are_pruned_after_restart() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let path = dir.path().join("cold.db");
|
||||||
|
let pool = crate::db::open_store(path.to_str().unwrap()).unwrap();
|
||||||
|
let raw = rusqlite::Connection::open(&path).unwrap();
|
||||||
|
let data = ChatData {
|
||||||
|
edit_message: [(1, edit_entry(7, unix_now() - 3600))]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
..ChatData::default()
|
||||||
|
};
|
||||||
|
raw.execute(
|
||||||
|
"INSERT INTO chat_state (chat_id, payload) VALUES ('7', ?1)",
|
||||||
|
rusqlite::params![serde_json::to_string(&data).unwrap()],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let store = ChatStore::new(pool);
|
||||||
|
assert!(store.cache.lock().get(&7).is_none());
|
||||||
|
assert_eq!(
|
||||||
|
store.prune_expired(Duration::from_secs(60)).await,
|
||||||
|
vec![(7, 1)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn an_idle_chat_is_evicted_and_its_state_reloads() {
|
async fn an_idle_chat_is_evicted_and_its_state_reloads() {
|
||||||
let dir = tempfile::tempdir().unwrap();
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user