Skip to content

GORM: Shared Mapping Registry O(M+N) Scaling (clean rebuild)#15678

Open
borinquenkid wants to merge 14 commits into
8.0.x-hibernate7from
8.0.x-hibernate7.gorm-scaling-clean
Open

GORM: Shared Mapping Registry O(M+N) Scaling (clean rebuild)#15678
borinquenkid wants to merge 14 commits into
8.0.x-hibernate7from
8.0.x-hibernate7.gorm-scaling-clean

Conversation

@borinquenkid
Copy link
Copy Markdown
Member

Description

This PR replaces #15656, which was rebuilt on a clean branch to remove a corruption commit from the history.

Problem: In multi-tenant GORM environments with many tenants (M) and domain classes (N), the previous implementation instantiated a full set of static API, instance API, and validation API objects per tenant per entity, producing O(M × N) object allocations. This caused excessive memory consumption and degraded startup performance as tenant counts scaled.

Solution: Refactor GormRegistry to use a single shared registry keyed by entity class name and qualifier (datasource/tenant), with the GORM API objects created once per entity per qualifier and reused across tenants. This reduces the allocation profile to O(M + N).

This rebuild also fixes two regression classes exposed by the new registry:

  1. Multi-tenancy resolution regressionsGormApiResolver and GormRegistry.registerEntityDatastores were routing DISCRIMINATOR/SCHEMA tenant IDs through the datasource connection lookup path, causing child datastores to be overwritten by the parent and PartitionedMultiTenancySpec.count() to NPE. Fixed by detecting multi-tenancy mode before delegating to getDatastoreForConnection, and by skipping non-DEFAULT qualifier registration when the qualifier resolves back to the parent (i.e., it's a runtime tenant ID, not a datasource name).

  2. Child datastore initialization orderHibernateDatastore (H5) and ChildHibernateDatastore (H7) were throwing ConfigurationException when getDatastoreForConnection was called for a sibling during initialization before all children were registered. Fixed to return null during the initialization phase so GormRegistry falls back gracefully and re-registers once initialization completes.

Test infrastructure: Added forkEvery = 1 to gradle/hibernate5-test-config.gradle and gradle/hibernate7-test-config.gradle. The root config uses forkEvery = 50/100 for speed, but with a shared GormRegistry singleton, TCK specs running in the same JVM before PartitionedMultiTenancySpec were clearing datastoresByQualifier["default"] and causing the NPE described above. Each test class now gets its own JVM.

Verified: H5 — 669 tests / 0 failures. H7 — 2960 tests / 0 failures.

Contributor Checklist

Issue and Scope

  • This PR is linked to an existing issue that has been acknowledged or approved by the project team. (Replaces GORM: Shared Mapping Registry O(M+N) Scaling #15656, which tracks the approved O(M+N) scaling work.)
  • This PR addresses the complete scope of the linked issue.
  • This PR contains a single, focused change.
  • This PR targets the correct branch (8.0.x-hibernate7 — major release branch; breaking API changes permitted).

Code Quality

  • I have added or updated tests that cover the changes introduced in this PR.
  • I have verified that all existing tests pass (H5: 669/0 failures, H7: 2960/0 failures).
  • My code follows the project's code style guidelines. ./gradlew codeStyle has been run and violations resolved.
  • This PR does not include unsolicited reformatting or unrelated refactoring.
  • Generative AI tooling was used in preparing this contribution with a quality model, consistent with the project's quality standards.

Licensing and Attribution

  • All contributed code is provided under the Apache License 2.0, and new source files include the appropriate Apache license header.
  • I have the necessary rights to submit this contribution and confirm it is my own original work.
  • Generative AI tooling use follows the ASF policy on generative tooling and is properly attributed.

Documentation

  • No new user-facing APIs are introduced; this is a performance/correctness fix to the internal registry.
  • The PR description explains what was changed and why.

borinquenkid and others added 7 commits May 25, 2026 10:27
Introduces shared-registry architecture to eliminate per-tenant API wrapper
duplication in multi-tenant environments with high entity/tenant cardinality.

Core changes:
- GormRegistry: normalization caches (entity keys, qualifiers), O(1) lookup paths
- GormApiResolver: simplified fallback chains, qualified API caching
- AbstractGormApiRegistry/sub-registries: normalized key/qualifier registration
- GormEnhancer: delegates API resolution through GormRegistry

Datastore integrations:
- Hibernate 7 and Hibernate 5: aligned to shared registry model
- MongoDB, Neo4j, SimpleMap, GraphQL: registry-pattern integration

Adjacent migrations:
- AsyncEntity: GormEnhancer.findStaticApi -> GormRegistry.instance.findStaticApi
- ByDatasourceDomainClassFetcher: GormEnhancer.findDatastore -> GormRegistry apiResolver
- TCK: added transaction-capable datastore support in GrailsDataTckManager

This commit excludes all collateral CodeNarc reformat changes (2,835 files
from commit 4add87e) and agent experiments, containing only the
optimization-specific module changes.

Agent collaboration note: Claude Sonnet 4.6 assisted with branch archaeology
and rebuild strategy; borinquenkid is the primary author and remains
responsible for the final changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Includes SessionResolver and ThreadLocalSessionResolver (new interfaces/classes
introduced by the O(M+N) scaling refactor), plus updates to AbstractDatastore,
AbstractMappingContext, and related core classes that the datastore modules
(SimpleMap, Hibernate 5/7) depend on at compile time.

Missed from initial clean rebuild commit.

Agent collaboration note: Claude Sonnet 4.6 assisted; borinquenkid is the
primary author and remains responsible for the final changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
During child datastore construction, GormRegistry.registerEntityDatastores
calls getDatastoreForConnection() before the parent's datastoresByConnectionSource
map is populated, throwing ConfigurationException for multi-datasource setups.

H5 anonymous child: add self-reference check so the child returns itself when
asked for its own connection name, rather than delegating to the parent map.

H7 ChildHibernateDatastore: use PARENT_HOLDER ThreadLocal to pass the parent
reference through the super() call before the parent field is assigned; also
pass the parent's datastoresByConnectionSource map to HibernateGormEnhancer
so it can resolve sibling datastores during initialize().

Fixes DataSource not found for name [secondary/schemaA] ConfigurationException
in multi-datasource and schema-per-tenant multi-tenancy test suites.

Agent collaboration note: Claude Sonnet 4.6 assisted; borinquenkid is the
primary author and remains responsible for the final changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The O(M+N) GormRegistry refactor exposed two classes of regression in
multi-tenancy and multi-datasource scenarios. This commit addresses both.

Production fixes:

GormApiResolver: Move the DISCRIMINATOR mode check before the
MultipleConnectionSourceCapableDatastore delegation so that tenant IDs are
never mistaken for datasource connection names. For the DEFAULT qualifier,
return the preferred (active-transaction) datastore directly rather than
re-routing through getDatastoreForConnection, which would return the parent
and mismatch the session factory already bound to the transaction.

GormRegistry.registerEntityDatastores: Stop overwriting child datastores with
the parent for non-DEFAULT qualifiers that resolve back to the parent. In
SCHEMA and DISCRIMINATOR mode the qualifier is a runtime tenant ID, not a
datasource name; routing it back to the parent is correct and must not clobber
the child entries added by addTenantForSchemaInternal.

GormRegistry.findTransactionManager: Fall back through the full apiResolver
when getDatastore returns null so that DISCRIMINATOR/SCHEMA tenant IDs still
resolve to a transaction manager.

HibernateDatastore (H5) / ChildHibernateDatastore (H7): Return null instead
of throwing ConfigurationException when getDatastoreForConnection is called
for a sibling that is not yet registered during initialization. GormRegistry
will re-register all entities with the correct datastores once initialization
completes. Child datastores also delegate to the parent for unrecognized
connection names so the lookup chain stays consistent.

HibernateGormInstanceApi (H7): Always resolve the template via the datastore
registry rather than caching a DEFAULT-qualifier instance, so that
preferred-datastore switching in multi-datasource transactions picks up the
correct session factory.

GrailsHibernateTransactionManager (H7): Remove debug System.err.println
statements left over from investigation.

Test infrastructure fixes:

gradle/hibernate5-test-config.gradle, gradle/hibernate7-test-config.gradle:
Set forkEvery = 1 so each test class runs in its own JVM. The root
test-config.gradle uses forkEvery = 50 (CI) / 100 (local) for speed; with a
shared GormRegistry singleton that per-test setup/teardown mutates, TCK specs
running before PartitionedMultiTenancySpec in the same JVM were clearing
datastoresByQualifier["default"], causing a NullPointerException in count()
when PartitionedMultiTenancySpec later resolved a GormPersistentEntity. forkEvery = 1
eliminates cross-class singleton contamination at the cost of extra JVM
startup overhead, which is acceptable given the test isolation requirement.

GrailsDataHibernate5TckManager: Add grailsConfig field and populate a local
ConfigObject from it in createSession(), fixing MissingPropertyException when
test specs assign grailsConfig before calling setup().

Verified: H5 669 tests / 0 failures, H7 2960 tests / 0 failures.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Local issue-tracking files have no Apache license header and are not
source artifacts. Add **/ISSUES.md to the RAT exclusion list alongside
the existing local-tasks.gradle exclusion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…saction routing

    - GormEnhancer: Remove generic type parameters (`<D>`) from deprecated API lookup methods (`findStaticApi`, `findInstanceApi`, `findValidationApi`). This fixes a `MissingMethodException` encountered
  when older compiled code or dynamic Groovy proxies call these methods without exact signature matches.
    - TransactionalTransform: Fix `IllegalStateException: No GORM implementations configured` in multi-tenant environments. Revert logic that incorrectly passed `tenantId` as a connection source
  qualifier to `GormRegistry`, and instead fetch the tenant-specific datastore using `getTargetDatastore().getDatastoreForTenantId(tenantId)`.
    - ServiceTransformation: Ensure the stateful `$datastore` field is properly generated for generic data services to resolve injection failures in tests.
@borinquenkid borinquenkid force-pushed the 8.0.x-hibernate7.gorm-scaling-clean branch from afa32a1 to 611e6bd Compare May 25, 2026 15:28
@borinquenkid borinquenkid requested review from matrei and sbglasius May 25, 2026 15:32
borinquenkid and others added 5 commits May 25, 2026 10:51
… artifacts

  Fixes TCK DataServiceConnectionRoutingSpec failures introduced during the O(M+N)
  scaling refactor. The decentralized API resolver changes caused getTargetDatastore(String)
  to ignore the explicitly-injected $targetDatastore and route through the API resolver
  instead, which could return a child datastore that has no knowledge of sibling connections.

  - AbstractDatastoreMethodDecoratingTransformation: getTargetDatastore(String) now checks
    $targetDatastore before falling back to the API resolver
  - ServiceTransformation: generateConnectionAwareTransactionManager uses getTargetDatastore()
    instead of getDatastore() for correct multi-datasource transaction manager resolution
  - GrailsDataHibernate7TckManager: fix setTargetDatastore array overload to use
    MultipleConnectionSourceCapableDatastore[] instead of Datastore[]
…) + entity re-registration in setup(), wired setTargetDatastore(multiDataSourceDatastore) in getServiceForConnection(), and wired

  setTargetDatastore(multiTenantMultiDataSourceDatastore) in getServiceForMultiTenantConnection(). This fixes 26 H5 failures.
  2. GrailsDataHibernate7TckManager.groovy — Wired setTargetDatastore(multiTenantMultiDataSourceDatastore) in getServiceForMultiTenantConnection(). This fixes 5 H7
  DataServiceMultiTenantConnectionRoutingSpec failures.
  3. GormApiResolver.groovy — Removed 6 residual System.out.println debug statements from ActiveSessionDatastoreSelector.
  4. ServiceTransformSpec.groovy — Added GormRegistry.reset() in setup()/cleanup() to prevent cross-spec registry pollution causing 3 flaky test failures.
  Root cause: AutoTimestampEventListener.beforeUpdate() unconditionally sets lastUpdated/dateCreated on every PreUpdate event, even when the entity has no user-intent changes. This caused
  PendingUpdate.run() to see a changed property, add it to $set, and increment the optimistic-locking version — breaking the MarkDirtyFalseSpec contract that a no-op save() must not increment version.

  Fix in MongoCodecEntityPersister.persistEntity():
  - Before cancelUpdate fires, capture a snapshot of all property values and whether the entity already had user-intent dirty state (hasPreExistingDirty).
  - After cancelUpdate, compare the snapshot to detect what changed. If the only changes are lastUpdated/dateCreated (auto-timestamp properties) with no pre-existing user-intent dirty state, restore those
  timestamps, call trackChanges(), and veto the update.
  - The veto condition requires onlyAutoTimestampChanged to be non-empty — entities without auto-timestamp properties (including those with embedded-only associations) are never incorrectly vetoed, even
  when the snapshot comparison misses embedded-object mutations (same object reference).
  - Also refactored persistEntity() to remove the unnecessary isUpdate && !session.isDirty(obj) early-return guard, which was incompatible with the snapshot logic.

  Other changes:
  - GrailsDataMongoTckManager: added GormRegistry.reset() in setup() for per-test datastore isolation (prevents stale GORM state polluting successive test features).
  - DirtyCheckingSupport: propagate dirty-checking into PersistentCollection items so collection-element mutations are visible to the parent entity's dirty check traversal.

  Tests verified: MarkDirtyFalseSpec, EmbeddedAssociationSpec, EmbeddedCollectionAndInheritanceSpec, EmbeddedCollectionWithOneToOneSpec, EmbeddedUnsetSpec, LastUpdatedSpec,
  BeforeUpdatePropertyPersistenceSpec, DirtyCheckUpdateSpec — plus full testSelected suite: 4588 tests, 0 failures.
…eDatastore

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@borinquenkid borinquenkid force-pushed the 8.0.x-hibernate7.gorm-scaling-clean branch from c5f7c32 to 4358b70 Compare May 29, 2026 01:28
borinquenkid and others added 2 commits May 29, 2026 07:50
…resolution

- currentGorm{Instance,Static,Validation}Api() throw IllegalStateException
  when GORM is uninitialized, instead of returning null. Callers such as
  DefaultLinkGenerator.getResourceId already catch IllegalStateException and
  fall back; this restores the pre-O(M+N) contract and fixes NPEs in
  VndErrorRenderingSpec, HalJsonRendererSpec, TableSpec and others.
- GormStaticApi.getGormPersistentEntity() falls back to the mapping context
  captured at construction when registry resolution returns null. Entity
  metadata is identical across tenants, so this stable reference fixes the
  withTenant(tenantId).count() NPE for DISCRIMINATOR multi-tenancy under
  cross-spec registry state (PartitionedMultiTenancySpec in the full suite).
- SimpleMapSession.isDirty() treats an identified instance absent from the
  backing map as dirty so save() re-inserts it. Fixes the unit-test pattern of
  saving @shared instances in setup() across feature iterations after
  clearData() empties the in-memory datastore (DefaultInputRenderingSpec).
- DefaultHalViewHelper.renderEntityProperties excludes the version property from
  embedded output, consistent with top-level GORM rendering. KeyValue entities
  gained an auto-mapped version under the GORM mapping strategy (HalEmbeddedSpec).
- Remove stale @NotYetImplemented on UniqueConstraintOnHasOneSpec; unique-on-hasOne
  now works, so the spec passes and the assertion is restored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes five of the six failing Functional Tests CI tasks. Each was a core GORM
contract that regressed in the shared-GormRegistry rewrite but surfaced far
downstream; root-cause fixes are guarded by core unit tests.

- SimpleMapSession: a rolled-back transaction set a session-level rollbackOnly
  flag that was never cleared, permanently turning flush() into a no-op on the
  (long-lived, thread-bound) test session. Subsequent save(flush:true) calls
  assigned an id and populated the first-level cache but never reached the
  backing map. Clear the marker on commit/rollback and reset it when a
  transaction begins. Adds SimpleMapSessionSpec coverage. Fixes the app1
  BookControllerSpec save/delete count()==0 failures.

- DataTest harness: the single shared GormRegistry resolves a domain mapped to a
  non-default datasource to a dedicated per-connection child datastore, but the
  unit-test interceptors only bound a session for the default datastore. Entities
  on non-default datasources ran in throwaway per-call sessions, so save()
  without an explicit flush was lost before an auto-flushing query. Bind (and
  symmetrically unbind) a session for every connection source; no-op for
  single-datasource specs. Adds NonDefaultDatasourceFlushSpec. Fixes demo33
  CarSpec.

- GormStaticApi.withTransaction(Map)/withNewTransaction(Map): replaced the broken
  definition.setProperty(key, value) call (no such method on the Java bean
  DefaultTransactionDefinition) with the property-set idiom definition[key]=value,
  plus CharSequence coercion and a clear error for unknown properties. Fixes
  CrossDatasourceTransactionSpec read-only transactions.

- DefaultHalViewHelper: reverted the embedded-version exclusion. Embedded HAL
  output renders the version property for versioned entities (functional TeamSpec
  depends on it); the unit-level HalEmbeddedSpec only saw an extra version:0
  because KeyValue entities now auto-map a version under the GORM mapping
  strategy, so its expectation is updated instead.

- demo33 UniqueConstraintOnHasOneSpec: removed a stale @NotYetImplemented (a
  second copy of an already-fixed spec) that fails as "passes unexpectedly".

- Bar/FooIntegrationSpec: assert datastore persistence through public, observable
  behavior (Mongo ObjectId / Hibernate sequential id) instead of the removed
  internal org.grails.datastore.gorm.GormEnhancer.findStaticApi probe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@testlens-app
Copy link
Copy Markdown

testlens-app Bot commented May 29, 2026

🚨 TestLens detected 19 failed tests 🚨

Here is what you can do:

  1. Inspect the test failures carefully.
  2. If you are convinced that some of the tests are flaky, you can mute them below.
  3. Finally, trigger a rerun by checking the rerun checkbox.

Test Summary

Check Project/Task Test Runs
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-graphql-grails-test-app:integrationTest CommentIntegrationSpec > test querying a comment with only the parent id
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate5-grails-database-per-tenant:integrationTest DatabasePerTenantIntegrationSpec > Test database per tenant
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate7-grails-database-per-tenant:integrationTest DatabasePerTenantIntegrationSpec > Test database per tenant
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate7-grails-database-per-tenant:integrationTest DatabasePerTenantIntegrationSpec > test saveBook with normal service
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate5-grails-database-per-tenant:test DatabasePerTenantSpec > Test database per tenant
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate7-grails-database-per-tenant:test DatabasePerTenantSpec > Test database per tenant
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate5-grails-database-per-tenant:test DatabasePerTenantSpec > Test should rollback changes in a previous test
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate7-grails-database-per-tenant:test DatabasePerTenantSpec > Test should rollback changes in a previous test
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate7-grails-multiple-datasources:integrationTest MultipleDataSourcesSpec > Test multiple data source persistence
CI - Groovy Joint Validation Build / build_grails :grails-data-hibernate5-core:test PartitionedMultiTenancySpec > Test partitioned multi tenancy
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate5-grails-hibernate-groovy-proxy:test ProxySpec > Test Proxy
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate7-grails-schema-per-tenant:integrationTest SchemaPerTenantIntegrationSpec > Test database per tenant
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate5-grails-schema-per-tenant:integrationTest SchemaPerTenantIntegrationSpec > Test database per tenant
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate7-grails-schema-per-tenant:test SchemaPerTenantSpec > Test database per tenant
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate5-grails-schema-per-tenant:test SchemaPerTenantSpec > Test database per tenant
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate5-grails-schema-per-tenant:test SchemaPerTenantSpec > Test should rollback changes in a previous test
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-hibernate7-grails-schema-per-tenant:test SchemaPerTenantSpec > Test should rollback changes in a previous test
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-graphql-grails-test-app:integrationTest TagIntegrationSpec > test a custom property can reference a domain with using joins
CI - Groovy Joint Validation Build / build_grails :grails-test-examples-graphql-grails-test-app:integrationTest UserRoleIntegrationSpec > test reading an entity with a complex composite id

🏷️ Commit: 39eadad
▶️ Tests: 26927 executed
⚪️ Checks: 75/75 completed

Test Failures (first 10 of 19)

CommentIntegrationSpec > test querying a comment with only the parent id (:grails-test-examples-graphql-grails-test-app:integrationTest in CI - Groovy Joint Validation Build / build_grails)
Condition not satisfied:

outCount == 1
|        |
2        false

	at grails.test.app.CommentIntegrationSpec.test querying a comment with only the parent id(CommentIntegrationSpec.groovy:136)
expected actual
1 2
DatabasePerTenantIntegrationSpec > Test database per tenant (:grails-test-examples-hibernate5-grails-database-per-tenant:integrationTest in CI - Groovy Joint Validation Build / build_grails)
Expected exception of type 'org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundException', but no exception was thrown
	at org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:84)
	at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:71)
	at example.DatabasePerTenantIntegrationSpec.$tt__$spock_feature_0_2(DatabasePerTenantIntegrationSpec.groovy:90)
	at example.DatabasePerTenantIntegrationSpec.Test database per tenant_closure5(DatabasePerTenantIntegrationSpec.groovy)
	at groovy.lang.Closure.call(Closure.java:433)
	at groovy.lang.Closure.call(Closure.java:422)
	at grails.gorm.transactions.GrailsTransactionTemplate$1.doInTransaction(GrailsTransactionTemplate.groovy:72)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:137)
	at grails.gorm.transactions.GrailsTransactionTemplate.executeAndRollback(GrailsTransactionTemplate.groovy:69)
	at example.DatabasePerTenantIntegrationSpec.Test database per tenant(DatabasePerTenantIntegrationSpec.groovy)
