Skip to main content

headless_lms_server/programs/
regrader.rs

1use std::{sync::Arc, time::Duration};
2
3use crate::config::FileStoreRuntimeConfig;
4use crate::config::program_config::ProgramConfig;
5use crate::domain::models_requests;
6use headless_lms_base::config::ApplicationConfiguration;
7use headless_lms_base::jwt::JwtKey;
8use headless_lms_models as models;
9use headless_lms_utils::error::is_db_disconnect;
10use headless_lms_utils::periodic_worker::{
11    PeriodicWorkerConfig, StillRunningLog, run_periodic_worker,
12};
13use models::library::regrading;
14use sqlx::PgPool;
15
16/**
17Starts a thread that will periodically send regrading submissions to the corresponding exercise services for regrading.
18*/
19pub async fn main() -> anyhow::Result<()> {
20    dotenvy::dotenv().ok();
21    ProgramConfig::ensure_default_rust_log_for_workers();
22    crate::setup_tracing()?;
23    let db_url = ProgramConfig::database_url_with_default();
24    let app_conf = ApplicationConfiguration::try_from_env()?;
25    let jwt_key = Arc::new(JwtKey::new(&app_conf.jwt_password)?);
26    let file_store =
27        crate::setup_file_store(&FileStoreRuntimeConfig::try_from_env()?, &app_conf.base_url).await;
28
29    // Since this is repeating every 10 seconds we can keep the connection open.
30    let db_pool = PgPool::connect(&db_url).await?;
31    let mut conn = db_pool.acquire().await?;
32
33    run_periodic_worker(
34        PeriodicWorkerConfig {
35            tick_interval: Duration::from_secs(10),
36            still_running: Some(StillRunningLog {
37                every: 60,
38                message: "running the regrader",
39                initial_ticks: 60,
40            }),
41            delay_missed_ticks: false,
42        },
43        async || {
44            let exercise_services_by_type =
45                models::exercise_service_info::get_upsert_all_exercise_services_by_type(
46                    &mut conn,
47                    models_requests::fetch_service_info,
48                )
49                .await?;
50            // do not stop the thread on error, report it and try again next tick
51            if let Err(err) = regrading::regrade(
52                &mut conn,
53                &exercise_services_by_type,
54                models_requests::make_grading_request_sender(
55                    Arc::clone(&jwt_key),
56                    app_conf.base_url.clone(),
57                ),
58                file_store.as_ref(),
59                &app_conf,
60            )
61            .await
62            {
63                tracing::error!("Error in regrader: {}", err);
64                if is_db_disconnect(&err) {
65                    // this usually happens if the database is reset while running bin/dev etc.
66                    tracing::info!(
67                        "regrader may have lost its connection to the db, trying to reconnect"
68                    );
69                    conn = db_pool.acquire().await?;
70                }
71            }
72            Ok(())
73        },
74    )
75    .await
76}