From db1ba5f20ee6b4e89b5920353c87d9c8faca8f34 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 4 Jul 2016 21:56:33 +1200 Subject: [PATCH] #759 - Use default value of 100 for JDBC buffer fetchSize for findEach() / findEachWhile() - Add global defaults ServerConfig jdbcFetchSizeFindEach, jdbcFetchSizeFindList --- .../com/avaje/ebean/config/ServerConfig.java | 41 ++++++++++++ .../com/avaje/ebeaninternal/api/SpiQuery.java | 5 ++ .../server/core/InternalConfiguration.java | 2 +- .../server/core/OrmQueryRequest.java | 7 ++ .../server/query/CQueryEngine.java | 40 +++++++---- .../server/querydefn/DefaultOrmQuery.java | 7 ++ .../avaje/ebean/config/ServerConfigTest.java | 4 ++ .../query/other/TestFindIterateHeapDump.java | 67 ++++++++++++------- 8 files changed, 133 insertions(+), 40 deletions(-) diff --git a/src/main/java/com/avaje/ebean/config/ServerConfig.java b/src/main/java/com/avaje/ebean/config/ServerConfig.java index 8cea8842b..0d0ec8d7a 100644 --- a/src/main/java/com/avaje/ebean/config/ServerConfig.java +++ b/src/main/java/com/avaje/ebean/config/ServerConfig.java @@ -163,6 +163,17 @@ public class ServerConfig { */ private int databaseSequenceBatchSize = 20; + /** + * JDBC fetchSize hint when using findList. Defaults to 0 leaving it up to the JDBC driver. + */ + private int jdbcFetchSizeFindList; + + /** + * JDBC fetchSize hint when using findEach/findEachWhile. Defaults to 100. Note that this does + * not apply to MySql as that gets special treatment (forward only etc). + */ + private int jdbcFetchSizeFindEach = 100; + /** * Suffix appended to the base table to derive the view that contains the union * of the base table and the history table in order to support asOf queries. @@ -719,6 +730,34 @@ public class ServerConfig { this.databaseSequenceBatchSize = databaseSequenceBatchSize; } + /** + * Return the default JDBC fetchSize hint for findList queries. + */ + public int getJdbcFetchSizeFindList() { + return jdbcFetchSizeFindList; + } + + /** + * Set the default JDBC fetchSize hint for findList queries. + */ + public void setJdbcFetchSizeFindList(int jdbcFetchSizeFindList) { + this.jdbcFetchSizeFindList = jdbcFetchSizeFindList; + } + + /** + * Return the default JDBC fetchSize hint for findEach/findEachWhile queries. + */ + public int getJdbcFetchSizeFindEach() { + return jdbcFetchSizeFindEach; + } + + /** + * Set the default JDBC fetchSize hint for findEach/findEachWhile queries. + */ + public void setJdbcFetchSizeFindEach(int jdbcFetchSizeFindEach) { + this.jdbcFetchSizeFindEach = jdbcFetchSizeFindEach; + } + /** * Return the ChangeLogPrepare. *

