8.0.0: data binding onto a domain instance re-evaluates static constraints on every bind (~8x slower map constructor than 7.x)
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 48/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- groovy
- Ambito
- backend, performance
Direzione di ricerca
Start by reading GrailsWebDataBinder.doBindInternal and DataBindingUtils.getUnbindablePropertyNames(Object), then trace how domain instances reach ValidationSupport.getConstrainedPropertiesForClass and BindingIncludeLists.propertyNamesWithBindableValue. The report identifies repeated constraint evaluation and reflective calls as the bottleneck; verify the behavior with the provided map-construction timing and stack-sample steps. Done means domain-instance binding no longer repeatedly evaluates static constraints and the measured slowdown is addressed without changing binding behavior.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
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 callsDataBindingUtils.addUnbindablePropertyNames(object, blackList);DataBindingUtils.getUnbindablePropertyNames(Object). For a domain classBindingIncludeLists.inheritsConstraintsMapis 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):constraintsMapthrowsMissingPropertyException;getConstraintsMap()throwsMissingMethodException;constraintsreturns thestatic constraintsClosure, 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 reflectivebindableConstraintValuecall 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
- Create a Grails 8.0.0 app with a domain class that has about 20 properties and a
static constraintsblock, with at least onebindable: false. - 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. - Take a few
jstacksamples while it runs. You will seeDataBindingUtils.getUnbindablePropertyNames→BindingIncludeLists.evaluateConstrainedProperties→Book$__clinit__closure1.doCall(the constraints closure), plusMissingPropertyException/MissingMethodExceptionbeing 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
- Lingua principale
- Groovy
- Stelle
- 2.9k
- Fork
- 978
- Merge medio
- 1g 2h
- PR unite (30g)
- 183
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di apache/grails-core
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
apache/grails-core#16635 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
apache/grails-core#16289 · 5 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
apache/grails-core#15764 ·
I maintainer di solito rispondono entro 1 giorno
-
Update documentation for Java 25Forse già presa @ruthst00 l’ha presa 10 giorni fa. Apertarelates-to: documentation weekly-action-item
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
apache/grails-core#15725 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 40/100
apache/grails-core#16660 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di apache/grails-core
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
-
run_all sleeps after the final job despite documenting delays between jobsForse già presa @Gambit-Checkmate l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
-
bnv-manager-v2: accept_invite creates the Mailcow domain admin before the commitForse già presa @myOmikron l’ha presa oggi. Apertabug ready-for-agent
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
-
BUG
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
Mai-with-u/MaiBot#2187 ·
I maintainer di solito rispondono entro 1 giorno
-
AG-UI bug pkg:core
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
pydantic/pydantic-ai#10538 ·
I maintainer di solito rispondono entro 1 giorno