Skip to main content

headless_lms_server/programs/
credit_registrar.rs

1//! The worker that owns the credit registration ledger: one process, several phases, each with its
2//! own interval and its own row in `credit_registration_phase_state`. Every iteration goes through
3//! the same dispatcher the test tick endpoint uses.
4
5use std::env;
6
7use sqlx::PgPool;
8
9use crate::config::program_config::ProgramConfig;
10use crate::domain::credit_registration_phases::worker_loop;
11use crate::setup_tracing;
12use headless_lms_base::config::ApplicationConfiguration;
13
14const PROCESS_NAME: &str = "credit-registrar";
15
16pub async fn main() -> anyhow::Result<()> {
17    run_credit_registration_worker(
18        PROCESS_NAME,
19        "Starting the credit registrar.",
20        "Still registering credits.",
21    )
22    .await
23}
24
25/// The bootstrap both credit-registration binaries share; they differ only in the phases
26/// `worker_loop::run` picks for `process_name` and in these messages.
27pub async fn run_credit_registration_worker(
28    process_name: &'static str,
29    start_message: &str,
30    still_running_message: &str,
31) -> anyhow::Result<()> {
32    // TODO: Audit that the environment access only happens in single-threaded code.
33    unsafe { env::set_var("RUST_LOG", "info,actix_web=info,sqlx=warn") };
34    dotenvy::dotenv().ok();
35    setup_tracing()?;
36
37    let db_url = ProgramConfig::database_url_with_default();
38    // Fails at boot without credentials, so a misconfigured deploy is loud instead of silently idle.
39    let app_configuration = ApplicationConfiguration::try_from_env()?;
40    let db_pool = PgPool::connect(&db_url).await?;
41
42    info!("{start_message}");
43    worker_loop::run(
44        process_name,
45        db_pool,
46        app_configuration,
47        still_running_message,
48    )
49    .await
50}