davidbairdala opened a new issue, #16334:
URL: https://github.com/apache/grails-core/issues/16334
### Expected Behavior
A method annotated `grails.gorm.transactions.NotTransactional` inside a
class annotated `grails.gorm.transactions.Transactional` runs without a
transaction. This is the documented contract (i.e the 8.0.0-M6 API doc for
`@NotTransactional` still reads "Marks a specific method to be skipped by the
Transactional annotation added at class level"), and it is the behaviour on
Grails 7.2.3: `javap` on the compiled service shows no `$tt__` wrapper for the
opted-out method.
### Actual Behaviour
On 8.0.0-M6 the transform wraps the opted-out method anyway. `javap -p`
shows a `$tt__optedOut(...)` wrapper, and
`TransactionSynchronizationManager.isActualTransactionActive()` returns true
inside the method at runtime.
Real-world consequence (production app, 265 domains, 741-test suite tracking
each 8.0 milestone): our CSV import engine's entry point is `@NotTransactional`
because the engine manages per-row `withNewTransaction` boundaries itself.
Under M6 it silently gains an outer transaction; a `Propagation.REQUIRED`
service call inside it (vocabulary get-or-create) joins that transaction
instead of committing, and the per-row transactions - reading through new
sessions - never see the row. A previously-green integration spec fails
deterministically on every row. This codebase has **115 `@NotTransactional`
methods**, all silently transactional under M6, including a message-queue
publisher kept non-transactional precisely so publishes do not ride a
transaction. Data-correctness-grade behaviour change.
Why we believe this is a regression, not a redesign: the M6 API doc states
the opt-out contract unchanged; the Grails 8 upgrade guide documents
intentional semantic breaks in detail (e.g. nullable-by-default) and says
nothing about transaction semantics; and no changelog/PR announces a change,
though the transform machinery churned in the M6 window (#16066
GormRegistry/SessionResolver landed in M6; #16063 on transaction-attribute
state is open; #16212 removed an "unintended @ReadOnly" in the same period).
The opt-out dates to GRAILS-10761 / #3228. The deliberate accessor-shape skip
(#14539) is separate, documented behaviour.
Secondary observation: generated wrapper naming is inconsistent at M6 across
classes in one compilation. Some services get `$tt__<method>(...)`, others
`$tt__<beanPropertyName>_<method>(...)` — possibly hinting at two transform
paths, one of which drops the `@NotTransactional` check.
### Steps To Reproduce
1. In a Grails 8.0.0-M6 app:
```groovy
import grails.gorm.transactions.Transactional
import grails.gorm.transactions.NotTransactional
@Transactional
class DemoService {
def wrapped() { }
@NotTransactional
def optedOut() { }
}
```
2. `./gradlew compileGroovy`
3. `javap -p build/classes/groovy/main/DemoService.class`
4. Observe `$tt__optedOut(...)` exists. Repeat on Grails 7.2.3: it does not.
### Environment Information
Linux, JDK 21 (Temurin 21.0.12), Gradle 8.14. Grails 8.0.0-M6 on the
grails-hibernate5 BOM (Hibernate 5.6.15), Groovy 5.1.0. Verified correct on
Grails 7.2.3 with a freshly compiled class
### Example Application
https://github.com/davidbairdala/grails8-nottransactional-repro
### Version
8.0.0-M6
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]