DATAREDIS-1119 - Support XINFO via ReactiveStreamCommands.

Original pull request: #519.
This commit is contained in:
Christoph Strobl
2020-03-27 08:47:27 +01:00
committed by Mark Paluch
parent c010472c42
commit 3f5533ea9a
7 changed files with 335 additions and 10 deletions

View File

@@ -41,6 +41,9 @@ import org.springframework.data.redis.connection.stream.PendingMessages;
import org.springframework.data.redis.connection.stream.PendingMessagesSummary;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoConsumer;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoGroup;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoStream;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.connection.stream.StreamReadOptions;
import org.springframework.data.redis.connection.stream.StreamRecords;
@@ -1030,6 +1033,101 @@ public interface ReactiveStreamCommands {
*/
Flux<CommandResponse<ReadCommand, Flux<ByteBufferRecord>>> read(Publisher<ReadCommand> commands);
/**
* @author Christoph Strobl
* @since 2.3
*/
class XInfoCommand extends KeyCommand {
private final @Nullable String groupName;
public XInfoCommand(@Nullable ByteBuffer key, @Nullable String groupName) {
super(key);
this.groupName = groupName;
}
public static XInfoCommand xInfo() {
return new XInfoCommand(null, null);
}
public XInfoCommand of(ByteBuffer key) {
return new XInfoCommand(key, groupName);
}
public XInfoCommand consumersIn(String groupName) {
return new XInfoCommand(getKey(), groupName);
}
public String getGroupName() {
return groupName;
}
}
/**
* Obtain general information about the stream stored at the specified {@literal key}.
*
* @param key the {@literal key} the stream is stored at.
* @return a {@link Mono} emitting {@link XInfoStream} when ready.
* @since 2.3
*/
default Mono<XInfoStream> xInfo(ByteBuffer key) {
return xInfo(Mono.just(XInfoCommand.xInfo().of(key))).next().map(CommandResponse::getOutput);
}
/**
* Obtain general information about the stream stored at the specified {@literal key}.
*
* @param commands must not be {@literal null}.
* @return never {@literal null}.
* @since 2.3
*/
Flux<CommandResponse<XInfoCommand, XInfoStream>> xInfo(Publisher<XInfoCommand> commands);
/**
* Obtain general information about the stream stored at the specified {@literal key}.
*
* @param key the {@literal key} the stream is stored at.
* @return a {@link Flux} emitting consumer group info one by one.
* @since 2.3
*/
default Flux<XInfoGroup> xInfoGroups(ByteBuffer key) {
return xInfoGroups(Mono.just(XInfoCommand.xInfo().of(key))).next().flatMapMany(CommandResponse::getOutput);
}
/**
* Obtain general information about the stream stored at the specified {@literal key}.
*
* @param commands must not be {@literal null}.
* @return never {@literal null}.
* @since 2.3
*/
Flux<CommandResponse<XInfoCommand, Flux<XInfoGroup>>> xInfoGroups(Publisher<XInfoCommand> commands);
/**
* Obtain information about every consumer in a specific {@literal consumer group} for the stream stored at the
* specified {@literal key}.
*
* @param key the {@literal key} the stream is stored at.
* @param groupName name of the {@literal consumer group}.
* @return a {@link Flux} emitting consumer info one by one.
* @since 2.3
*/
default Flux<XInfoConsumer> xInfoConsumers(ByteBuffer key, String groupName) {
return xInfoConsumers(Mono.just(XInfoCommand.xInfo().of(key).consumersIn(groupName))).next()
.flatMapMany(CommandResponse::getOutput);
}
/**
* Obtain information about every consumer in a specific {@literal consumer group} for the stream stored at the
* specified {@literal key}.
*
* @param commands must not be {@literal null}.
* @return never {@literal null}.
* @since 2.3
*/
Flux<CommandResponse<XInfoCommand, Flux<XInfoConsumer>>> xInfoConsumers(Publisher<XInfoCommand> commands);
class GroupCommand extends KeyCommand {
private final GroupCommandAction action;

View File

@@ -20,6 +20,7 @@ import io.lettuce.core.XClaimArgs;
import io.lettuce.core.XReadArgs;
import io.lettuce.core.XReadArgs.StreamOffset;
import io.lettuce.core.cluster.api.reactive.RedisClusterReactiveCommands;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoConsumer;
import reactor.core.publisher.Flux;
import java.nio.ByteBuffer;
@@ -39,6 +40,8 @@ import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.PendingMessages;
import org.springframework.data.redis.connection.stream.PendingMessagesSummary;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoGroup;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoStream;
import org.springframework.data.redis.connection.stream.StreamReadOptions;
import org.springframework.data.redis.connection.stream.StreamRecords;
import org.springframework.data.redis.util.ByteUtils;
@@ -344,6 +347,41 @@ class LettuceReactiveStreamCommands implements ReactiveStreamCommands {
.map(it -> StreamRecords.newRecord().in(it.getStream()).withId(it.getId()).ofBuffer(it.getBody()));
}
@Override
public Flux<CommandResponse<XInfoCommand, XInfoStream>> xInfo(Publisher<XInfoCommand> commands) {
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
Assert.notNull(command.getKey(), "Key must not be null!");
return cmd.xinfoStream(command.getKey()).collectList().map(XInfoStream::fromList)
.map(it -> new CommandResponse<>(command, it));
}));
}
@Override
public Flux<CommandResponse<XInfoCommand, Flux<XInfoGroup>>> xInfoGroups(Publisher<XInfoCommand> commands) {
return connection.execute(cmd -> Flux.from(commands).map(command -> {
Assert.notNull(command.getKey(), "Key must not be null!");
return new CommandResponse(command, cmd.xinfoGroups(command.getKey()).map(it -> XInfoGroup.fromList((List<Object>) it)));
}));
}
@Override
public Flux<CommandResponse<XInfoCommand, Flux<XInfoConsumer>>> xInfoConsumers(Publisher<XInfoCommand> commands) {
return connection.execute(cmd -> Flux.from(commands).map(command -> {
Assert.notNull(command.getKey(), "Key must not be null!");
ByteBuffer groupName = ByteUtils.getByteBuffer(command.getGroupName());
return new CommandResponse(command, cmd.xinfoConsumers(command.getKey(), groupName).map(it -> new XInfoConsumer(command.getGroupName(), (List<Object>) it)));
}));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveStreamCommands#xRevRange(org.reactivestreams.Publisher)

View File

@@ -315,6 +315,10 @@ public class StreamInfo {
private XInfoGroup(List<Object> raw) {
super(raw, DEFAULT_TYPE_HINTS);
}
public static XInfoGroup fromList(List<Object> raw) {
return new XInfoGroup(raw);
}
/**
* The {@literal consumer group} name. Corresponds to {@literal name}.

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.data.redis.core;
import org.springframework.data.redis.connection.stream.PendingMessages;
import org.springframework.data.redis.connection.stream.PendingMessagesSummary;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -36,9 +34,14 @@ import org.springframework.data.redis.connection.RedisZSetCommands.Limit;
import org.springframework.data.redis.connection.stream.ByteBufferRecord;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.PendingMessages;
import org.springframework.data.redis.connection.stream.PendingMessagesSummary;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.Record;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoConsumer;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoGroup;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoStream;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.connection.stream.StreamReadOptions;
import org.springframework.data.redis.hash.HashMapper;
@@ -185,6 +188,43 @@ class DefaultReactiveStreamOperations<K, HK, HV> implements ReactiveStreamOperat
return createMono(connection -> connection.xGroupDestroy(rawKey(key), group));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#consumers(java.lang.Object)
*/
@Override
public Flux<XInfoConsumer> consumers(K key, String group) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(group, "Group must not be null!");
return createFlux(connection -> connection.xInfoConsumers(rawKey(key), group));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#info(java.lang.Object)
*/
@Override
public Mono<XInfoStream> info(K key) {
Assert.notNull(key, "Key must not be null!");
return createMono(connection -> connection.xInfo(rawKey(key)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#groups(java.lang.Object)
*/
@Override
public Flux<XInfoGroup> groups(K key) {
Assert.notNull(key, "Key must not be null!");
return createFlux(connection -> connection.xInfoGroups(rawKey(key)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.StreamOperations#pending(java.lang.Object, java.lang.String, org.springframework.data.domain.Range, java.lang.Long)
@@ -218,7 +258,6 @@ class DefaultReactiveStreamOperations<K, HK, HV> implements ReactiveStreamOperat
return createMono(connection -> connection.xPending(rawKey, group));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#size(java.lang.Object)

View File

@@ -25,6 +25,9 @@ import org.reactivestreams.Publisher;
import org.springframework.data.domain.Range;
import org.springframework.data.redis.connection.RedisZSetCommands.Limit;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoConsumer;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoGroup;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoStream;
import org.springframework.data.redis.hash.HashMapper;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -194,6 +197,36 @@ public interface ReactiveStreamOperations<K, HK, HV> extends HashMapperProvider<
*/
Mono<String> destroyGroup(K key, String group);
/**
* Obtain information about every consumer in a specific {@literal consumer group} for the stream stored at the
* specified {@literal key}.
*
* @param key the {@literal key} the stream is stored at.
* @param group name of the {@literal consumer group}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.3
*/
Flux<XInfoConsumer> consumers(K key, String group);
/**
* Obtain information about {@literal consumer groups} associated with the stream stored at the specified
* {@literal key}.
*
* @param key the {@literal key} the stream is stored at.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.3
*/
Flux<XInfoGroup> groups(K key);
/**
* Obtain general information about the stream stored at the specified {@literal key}.
*
* @param key the {@literal key} the stream is stored at.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.3
*/
Mono<XInfoStream> info(K key);
/**
* Obtain the {@link PendingMessagesSummary} for a given {@literal consumer group}.
*