Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1625,6 +1625,7 @@ public T persist(final T entity) {
final TransactionLegacy txn = TransactionLegacy.currentTxn();
PreparedStatement pstmt = null;
String sql = null;
boolean committed = false;
try {
txn.start();
for (final Pair<String, Attribute[]> pair : _insertSqls) {
Expand Down Expand Up @@ -1673,6 +1674,7 @@ public T persist(final T entity) {
insertElementCollection(entity, _idAttributes.get(_table)[0], id, ecAttributes);
}
txn.commit();
committed = true;
} catch (final SQLException e) {
logger.error("DB Exception on: " + pstmt, e);
handleEntityExistsException(e);
Expand All @@ -1681,6 +1683,10 @@ public T persist(final T entity) {
throw new CloudRuntimeException("Problem with getting the ec attribute ", e);
} catch (IllegalAccessException e) {
throw new CloudRuntimeException("Problem with getting the ec attribute ", e);
} finally {
if (!committed) {
txn.rollback();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be possible to just rollback in the exception handlers instead?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd keep the flag + finally over rollback-in-catch, for two reasons:

  1. finally covers exception types the catch clauses don't. The try block does field reflection and SQL value coercion in a loop, so an unchecked RuntimeException (NPE, ClassCastException, etc.) could skip all three catch clauses but would still hit finally. Rollback-in-catch only protects SQLException/NoSuchFieldException/IllegalAccessException; finally protects the whole try block, which is the actual gap we're closing.

  2. TransactionLegacy.rollback() isn't nesting-aware the way commit() is - it strips every START_TXN marker off the stack and does one physical rollback, so it needs to run exactly once per persist() call regardless of which branch got there. A single finally guarantees that; three (and eventually ~20+ once fix: release START_TXN nesting level on SQLException paths in GenericDaoBase sibling methods #13926 covers the sibling methods) separate call sites don't, and a future added catch clause could reintroduce the leak by omission - the same class of mistake that caused this bug.

Happy to reconsider if there's a case I'm missing, but I think the flag is earning its keep here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ai aided analysis ^^

}
}

return _idField != null ? findByIdIncludingRemoved(id) : null;
Expand Down