mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1202 - Automatically remove old query plans from Ebean's internal query plan cache
This commit is contained in:
@@ -435,6 +435,11 @@ public class ServerConfig {
|
||||
|
||||
private String jodaLocalTimeMode;
|
||||
|
||||
/**
|
||||
* Time to live for query plans - defaults to 5 minutes.
|
||||
*/
|
||||
private int queryPlanTTLSeconds = 60 * 5;
|
||||
|
||||
/**
|
||||
* Set to true to globally disable L2 caching (typically for performance testing).
|
||||
*/
|
||||
@@ -2595,6 +2600,7 @@ public class ServerConfig {
|
||||
dbTypeConfig.setGeometrySRID(srid);
|
||||
}
|
||||
|
||||
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
|
||||
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
|
||||
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
|
||||
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
|
||||
@@ -2817,6 +2823,20 @@ public class ServerConfig {
|
||||
this.disableL2Cache = disableL2Cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query plan time to live.
|
||||
*/
|
||||
public int getQueryPlanTTLSeconds() {
|
||||
return queryPlanTTLSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query plan time to live.
|
||||
*/
|
||||
public void setQueryPlanTTLSeconds(int queryPlanTTLSeconds) {
|
||||
this.queryPlanTTLSeconds = queryPlanTTLSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the DB migration against the DataSource.
|
||||
*/
|
||||
|
||||
@@ -151,6 +151,7 @@ public class InternalConfiguration {
|
||||
this.beanDescriptorManager = new BeanDescriptorManager(this);
|
||||
Map<String, String> asOfTableMapping = beanDescriptorManager.deploy();
|
||||
Map<String, String> draftTableMap = beanDescriptorManager.getDraftTableMap();
|
||||
beanDescriptorManager.scheduleBackgroundTrim();
|
||||
|
||||
this.dataTimeZone = initDataTimeZone();
|
||||
this.binder = getBinder(typeManager, databasePlatform, dataTimeZone);
|
||||
|
||||
@@ -89,6 +89,7 @@ import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -1530,6 +1531,24 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim query plans not used since the passed in epoch time.
|
||||
*/
|
||||
public List<CQueryPlan> trimQueryPlans(long unusedSince) {
|
||||
|
||||
List<CQueryPlan> list = new ArrayList<>();
|
||||
|
||||
Iterator<CQueryPlan> it = queryPlanCache.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
CQueryPlan queryPlan = it.next();
|
||||
if (queryPlan.getLastQueryTime() < unusedSince) {
|
||||
it.remove();
|
||||
list.add(queryPlan);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the postLoad if a BeanPostLoad exists for this bean.
|
||||
*/
|
||||
|
||||
@@ -49,6 +49,7 @@ import io.ebeaninternal.server.persist.platform.MultiValueBind;
|
||||
import io.ebeaninternal.server.properties.BeanPropertiesReader;
|
||||
import io.ebeaninternal.server.properties.BeanPropertyAccess;
|
||||
import io.ebeaninternal.server.properties.EnhanceBeanPropertyAccess;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.xmlmapping.XmlMappingReader;
|
||||
import io.ebeaninternal.xmlmapping.model.XmAliasMapping;
|
||||
import io.ebeaninternal.xmlmapping.model.XmColumnMapping;
|
||||
@@ -80,6 +81,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -190,6 +192,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
*/
|
||||
private final Map<String, String> draftTableMap = new HashMap<>();
|
||||
|
||||
private final int queryPlanTTLSeconds;
|
||||
|
||||
/**
|
||||
* Create for a given database dbConfig.
|
||||
*/
|
||||
@@ -207,6 +211,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
this.multiValueBind = config.getMultiValueBind();
|
||||
this.idBinderFactory = new IdBinderFactory(databasePlatform.isIdInExpandedForm(), multiValueBind);
|
||||
this.eagerFetchLobs = serverConfig.isEagerFetchLobs();
|
||||
this.queryPlanTTLSeconds = serverConfig.getQueryPlanTTLSeconds();
|
||||
|
||||
this.asOfViewSuffix = getAsOfViewSuffix(databasePlatform, serverConfig);
|
||||
String versionsBetweenSuffix = getVersionsBetweenSuffix(databasePlatform, serverConfig);
|
||||
@@ -236,6 +241,25 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
this.changeLogRegister = config.changeLogRegister(bootupClasses.getChangeLogRegister());
|
||||
}
|
||||
|
||||
/**
|
||||
* Run periodic trim of query plans.
|
||||
*/
|
||||
public void scheduleBackgroundTrim() {
|
||||
backgroundExecutor.executePeriodically(this::trimQueryPlans, 30L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void trimQueryPlans() {
|
||||
long lastUsed = System.currentTimeMillis() - (queryPlanTTLSeconds * 1000L);
|
||||
for (BeanDescriptor<?> descriptor : immutableDescriptorList) {
|
||||
if (!descriptor.isEmbedded()) {
|
||||
List<CQueryPlan> trimmedPlans = descriptor.trimQueryPlans(lastUsed);
|
||||
if (!trimmedPlans.isEmpty()) {
|
||||
logger.trace("trimmed {} query plans for type:{}", trimmedPlans.size(), descriptor.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the AsOfViewSuffix based on the DbHistorySupport.
|
||||
*/
|
||||
|
||||
@@ -252,7 +252,7 @@ class CQueryBuilder {
|
||||
// skip building the SqlTree and Sql string
|
||||
predicates.prepare(false);
|
||||
String sql = queryPlan.getSql();
|
||||
return new CQueryRowCount(request, predicates, sql);
|
||||
return new CQueryRowCount(queryPlan, request, predicates, sql);
|
||||
}
|
||||
|
||||
predicates.prepare(true);
|
||||
@@ -283,7 +283,7 @@ class CQueryBuilder {
|
||||
queryPlan = new CQueryPlan(request, sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql());
|
||||
request.putQueryPlan(queryPlan);
|
||||
|
||||
return new CQueryRowCount(request, predicates, sql);
|
||||
return new CQueryRowCount(queryPlan, request, predicates, sql);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,8 @@ import java.sql.SQLException;
|
||||
*/
|
||||
class CQueryRowCount {
|
||||
|
||||
private final CQueryPlan queryPlan;
|
||||
|
||||
/**
|
||||
* The overall find request wrapper object.
|
||||
*/
|
||||
@@ -54,7 +56,8 @@ class CQueryRowCount {
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
*/
|
||||
CQueryRowCount(OrmQueryRequest<?> request, CQueryPredicates predicates, String sql) {
|
||||
CQueryRowCount(CQueryPlan queryPlan, OrmQueryRequest<?> request, CQueryPredicates predicates, String sql) {
|
||||
this.queryPlan = queryPlan;
|
||||
this.request = request;
|
||||
this.query = request.getQuery();
|
||||
this.sql = sql;
|
||||
@@ -116,6 +119,7 @@ class CQueryRowCount {
|
||||
rowCount = rset.getInt(1);
|
||||
|
||||
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
|
||||
queryPlan.executionTime(rowCount, executionTimeMicros, query.getParentNode());
|
||||
request.slowQueryCheck(executionTimeMicros, rowCount);
|
||||
return rowCount;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user