DatabasePerTenantIntegrationSpec > Test database per tenant (:grails-test-examples-hibernate7-grails-database-per-tenant:integrationTest in CI - Groovy Joint Validation Build / build_grails)
Expected exception of type 'org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundException', but no exception was thrown
	at org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:84)
	at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:71)
	at example.DatabasePerTenantIntegrationSpec.$tt__$spock_feature_0_2(DatabasePerTenantIntegrationSpec.groovy:90)
	at example.DatabasePerTenantIntegrationSpec.Test database per tenant_closure5(DatabasePerTenantIntegrationSpec.groovy)
	at groovy.lang.Closure.call(Closure.java:433)
	at groovy.lang.Closure.call(Closure.java:422)
	at grails.gorm.transactions.GrailsTransactionTemplate$1.doInTransaction(GrailsTransactionTemplate.groovy:72)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:137)
	at grails.gorm.transactions.GrailsTransactionTemplate.executeAndRollback(GrailsTransactionTemplate.groovy:69)
	at example.DatabasePerTenantIntegrationSpec.Test database per tenant(DatabasePerTenantIntegrationSpec.groovy)
DatabasePerTenantIntegrationSpec > test saveBook with normal service (:grails-test-examples-hibernate7-grails-database-per-tenant:integrationTest in CI - Groovy Joint Validation Build / build_grails)
Condition not satisfied:

