Skip to content

Commit acb02de

Browse files
authored
Clear MySQL prepared statements cache on connection reset (#1428)
* Clear MySQL prepared statements cache on connection reset See #1424 Otherwise, the client emits an error such as: io.vertx.mysqlclient.MySQLException: {errorMessage=Unknown prepared statement handler (1) given to mysql_stmt_precheck, errorCode=1243, sqlState=HY000} Signed-off-by: Thomas Segismont <tsegismont@gmail.com> * Add LruCacheTest#testCacheCleared Signed-off-by: Thomas Segismont <tsegismont@gmail.com> --------- Signed-off-by: Thomas Segismont <tsegismont@gmail.com>
1 parent ca64aca commit acb02de

File tree

7 files changed

+72
-12
lines changed

7 files changed

+72
-12
lines changed

vertx-mysql-client/src/main/java/io/vertx/mysqlclient/impl/MySQLSocketConnection.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import io.vertx.mysqlclient.MySQLAuthenticationPlugin;
3131
import io.vertx.mysqlclient.MySQLConnectOptions;
3232
import io.vertx.mysqlclient.SslMode;
33+
import io.vertx.mysqlclient.impl.codec.ClearCachedStatementsEvent;
3334
import io.vertx.mysqlclient.impl.codec.MySQLCodec;
3435
import io.vertx.mysqlclient.impl.codec.MySQLPacketDecoder;
3536
import io.vertx.mysqlclient.impl.command.InitialHandshakeCommand;
@@ -114,6 +115,21 @@ protected <R> void doSchedule(CommandBase<R> cmd, Handler<AsyncResult<R>> handle
114115
}
115116
}
116117

118+
@Override
119+
protected void handleMessage(Object msg) {
120+
if (msg == ClearCachedStatementsEvent.INSTANCE) {
121+
clearCachedStatements();
122+
} else {
123+
super.handleMessage(msg);
124+
}
125+
}
126+
127+
private void clearCachedStatements() {
128+
if (this.psCache != null) {
129+
this.psCache.clear();
130+
}
131+
}
132+
117133
public Future<Void> upgradeToSsl(ClientSSLOptions sslOptions) {
118134
return socket.upgradeToSsl(sslOptions);
119135
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.vertx.mysqlclient.impl.codec;
2+
3+
/**
4+
* An event that signals all cached statements must be cleared from the cache.
5+
*/
6+
public class ClearCachedStatementsEvent {
7+
8+
public static final ClearCachedStatementsEvent INSTANCE = new ClearCachedStatementsEvent();
9+
10+
private ClearCachedStatementsEvent() {
11+
// Singleton
12+
}
13+
}

vertx-mysql-client/src/main/java/io/vertx/mysqlclient/impl/codec/ResetConnectionCommandCodec.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ void encode(MySQLEncoder encoder) {
3030

3131
@Override
3232
void decodePayload(ByteBuf payload, int payloadLength) {
33+
encoder.chctx.fireChannelRead(ClearCachedStatementsEvent.INSTANCE);
3334
handleOkPacketOrErrorPacketPayload(payload);
3435
}
3536

vertx-mysql-client/src/test/java/io/vertx/mysqlclient/MySQLUtilityCommandTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import io.vertx.ext.unit.junit.VertxUnitRunner;
1818
import io.vertx.sqlclient.Row;
1919
import io.vertx.sqlclient.RowSet;
20+
import io.vertx.sqlclient.Tuple;
2021
import org.junit.After;
2122
import org.junit.Assume;
2223
import org.junit.Before;
@@ -160,6 +161,19 @@ public void testResetConnection(TestContext ctx) {
160161
}));
161162
}
162163

164+
@Test
165+
public void testResetConnectionClearsPreparedStatementCache(TestContext ctx) {
166+
Assume.assumeFalse(rule.isUsingMySQL5_6());
167+
MySQLConnectOptions connectOptions = new MySQLConnectOptions(options).setCachePreparedStatements(true);
168+
MySQLConnection.connect(vertx, connectOptions).onComplete(ctx.asyncAssertSuccess(conn -> {
169+
conn.preparedQuery("SELECT 1").execute(Tuple.tuple()).onComplete(ctx.asyncAssertSuccess(res1 -> {
170+
conn.resetConnection().onComplete(ctx.asyncAssertSuccess(rst -> {
171+
conn.preparedQuery("SELECT 1").execute(Tuple.tuple()).onComplete(ctx.asyncAssertSuccess());
172+
}));
173+
}));
174+
}));
175+
}
176+
163177
@Test
164178
public void testChangeUser(TestContext ctx) {
165179
MySQLConnection.connect(vertx, options).onComplete( ctx.asyncAssertSuccess(conn -> {

vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/cache/LruCache.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,10 @@
1111

1212
package io.vertx.sqlclient.impl.cache;
1313

14-
import io.vertx.core.AsyncResult;
15-
import io.vertx.core.Handler;
16-
import io.vertx.sqlclient.impl.PreparedStatement;
17-
18-
import java.util.ArrayList;
19-
import java.util.Collections;
20-
import java.util.Iterator;
21-
import java.util.LinkedHashMap;
22-
import java.util.List;
23-
import java.util.Map;
14+
import java.util.*;
2415

2516
/**
26-
* A LRU replacement strategy cache based on {@link java.util.LinkedHashMap} for prepared statements.
17+
* An LRU replacement strategy cache based on {@link java.util.LinkedHashMap} for prepared statements.
2718
*/
2819
public class LruCache<K, V> extends LinkedHashMap<K, V> {
2920

@@ -75,4 +66,10 @@ protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
7566
return false;
7667
}
7768
}
69+
70+
@Override
71+
public void clear() {
72+
super.clear();
73+
removed = null;
74+
}
7875
}

vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/cache/PreparedStatementCache.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,13 @@ public boolean isFull() {
6565
public int size() {
6666
return cache.size();
6767
}
68+
69+
/**
70+
* Clears the cache.
71+
* <p>
72+
* This method must be called only when the cached prepared statements have been released (e.g. with a connection reset).
73+
*/
74+
public void clear() {
75+
cache.clear();
76+
}
6877
}

vertx-sql-client/src/test/java/io/vertx/sqlclient/impl/LruCacheTest.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import java.util.List;
77

8-
import static org.junit.Assert.assertEquals;
8+
import static org.junit.Assert.*;
99

1010
public class LruCacheTest {
1111

@@ -37,4 +37,14 @@ public void testEvict() {
3737
assertEquals("value-0", evicted);
3838
assertEquals(1023, cache.size());
3939
}
40+
41+
@Test
42+
public void testCacheCleared() {
43+
LruCache<String, String> cache = new LruCache<>(42);
44+
List<String> evicted = cache.cache("foo", "bar");
45+
assertTrue(evicted.isEmpty());
46+
assertNotNull(cache.get("foo"));
47+
cache.clear();
48+
assertNull(cache.get("foo"));
49+
}
4050
}

0 commit comments

Comments
 (0)