From c21f27417fb24429efde47776b3f6123275e8871 Mon Sep 17 00:00:00 2001 From: Gabriel Erzse Date: Tue, 5 Mar 2024 09:41:07 +0200 Subject: [PATCH 1/3] Support the MAXAGE option for CLIENT KILL Issue #3731 Starting with Redis 7.6, the CLIENT KILL command has a new option, called MAXAGE, to kill clients older than a given age. Add support for this new option. --- .../java/redis/clients/jedis/Protocol.java | 2 +- .../jedis/commands/ClientCommands.java | 6 ++--- .../jedis/params/ClientKillParams.java | 10 ++++++++ .../commands/jedis/ClientCommandsTest.java | 24 +++++++++++++++++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index 31a93e3ebb..531ee768c9 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -301,7 +301,7 @@ public static enum Keyword implements Rawable { DELETE, LIBRARYNAME, WITHCODE, DESCRIPTION, GETKEYS, GETKEYSANDFLAGS, DOCS, FILTERBY, DUMP, MODULE, ACLCAT, PATTERN, DOCTOR, LATEST, HISTORY, USAGE, SAMPLES, PURGE, STATS, LOADEX, CONFIG, ARGS, RANK, NOW, VERSION, ADDR, SKIPME, USER, LADDR, - CHANNELS, NUMPAT, NUMSUB, SHARDCHANNELS, SHARDNUMSUB, NOVALUES; + CHANNELS, NUMPAT, NUMSUB, SHARDCHANNELS, SHARDNUMSUB, NOVALUES, MAXAGE; private final byte[] raw; diff --git a/src/main/java/redis/clients/jedis/commands/ClientCommands.java b/src/main/java/redis/clients/jedis/commands/ClientCommands.java index edcfbd602e..75bda24a30 100644 --- a/src/main/java/redis/clients/jedis/commands/ClientCommands.java +++ b/src/main/java/redis/clients/jedis/commands/ClientCommands.java @@ -30,10 +30,10 @@ public interface ClientCommands { String clientKill(String ip, int port); /** - * Close a given client connection. + * Close client connections based on certain selection parameters. * - * @param params Connection info will be closed - * @return Close success return OK + * @param params Parameters defining what client connections to close. + * @return The number of client connections that were closed. */ long clientKill(ClientKillParams params); diff --git a/src/main/java/redis/clients/jedis/params/ClientKillParams.java b/src/main/java/redis/clients/jedis/params/ClientKillParams.java index 12c65be882..0082ef3340 100644 --- a/src/main/java/redis/clients/jedis/params/ClientKillParams.java +++ b/src/main/java/redis/clients/jedis/params/ClientKillParams.java @@ -67,6 +67,16 @@ public ClientKillParams laddr(String ip, int port) { return addParam(Keyword.LADDR, ip + ':' + port); } + /** + * Kill clients older than {@code maxAge} seconds. + * + * @param maxAge Clients older than this number of seconds will be killed. + * @return The {@code ClientKillParams} instance, for call chaining. + */ + public ClientKillParams maxAge(long maxAge) { + return addParam(Keyword.MAXAGE, maxAge); + } + @Override public void addParams(CommandArguments args) { params.forEach(kv -> args.add(kv.getKey()).add(kv.getValue())); diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java index 8c3f35db49..95c024d0fa 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java @@ -233,6 +233,26 @@ public void killUser() { } } + @Test + public void killMaxAge() throws InterruptedException { + long maxAge = 2; + + // sleep twice the maxAge, to be sure + Thread.sleep(maxAge * 2 * 1000); + + Jedis client2 = new Jedis(hnp.getHost(), hnp.getPort(), 500); + client2.auth("foobared"); + + long killedClients = jedis.clientKill(new ClientKillParams().maxAge(maxAge)); + + // The reality is that some tests leak clients, so we can't assert + // on the exact number of killed clients. + assertTrue(killedClients > 0); + + assertDisconnected(client); + assertConnected(client2); + } + @Test public void clientInfo() { String info = client.clientInfo(); @@ -267,6 +287,10 @@ private void assertDisconnected(Jedis j) { } } + private void assertConnected(Jedis j) { + assertEquals("PONG", j.ping()); + } + private String findInClientList() { for (String clientInfo : jedis.clientList().split("\n")) { if (pattern.matcher(clientInfo).find()) { From 22850710d3fef4a0b7640097d533279130b7a52a Mon Sep 17 00:00:00 2001 From: Gabriel Erzse Date: Thu, 7 Mar 2024 13:07:55 +0200 Subject: [PATCH 2/3] Ensure clients are closed in some tests --- .../commands/jedis/ClientCommandsTest.java | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java index 95c024d0fa..d47b284baa 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java @@ -222,14 +222,15 @@ public void killAddrIpPort() { @Test public void killUser() { - Jedis client2 = new Jedis(hnp.getHost(), hnp.getPort(), 500); - client.aclSetUser("test_kill", "on", "+acl", ">password1"); - try { - client2.auth("test_kill", "password1"); - assertEquals(1, jedis.clientKill(new ClientKillParams().user("test_kill"))); - assertDisconnected(client2); - } finally { - jedis.aclDelUser("test_kill"); + try (Jedis client2 = new Jedis(hnp.getHost(), hnp.getPort(), 500)) { + client.aclSetUser("test_kill", "on", "+acl", ">password1"); + try { + client2.auth("test_kill", "password1"); + assertEquals(1, jedis.clientKill(new ClientKillParams().user("test_kill"))); + assertDisconnected(client2); + } finally { + jedis.aclDelUser("test_kill"); + } } } @@ -240,17 +241,18 @@ public void killMaxAge() throws InterruptedException { // sleep twice the maxAge, to be sure Thread.sleep(maxAge * 2 * 1000); - Jedis client2 = new Jedis(hnp.getHost(), hnp.getPort(), 500); - client2.auth("foobared"); + try (Jedis client2 = new Jedis(hnp.getHost(), hnp.getPort(), 500)) { + client2.auth("foobared"); - long killedClients = jedis.clientKill(new ClientKillParams().maxAge(maxAge)); + long killedClients = jedis.clientKill(new ClientKillParams().maxAge(maxAge)); - // The reality is that some tests leak clients, so we can't assert - // on the exact number of killed clients. - assertTrue(killedClients > 0); + // The reality is that some tests leak clients, so we can't assert + // on the exact number of killed clients. + assertTrue(killedClients > 0); - assertDisconnected(client); - assertConnected(client2); + assertDisconnected(client); + assertConnected(client2); + } } @Test From 95efbffcd2b12d5a066e78083b0bba5e6412f381 Mon Sep 17 00:00:00 2001 From: Gabriel Erzse Date: Thu, 7 Mar 2024 18:51:21 +0200 Subject: [PATCH 3/3] More concise unit test code Co-authored-by: M Sazzadul Hoque <7600764+sazzad16@users.noreply.github.com> --- .../jedis/commands/jedis/ClientCommandsTest.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java index d47b284baa..cf7760dbf4 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java @@ -222,15 +222,14 @@ public void killAddrIpPort() { @Test public void killUser() { + client.aclSetUser("test_kill", "on", "+acl", ">password1"); try (Jedis client2 = new Jedis(hnp.getHost(), hnp.getPort(), 500)) { - client.aclSetUser("test_kill", "on", "+acl", ">password1"); - try { - client2.auth("test_kill", "password1"); - assertEquals(1, jedis.clientKill(new ClientKillParams().user("test_kill"))); - assertDisconnected(client2); - } finally { - jedis.aclDelUser("test_kill"); - } + client2.auth("test_kill", "password1"); + + assertEquals(1, jedis.clientKill(new ClientKillParams().user("test_kill"))); + assertDisconnected(client2); + } finally { + jedis.aclDelUser("test_kill"); } }