anotherBookService.countBooks() == 1
|                  |            |
|                  2            false
<example.AnotherBookService@665c8e0f $transactionManager=org.grails.orm.hibernate.GrailsHibernateTransactionManager@92adb3b $targetDatastore=HibernateDatastore: default>

	at example.DatabasePerTenantIntegrationSpec.$tt__$spock_feature_0_1(DatabasePerTenantIntegrationSpec.groovy:81)
	at example.DatabasePerTenantIntegrationSpec.test saveBook with normal service_closure4(DatabasePerTenantIntegrationSpec.groovy)
	at groovy.lang.Closure.call(Closure.java:433)
	at groovy.lang.Closure.call(Closure.java:422)
	at grails.gorm.transactions.GrailsTransactionTemplate$1.doInTransaction(GrailsTransactionTemplate.groovy:72)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:137)
	at grails.gorm.transactions.GrailsTransactionTemplate.executeAndRollback(GrailsTransactionTemplate.groovy:69)
	at example.DatabasePerTenantIntegrationSpec.test saveBook with normal service(DatabasePerTenantIntegrationSpec.groovy)
expected actual
1 2
DatabasePerTenantSpec > Test database per tenant (:grails-test-examples-hibernate7-grails-database-per-tenant:test in CI - Groovy Joint Validation Build / build_grails)
Expected exception of type 'org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundException', but no exception was thrown
	at org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:84)
	at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:71)
	at example.DatabasePerTenantSpec.Test database per tenant(DatabasePerTenantSpec.groovy:70)
