Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using transaction got hang when if there is an error from database #1136

Closed
harmnot opened this issue Jun 17, 2024 · 3 comments
Closed

using transaction got hang when if there is an error from database #1136

harmnot opened this issue Jun 17, 2024 · 3 comments

Comments

@harmnot
Copy link

harmnot commented Jun 17, 2024

Versions/Environment

  1. What version of Rust are you using? rustup 1.27.1 (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.11 ( 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

@isabelatkinson
Copy link
Contributor

isabelatkinson commented Jun 21, 2024

Hi @harmnot, thanks for opening this issue!

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

Are you able to determine on which line specifically your code is hanging? It's difficult to know what exactly is causing the problem without knowing exactly where it's coming from.

is that any way to handle auto abort transactional without call abort_transaction() ?

If a session with an active transaction goes out of scope, the transaction will be aborted automatically in the Drop implementation for ClientSession. That said, I recommend calling abort_transaction specifically if you know that you want to abort your transaction, because running asynchronous operations in Drop implementations can sometimes cause issues.

Copy link

There has not been any recent activity on this ticket, so we are marking it as stale. If we do not hear anything further from you, this issue will be automatically closed in one week.

@github-actions github-actions bot added the Stale label Jun 29, 2024
Copy link

github-actions bot commented Jul 7, 2024

There has not been any recent activity on this ticket, so we are closing it. Thanks for reaching out and please feel free to file a new issue if you have further questions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants