Skip to content

Commit fc957ec

Browse files
Ivaylo MitrevSanne
authored andcommitted
HHH-14948 - Reduce the size of the imports cache in the metamodel
We have agreed to mitigate a memory issue by introducing a hard-coded (for the time being) cap on the imports cache in MetamodelImpl. This would preserve the previously available performance improvement for the majority of applications using Hibernate (which are expected to have less than 1000 imports) while mitigating an unbounded memory consumption issue for applications that exceed this limit when dynamically generating queries with random entity aliases. The commit also adds a not-so-beautiful test, which, however, is beneficial considering the consequences of introduing a regression.
1 parent f6f7654 commit fc957ec

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetamodelImpl.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,13 @@ public String getImportedClassName(String className) {
632632
return className;
633633
}
634634
catch ( ClassLoadingException cnfe ) {
635-
imports.put( className, INVALID_IMPORT );
635+
// This check doesn't necessarily mean that the map can't exceed 1000 elements because
636+
// new entries might be added _while_ performing the check (making it 1000+ since size() isn't
637+
// synchronized). Regardless, this would pass as "good enough" to prevent the map from growing
638+
// above a certain threshold, thus, avoiding memory issues.
639+
if ( imports.size() < 1_000 ) {
640+
imports.put( className, INVALID_IMPORT );
641+
}
636642
return null;
637643
}
638644
}

hibernate-core/src/test/java/org/hibernate/test/hql/QuerySplitterTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@
66
*/
77
package org.hibernate.test.hql;
88

9+
import java.lang.reflect.Field;
10+
import java.util.Map;
11+
import java.util.stream.IntStream;
912
import javax.persistence.Entity;
1013
import javax.persistence.Id;
1114
import javax.persistence.Table;
1215

1316
import org.hibernate.hql.internal.QuerySplitter;
17+
import org.hibernate.metamodel.internal.MetamodelImpl;
1418

1519
import org.hibernate.testing.TestForIssue;
1620
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
@@ -47,6 +51,29 @@ public void testQueryWithEntityNameAsStringLiteral2() {
4751
);
4852
}
4953

54+
@Test
55+
@TestForIssue(jiraKey = "HHH-14948")
56+
public void testMemoryConsumptionOfFailedImportsCache() throws NoSuchFieldException, IllegalAccessException {
57+
58+
IntStream.range( 0, 1001 )
59+
.forEach( i -> QuerySplitter.concreteQueries(
60+
"from Employee e join e.company" + i,
61+
sessionFactory()
62+
) );
63+
64+
MetamodelImpl metamodel = (MetamodelImpl) sessionFactory().getMetamodel();
65+
66+
Field field = MetamodelImpl.class.getDeclaredField( "imports" );
67+
field.setAccessible( true );
68+
69+
//noinspection unchecked
70+
Map<String, String> imports = (Map<String, String>) field.get( metamodel );
71+
72+
// VERY hard-coded, but considering the possibility of a regression of a memory-related issue,
73+
// it should be worth it
74+
assertEquals( 1000, imports.size() );
75+
}
76+
5077
@Test
5178
@TestForIssue(jiraKey = "HHH-7973" )
5279
public void testQueryWithEntityNameAsStringLiteralAndEscapeQuoteChar() {

0 commit comments

Comments
 (0)