Mutable global configuration in rust / axum / tokio

I am trying to create a global configuration that can be used by axum handlers, and that is being updated periodically (in real life from s3, in the example below I kept it simple).

I have created the code below with the help of CoPilot. It seems to work but the code feels weird with all the cloning in the main function, but this is the only way I could make to compile.

Is there any other more elegant way to do it?

use std::sync::{Arc};

use axum::{extract::State, routing::get, Router};
use chrono::Local;
use serde::{Deserialize, Serialize};
use tokio::{net::TcpListener, spawn};
use tokio_schedule::{every, Job};
use tokio::sync::RwLock;


#[derive(Debug, Serialize, Deserialize, Clone)]
struct Config1 {
    time: String
}

struct ServiceState {
    config1: Config1
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

    println!("Hello, world!");

    let service_state = ServiceState {
        config1: read_config().await
    };
    let state_arc = Arc::new(RwLock::new(service_state));
    let state_arc_clone = Arc::clone(&state_arc);

    let every_30_seconds = every(3).seconds().perform(move || {
        let arc_clone = Arc::clone(&state_arc_clone);
        update_config(arc_clone)
    });
    spawn(every_30_seconds);

    let listener = TcpListener::bind("0.0.0.0:3000").await?;

    
    let app = Router::new()
        .route("/config", get(get_config))
        .with_state(state_arc);

    axum::serve(listener, app).await?;

    Ok(())
}

async fn get_config(State(service_state): State<Arc<RwLock<ServiceState>>>) -> axum::Json<Config1> {
    
    let state = service_state.read().await;
    axum::Json(state.config1.clone())
}

async fn update_config(state_arc : Arc<RwLock<ServiceState>>) {
    let new_config = read_config().await;
    state_arc.write().await.config1 = new_config;
}

async fn read_config() -> Config1 {
    let local_time = Local::now();
    Config1 {
        time: local_time.to_string()
    }
}

copilot will happily generate nonsense, as it has done here. don't use it.

read_config does not read anything. it just puts the current time in a string.

generally global state is considered harmful, as it makes lots of things much harder (eg. unit testing individual testing) but if you're going to use global state, you need to have an actual global variable. the easiest way to do this is via a static and RwLock.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.