diff --git a/src/peerstate.rs b/src/peerstate.rs index 84a2d1851..3e216b126 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -496,6 +496,30 @@ impl Peerstate { } } +/// Removes duplicate peerstates from `acpeerstates` database table. +/// +/// Normally there should be no more than one peerstate per address. +/// However, the database does not enforce this condition. +/// +/// Previously there were bugs that caused creation of additional +/// peerstates when existing peerstate could not be read due to a +/// temporary database error or a failure to parse stored data. This +/// procedure fixes the problem by removing duplicate records. +pub(crate) async fn deduplicate_peerstates(sql: &Sql) -> Result<()> { + sql.execute( + "DELETE FROM acpeerstates + WHERE id NOT IN ( + SELECT MIN(id) + FROM acpeerstates + GROUP BY addr + )", + paramsv![], + ) + .await?; + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/sql.rs b/src/sql.rs index 9809ff936..99ae09134 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -20,7 +20,7 @@ use crate::dc_tools::{dc_delete_file, time}; use crate::ephemeral::start_ephemeral_timers; use crate::message::Message; use crate::param::{Param, Params}; -use crate::peerstate::Peerstate; +use crate::peerstate::{deduplicate_peerstates, Peerstate}; use crate::stock_str; #[macro_export] @@ -598,6 +598,10 @@ pub async fn housekeeping(context: &Context) -> Result<()> { ); } + if let Err(err) = deduplicate_peerstates(&context.sql).await { + warn!(context, "Failed to deduplicate peerstates: {}", err) + } + context.schedule_quota_update().await?; if let Err(e) = context