Repository navigation
Conversation
When persist() threw after txn.start(), the START_TXN it pushed stayed on the stack, so the caller's later commit() saw an open level and silently skipped the commit (apache#13905). Calling TransactionLegacy.rollback() on failure balances the stack, but it strips every START_TXN and rolls back the caller's whole transaction, which hurts the callers that catch EntityExistsException and carry on. persist() now takes a savepoint when it runs inside a caller's transaction. It releases the savepoint on success and rolls back to it on failure, so only the failed insert is undone and the caller's transaction carries on. When persist() runs on its own it still starts, commits and rolls back its own transaction. setSavepoint() and releaseSavepoint() now update the stack only after the JDBC call succeeds, so a failed SAVEPOINT or RELEASE SAVEPOINT can't leave the stack out of step with the connection. The new test sits in com.cloud.utils because framework/db's surefire configuration excludes the tests in com.cloud.utils.db. Builds on apache#13925 by waterWang.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## 4.20 #14511 +/- ##
============================================
+ Coverage 16.43% 16.47% +0.04%
- Complexity 13676 13795 +119
============================================
Files 5669 5669
Lines 501631 501639 +8
Branches 60939 60943 +4
============================================
+ Hits 82420 82669 +249
+ Misses 409975 409690 -285
- Partials 9236 9280 +44
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR fixes
GenericDaoBase.persist()so that a failed insert only undoes its own work.Today, when the insert throws after
txn.start(), theSTART_TXNthatpersist()pushed stays on the stack. The caller's latercommit()then finds an open level and skips the commit without any error (#13905). #13925 balances the stack by callingTransactionLegacy.rollback()in afinally, butrollback()strips everySTART_TXNand rolls back the caller's whole transaction. That hurts the callers that catchEntityExistsExceptioninside their own transaction and carry on, such asUsageManagerImpl.createHelperRecord().With this change:
persist()runs inside a caller's transaction, it takes a savepoint, releases it on success and rolls back to it on failure. The failed insert is undone and the caller's transaction carries on.persist()runs on its own, it starts, commits and rolls back its own transaction, as fix: release START_TXN nesting level on persist() SQLException path (#13905) #13925 does.TransactionLegacy.setSavepoint()andreleaseSavepoint()now update the stack only after the JDBC call succeeds. Before, a failedSAVEPOINTleft a plainSTART_TXNbehind, and a failedRELEASE SAVEPOINTremoved the entry the rollback needed. Nothing else calls these methods today.This builds on @waterWang's #13925, which had the flag and
finallystructure. #13926 applies the same pattern to the sibling methods (update(),remove(),expunge()and so on), and I'll follow up with those once the approach here is agreed.Fixes: #13905
Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
How Has This Been Tested?
The new unit test
GenericDaoBasePersistTransactionTest(6 tests) runspersist()against a mockedConnectionthroughTransactionLegacy.transitToUserManagedConnection(). It covers a failing and a succeeding insert, both inside a caller's transaction and on their own, plus a failingSAVEPOINTandRELEASE SAVEPOINT.To check the tests catch the problem, I also ran them against:
conn.rollback()is called instead of rolling back to the savepoint.I also ran a full build of 4.20 with this change (
mvn -P developer -Dsimulator install) on Ubuntu 24.04, OpenJDK 11.0.32.1 and Maven 3.8.7. All 9,295 unit tests across 85 modules passed. The only failure was the apidoc packaging step, because the build box had nobzip2.The new test sits in
com.cloud.utilsnext toDbUtilTest, because framework/db's surefire configuration excludescom/cloud/utils/db/*Test*. Most of the tests behind that exclusion fail when run by hand (12 failures and 3 errors on 4.20), which is probably worth its own issue.How did you try to break this feature and the system with this change?
SAVEPOINTandRELEASE SAVEPOINTfail. The stack stays consistent in both cases, and the caller'scommit()still commits.persist()leaves its ownSTART_TXNbehind (for exampleinsertElementCollection()),rollback(savepoint)still unwinds it, becauseremoveTxn()drops everySTART_TXNabove the savepoint.persist()now sendsSAVEPOINTandRELEASE SAVEPOINT. If that's a concern, the savepoint could be limited to inserts that span more than one table or write an element collection, since InnoDB already undoes a single failed statement.