|
12 | 12 | import jakarta.persistence.Table; |
13 | 13 | import org.hibernate.cfg.QuerySettings; |
14 | 14 | import org.hibernate.query.hql.HqlTranslator; |
15 | | -import org.hibernate.query.sqm.tree.SqmStatement; |
| 15 | +import org.hibernate.testing.memory.MemoryUsageUtil; |
16 | 16 | import org.hibernate.testing.orm.junit.DomainModel; |
17 | 17 | import org.hibernate.testing.orm.junit.Jira; |
18 | 18 | import org.hibernate.testing.orm.junit.ServiceRegistry; |
|
41 | 41 | @Jira("https://hibernate.atlassian.net/browse/HHH-19240") |
42 | 42 | public class HqlParserMemoryUsageTest { |
43 | 43 |
|
44 | | - private static final String HQL = """ |
45 | | - SELECT DISTINCT u.id |
46 | | - FROM AppUser u |
47 | | - LEFT JOIN u.addresses a |
48 | | - LEFT JOIN u.orders o |
49 | | - LEFT JOIN o.orderItems oi |
50 | | - LEFT JOIN oi.product p |
51 | | - LEFT JOIN p.discounts d |
52 | | - WHERE u.id = :userId |
53 | | - AND ( |
54 | | - CASE |
55 | | - WHEN u.name = 'SPECIAL_USER' THEN TRUE |
56 | | - ELSE ( |
57 | | - CASE |
58 | | - WHEN a.city = 'New York' THEN TRUE |
59 | | - ELSE ( |
60 | | - p.category.name = 'Electronics' |
61 | | - OR d.code LIKE '%DISC%' |
62 | | - OR u.id IN ( |
63 | | - SELECT u2.id |
64 | | - FROM AppUser u2 |
65 | | - JOIN u2.orders o2 |
66 | | - JOIN o2.orderItems oi2 |
67 | | - JOIN oi2.product p2 |
68 | | - WHERE p2.price > ( |
69 | | - SELECT AVG(p3.price) FROM Product p3 |
70 | | - ) |
71 | | - ) |
72 | | - ) |
73 | | - END |
74 | | - ) |
75 | | - END |
76 | | - ) |
77 | | - """; |
| 44 | + private static final String HQL = "SELECT DISTINCT u.id\n" + |
| 45 | + "FROM AppUser u\n" + |
| 46 | + "LEFT JOIN u.addresses a\n" + |
| 47 | + "LEFT JOIN u.orders o\n" + |
| 48 | + "LEFT JOIN o.orderItems oi\n" + |
| 49 | + "LEFT JOIN oi.product p\n" + |
| 50 | + "LEFT JOIN p.discounts d\n" + |
| 51 | + "WHERE u.id = :userId\n" + |
| 52 | + "AND (\n" + |
| 53 | + " CASE\n" + |
| 54 | + " WHEN u.name = 'SPECIAL_USER' THEN TRUE\n" + |
| 55 | + " ELSE (\n" + |
| 56 | + " CASE\n" + |
| 57 | + " WHEN a.city = 'New York' THEN TRUE\n" + |
| 58 | + " ELSE (\n" + |
| 59 | + " p.category.name = 'Electronics'\n" + |
| 60 | + " OR d.code LIKE '%DISC%'\n" + |
| 61 | + " OR u.id IN (\n" + |
| 62 | + " SELECT u2.id\n" + |
| 63 | + " FROM AppUser u2\n" + |
| 64 | + " JOIN u2.orders o2\n" + |
| 65 | + " JOIN o2.orderItems oi2\n" + |
| 66 | + " JOIN oi2.product p2\n" + |
| 67 | + " WHERE p2.price > (\n" + |
| 68 | + " SELECT AVG(p3.price) FROM Product p3\n" + |
| 69 | + " )\n" + |
| 70 | + " )\n" + |
| 71 | + " )\n" + |
| 72 | + " END\n" + |
| 73 | + " )\n" + |
| 74 | + " END\n" + |
| 75 | + ")\n"; |
78 | 76 |
|
79 | 77 |
|
80 | 78 | @Test |
81 | 79 | public void testParserMemoryUsage(SessionFactoryScope scope) { |
82 | 80 | final HqlTranslator hqlTranslator = scope.getSessionFactory().getQueryEngine().getHqlTranslator(); |
83 | | - final Runtime runtime = Runtime.getRuntime(); |
84 | 81 |
|
85 | 82 | // Ensure classes and basic stuff is initialized in case this is the first test run |
86 | 83 | hqlTranslator.translate( "from AppUser", AppUser.class ); |
87 | | - runtime.gc(); |
88 | | - runtime.gc(); |
89 | | - |
90 | | - // Track memory usage before execution |
91 | | - long totalMemoryBefore = runtime.totalMemory(); |
92 | | - long usedMemoryBefore = totalMemoryBefore - runtime.freeMemory(); |
93 | | - |
94 | | - System.out.println("Memory Usage Before Create Query:"); |
95 | | - System.out.println("----------------------------"); |
96 | | - System.out.println("Total Memory: " + (totalMemoryBefore / 1024) + " KB"); |
97 | | - System.out.println("Used Memory : " + (usedMemoryBefore / 1024) + " KB"); |
98 | | - System.out.println(); |
99 | | - |
100 | | - // Create query |
101 | | - SqmStatement<Long> statement = hqlTranslator.translate( HQL, Long.class ); |
102 | | - |
103 | | - // Track memory usage after execution |
104 | | - long totalMemoryAfter = runtime.totalMemory(); |
105 | | - long usedMemoryAfter = totalMemoryAfter - runtime.freeMemory(); |
106 | | - |
107 | | - System.out.println("Memory Usage After Create Query:"); |
108 | | - System.out.println("----------------------------"); |
109 | | - System.out.println("Total Memory: " + (totalMemoryAfter / 1024) + " KB"); |
110 | | - System.out.println("Used Memory : " + (usedMemoryAfter / 1024) + " KB"); |
111 | | - System.out.println(); |
112 | | - |
113 | | - System.out.println("Memory increase After Parsing:"); |
114 | | - System.out.println("----------------------------"); |
115 | | - System.out.println("Total Memory increase: " + ((totalMemoryAfter - totalMemoryBefore) / 1024) + " KB"); |
116 | | - System.out.println("Used Memory increase : " + ((usedMemoryAfter - usedMemoryBefore) / 1024) + " KB"); |
117 | | - System.out.println(); |
118 | | - |
119 | | - runtime.gc(); |
120 | | - runtime.gc(); |
121 | | - |
122 | | - // Track memory usage after execution |
123 | | - long totalMemoryAfterGc = runtime.totalMemory(); |
124 | | - long usedMemoryAfterGc = totalMemoryAfterGc - runtime.freeMemory(); |
125 | | - |
126 | | - System.out.println("Memory Usage After Create Query and GC:"); |
127 | | - System.out.println("----------------------------"); |
128 | | - System.out.println("Total Memory: " + (totalMemoryAfterGc / 1024) + " KB"); |
129 | | - System.out.println("Used Memory : " + (usedMemoryAfterGc / 1024) + " KB"); |
130 | | - System.out.println(); |
131 | | - |
132 | | - System.out.println("Memory overhead of Parsing:"); |
133 | | - System.out.println("----------------------------"); |
134 | | - System.out.println("Total Memory increase: " + ((totalMemoryAfter - totalMemoryAfterGc) / 1024) + " KB"); |
135 | | - System.out.println("Used Memory increase : " + ((usedMemoryAfter - usedMemoryAfterGc) / 1024) + " KB"); |
136 | | - System.out.println(); |
137 | 84 |
|
138 | 85 | // During testing, before the fix for HHH-19240, the allocation was around 500+ MB, |
139 | 86 | // and after the fix it dropped to 170 - 250 MB |
140 | | - final long memoryConsumption = usedMemoryAfter - usedMemoryAfterGc; |
141 | | - assertTrue( usedMemoryAfter - usedMemoryAfterGc < 256_000_000, "Parsing of queries consumes too much memory (" + ( memoryConsumption / 1024 ) + " KB), when at most 256 MB are expected" ); |
| 87 | + final long memoryUsage = MemoryUsageUtil.estimateMemoryUsage( () -> hqlTranslator.translate( HQL, Long.class ) ); |
| 88 | + System.out.println( "Memory Consumption: " + (memoryUsage / 1024) + " KB" ); |
| 89 | + assertTrue( memoryUsage < 256_000_000, "Parsing of queries consumes too much memory (" + ( memoryUsage / 1024 ) + " KB), when at most 256 MB are expected" ); |
142 | 90 | } |
143 | 91 |
|
144 | 92 | @Entity(name = "Address") |
|
0 commit comments