DatabasePerTenantSpec > Test database per tenant (:grails-test-examples-hibernate5-grails-database-per-tenant:test in CI - Groovy Joint Validation Build / build_grails)
Expected exception of type 'org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundException', but no exception was thrown
	at org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:84)
	at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:71)
	at example.DatabasePerTenantSpec.Test database per tenant(DatabasePerTenantSpec.groovy:70)
DatabasePerTenantSpec > Test should rollback changes in a previous test (:grails-test-examples-hibernate7-grails-database-per-tenant:test in CI - Groovy Joint Validation Build / build_grails)
Expected exception of type 'org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundException', but no exception was thrown
	at org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:84)
	at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:71)
	at example.DatabasePerTenantSpec.$tt__$spock_feature_1_0(DatabasePerTenantSpec.groovy:54)
	at example.DatabasePerTenantSpec.Test should rollback changes in a previous test_closure1(DatabasePerTenantSpec.groovy)
	at groovy.lang.Closure.call(Closure.java:433)
	at groovy.lang.Closure.call(Closure.java:422)
	at grails.gorm.transactions.GrailsTransactionTemplate$1.doInTransaction(GrailsTransactionTemplate.groovy:72)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:137)
	at grails.gorm.transactions.GrailsTransactionTemplate.executeAndRollback(GrailsTransactionTemplate.groovy:69)
	at example.DatabasePerTenantSpec.Test should rollback changes in a previous test(DatabasePerTenantSpec.groovy)
