This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a commit to branch feat/gorm-registry-neo4j
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit a5b4e3ce3cf24cbac74d3bd6902739a348782633
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Sat Jun 27 11:28:41 2026 -0500

    feat: wire Neo4j adapter to GormRegistry O(M+N) scaling
    
    Register Neo4j GORM APIs with GormRegistry in Neo4jGormEnhancer and update
    affected Neo4j tests and TCK specs to use the registry-based API path.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---
 grails-data-neo4j/build.gradle                     |  30 +++---
 .../main/groovy/grails/neo4j/Neo4jEntity.groovy    |  30 +++---
 .../src/main/groovy/grails/neo4j/Node.groovy       |  17 ++--
 .../main/groovy/grails/neo4j/Relationship.groovy   |   4 +-
 .../datastore/gorm/neo4j/Neo4jDatastore.java       |   1 +
 .../gorm/neo4j/api/Neo4jGormStaticApi.groovy       |   8 +-
 .../gorm/neo4j/collection/Neo4jPath.groovy         |   6 +-
 .../groovy/grails/gorm/tests/ValidationSpec.groovy |   4 +-
 .../neo4j/Neo4jTenantContextProfilingSpec.groovy   | 113 +++++++++++++++++++++
 9 files changed, 166 insertions(+), 47 deletions(-)

