Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for CLIENT SETINFO, CLIENT INFO, and enhanced CLIENT LIST #2451

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,16 @@ public RedisFuture<String> clientList() {
return dispatch(commandBuilder.clientList());
}

@Override
public RedisFuture<String> clientList(ClientListArgs clientListArgs) {
return dispatch(commandBuilder.clientList(clientListArgs));
}

@Override
public RedisFuture<String> clientInfo() {
return dispatch(commandBuilder.clientInfo());
}

@Override
public RedisFuture<String> clientNoEvict(boolean on) {
return dispatch(commandBuilder.clientNoEvict(on));
Expand All @@ -360,6 +370,11 @@ public RedisFuture<String> clientSetname(K name) {
return dispatch(commandBuilder.clientSetname(name));
}

@Override
public RedisFuture<String> clientSetinfo(K key, V value) {
return dispatch(commandBuilder.clientSetinfo(key, value));
}

@Override
public RedisFuture<String> clientTracking(TrackingArgs args) {
return dispatch(commandBuilder.clientTracking(args));
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,16 @@ public Mono<String> clientList() {
return createMono(commandBuilder::clientList);
}

@Override
public Mono<String> clientList(ClientListArgs clientListArgs) {
return createMono(() -> commandBuilder.clientList(clientListArgs));
}

@Override
public Mono<String> clientInfo() {
return createMono(commandBuilder::clientInfo);
}

@Override
public Mono<String> clientNoEvict(boolean on) {
return createMono(() -> commandBuilder.clientNoEvict(on));
Expand All @@ -378,6 +388,11 @@ public Mono<String> clientSetname(K name) {
return createMono(() -> commandBuilder.clientSetname(name));
}

@Override
public Mono<String> clientSetinfo(K key, V value) {
return createMono(() -> commandBuilder.clientSetinfo(key, value));
}

@Override
public Mono<String> clientTracking(TrackingArgs args) {
return createMono(() -> commandBuilder.clientTracking(args));
Expand Down
155 changes: 155 additions & 0 deletions src/main/java/io/lettuce/core/ClientListArgs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* Copyright 2011-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.lettuce.core;

import io.lettuce.core.internal.LettuceAssert;
import io.lettuce.core.protocol.CommandArgs;
import io.lettuce.core.protocol.CommandType;

import java.util.ArrayList;
import java.util.List;

import static io.lettuce.core.protocol.CommandKeyword.ID;

/**
*
* Argument list builder for the Redis <a href="https://redis.io/commands/client-list">CLIENT LIST</a> command.
* <p>
* {@link ClientListArgs} is a mutable object and instances should be used only once to avoid shared mutable state.
*
* @author Mikhael Sokolov
* @since 6.3
*/
public class ClientListArgs implements CompositeArgument {

private enum Type {
NORMAL, MASTER, SLAVE, PUBSUB
}

private List<Long> ids;

private Type type;

/**
* Builder entry points for {@link ClientListArgs}.
*/
public static class Builder {

/**
* Utility constructor.
*/
private Builder() {
}

/**
* Creates new {@link ClientListArgs} setting {@literal client-id}.
*
* @param id client ids.
* @return new {@link ClientListArgs} with {@literal client-id} set.
* @see ClientListArgs#ids(long...)
*/
public static ClientListArgs ids(long... id) {
return new ClientListArgs().ids(id);
}

/**
* Creates new {@link ClientListArgs} setting {@literal TYPE PUBSUB}.
*
* @return new {@link ClientListArgs} with {@literal TYPE PUBSUB} set.
* @see ClientListArgs#type(Type)
*/
public static ClientListArgs typePubsub() {
return new ClientListArgs().type(Type.PUBSUB);
}

/**
* Creates new {@link ClientListArgs} setting {@literal TYPE NORMAL}.
*
* @return new {@link ClientListArgs} with {@literal TYPE NORMAL} set.
* @see ClientListArgs#type(Type)
*/
public static ClientListArgs typeNormal() {
return new ClientListArgs().type(Type.NORMAL);
}

/**
* Creates new {@link ClientListArgs} setting {@literal TYPE MASTER}.
*
* @return new {@link ClientListArgs} with {@literal TYPE MASTER} set.
* @see ClientListArgs#type(Type)
* @since 5.0.4
*/
public static ClientListArgs typeMaster() {
return new ClientListArgs().type(Type.MASTER);
}

/**
* Creates new {@link ClientListArgs} setting {@literal TYPE SLAVE}.
*
* @return new {@link ClientListArgs} with {@literal TYPE SLAVE} set.
* @see ClientListArgs#type(Type)
*/
public static ClientListArgs typeSlave() {
return new ClientListArgs().type(Type.SLAVE);
}
}

/**
* Filter the clients with its client {@code ids}.
*
* @param ids client ids
* @return {@code this} {@link ClientListArgs}.
*/
public ClientListArgs ids(long... ids) {
LettuceAssert.notNull(ids, "Ids must not be null");

this.ids = new ArrayList<>(ids.length);

for (long id : ids) {
this.ids.add(id);
}
return this;
}

/**
* This filters the connections of all the clients in the specified {@link ClientListArgs.Type}. Note that clients blocked into the
* {@literal MONITOR} command are considered to belong to the normal class.
*
* @param type must not be {@code null}.
* @return {@code this} {@link ClientListArgs}.
*/
public ClientListArgs type(Type type) {

LettuceAssert.notNull(type, "Type must not be null");

this.type = type;
return this;
}

@Override
public <K, V> void build(CommandArgs<K, V> args) {
if (ids != null && !ids.isEmpty()) {
args.add(ID);
for (Long id : ids) {
args.add(id);
}
}

if (type != null) {
args.add(CommandType.TYPE).add(type.name().toLowerCase());
}
}
}
27 changes: 23 additions & 4 deletions src/main/java/io/lettuce/core/RedisCommandBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,19 @@ Command<K, V, String> clientList() {
return createCommand(CLIENT, new StatusOutput<>(codec), args);
}

Command<K, V, String> clientList(ClientListArgs clientListArgs) {
LettuceAssert.notNull(clientListArgs, "ClientListArgs " + MUST_NOT_BE_NULL);

CommandArgs<K, V> args = new CommandArgs<>(codec).add(LIST);
clientListArgs.build(args);
return createCommand(CLIENT, new StatusOutput<>(codec), args);
}

Command<K, V, String> clientInfo() {
CommandArgs<K, V> args = new CommandArgs<>(codec).add(CommandKeyword.INFO);
return createCommand(CLIENT, new StatusOutput<>(codec), args);
}

Command<K, V, String> clientNoEvict(boolean on) {
CommandArgs<K, V> args = new CommandArgs<>(codec).add("NO-EVICT").add(on ? ON : OFF);
return createCommand(CLIENT, new StatusOutput<>(codec), args);
Expand All @@ -500,6 +513,12 @@ Command<K, V, String> clientSetname(K name) {
return createCommand(CLIENT, new StatusOutput<>(codec), args);
}

Command<K, V, String> clientSetinfo(K key, V value) {
CommandArgs<K, V> args = new CommandArgs<>(codec).add(SETINFO).addKey(key).addValue(value);
return createCommand(CLIENT, new StatusOutput<>(codec), args);
}


Command<K, V, String> clientTracking(TrackingArgs trackingArgs) {
LettuceAssert.notNull(trackingArgs, "TrackingArgs " + MUST_NOT_BE_NULL);

Expand Down Expand Up @@ -612,7 +631,7 @@ Command<K, V, List<K>> clusterGetKeysInSlot(int slot, int count) {
}

Command<K, V, String> clusterInfo() {
CommandArgs<K, V> args = new CommandArgs<>(codec).add(INFO);
CommandArgs<K, V> args = new CommandArgs<>(codec).add(CommandType.INFO);

return createCommand(CLUSTER, new StatusOutput<>(codec), args);
}
Expand Down Expand Up @@ -738,7 +757,7 @@ Command<K, V, List<Object>> commandInfo(String... commands) {
LettuceAssert.noNullElements(commands, "Commands " + MUST_NOT_CONTAIN_NULL_ELEMENTS);

CommandArgs<String, String> args = new CommandArgs<>(StringCodec.UTF8);
args.add(INFO);
args.add(CommandKeyword.INFO);

for (String command : commands) {
args.add(command);
Expand Down Expand Up @@ -1574,14 +1593,14 @@ Command<K, V, Double> incrbyfloat(K key, double amount) {
}

Command<K, V, String> info() {
return createCommand(INFO, new StatusOutput<>(codec));
return createCommand(CommandType.INFO, new StatusOutput<>(codec));
}

Command<K, V, String> info(String section) {
LettuceAssert.notNull(section, "Section " + MUST_NOT_BE_NULL);

CommandArgs<K, V> args = new CommandArgs<>(codec).add(section);
return createCommand(INFO, new StatusOutput<>(codec), args);
return createCommand(CommandType.INFO, new StatusOutput<>(codec), args);
}

Command<K, V, List<K>> keys(K pattern) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.List;
import java.util.Map;

import io.lettuce.core.ClientListArgs;
import io.lettuce.core.FlushMode;
import io.lettuce.core.KillArgs;
import io.lettuce.core.RedisFuture;
Expand Down Expand Up @@ -109,6 +110,22 @@ public interface RedisServerAsyncCommands<K, V> {
*/
RedisFuture<String> clientList();

/**
* Get the list of client connections which are filtered by {@code clientListArgs}.
*
* @return String bulk-string-reply a unique string, formatted as follows: One client connection per line (separated by LF),
* each line is composed of a succession of property=value fields separated by a space character.
*/
RedisFuture<String> clientList(ClientListArgs clientListArgs);

/**
* Get the list of the current client connection.
*
* @return String bulk-string-reply a unique string, formatted as a succession of property=value fields separated by a space character.
* @since 6.3
*/
RedisFuture<String> clientInfo();

/**
* Sets the client eviction mode for the current connection.
*
Expand All @@ -134,6 +151,16 @@ public interface RedisServerAsyncCommands<K, V> {
*/
RedisFuture<String> clientSetname(K name);

/**
* Assign various info attributes to the current connection.
*
* @param key the key.
* @param value the value.
* @return simple-string-reply {@code OK} if the connection name was successfully set.
* @since 6.3
*/
RedisFuture<String> clientSetinfo(K key, V value);

/**
* Enables the tracking feature of the Redis server, that is used for server assisted client side caching. Tracking messages
* are either available when using the RESP3 protocol or through Pub/Sub notification when using RESP2.
Expand Down Expand Up @@ -430,6 +457,7 @@ public interface RedisServerAsyncCommands<K, V> {
* @return String simple-string-reply.
* @deprecated since 6.1.7, use {@link #replicaof(String, int)} instead.
*/
@Deprecated
RedisFuture<String> slaveof(String host, int port);

/**
Expand All @@ -438,6 +466,7 @@ public interface RedisServerAsyncCommands<K, V> {
* @return String simple-string-reply.
* @deprecated since 6.1.7, use {@link #replicaofNoOne()} instead.
*/
@Deprecated
RedisFuture<String> slaveofNoOne();

/**
Expand Down
Loading