-
Notifications
You must be signed in to change notification settings - Fork 39
EF-280: Treat dispose of an active MongoTransaction as rollback #263
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,6 @@ | |
|
|
||
| using System; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.EntityFrameworkCore; | ||
|
|
@@ -91,7 +90,6 @@ internal static MongoTransaction Start( | |
|
|
||
| var transaction = new MongoTransaction(session, context, transactionId, transactionManager, transactionLogger); | ||
| transactionLogger.TransactionStarted(transaction, async, startTime, stopwatch.Elapsed); | ||
|
|
||
| return transaction; | ||
| } | ||
|
|
||
|
|
@@ -234,22 +232,51 @@ public TransactionOptions TransactionOptions | |
| /// <inheritdoc /> | ||
| public void Dispose() | ||
| { | ||
| if (_transactionState == TransactionState.Disposed) return; | ||
| switch (_transactionState) | ||
| { | ||
| case TransactionState.Disposed: | ||
| return; | ||
|
|
||
| case TransactionState.Active: | ||
| Rollback(); | ||
| break; | ||
|
|
||
| case TransactionState.Committed: | ||
| case TransactionState.RolledBack: | ||
| case TransactionState.Failed: | ||
| break; | ||
|
|
||
| default: | ||
| throw new InvalidOperationException($"Can not Dispose MongoTransaction {TransactionId} because it is {_transactionState}."); | ||
| } | ||
|
|
||
| AssertCorrectState("Dispose", TransactionState.Committed, TransactionState.RolledBack, TransactionState.Failed); | ||
| _transactionState = TransactionState.Disposed; | ||
| session.Dispose(); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| public ValueTask DisposeAsync() | ||
| public async ValueTask DisposeAsync() | ||
| { | ||
| if (_transactionState == TransactionState.Disposed) return ValueTask.CompletedTask; | ||
| switch (_transactionState) | ||
| { | ||
| case TransactionState.Disposed: | ||
| return; | ||
|
|
||
| case TransactionState.Active: | ||
| await RollbackAsync(); | ||
| break; | ||
|
|
||
| case TransactionState.Committed: | ||
| case TransactionState.RolledBack: | ||
| case TransactionState.Failed: | ||
| break; | ||
|
|
||
| default: | ||
| throw new InvalidOperationException($"Can not Dispose MongoTransaction {TransactionId} because it is {_transactionState}."); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we throw instead of rolling back for the "Committing" and "RollingBack" states? Also, do we want to throw by default if a new state is introduced? If we're responsible for the lifetime of the transaction, and it is disposed while in anything other than a final state (Failed, Committed, RolledBack), then I think we should attempt to roll back, since the transaction is going away.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Committing and RollingBack are in-progress states. They should never get here. Even if we do I don't think we can roll-back mid-commit and I'm sure we can't roll back mid roll back.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you point me to where these states are defined so I can read up on it?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's an enum at the start of this file. We set it to Committing at the start of the Commit methods, and RollingBack at the start of the RollBack methods. If it's either of these states when disposing it basically has to either be either a bug in our code or a concurrency issue where they are trying to use the non-thread-safe MongoTransaction across multiple threads and one is doing a dispose while the other is performing a commit or rollback.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I spent some more time looking at all the code here, rather than just the PR, and I now understand these are just states we have, not the server or driver. Given that, as you say, this is not thread-safe, then Committing and RollingBack are not observable outside the class for any thread-safe correct code. So, first, why even have them? Second, if we detect that we are in a state that can only be reached by executing in a thread-unsafe manner, then we should explicitly say that in the exception message so that people know this is why the code threw--like what System.Collections.Generic does when you try to modify a collection while it is in use. |
||
| } | ||
|
|
||
| AssertCorrectState("Dispose", TransactionState.Committed, TransactionState.RolledBack, TransactionState.Failed); | ||
| _transactionState = TransactionState.Disposed; | ||
| session.Dispose(); | ||
| return ValueTask.CompletedTask; | ||
| } | ||
|
|
||
| private void AssertCorrectState(string action, TransactionState validState) | ||
|
|
@@ -258,11 +285,4 @@ private void AssertCorrectState(string action, TransactionState validState) | |
| throw new | ||
| InvalidOperationException($"Can not {action} MongoTransaction {TransactionId} because it is {_transactionState}."); | ||
| } | ||
|
|
||
| private void AssertCorrectState(string action, params TransactionState[] validStates) | ||
| { | ||
| if (!validStates.Contains(_transactionState)) | ||
| throw new | ||
| InvalidOperationException($"Can not {action} MongoTransaction {TransactionId} because it is {_transactionState}."); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.