mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
InTuples literal mode - don't cache ebean compiled query plan
- Don't cache ebean compiled query plan when InTuples goes into literal mode - Postgres maxInBinding = 32_000; - SQLite maxInBinding = 800; - Note that not caching the compiled query plan means that metrics visitor isn't going to see it, we lose collection of that query execution metric. We might need a plan to fix this later.
This commit is contained in:
@@ -5,6 +5,11 @@ package io.ebeaninternal.api;
|
||||
*/
|
||||
public interface CQueryPlanKey {
|
||||
|
||||
/**
|
||||
* Return true if the query plan should be cached.
|
||||
*/
|
||||
boolean useCache();
|
||||
|
||||
/**
|
||||
* Used by read audit such that we can log read audit entries without the full sql
|
||||
* (which would make the read audit logs verbose).
|
||||
|
||||
@@ -1503,7 +1503,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
}
|
||||
|
||||
public void queryPlan(CQueryPlanKey key, CQueryPlan plan) {
|
||||
queryPlanCache.put(key, plan);
|
||||
if (key.useCache()) {
|
||||
queryPlanCache.put(key, plan);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -612,9 +612,9 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
}
|
||||
for (SpiExpression expr : list) {
|
||||
expr.queryPlanHash(builder);
|
||||
builder.append(",");
|
||||
builder.append(',');
|
||||
}
|
||||
builder.append("]");
|
||||
builder.append(']');
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -66,7 +66,7 @@ final class InTuplesExpression extends AbstractExpression {
|
||||
if (maxInBinding == 0) {
|
||||
return 5000 / propertyCount;
|
||||
}
|
||||
return Math.min((maxInBinding / propertyCount) - 200, 5000);
|
||||
return (maxInBinding / propertyCount) - 200;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,19 +159,19 @@ final class InTuplesExpression extends AbstractExpression {
|
||||
*/
|
||||
@Override
|
||||
public void queryPlanHash(StringBuilder builder) {
|
||||
if (literalMode) {
|
||||
builder.delete(0, builder.length());
|
||||
builder.append("$NoCache/").append(UUID.randomUUID()).append('/');
|
||||
return;
|
||||
}
|
||||
if (not) {
|
||||
builder.append("Not");
|
||||
}
|
||||
builder.append("InTuple[");
|
||||
if (literalMode) {
|
||||
builder.append(UUID.randomUUID());
|
||||
} else {
|
||||
for (String property : properties) {
|
||||
builder.append(property).append("-");
|
||||
}
|
||||
builder.append(entries.size());
|
||||
for (String property : properties) {
|
||||
builder.append(property).append("-");
|
||||
}
|
||||
builder.append("]");
|
||||
builder.append(entries.size()).append(']');
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,6 +18,11 @@ public class NativeSqlQueryPlanKey implements CQueryPlanKey {
|
||||
return partialKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useCache() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CQueryPlanKey withDeleteByIds() {
|
||||
throw new IllegalStateException("Not allowed");
|
||||
|
||||
@@ -24,6 +24,11 @@ final class RawSqlQueryPlanKey implements CQueryPlanKey {
|
||||
return partialKey() + ":r";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useCache() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CQueryPlanKey withDeleteByIds() {
|
||||
throw new IllegalStateException("Not allowed");
|
||||
|
||||
@@ -1047,13 +1047,13 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
if (temporalMode != SpiQuery.TemporalMode.CURRENT) {
|
||||
sb.append("/tm").append(temporalMode.ordinal());
|
||||
if (versionsStart != null) {
|
||||
sb.append("v");
|
||||
sb.append('v');
|
||||
}
|
||||
}
|
||||
if (forUpdate != null) {
|
||||
sb.append("/fu").append(forUpdate.ordinal());
|
||||
if (lockType != null) {
|
||||
sb.append("t").append(lockType.ordinal());
|
||||
sb.append('t').append(lockType.ordinal());
|
||||
}
|
||||
}
|
||||
if (id != null) {
|
||||
@@ -1092,27 +1092,27 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
if (detail != null) {
|
||||
sb.append("/d[");
|
||||
detail.queryPlanHash(sb);
|
||||
sb.append("]");
|
||||
sb.append(']');
|
||||
}
|
||||
if (bindParams != null) {
|
||||
sb.append("/b[");
|
||||
bindParams.buildQueryPlanHash(sb);
|
||||
sb.append("]");
|
||||
sb.append(']');
|
||||
}
|
||||
if (whereExpressions != null) {
|
||||
sb.append("/w[");
|
||||
whereExpressions.queryPlanHash(sb);
|
||||
sb.append("]");
|
||||
sb.append(']');
|
||||
}
|
||||
if (havingExpressions != null) {
|
||||
sb.append("/h[");
|
||||
havingExpressions.queryPlanHash(sb);
|
||||
sb.append("]");
|
||||
sb.append(']');
|
||||
}
|
||||
if (updateProperties != null) {
|
||||
sb.append("/u[");
|
||||
updateProperties.buildQueryPlanHash(sb);
|
||||
sb.append("]");
|
||||
sb.append(']');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ final class OrmQueryPlanKey implements CQueryPlanKey {
|
||||
return new OrmQueryPlanKey(description + "/deleteByIds", 0, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useCache() {
|
||||
return !description.startsWith("$NoCache");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String partialKey() {
|
||||
return description;
|
||||
|
||||
@@ -81,9 +81,11 @@ class TestInTuplesWithLocalDate extends BaseTestCase {
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("where (t0.edate,t0.hours) in ((?,?),(?,?),(?,?),(?,?),(?,?)) and t1.name = ?");
|
||||
if (isMySql()) {
|
||||
assertThat(sql.get(1)).contains("where (t0.edate,t0.hours) in (({d '2023-08-16'},0),({d '2023-08-16'},100),({d '2023-08-16'},101)");
|
||||
assertThat(sql.get(1)).contains("where (t0.edate,t0.hours) in (({d '2023-08-16'},0),({d '2023-08-16'},100),({d '2023-08-16'},101),(");
|
||||
} else if (isPostgresCompatible()) {
|
||||
assertThat(sql.get(1)).contains("where (t0.edate,t0.hours) in ((?,?),(?,?),(?,?),(");
|
||||
} else {
|
||||
assertThat(sql.get(1)).contains("where (t0.edate,t0.hours) in ((date '2023-08-16',0),(date '2023-08-16',100),(date '2023-08-16',101)");
|
||||
assertThat(sql.get(1)).contains("where (t0.edate,t0.hours) in ((date '2023-08-16',0),(date '2023-08-16',100),(date '2023-08-16',101),(");
|
||||
}
|
||||
|
||||
DB.deleteAll(allStats);
|
||||
|
||||
+8
-3
@@ -459,11 +459,11 @@ public class TestCacheViaComplexNaturalKey3 extends BaseTestCase {
|
||||
@Test
|
||||
void inTuples_literalMode() {
|
||||
InTuples tuples = InTuples.of("sku", "code");
|
||||
// add more entries than threshold triggers literal mode
|
||||
tuples.add("hi", 123);
|
||||
tuples.add("bye", 121);
|
||||
// add more entries than threshold triggers literal mode
|
||||
for (int i = 0; i < 5_000; i++) {
|
||||
tuples.add(UUID.randomUUID(), i);
|
||||
tuples.add("x", i);
|
||||
}
|
||||
|
||||
LoggedSql.start();
|
||||
@@ -478,6 +478,11 @@ public class TestCacheViaComplexNaturalKey3 extends BaseTestCase {
|
||||
|
||||
List<String> sql = LoggedSql.stop();
|
||||
|
||||
assertSql(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and (t0.sku,t0.code) in (('hi',123),('bye',121),('");
|
||||
if (isPostgresCompatible()) {
|
||||
// didn't exceed postgres threshold
|
||||
assertSql(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and (t0.sku,t0.code) in ((?,?),(?,?),(");
|
||||
} else {
|
||||
assertSql(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and (t0.sku,t0.code) in (('hi',123),('bye',121),('");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
public PostgresPlatform() {
|
||||
super();
|
||||
this.platform = Platform.POSTGRES;
|
||||
this.maxInBinding = 32_000; // technically 32_767
|
||||
this.supportsNativeIlike = true;
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.selectCountWithAlias = true;
|
||||
|
||||
@@ -13,6 +13,7 @@ public class SQLitePlatform extends DatabasePlatform {
|
||||
public SQLitePlatform() {
|
||||
super();
|
||||
this.platform = Platform.SQLITE;
|
||||
this.maxInBinding = 800; // technically 999
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
this.dbIdentity.setSupportsSequence(false);
|
||||
|
||||
Reference in New Issue
Block a user