DatabasePerTenantSpec > Test should rollback changes in a previous test (:grails-test-examples-hibernate5-grails-database-per-tenant:test in CI - Groovy Joint Validation Build / build_grails)
Expected exception of type 'org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundException', but no exception was thrown
	at org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:84)
	at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:71)
	at example.DatabasePerTenantSpec.$tt__$spock_feature_1_0(DatabasePerTenantSpec.groovy:54)
	at example.DatabasePerTenantSpec.Test should rollback changes in a previous test_closure1(DatabasePerTenantSpec.groovy)
	at groovy.lang.Closure.call(Closure.java:433)
	at groovy.lang.Closure.call(Closure.java:422)
	at grails.gorm.transactions.GrailsTransactionTemplate$1.doInTransaction(GrailsTransactionTemplate.groovy:72)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:137)
	at grails.gorm.transactions.GrailsTransactionTemplate.executeAndRollback(GrailsTransactionTemplate.groovy:69)
	at example.DatabasePerTenantSpec.Test should rollback changes in a previous test(DatabasePerTenantSpec.groovy)
MultipleDataSourcesSpec > Test multiple data source persistence (:grails-test-examples-hibernate7-grails-multiple-datasources:integrationTest in CI - Groovy Joint Validation Build / build_grails)
java.lang.IllegalArgumentException: Unknown entity type 'ds2.Book' ('Book' is not annotated '@Entity')
	at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:696)
	at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:675)
	at org.grails.orm.hibernate.HibernateGormInstanceApi.performPersist_closure11(HibernateGormInstanceApi.groovy:556)
	at groovy.lang.Closure.call(Closure.java:433)
	at org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:449)
	at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:388)
	at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:217)
	at org.grails.orm.hibernate.HibernateGormInstanceApi.performPersist(HibernateGormInstanceApi.groovy:553)
	at org.grails.orm.hibernate.HibernateGormInstanceApi.performUpsert_closure9(HibernateGormInstanceApi.groovy:490)
	at groovy.lang.Closure.call(Closure.java:433)
	at org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:449)
	at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:388)
	at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:217)
	at org.grails.orm.hibernate.HibernateGormInstanceApi.performUpsert(HibernateGormInstanceApi.groovy:475)
	at org.grails.orm.hibernate.HibernateGormInstanceApi.save(HibernateGormInstanceApi.groovy:299)
	at org.grails.datastore.gorm.GormEntity$Trait$Helper.save(GormEntity.groovy:169)
	at functionaltests.MultipleDataSourcesSpec._tt___spock_feature_0_0_closure3(MultipleDataSourcesSpec.groovy:41)
	at groovy.lang.Closure.call(Closure.java:433)
	at groovy.lang.Closure.call(Closure.java:422)
	at grails.gorm.transactions.GrailsTransactionTemplate$2.doInTransaction(GrailsTransactionTemplate.groovy:98)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:137)
	at grails.gorm.transactions.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:95)
	at org.grails.datastore.gorm.GormStaticApi.withTransaction(GormStaticApi.groovy:632)
	at org.grails.datastore.gorm.GormEntity$Trait$Helper.withTransaction(GormEntity.groovy:1049)
	at functionaltests.MultipleDataSourcesSpec.$tt__$spock_feature_0_0(MultipleDataSourcesSpec.groovy:40)
	at functionaltests.MultipleDataSourcesSpec.Test multiple data source persistence_closure1(MultipleDataSourcesSpec.groovy)
	at groovy.lang.Closure.call(Closure.java:433)
	at groovy.lang.Closure.call(Closure.java:422)
	at grails.gorm.transactions.GrailsTransactionTemplate$1.doInTransaction(GrailsTransactionTemplate.groovy:72)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:137)
	at grails.gorm.transactions.GrailsTransactionTemplate.executeAndRollback(GrailsTransactionTemplate.groovy:69)
	at functionaltests.MultipleDataSourcesSpec.Test multiple data source persistence(MultipleDataSourcesSpec.groovy)
