Rust driver "AtlasError", message: "CMD_NOT_ALLOWED: refreshSessions

Hi I’m executing a long task using the rust driver on my MongoDB atlas cluster. I was getting the “CursorNotFound” error, so I modified my code to refresh the session every 5 minutes, but now I’m getting this error “CMD_NOT_ALLOWED: refreshSessions” instead, can someone help on this? what am I doing wrong?

fn get_session_id(session: &mut ClientSession) -> Result<Bson, &str> {
    match session.id().get("id") {
        Some(id) => {
            debug!("session id: {:?}", id);
            Ok(id.clone())
        }
        None => {
            error!("no session id");
            Err("no session id")
        }
    }
}

pub async fn some_big_calculation(client: &Client) -> Result<(), Box<dyn std::error::Error>> {
    // Get a handle to a database.
    let database = client.database("database");

    // List the names of the collections in that database.
    for collection_name in database.list_collection_names(None).await? {
        info!("collection_name: {}", collection_name);
        let collection: Collection<Document> = database.collection(&collection_name);
        let mut current_session = client.start_session(None).await?;
        // Query the documents in the collection with a filter and an option.
        let filter = doc! { "some_field": { "$exists": false } };
        let find_options = FindOptions::builder().sort(doc! { "_id": -1 }).build();
        let mut cursor = collection
            .find_with_session(filter, find_options, &mut current_session)
            .await?;

        let mut last_id: i64 = 0;
        if let Some(last_document) = cursor.with_session(&mut current_session).next().await {
            let document = last_document?;
            last_id = document.get_i64("_id")?;

            //do some calculation with the first document here
            // the first document is a special case
        } else {
            error!("Cursor not found, collection_name: {}", collection_name);
            continue;
        }

        let wait_time = Duration::minutes(5);
        let mut start = Instant::now();
        let session_id = get_session_id(&mut current_session)?;

        // Iterate over the results of the cursor.
        // It is the previous document because we are ordering in descendent order by date/id
        while let Some(previous_document) = cursor.with_session(&mut current_session).next().await {
            let document = previous_document?;
            let previous_id = document.get_i64("_id")?;

            let some_field = expensive_calculation();
            let filter = doc! { "_id" :  last_id };
            let update = doc! {"$set" : { "some_field": some_field}};

            let update_result = collection.update_one(filter, update, None).await?;
            info!(
                "collection: {}, _id: {}, update_result: {:?}",
                collection_name, last_id, update_result
            );

            last_id = previous_id;

            // Check if more than 5 minutes have passed since the last refresh
            match wait_time.checked_sub(start.elapsed()) > Some(0.seconds()) {
                true => {
                    debug!(
                        "remaining time: {:?}",
                        wait_time.checked_sub(start.elapsed())
                    )
                }
                false => {
                    info!("5 min passed, refreshing session");
                    start = Instant::now();
                    
                    let r = database
                        .run_command(doc! { "refreshSessions": [ {"id": &session_id}] }, None)
                        .await?;
                    info!("{:?}", r);
                }
            }
        }
    }

    Ok(())
}

And the problem is that if I don’t try to refresh the session I always get this other error:

Error { kind: CommandError(CommandError { code: 43, code_name: “CursorNotFound”, message: “cursor id 7688697219134251972 not found”, labels: [] }), labels: [] }

Hi @Adrian_Espinosa!

This is not an issue with the Rust driver but rather a limitation of Atlas shared tier, which does not currently support the refreshSessions command. As a workaround, you can issue a ping command using the session which should also refresh it.

database.run_command_with_session(doc! { "ping": 1 }, None, &mut current_session).await?;

Note: if you’re on MongoDB < 4.0.7, you’ll need to use a command that requires authentication instead of ping, e.g. listDatabases.

Thank you for your help @Patrick_Freed, but looks like I can’t run the command you recommended:

database.run_command_with_session(doc! { "ping": 1 }, None, &mut current_session).await?;

because it requires the &mut current_session, but the current_session was borrowed already in the while loop. This line to be specific:

        while let Some(previous_document) = cursor.with_session(&mut current_session).next().await {

Do you know how I Can fix this error?

cannot borrow current_session as mutable more than once at a time
second mutable borrow occurs here…

Thanks!

That error is in fact caused by a driver bug, thanks for reporting it! I filed RUST-796 to track the work for getting that fixed.

In the meantime, you can work around it by doing something like this:

loop {
    if let Some(doc) = cursor.with_session(&mut session).next().await {
        // do stuff with doc
    }
    db.run_command_with_session(doc! { "ping": 1 }, None, &mut current_session).await?;
}

This ensures the mutable reference to the session is released before you run the ping to satisfy the borrow checker.

Thanks for the help @Patrick_Freed!

No problem! Please let us know if you run into any further issues.

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.