diff --git a/grails-data-neo4j/build.gradle b/grails-data-neo4j/build.gradle
index 7ad741d1a6..b0c0b8d6bf 100644
--- a/grails-data-neo4j/build.gradle
+++ b/grails-data-neo4j/build.gradle
@@ -1,20 +1,18 @@
 /*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *  
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
  */
 
 buildscript {
diff --git 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Neo4jEntity.groovy
 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Neo4jEntity.groovy
index 532cedb594..f727aeafd5 100644
--- 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Neo4jEntity.groovy
+++ 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Neo4jEntity.groovy
@@ -22,8 +22,8 @@ import grails.gorm.MultiTenant
 import grails.gorm.api.GormAllOperations
 import grails.gorm.multitenancy.Tenants
 import groovy.transform.CompileStatic
-import org.grails.datastore.gorm.GormEnhancer
 import org.grails.datastore.gorm.GormEntity
+import org.grails.datastore.gorm.GormRegistry
 import org.grails.datastore.gorm.GormStaticApi
 import org.grails.datastore.gorm.neo4j.GraphPersistentEntity
 import org.grails.datastore.gorm.neo4j.Neo4jDatastore
@@ -78,7 +78,7 @@ trait Neo4jEntity<D> implements GormEntity<D>, 
DynamicAttributes {
     def getAt(String name) {
         def val = DynamicAttributes.super.getAt(name)
         if(val == null) {
-            GormStaticApi staticApi = GormEnhancer.findStaticApi(getClass())
+            GormStaticApi staticApi = 
GormRegistry.instance.findStaticApi(getClass())
             GraphPersistentEntity entity = (GraphPersistentEntity) 
staticApi.gormPersistentEntity
             if(entity.hasDynamicAssociations()) {
                 def id = ident()
@@ -101,7 +101,7 @@ trait Neo4jEntity<D> implements GormEntity<D>, 
DynamicAttributes {
      * @return The statement result
      */
     Result cypher(CharSequence cypher, Map params) {
-        GormEnhancer.findDatastore(getClass()).withSession { Neo4jSession 
session ->
+        
GormRegistry.instance.apiResolver.findDatastore(getClass()).withSession { 
Neo4jSession session ->
             QueryRunner boltSession = getStatementRunner(session)
 
             String queryString
@@ -125,7 +125,7 @@ trait Neo4jEntity<D> implements GormEntity<D>, 
DynamicAttributes {
      * @return The statement result
      */
     Result cypher(String cypher, List params) {
-        GormEnhancer.findDatastore(getClass()).withSession { Neo4jSession 
session ->
+        
GormRegistry.instance.apiResolver.findDatastore(getClass()).withSession { 
Neo4jSession session ->
             QueryRunner boltSession = getStatementRunner(session)
 
             Map<String, Object> paramsMap = new LinkedHashMap()
@@ -148,7 +148,7 @@ trait Neo4jEntity<D> implements GormEntity<D>, 
DynamicAttributes {
      * @return
      */
     Result cypher(String queryString) {
-        GormEnhancer.findDatastore(getClass()).withSession { Neo4jSession 
session ->
+        
GormRegistry.instance.apiResolver.findDatastore(getClass()).withSession { 
Neo4jSession session ->
             Map<String, Object> arguments
             if (session.getDatastore().multiTenancyMode == 
MultiTenancySettings.MultiTenancyMode.DISCRIMINATOR) {
                 if (!queryString.contains("\$tenantId")) {
@@ -175,7 +175,7 @@ trait Neo4jEntity<D> implements GormEntity<D>, 
DynamicAttributes {
      */
     @Deprecated
     static Result cypherStatic(CharSequence queryString, Map params) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).cypherStatic(queryString, params)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).cypherStatic(queryString, params)
     }
 
     /**
@@ -187,7 +187,7 @@ trait Neo4jEntity<D> implements GormEntity<D>, 
DynamicAttributes {
      */
     @Deprecated
     static Result cypherStatic(CharSequence queryString, List params) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).cypherStatic(queryString, params)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).cypherStatic(queryString, params)
     }
 
     /**
@@ -199,7 +199,7 @@ trait Neo4jEntity<D> implements GormEntity<D>, 
DynamicAttributes {
      */
     @Deprecated
     static Result cypherStatic(CharSequence queryString) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).cypherStatic(queryString)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).cypherStatic(queryString)
     }
 
     /**
@@ -209,7 +209,7 @@ trait Neo4jEntity<D> implements GormEntity<D>, 
DynamicAttributes {
      * @return The statement result
      */
     static Result executeCypher(CharSequence queryString, Map params) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).cypherStatic(queryString, params)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).cypherStatic(queryString, params)
     }
 
     /**
@@ -219,33 +219,33 @@ trait Neo4jEntity<D> implements GormEntity<D>, 
DynamicAttributes {
      * @return The statement result
      */
     static Result executeCypher(CharSequence queryString) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).cypherStatic(queryString)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).cypherStatic(queryString)
     }
     /**
      * Varargs version of {@link #findAll(java.lang.String, 
java.util.Collection, java.util.Map)}
      */
     static List<D> findAll(CharSequence query, Object[] params) {
-        ((Neo4jGormStaticApi) GormEnhancer.findStaticApi(this)).findAll(query, 
Arrays.asList(params))
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).findAll(query, Arrays.asList(params))
     }
 
     /**
      * Varargs version of {@link #findAll(java.lang.String, 
java.util.Collection, java.util.Map)}
      */
     static List<D> findAll(CharSequence query, Map params) {
-        ((Neo4jGormStaticApi) GormEnhancer.findStaticApi(this)).findAll(query, 
params)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).findAll(query, params)
     }
     /**
      * Varargs version of {@link #findAll(java.lang.String, 
java.util.Collection, java.util.Map)}
      */
     static D find(CharSequence query, Object[] params) {
-        ((Neo4jGormStaticApi) GormEnhancer.findStaticApi(this)).find(query, 
Arrays.asList(params))
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).find(query, Arrays.asList(params))
     }
 
     /**
      * Varargs version of {@link #findAll(java.lang.String, 
java.util.Collection, java.util.Map)}
      */
     static D find(CharSequence query, Map params) {
-        ((Neo4jGormStaticApi) GormEnhancer.findStaticApi(this)).find(query, 
params)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).find(query, params)
     }
     /**
      * Perform an operation with the given connection
@@ -255,7 +255,7 @@ trait Neo4jEntity<D> implements GormEntity<D>, 
DynamicAttributes {
      * @return The return value of the closure
      */
     static <T> T withConnection(String connectionName, 
@DelegatesTo(GormAllOperations) Closure callable) {
-        def staticApi = GormEnhancer.findStaticApi(this, connectionName)
+        def staticApi = GormRegistry.instance.findStaticApi((Class<D>) this, 
connectionName)
         return (T) staticApi.withNewSession {
             callable.setDelegate(staticApi)
             return callable.call()
diff --git 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Node.groovy
 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Node.groovy
index c39ba331ef..40e7269973 100644
--- 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Node.groovy
+++ 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Node.groovy
@@ -22,6 +22,7 @@ package grails.neo4j
 import groovy.transform.CompileStatic
 import org.grails.datastore.gorm.GormEnhancer
 import org.grails.datastore.gorm.GormEntity
+import org.grails.datastore.gorm.GormRegistry
 import org.grails.datastore.gorm.neo4j.api.Neo4jGormStaticApi
 import org.grails.datastore.gorm.schemaless.DynamicAttributes
 
@@ -61,7 +62,7 @@ trait Node<D> implements Neo4jEntity<D>, GormEntity<D>, 
DynamicAttributes {
      * @return The path
      */
     static Path<D, D> findPath(CharSequence cypher) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).findPath(cypher, Collections.emptyMap())
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).findPath(cypher, Collections.emptyMap())
     }
 
     /**
@@ -73,7 +74,7 @@ trait Node<D> implements Neo4jEntity<D>, GormEntity<D>, 
DynamicAttributes {
      * @return The path or null if non exists
      */
     static <F, T> Path<F, T> findShortestPath(F from, T to, int maxDistance = 
10) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).findShortestPath(from, to, maxDistance)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).findShortestPath(from, to, maxDistance)
     }
 
     /**
@@ -84,7 +85,7 @@ trait Node<D> implements Neo4jEntity<D>, GormEntity<D>, 
DynamicAttributes {
      * @return The relationship or null if it doesn't exist
      */
     static <F extends GormEntity, T extends GormEntity> Relationship<F, T> 
