[
https://issues.apache.org/jira/browse/CAY-2988?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Andrus Adamchik updated CAY-2988:
---------------------------------
Summary: Iterated queries commit transactions they don't own (was: Nested
iterated queries transaction handling)
> Iterated queries commit transactions they don't own
> ---------------------------------------------------
>
> Key: CAY-2988
> URL: https://issues.apache.org/jira/browse/CAY-2988
> Project: Cayenne
> Issue Type: Bug
> Reporter: Andrus Adamchik
> Assignee: Andrus Adamchik
> Priority: Major
> Fix For: 5.0-M3
>
>
> Looks like M2 has a regression vs 4.2.x in transaction handling in result
> iterators. Since 5.0, an iterated query that joins a transaction it did not
> create produces an iterator that commits the transaction owned by someone
> else. Two ways this breaks:
> 1. Single iterator in a caller-managed transaction. Opening and closing an
> iterator inside TransactionManager.performInTransaction(...) commits the
> caller's transaction early while the iterator is still open, and the caller's
> own commit fails with IllegalStateException: Transaction must have
> 'STATUS_ACTIVE' to be committed. Current status: STATUS_COMMITTED.
> 2. Nested iterators. When a second iterator is opened and closed while a
> first is still streaming, closing the inner one commits the shared
> transaction and returns its connections to the pool. The outer iterator then
> fails mid-read with SQLException: Operation not allowed after ResultSet
> closed, and its own close throws STATUS_COMMITTED. This is what LinkMove's
> DeleteTask does — it reads source keys through a nested iterator while the
> target iterator is
> open — so any LinkMove delete task running inside an application-managed
> transaction fails.
> In both cases connections are returned to the pool while still in use, so
> unrelated later operations can fail too. E.g.:
> {noformat}
> org.apache.cayenne.CayenneRuntimeException: [v.5.0-SNAPSHOT Jul 18 2026
> 19:33:50] java.sql.SQLNonTransientConnectionException: connection exception:
> connection does not exist
> at
> org.apache.cayenne.tx.CayenneTransaction.processRollback(CayenneTransaction.java:140)
> at
> org.apache.cayenne.tx.BaseTransaction.rollback(BaseTransaction.java:183)
> at
> org.apache.cayenne.tx.DefaultTransactionManager$BaseTransactionHandler.performInNewTransaction(DefaultTransactionManager.java:171)
> at
> org.apache.cayenne.tx.DefaultTransactionManager$NestedTransactionHandler.handle(DefaultTransactionManager.java:101)
> at
> org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:62)
> at
> org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:41)
> at
> org.apache.cayenne.access.DataContextIteratedQueryIT.iterator_NestedIteratorInCallerTransaction(DataContextIteratedQueryIT.java:262)
> ...
> Caused by: java.sql.SQLNonTransientConnectionException: connection exception:
> connection does not exist
> at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
> at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
> at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
> at org.hsqldb.jdbc.JDBCUtil.connectionClosedException(Unknown Source)
> at org.hsqldb.jdbc.JDBCConnection.checkClosed(Unknown Source)
> at org.hsqldb.jdbc.JDBCConnection.rollback(Unknown Source)
> at
> org.apache.cayenne.datasource.PoolAwareConnection.rollback(PoolAwareConnection.java:278)
> at
> org.apache.cayenne.tx.TransactionConnectionDecorator.rollback(TransactionConnectionDecorator.java:200)
> at
> org.apache.cayenne.tx.CayenneTransaction.processRollback(CayenneTransaction.java:130)
> ... 80 more
> Caused by: org.hsqldb.HsqlException: connection exception: connection does
> not exist
> at org.hsqldb.error.Error.error(Unknown Source)
> at org.hsqldb.error.Error.error(Unknown Source)
> ... 87 more
> {noformat}
> h2. ...
>
--
This message was sent by Atlassian Jira
(v8.20.10#820010)