use std::time::Duration; use models::users::user_recent_mentions; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; use time::OffsetDateTime; use tracing::Instrument; use crate::errors::ChatError; use crate::services::service::ChatService; pub(crate) const STUCK_CLAIM_MINUTES: i64 = 10; const MENTION_RETENTION_DAYS: i64 = 90; impl ChatService { pub async fn prune_old_mentions(&self) -> Result<(), ChatError> { let cutoff = OffsetDateTime::now_utc() - time::Duration::days(MENTION_RETENTION_DAYS); user_recent_mentions::Entity::delete_many() .filter(user_recent_mentions::Column::CreatedAt.lt(cutoff)) .exec(self.database.write()) .await?; Ok(()) } } pub async fn run_background_tasks(service: ChatService) { let mut interval = tokio::time::interval(Duration::from_secs(30)); interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay); let mut tick: u32 = 0; loop { interval.tick().await; let request_id = uuid::Uuid::now_v7().to_string(); let span = tracing::info_span!("chat.background", request_id = %request_id); if let Err(error) = export::middleware::with_request_id( Some(request_id.as_str()), service.deliver_due_reminders().instrument(span), ) .await { tracing::warn!(%error, "chat reminder delivery failed"); } let span = tracing::info_span!("chat.background", request_id = %request_id); if let Err(error) = export::middleware::with_request_id( Some(request_id.as_str()), service.deliver_pending_webhook_events().instrument(span), ) .await { tracing::warn!(%error, "chat outbound webhook delivery failed"); } tick = tick.wrapping_add(1); if tick.is_multiple_of(120) { if let Err(error) = export::middleware::with_request_id( Some(request_id.as_str()), service.prune_old_mentions(), ) .await { tracing::warn!(%error, "chat mention prune failed"); } if let Err(error) = export::middleware::with_request_id( Some(request_id.as_str()), service.cleanup_stale_attachments(), ) .await { tracing::warn!(%error, "chat attachment cleanup failed"); } } } }