findRelationship(F from, T to) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).findRelationship(from, to)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).findRelationship(from, to)
     }
 
     /**
@@ -95,7 +96,7 @@ trait Node<D> implements Neo4jEntity<D>, GormEntity<D>, 
DynamicAttributes {
      * @return The relationship or null if it doesn't exist
      */
     static <F extends GormEntity, T extends GormEntity> List<Relationship<F, 
T>> findRelationships(F from, T to, Map params = Collections.emptyMap()) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).findRelationships(from, to, params)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).findRelationships(from, to, params)
     }
 
     /**
@@ -106,7 +107,7 @@ trait Node<D> implements Neo4jEntity<D>, GormEntity<D>, 
DynamicAttributes {
      * @return The relationship or null if it doesn't exist
      */
     static <F extends GormEntity, T extends GormEntity> List<Relationship<F, 
T>> findRelationships(Class<F> from, Class<T> to, Map params = 
Collections.emptyMap()) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).findRelationships(from, to, params)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).findRelationships(from, to, params)
     }
     /**
      * Execute cypher that finds a path to the given entity
@@ -116,7 +117,7 @@ trait Node<D> implements Neo4jEntity<D>, GormEntity<D>, 
DynamicAttributes {
      * @return The path or null if non exists
      */
     static Path<D, D> findPath(CharSequence cypher, Map params) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).findPath(cypher, params)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).findPath(cypher, params)
     }
 
     /**
@@ -127,7 +128,7 @@ trait Node<D> implements Neo4jEntity<D>, GormEntity<D>, 
DynamicAttributes {
      * @return The path
      */
     static <T> Path<D, T> findPathTo(Class<T> type, CharSequence cypher) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).findPathTo(type, cypher, 
Collections.emptyMap())
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).findPathTo(type, cypher, Collections.emptyMap())
     }
 
     /**
@@ -137,6 +138,6 @@ trait Node<D> implements Neo4jEntity<D>, GormEntity<D>, 
DynamicAttributes {
      * @return The path
      */
     static <T> Path<D, T> findPathTo(Class<T> type, CharSequence cypher, Map 
params) {
-        ((Neo4jGormStaticApi) 
GormEnhancer.findStaticApi(this)).findPathTo(type, cypher, params)
+        ((Neo4jGormStaticApi) GormRegistry.instance.findStaticApi((Class<D>) 
this)).findPathTo(type, cypher, params)
     }
 }
\ No newline at end of file
diff --git 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Relationship.groovy
 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Relationship.groovy
index f8b9c2260c..1fea245d08 100644
--- 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Relationship.groovy
+++ 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Relationship.groovy
@@ -20,8 +20,8 @@
 package grails.neo4j
 
 import groovy.transform.CompileStatic
-import org.grails.datastore.gorm.GormEnhancer
 import org.grails.datastore.gorm.GormEntity
+import org.grails.datastore.gorm.GormRegistry
 import org.grails.datastore.gorm.neo4j.RelationshipPersistentEntity
 import org.grails.datastore.gorm.schemaless.DynamicAttributes
 
