-
Notifications
You must be signed in to change notification settings - Fork 924
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
The transaction is not automatically rolled back on commit timeout #3426
Comments
A commit failure is a wicked case: we cannot easily know if the transaction is still considered ongoing on the DB side or not. So, attempting a rollback could add an additional failure, which may hide the original one and prevent understanding what is the actual issue. Any change here would have to be done carefully. |
Valid points, although maybe attempting rollback would still succeed in some cases, thus reducing the chances of ending up with DB inconsistencies. On the optional rollback, could it be as easy as allowing the commitFailed flag to be manually unset in a catch block? Similar to markRollbackOnly from hibernate - example here. BTW, is there a complete example / unit-test for manually managing the ADO.NET transactions, as partially detailed in the official docs here? How does one obtain/create the ADO.NET transaction object in that case? |
Inconsistencies are not possible unless the database itself is utterly failing in its transaction management. A pending transaction will not finish with any of its parts committed without a successful commit. It can only end-up rollback-ed. This happens automatically with most if not all databases, due to their transaction timeout.
With user supplied connections, the transaction can be manually managed, since the application provides and manages itself the connection. But NHibernate will be unaware of the transaction, which will prevent some features to work as intended. (Flush on commit, second level cache, to name a few.) |
Ah, no, I'm referring to ending up with duplicated data, for example, say when re-running the same insert code, if assuming failure on the first attempt.
Thanks for clarifying on that and pointing out the downsides! |
Then we are back to "wicked case": we cannot assume anything in case of a commit failure. By example, is it a network issue before reaching the DB (so transaction still ongoing), or after, on receiving its response? (So transaction actually commited but the client has not received the acknowledgment.) No way to know, excepted querying the database (after having dropped the faulted connection and session). Attempting a rollback will not rollback anything in the second case, and re-running the insert would then still cause duplicates. Dropping the connection and session (disposing of it, so closing it) would cause the database to rollback any pending transaction. Querying it would then allow to correctly assess the situation. But of course, that is not something that can be generically handled. It would need dedicated code in the application for each case, which would be quite a burden to implement. A more usual pattern is to let the user handle this by telling her/him to check the data after the failure.
That does apply to any NHIbernate interface obtained from a session and bound to it, like the |
Thanks for the detailed reply.
Right, I was afraid that that might be the only reliable way.
Yes, given the "using".
Even without an explicit Rollback call on the ADO transaction object? Because the NHibernate transaction object won't do that, given that the commitFailed flag was set on exception. |
If the connection is actually closed, the DbTransaction is rollbacked. (Note that with transaction scopes, that is another subject.) But it seems that connection pooling may not guard against ongoing transaction when putting back a connection in the pool according to this SO answer, which is unexpected for me. I know the pooling mechanism do some "reset" operation on the connection to ensure that on next get, it is in its default state. That surprises me that it would not handle the possibility of ongoing transactions to rollback them too. The Microsoft documentation looks unhelpful to me in this regard, it only discusses the case of ambient transactions (transaction scopes). According to this blog post, things are "not so bad": the connection can be returned to the pool with an ongoing transaction, but its next use will reset it, causing a transaction rollback. Still its next reuse (or actual closing due to inactivity) can be long to come, which can cause issues. And all this is only valid for SQL Server. NHibernate addresses many other databases, which may have different issues with this subject. |
Thanks again for the detailed reply. |
I'm using the typical exception handling with two using blocks, as detailed here.
Occasionally, I'm getting "Commit failed" during NHibernate.Transaction.AdoTransaction.Commit():
Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding. ---> System.ComponentModel.Win32Exception (0x80004005): The wait operation timed out
And still, the transaction is not automatically rolled back in this case.
Is this by design? In fact, I see that in AdoTransaction, Commit sets the commitFailed flag on exception, and then Rollback bails out if that is true.
To contrast that, I see here that hibernate (Java) supports rollback on timeout:
The text was updated successfully, but these errors were encountered: