-
Notifications
You must be signed in to change notification settings - Fork 121
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
Transactions #28
Comments
So let's write the possibilities here: Calling methodThe transaction API could work in two ways. First is to return a transaction object: let mut tx = client.start_transaction().await?
tx.query(..).await?; This is quite straightforward and easier to abstract. Has some problems with transaction lifetimes we'll come back to later. The second would be a closure-based approach: client.with_transaction(|tx| async move {
tx.query(..).await
}).await?; This makes it easier to manage the transaction lifetime, but the API is harder for situations, where one wants to store the transaction somewhere or create an abstraction over the way transactions are created (nobody else uses this approach). Transaction APIThe It should not be possible to use a Nesting should be possible by calling Transaction lifetimeIn a perfect world the user always calls either Traditionally in synchronous Rust it has been quite easy to just implement With asynchronous Rust, there needs to be a polling
Of course having an async destructor would make all of this much easier. Boats wrote a good article about the issue: https://boats.gitlab.io/blog/post/poll-drop/ |
I think diesel does. Of course it's a different story with async APIs, since async closures aren't a thing yet. |
This pull request enables the basis for spawning transactions: #55 On top of that change it should be possible to build a higher level transaction API. |
Any progress on this? |
Is there an actual need for transaction API in Tiberius? In the current state we can't guarantee an automatic It's not that tricky to make it work by yourself: client.simple_query("BEGIN TRAN").await?;
/// deal with your business, do not panic or error out
match result {
Ok(_) => client.simple_query("COMMIT").await?,
Err(_) => client.simple_query("ROLLBACK").await?,
} Now adding higher level functionality on top of what we have now in Rust, especially automatic rollbacks is kind of cumbersome and if we'd not guarantee on doing a I'm ready for suggestions how this could be done now in Tiberius, and I'm also happy if some higher level crate (such as Diesel) would implement SQL Server support with Tiberius, and would also solve the For now, yes transactions work and yes, you need to structure your code a bit to make them safe. |
Correct me if I'm wrong, but won't a transaction get a rollback if the connection is closed? If so couldn't I just use |
Yes. But typically you want to reuse the connection between requests, so you either hold it in a mutex, or use a connection pool. Not triggering a |
How about this ?
|
This example is quite nice for the throughput due to not opening any long-running transactions. The downside is you must write your logic in T-SQL, losing some of the compile-time checks from Rust. But, throughput-wise I'd prefer this approach for sure... |
@esheppa has experimental branch for long-running transaction api in this pull request. Please take a look if interested and maybe get the ball rolling again, if you'd think the api works for you. |
We need a higher-level system for managing transactions. It is possible to trigger transactions with the normal
BEGIN/COMMIT/ROLLBACK
mechanism, but an abstraction that handles proper cleanup, committing, rolling back and so on, and offers the same query mechanisms as theClient
would be useful.The transaction should be started from the
Client
. When started, Rust's ownership system should prevent the usage ofClient
until theTransaction
is dropped. TheTransaction
should provide at least the basics:execute
andquery
, and additionallyrollback
andcommit
methods.When the
Transaction
is dropped, it should know did the user finish it properly using therollback
orcommit
method, and if not, message the client to trigger a rollback before allowing more queries to be handled.The last part can be done in many ways, and the tricky thing here is if wanting to set the
ROLLBACK
query in theDrop
trait, which is not by design asynchronous. Transaction should have a mutable reference to theClient
, so one way of doing this would be to set a flag in theClient
about a dirty state, and the client (orConnection
) takes care of triggering theROLLBACK
call before executing the next query.The text was updated successfully, but these errors were encountered: