[ 
https://issues.apache.org/jira/browse/TOMEE-4652?focusedWorklogId=1031926&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1031926
 ]

ASF GitHub Bot logged work on TOMEE-4652:
-----------------------------------------

                Author: ASF GitHub Bot
            Created on: 23/Jul/26 19:06
            Start Date: 23/Jul/26 19:06
    Worklog Time Spent: 10m 
      Work Description: jungm opened a new pull request, #2849:
URL: https://github.com/apache/tomee/pull/2849

   ## TOMEE-4652
   
   A servlet or JSP that leaves a bean-managed `UserTransaction` incomplete 
leaks that transaction to the **next request served on the same pooled Tomcat 
exec thread**. The victim request then sees a bogus transaction status — either 
missing an expected `IllegalStateException` or getting a 
`NotSupportedException: Nested Transactions are not supported` on its own 
`begin()`. Which tests fail depends on which request lands on which thread, 
which is why the Transactions 2.0 TCK web vehicles (servlet + JSP) fail 
non-deterministically.
   
   ### Root cause
   
   Geronimo's `TransactionManagerImpl` keeps the thread-to-transaction 
association (and the per-thread transaction timeout) in `ThreadLocal`s that are 
only cleared by `commit()` / `rollback()`. EJBs are wrapped by container 
interceptors that restore the thread state at the end of the call; **plain 
servlets have no equivalent**, and `OpenEJBValve`'s request-teardown `finally` 
block cleaned up only the security context. Since Tomcat pools its worker 
threads, the association survives into the next request.
   
   ### Fix
   
   - **`TransactionCleanup`** (new) — rolls back and unassociates any 
transaction still active on the thread at request end, and resets the 
per-thread transaction timeout (which leaks the same way, since Geronimo only 
clears it inside `begin()`). If the rollback itself fails it falls back to 
`suspend()` so the association never survives the request.
   - Invoked from the request-teardown `finally` in **`OpenEJBValve`** (sync 
path) and **`OpenEJBSecurityListener.asyncExit()`** (async 
complete/error/timeout).
   - **`CoreUserTransaction.resetError(null)`** now `remove()`s the `ERROR` 
ThreadLocal instead of `set(null)`, so pooled threads don't keep an empty entry 
pinned. Separate hygiene issue, not the TCK cause.
   
   ### Testing
   
   `UserTransactionLeakTest` forces two sequential requests onto a single exec 
thread (`maxThreads=1`) and asserts both actually shared the thread (so it 
can't pass vacuously), that the second request sees `STATUS_NO_TRANSACTION`, 
and that it can still run a transaction of its own.
   
   Verified red/green: with the cleanup call removed the test fails with 
`expected:<[STATUS_NO_TRANSACTION]> but was:<[leaked status 0]>` and a 
follow-up `NotSupportedException: Nested Transactions are not supported` — 
matching the issue exactly; with the fix it passes. `tomee-catalina` and 
`tomee-embedded` suites are green.
   
   ### Notes for reviewers
   
   - The full Jakarta Transactions 2.0 TCK was not run here. To confirm end to 
end, remove the three excluded areas from 
`runner-standalone/exclusions/transactions.txt` in the `apache/tomee-tck` 
harness and rerun the 49-test baseline.
   - Pre-existing failures unrelated to this change exist on `main` in 
`StatefulBeanManagedTest`, `InterfaceTransactionTest`, and 
`TransactionPropagationTest` (confirmed identical on a clean checkout).
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)




Issue Time Tracking
-------------------

            Worklog Id:     (was: 1031926)
    Remaining Estimate: 0h
            Time Spent: 10m

> UserTransaction state leaks across pooled Tomcat threads between requests
> -------------------------------------------------------------------------
>
>                 Key: TOMEE-4652
>                 URL: https://issues.apache.org/jira/browse/TOMEE-4652
>             Project: TomEE
>          Issue Type: Bug
>          Components: TomEE Core Server
>            Reporter: Markus Jung
>            Assignee: Markus Jung
>            Priority: Major
>          Time Spent: 10m
>  Remaining Estimate: 0h
>
> When a servlet or JSP request leaves a {{UserTransaction}} in a non-clean 
> state, the next request served on the same pooled Tomcat exec thread inherits 
> that state. The victim request then either misses an expected 
> {{IllegalStateException}} or gets an exception it does not expect. This is a 
> leaker/victim pair: the same test fails in one vehicle and passes in the 
> other, and which tests fail depends on which request lands on which thread.
> The Transactions 2.0 TCK web vehicles show this directly. At the full 
> baseline (no exclusions), 49 tests run, 40 pass, 9 fail. All three 
> signature-test vehicles pass, so the fault sits in {{UserTransaction}} 
> handling, not in transaction propagation itself. The first failures in test 
> order sit in the rollback area, before any {{setTransactionTimeout}} call 
> runs, which rules out a timeout-related cause for those failures.
> Run alone on a fresh server, each area behaves correctly on its own: the 
> rollback area passes 10 of 10, {{settransactiontimeout}} passes 4 of 4, and 
> {{setrollbackonly}} passes 7 of 8 (its one failure, the last request in that 
> area, is a victim of its own earlier request, not a new bug). There is no gap 
> around commit-after-timeout: {{settransactiontimeout001}} sleeps 30 seconds 
> before calling {{commit()}}, and when it reaches that call in isolation, 
> {{commit()}} throws as required.
> Because a failing request poisons whichever request follows it on the same 
> thread, excluding only the ids that fail at baseline just moves the failure 
> onto different tests (a 9-id exclusion list leaves 4 different tests 
> failing). All three areas are excluded whole in the harness so the default 
> run stays stable and green.
> h2. Steps to reproduce / TCK reference
> Run the Jakarta Transactions 2.0 TCK web vehicles (servlet and JSP) against 
> TomEE 11 without exclusions. Affected test classes and methods, currently 
> excluded in {{runner-standalone/exclusions/transactions.txt}} in the 
> apache/tomee-tck harness repo:
> * 
> {{com/sun/ts/tests/jta/ee/usertransaction/rollback/UserRollbackClient.java}} 
> — {{testUserRollback001}} through {{testUserRollback005}}, each {{_from_jsp}} 
> and {{_from_servlet}}
> * 
> {{com/sun/ts/tests/jta/ee/usertransaction/setrollbackonly/UserSetRollbackOnlyClient.java}}
>  — {{testUserSetRollbackOnly001}} through {{testUserSetRollbackOnly004}}, 
> each {{_from_jsp}} and {{_from_servlet}}
> * 
> {{com/sun/ts/tests/jta/ee/usertransaction/settransactiontimeout/UserSetTransactionTimeoutClient.java}}
>  — {{testUserSetTransactionTimeout001}} and 
> {{testUserSetTransactionTimeout002}}, each {{_from_jsp}} and {{_from_servlet}}
> To confirm the fix, remove these three areas from {{transactions.txt}} and 
> rerun the full baseline; all 49 tests should pass regardless of 
> thread-to-request assignment.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to