Skip to content

Commit 2e9c5d8

Browse files
committed
Merge branch 'query-cache-oom-for-too-large-beans'
2 parents 3431178 + 93edc84 commit 2e9c5d8

File tree

8 files changed

+107
-10
lines changed

8 files changed

+107
-10
lines changed

ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.ebeaninternal.api;
22

3+
import io.ebean.bean.EntityBean;
34
import java.util.ArrayList;
45
import java.util.List;
56

@@ -13,10 +14,23 @@ public final class BindValuesKey {
1314

1415
private final List<Object> values = new ArrayList<>();
1516

17+
private final SpiEbeanServer server;
18+
19+
public BindValuesKey(SpiEbeanServer server) {
20+
this.server = server;
21+
}
22+
1623
/**
1724
* Add a bind value.
1825
*/
1926
public BindValuesKey add(Object value) {
27+
if (value instanceof EntityBean) {
28+
// only interested in id to keep the memory footprint low
29+
Object id = server.beanId(value);
30+
if (id != null) {
31+
value = id;
32+
}
33+
}
2034
values.add(value);
2135
return this;
2236
}
@@ -31,5 +45,4 @@ public int hashCode() {
3145
return values.hashCode();
3246
}
3347

34-
3548
}

ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1244,7 +1244,7 @@ public final void queryBindKey(BindValuesKey key) {
12441244
public final HashQuery queryHash() {
12451245
// calculateQueryPlanHash is called just after potential AutoTune tuning
12461246
// so queryPlanHash is calculated well before this method is called
1247-
BindValuesKey bindKey = new BindValuesKey();
1247+
BindValuesKey bindKey = new BindValuesKey(server);
12481248
queryBindKey(bindKey);
12491249
return new HashQuery(queryPlanKey, bindKey);
12501250
}

ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public void assert_queryBindHash_isSame(RawExpression exp0, RawExpression exp1)
7070
}
7171

7272
private BindValuesKey bindKey(RawExpression query) {
73-
BindValuesKey bindValuesKey = new BindValuesKey();
73+
BindValuesKey bindValuesKey = new BindValuesKey(spiEbeanServer());
7474
query.queryBindKey(bindValuesKey);
7575
return bindValuesKey;
7676
}

ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindValuesKeyTest.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
package io.ebeaninternal.server.querydefn;
22

33
import io.ebeaninternal.api.BindValuesKey;
4+
import io.ebeaninternal.server.deploy.BaseTest;
45
import org.junit.jupiter.api.Test;
56

67
import static org.assertj.core.api.Assertions.assertThat;
78

8-
public class BindValuesKeyTest {
9+
public class BindValuesKeyTest extends BaseTest {
910

1011
@Test
1112
public void update_with_null() {
1213

13-
BindValuesKey hash = new BindValuesKey();
14+
BindValuesKey hash = new BindValuesKey(spiEbeanServer());
1415
hash.add(1).add(null).add("hello");
1516

16-
BindValuesKey hash2 = new BindValuesKey();
17+
BindValuesKey hash2 = new BindValuesKey(spiEbeanServer());
1718
hash2.add(1).add(null).add("hello");
1819

1920
assertThat(hash).isEqualTo(hash2);
@@ -22,13 +23,13 @@ public void update_with_null() {
2223
@Test
2324
public void notEqual() {
2425

25-
BindValuesKey hash = new BindValuesKey();
26+
BindValuesKey hash = new BindValuesKey(spiEbeanServer());
2627
hash.add(1).add(null).add("hello");
2728

28-
BindValuesKey hash2 = new BindValuesKey();
29+
BindValuesKey hash2 = new BindValuesKey(spiEbeanServer());
2930
hash2.add(1).add("hello");
3031

31-
BindValuesKey hash3 = new BindValuesKey();
32+
BindValuesKey hash3 = new BindValuesKey(spiEbeanServer());
3233
hash2.add(1).add(null);
3334

3435
assertThat(hash).isNotEqualTo(hash2);

ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ private <T> void prepare(DefaultOrmQuery<T> q1, DefaultOrmQuery<T> q2) {
139139
}
140140

141141
private BindValuesKey bindKey(DefaultOrmQuery<Order> query) {
142-
BindValuesKey key = new BindValuesKey();
142+
BindValuesKey key = new BindValuesKey(spiEbeanServer());
143143
query.queryBindKey(key);
144144
return key;
145145
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.tests.model.memleak;
2+
3+
import jakarta.persistence.Entity;
4+
import jakarta.persistence.Id;
5+
6+
/**
7+
* Class with @Cache(enableQueryCache = true)
8+
*
9+
* @author Jonas Fr&ouml;hler, FOCONIS AG
10+
*/
11+
@Entity
12+
public class MemleakChild {
13+
14+
@Id
15+
Long id;
16+
17+
String name;
18+
19+
transient byte[] memConsumer = new byte[1000000];
20+
21+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.tests.model.memleak;
2+
3+
import io.ebean.annotation.Cache;
4+
import jakarta.persistence.Entity;
5+
import jakarta.persistence.Id;
6+
import jakarta.persistence.ManyToOne;
7+
8+
/**
9+
* Class that holds the {@link MemleakChild}.
10+
*
11+
* @author Jonas Fr&ouml;hler, Foconis Analytics GmbH
12+
*/
13+
@Entity
14+
@Cache(enableQueryCache = true)
15+
public class MemleakParent {
16+
17+
@Id
18+
Long id;
19+
20+
@ManyToOne()
21+
MemleakChild child;
22+
23+
String name;
24+
25+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.tests.model.memleak;
2+
3+
import io.ebean.DB;
4+
import io.ebean.xtest.BaseTestCase;
5+
import org.junit.jupiter.api.Disabled;
6+
import org.junit.jupiter.api.Test;
7+
8+
/**
9+
* The query cache holds the bean (MemleakChild). If there are "big" beans, then an OOM may occur.
10+
*
11+
* @author Jonas Fr&ouml;hler, Foconis Analytics GmbH
12+
*/
13+
public class TestQueryCacheHoldsBean extends BaseTestCase {
14+
15+
@Test
16+
@Disabled("Run manually with -Xmx128M")
17+
void testQueryCacheHoldsBean() {
18+
for (long id = 0; id <= 1000; id++) {
19+
DB.find(MemleakParent.class)
20+
.where().eq("id", id)
21+
.eq("child", DB.reference(MemleakChild.class, id))
22+
.setUseQueryCache(true)
23+
.findOne();
24+
}
25+
}
26+
27+
@Test
28+
void queryCacheHoldsBean() {
29+
var query = DB.find(MemleakParent.class)
30+
.where().eq("id", 989898)
31+
.eq("child", DB.reference(MemleakChild.class, 4534535))
32+
.setUseQueryCache(true);
33+
34+
query.findOne();
35+
query.findOne();
36+
}
37+
}

0 commit comments

Comments
 (0)