maxcountryman / axum-sessions

šŸ„  Cookie-based sessions for Axum via async-session.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to persist postgres sessions between server restarts or load balanced servers?

abcd-ca opened this issue Ā· comments

I've almost got things working perfectly, I'm just missing one detail that I can't put my finger on. Here's what I've got:

  • axum
  • axum-session
  • async-sqlx-session

And here's a pared down version of main.rs

#[tokio::main]
async fn main() {
    let db_url = env::var("DATABASE_URL").unwrap();

    // session cookie ------ start
    let store = PostgresSessionStore::new(&db_url)
        .await
        .map_err(|e| {
            eprintln!("Database error: {:?}", e);
            RestAPIError::InternalServerError
        })
        .expect("Could not create session store.");
    store
        .migrate()
        .await
        .expect("Could not migrate session store.");
    store.spawn_cleanup_task(Duration::from_secs(60 * 60));
    // let mut session = Session::new();
    //
    // let cookie_value = store.store_session(session).await.unwrap().unwrap();
    // let session = store.load_session(cookie_value).await.unwrap();
    // println!("main session: {:?}", session);

    let secret = random::<[u8; 128]>();
    let session_layer = SessionLayer::new(store, &secret)
        .with_secure(true)
        .with_same_site_policy(SameSite::Strict)
        .with_persistence_policy(PersistencePolicy::ChangedOnly);
    // session cookie ------ end

    let routes_all = Router::new()
        .nest("/api/campaign", routes_campaign::routes())
        .layer(session_layer);

    // start server -------- start
    // Default IP as a fallback. This is what it should be for running on my local dev machine without Docker
    let default_ip = "127.0.0.1";

    let host: IpAddr = env::var("SERVER_LISTEN_HOST")
        .unwrap_or(default_ip.to_string()) // Use the default IP if the env variable is missing
        .parse() // Try parsing the string into an IpAddr
        .expect("Failed to parse host (listen on) IP address"); // If parsing fails, this will panic. You can handle this more gracefully if needed.

    let addr = SocketAddr::from((host, 8080));
    println!("Listening on {addr}\n");
    axum::Server::bind(&addr)
        .serve(routes_all.into_make_service())
        .await
        .unwrap();

    // start server -------- end
}

What I am finding is that the database session tables are getting updated correctly and the session persists between browser page reloads but if I stop my server and restart it, a new session is created and the old one is orphaned in the database. So, on start, the existing session is not being rehydrated. I think this must have something to do with the way the session is instantiated and is not using the sid cookie passed by the browser but I'm trying to work out what I need to modify.

Thanks for looking.

Thanks for reporting this. We've since moved development to tower-sessions. Can I ask you to try migrating to that first?

@maxcountryman Thank you, I migrated to tower-sessions and it works like a charm. Only comment is that it's a bit harder to see the session data in the db when developing since it's a blob now rather than JSON string. The API is nicer now though and I like how the Postgres store is built in. Nice work, team!