diff --git a/src/main/java/com/avaje/ebean/config/DataSourceConfig.java b/src/main/java/com/avaje/ebean/config/DataSourceConfig.java index d6481e28c..9ed13c363 100644 --- a/src/main/java/com/avaje/ebean/config/DataSourceConfig.java +++ b/src/main/java/com/avaje/ebean/config/DataSourceConfig.java @@ -42,14 +42,14 @@ public class DataSourceConfig { private int leakTimeMinutes = 30; - private int maxInactiveTimeSecs = 900; + private int maxInactiveTimeSecs = 720; private int pstmtCacheSize = 20; private int cstmtCacheSize = 20; private int waitTimeoutMillis = 1000; - + private String poolListener; private boolean offline; diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/BusyConnectionBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/BusyConnectionBuffer.java index dc5a314c2..98b55d682 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/BusyConnectionBuffer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/BusyConnectionBuffer.java @@ -5,6 +5,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebeaninternal.server.lib.sql.PooledConnectionStatistics.LoadValues; + /** * A buffer especially designed for Busy PooledConnections. *
@@ -21,6 +26,8 @@ import java.util.List; */ class BusyConnectionBuffer { + private static final Logger logger = LoggerFactory.getLogger(BusyConnectionBuffer.class); + private PooledConnection[] slots; private int growBy; @@ -66,8 +73,13 @@ class BusyConnectionBuffer { return size; } - protected boolean isEmpty(){ - return size == 0; + protected boolean isEmpty() { + for (int i = 0; i < slots.length; i++) { + if (slots[i] != null) { + return false; + } + } + return true; } protected int add(PooledConnection pc){ @@ -75,23 +87,37 @@ class BusyConnectionBuffer { // grow the capacity setCapacity(slots.length + growBy); } - ++size; int slot = nextEmptySlot(); pc.setSlotId(slot); slots[slot] = pc; - return size; + return ++size; } protected boolean remove(PooledConnection pc) { - --size; + int slotId = pc.getSlotId(); if (slots[slotId] != pc){ + PooledConnection heldBy = slots[slotId]; + logger.warn("Failed to remove from slot[{}] PooledConnection[{}] - HeldBy[{}]", pc.getSlotId(), pc, heldBy); return false; } slots[slotId] = null; + --size; return true; } + /** + * Collect the load statistics from all the busy connections. + * @param reset + */ + protected void collectStatistics(LoadValues values, boolean reset) { + + for (int i = 0; i < slots.length; i++) { + if (slots[i] != null){ + values.plus(slots[i].getStatistics().getValues(reset)); + } + } + } /** * Get a shallow read only List of the busy connections. diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePool.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePool.java index c54d5582e..3cb861427 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePool.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePool.java @@ -15,11 +15,12 @@ import java.util.Set; import javax.persistence.PersistenceException; import javax.sql.DataSource; -import com.avaje.ebean.config.DataSourceConfig; -import com.avaje.ebeaninternal.api.ClassUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.avaje.ebean.config.DataSourceConfig; +import com.avaje.ebeaninternal.api.ClassUtil; + /** * A robust DataSource. *
@@ -73,7 +74,7 @@ public class DataSourcePool implements DataSource { * The sql used to test a connection. */ private final String heartbeatsql; - + private final int heartbeatFreqSecs; /** @@ -573,7 +574,15 @@ public class DataSourcePool implements DataSource { } queue.returnPooledConnection(pooledConnection); } - + + /** + * Collect statistics of a connection that is fully closing + */ + protected void reportClosingConnection(PooledConnection pooledConnection) { + + queue.reportClosingConnection(pooledConnection); + } + /** * Returns information describing connections that are currently being used. */ @@ -834,6 +843,14 @@ public class DataSourcePool implements DataSource { public Status getStatus(boolean reset) { return queue.getStatus(reset); } + + /** + * Return the aggregated load statistics collected on all the connections in the pool. + */ + public DataSourcePoolStatistics getStatistics(boolean reset) { + + return queue.getStatistics(reset); + } /** * Deregister the JDBC driver. @@ -873,8 +890,8 @@ public class DataSourcePool implements DataSource { } public String toString() { - return "min:" + minSize + " max:" + maxSize + " free:" + free + " busy:" + busy + " waiting:" + waiting - + " highWaterMark:" + highWaterMark + " waitCount:" + waitCount + " hitCount:" + hitCount; + return "min[" + minSize + "] max[" + maxSize + "] free[" + free + "] busy[" + busy + "] waiting[" + waiting + + "] highWaterMark[" + highWaterMark + "] waitCount[" + waitCount + "] hitCount[" + hitCount+"]"; } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePoolStatistics.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePoolStatistics.java new file mode 100644 index 000000000..5bdc1c9ad --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePoolStatistics.java @@ -0,0 +1,97 @@ +package com.avaje.ebeaninternal.server.lib.sql; + +/** + * Represents aggregated statistics collected from the DataSourcePool. + *
+ * The goal is to present insight into the overload load of the DataSourcePool. + * These statistics can be collected and reported regularly to show load over + * time. + *
+ *+ * Each pooled connection collects statistics. When a pooled connection is fully + * closed it can report it's statistics to the pool to be included as part of + * the collected statistics. + *
+ */ +public class DataSourcePoolStatistics { + + private final long collectionStart; + + private final long count; + + private final long errorCount; + + private final long hwmMicros; + + private final long totalMicros; + + /** + * No statistics collected. + */ + public DataSourcePoolStatistics() { + this.collectionStart = 0; + this.count = 0; + this.errorCount = 0; + this.hwmMicros = 0; + this.totalMicros = 0; + } + + /** + * Construct with statistics collected. + */ + public DataSourcePoolStatistics(long collectionStart, long count, long errorCount, long hwmMicros, long totalMicros) { + this.collectionStart = collectionStart; + this.count = count; + this.errorCount = errorCount; + this.hwmMicros = hwmMicros; + this.totalMicros = totalMicros; + } + + public String toString() { + return "count[" + count + "] errors[" + errorCount + "] totalMicros[" + totalMicros + "] hwmMicros[" + hwmMicros + + "] avgMicros[" + getAvgMicros() + "]"; + } + + /** + * Return the start time this set of statistics was collected from. + */ + public long getCollectionStart() { + return collectionStart; + } + + /** + * Return the total number of 'get connection' requests. + */ + public long getCount() { + return count; + } + + /** + * Return the number of SQLExceptions reported. + */ + public long getErrorCount() { + return errorCount; + } + + /** + * Return the high water mark for the duration a connection was busy/used. + */ + public long getHwmMicros() { + return hwmMicros; + } + + /** + * Return the aggregate time connections were busy/used. + */ + public long getTotalMicros() { + return totalMicros; + } + + /** + * Return the average time connections were busy/used. + */ + public long getAvgMicros() { + return (totalMicros == 0) ? 0 : totalMicros / count; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedPreparedStatement.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedPreparedStatement.java index 3b3b40344..439b0731f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedPreparedStatement.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedPreparedStatement.java @@ -31,18 +31,17 @@ public class ExtendedPreparedStatement extends ExtendedStatement implements Prep /** * The SQL used to create the underlying PreparedStatement. */ - final String sql; + private final String sql; /** * The key used to cache this in the connection. */ - final String cacheKey; + private final String cacheKey; /** * Create a wrapped PreparedStatement that can be cached. */ - public ExtendedPreparedStatement(PooledConnection pooledConnection, PreparedStatement pstmt, - String sql, String cacheKey) { + public ExtendedPreparedStatement(PooledConnection pooledConnection, PreparedStatement pstmt, String sql, String cacheKey) { super(pooledConnection, pstmt); this.sql = sql; this.cacheKey = cacheKey; diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/FreeConnectionBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/FreeConnectionBuffer.java index 23f160091..813740633 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/FreeConnectionBuffer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/FreeConnectionBuffer.java @@ -3,6 +3,8 @@ package com.avaje.ebeaninternal.server.lib.sql; import java.util.ArrayList; import java.util.List; +import com.avaje.ebeaninternal.server.lib.sql.PooledConnectionStatistics.LoadValues; + /** * A buffer designed especially to hold free pooled connections. *@@ -48,11 +50,31 @@ class FreeConnectionBuffer { * Add at connection. */ protected void add(PooledConnection pc) { + if (conns[addIndex] != null) { + throw new RuntimeException("Buffer slot ["+addIndex+"] already full?"); + } conns[addIndex] = pc; addIndex = inc(addIndex); ++size; } + protected void closeAll(boolean logErrors) { + + final PooledConnection[] items = this.conns; + + this.conns = new PooledConnection[items.length]; + this.size = 0; + this.removeIndex = 0; + this.addIndex = 0; + + for (int i = 0; i < items.length; i++) { + PooledConnection c = items[i]; + if (c != null) { + c.closeConnectionFully(logErrors); + } + } + } + /** * Remove a connection at current remove position. */ @@ -79,6 +101,18 @@ class FreeConnectionBuffer { return copy; } + /** + * Collect the load statistics from all the free connections. + */ + protected void collectStatistics(LoadValues values, boolean reset) { + + for (int i = 0; i < conns.length; i++) { + if (conns[i] != null){ + values.plus(conns[i].getStatistics().getValues(reset)); + } + } + } + /** * Set the free list to be the connections in this copy. This is done after * unused connections have been trimmed. diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PooledConnection.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PooledConnection.java index 5b29b7fb5..d6c3293bc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PooledConnection.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PooledConnection.java @@ -34,8 +34,7 @@ import org.slf4j.LoggerFactory; * statement that was executed. Keeps statistics on how long it is in use. *
*/ -public class PooledConnection extends ConnectionDelegator -{ +public class PooledConnection extends ConnectionDelegator { private static final Logger logger = LoggerFactory.getLogger(PooledConnection.class); @@ -45,49 +44,49 @@ public class PooledConnection extends ConnectionDelegator * Set when connection is idle in the pool. In general when in the pool the * connection should not be modified. */ - static final int STATUS_IDLE = 88; + private static final int STATUS_IDLE = 88; /** * Set when connection given to client. */ - static final int STATUS_ACTIVE = 89; + private static final int STATUS_ACTIVE = 89; /** * Set when commit() or rollback() called. */ - static final int STATUS_ENDED = 87; + private static final int STATUS_ENDED = 87; /** * Name used to identify the PooledConnection for logging. */ - final String name; + private final String name; /** * The pool this connection belongs to. */ - final DataSourcePool pool; + private final DataSourcePool pool; /** * The underlying connection. */ - final Connection connection; + private final Connection connection; /** * The time this connection was created. */ - final long creationTime; + private final long creationTime; /** * Cache of the PreparedStatements */ - final PstmtCache pstmtCache; + private final PstmtCache pstmtCache; - final Object pstmtMonitor = new Object(); + private final Object pstmtMonitor = new Object(); /** * The status of the connection. IDLE, ACTIVE or ENDED. */ - int status = STATUS_IDLE; + private int status = STATUS_IDLE; /** * Set this to true if the connection will be busy for a long time. @@ -95,56 +94,53 @@ public class PooledConnection extends ConnectionDelegator * This means it should skip the suspected connection pool leak checking. * */ - boolean longRunning; + private boolean longRunning; /** * Flag to indicate that this connection had errors and should be checked to * make sure it is okay. */ - boolean hadErrors; + private boolean hadErrors; /** * The last start time. When the connection was given to a thread. */ - long startUseTime; + private long startUseTime; /** * The last end time of this connection. This is to calculate the usage * time. */ - long lastUseTime; + private long lastUseTime; + + private long exeStartNanos; + + private final PooledConnectionStatistics stats = new PooledConnectionStatistics(); /** * The last statement executed by this connection. */ - String lastStatement; - - /** - * The number of hits against the preparedStatement cache. - */ - int pstmtHitCounter; - - /** - * The number of misses against the preparedStatement cache. - */ - int pstmtMissCounter; + private String lastStatement; /** * The non avaje method that created the connection. */ - String createdByMethod; + private String createdByMethod; /** * Used to find connection pool leaks. */ - StackTraceElement[] stackTrace; + private StackTraceElement[] stackTrace; - int maxStackTrace; + private int maxStackTrace; /** * Slot position in the BusyConnectionBuffer. */ - int slotId; + private int slotId; + + private boolean resetIsolationReadOnlyRequired; + /** * Construct the connection that can refer back to the pool it belongs to. @@ -165,47 +161,47 @@ public class PooledConnection extends ConnectionDelegator this.lastUseTime = creationTime; } - /** - * For testing the pool without real connections. - */ - protected PooledConnection(String name) { - super(null); - this.name = name; - this.pool = null; - this.connection = null; - this.pstmtCache = null; - this.maxStackTrace = 0; - this.creationTime = System.currentTimeMillis(); - this.lastUseTime = creationTime; - } - - /** - * Return the slot position in the busy buffer. - */ - public int getSlotId() { - return slotId; - } + /** + * For testing the pool without real connections. + */ + protected PooledConnection(String name) { + super(null); + this.name = name; + this.pool = null; + this.connection = null; + this.pstmtCache = null; + this.maxStackTrace = 0; + this.creationTime = System.currentTimeMillis(); + this.lastUseTime = creationTime; + } - /** - * Set the slot position in the busy buffer. - */ - public void setSlotId(int slotId) { - this.slotId = slotId; - } + /** + * Return the slot position in the busy buffer. + */ + public int getSlotId() { + return slotId; + } - /** - * Return the DataSourcePool that this connection belongs to. - */ - public DataSourcePool getDataSourcePool() { - return pool; - } + /** + * Set the slot position in the busy buffer. + */ + public void setSlotId(int slotId) { + this.slotId = slotId; + } - /** - * Return the time the connection was created. - */ - public long getCreationTime() { - return creationTime; - } + /** + * Return the DataSourcePool that this connection belongs to. + */ + public DataSourcePool getDataSourcePool() { + return pool; + } + + /** + * Return the time the connection was created. + */ + public long getCreationTime() { + return creationTime; + } /** * Return a string to identify the connection. @@ -214,16 +210,24 @@ public class PooledConnection extends ConnectionDelegator return name; } + public String getNameSlot() { + return name+":"+slotId; + } + public String toString() { - return name; + return getDescription(); } public String getDescription() { - return "name["+name+"] startTime["+getStartUseTime()+"] stmt["+getLastStatement()+"] createdBy["+getCreatedByMethod()+"]"; + return "name["+name+"] slot["+slotId+"] startTime["+getStartUseTime()+"] stmt["+getLastStatement()+"] createdBy["+getCreatedByMethod()+"]"; } - public String getStatistics() { - return "name["+name+"] startTime["+getStartUseTime()+"] pstmtHits["+pstmtHitCounter+"] pstmtMiss["+pstmtMissCounter+"] "+pstmtCache.getDescription(); + public String getPstmtStatistics() { + return "name["+name+"] startTime["+getStartUseTime()+"] "+pstmtCache.getDescription(); + } + + public PooledConnectionStatistics getStatistics() { + return stats; } /** @@ -253,21 +257,25 @@ public class PooledConnection extends ConnectionDelegator */ public void closeConnectionFully(boolean logErrors) { - String msg = "Closing Connection[" + getName() + "]" + " psReuse[" + pstmtHitCounter - + "] psCreate[" + pstmtMissCounter + "] psSize[" + pstmtCache.size() + "]"; - - logger.debug(msg); + if (pool != null) { + // allow collection of load statistics + pool.reportClosingConnection(this); + } + + if (logger.isDebugEnabled()) { + logger.debug("Closing Connection[{}] slot[{}] Stats: {} , PstmtStats: {} ", name, slotId, stats.getValues(false), pstmtCache.getDescription()); + } try { if (connection.isClosed()) { // Typically the JDBC Driver has its own JVM shutdown hook and already // closed the connections in our DataSource pool so making this DEBUG level - logger.debug("Closing Connection[" + getName() + "] that is already closed?"); + logger.debug("Closing Connection[{}] that is already closed?", name); return; } } catch (SQLException ex) { if (logErrors) { - logger.error("Error when fully closing connection [" + getName() + "]", ex); + logger.error("Error checking if connection [" + getNameSlot() + "] is closed", ex); } } @@ -287,8 +295,8 @@ public class PooledConnection extends ConnectionDelegator try { connection.close(); } catch (SQLException ex) { - if (logErrors) { - logger.error("Error when fully closing connection [" + getName() + "]", ex); + if (logErrors || logger.isDebugEnabled()) { + logger.error("Error when fully closing connection [" + getNameSlot() + "]", ex); } } } @@ -337,26 +345,15 @@ public class PooledConnection extends ConnectionDelegator protected void returnPreparedStatement(ExtendedPreparedStatement pstmt) { synchronized (pstmtMonitor) { - ExtendedPreparedStatement alreadyInCache = pstmtCache.get(pstmt.getCacheKey()); + if (!pstmtCache.returnStatement(pstmt)) { + try { + // Already an entry in the cache with the exact same SQL... + pstmt.closeDestroy(); - if (alreadyInCache == null) { - // add the returning prepared statement to the cache. - // Note that the LRUCache will automatically close fully old unused - // PStmts when the cache has hit its maximum size. - pstmtCache.put(pstmt.getCacheKey(), pstmt); - - } else { - try { - // if a entry in the cache exists for the exact same SQL... - // then remove it from the cache and close it fully. - // Only having one PreparedStatement per unique SQL - // statement - pstmt.closeDestroy(); - - } catch (SQLException e) { - logger.error("Error closing Pstmt", e); - } - } + } catch (SQLException e) { + logger.error("Error closing Pstmt", e); + } + } } } @@ -392,12 +389,10 @@ public class PooledConnection extends ConnectionDelegator ExtendedPreparedStatement pstmt = pstmtCache.remove(cacheKey); if (pstmt != null) { - pstmtHitCounter++; return pstmt; } // create a new PreparedStatement - pstmtMissCounter++; PreparedStatement actualPstmt; if (useFlag) { actualPstmt = connection.prepareStatement(sql, flag); @@ -420,7 +415,6 @@ public class PooledConnection extends ConnectionDelegator } try { // no caching when creating PreparedStatements this way - pstmtMissCounter++; lastStatement = sql; return connection.prepareStatement(sql, resultSetType, resultSetConcurreny); } catch (SQLException ex) { @@ -436,6 +430,7 @@ public class PooledConnection extends ConnectionDelegator protected void resetForUse() { this.status = STATUS_ACTIVE; this.startUseTime = System.currentTimeMillis(); + this.exeStartNanos = System.nanoTime(); this.createdByMethod = null; this.lastStatement = null; this.hadErrors = false; @@ -480,6 +475,9 @@ public class PooledConnection extends ConnectionDelegator throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "close()"); } + long durationNanos = System.nanoTime() - exeStartNanos; + stats.add(durationNanos, hadErrors); + if (hadErrors) { if (!pool.validateConnection(this)) { // the connection is BAD, close it and test the pool @@ -530,12 +528,11 @@ public class PooledConnection extends ConnectionDelegator try { if (connection != null && !connection.isClosed()) { // connect leak? - String msg = "Closing Connection[" + getName() + "] on finalize()."; - logger.warn(msg); + logger.warn("Closing Connection[" + getName() + "] on finalize()."); closeConnectionFully(false); } } catch (Exception e) { - logger.error(null, e); + logger.error("Error when finalize is closing a connection? (unexpected)", e); } super.finalize(); } @@ -581,7 +578,6 @@ public class PooledConnection extends ConnectionDelegator } } - boolean resetIsolationReadOnlyRequired = false; /** * Also note the read only status needs to be reset when put back into the @@ -614,327 +610,326 @@ public class PooledConnection extends ConnectionDelegator } } - // - // - // Simple wrapper methods which pass a method call onto the acutal - // connection object. These methods are safe-guarded to prevent use of - // the methods whilst the connection is in the connection pool. - // - // - public void clearWarnings() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "clearWarnings()"); - } - connection.clearWarnings(); - } + // + // + // Simple wrapper methods which pass a method call onto the acutal + // connection object. These methods are safe-guarded to prevent use of + // the methods whilst the connection is in the connection pool. + // + // + public void clearWarnings() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "clearWarnings()"); + } + connection.clearWarnings(); + } - public void commit() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "commit()"); - } - try { - status = STATUS_ENDED; - connection.commit(); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public boolean getAutoCommit() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getAutoCommit()"); - } - return connection.getAutoCommit(); - } + public void commit() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "commit()"); + } + try { + status = STATUS_ENDED; + connection.commit(); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } - public String getCatalog() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getCatalog()"); - } - return connection.getCatalog(); - } + public boolean getAutoCommit() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getAutoCommit()"); + } + return connection.getAutoCommit(); + } - public DatabaseMetaData getMetaData() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getMetaData()"); - } - return connection.getMetaData(); - } + public String getCatalog() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getCatalog()"); + } + return connection.getCatalog(); + } - public int getTransactionIsolation() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getTransactionIsolation()"); - } - return connection.getTransactionIsolation(); - } + public DatabaseMetaData getMetaData() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getMetaData()"); + } + return connection.getMetaData(); + } - public Map- * Used to help finding connection pool leaks. - *
- */ - public String getCreatedByMethod() { - if (createdByMethod != null) { - return createdByMethod; - } - if (stackTrace == null) { - return null; - } + public CallableStatement prepareCall(String s, int i, int x, int y) throws SQLException { + try { + return connection.prepareCall(s, i, x, y); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } - for (int j = 0; j < stackTrace.length; j++) { - String methodLine = stackTrace[j].toString(); - if (skipElement(methodLine)) { - // ignore these methods... - } else { - createdByMethod = methodLine; - return createdByMethod; - } - } + /** + * Returns the method that created the connection. + *+ * Used to help finding connection pool leaks. + *
+ */ + public String getCreatedByMethod() { + if (createdByMethod != null) { + return createdByMethod; + } + if (stackTrace == null) { + return null; + } - return null; - } + for (int j = 0; j < stackTrace.length; j++) { + String methodLine = stackTrace[j].toString(); + if (skipElement(methodLine)) { + // ignore these methods... + } else { + createdByMethod = methodLine; + return createdByMethod; + } + } - private boolean skipElement(String methodLine) { - if (methodLine.startsWith("java.lang.")) { - return true; - } else if (methodLine.startsWith("java.util.")) { - return true; - } else if (methodLine.startsWith("com.avaje.ebeaninternal.server.query.CallableQuery.+ * These are aggregated up to get a total for the DataSourcePool. + *
+ */ + public static class LoadValues { + + private long collectionStart; + private long count; + private long errorCount; + private long hwmMicros; + private long totalMicros; + + public LoadValues() { + } + + public LoadValues(long collectionStart, long count, long errorCount, long hwmMicros, long totalMicros) { + this.collectionStart = collectionStart; + this.count = count; + this.errorCount = errorCount; + this.hwmMicros = hwmMicros; + this.totalMicros = totalMicros; + } + + public void plus(LoadValues additional) { + collectionStart = (collectionStart == 0) ? additional.collectionStart : Math.min(collectionStart, additional.collectionStart); + count += additional.count; + errorCount += additional.errorCount; + hwmMicros = Math.max(hwmMicros, additional.hwmMicros); + totalMicros += additional.totalMicros; + } + + public String toString() { + return "count["+count+"] errors["+errorCount+"] totalMicros["+totalMicros+"] hwmMicros["+hwmMicros+"] avgMicros["+getAvgMicros()+"]"; + } + + public long getCollectionStart() { + return collectionStart; + } + + public long getCount() { + return count; + } + + public long getErrorCount() { + return errorCount; + } + + public long getHwmMicros() { + return hwmMicros; + } + + public long getTotalMicros() { + return totalMicros; + } + + public long getAvgMicros() { + return (count == 0) ? 0 : totalMicros/count; + } + } + + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PstmtCache.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PstmtCache.java index 065d3c4ce..f28bfe46d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PstmtCache.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PstmtCache.java @@ -14,37 +14,37 @@ public class PstmtCache extends LinkedHashMap