Repository: redis/lettuce
Pull request #2782 (2024-03-12 15:08:56)

Add support CLIENT KILL [MAXAGE]
closes [https://github.com/lettuce-io/lettuce-core/issues/2765](https://github.com/lettuce-io/lettuce-core/issues/2765)

<!--
Thank you for proposing a pull request. This template will guide you through the essential steps necessary for a pull request.
-->
Make sure that:

- [x] You have read the [contribution guidelines](https://github.com/lettuce-io/lettuce-core/blob/main/.github/CONTRIBUTING.md).
- [x] You have created a feature request first to discuss your contribution intent. Please reference the feature request ticket number in the pull request.
- [x] You use the code formatters provided [here](https://github.com/lettuce-io/lettuce-core/blob/main/formatting.xml) and have them applied to your changes. Don’t submit any formatting related changes.
- [x] You submit test cases (unit or integration tests) that back your changes.

 <!--
Great! Live long and prosper.
-->


tishun: @mp911de should we merge this one?
mp911de: I have a few changes to propose:

* Set `@since` tags to 7.0
* Improve the test arrangement as a Redis node can have multiple inactive clients. We should be able to clearly identify the clients to disconnect

Other than that, it's fine to merge this PR.
tishun: @dengliming would you be able to address the changes @mp911de suggested?

Patch:
diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml
index 3846e6008..692738de4 100644
--- a/.github/workflows/cicd.yaml
+++ b/.github/workflows/cicd.yaml
@@ -36,9 +36,7 @@ jobs:
           java-version: 8
       - name: Install missing dependencies to container
         run: |
-          : # workaround for apt update issue related to Clearsigned file
-          : # details on https://github.com/orgs/community/discussions/120966#discussioncomment-9211925
-          sudo apt update || true
+          sudo apt update
           sudo apt install -y stunnel make git gcc
       - name: Maven offline
         run: |
diff --git a/src/main/java/io/lettuce/core/KillArgs.java b/src/main/java/io/lettuce/core/KillArgs.java
index 2a71b051d..d24325c50 100644
--- a/src/main/java/io/lettuce/core/KillArgs.java
+++ b/src/main/java/io/lettuce/core/KillArgs.java
@@ -54,6 +54,8 @@ public class KillArgs implements CompositeArgument {
 
     private String username;
 
+    private Long maxAge;
+
     /**
      * Builder entry points for {@link KillArgs}.
      */
@@ -160,6 +162,17 @@ public class KillArgs implements CompositeArgument {
             return new KillArgs().user(username);
         }
 
+        /**
+         * Creates new {@link KillArgs} setting {@literal MAXAGE}.
+         *
+         * @return new {@link KillArgs} with {@literal MAXAGE} set.
+         * @see KillArgs#maxAge(Long)
+         * @since 7.0
+         */
+        public static KillArgs maxAge(Long maxAge) {
+            return new KillArgs().maxAge(maxAge);
+        }
+
     }
 
     /**
@@ -241,6 +254,21 @@ public class KillArgs implements CompositeArgument {
         return this;
     }
 
+    /**
+     * Closes all the connections that are older than the specified age, in seconds.
+     *
+     * @param maxAge must not be {@code null}.
+     * @return {@code this} {@link KillArgs}.
+     * @since 7.0
+     */
+    public KillArgs maxAge(Long maxAge) {
+
+        LettuceAssert.notNull(maxAge, "MaxAge must not be null");
+
+        this.maxAge = maxAge;
+        return this;
+    }
+
     /**
      * Closes all the connections that are authenticated with the specified ACL {@code username}.
      *
@@ -282,6 +310,10 @@ public class KillArgs implements CompositeArgument {
         if (username != null) {
             args.add("USER").add(username);
         }
+
+        if (maxAge != null) {
+            args.add("MAXAGE").add(maxAge);
+        }
     }
 
 }


Test patch:
diff --git a/src/test/java/io/lettuce/core/commands/ServerCommandIntegrationTests.java b/src/test/java/io/lettuce/core/commands/ServerCommandIntegrationTests.java
index 610079abf..b9be862e7 100644
--- a/src/test/java/io/lettuce/core/commands/ServerCommandIntegrationTests.java
+++ b/src/test/java/io/lettuce/core/commands/ServerCommandIntegrationTests.java
@@ -33,6 +33,7 @@ import java.util.regex.Pattern;
 
 import javax.inject.Inject;
 
+import io.lettuce.test.condition.RedisConditions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -186,6 +187,24 @@ public class ServerCommandIntegrationTests extends TestSupport {
         redis.aclDeluser("test_kill");
     }
 
+    @Test
+    void clientKillMaxAge() throws InterruptedException {
+        // can not find other new command to use `@EnabledOnCommand` for now, so check the version
+        assumeTrue(RedisConditions.of(redis).hasVersionGreaterOrEqualsTo("8.0"));
+
+        RedisCommands<String, String> connection2 = client.connect().sync();
+        long inactiveId = connection2.clientId();
+        long maxAge = 2L;
+        // sleep for maxAge * 2 seconds, to be sure
+        TimeUnit.SECONDS.sleep(maxAge * 2);
+        RedisCommands<String, String> connection3 = client.connect().sync();
+        long activeId = connection3.clientId();
+        assertThat(redis.clientKill(KillArgs.Builder.maxAge(maxAge))).isGreaterThan(0);
+
+        assertThat(redis.clientList(ClientListArgs.Builder.ids(inactiveId))).isBlank();
+        assertThat(redis.clientList(ClientListArgs.Builder.ids(activeId))).isNotBlank();
+    }
+
     @Test
     void clientId() {
         assertThat(redis.clientId()).isNotNull();