using transaction got hang when if there is an error from database
Versions/Environment
- What version of Rust are you using?
rustup 1.27.1 (2024-04-24) - What operating system are you using?
OS X - Sonoma 14.4.1 - What versions of the driver and its dependencies are you using?
registry+https://github.com/rust-lang/crates.io-index#[email protected]andregistry+https://github.com/rust-lang/crates.io-index#[email protected] - What version of MongoDB are you using?
Mongo Atlas 7.0.11 ( free tier ) - 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
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.
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.
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.