diff --git a/src/main/java/redis/clients/jedis/BuilderFactory.java b/src/main/java/redis/clients/jedis/BuilderFactory.java index c7349f3e89..18bcc5a31c 100644 --- a/src/main/java/redis/clients/jedis/BuilderFactory.java +++ b/src/main/java/redis/clients/jedis/BuilderFactory.java @@ -997,6 +997,53 @@ public Map build(Object data) { } }; + public static final Builder> LATENCY_LATEST_RESPONSE = new Builder>() { + @Override + public Map build(Object data) { + if (data == null) { + return null; + } + + List rawList = (List) data; + Map map = new HashMap<>(rawList.size()); + + for (Object rawLatencyLatestInfo : rawList) { + if (rawLatencyLatestInfo == null) { + continue; + } + + LatencyLatestInfo latestInfo = LatencyLatestInfo.LATENCY_LATEST_BUILDER.build(rawLatencyLatestInfo); + String name = latestInfo.getCommand(); + map.put(name, latestInfo); + } + + return map; + } + }; + + public static final Builder> LATENCY_HISTORY_RESPONSE = new Builder>() { + @Override + public List build(Object data) { + if (data == null) { + return null; + } + + List rawList = (List) data; + List response = new ArrayList<>(rawList.size()); + + for (Object rawLatencyHistoryInfo : rawList) { + if (rawLatencyHistoryInfo == null) { + continue; + } + + LatencyHistoryInfo historyInfo = LatencyHistoryInfo.LATENCY_HISTORY_BUILDER.build(rawLatencyHistoryInfo); + response.add(historyInfo); + } + + return response; + } + }; + private static final Builder>> CLUSTER_SHARD_SLOTS_RANGES = new Builder>>() { @Override diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 181fe279a2..8595a14de7 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -14,6 +14,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; +import java.util.Arrays; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; @@ -9280,6 +9281,26 @@ public String latencyDoctor() { return connection.getBulkReply(); } + public Map latencyLatest() { + checkIsInMultiOrPipeline(); + connection.sendCommand(LATENCY, LATEST); + return BuilderFactory.LATENCY_LATEST_RESPONSE.build(connection.getOne()); + } + + public List latencyHistory(LatencyEvent event) { + checkIsInMultiOrPipeline(); + connection.sendCommand(new CommandArguments(LATENCY).add(HISTORY).add(event)); + return BuilderFactory.LATENCY_HISTORY_RESPONSE.build(connection.getOne()); + } + + public long latencyReset(LatencyEvent... events) { + checkIsInMultiOrPipeline(); + CommandArguments arguments = new CommandArguments(LATENCY).add(Keyword.RESET); + Arrays.stream(events).forEach(arguments::add); + connection.sendCommand(arguments); + return connection.getIntegerReply(); + } + @Override public StreamEntryID xadd(final String key, final StreamEntryID id, final Map hash) { checkIsInMultiOrPipeline(); diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index cba60115b3..24830637fb 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -299,7 +299,7 @@ public static enum Keyword implements Rawable { REV, WITHCOORD, WITHDIST, WITHHASH, ANY, FROMMEMBER, FROMLONLAT, BYRADIUS, BYBOX, BYLEX, BYSCORE, STOREDIST, TO, FORCE, TIMEOUT, DB, UNLOAD, ABORT, IDX, MINMATCHLEN, WITHMATCHLEN, FULL, DELETE, LIBRARYNAME, WITHCODE, DESCRIPTION, GETKEYS, GETKEYSANDFLAGS, DOCS, FILTERBY, DUMP, - MODULE, ACLCAT, PATTERN, DOCTOR, USAGE, SAMPLES, PURGE, STATS, LOADEX, CONFIG, ARGS, RANK, + 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; diff --git a/src/main/java/redis/clients/jedis/args/LatencyEvent.java b/src/main/java/redis/clients/jedis/args/LatencyEvent.java new file mode 100644 index 0000000000..bd11d6a104 --- /dev/null +++ b/src/main/java/redis/clients/jedis/args/LatencyEvent.java @@ -0,0 +1,24 @@ +package redis.clients.jedis.args; + +import redis.clients.jedis.util.SafeEncoder; + +public enum LatencyEvent implements Rawable { + + ACTIVE_DEFRAG_CYCLE("active-defrag-cycle"), AOF_FSYNC_ALWAYS("aof-fsync-always"), AOF_STAT("aof-stat"), + AOF_REWRITE_DIFF_WRITE("aof-rewrite-diff-write"), AOF_RENAME("aof-rename"), AOF_WRITE("aof-write"), + AOF_WRITE_ACTIVE_CHILD("aof-write-active-child"), AOF_WRITE_ALONE("aof-write-alone"), + AOF_WRITE_PENDING_FSYNC("aof-write-pending-fsync"), COMMAND("command"), EXPIRE_CYCLE("expire-cycle"), + EVICTION_CYCLE("eviction-cycle"), EVICTION_DEL("eviction-del"), FAST_COMMAND("fast-command"), + FORK("fork"), RDB_UNLINK_TEMP_FILE("rdb-unlink-temp-file"); + + private final byte[] raw; + + private LatencyEvent(String s) { + raw = SafeEncoder.encode(s); + } + + @Override + public byte[] getRaw() { + return raw; + } +} diff --git a/src/main/java/redis/clients/jedis/commands/ServerCommands.java b/src/main/java/redis/clients/jedis/commands/ServerCommands.java index 511357ac1d..3aeff1153b 100644 --- a/src/main/java/redis/clients/jedis/commands/ServerCommands.java +++ b/src/main/java/redis/clients/jedis/commands/ServerCommands.java @@ -1,12 +1,18 @@ package redis.clients.jedis.commands; import redis.clients.jedis.args.FlushMode; +import redis.clients.jedis.args.LatencyEvent; import redis.clients.jedis.args.SaveMode; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.params.LolwutParams; import redis.clients.jedis.params.ShutdownParams; +import redis.clients.jedis.resps.LatencyHistoryInfo; +import redis.clients.jedis.resps.LatencyLatestInfo; import redis.clients.jedis.util.KeyValue; +import java.util.List; +import java.util.Map; + public interface ServerCommands { /** @@ -246,4 +252,10 @@ default void shutdown(SaveMode saveMode) throws JedisException { * @return the report */ String latencyDoctor(); + + Map latencyLatest(); + + List latencyHistory(LatencyEvent events); + + long latencyReset(LatencyEvent... events); } diff --git a/src/main/java/redis/clients/jedis/resps/LatencyHistoryInfo.java b/src/main/java/redis/clients/jedis/resps/LatencyHistoryInfo.java new file mode 100644 index 0000000000..68867e74e6 --- /dev/null +++ b/src/main/java/redis/clients/jedis/resps/LatencyHistoryInfo.java @@ -0,0 +1,38 @@ +package redis.clients.jedis.resps; + +import redis.clients.jedis.Builder; + +import java.util.List; + +import static redis.clients.jedis.BuilderFactory.LONG; + +public class LatencyHistoryInfo { + + private final long timestamp; + private final long latency; + + public LatencyHistoryInfo(long timestamp, long latency) { + this.timestamp = timestamp; + this.latency = latency; + } + + public long getTimestamp() { + return timestamp; + } + + public long getLatency() { + return latency; + } + + public static final Builder LATENCY_HISTORY_BUILDER = new Builder() { + @Override + public LatencyHistoryInfo build(Object data) { + List commandData = (List) data; + + long timestamp = LONG.build(commandData.get(0)); + long latency = LONG.build(commandData.get(1)); + + return new LatencyHistoryInfo(timestamp, latency); + } + }; +} diff --git a/src/main/java/redis/clients/jedis/resps/LatencyLatestInfo.java b/src/main/java/redis/clients/jedis/resps/LatencyLatestInfo.java new file mode 100644 index 0000000000..b441ea9d75 --- /dev/null +++ b/src/main/java/redis/clients/jedis/resps/LatencyLatestInfo.java @@ -0,0 +1,53 @@ +package redis.clients.jedis.resps; + +import redis.clients.jedis.Builder; + +import java.util.List; + +import static redis.clients.jedis.BuilderFactory.LONG; +import static redis.clients.jedis.BuilderFactory.STRING; + +public class LatencyLatestInfo { + + private final String command; + private final long timestamp; + private final long lastEventLatency; + private final long maxEventLatency; + + public LatencyLatestInfo(String command, long timestamp, long lastEventLatency, long maxEventLatency) { + this.command = command; + this.timestamp = timestamp; + this.lastEventLatency = lastEventLatency; + this.maxEventLatency = maxEventLatency; + } + + public String getCommand() { + return command; + } + + public long getTimestamp() { + return timestamp; + } + + public long getLastEventLatency() { + return lastEventLatency; + } + + public long getMaxEventLatency() { + return maxEventLatency; + } + + public static final Builder LATENCY_LATEST_BUILDER = new Builder() { + @Override + public LatencyLatestInfo build(Object data) { + List commandData = (List) data; + + String command = STRING.build(commandData.get(0)); + long timestamp = LONG.build(commandData.get(1)); + long lastEventLatency = LONG.build(commandData.get(2)); + long maxEventLatency = LONG.build(commandData.get(3)); + + return new LatencyLatestInfo(command, timestamp, lastEventLatency, maxEventLatency); + } + }; +} diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ControlCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ControlCommandsTest.java index df3fef6886..cb95a9489b 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ControlCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ControlCommandsTest.java @@ -30,12 +30,15 @@ import redis.clients.jedis.JedisMonitor; import redis.clients.jedis.Protocol; import redis.clients.jedis.args.ClientPauseMode; +import redis.clients.jedis.args.LatencyEvent; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.HostAndPorts; import redis.clients.jedis.params.CommandListFilterByParams; import redis.clients.jedis.params.LolwutParams; import redis.clients.jedis.resps.CommandDocument; import redis.clients.jedis.resps.CommandInfo; +import redis.clients.jedis.resps.LatencyHistoryInfo; +import redis.clients.jedis.resps.LatencyLatestInfo; import redis.clients.jedis.util.AssertUtil; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.SafeEncoder; @@ -440,6 +443,23 @@ public void latencyDoctor() { assertNotNull(report); } + @Test + public void latencyLatest() { + Map report = jedis.latencyLatest(); + assertNotNull(report); + } + + @Test + public void latencyHistoryFork() { + List report = jedis.latencyHistory(LatencyEvent.FORK); + assertNotNull(report); + } + + @Test + public void latencyReset() { + assertTrue(jedis.latencyReset() >= 0); + } + @Test public void commandCount() { assertTrue(jedis.commandCount() > 100);