James Daugherty created GROOVY-11841:
----------------------------------------
Summary: Metaclass Concurrency Problem
Key: GROOVY-11841
URL: https://issues.apache.org/jira/browse/GROOVY-11841
Project: Groovy
Issue Type: Bug
Affects Versions: 4.0.0
Reporter: James Daugherty
I'm upgrading a highly concurrent application that uses Spring Batch to
read/write data in parallel. After upgrading to Groovy 4, I'm seeing stack
traces like this:
{code:java}
java.util.ConcurrentModificationException
at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1221)
at groovy.lang.MetaClassImpl.getMetaProperty(MetaClassImpl.java:308)
at
org.grails.datastore.mapping.reflect.ClassPropertyFetcher.getStaticPropertyValuesFromInheritanceHierarchy(ClassPropertyFetcher.java:229)
at
org.grails.datastore.mapping.reflect.ClassPropertyFetcher.getStaticPropertyValuesFromInheritanceHierarchy(ClassPropertyFetcher.java:219)
at
org.grails.datastore.gorm.validation.constraints.eval.DefaultConstraintEvaluator.evaluate(DefaultConstraintEvaluator.java:116)
at
org.grails.datastore.gorm.validation.constraints.eval.DefaultConstraintEvaluator.evaluate(DefaultConstraintEvaluator.java:111)
at
org.grails.datastore.gorm.validation.constraints.eval.DefaultConstraintEvaluator.evaluate(DefaultConstraintEvaluator.java:106)
at
grails.gorm.validation.PersistentEntityValidator.<init>(PersistentEntityValidator.groovy:71)
at
org.grails.datastore.gorm.validation.constraints.registry.DefaultValidatorRegistry.getValidator(DefaultValidatorRegistry.groovy:85)
at
org.grails.datastore.gorm.validation.jakarta.JakartaValidatorRegistry.getValidator(JakartaValidatorRegistry.groovy:127)
at
org.grails.datastore.mapping.model.AbstractMappingContext.getEntityValidator(AbstractMappingContext.java:219)
at
org.grails.datastore.gorm.GormValidationApi.getValidator(GormValidationApi.groovy:82)
at
org.grails.orm.hibernate.AbstractHibernateGormValidationApi.validate(AbstractHibernateGormValidationApi.groovy:61)
at
org.grails.orm.hibernate.AbstractHibernateGormValidationApi.validate(AbstractHibernateGormValidationApi.groovy:55)
at
org.grails.datastore.gorm.GormValidateable$Trait$Helper.validate(GormValidateable.groovy:76)
at com.triu.system.SystemDirectQuery.validate(SystemDirectQuery.groovy)
at
org.grails.orm.hibernate.support.ClosureEventListener.doValidate(ClosureEventListener.java:306)
at
org.grails.orm.hibernate.support.ClosureEventListener$7.call(ClosureEventListener.java:272)
at
org.grails.orm.hibernate.support.ClosureEventListener$7.call(ClosureEventListener.java:261)
at
org.grails.orm.hibernate.support.ClosureEventListener.doWithManualSession(ClosureEventListener.java:385)
at
org.grails.orm.hibernate.support.ClosureEventListener.onPreUpdate(ClosureEventListener.java:261)
at
org.grails.orm.hibernate.event.listener.HibernateEventListener.onPreUpdate(HibernateEventListener.java:158)
{code}
GROOVY-9631 replaced these data structures with LinkedHashmaps. A side effect
of this is that code that previously executed concurrently without issue seems
to be no longer thread safe.
[https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/LinkedHashMap.html]
has this warning too:
{code:java}
Note that this implementation is not synchronized.
If multiple threads access a linked hash map concurrently, and at least
one of the threads modifies the map structurally, it must be
synchronized externally. This is typically accomplished by
synchronizing on some object that naturally encapsulates the map.
If no such object exists, the map should be "wrapped" using the
Collections.synchronizedMap
method. This is best done at creation time, to prevent accidental
unsynchronized access to the map:{code}
It makes sense to lock down writes to maps, but since the read is non-thread
safe it seems this could still be problematic.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)