Skip to content

Commit

Permalink
Fix for Bug#88021 (26939943), High GC pressure when driver configured
Browse files Browse the repository at this point in the history
with serversideprepared statements.
  • Loading branch information
soklakov committed Nov 3, 2017
1 parent f3dc08c commit 25fa4e0
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 38 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

Version 5.1.45

- Fix for Bug#88021 (26939943), High GC pressure when driver configured with serversideprepared statements.
Thanks to Johnathan Crawford for his contribution.

- Fix for Bug#26724085, CHARSET MAPPING TO BE UPDATED FOR MYSQL 8.0.3.

- Fix for Bug#26794652, TEST FAILING DUE TO BINARY LOGGING ENABLED BY DEFAULT IN MYSQL 8.0.3.
Expand Down
55 changes: 24 additions & 31 deletions src/com/mysql/jdbc/ConnectionImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,18 +190,20 @@ public List<Extension> getInterceptors() {
* names soon will, so current catalog is a (hidden) component of the name.
*/
static class CompoundCacheKey {
String componentOne;
final String componentOne;

String componentTwo;
final String componentTwo;

int hashCode;
final int hashCode;

CompoundCacheKey(String partOne, String partTwo) {
this.componentOne = partOne;
this.componentTwo = partTwo;

// Handle first component (in most cases, currentCatalog being NULL....
this.hashCode = (((this.componentOne != null) ? this.componentOne : "") + this.componentTwo).hashCode();
int hc = 17;
hc = 31 * hc + (this.componentOne != null ? this.componentOne.hashCode() : 0);
hc = 31 * hc + (this.componentTwo != null ? this.componentTwo.hashCode() : 0);
this.hashCode = hc;
}

/*
Expand Down Expand Up @@ -440,7 +442,7 @@ private static boolean nullSafeCompare(String s1, String s2) {
* synchronization and at the same time save memory (each charset converter
* takes approx 65K of static data).
*/
private Map<String, Object> charsetConverterMap = new HashMap<String, Object>(CharsetMapping.getNumberOfCharsetsConfigured());
private final Map<String, Object> charsetConverterMap = new HashMap<String, Object>(CharsetMapping.getNumberOfCharsetsConfigured());

/** The point in time when this connection was created */
private long connectionCreationTimeMillis = 0;
Expand Down Expand Up @@ -554,7 +556,7 @@ private static boolean nullSafeCompare(String s1, String s2) {
*/
private final CopyOnWriteArrayList<Statement> openStatements = new CopyOnWriteArrayList<Statement>();

private LRUCache parsedCallableStatementCache;
private LRUCache<CompoundCacheKey, CallableStatement.CallableStatementParamInfo> parsedCallableStatementCache;

private boolean parserKnowsUnicode = false;

Expand All @@ -581,7 +583,7 @@ private static boolean nullSafeCompare(String s1, String s2) {
private boolean readOnly = false;

/** Cache of ResultSet metadata */
protected LRUCache resultSetMetadataCache;
protected LRUCache<String, CachedResultSetMetaData> resultSetMetadataCache;

/** The timezone of the server */
private TimeZone serverTimezoneTZ = null;
Expand Down Expand Up @@ -614,8 +616,8 @@ private static boolean nullSafeCompare(String s1, String s2) {
*/
private boolean useServerPreparedStmts = false;

private LRUCache serverSideStatementCheckCache;
private LRUCache serverSideStatementCache;
private LRUCache<String, Boolean> serverSideStatementCheckCache;
private LRUCache<CompoundCacheKey, ServerPreparedStatement> serverSideStatementCache;
private Calendar sessionCalendar;

private Calendar utcCalendar;
Expand Down Expand Up @@ -1009,7 +1011,7 @@ private boolean canHandleAsServerPreparedStatement(String sql) throws SQLExcepti

if (getCachePreparedStatements()) {
synchronized (this.serverSideStatementCheckCache) {
Boolean flag = (Boolean) this.serverSideStatementCheckCache.get(sql);
Boolean flag = this.serverSideStatementCheckCache.get(sql);

if (flag != null) {
return flag.booleanValue();
Expand Down Expand Up @@ -2312,22 +2314,22 @@ private void createPreparedStatementCaches() throws SQLException {
}

if (getUseServerPreparedStmts()) {
this.serverSideStatementCheckCache = new LRUCache(cacheSize);
this.serverSideStatementCheckCache = new LRUCache<String, Boolean>(cacheSize);

this.serverSideStatementCache = new LRUCache(cacheSize) {
this.serverSideStatementCache = new LRUCache<CompoundCacheKey, ServerPreparedStatement>(cacheSize) {

private static final long serialVersionUID = 7692318650375988114L;

@Override
protected boolean removeEldestEntry(java.util.Map.Entry<Object, Object> eldest) {
protected boolean removeEldestEntry(java.util.Map.Entry<CompoundCacheKey, ServerPreparedStatement> eldest) {
if (this.maxElements <= 1) {
return false;
}

boolean removeIt = super.removeEldestEntry(eldest);

if (removeIt) {
ServerPreparedStatement ps = (ServerPreparedStatement) eldest.getValue();
ServerPreparedStatement ps = eldest.getValue();
ps.isCached = false;
ps.setClosed(false);

Expand Down Expand Up @@ -3163,15 +3165,15 @@ private void initializeDriverProperties(Properties info) throws SQLException {
}

if (getCacheCallableStatements()) {
this.parsedCallableStatementCache = new LRUCache(getCallableStatementCacheSize());
this.parsedCallableStatementCache = new LRUCache<CompoundCacheKey, CallableStatement.CallableStatementParamInfo>(getCallableStatementCacheSize());
}

if (getAllowMultiQueries()) {
setCacheResultSetMetadata(false); // we don't handle this yet
}

if (getCacheResultSetMetadata()) {
this.resultSetMetadataCache = new LRUCache(getMetadataCacheSize());
this.resultSetMetadataCache = new LRUCache<String, CachedResultSetMetaData>(getMetadataCacheSize());
}

if (getSocksProxyHost() != null) {
Expand Down Expand Up @@ -3973,8 +3975,7 @@ public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int
synchronized (this.parsedCallableStatementCache) {
CompoundCacheKey key = new CompoundCacheKey(getCatalog(), sql);

CallableStatement.CallableStatementParamInfo cachedParamInfo = (CallableStatement.CallableStatementParamInfo) this.parsedCallableStatementCache
.get(key);
CallableStatement.CallableStatementParamInfo cachedParamInfo = this.parsedCallableStatementCache.get(key);

if (cachedParamInfo != null) {
cStmt = CallableStatement.getInstance(getMultiHostSafeProxy(), cachedParamInfo);
Expand Down Expand Up @@ -4086,8 +4087,7 @@ public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType
if (this.useServerPreparedStmts && canServerPrepare) {
if (this.getCachePreparedStatements()) {
synchronized (this.serverSideStatementCache) {
pStmt = (com.mysql.jdbc.ServerPreparedStatement) this.serverSideStatementCache
.remove(makePreparedStatementCacheKey(this.database, sql));
pStmt = this.serverSideStatementCache.remove(new CompoundCacheKey(this.database, sql));

if (pStmt != null) {
((com.mysql.jdbc.ServerPreparedStatement) pStmt).setClosed(false);
Expand Down Expand Up @@ -4277,18 +4277,11 @@ public void realClose(boolean calledExplicitly, boolean issueRollback, boolean s

}

private String makePreparedStatementCacheKey(String catalog, String query) {
StringBuilder key = new StringBuilder();
key.append("/*").append(catalog).append("*/");
key.append(query);
return key.toString();
}

public void recachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException {
synchronized (getConnectionMutex()) {
if (getCachePreparedStatements() && pstmt.isPoolable()) {
synchronized (this.serverSideStatementCache) {
Object oldServerPrepStmt = this.serverSideStatementCache.put(makePreparedStatementCacheKey(pstmt.currentCatalog, pstmt.originalSql), pstmt);
Object oldServerPrepStmt = this.serverSideStatementCache.put(new CompoundCacheKey(pstmt.currentCatalog, pstmt.originalSql), pstmt);
if (oldServerPrepStmt != null && oldServerPrepStmt != pstmt) {
((ServerPreparedStatement) oldServerPrepStmt).isCached = false;
((ServerPreparedStatement) oldServerPrepStmt).setClosed(false);
Expand All @@ -4303,7 +4296,7 @@ public void decachePreparedStatement(ServerPreparedStatement pstmt) throws SQLEx
synchronized (getConnectionMutex()) {
if (getCachePreparedStatements() && pstmt.isPoolable()) {
synchronized (this.serverSideStatementCache) {
this.serverSideStatementCache.remove(makePreparedStatementCacheKey(pstmt.currentCatalog, pstmt.originalSql));
this.serverSideStatementCache.remove(new CompoundCacheKey(pstmt.currentCatalog, pstmt.originalSql));
}
}
}
Expand Down Expand Up @@ -5233,7 +5226,7 @@ public boolean versionMeetsMinimum(int major, int minor, int subminor) throws SQ
public CachedResultSetMetaData getCachedMetaData(String sql) {
if (this.resultSetMetadataCache != null) {
synchronized (this.resultSetMetadataCache) {
return (CachedResultSetMetaData) this.resultSetMetadataCache.get(sql);
return this.resultSetMetadataCache.get(sql);
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/com/mysql/jdbc/PerConnectionLRUFactory.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
The MySQL Connector/J is licensed under the terms of the GPLv2
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
Expand Down Expand Up @@ -40,13 +40,13 @@ public CacheAdapter<String, ParseInfo> getInstance(Connection forConnection, Str

class PerConnectionLRU implements CacheAdapter<String, ParseInfo> {
private final int cacheSqlLimit;
private final LRUCache cache;
private final LRUCache<String, ParseInfo> cache;
private final Connection conn;

protected PerConnectionLRU(Connection forConnection, int cacheMaxSize, int maxKeySize) {
final int cacheSize = cacheMaxSize;
this.cacheSqlLimit = maxKeySize;
this.cache = new LRUCache(cacheSize);
this.cache = new LRUCache<String, ParseInfo>(cacheSize);
this.conn = forConnection;
}

Expand All @@ -56,7 +56,7 @@ public ParseInfo get(String key) {
}

synchronized (this.conn.getConnectionMutex()) {
return (ParseInfo) this.cache.get(key);
return this.cache.get(key);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/com/mysql/jdbc/util/LRUCache.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
The MySQL Connector/J is licensed under the terms of the GPLv2
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
Expand All @@ -26,7 +26,7 @@
import java.util.LinkedHashMap;
import java.util.Map.Entry;

public class LRUCache extends LinkedHashMap<Object, Object> {
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = 1L;
protected int maxElements;

Expand All @@ -41,7 +41,7 @@ public LRUCache(int maxSize) {
* @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
*/
@Override
protected boolean removeEldestEntry(Entry<Object, Object> eldest) {
protected boolean removeEldestEntry(Entry<K, V> eldest) {
return (size() > this.maxElements);
}
}

0 comments on commit 25fa4e0

Please sign in to comment.