Sharing a hashmap between two threads in actix web

Im trying to figure out the best way to share a hashmap across threads in an actix web server. So far i've been able to do it successfully using Mutex and RwLock. but i ideally would like something like Arc-Swap which does seem to be more efficient than the above primitives. However , im unable to update the data wrapped under an ArcSwap and im not sure why

heres my code

fn update_timestamps(data: Arc<ArcSwapAny<Arc<HashMap<String, String>>>>) -> () {
    loop {
        thread::sleep(Duration::from_secs(5));
        println!("Updating time........");
        let now: DateTime<Local> = Local::now();

        let mut new_data = *data.load();

        *data.swap(Arc::clone(new_data));
    }
}

async fn get_latest_timestamp(
    data: web::Data<Arc<ArcSwapAny<Arc<HashMap<String, String>>>>>,
) -> String {
    let wrapped_data = data.load();

    match wrapped_data.get("time_now") {
        Some(i) => String::from(i),
        None => String::from("None"),
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let mut hash = HashMap::new();
    hash.insert(String::from("time_now"), Local::now().to_rfc2822());

    let data = Arc::new(ArcSwap::from_pointee(hash));
    let hash_clone = Arc::clone(&data);

    thread::spawn(move || update_timestamps(hash_clone));

    HttpServer::new(move || {
        App::new()
            .data(data.clone())
            .route("/", web::get().to(get_latest_timestamp))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Im getting an error in this line
*data.swap(Arc::clone(new_data));

mismatched types
expected reference `&Arc<HashMap<String, String>>`
      found struct `Arc<HashMap<String, String>>`

but if i add the borrow - i get the below error

cannot move out of an `Arc`
move occurs because value has type `HashMap<String, String>`, which does not implement the `Copy` trait

so is there a way i can move the HashMap out of Arc ?

Or am i doing something else wrong?

I think it should be something like:

// ...
let mut new_data = (*data.load()).clone();

data.swap(Arc::clone(new_data));
// ...

By the way why are you using Arc<AnyArcSwap<Any<HashMap<_, _>>>> instead of plain AnyArcSwap<HashMap<_, _>>?

Try *data.swap(Arc::clone(&new_data));, see if that helps (though success depends on the actual type of new_data which is inferred here).

Also note that since it appears you want to write to the shared HashMap, the Mutex can cause contention issues between threads.

i already tried this - i get the below error

cannot move out of an `Arc`
move occurs because value has type `HashMap<String, String>`, which does not implement the `Copy` trait

Actix-web requires data that needs to be shared between threads to be wrapped in an arc - please let me know if im wrong

You generally can't use a bare dereference on non-Copy types. Try cloning them instead.

I don't believe that and Arc is necessary if the data is immutably used by the various handlers. But then the type does need to be Send + Sync IIRC.

However if you need to write to the shared data, and are willing to accept the contention issues, then wrapping the data in an Arc can be useful to make "cloning the data" cheap, by cloning the Arc ptr instead of the original type.

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.