mongo-rust-driver icon indicating copy to clipboard operation
mongo-rust-driver copied to clipboard

using transaction got hang when if there is an error from database , and all read data got hang also forever

Open harmnot opened this issue 4 months ago • 3 comments

Versions/Environment

  1. What version of Rust are you using? rustup 1.27.1 (54dd3d00f 2024-04-24)
  2. What operating system are you using? OS X - Sonoma 14.4.1
  3. What versions of the driver and its dependencies are you using? registry+https://github.com/rust-lang/crates.io-index#[email protected] and registry+https://github.com/rust-lang/crates.io-index#[email protected]
  4. What version of MongoDB are you using? Mongo Atlas 7.0.12 Shared( free tier )
  5. What is your MongoDB topology (standalone, replica set, sharded cluster, serverless)? sharded cluster,

Describe the bug

I have a repository trait that use on services via transaction, here is my code

fn insert_one_transactional<P>(
        &self,
        mut params: InsertOneTransactionalParams<P>,
    ) -> Result<ResultInsertOneRepository, ErrorIdentifier>
    where
        P: DeserializeOwned + Unpin + Send + Sync + Serialize + Debug,
    {
        return executor::block_on(async {
            let col = self.mongo.database.collection(params.col_name.to_owned().as_str());
            if params.transactional.is_none() {
                let session_result = self.mongo.client.start_session(None);

                let session = match session_result.await {
                    Ok(session) => session,
                    Err(err) => {
                        return Err(ErrorIdentifier {
                            hint: HINT_KEY_WRITE_DATA.to_string(),
                            details: None,
                            code: ErrorGlobal::Repository,
                            message: err.to_string(),
                        });
                    }
                };
                params.transactional = Some(session);
                match params.transactional.as_mut().unwrap().start_transaction(None).await {
                    Err(err) => {
                        // Handle the error here
                        return Err(self.process_error(err, params.col_name.to_owned()));
                    }
                    _ => {}
                }
            }

            let result = col.insert_one_with_session(
                params.doc,
                params.options,
                params.transactional.as_mut().unwrap(),
            ).await;

            match result {
                Ok(res) => {
                    Ok(ResultInsertOneRepository {
                        id: res.inserted_id.as_object_id().unwrap().to_hex(),
                        transactional: params.transactional.unwrap(),
                    })
                }
                Err(err) => {
                    return Err(self.process_error(err, params.col_name));
                }
            }
        });
    }

when I was using the function above on non looping case, it was works well, but when I have loop case which mutable the transactional, and then got error from db ( specially validation, I accidentally made it error to see the bug ) it was stuck / hang, I tried log in Err but it was not received on there

is that any way to handle auto abort transactional without call abort_transaction() ? is that something wrong with code because I did wrong way to use that transaction ??

here how I use those function

   fn handle_helper_function(&self, props: ParamsHandleCartSomething, mut trx: Transactional) -> Result<ResultHandleCartSomething, ErrorIdentifier> {
        let mut sub_total = 0.0;
        for (index, item) in props.custom_items.iter().enumerate() {
            if item.quantity < 1 {
                return Err(ErrorIdentifier {
                    code: ErrorGlobal::UseCase,
                    message: "Quantity must be greater than 0".to_string(),
                    details: None,
                    hint: "quantity_less_than_1_on_custom".to_string(),
                });
            }

            for (_z_index, part) in item.parts.iter().enumerate() {
                let product_part_data = self.service_product.get_one((part.to_owned().product_id, props.lang, false, None));
                if let Err(e) = product_part_data {
                    return Err(e);
                }

                sub_total += price_product_val * item.quantity as f64;

                let data_order_ipl = OrderPlaPla::<ProResponse> {
                    ..Default::default()
                };

                let params_insert_order_pl = InsertOneTransactionalParams {
                    col_name: EnumCollectionModel::OrderPlaPla,
                    options: None,
                    doc: data_order_ipl.clone(),
                    transactional: Option::from(trx),
                };

                let result_order_ipl  = self.repository.insert_one_transactional(params_insert_order_pl);
                if let Err(e) = result_order_ipl {
                    return Err(e);
                }
                trx = result_order_ipl.unwrap().transactional;
            }

            let msr_id = DatabaseId::new();
            let msr = YaYaYa {
                ..Default::default()
            };

            let params_insert_plapla = InsertOneTransactionalParams {
                col_name: EnumCollectionModel::PlaPla,
                options: None,
                doc: msr.clone(),
                transactional: Option::from(trx),
            };

            let result_plapla = self.repository.insert_one_transactional(params_insert_plapla);
            if let Err(e) = result_plapla {
                return Err(e);
            }
            trx = result_plapla.unwrap().transactional;


            let data_order_blabla  = OrderMeasurement {
                ..Default::default()
            };

            let params_insert_blabla = InsertOneTransactionalParams {
                col_name: EnumCollectionModel::ColNameHere,
                options: None,
                doc: data_order_blabla.clone(),
                transactional: Option::from(trx),
            };

            let result_order_blabla  = self.repository.insert_one_transactional(params_insert_blabla);
            if let Err(e) = result_order_blabla {
                return Err(e);
            }
            trx = result_order_blabla.unwrap().transactional;
        }

        Ok(ResultHandleCartCustom {
            trx,
            sub_total,
        })
    }

at the end function above, I commit the transition like this IF not error ( but in fact the code not arrive on here yet , because the code above was bug )

pub fn commit_transaction(transactional: Option<Transactional>) -> Result<(), ErrorIdentifier> {
    match transactional {
        Some(mut transactional) => {
            // Use block_on to run the future and get the result
            let commit_result = block_on(async {
                transactional.commit_transaction().await
            });

            match commit_result {
                Ok(_) => {}
                Err(e) => {
                    return Err(ErrorIdentifier {
                        code: ErrorGlobal::Repository,
                        message: e.to_string(),
                        hint: "error_transactional".to_string(),
                        details: None,
                    })
                }
            }
        }
        None => {}
    }
    Ok(())
}

I am using future on my whole project

when I have transaction function insert_one_transactional on looping, sometimes the MongoDB hang or keep loading, nothing give any response, when I cancel the request via postman and then try again , I got hang also and all read and other write clause are not working at all, sometimes I got error like this

Kind: Command failed: Error code 112 (WriteConflict): Caused by ::  :: Please retry your operation or multi-document transaction., labels: {\"TransientTransactionError\"}"

I believed something wrong with the transaction , I got stuck for few days only for this issue on my project :(

this was my issues also : https://github.com/mongodb/mongo-rust-driver/issues/1136 , I thought it solved, it still happened now :(

harmnot avatar Sep 30 '24 13:09 harmnot