Caused by: org.hibernate.UnknownEntityTypeException: Unknown entity type 'ds2.Book' ('Book' is not annotated '@Entity')
	at org.hibernate.internal.SessionImpl.getEntityPersister(SessionImpl.java:1619)
	at org.hibernate.engine.internal.ForeignKeys.isTransient(ForeignKeys.java:313)
	at org.hibernate.event.internal.EntityState.getEntityState(EntityState.java:54)
	at org.hibernate.event.internal.DefaultPersistEventListener.persist(DefaultPersistEventListener.java:82)
	at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:74)
	at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:50)
	at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:138)
	at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:692)
	... 31 more
Caused by: org.hibernate.UnknownEntityTypeException: Unknown entity type 'ds2.Book'
	at org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl.getEntityDescriptor(MappingMetamodelImpl.java:326)
	at org.hibernate.internal.AbstractSharedSessionContract.requireEntityPersister(AbstractSharedSessionContract.java:1111)
	at org.hibernate.internal.SessionImpl.getEntityPersister(SessionImpl.java:1595)
	at org.hibernate.internal.SessionImpl.getEntityPersister(SessionImpl.java:1609)
	... 38 more
PartitionedMultiTenancySpec > Test partitioned multi tenancy (:grails-data-hibernate5-core:test in CI - Groovy Joint Validation Build / build_grails)
Condition failed with Exception:

