Skip to main content

headless_lms_server/programs/
credit_registration_workers.rs

1//! The two credit registration worker processes. They share a bootstrap and differ only in the
2//! phases `worker_loop::run` picks for their process.
3
4use sqlx::postgres::PgPoolOptions;
5
6use crate::config::program_config::ProgramConfig;
7use crate::setup_tracing;
8use headless_lms_base::config::ApplicationConfiguration;
9use headless_lms_credit_registration::{WorkerProcess, worker_loop};
10
11/// Runs the worker that owns the credit registration ledger.
12pub async fn credit_registrar_main() -> anyhow::Result<()> {
13    run(
14        WorkerProcess::CreditRegistrar,
15        "Starting the credit registrar.",
16        "Still registering credits.",
17    )
18    .await
19}
20
21/// Runs the worker that owns everything about credit registration except the ledger: enrolment
22/// discovery, the account-linking mails and the course configuration check. Separate from the
23/// registrar because none of its phases move a ledger row and its intervals are hours, not seconds.
24pub async fn suotar_syncer_main() -> anyhow::Result<()> {
25    run(
26        WorkerProcess::SuotarSyncer,
27        "Starting the Suotar syncer.",
28        "Still syncing with the study registry.",
29    )
30    .await
31}
32
33async fn run(
34    process: WorkerProcess,
35    start_message: &str,
36    still_running_message: &str,
37) -> anyhow::Result<()> {
38    dotenvy::dotenv().ok();
39    ProgramConfig::ensure_default_rust_log_for_workers();
40    setup_tracing()?;
41
42    let db_url = ProgramConfig::database_url_with_default();
43    // Fails at boot without credentials, so a misconfigured deploy is loud instead of silently idle.
44    let app_configuration = ApplicationConfiguration::try_from_env()?;
45    // Every phase loop and its heartbeat keeper may hold a connection at once.
46    let db_pool = PgPoolOptions::new()
47        .max_connections(20)
48        .connect(&db_url)
49        .await?;
50
51    info!("{start_message}");
52    let result = worker_loop::run(process, db_pool, app_configuration, still_running_message).await;
53    if let Err(error) = &result {
54        error!(process = process.as_str(), error = %error, "Worker loop exited with an error");
55    }
56    result?;
57    Ok(())
58}