@@ -2405,6 +2444,8 @@ public class ServerConfig { asOfSysPeriod = p.get("asOfSysPeriod", asOfSysPeriod); historyTableSuffix = p.get("historyTableSuffix", historyTableSuffix); dataSourceJndiName = p.get("dataSourceJndiName", dataSourceJndiName); + jdbcFetchSizeFindEach = p.getInt("jdbcFetchSizeFindEach", jdbcFetchSizeFindEach); + jdbcFetchSizeFindList = p.getInt("jdbcFetchSizeFindList", jdbcFetchSizeFindList); databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize); databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue); databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse); diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java index 5cb45d3e0..40218a10c 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java @@ -625,6 +625,11 @@ public interface SpiQuery extends Query { */ void setGeneratedSql(String generatedSql); + /** + * Set the JDBC fetchSize buffer hint if not explicitly set. + */ + void setDefaultFetchBuffer(int fetchSize); + /** * Return the hint for Statement.setFetchSize(). */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java index 45eceb8a4..219acce17 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java @@ -137,7 +137,7 @@ public class InternalConfiguration { this.dataTimeZone = initDataTimeZone(); this.binder = getBinder(typeManager, databasePlatform, dataTimeZone); - this.cQueryEngine = new CQueryEngine(databasePlatform, binder, asOfTableMapping, serverConfig.getAsOfSysPeriod(), draftTableMap); + this.cQueryEngine = new CQueryEngine(serverConfig, databasePlatform, binder, asOfTableMapping, draftTableMap); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java index bfaf3207c..0aa5a472b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java @@ -520,4 +520,11 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe public String getBaseTableAlias() { return query.getAlias() == null ? beanDescriptor.getBaseTableAlias() : query.getAlias(); } + + /** + * Set the JDBC buffer fetchSize hint if not set explicitly. + */ + public void setDefaultFetchBuffer(int fetchSize) { + query.setDefaultFetchBuffer(fetchSize); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java index 839cb47df..425e488f8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java @@ -1,5 +1,6 @@ package com.avaje.ebeaninternal.server.query; +import com.avaje.ebean.config.ServerConfig; import com.avaje.ebeaninternal.server.core.QueryIterator; import com.avaje.ebean.ValuePair; import com.avaje.ebean.Version; @@ -35,15 +36,22 @@ public class CQueryEngine { private static final String T0 = "t0"; + private final int defaultFetchSizeFindList; + + private final int defaultFetchSizeFindEach; + private final boolean forwardOnlyHintOnFindIterate; private final CQueryBuilder queryBuilder; private final CQueryHistorySupport historySupport; - public CQueryEngine(DatabasePlatform dbPlatform, Binder binder, Map asOfTableMapping, String asOfSysPeriod, Map draftTableMap) { + public CQueryEngine(ServerConfig serverConfig, DatabasePlatform dbPlatform, Binder binder, Map asOfTableMapping, Map draftTableMap) { + this.defaultFetchSizeFindEach = serverConfig.getJdbcFetchSizeFindEach(); + this.defaultFetchSizeFindList = serverConfig.getJdbcFetchSizeFindList(); this.forwardOnlyHintOnFindIterate = dbPlatform.isForwardOnlyHintOnFindIterate(); - this.historySupport = new CQueryHistorySupport(dbPlatform.getHistorySupport(), asOfTableMapping, asOfSysPeriod); + + this.historySupport = new CQueryHistorySupport(dbPlatform.getHistorySupport(), asOfTableMapping, serverConfig.getAsOfSysPeriod()); this.queryBuilder = new CQueryBuilder(dbPlatform, binder, historySupport, new CQueryDraftSupport(draftTableMap)); } @@ -91,11 +99,7 @@ public class CQueryEngine { BeanIdList list = rcQuery.findIds(); if (request.isLogSql()) { - String logSql = rcQuery.getGeneratedSql(); - if (TransactionManager.SQL_LOGGER.isTraceEnabled()) { - logSql = Str.add(logSql, "; --bind(", rcQuery.getBindLog(), ")"); - } - request.logSql(logSql); + logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog()); } if (request.isLogSummary()) { @@ -115,6 +119,14 @@ public class CQueryEngine { } } + private void logGeneratedSql(OrmQueryRequest request, String sql, String bindLog) { + String logSql = sql; + if (TransactionManager.SQL_LOGGER.isTraceEnabled()) { + logSql = Str.add(logSql, "; --bind(", bindLog, ")"); + } + request.logSql(logSql); + } + /** * Build and execute the row count query. */ @@ -126,11 +138,7 @@ public class CQueryEngine { int rowCount = rcQuery.findRowCount(); if (request.isLogSql()) { - String logSql = rcQuery.getGeneratedSql(); - if (TransactionManager.SQL_LOGGER.isTraceEnabled()) { - logSql = Str.add(logSql, "; --bind(", rcQuery.getBindLog(), ")"); - } - request.logSql(logSql); + logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog()); } if (request.isLogSummary()) { @@ -138,7 +146,6 @@ public class CQueryEngine { } if (request.getQuery().isFutureFetch()) { - logger.debug("Future findRowCount completed!"); request.getTransaction().end(); } @@ -159,7 +166,9 @@ public class CQueryEngine { request.setCancelableQuery(cquery); try { - + if (defaultFetchSizeFindEach > 0) { + request.setDefaultFetchBuffer(defaultFetchSizeFindEach); + } if (!cquery.prepareBindExecuteQueryForwardOnly(forwardOnlyHintOnFindIterate)) { // query has been cancelled already logger.trace("Future fetch already cancelled"); @@ -301,6 +310,9 @@ public class CQueryEngine { request.setCancelableQuery(cquery); try { + if (defaultFetchSizeFindList > 0) { + request.setDefaultFetchBuffer(defaultFetchSizeFindList); + } if (!cquery.prepareBindExecuteQuery()) { // query has been cancelled already logger.trace("Future fetch already cancelled"); diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 996a4c087..4d74c5043 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1366,6 +1366,13 @@ public class DefaultOrmQuery implements SpiQuery { this.generatedSql = generatedSql; } + @Override + public void setDefaultFetchBuffer(int fetchSize) { + if (bufferFetchSizeHint == 0) { + bufferFetchSizeHint = fetchSize; + } + } + @Override public Query setBufferFetchSizeHint(int bufferFetchSizeHint) { this.bufferFetchSizeHint = bufferFetchSizeHint; diff --git a/src/test/java/com/avaje/ebean/config/ServerConfigTest.java b/src/test/java/com/avaje/ebean/config/ServerConfigTest.java index 6701aa3dd..2f171ad7c 100644 --- a/src/test/java/com/avaje/ebean/config/ServerConfigTest.java +++ b/src/test/java/com/avaje/ebean/config/ServerConfigTest.java @@ -32,12 +32,16 @@ public class ServerConfigTest { props.setProperty("persistBatch", "INSERT"); props.setProperty("persistBatchOnCascade", "INSERT"); props.setProperty("dbuuid","binary"); + props.setProperty("jdbcFetchSizeFindEach", "42"); + props.setProperty("jdbcFetchSizeFindList", "43"); serverConfig.loadFromProperties(props); assertEquals(PersistBatch.INSERT, serverConfig.getPersistBatch()); assertEquals(PersistBatch.INSERT, serverConfig.getPersistBatchOnCascade()); assertEquals(ServerConfig.DbUuid.BINARY, serverConfig.getDbUuid()); + assertEquals(42, serverConfig.getJdbcFetchSizeFindEach()); + assertEquals(43, serverConfig.getJdbcFetchSizeFindList()); serverConfig.setPersistBatch(PersistBatch.NONE); serverConfig.setPersistBatchOnCascade(PersistBatch.NONE); diff --git a/src/test/java/com/avaje/tests/query/other/TestFindIterateHeapDump.java b/src/test/java/com/avaje/tests/query/other/TestFindIterateHeapDump.java index bd7b2b8ac..652e6257e 100644 --- a/src/test/java/com/avaje/tests/query/other/TestFindIterateHeapDump.java +++ b/src/test/java/com/avaje/tests/query/other/TestFindIterateHeapDump.java @@ -1,19 +1,18 @@ package com.avaje.tests.query.other; -import java.io.File; -import java.lang.management.ManagementFactory; - -import javax.management.MBeanServer; - -import org.junit.Ignore; -import org.junit.Test; - import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.Transaction; +import com.avaje.ebean.QueryEachConsumer; import com.avaje.tests.model.basic.EBasic; import com.sun.management.HotSpotDiagnosticMXBean; +import org.junit.Ignore; +import org.junit.Test; + +import javax.management.MBeanServer; +import java.io.File; +import java.lang.management.ManagementFactory; +import java.util.concurrent.atomic.AtomicInteger; @SuppressWarnings("restriction") public class TestFindIterateHeapDump extends BaseTestCase { @@ -34,25 +33,43 @@ public class TestFindIterateHeapDump extends BaseTestCase { @Test public void test() { - EbeanServer server = Ebean.getServer(null); + EbeanServer server = Ebean.getDefaultServer(); - Transaction transaction = server.beginTransaction(); - try { - transaction.setBatchMode(true); - transaction.setBatchSize(20); - for (int i = 0; i < 10000; i++) { - EBasic dumbModel = new EBasic(); - dumbModel.setName("Hello"); - server.save(dumbModel); - } - transaction.commit(); - - } finally { - transaction.end(); - } +// Transaction transaction = server.beginTransaction(); +// try { +// transaction.setBatchMode(true); +// transaction.setBatchSize(20); +// for (int i = 0; i < 20000; i++) { +// EBasic dumbModel = new EBasic(); +// dumbModel.setName("Goodbye now"); +// server.save(dumbModel); +// } +// transaction.commit(); +// +// } finally { +// transaction.end(); +// } +// +// if (true) { +// return; +// } // Intentionally not iterating through the iterator to + final AtomicInteger counter = new AtomicInteger(); + + server.find(EBasic.class) + .findEach(new QueryEachConsumer() { + @Override + public void accept(EBasic bean) { + + int count = counter.incrementAndGet(); + if (count == 1) { + dumpHeap("heap-dump13-initial.snapshot", true); + } + } + }); + // try { // while (iterate.hasNext()) { // EBasic eBasic = iterate.next(); @@ -62,7 +79,7 @@ public class TestFindIterateHeapDump extends BaseTestCase { // iterate.close(); // } - String fileName = "heap-dump6.snapshot"; + String fileName = "heap-dump13.snapshot"; File file = new File(fileName); if (file.exists())