You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by jd...@apache.org on 2026/05/14 12:38:38 UTC
(grails-core) branch 8.0.x-hibernate7 updated: Remove completed AI artifacts
This is an automated email from the ASF dual-hosted git repository.
jdaugherty pushed a commit to branch 8.0.x-hibernate7
in repository https://gitbox.apache.org/repos/asf/grails-core.git
The following commit(s) were added to refs/heads/8.0.x-hibernate7 by this push:
new 13e62bbfe8 Remove completed AI artifacts
13e62bbfe8 is described below
commit 13e62bbfe816fa208ee8c06d53003f9f24910306
Author: James Daugherty <jd...@jdresources.net>
AuthorDate: Thu May 14 08:38:30 2026 -0400
Remove completed AI artifacts
---
grails-data-hibernate7/AGENTS.md | 217 ---------------------------------------
grails-data-hibernate7/ISSUES.md | 146 --------------------------
2 files changed, 363 deletions(-)
diff --git a/grails-data-hibernate7/AGENTS.md b/grails-data-hibernate7/AGENTS.md
deleted file mode 100644
index cc06154c43..0000000000
--- a/grails-data-hibernate7/AGENTS.md
+++ /dev/null
@@ -1,217 +0,0 @@
-<!--
- 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.
--->
-
-# HIBERNATE7-UPGRADE-PROGRESS.md
-
-## Completed: GrailsPropertyBinder Simplification
-
-**Objective:** Refactor the `GrailsPropertyBinder` class to consolidate the binder application logic into a single, unified conditional structure, reducing redundancy and improving code readability.
-
-**Status: COMPLETED**
-
-The `bindProperty` method in `GrailsPropertyBinder.java` has been successfully refactored. The core binder application logic is now contained within a single primary conditional block that dispatches to specific binders based on the GORM property type.
-
-**Key Changes:**
-- **Consolidated Dispatcher:** Introduced a single `if-else if` chain in `GrailsPropertyBinder.bindProperty` that returns a Hibernate `Value`.
-- **Centralized Property Creation:** The creation and addition of the Hibernate `Property` have been moved to the callers (e.g., `ClassPropertiesBinder`, `ComponentUpdater`, `CompositeIdBinder`) using the `PropertyFromValueCreator` utility. This ensures a single, unified entry point for property creation across different binding scenarios.
-- **Redundancy Removed:** Replaced scattered `createProperty` and `addProperty` calls with a consistent pattern, significantly improving maintainability.
-
-## GrailsDomainBinder Analysis
-
-**Objective:** Document the core components and dependencies used by `GrailsDomainBinder` during the Hibernate 7 mapping process.
-
-`GrailsDomainBinder` is the central entry point for binding Grails domain classes to the Hibernate meta-model. It coordinates various specialized binder classes to handle entities, properties, identifiers, and collections.
-
-### Core Dependencies (org.grails.orm.hibernate.cfg.*)
-
-#### Logical Domain Mapping (cfg package)
-* **GrailsHibernatePersistentEntity**: Core interface extending `PersistentEntity` with Hibernate-specific mapping capabilities (discriminators, data sources, etc.).
-* **HibernatePersistentEntity**: Default implementation of `GrailsHibernatePersistentEntity`.
-* **Mapping**: Groovy DSL representation of GORM mapping configurations.
-* **HibernateMappingContext**: Specialized `MappingContext` for Hibernate.
-* **HibernateMappingContextConfiguration**: Coordinates the creation of Hibernate `Metadata` and `SessionFactory` using GORM entities.
-* **PersistentEntityNamingStrategy**: Strategy interface for resolving physical names (tables, columns).
-* **NamingStrategyWrapper**: Wraps Hibernate's `PhysicalNamingStrategy` for GORM usage.
-* **MappingCacheHolder**: Singleton used to cache `Mapping` instances for entities to avoid repeated DSL evaluation.
-
-#### Property & Value Binding (cfg.domainbinding package)
-* **GrailsPropertyBinder**: Main coordinator for binding individual persistent properties to Hibernate `Value` objects.
-* **PropertyBinder**: Binds Hibernate `Property` objects, handling updatable/insertable flags.
-* **SimpleValueBinder**: Binds simple types (String, Integer, etc.) to Hibernate `BasicValue`.
-* **SimpleValueColumnBinder**: Handles the binding of columns to `SimpleValue` instances.
-* **ComponentPropertyBinder**: Specialized binder for GORM embedded components.
-* **ComponentBinder**: Binds Hibernate `Component` instances.
-* **EnumTypeBinder**: Handles the binding of Java Enums using `GrailsEnumType`.
-* **OneToOneBinder / ManyToOneBinder**: Handle GORM associations and their corresponding Hibernate mappings.
-* **ManyToOneValuesBinder**: Specifically handles the `Value` binding for many-to-one associations.
-* **CollectionBinder**: Handles GORM collections (Set, List, Map) and their Hibernate `Collection` mappings.
-* **PropertyFromValueCreator**: Utility to create Hibernate `Property` instances from a `Value`.
-
-#### Identifier & Version Binding (cfg.domainbinding package)
-* **IdentityBinder**: Main coordinator for binding entity identifiers (simple or composite).
-* **SimpleIdBinder**: Binds simple primary keys.
-* **CompositeIdBinder**: Binds composite primary keys.
-* **BasicValueCreator**: Factory for creating identifier `Value` objects and their generators.
-* **VersionBinder**: Binds the version property used for optimistic locking.
-* **NaturalIdentifierBinder**: Binds properties marked as `naturalId`.
-
-#### ID Generators (cfg.domainbinding.generator package)
-* **GrailsSequenceWrapper**: Wraps Hibernate 7 generator creation.
-* **GrailsSequenceGeneratorEnum**: Enum mapping Grails generator names to Hibernate 7 `Generator` implementations.
-* **GrailsIdentityGenerator / GrailsIncrementGenerator / GrailsNativeGenerator / GrailsSequenceStyleGenerator / GrailsTableGenerator**: GORM-specific extensions of Hibernate 7 generators.
-
-#### Sub-mapping & Collection Types (cfg.domainbinding.collectionType package)
-* **CollectionHolder**: Context object passed through binders to maintain collection state.
-* **ListCollectionType / SetCollectionType / MapCollectionType / BagCollectionType**: Metadata classes defining how different GORM collections are mapped.
-
-#### Second Pass Binding (cfg.domainbinding.secondpass package)
-* **GrailsSecondPass**: Base interface for binding operations that must occur after all entities are initially processed.
-* **CollectionSecondPassBinder / ListSecondPassBinder / MapSecondPassBinder**: Implementations handling the final binding of associations and collection elements.
-
-#### Miscellaneous Utilities
-* **NamespaceNameExtractor**: Extracts schema and catalog information from Hibernate metadata.
-* **TableNameFetcher**: Resolves the table name for a given entity using the naming strategy.
-* **DefaultColumnNameFetcher**: Resolves default column names for properties.
-* **ColumnNameForPropertyAndPathFetcher**: Resolves column names considering embedded paths.
-* **BackticksRemover**: Utility for handling database identifiers with quotes. Replaced redundant `BackTigsTrimmer`.
-* **ConfigureDerivedPropertiesConsumer**: Applies `derived` flag to properties based on mapping.
-* **GrailsHibernateUtil**: General utility methods for Hibernate integration.
-
-### Migration Status Breakdown
-
-#### Main Classes
-
-| Class | Package | Status | Notes |
-| :--- | :--- | :--- | :--- |
-| `GrailsDomainBinder` | `org.grails.orm.hibernate.cfg` | Migrated | Main entry point for domain binding. Implements `AdditionalMappingContributor`, `TypeContributor`. |
-| `HibernateMappingContext` | `org.grails.orm.hibernate.cfg` | Migrated | |
-| `GrailsHibernatePersistentEntity` | `org.grails.orm.hibernate.cfg` | Migrated | |
-| `GrailsHibernatePersistentProperty` | `org.grails.orm.hibernate.cfg` | Migrated | |
-| `GrailsHibernateUtil` | `org.grails.orm.hibernate.cfg` | Migrated | |
-| `MappingCacheHolder` | `org.grails.orm.hibernate.cfg` | Migrated | |
-| `PersistentEntityNamingStrategy` | `org.grails.orm.hibernate.cfg` | Migrated | |
-| `NamingStrategyWrapper` | `org.grails.orm.hibernate.cfg.domainbinding` | Migrated | |
-
-#### Binders (`org.grails.orm.hibernate.cfg.domainbinding`)
-
-| Class | Status | Notes |
-| :--- | :--- | :--- |
-| `ClassBinder` | Migrated | Binds `PersistentClass` basic info. |
-| `EnumTypeBinder` | Migrated | |
-| `PropertyFromValueCreator` | Migrated | |
-| `ComponentPropertyBinder` | Migrated | |
-| `GrailsPropertyBinder` | Migrated | Simplified and consolidated. |
-| `CollectionBinder` | Migrated | |
-| `CompositeIdBinder` | Migrated | |
-| `IdentityBinder` | Migrated | |
-| `VersionBinder` | Migrated | |
-| `SimpleValueBinder` | Migrated | |
-| `OneToOneBinder` | Migrated | |
-| `ManyToOneBinder` | Migrated | |
-| `ColumnBinder` | Migrated | |
-| `ColumnConfigToColumnBinder` | Migrated | |
-| `SimpleValueColumnBinder` | Migrated | |
-| `NaturalIdentifierBinder` | Migrated | |
-| `IndexBinder` | Migrated | |
-| `ComponentBinder` | Migrated | |
-| `SimpleIdBinder` | Migrated | |
-| `SimpleValueBinder` | Migrated | |
-
-#### Collection Types (`org.grails.orm.hibernate.cfg.domainbinding.collectionType`)
-
-| Class | Status | Notes |
-| :--- | :--- | :--- |
-| `CollectionHolder` | Migrated | |
-| `BagCollectionType` | Migrated | |
-| `ListCollectionType` | Migrated | |
-| `MapCollectionType` | Migrated | |
-| `SetCollectionType` | Migrated | |
-| `SortedSetCollectionType` | Migrated | |
-
-#### Second Pass Binders (`org.grails.orm.hibernate.cfg.domainbinding.secondpass`)
-
-| Class | Status | Notes |
-| :--- | :--- | :--- |
-| `CollectionSecondPassBinder` | Migrated | Unidirectional many-to-many support implemented. |
-| `GrailsSecondPass` | Migrated | |
-| `ListSecondPass` | Migrated | |
-| `ListSecondPassBinder` | Migrated | |
-| `MapSecondPass` | Migrated | |
-| `MapSecondPassBinder` | Migrated | |
-| `SetSecondPass` | Migrated | |
-
-#### Generators (`org.grails.orm.hibernate.cfg.domainbinding` and `generator` subpackage)
-
-| Class | Status | Notes |
-| :--- | :--- | :--- |
-| `GrailsIdentityGenerator` | Migrated | |
-| `GrailsIncrementGenerator` | Migrated | Contains reflection hacks for Hibernate 7, to be removed in Hibernate 8. |
-| `GrailsNativeGenerator` | Migrated | |
-| `GrailsSequenceStyleGenerator` | Migrated | |
-| `GrailsTableGenerator` | Migrated | |
-| `GrailsSequenceGeneratorEnum` | Migrated | In `generator` subpackage. |
-| `GrailsSequenceWrapper` | Migrated | In `generator` subpackage. |
-
-#### Fetchers and Utilities (`org.grails.orm.hibernate.cfg.domainbinding`)
-
-| Class | Status | Notes |
-| :--- | :--- | :--- |
-| `ColumnNameForPropertyAndPathFetcher` | Migrated | |
-| `TableNameFetcher` | Migrated | |
-| `DefaultColumnNameFetcher` | Migrated | |
-| `SimpleValueColumnFetcher` | Migrated | |
-| `CascadeBehaviorFetcher` | Migrated | |
-| `NamespaceNameExtractor` | Migrated | |
-| `ForeignKeyColumnCountCalculator` | Migrated | |
-| `TableForManyCalculator` | Migrated | |
-| `UniqueNameGenerator` | Migrated | |
-| `BackticksRemover` | Migrated | |
-| `BasicValueCreator` | Migrated | |
-
-## Utility Class Refactoring & Mock Compatibility
-
-**Objective:** Modernize utility classes in `domainbinding.util` to use Hibernate-specific GORM types while maintaining compatibility with Spock mocks.
-
-**Summary of Changes:**
-- **Refactored Utility Classes:** Updated `CreateKeyForProps`, `TableForManyCalculator`, `DefaultColumnNameFetcher`, `ConfigureDerivedPropertiesConsumer`, and `NamingStrategyWrapper` to use `GrailsHibernatePersistentProperty` and `GrailsHibernatePersistentEntity` where possible.
-- **Mock Compatibility Fixes:** Addressed `ClassCastException` in Spock specs by:
- - Reverting public method signatures to use base interfaces (`PersistentProperty`, `PersistentEntity`) where required by mocks.
- - Implementing internal safe casting using `instanceof` pattern matching.
- - Updating test stubs to include `additionalInterfaces: [GrailsHibernatePersistentProperty]`.
-- **Logic Improvements:**
- - Updated `getDiscriminatorValue` in `GrailsHibernatePersistentEntity` to default to `getJavaClass().getSimpleName()` to match GORM conventions and test expectations.
- - Fixed `getMultiTenantFilterCondition` to safely handle non-Hibernate tenantId properties in test environments.
-- **Verification:** Verified that all 1045 tests in `:grails-data-hibernate7-core` are passing, confirming that the refactorings and modernizations have not introduced regressions.
-
-## Remaining Known Issues / TODOs
-
-- `GrailsIncrementGenerator`: Reflection hacks for Hibernate 7 (scheduled for removal in Hibernate 8).
-
-## Testing Guidelines
-
-* **HibernateGormDatastoreSpec**: Use `HibernateGormDatastoreSpec` for all Hibernate 7 integration and domain binding specifications. Unit tests in Hibernate 7 cannot effectively mock Hibernate internal classes; this spec provides the necessary environment.
-* **Entity Creation**: While `GrailsHibernatePersistentEntity` can be created manually using `createHibernatePersistentEntity`, it is more convenient to use `manager.addAllDomainClasses([...])` within `setupSpec()` to register entities for the test.
-* **Top-Level Entities**: Entity classes used in tests must be defined as top-level classes within the same Groovy file as the specification.
-* **Unique Class Names**: Ensure all domain classes defined within a spec have globally unique names within the package to avoid collisions with other test classes during parallel execution.
-* **Real Entities**: Prefer using real entities defined as top-level classes in the spec file over heavy mocking when testing binder logic.
-
-## Resolved Issues
-
-- `CollectionSecondPassBinder`: Unidirectional many-to-many support implemented.
-- **Multitenancy & CompositeId:** `MultiTenancyBidirectionalManyToManySpec` and `GlobalConstraintWithCompositeIdSpec` have been fixed and validated.
diff --git a/grails-data-hibernate7/ISSUES.md b/grails-data-hibernate7/ISSUES.md
deleted file mode 100644
index b6d092f0bd..0000000000
--- a/grails-data-hibernate7/ISSUES.md
+++ /dev/null
@@ -1,146 +0,0 @@
-<!--
- 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.
--->
-# Resolved Issues: Memory Leak in Multi-Tenancy
-
-## STATUS: RESOLVED
-
-### Summary of Fixes
-1. **Flyweight Template Implementation:** `HibernateDatastore` now lazily initializes a single, shared `GrailsHibernateTemplate` instance. This instance is shared across all GORM API bridges (`HibernateGormStaticApi`, `HibernateGormInstanceApi`, `HibernateGormValidationApi`) and `HibernateSession`.
- - **Result:** 99.7% reduction in heavy template objects in multi-tenant environments.
-2. **Registry Cleanup Fix:** Corrected a bug in `GormEnhancer.close()` where datastore references were being leaked because the `remove()` call was using the `datastore` object instead of the `entityName` string key.
-3. **Static Map Optimization:** Refactored `GormEnhancer` to prevent map mutation via Groovy's `withDefault` during the cleanup phase.
-4. **Bootstrapping Stability:** Used lazy initialization for the shared template to ensure `SessionFactory` services are fully available before the template is created.
-
-### Verification Results
-1. **Flyweight Template:** Full TCK suite (2,923 tests) now passes. Distinct `GrailsHibernateTemplate` instances reduced from **12** to **4** in a 4-tenant test case.
- - **Absolute Saving:** ~149 GB projected for 1,000 tenants / 100 classes.
-2. **InstanceApiHelper Singleton:** Reduced `InstanceApiHelper` count from one-per-class to one-per-datastore.
- - **Absolute Saving:** ~99,000 objects (~3.1 MB) removed from heap tracking per 100,000 Class-Tenant pairs.
-3. **Registry Stability:** `GormEnhancer` now correctly purges datastores and entities, resulting in a stable memory floor across long-running test suites.
-
----
-
-## Post-Fix Relative Memory Footprint Analysis
-
-Even with the heavy `GrailsHibernateTemplate` shared, GORM maintains a high instance count for coordination objects. The estimated relative weight for a single **(Class × Tenant)** pair is:
-
-| Component | Relative Weight | % of Remainder | Reason for Persistence |
-| :--- | :--- | :--- | :--- |
-| **`HibernateGormStaticApi`** | **40%** | ~2 KB | Holds `List<FinderMethod>` and `HibernateSession`. |
-| **`HibernateSession`** | **25%** | ~1 KB | Maintains local references to `MappingContext`. |
-| **`HibernateGormInstanceApi`** | **20%** | ~1 KB | Holds a redundant `InstanceApiHelper`. |
-| **`HibernateGormValidationApi`** | **10%** | ~0.5 KB | Bridge references. |
-| **Registry Map Overhead** | **5%** | ~0.2 KB | `ConcurrentHashMap` bucket/node overhead. |
-
-### The "Death by a Thousand Cuts" (Scalability Residue)
-At a scale of 100 classes and 1,000 tenants, the system still pins **~400,000 coordination objects** in static memory.
-
-1. **Redundant `InstanceApiHelper`:** Re-instantiated in every `HibernateGormInstanceApi`. This is stateless logic and should be a singleton per datastore.
-2. **Redundant `HibernateSession`:** The static API holds a wrapper that could be shared or lazily initialized.
-3. **Map Key Bloat:** The full class name string is repeated as a key across thousands of map entries. Lack of String interning or integer-based indexing causes significant "shallow heap" waste.
-4. **Metaspace Inflation:** The sheer volume of Groovy Metaclass entries created for 100,000 combinations risks Metaspace exhaustion and degrades GC performance.
-
-## Current Scalability Limits & System Assertions
-
-The current GORM architecture (Hibernate 7 implementation) remains bounded by a **Tenant-Singleton** model. While recent flyweight optimizations have significantly reduced heap pressure, the underlying coordination mechanism poses the following hard limits:
-
-### Hard Scalability Limitations
-1. **Metaspace Fragmentation:** GORM relies on per-tenant `ExpandoMetaClass` modifications and registry entries. Metaspace is not reclaimed by Heap GC, creating a hard **"uptime ceiling"** for nodes.
-2. **Registry Bloat:** For every `(Domain Class × Tenant)` pair, the system pins 4 coordination objects in `static` memory. In a typical application with 100 domain classes, every 100 new tenants adds **40,000 long-lived objects** to the heap.
-3. **Eager Registration Ceiling:** Current code (post-stabilization) blindly expands qualifiers for all tenants during bootstrap. Without the safety ceiling (50k threshold), massive multi-tenant deployments will trigger OOM during the bootstrap phase.
-
-### Operational Assertions
-Based on the current implementation, we assert the following safe operating ranges:
-
-| Metric | Safe Range | High Risk Range | Critical Failure |
-| :--- | :--- | :--- | :--- |
-| **Total Domain Classes** | < 150 | 150 - 300 | > 500 |
-| **Total Tenants** | < 200 | 200 - 500 | > 1,000 |
-| **Total API Objects** | < 30,000 | 30k - 50,000 | > 100,000 |
-
-### Application Suitability
-- **Small-to-Medium Apps:** Fully supported and stable.
-- **Enterprise SaaS:** High risk. Nodes will require frequent restarts (weekly or monthly) to "flush" Metaspace fragmentation.
-- **Dynamic Schema SaaS:** **NOT SUPPORTED.** The lack of Tenant Lifecycle management (no eviction) leads to inevitable system failure as new schemas are added at runtime.
-
-### Immediate Requirement for June Release
-To stabilize these limits, the system **must** transition to a **Class-Singleton** orchestrator model where `GormStaticApi` instances are shared across all tenants, resolving the tenant context dynamically at method execution time. Until this transition is complete, the **50,000 API object ceiling** remains a mandatory safety requirement for production stability.
-
-### Root Cause
-1. **Static Registry:** `GormEnhancer` maintains four `static ConcurrentHashMap` objects (`STATIC_APIS`, `INSTANCE_APIS`, `VALIDATION_APIS`, `DATASTORES`) that hold strong references to API objects, keyed by the tenant qualifier.
-2. **Redundant Object Creation:** Every `HibernateGormStaticApi` instance creates its own `GrailsHibernateTemplate`. Each `GrailsHibernateTemplate` is a heavy object containing a `SQLErrorCodeSQLExceptionTranslator` (which loads extensive XML mappings).
-3. **Tenant-Class Explosion:** Memory usage grows at a rate of `(Number of Tenants) * (Number of Domain Classes) * (Size of GrailsHibernateTemplate)`. In a system with 100 classes and 1,000 tenants, this creates 100,000 heavy template objects.
-4. **Redundant Registration:** In `HibernateDatastore.java`, `addTenantForSchema` calls `registerAllEntitiesWithEnhancer()` repeatedly, which creates new API and template objects even if the tenant was already registered, rapidly exhausting the heap.
-5. **Broken `close()` Logic:** The `GormEnhancer.close()` method contains a logic error: `DATASTORES.get(q)?.remove(datastore)`. Since the inner map is keyed by **Entity Name** (String), passing the `datastore` (Object) fails to remove the entry, leaving the datastore reference in memory forever.
-6. **Groovy `withDefault` Leak:** `GormEnhancer` uses `ConcurrentHashMap.withDefault` for its static maps. In Groovy, `withDefault` is not just a fallback; it **mutates** the map by adding the default value for any accessed key. This causes registry growth during both registration and cleanup/lookup phases.
-7. **Criteria Session Leaks:** `HibernateCriteriaBuilder` automatically opens and binds Hibernate sessions if one is not present. In complex multi-tenant tests, if these builders are not explicitly closed, it leads to a build-up of open sessions and JPA Criteria builder state, contributing to the OOM.
-8. **Query-State and API Bridge Explosion:** The delegation chain and API initialization cause a combinatorial explosion of heavy state objects:
- - `HibernateGormStaticApi` creates a `HibernateSession` AND a `GrailsHibernateTemplate`.
- - `HibernateGormInstanceApi` creates its own `GrailsHibernateTemplate`.
- - `HibernateGormValidationApi` creates its own `GrailsHibernateTemplate`.
- - `HibernateSession` (used by bridges) creates its own `GrailsHibernateTemplate` in its constructor.
- - `HibernateHqlQueryCreator` creates a new `HibernateSession` for *every* HQL query (Select/Mutation).
- - `HibernateQuery` (used by Criteria) also creates its own `HibernateSession`.
-
- **Result:** For every (Domain Class, Tenant) pair, GORM is maintaining at least 4 redundant `GrailsHibernateTemplate` instances. Since each template holds a heavy `SQLErrorCodeSQLExceptionTranslator`, this leads to massive heap exhaustion in multi-tenant environments.
-
-## Memory Footprint Analysis (Verified via Profiling)
-
-Empirical testing using `System.identityHashCode` tracking of `GrailsHibernateTemplate` instances in `SchemaMultiTenantSpec` (4 tenants, 1 domain class) showed the following results:
-
-| Implementation | Distinct Template Instances | Formula |
-| :--- | :--- | :--- |
-| **Baseline (Broken)** | **12** | `(4 Tenants) * (1 Class) * (3 API Types)` |
-| **Flyweight Template (Fixed)** | **4** | `(4 Tenants) * (1 Shared Template per Datastore)` |
-
-**Projected Scalability (100 Classes + 1,000 Tenants):**
-- **Baseline:** 300,000 heavy template objects (~150 GB Heap)
-- **Flyweight Fix:** 1,000 shared template objects (~1 GB Heap) - **99.7% Reduction**
-
-### Remaining Challenges: API Object Proliferation
-While the template is now shared, GORM still creates unique `GormStaticApi`, `GormInstanceApi`, and `GormValidationApi` instances for every `(Class, Tenant)` pair. In a full test suite run (2,400+ tests), these "lightweight" wrappers still accumulate in `static` maps, eventually causing an OOM (observed in `mysql-cj-abandoned-connection-cleanup` thread after 2,474 tests).
-
-## Resource Leaks: Sessions and Connections
-Beyond heap exhaustion, the implementation poses a risk of **Database Connection Pool exhaustion**:
-- `HibernateCriteriaBuilder` and `HibernateHqlQueryCreator` frequently instantiate `HibernateSession`.
-- In multi-tenant environments where sessions are opened/closed rapidly, any failure to close a `HibernateSession` wrapper leaks the underlying Hibernate `Session` and its database connection.
-
-## Architectural Analysis: The Static-Dynamic Conflict
-
-The memory exhaustion observed is not a simple bug but a result of **Architectural Friction** between GORM's design and Hibernate 7's requirements:
-
-### 1. Static-at-Startup vs. Dynamic-at-Runtime
-GORM's "Magic" (e.g., `Book.list()`) relies on modifying Groovy Metaclasses at startup. In a non-multi-tenant app, this happens once. In multi-tenant modes (SCHEMA/DATABASE), GORM must provide a specific "personality" for every domain class for *every* tenant. GORM implements this by creating unique API instances per tenant. The framework is essentially "re-starting" its metadata engine for every new tenant discovered.
-
-### 2. The Hibernate 7 "Metamodel Tax"
-Hibernate 7 has significantly increased the complexity of its internal metamodel and JPA Criteria builder. While Hibernate 7 itself is highly optimized, the GORM "Bridge" (the API objects) has not been updated to use a **Flyweight Pattern**. Instead of sharing immutable metadata, each bridge object (StaticApi, InstanceApi) re-instantiates heavy Hibernate/Spring wrappers.
-
-### 3. Lack of Tenant Lifecycle Management
-Grails provides excellent **Tenant Resolution** (finding who the tenant is) but lacks **Tenant Lifecycle Management** (handling the "birth and death" of a tenant's metadata).
-- **The Accumulation:** Once a tenant is resolved and its metadata is "GORM-enhanced" and cached in the `GormEnhancer` statics, it is pinned there for the life of the JVM.
-- **The Explosion:** In dynamic environments (e.g., SaaS apps where schemas are added on-the-fly), the system has no mechanism to evict inactive tenant metadata, leading to inevitable heap exhaustion.
-
-### 4. Thread-Local "Memory Anchors"
-The use of `TransactionSynchronizationManager` to bind sessions in `HibernateCriteriaBuilder` creates "Memory Anchors." If a Criteria query is initialized but never executed or closed (common in complex DSL failures), the session — along with its heavy Hibernate 7 state — remains pinned to the **ThreadLocal** map. In thread-pooled environments (Tomcat/Jetty), this memory is never released and pollutes subsequent requests.
-
-### Proposed Fixes
-- **[COMPLETED] Flyweight Template:** Refactor `HibernateDatastore` to hold a single, shared instance of `GrailsHibernateTemplate`.
-- **[COMPLETED] API Bridge Refactoring:** Updated `HibernateGormStaticApi`, `HibernateGormInstanceApi`, and `HibernateGormValidationApi` to receive the shared template.
-- **[COMPLETED] Fix `close()` Bug:** Corrected the `DATASTORES.get(q)?.remove(datastore)` logic in `GormEnhancer`.
-- **Refactor `GormEnhancer`:** Move away from static maps to instance-based maps managed by the `Datastore` instance.
-- **LRU/Weak Cache:** Implement a `WeakHashMap` or a LRU cache for tenant-specific API objects.
-- **[COMPLETED] InstanceApiHelper Singleton:** Refactored `InstanceApiHelper` to be a singleton per datastore instead of per-class.