MultiTenantAuthor.withTenant("moreBooks").count() == 2
|                 |                       |
|                 |                       java.lang.IllegalArgumentException: Not an entity: class org.grails.orm.hibernate.connections.MultiTenantAuthor
|                 |                       	at org.hibernate.metamodel.internal.MetamodelImpl.entity(MetamodelImpl.java:567)
|                 |                       	at org.hibernate.query.criteria.internal.QueryStructure.from(QueryStructure.java:128)
|                 |                       	at org.hibernate.query.criteria.internal.CriteriaQueryImpl.from(CriteriaQueryImpl.java:158)
|                 |                       	at org.grails.orm.hibernate.AbstractHibernateGormStaticApi.count_closure6(AbstractHibernateGormStaticApi.groovy:238)
|                 |                       	at org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:331)
|                 |                       	at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:275)
|                 |                       	at org.grails.orm.hibernate.GrailsHibernateTemplate.executeWithNewSession(GrailsHibernateTemplate.java:176)
|                 |                       	at org.grails.orm.hibernate.GrailsHibernateTemplate.executeWithExistingOrCreateNewSession(GrailsHibernateTemplate.java:230)
|                 |                       	at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:143)
|                 |                       	at org.grails.orm.hibernate.AbstractHibernateGormStaticApi.count(AbstractHibernateGormStaticApi.groovy:235)
|                 |                       	at org.grails.orm.hibernate.connections.PartitionedMultiTenancySpec.$tt__$spock_feature_0_0(PartitionedMultiTenancySpec.groovy:127)
|                 |                       	at groovy.lang.Closure.call(Closure.java:433)
|                 |                       	at groovy.lang.Closure.call(Closure.java:422)
|                 |                       	at grails.gorm.transactions.GrailsTransactionTemplate$1.doInTransaction(GrailsTransactionTemplate.groovy:72)
|                 |                       	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:137)
|                 |                       	at grails.gorm.transactions.GrailsTransactionTemplate.executeAndRollback(GrailsTransactionTemplate.groovy:69)
|                 <org.grails.orm.hibernate.HibernateGormStaticApi@128f487d identityType=null classLoader=jdk.internal.loader.ClassLoaders$AppClassLoader@5a07e868 instanceApi=null defaultFlushMode=0 finders=[] persistentClass=class org.grails.orm.hibernate.connections.MultiTenantAuthor registry=org.grails.datastore.gorm.GormRegistry@10f1cc85 qualifier=moreBooks mappingContext=org.grails.orm.hibernate.cfg.HibernateMappingContext@7a2b8e4c methods=null extendedMethods=null datastoreResolver=org.grails.orm.hibernate.HibernateGormStaticApi$1@120fedd4>
class org.grails.orm.hibernate.connections.MultiTenantAuthor

	at org.grails.orm.hibernate.connections.PartitionedMultiTenancySpec.$tt__$spock_feature_0_0(PartitionedMultiTenancySpec.groovy:127)
	at org.grails.orm.hibernate.connections.PartitionedMultiTenancySpec.Test partitioned multi tenancy_closure4(PartitionedMultiTenancySpec.groovy)
	at groovy.lang.Closure.call(Closure.java:433)
	at groovy.lang.Closure.call(Closure.java:422)
	at grails.gorm.transactions.GrailsTransactionTemplate$1.doInTransaction(GrailsTransactionTemplate.groovy:72)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:137)
	at grails.gorm.transactions.GrailsTransactionTemplate.executeAndRollback(GrailsTransactionTemplate.groovy:69)
	at org.grails.orm.hibernate.connections.PartitionedMultiTenancySpec.Test partitioned multi tenancy(PartitionedMultiTenancySpec.groovy)
