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:
robin.bygrave
2023-08-17 15:08:16 +12:00
parent 9e4007d25a
commit 76c889c6d9
12 changed files with 55 additions and 24 deletions
@@ -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);
}
}
/**
@@ -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);
@@ -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);