@@ -60,7 +60,7 @@ trait Relationship<F,T> implements DynamicAttributes, 
Serializable {
      */
     String type() {
         if(this.theType == null) {
-            theType = 
((RelationshipPersistentEntity)GormEnhancer.findEntity(getClass())).type()
+            theType = ((RelationshipPersistentEntity) 
GormRegistry.instance.apiResolver.findEntity(getClass())).type()
         }
         return theType
     }
diff --git 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastore.java
 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastore.java
index 2d023db616..a47f600a1a 100644
--- 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastore.java
+++ 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastore.java
@@ -581,6 +581,7 @@ public class Neo4jDatastore extends AbstractDatastore 
implements Closeable, Stat
     @Override
     @PreDestroy
     public void close() throws IOException {
+        
org.grails.datastore.gorm.GormRegistry.getInstance().removeDatastore(this);
         try {
             try {
                 gormEnhancer.close();
diff --git 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/api/Neo4jGormStaticApi.groovy
 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/api/Neo4jGormStaticApi.groovy
index f41046e690..6551ccd71a 100644
--- 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/api/Neo4jGormStaticApi.groovy
+++ 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/api/Neo4jGormStaticApi.groovy
@@ -534,7 +534,13 @@ RETURN DISTINCT(r), from, to$skip$limit"""
             if (!queryString.contains("\$tenantId")) {
                 throw new TenantNotFoundException("Query does not specify a 
tenant id, but multi tenant mode is DISCRIMINATOR!")
             } else {
-                paramsMap.put(GormProperties.TENANT_IDENTITY, 
Tenants.currentId(Neo4jDatastore))
+                Serializable tenantId
+                if (qualifier != null && qualifier != 
ConnectionSource.DEFAULT) {
+                    tenantId = qualifier
+                } else {
+                    tenantId = Tenants.currentId(Neo4jDatastore)
+                }
+                paramsMap.put(GormProperties.TENANT_IDENTITY, tenantId)
             }
         }
     }
diff --git 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPath.groovy
 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPath.groovy
index bd31686547..da942dd477 100644
--- 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPath.groovy
+++ 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPath.groovy
@@ -23,7 +23,7 @@ import grails.neo4j.Neo4jEntity
 import grails.neo4j.Path
 import grails.neo4j.Relationship
 import groovy.transform.CompileStatic
-import org.grails.datastore.gorm.GormEnhancer
+import org.grails.datastore.gorm.GormRegistry
 import org.grails.datastore.gorm.neo4j.GraphPersistentEntity
 import org.grails.datastore.gorm.neo4j.Neo4jDatastore
 import org.grails.datastore.gorm.neo4j.Neo4jMappingContext
@@ -83,7 +83,7 @@ class Neo4jPath<S extends Neo4jEntity<S>, E extends 
Neo4jEntity<E>> implements P
     S start() {
         if(start == null) {
             Class clazz = from.javaClass
-            Neo4jEntityPersister persister = (Neo4jEntityPersister 
)GormEnhancer.findDatastore(clazz).currentSession.getPersister(clazz)
+            Neo4jEntityPersister persister = (Neo4jEntityPersister 
)GormRegistry.instance.apiResolver.findDatastore(clazz).currentSession.getPersister(clazz)
             start = (S)persister.unmarshallOrFromCache(from, neo4jPath.start())
         }
         return start
@@ -93,7 +93,7 @@ class Neo4jPath<S extends Neo4jEntity<S>, E extends 
Neo4jEntity<E>> implements P
     E end() {
         if(end == null) {
             Class clazz = to.javaClass
-            Neo4jEntityPersister persister = (Neo4jEntityPersister 
)GormEnhancer.findDatastore(clazz).currentSession.getPersister(clazz)
+            Neo4jEntityPersister persister = (Neo4jEntityPersister 
)GormRegistry.instance.apiResolver.findDatastore(clazz).currentSession.getPersister(clazz)
 
             end = (E)persister.unmarshallOrFromCache(to, neo4jPath.end())
         }
diff --git 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/grails/gorm/tests/ValidationSpec.groovy
 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/grails/gorm/tests/ValidationSpec.groovy
index 24b0ec06bb..eec404cfc4 100644
--- 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/grails/gorm/tests/ValidationSpec.groovy
+++ 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/grails/gorm/tests/ValidationSpec.groovy
@@ -19,7 +19,7 @@
 
 package grails.gorm.tests
 
-import org.grails.datastore.gorm.GormEnhancer
+import org.grails.datastore.gorm.GormRegistry
 import org.grails.datastore.gorm.validation.CascadingValidator
 import org.grails.datastore.mapping.model.PersistentEntity
 import org.springframework.validation.Validator
@@ -38,7 +38,7 @@ class ValidationSpec extends GormDatastoreSpec {
     def setup() {
         for(cls in domainClasses) {
             setupValidator(cls)
-            GormEnhancer.findValidationApi(cls).validator = null
+            GormRegistry.instance.findValidationApi(cls).validator = null
         }
     }
 
diff --git 
a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/org/grails/datastore/gorm/neo4j/Neo4jTenantContextProfilingSpec.groovy
 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/org/grails/datastore/gorm/neo4j/Neo4jTenantContextProfilingSpec.groovy
new file mode 100644
index 0000000000..f983b46015
--- /dev/null
+++ 
b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/org/grails/datastore/gorm/neo4j/Neo4jTenantContextProfilingSpec.groovy
@@ -0,0 +1,113 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  'License'); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.grails.datastore.gorm.neo4j
+
+import grails.gorm.MultiTenant
+import grails.gorm.multitenancy.Tenants
+import org.grails.datastore.gorm.GormRegistry
+import org.grails.datastore.gorm.DatastoreResolver
+import org.grails.datastore.gorm.multitenancy.TenantDelegatingGormOperations
+import org.grails.datastore.mapping.core.Datastore
+import org.grails.datastore.mapping.multitenancy.MultiTenantCapableDatastore
+import org.grails.datastore.mapping.multitenancy.MultiTenancySettings
+import org.grails.datastore.mapping.model.MappingContext
+import org.grails.datastore.gorm.neo4j.api.Neo4jGormStaticApi
+import spock.lang.Specification
+
+class Neo4jTenantContextProfilingSpec extends Specification {
+
+    void setup() {
+        GormRegistry.instance.reset()
+    }
+
+    void cleanup() {
+        GormRegistry.instance.reset()
+    }
+
+    void "profile neo4j tenant wrapping overhead"() {
+        given:
+        def mappingContext = Stub(MappingContext)
+        def datastore = Stub(MultiTenantCapableDatastore) {
+            getMultiTenancyMode() >> 
MultiTenancySettings.MultiTenancyMode.DATABASE
+            getMappingContext() >> mappingContext
+            getDatastoreForTenantId(_) >> { return it[0] == null ? delegate : 
delegate }
+        }
+        
+        def registry = GormRegistry.instance
+        registry.registerDatastore("default", datastore)
+        
+        def staticApi = new DummyNeo4jStaticApi(TenantEntity, datastore)
+        def ops = new TenantDelegatingGormOperations<TenantEntity>((Datastore) 
datastore, "tenant1", staticApi)
+        def qualifiedApi = staticApi.forQualifier("tenant1")
+        
+        int iterations = 1000
+
+        when: "Calling operations repeatedly via 
TenantDelegatingGormOperations (wrapped every time)"
+        long startWrapped = System.currentTimeMillis()
+        for (int i = 0; i < iterations; i++) {
+            ops.exists(1L)
+        }
+        long endWrapped = System.currentTimeMillis()
+
+        and: "Calling operations via qualified API (unwrapped, but pre-bound)"
+        long startQualified = System.currentTimeMillis()
+        for (int i = 0; i < iterations; i++) {
+            qualifiedApi.exists(1L)
+        }
+        long endQualified = System.currentTimeMillis()
+
+        and: "Calling operations via closure block (wrapped once)"
+        long startBlock = System.currentTimeMillis()
+        Tenants.withId((MultiTenantCapableDatastore) datastore, "tenant1") {
+            for (int i = 0; i < iterations; i++) {
+                staticApi.exists(1L)
+            }
+        }
+        long endBlock = System.currentTimeMillis()
+
+        then:
+        println "Neo4j Single block wrapped operations: ${endBlock - 
startBlock} ms"
+        println "Neo4j Qualified API operations: ${endQualified - 
startQualified} ms"
+        println "Neo4j Per-method wrapped operations: ${endWrapped - 
startWrapped} ms"
+        
+        true
+    }
+
+    static class TenantEntity implements MultiTenant<TenantEntity> {
+        Long id
+    }
+
+    static class DummyNeo4jStaticApi extends Neo4jGormStaticApi<TenantEntity> {
+        DummyNeo4jStaticApi(Class<TenantEntity> persistentClass, 
MultiTenantCapableDatastore datastore) {
+            super(persistentClass, (Neo4jDatastore) datastore, [], new 
org.grails.datastore.gorm.DatastoreResolver() {
+                @Override org.grails.datastore.mapping.core.Datastore 
resolve() { return (Datastore) datastore }
+            })
+        }
+
+        @Override
+        boolean exists(Serializable id) {
+            return true
+        }
+
+        @Override
+        org.grails.datastore.gorm.GormStaticApi<TenantEntity> 
forQualifier(String qualifier) {
+            return this
+        }
+    }
+}

Reply via email to