use of com.mysql.cj.util.LRUCache in project JavaSegundasQuintas by ecteruel.
the class StatementRegressionTest method testBug87429.
/**
* Tests fix for Bug#87429 - repeated close of ServerPreparedStatement causes memory leak.
*
* Original de-cache on double close() behavior modified by:
* WL#11101 - Remove de-cache and close of SSPSs on double call to close().
*
* @throws Exception
*/
@Test
public void testBug87429() throws Exception {
Field stmtsCacheField = ConnectionImpl.class.getDeclaredField("serverSideStatementCache");
stmtsCacheField.setAccessible(true);
ToIntFunction<Connection> getStmtsCacheSize = (c) -> {
try {
LRUCache<?, ?> stmtsCacheObj = (LRUCache<?, ?>) stmtsCacheField.get(c);
return stmtsCacheObj == null ? -1 : stmtsCacheObj.size();
} catch (IllegalArgumentException | IllegalAccessException e) {
fail("Fail getting the statemets cache size.");
return -1;
}
};
final String sql1 = "SELECT 1, ?";
final String sql2 = "SELECT 2, ?";
boolean useSPS = false;
boolean cachePS = false;
do {
Properties props = new Properties();
props.setProperty(PropertyKey.useSSL.getKeyName(), "false");
props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), Boolean.toString(useSPS));
props.setProperty(PropertyKey.cachePrepStmts.getKeyName(), Boolean.toString(cachePS));
props.setProperty(PropertyKey.prepStmtCacheSize.getKeyName(), "5");
boolean cachedSPS = useSPS && cachePS;
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
// Single PreparedStatement, closed multiple times.
for (int i = 0; i < 100; i++) {
this.pstmt = testConn.prepareStatement(sql1);
assertTrue(this.pstmt.isPoolable());
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
// Close & cache.
this.pstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
// No-op.
this.pstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
// No-op.
this.pstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
try {
// De-caches the statement or no-op if not cached.
this.pstmt.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
this.pstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
testConn = (JdbcConnection) getConnectionWithProps(props);
// Multiple PreparedStatements interchanged, two queries, closed multiple times.
for (int i = 0; i < 100; i++) {
for (int j = 1; j <= 4; j++) {
PreparedStatement pstmt1 = testConn.prepareStatement(j == 1 ? sql2 : sql1);
PreparedStatement pstmt2 = testConn.prepareStatement(j == 2 ? sql2 : sql1);
PreparedStatement pstmt3 = testConn.prepareStatement(j == 3 ? sql2 : sql1);
PreparedStatement pstmt4 = testConn.prepareStatement(j == 4 ? sql2 : sql1);
assertEquals(4, testConn.getActiveStatementCount());
// Close and cache statements successively.
pstmt4.close();
// No-op.
pstmt4.close();
assertEquals(cachedSPS ? 4 : 3, testConn.getActiveStatementCount());
pstmt3.close();
// No-op.
pstmt3.close();
assertEquals(cachedSPS ? (j >= 3 ? 4 : 3) : 2, testConn.getActiveStatementCount());
pstmt2.close();
// No-op.
pstmt2.close();
assertEquals(cachedSPS ? (j >= 2 ? 3 : 2) : 1, testConn.getActiveStatementCount());
pstmt1.close();
// No-op.
pstmt1.close();
assertEquals(cachedSPS ? 2 : 0, testConn.getActiveStatementCount());
// No-ops.
pstmt4.close();
pstmt4.close();
pstmt3.close();
pstmt3.close();
pstmt2.close();
pstmt2.close();
pstmt1.close();
pstmt1.close();
assertEquals(cachedSPS ? 2 : 0, testConn.getActiveStatementCount());
// De-cache statements successively.
try {
// De-caches the statement or no-op if not cached.
pstmt4.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS && j == 4, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
pstmt4.close();
assertEquals(cachedSPS ? (j < 4 ? 2 : 1) : 0, testConn.getActiveStatementCount());
try {
// De-caches the statement or no-op if not cached.
pstmt3.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS && j == 3, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
pstmt3.close();
assertEquals(cachedSPS ? (j < 3 ? 2 : 1) : 0, testConn.getActiveStatementCount());
try {
// De-caches the statement or no-op if not cached.
pstmt2.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS && j == 2, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
pstmt2.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
try {
// De-caches the statement or no-op if not cached.
pstmt1.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
pstmt1.close();
assertEquals(0, testConn.getActiveStatementCount());
}
}
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
} while ((useSPS = !useSPS) || (cachePS = !cachePS));
}
use of com.mysql.cj.util.LRUCache in project ABC by RuiPinto96274.
the class StatementRegressionTest method testBug87429.
/**
* Tests fix for Bug#87429 - repeated close of ServerPreparedStatement causes memory leak.
*
* Original de-cache on double close() behavior modified by:
* WL#11101 - Remove de-cache and close of SSPSs on double call to close().
*
* @throws Exception
*/
@Test
public void testBug87429() throws Exception {
Field stmtsCacheField = ConnectionImpl.class.getDeclaredField("serverSideStatementCache");
stmtsCacheField.setAccessible(true);
ToIntFunction<Connection> getStmtsCacheSize = (c) -> {
try {
LRUCache<?, ?> stmtsCacheObj = (LRUCache<?, ?>) stmtsCacheField.get(c);
return stmtsCacheObj == null ? -1 : stmtsCacheObj.size();
} catch (IllegalArgumentException | IllegalAccessException e) {
fail("Fail getting the statemets cache size.");
return -1;
}
};
final String sql1 = "SELECT 1, ?";
final String sql2 = "SELECT 2, ?";
boolean useSPS = false;
boolean cachePS = false;
do {
Properties props = new Properties();
props.setProperty(PropertyKey.useSSL.getKeyName(), "false");
props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), Boolean.toString(useSPS));
props.setProperty(PropertyKey.cachePrepStmts.getKeyName(), Boolean.toString(cachePS));
props.setProperty(PropertyKey.prepStmtCacheSize.getKeyName(), "5");
boolean cachedSPS = useSPS && cachePS;
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
// Single PreparedStatement, closed multiple times.
for (int i = 0; i < 100; i++) {
this.pstmt = testConn.prepareStatement(sql1);
assertTrue(this.pstmt.isPoolable());
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
// Close & cache.
this.pstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
// No-op.
this.pstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
// No-op.
this.pstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
try {
// De-caches the statement or no-op if not cached.
this.pstmt.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
this.pstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
testConn = (JdbcConnection) getConnectionWithProps(props);
// Multiple PreparedStatements interchanged, two queries, closed multiple times.
for (int i = 0; i < 100; i++) {
for (int j = 1; j <= 4; j++) {
PreparedStatement pstmt1 = testConn.prepareStatement(j == 1 ? sql2 : sql1);
PreparedStatement pstmt2 = testConn.prepareStatement(j == 2 ? sql2 : sql1);
PreparedStatement pstmt3 = testConn.prepareStatement(j == 3 ? sql2 : sql1);
PreparedStatement pstmt4 = testConn.prepareStatement(j == 4 ? sql2 : sql1);
assertEquals(4, testConn.getActiveStatementCount());
// Close and cache statements successively.
pstmt4.close();
// No-op.
pstmt4.close();
assertEquals(cachedSPS ? 4 : 3, testConn.getActiveStatementCount());
pstmt3.close();
// No-op.
pstmt3.close();
assertEquals(cachedSPS ? (j >= 3 ? 4 : 3) : 2, testConn.getActiveStatementCount());
pstmt2.close();
// No-op.
pstmt2.close();
assertEquals(cachedSPS ? (j >= 2 ? 3 : 2) : 1, testConn.getActiveStatementCount());
pstmt1.close();
// No-op.
pstmt1.close();
assertEquals(cachedSPS ? 2 : 0, testConn.getActiveStatementCount());
// No-ops.
pstmt4.close();
pstmt4.close();
pstmt3.close();
pstmt3.close();
pstmt2.close();
pstmt2.close();
pstmt1.close();
pstmt1.close();
assertEquals(cachedSPS ? 2 : 0, testConn.getActiveStatementCount());
// De-cache statements successively.
try {
// De-caches the statement or no-op if not cached.
pstmt4.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS && j == 4, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
pstmt4.close();
assertEquals(cachedSPS ? (j < 4 ? 2 : 1) : 0, testConn.getActiveStatementCount());
try {
// De-caches the statement or no-op if not cached.
pstmt3.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS && j == 3, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
pstmt3.close();
assertEquals(cachedSPS ? (j < 3 ? 2 : 1) : 0, testConn.getActiveStatementCount());
try {
// De-caches the statement or no-op if not cached.
pstmt2.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS && j == 2, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
pstmt2.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
try {
// De-caches the statement or no-op if not cached.
pstmt1.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
pstmt1.close();
assertEquals(0, testConn.getActiveStatementCount());
}
}
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
} while ((useSPS = !useSPS) || (cachePS = !cachePS));
}
use of com.mysql.cj.util.LRUCache in project JavaSegundasQuintas by ecteruel.
the class StatementsTest method testServerPreparedStatementsCaching.
/**
* WL#11101 - Remove de-cache and close of SSPSs on double call to close()
*
* @throws Exception
*/
@Test
public void testServerPreparedStatementsCaching() throws Exception {
// Non prepared statements must be non-poolable by default.
assertFalse(this.stmt.isPoolable());
Field stmtsCacheField = ConnectionImpl.class.getDeclaredField("serverSideStatementCache");
stmtsCacheField.setAccessible(true);
ToIntFunction<Connection> getStmtsCacheSize = (c) -> {
try {
LRUCache<?, ?> stmtsCacheObj = (LRUCache<?, ?>) stmtsCacheField.get(c);
return stmtsCacheObj == null ? -1 : stmtsCacheObj.size();
} catch (IllegalArgumentException | IllegalAccessException e) {
fail("Fail getting the statemets cache size.");
return -1;
}
};
Function<Connection, ServerPreparedStatement> getStmtsCacheSingleElem = (c) -> {
try {
@SuppressWarnings("unchecked") LRUCache<?, ServerPreparedStatement> stmtsCacheObj = (LRUCache<?, ServerPreparedStatement>) stmtsCacheField.get(c);
return stmtsCacheObj.get(stmtsCacheObj.keySet().iterator().next());
} catch (IllegalArgumentException | IllegalAccessException e) {
fail("Fail getting the statemets cache element.");
return null;
}
};
final String sql1 = "SELECT 1, ?";
final String sql2 = "SELECT 2, ?";
boolean useSPS = false;
boolean cachePS = false;
do {
Properties props = new Properties();
props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), Boolean.toString(useSPS));
props.setProperty(PropertyKey.cachePrepStmts.getKeyName(), Boolean.toString(cachePS));
props.setProperty(PropertyKey.prepStmtCacheSize.getKeyName(), "5");
boolean cachedSPS = useSPS && cachePS;
/*
* Cache the prepared statement and de-cache it later.
* (*) if server prepared statement and caching is enabled.
*/
{
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
PreparedStatement testPstmt = testConn.prepareStatement(sql1);
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertTrue(testPstmt.isPoolable());
// Caches this PS (*).
testPstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
// No-op.
testPstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
if (cachedSPS) {
assertTrue(testPstmt.isPoolable());
// De-caches this PS; it gets automatically closed (*).
testPstmt.setPoolable(false);
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(0, getStmtsCacheSize.applyAsInt(testConn));
}
// No-op.
testPstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertThrows(SQLException.class, "No operations allowed after statement closed\\.", () -> {
testPstmt.setPoolable(false);
return null;
});
assertThrows(SQLException.class, "No operations allowed after statement closed\\.", () -> {
testPstmt.isPoolable();
return null;
});
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
/*
* Set not to cache the prepared statement.
* (*) if server prepared statement and caching is enabled.
*/
{
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
PreparedStatement testPstmt = testConn.prepareStatement(sql1);
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertTrue(testPstmt.isPoolable());
// Don't cache this PS (*).
testPstmt.setPoolable(false);
assertFalse(testPstmt.isPoolable());
// Doesn't cache this PS (*).
testPstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
// No-op.
testPstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertThrows(SQLException.class, "No operations allowed after statement closed\\.", () -> {
testPstmt.setPoolable(true);
return null;
});
assertThrows(SQLException.class, "No operations allowed after statement closed\\.", () -> {
testPstmt.isPoolable();
return null;
});
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
/*
* Set not to cache the prepared statement but change mind before closing it.
* Reuse the cached prepared statement and don't re-cache it.
* (*) if server prepared statement and caching is enabled.
*/
{
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
PreparedStatement testPstmt = testConn.prepareStatement(sql1);
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
// Don't cache this PS (*).
testPstmt.setPoolable(false);
assertFalse(testPstmt.isPoolable());
testPstmt.setPoolable(true);
// Changed my mind, let it be cached (*).
assertTrue(testPstmt.isPoolable());
// Caches this PS (*).
testPstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
// No-op.
testPstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
PreparedStatement testPstmtOld = testPstmt;
// Takes the cached statement (*), or creates a fresh one.
testPstmt = testConn.prepareStatement(sql1);
if (cachedSPS) {
assertSame(testPstmtOld, testPstmt);
} else {
assertNotSame(testPstmtOld, testPstmt);
}
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertTrue(testPstmt.isPoolable());
// Don't cache this PS (*).
testPstmt.setPoolable(false);
assertFalse(testPstmt.isPoolable());
// Doesn't cache this PS (*).
testPstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
testPstmtOld = testPstmt;
// Creates a fresh prepared statement.
testPstmt = testConn.prepareStatement(sql1);
assertNotSame(testPstmtOld, testPstmt);
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertTrue(testPstmt.isPoolable());
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
/*
* Caching of multiple copies of same prepared statement.
* (*) if server prepared statement and caching is enabled.
*/
{
int psCount = 5;
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
PreparedStatement[] testPstmts = new PreparedStatement[psCount];
for (int i = 0; i < psCount; i++) {
testPstmts[i] = testConn.prepareStatement(sql1);
}
assertEquals(5, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
for (int i = 0; i < psCount; i++) {
assertTrue(testPstmts[i].isPoolable());
// Caches this PS and replaces existing if same (*).
testPstmts[i].close();
assertEquals(cachedSPS ? psCount - i : psCount - i - 1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
if (cachedSPS) {
assertSame(testPstmts[i], getStmtsCacheSingleElem.apply(testConn));
}
}
PreparedStatement testPstmt = testConn.prepareStatement(sql1);
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
for (int i = 0; i < psCount; i++) {
if (cachedSPS && i == psCount - 1) {
assertSame(testPstmts[i], testPstmt);
} else {
assertNotSame(testPstmts[i], testPstmt);
}
}
// Don't cache this PS (*).
testPstmt.setPoolable(false);
// Doesn't cache this PS (*).
testPstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
/*
* Combine caching different prepared statements.
* (*) if server prepared statement and caching is enabled.
*/
{
int psCount = 5;
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
PreparedStatement[] testPstmts1 = new PreparedStatement[psCount];
for (int i = 0; i < psCount; i++) {
testPstmts1[i] = testConn.prepareStatement(sql1);
}
PreparedStatement testPstmt = testConn.prepareStatement(sql2);
assertEquals(6, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertTrue(testPstmt.isPoolable());
// Caches this PS (*).
testPstmt.close();
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
for (int i = 0; i < psCount; i++) {
assertTrue(testPstmts1[i].isPoolable());
// Caches this PS and replaces existing if same (*).
testPstmts1[i].close();
assertEquals(cachedSPS ? psCount - i + 1 : psCount - i - 1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 2 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
PreparedStatement testPstmt1 = testConn.prepareStatement(sql1);
assertEquals(cachedSPS ? 2 : 1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
for (int i = 0; i < psCount; i++) {
if (cachedSPS && i == psCount - 1) {
assertSame(testPstmts1[i], testPstmt1);
} else {
assertNotSame(testPstmts1[i], testPstmt1);
}
}
PreparedStatement testPstmt2 = testConn.prepareStatement(sql2);
assertEquals(2, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
if (cachedSPS) {
assertSame(testPstmt, testPstmt2);
} else {
assertNotSame(testPstmt, testPstmt2);
}
// Don't cache this PS (*).
testPstmt1.setPoolable(false);
// Doesn't cache this PS (*).
testPstmt1.close();
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
// Don't cache this PS (*).
testPstmt2.setPoolable(false);
// Doesn't cache this PS (*).
testPstmt2.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
} while ((useSPS = !useSPS) || (cachePS = !cachePS));
}
use of com.mysql.cj.util.LRUCache in project aws-mysql-jdbc by awslabs.
the class StatementsTest method testServerPreparedStatementsCaching.
/**
* WL#11101 - Remove de-cache and close of SSPSs on double call to close()
*
* @throws Exception
*/
@Test
public void testServerPreparedStatementsCaching() throws Exception {
// Non prepared statements must be non-poolable by default.
assertFalse(this.stmt.isPoolable());
Field stmtsCacheField = ConnectionImpl.class.getDeclaredField("serverSideStatementCache");
stmtsCacheField.setAccessible(true);
ToIntFunction<Connection> getStmtsCacheSize = (c) -> {
try {
LRUCache<?, ?> stmtsCacheObj = (LRUCache<?, ?>) stmtsCacheField.get(c);
return stmtsCacheObj == null ? -1 : stmtsCacheObj.size();
} catch (IllegalArgumentException | IllegalAccessException e) {
fail("Fail getting the statemets cache size.");
return -1;
}
};
Function<Connection, ServerPreparedStatement> getStmtsCacheSingleElem = (c) -> {
try {
@SuppressWarnings("unchecked") LRUCache<?, ServerPreparedStatement> stmtsCacheObj = (LRUCache<?, ServerPreparedStatement>) stmtsCacheField.get(c);
return stmtsCacheObj.get(stmtsCacheObj.keySet().iterator().next());
} catch (IllegalArgumentException | IllegalAccessException e) {
fail("Fail getting the statemets cache element.");
return null;
}
};
final String sql1 = "SELECT 1, ?";
final String sql2 = "SELECT 2, ?";
boolean useSPS = false;
boolean cachePS = false;
do {
Properties props = new Properties();
props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name());
props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), Boolean.toString(useSPS));
props.setProperty(PropertyKey.cachePrepStmts.getKeyName(), Boolean.toString(cachePS));
props.setProperty(PropertyKey.prepStmtCacheSize.getKeyName(), "5");
boolean cachedSPS = useSPS && cachePS;
/*
* Cache the prepared statement and de-cache it later.
* (*) if server prepared statement and caching is enabled.
*/
{
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
PreparedStatement testPstmt = testConn.prepareStatement(sql1);
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertTrue(testPstmt.isPoolable());
// Caches this PS (*).
testPstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
// No-op.
testPstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
if (cachedSPS) {
assertTrue(testPstmt.isPoolable());
// De-caches this PS; it gets automatically closed (*).
testPstmt.setPoolable(false);
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(0, getStmtsCacheSize.applyAsInt(testConn));
}
// No-op.
testPstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertThrows(SQLException.class, "No operations allowed after statement closed\\.", () -> {
testPstmt.setPoolable(false);
return null;
});
assertThrows(SQLException.class, "No operations allowed after statement closed\\.", () -> {
testPstmt.isPoolable();
return null;
});
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
/*
* Set not to cache the prepared statement.
* (*) if server prepared statement and caching is enabled.
*/
{
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
PreparedStatement testPstmt = testConn.prepareStatement(sql1);
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertTrue(testPstmt.isPoolable());
// Don't cache this PS (*).
testPstmt.setPoolable(false);
assertFalse(testPstmt.isPoolable());
// Doesn't cache this PS (*).
testPstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
// No-op.
testPstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertThrows(SQLException.class, "No operations allowed after statement closed\\.", () -> {
testPstmt.setPoolable(true);
return null;
});
assertThrows(SQLException.class, "No operations allowed after statement closed\\.", () -> {
testPstmt.isPoolable();
return null;
});
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
/*
* Set not to cache the prepared statement but change mind before closing it.
* Reuse the cached prepared statement and don't re-cache it.
* (*) if server prepared statement and caching is enabled.
*/
{
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
PreparedStatement testPstmt = testConn.prepareStatement(sql1);
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
// Don't cache this PS (*).
testPstmt.setPoolable(false);
assertFalse(testPstmt.isPoolable());
testPstmt.setPoolable(true);
// Changed my mind, let it be cached (*).
assertTrue(testPstmt.isPoolable());
// Caches this PS (*).
testPstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
// No-op.
testPstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
PreparedStatement testPstmtOld = testPstmt;
// Takes the cached statement (*), or creates a fresh one.
testPstmt = testConn.prepareStatement(sql1);
if (cachedSPS) {
assertSame(testPstmtOld, testPstmt);
} else {
assertNotSame(testPstmtOld, testPstmt);
}
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertTrue(testPstmt.isPoolable());
// Don't cache this PS (*).
testPstmt.setPoolable(false);
assertFalse(testPstmt.isPoolable());
// Doesn't cache this PS (*).
testPstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
testPstmtOld = testPstmt;
// Creates a fresh prepared statement.
testPstmt = testConn.prepareStatement(sql1);
assertNotSame(testPstmtOld, testPstmt);
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertTrue(testPstmt.isPoolable());
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
/*
* Caching of multiple copies of same prepared statement.
* (*) if server prepared statement and caching is enabled.
*/
{
int psCount = 5;
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
PreparedStatement[] testPstmts = new PreparedStatement[psCount];
for (int i = 0; i < psCount; i++) {
testPstmts[i] = testConn.prepareStatement(sql1);
}
assertEquals(5, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
for (int i = 0; i < psCount; i++) {
assertTrue(testPstmts[i].isPoolable());
// Caches this PS and replaces existing if same (*).
testPstmts[i].close();
assertEquals(cachedSPS ? psCount - i : psCount - i - 1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
if (cachedSPS) {
assertSame(testPstmts[i], getStmtsCacheSingleElem.apply(testConn));
}
}
PreparedStatement testPstmt = testConn.prepareStatement(sql1);
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
for (int i = 0; i < psCount; i++) {
if (cachedSPS && i == psCount - 1) {
assertSame(testPstmts[i], testPstmt);
} else {
assertNotSame(testPstmts[i], testPstmt);
}
}
// Don't cache this PS (*).
testPstmt.setPoolable(false);
// Doesn't cache this PS (*).
testPstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
/*
* Combine caching different prepared statements.
* (*) if server prepared statement and caching is enabled.
*/
{
int psCount = 5;
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
PreparedStatement[] testPstmts1 = new PreparedStatement[psCount];
for (int i = 0; i < psCount; i++) {
testPstmts1[i] = testConn.prepareStatement(sql1);
}
PreparedStatement testPstmt = testConn.prepareStatement(sql2);
assertEquals(6, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
assertTrue(testPstmt.isPoolable());
// Caches this PS (*).
testPstmt.close();
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
for (int i = 0; i < psCount; i++) {
assertTrue(testPstmts1[i].isPoolable());
// Caches this PS and replaces existing if same (*).
testPstmts1[i].close();
assertEquals(cachedSPS ? psCount - i + 1 : psCount - i - 1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 2 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
PreparedStatement testPstmt1 = testConn.prepareStatement(sql1);
assertEquals(cachedSPS ? 2 : 1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
for (int i = 0; i < psCount; i++) {
if (cachedSPS && i == psCount - 1) {
assertSame(testPstmts1[i], testPstmt1);
} else {
assertNotSame(testPstmts1[i], testPstmt1);
}
}
PreparedStatement testPstmt2 = testConn.prepareStatement(sql2);
assertEquals(2, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
if (cachedSPS) {
assertSame(testPstmt, testPstmt2);
} else {
assertNotSame(testPstmt, testPstmt2);
}
// Don't cache this PS (*).
testPstmt1.setPoolable(false);
// Doesn't cache this PS (*).
testPstmt1.close();
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
// Don't cache this PS (*).
testPstmt2.setPoolable(false);
// Doesn't cache this PS (*).
testPstmt2.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
} while ((useSPS = !useSPS) || (cachePS = !cachePS));
}
use of com.mysql.cj.util.LRUCache in project aws-mysql-jdbc by awslabs.
the class StatementRegressionTest method testBug87429.
/**
* Tests fix for Bug#87429 - repeated close of ServerPreparedStatement causes memory leak.
*
* Original de-cache on double close() behavior modified by:
* WL#11101 - Remove de-cache and close of SSPSs on double call to close().
*
* @throws Exception
*/
@Test
public void testBug87429() throws Exception {
Field stmtsCacheField = ConnectionImpl.class.getDeclaredField("serverSideStatementCache");
stmtsCacheField.setAccessible(true);
ToIntFunction<Connection> getStmtsCacheSize = (c) -> {
try {
LRUCache<?, ?> stmtsCacheObj = (LRUCache<?, ?>) stmtsCacheField.get(c);
return stmtsCacheObj == null ? -1 : stmtsCacheObj.size();
} catch (IllegalArgumentException | IllegalAccessException e) {
fail("Fail getting the statemets cache size.");
return -1;
}
};
final String sql1 = "SELECT 1, ?";
final String sql2 = "SELECT 2, ?";
boolean useSPS = false;
boolean cachePS = false;
do {
Properties props = new Properties();
props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name());
props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), Boolean.toString(useSPS));
props.setProperty(PropertyKey.cachePrepStmts.getKeyName(), Boolean.toString(cachePS));
props.setProperty(PropertyKey.prepStmtCacheSize.getKeyName(), "5");
boolean cachedSPS = useSPS && cachePS;
JdbcConnection testConn = (JdbcConnection) getConnectionWithProps(props);
// Single PreparedStatement, closed multiple times.
for (int i = 0; i < 100; i++) {
this.pstmt = testConn.prepareStatement(sql1);
assertTrue(this.pstmt.isPoolable());
assertEquals(1, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
// Close & cache.
this.pstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
// No-op.
this.pstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
// No-op.
this.pstmt.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 1 : -1, getStmtsCacheSize.applyAsInt(testConn));
try {
// De-caches the statement or no-op if not cached.
this.pstmt.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
this.pstmt.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
}
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
assertEquals(cachedSPS ? 0 : -1, getStmtsCacheSize.applyAsInt(testConn));
testConn = (JdbcConnection) getConnectionWithProps(props);
// Multiple PreparedStatements interchanged, two queries, closed multiple times.
for (int i = 0; i < 100; i++) {
for (int j = 1; j <= 4; j++) {
PreparedStatement pstmt1 = testConn.prepareStatement(j == 1 ? sql2 : sql1);
PreparedStatement pstmt2 = testConn.prepareStatement(j == 2 ? sql2 : sql1);
PreparedStatement pstmt3 = testConn.prepareStatement(j == 3 ? sql2 : sql1);
PreparedStatement pstmt4 = testConn.prepareStatement(j == 4 ? sql2 : sql1);
assertEquals(4, testConn.getActiveStatementCount());
// Close and cache statements successively.
pstmt4.close();
// No-op.
pstmt4.close();
assertEquals(cachedSPS ? 4 : 3, testConn.getActiveStatementCount());
pstmt3.close();
// No-op.
pstmt3.close();
assertEquals(cachedSPS ? (j >= 3 ? 4 : 3) : 2, testConn.getActiveStatementCount());
pstmt2.close();
// No-op.
pstmt2.close();
assertEquals(cachedSPS ? (j >= 2 ? 3 : 2) : 1, testConn.getActiveStatementCount());
pstmt1.close();
// No-op.
pstmt1.close();
assertEquals(cachedSPS ? 2 : 0, testConn.getActiveStatementCount());
// No-ops.
pstmt4.close();
pstmt4.close();
pstmt3.close();
pstmt3.close();
pstmt2.close();
pstmt2.close();
pstmt1.close();
pstmt1.close();
assertEquals(cachedSPS ? 2 : 0, testConn.getActiveStatementCount());
// De-cache statements successively.
try {
// De-caches the statement or no-op if not cached.
pstmt4.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS && j == 4, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
pstmt4.close();
assertEquals(cachedSPS ? (j < 4 ? 2 : 1) : 0, testConn.getActiveStatementCount());
try {
// De-caches the statement or no-op if not cached.
pstmt3.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS && j == 3, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
pstmt3.close();
assertEquals(cachedSPS ? (j < 3 ? 2 : 1) : 0, testConn.getActiveStatementCount());
try {
// De-caches the statement or no-op if not cached.
pstmt2.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS && j == 2, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
pstmt2.close();
assertEquals(cachedSPS ? 1 : 0, testConn.getActiveStatementCount());
try {
// De-caches the statement or no-op if not cached.
pstmt1.setPoolable(false);
} catch (SQLException e) {
assertFalse(cachedSPS, "Exception [" + e.getMessage() + "] not expected.");
}
// No-op.
pstmt1.close();
assertEquals(0, testConn.getActiveStatementCount());
}
}
testConn.close();
assertEquals(0, testConn.getActiveStatementCount());
} while ((useSPS = !useSPS) || (cachePS = !cachePS));
}
Aggregations