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