Caused by: java.lang.IllegalArgumentException: Not an entity: class org.grails.orm.hibernate.connections.MultiTenantAuthor
	at org.hibernate.metamodel.internal.MetamodelImpl.entity(MetamodelImpl.java:567)
	at org.hibernate.query.criteria.internal.QueryStructure.from(QueryStructure.java:128)
	at org.hibernate.query.criteria.internal.CriteriaQueryImpl.from(CriteriaQueryImpl.java:158)
	at org.grails.orm.hibernate.AbstractHibernateGormStaticApi.count_closure6(AbstractHibernateGormStaticApi.groovy:238)
	at org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:331)
	at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:275)
	at org.grails.orm.hibernate.GrailsHibernateTemplate.executeWithNewSession(GrailsHibernateTemplate.java:176)
	at org.grails.orm.hibernate.GrailsHibernateTemplate.executeWithExistingOrCreateNewSession(GrailsHibernateTemplate.java:230)
	at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:143)
	at org.grails.orm.hibernate.AbstractHibernateGormStaticApi.count(AbstractHibernateGormStaticApi.groovy:235)
	at org.grails.orm.hibernate.connections.PartitionedMultiTenancySpec.$tt__$spock_feature_0_0(PartitionedMultiTenancySpec.groovy:127)
	at groovy.lang.Closure.call(Closure.java:433)
	at groovy.lang.Closure.call(Closure.java:422)
	at grails.gorm.transactions.GrailsTransactionTemplate$1.doInTransaction(GrailsTransactionTemplate.groovy:72)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:137)
	at grails.gorm.transactions.GrailsTransactionTemplate.executeAndRollback(GrailsTransactionTemplate.groovy:69)

Muted Tests

Select tests to mute in this pull request:

  • CommentIntegrationSpec > test querying a comment with only the parent id
  • DatabasePerTenantIntegrationSpec > Test database per tenant
  • DatabasePerTenantIntegrationSpec > test saveBook with normal service
  • DatabasePerTenantSpec > Test database per tenant
  • DatabasePerTenantSpec > Test should rollback changes in a previous test
  • MultipleDataSourcesSpec > Test multiple data source persistence
  • PartitionedMultiTenancySpec > Test partitioned multi tenancy
  • ProxySpec > Test Proxy
  • SchemaPerTenantIntegrationSpec > Test database per tenant
  • SchemaPerTenantSpec > Test database per tenant
  • SchemaPerTenantSpec > Test should rollback changes in a previous test
  • TagIntegrationSpec > test a custom property can reference a domain with using joins
  • UserRoleIntegrationSpec > test reading an entity with a complex composite id

Reuse successful test results:

  • ♻️ Only rerun the tests that failed or were muted before

Click the checkbox to trigger a rerun:

  • Rerun jobs

Learn more about TestLens at testlens.app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant