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
Lost update after successfull commit #1697
Comments
in order for this to work as you expect you need to turn autocommit off. |
Autocommit is already set to off.
gives false. |
send me a small example that fails. |
output
|
Yes, this exactly reproduces the problematic behavior. Since Therefore, accepted behavior would be either the final commit to fail (which seems to be the PostgreSQL way) or the final commit to succeed and all successful inserts to be persisted (which would be the Oracle way). |
well I get what you are trying to say however here are the javadocs: void commit() Note the function returns void so not much we can do there and as per the docs we can only throw an exception under two cases. |
Does setting autosave to always produce the behavior you want?
…On Wed, Feb 5, 2020 at 12:19 PM Dave Cramer ***@***.***> wrote:
well I get what you are trying to say however here are the javadocs:
void commit()
throws SQLException
Makes all changes made since the previous commit/rollback permanent and
releases any database locks currently held by this Connection object. This
method should be used only when auto-commit mode has been disabled.
Throws:
*SQLException - if a database access error occurs, this method is called
while participating in a distributed transaction, if this method is called
on a closed connection or this Connection object is in auto-commit mode*
Note the function returns void so not much we can do there and as per the
docs we can only throw an exception under two cases.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#1697?email_source=notifications&email_token=AAW3U3M63LB5MZYKMEJKYXDRBL7KJA5CNFSM4KQOG56KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEK4OJ3A#issuecomment-582542572>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAW3U3ORB4IGGVWO4VK3UCDRBL7KJANCNFSM4KQOG56A>
.
|
also for more information:
The commit executes with no issues. |
Don't understand that. If I do the following:
The commit() fails with the message that the transaction has partially rolled back. The exactly same result I expect regardless of the order of the two updates (the failing and the succeeding one). And no, auto-commit is not an option since a all-or-nothing semantics is required. Suppose the slightly modified code:
You would expect that either all changes from the three steps are persistet, or nothing, right? That's what the ACID properties of a database should guarantee. This must hold even if one of the steps fail. And that's what all other RDBMs I've seen so far do - including MySQL, Oracle, DB2, MSSQL, H2, Derby. With PostgreSQL however, it depends on the order of the failing and succeeding steps. |
Well, pretty sure once the transaction has failed nothing will be committed. In fact I'll give you long odds on it, unless you set autosave. In the second example you get all or nothing, again presuming autosave has not been set. My guess is the first example the commit is not failing but the second executeUpdate. |
@haumacher, setting autosave to always results in behavior basically same as ojdbc. @davecramer, I think a plausible reading of the commit javadoc javadoc:
This could plausibly be interpreted that a "database access error" has occurred because not all changes since the previous commit/rollback are being made permanent. To make sure I am understanding what is going on under the covers, in this scenario is the database actually rolling back to the previous savepoint (in this case the transaction start?). |
commit doesn't actually fail and there is no access error so AFAICT we are doing the right thing here. |
@davecramer I am not saying that current behavior is definitively wrong, but it is surprising to those coming from other jdbc driver implementations like ojdbc. It seems that postgres basically rollsback on error. In the case of an open transaction with no save points, this involves rolling back to the beginning of the transaction. This does not cleanly align to the jdbc api. A bit of this is thinking out loud, but I wonder if failing any call to commit after this type of failure in this scenario would be less surprising. |
I'm torn between least surprising and looking less like postgres |
found this https://crate.io/docs/sql-99/en/latest/chapters/36.html#commit-statement
while it may be surprising it is the SQL spec. |
Using the snippet above
If I execute now the commit then it succeeds and i expect due to https://crate.io/docs/sql-99/en/latest/chapters/36.html#commit-statement
the successfull changes, here |
So let me understand this. You would expect Thoughts on this statement from the same page. "An SQL-transaction (transaction) is a sequence of executions of SQL-statements that is atomic with respect to recovery. That is to say: either the execution result is completely successful, or it has no effect on any SQL-schemas or SQL-data.” – The SQL Standard |
If
Yes. Another plausible option is that the But none of the above mentioned options occurs. Instead, PostgreSQL uses a mix of the two. To make it clear, dependent on the result of Based on your argument, one might rather wonder why the |
Pretty sure nothing is committed. The example I provided above does not commit anything. |
Exactly, but the commit
i expect that changes to the database are made which is wrong. |
ah, ok. The problem is that the JDBC API specifies that an exception is only thrown in a few cases as I eluded to above: SQLException - if a database access error occurs, this method is called while participating in a distributed transaction, if this method is called on a closed connection or this Connection object is in auto-commit mode None of the above has occurred. |
@tl-sfo, set the autosave property to always. In the scenario you described, both @davecramer, as I stated earlier, I think a liberal reading of the spec would allow for a SQLException to be thrown in the case that |
@bokken I'd be OK if we had a property which changed this behaviour. Given that the driver has had this behaviour for 20 odd years, changing it now would likely break a few apps :) |
@bokken Does setting the |
Yes, a savepoint is created for each transaction. |
@tl-sfo, my observation is that while there is work associated with the save points, the observed time impact is negligible. |
Let me explain the background of our request in a little bit more detail. We are not facing a concrete single problem, where we could easily add an explicit savepoint to fix it as described above. Upon request from a large customer, we are currently adding postgresql support to the database abstraction layout of our app framework Top-Logic. Our hope was to support postgresql "without any restriction" - not imposing any constraints on the connection settings. Unfortunately, this seems not to be possible due to the issue described here. Let me show: The central transaction handler of the the framework in principal looks like this:
The assumption behind the code above is that if If we announce "postgresql support" for our framework, we not really like to add a side-note that one has to configure the connection to autosafe mode "for safety reasons". If the user fails to do that configuration, things might work perfectly well for the next two years, until this buggy user-specific exception handler is triggered causing the app to crash and waiting for a reboot. @bokken's suggestion "Once a failure occurs with no savepoint, the connection enters a "rollback only" mode. Any use of the connection other than rollback would result in an exception." would be the ideal solution. Of cause, changing anything has the potential to break something - but in this situation, I don't think that the impact would be broadly noticable. The suggested change only affects the response to a situation where the application has encountered an error anyway (invalid exception handling without safepoints). The change only affects the response to that error (reporting it instead of simply ignoring it). |
So I'm looking at this code and trying to figure out why you always rollback the connection. Luckily no other driver throws an exception for that. That said are you catching the SQLException thrown by the bad statement in makeChangesToObjectsPersistent? If not then this should just work as is. |
As said, we are developing a framework. Not I'm catching this exception, but some application specific code possibly could catch the What should be the reason for throwing an exception from |
OK, So I get your conundrum however as a driver we write to the spec, so please understand mine. Here's the javadoc for when commit throws an exception:
Here's the javadoc for when rollback throws an exception
Note: they are identical. Based on your logic rollback() should also throw an exception in the case that the transaction failed. Have a read https://debezium.io/blog/2018/12/05/automating-cache-invalidation-with-change-data-capture/ for a different way to handle caches. Now it appears that others have interpreted the spec differently to suit themselves. I would still be willing to accept a PR that threw an exception in the case of the commit failing but this can't be the default behaviour. It would have to be a property provided on the connection URL or in the properties of the connection. |
I've implemented a workaround in #1729, reviews are welcome. |
…mit command Hopefully, server would fix the error in the future. See discussion in pgsql-hackers: https://www.postgresql.org/message-id/b9fb50dc-0f6e-15fb-6555-8ddb86f4aa71%40postgresfriends.org fixes pgjdbc#1697
Cool, thanks for the fix! What do you think is the best way for an application to determine the PostgreSQL driver version (to check that the fix is applied and no auto-safe handling must be enforced)? I think of a configuration, where the database driver is configured in the web server and made available to the application through JNDI. |
The API has getMajorVersion and getMinorVersion. Seems like that should be enough |
…mit command (#1729) The feature is enabled by default. It can be disabled by setting raiseExceptionOnSilentRollback=false connection property. See discussion in pgsql-hackers: https://www.postgresql.org/message-id/b9fb50dc-0f6e-15fb-6555-8ddb86f4aa71%40postgresfriends.org fixes #1697
I'm submitting a ...
Describe the issue
At the beginning we define a table with a not null column of type integer. Afterwards we insert a valid integer and then null which is invalid. At the end a commit on the given connection is executed und successfull, but the valid insert is lost.
If we change the order of the insertion i.e. execute the invalid insertion first, then the commit fails, which is expected.
Driver Version?
42.2.9
Java Version?
jdk1.8.0_172
OS Version?
Windows 10 Pro
PostgreSQL Version?
12
To Reproduce
Expected behaviour
Commit should fail.
The text was updated successfully, but these errors were encountered: