Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

8.0.0: data binding onto a domain instance re-evaluates static constraints on every bind (~8x slower map constructor than 7.x)

Aperta
#16,558 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

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

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

Lingua principale
Groovy
Stelle
2.9k
Fork
978
Merge medio
1g 2h
PR unite (30g)
183

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di apache/grails-core

Tutte le issue di apache/grails-core

Issue simili

Altre issue su Backend & API Design

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.