Skip to main content

headless_lms_credit_registration/use_cases/
retention_sweep.rs

1//! The `retention-sweep` phase: clearing out records past their retention window.
2
3use chrono::{Duration, Utc};
4use headless_lms_models::credit_registration_enrolment_check_outcomes;
5use headless_lms_models::student_number_verification_tokens::soft_delete_expired;
6use headless_lms_models::suotar_api_calls::{RETENTION_DAYS, delete_older_than};
7use sqlx::PgPool;
8
9use crate::error::CreditRegistrationResult;
10use crate::workflow::Counts;
11
12/// How much one retention sweep removes from each table.
13const SWEEP_LIMIT: i64 = 500;
14/// Above the tens of thousands of checks a day the hourly sweep has to keep up with.
15const OUTCOME_SWEEP_LIMIT: i64 = 5000;
16
17/// The call log's 90-day window, the enrolment check log's own longer one, and the expired linking
18/// tokens.
19///
20/// Bounded per iteration and run hourly, so the first sweep after the window opens clears a backlog
21/// over several hours instead of in one statement that locks every `credit_registration_events` row
22/// referencing it.
23pub(crate) async fn run(pool: &PgPool) -> CreditRegistrationResult<Counts> {
24    let mut conn = pool.acquire().await?;
25    let cutoff = Utc::now() - Duration::days(RETENTION_DAYS);
26    let purged_calls = delete_older_than(&mut conn, cutoff, SWEEP_LIMIT).await?;
27    let purged_outcomes = credit_registration_enrolment_check_outcomes::delete_older_than(
28        &mut conn,
29        Utc::now() - Duration::days(credit_registration_enrolment_check_outcomes::RETENTION_DAYS),
30        OUTCOME_SWEEP_LIMIT,
31    )
32    .await?;
33    let retired_tokens = soft_delete_expired(&mut conn, SWEEP_LIMIT).await?;
34    if purged_calls > 0 || purged_outcomes > 0 || retired_tokens > 0 {
35        info!(
36            purged_calls,
37            purged_outcomes,
38            retired_tokens,
39            "Retention sweep purged expired credit registration records"
40        );
41    }
42    Ok(Counts::processed(
43        i64::try_from(purged_calls + purged_outcomes + retired_tokens).unwrap_or(i64::MAX),
44    ))
45}