rodrigolimaivt opened a new issue, #16558:
URL: https://github.com/apache/grails-core/issues/16558

   ### Expected Behavior
   
   Binding a map onto a domain instance (`new Book(title: 'x', pages: 1)`, 
`bindData`, `obj.properties = params`) should cost about what it cost on Grails 
7.x. The list of `bindable: false` properties of a domain class depends only on 
the class, so it should be computed once per class.
   
   ### Actual Behaviour
   
   On 8.0.0 every bind onto a domain instance recomputes that list from 
scratch. The work goes through:
   
   - `GrailsWebDataBinder.doBindInternal`, which calls 
`DataBindingUtils.addUnbindablePropertyNames(object, blackList)`;
   - `DataBindingUtils.getUnbindablePropertyNames(Object)`. For a domain class 
`BindingIncludeLists.inheritsConstraintsMap` is false, so it takes the instance 
path, which by design is not cached ("may differ between instances of the same 
class, so do not cache by Class here");
   - `getConstrainedProperties(object)`:
     - `constraintsMap` throws `MissingPropertyException`;
     - `getConstraintsMap()` throws `MissingMethodException`;
     - `constraints` returns the `static constraints` **Closure**, not a Map;
     - so it falls through to 
`ValidationSupport.getConstrainedPropertiesForClass`, which evaluates the 
constraints closure again on every bind;
   - then `BindingIncludeLists.propertyNamesWithBindableValue`, which makes a 
reflective `bindableConstraintValue` call per constrained property.
   
   The class-level cache `CLASS_TO_UNBINDABLE_PROPERTY_NAMES` only applies to 
the `inheritsConstraintsMap` case, and even then only when reload is disabled. 
So domain classes never hit a cache, in production either.
   
   Measured on the same app and database. The code was run 20,000 times in the 
web console, in a `bootRun` app; times are µs per call. The domain class has 24 
constrained properties, 2 of them `bindable: false`.
   
   | | 7.2.x | 8.0.0 |
   |---|---|---|
   | `new Domain(a: 1.0, b: 1.0, c: 1.0, d: 1.0)` | 71 | **563** |
   | `new Domain()` + 4 setters | 4 | 12 |
   | `DataBindingUtils.getConstrainedProperties(instance)` | – | 269 |
   | `BindingIncludeLists.propertyNamesWithBindableValue(map, false)` with the 
map already built | – | 155 |
   
   Real impact: pages that build domain objects from query rows got 20–55% 
slower. The same goes for a domain whose field initializer is `Other other = 
new Other(value: ...)`, because it runs for every instance and every Hibernate 
proxy.
   
   After caching the result per domain class in 
`getUnbindablePropertyNames(Object)` (key: `object.getClass()` when it is a 
`GormEntity`), the map constructor went from 563 µs to 122 µs, and the affected 
pages are back near 7.x times. The rest of the gap is in 
`GrailsWebDataBinder.getNestedBindingIncludeList`, which also runs per property.
   
   ### Steps To Reproduce
   
   1. Create a Grails 8.0.0 app with a domain class that has about 20 
properties and a `static constraints` block, with at least one `bindable: 
false`.
   2. Time `20000.times { new Book(title: 'x', pages: 1, price: 1.0, isbn: 'y') 
}`, for example in a service or in the console, on 8.0.0 and on 7.x.
   3. Take a few `jstack` samples while it runs. You will see 
`DataBindingUtils.getUnbindablePropertyNames` → 
`BindingIncludeLists.evaluateConstrainedProperties` → 
`Book$__clinit__closure1.doCall` (the constraints closure), plus 
`MissingPropertyException` / `MissingMethodException` being built.
   
   ### Environment Information
   
   - Grails 8.0.0, GORM 8.0.0 (Hibernate 5), Groovy 5.1.3, Spring Boot 4.1.1
   - Java 25, macOS
   - Measured in `bootRun`. Looking at the code, production takes the same 
path, because domain instances never use the class cache.
   
   ### Example Application
   
   _No response_
   
   ### Version
   
   8.0.0
   


-- 
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