Skip to content

RUST-107 Convenient transactions #849

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

Merged
merged 25 commits into from
Apr 12, 2023

Conversation

abr-egn
Copy link
Contributor

@abr-egn abr-egn commented Apr 4, 2023

RUST-107

This adds the convenient transactions API and associated tests. Running the v2 spec tests required quite a bit of refactoring in order to allow recursively running operations within the withTransaction operation.

}
}

pub(crate) struct OpSessions<'a> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The split here between OpSessions and OpRunner is to satisfy the borrow checker during recursive operation, in which distinct mutable borrows of each are needed.

operation: &Operation,
mut sessions: OpSessions<'b>,
) -> Option<Result<Option<bson::Bson>, crate::error::Error>> {
if operation.name == "withTransaction" {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not thrilled about special-casing withTransaction by name, but I couldn't think of a better way to do this.

return Some(operation.execute_recursive(self, sessions).await);
}

let db = match &operation.database_options {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Despite what github's diff says, the rest of the logic in this method is unchanged from the original loop.

async fn run_operation(
&mut self,
operation: &Operation,
) -> Option<Result<Option<bson::Bson>, crate::error::Error>> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type here is a little complex because along with succeeding or failing an operation can also be skipped.

@@ -87,6 +97,74 @@ pub(crate) struct Operation {
pub(crate) session: Option<String>,
}

impl Operation {
pub(crate) fn assert_result_matches(&self, result: &Result<Option<Bson>>, description: &str) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was also moved unchanged from its original place in run_v2_test.

async move {
for op in operations.iter() {
let sessions = OpSessions {
session0: Some(session),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is literally the same pointer as session0 passed to execute_recursive, but to satisfy the borrow checker we need to re-bind it with the lifetime provided to the callback.

@abr-egn abr-egn marked this pull request as ready for review April 4, 2023 18:36
@abr-egn abr-egn requested a review from isabelatkinson April 4, 2023 18:36
options: impl Into<Option<TransactionOptions>>,
) -> Result<R>
where
F: for<'a> FnMut(&'a mut ClientSession, &'a mut C) -> BoxFuture<'a, Result<R>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way we could rewrite this signature to avoid including BoxFuture? futures_core is a pre-1.0 crate, so I think we need to be cautious about including its API in our public API. I know BoxFuture specifically was broken a while ago to add the lifetime parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, that's unfortunate. The type erasure is necessary - there's no way to express for<'a> FnMut(&'a mut ClientSession, &'a mut C) -> impl Future<'a, Result<R>> currently without it. Since BoxFuture is just a type alias, I've duplicated the definition here instead of using the one from the futures_core crate.

@@ -561,6 +566,107 @@ impl ClientSession {
}
}

/// Runs a callback inside a transaction. Transient transaction errors will cause the callback
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a bit more detail here explaining that the transaction will be started and committed/aborted within this method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

/// # Ok(())
/// # }
/// ```
pub async fn with_transaction<R, C, F>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll also need to add this method to the sync API

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Unfortunately, I had to duplicate the body of the function rather than just wrapping the async one to avoid nested block_on calls from within the callback.

@abr-egn abr-egn requested a review from isabelatkinson April 10, 2023 16:51
@abr-egn
Copy link
Contributor Author

abr-egn commented Apr 11, 2023

Note that the evergreen failures are unrelated - testing against AWS auth is broken right now due to https://jira.mongodb.org/browse/BF-28319.

Copy link
Contributor

@isabelatkinson isabelatkinson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@abr-egn abr-egn merged commit c2eb321 into mongodb:main Apr 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants