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]

Reply via email to