DATAREDIS-1042 - Allow consumer group creation without an existing stream.

Original pull request: #527.
This commit is contained in:
Tugdual Grall
2020-05-03 17:32:22 +02:00
committed by Mark Paluch
parent c4623ef059
commit d7b8d9b018
17 changed files with 260 additions and 13 deletions

View File

@@ -67,6 +67,7 @@ import org.springframework.util.ObjectUtils;
* @author Thomas Darimont
* @author Mark Paluch
* @author Ninad Divadkar
* @author Tugdual Grall
*/
public class DefaultStringRedisConnection implements StringRedisConnection, DecoratedRedisConnection {
@@ -3691,6 +3692,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return convertAndReturn(delegate.xGroupCreate(serialize(key), group, readOffset), identityConverter);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#xGroupCreate(java.lang.String, org.springframework.data.redis.connection.RedisStreamCommands.ReadOffset, java.lang.String, boolean)
*/
@Override
public String xGroupCreate(String key, ReadOffset readOffset, String group, boolean mkStream) {
return convertAndReturn(delegate.xGroupCreate(serialize(key), group, readOffset, mkStream), identityConverter);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#xGroupDelConsumer(java.lang.String, org.springframework.data.redis.connection.RedisStreamCommands.Consumer)
@@ -3888,6 +3898,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return delegate.xGroupCreate(key, groupName, readOffset);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStreamCommands#xGroupCreate(byte[], org.springframework.data.redis.connection.RedisStreamCommands.ReadOffset, java.lang.String, boolean)
*/
@Override
public String xGroupCreate(byte[] key, String groupName, ReadOffset readOffset, boolean mkStream) {
return delegate.xGroupCreate(key, groupName, readOffset, mkStream);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStreamCommands#xGroupDelConsumer(byte[], org.springframework.data.redis.connection.RedisStreamCommands.Consumer)

View File

@@ -54,6 +54,7 @@ import org.springframework.lang.Nullable;
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Tugdual Grall
* @since 2.0
*/
public interface DefaultedRedisConnection extends RedisConnection {
@@ -484,6 +485,13 @@ public interface DefaultedRedisConnection extends RedisConnection {
return streamCommands().xGroupCreate(key, groupName, readOffset);
}
/** @deprecated in favor of {@link RedisConnection#streamCommands()}}. */
@Override
@Deprecated
default String xGroupCreate(byte[] key, String groupName, ReadOffset readOffset, boolean mkStream) {
return streamCommands().xGroupCreate(key, groupName, readOffset, mkStream);
}
/** @deprecated in favor of {@link RedisConnection#streamCommands()}}. */
@Override
@Deprecated

View File

@@ -56,6 +56,7 @@ import org.springframework.util.StringUtils;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Tugdual Grall
* @since 2.2
*/
public interface ReactiveStreamCommands {
@@ -1134,15 +1135,22 @@ public interface ReactiveStreamCommands {
private final @Nullable String groupName;
private final @Nullable String consumerName;
private final @Nullable ReadOffset offset;
private final boolean mkStream;
public GroupCommand(@Nullable ByteBuffer key, GroupCommandAction action, @Nullable String groupName,
@Nullable String consumerName, @Nullable ReadOffset offset) {
@Nullable String consumerName, @Nullable ReadOffset offset, boolean mkStream) {
super(key);
this.action = action;
this.groupName = groupName;
this.consumerName = consumerName;
this.offset = offset;
this.mkStream = mkStream;
}
public GroupCommand(@Nullable ByteBuffer key, GroupCommandAction action, @Nullable String groupName,
@Nullable String consumerName, @Nullable ReadOffset offset) {
this(key, action, groupName, consumerName, offset, false);
}
public static GroupCommand createGroup(String group) {
@@ -1161,6 +1169,10 @@ public interface ReactiveStreamCommands {
return new GroupCommand(null, GroupCommandAction.DELETE_CONSUMER, consumer.getGroup(), consumer.getName(), null);
}
public GroupCommand makeStream(boolean mkStream) {
return new GroupCommand(getKey(), action, groupName, consumerName, offset,mkStream);
}
public GroupCommand at(ReadOffset offset) {
return new GroupCommand(getKey(), action, groupName, consumerName, offset);
}
@@ -1173,6 +1185,10 @@ public interface ReactiveStreamCommands {
return new GroupCommand(getKey(), action, groupName, consumerName, offset);
}
public boolean getMkStream() {
return this.mkStream;
}
@Nullable
public ReadOffset getReadOffset() {
return this.offset;
@@ -1211,6 +1227,20 @@ public interface ReactiveStreamCommands {
.map(CommandResponse::getOutput);
}
/**
* Create a consumer group.
*
* @param key key the {@literal key} the stream is stored at.
* @param groupName name of the consumer group to create.
* @param readOffset the offset to start at.
* @param mkStream if true the group will create the stream if needed (MKSTREAM)
* @return the {@link Mono} emitting {@literal ok} if successful.
*/
default Mono<String> xGroupCreate(ByteBuffer key, String groupName, ReadOffset readOffset, boolean mkStream) {
return xGroup(Mono.just(GroupCommand.createGroup(groupName).forStream(key).at(readOffset).makeStream(mkStream))).next()
.map(CommandResponse::getOutput);
}
/**
* Delete a consumer from a consumer group.
*

View File

@@ -39,6 +39,7 @@ import org.springframework.util.StringUtils;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Tugdual Grall
* @see <a href="https://redis.io/topics/streams-intro">Redis Documentation - Streams</a>
* @since 2.2
*/
@@ -464,6 +465,18 @@ public interface RedisStreamCommands {
@Nullable
String xGroupCreate(byte[] key, String groupName, ReadOffset readOffset);
/**
* Create a consumer group.
*
* @param key the {@literal key} the stream is stored at.
* @param groupName name of the consumer group to create.
* @param readOffset the offset to start at.
* @param mkStream if true the group will create the stream if not already present (MKSTREAM)
* @return {@literal ok} if successful. {@literal null} when used in pipeline / transaction.
*/
@Nullable
String xGroupCreate(byte[] key, String groupName, ReadOffset readOffset, boolean mkStream);
/**
* Delete a consumer from a consumer group.
*

View File

@@ -60,6 +60,7 @@ import org.springframework.lang.Nullable;
* @author David Liu
* @author Mark Paluch
* @author Ninad Divadkar
* @author Tugdual Grall
* @see RedisCallback
* @see RedisSerializer
* @see StringRedisTemplate
@@ -2109,6 +2110,19 @@ public interface StringRedisConnection extends RedisConnection {
@Nullable
String xGroupCreate(String key, ReadOffset readOffset, String group);
/**
* Create a consumer group.
*
* @param key
* @param readOffset
* @param group name of the consumer group.
* @param mkStream if true the group will create the stream if needed (MKSTREAM)
* @since
* @return {@literal true} if successful. {@literal null} when used in pipeline / transaction.
*/
@Nullable
String xGroupCreate(String key, ReadOffset readOffset, String group, boolean mkStream);
/**
* Delete a consumer from a consumer group.
*

View File

@@ -17,6 +17,7 @@ package org.springframework.data.redis.connection.lettuce;
import io.lettuce.core.XAddArgs;
import io.lettuce.core.XClaimArgs;
import io.lettuce.core.XGroupCreateArgs;
import io.lettuce.core.XReadArgs;
import io.lettuce.core.XReadArgs.StreamOffset;
import io.lettuce.core.cluster.api.reactive.RedisClusterReactiveCommands;
@@ -51,6 +52,7 @@ import org.springframework.util.Assert;
* {@link ReactiveStreamCommands} implementation for {@literal Lettuce}.
*
* @author Mark Paluch
* @author Tugdual Grall
* @since 2.2
*/
class LettuceReactiveStreamCommands implements ReactiveStreamCommands {
@@ -188,8 +190,12 @@ class LettuceReactiveStreamCommands implements ReactiveStreamCommands {
StreamOffset offset = StreamOffset.from(command.getKey(), command.getReadOffset().getOffset());
return cmd.xgroupCreate(offset, ByteUtils.getByteBuffer(command.getGroupName()))
.map(it -> new CommandResponse<>(command, it));
return cmd.xgroupCreate(offset,
ByteUtils.getByteBuffer(command.getGroupName()),
XGroupCreateArgs.Builder.mkstream( command.getMkStream()))
.map(it ->
new CommandResponse<>(command, it)
);
}
if (command.getAction().equals(GroupCommandAction.DELETE_CONSUMER)) {

View File

@@ -17,6 +17,7 @@ package org.springframework.data.redis.connection.lettuce;
import io.lettuce.core.XAddArgs;
import io.lettuce.core.XClaimArgs;
import io.lettuce.core.XGroupCreateArgs;
import io.lettuce.core.XReadArgs;
import io.lettuce.core.cluster.api.async.RedisClusterAsyncCommands;
import io.lettuce.core.cluster.api.sync.RedisClusterCommands;
@@ -47,6 +48,7 @@ import org.springframework.util.Assert;
/**
* @author Mark Paluch
* @author Tugdual Grall
* @since 2.2
*/
@RequiredArgsConstructor
@@ -216,6 +218,15 @@ class LettuceStreamCommands implements RedisStreamCommands {
*/
@Override
public String xGroupCreate(byte[] key, String groupName, ReadOffset readOffset) {
return xGroupCreate(key, groupName, readOffset, false);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStreamCommands#xGroupCreate(byte[], org.springframework.data.redis.connection.RedisStreamCommands.ReadOffset, java.lang.String, boolean)
*/
@Override
public String xGroupCreate(byte[] key, String groupName, ReadOffset readOffset, boolean mkSteam) {
Assert.notNull(key, "Key must not be null!");
Assert.hasText(groupName, "Group name must not be null or empty!");
@@ -226,15 +237,17 @@ class LettuceStreamCommands implements RedisStreamCommands {
if (isPipelined()) {
pipeline(connection
.newLettuceResult(getAsyncConnection().xgroupCreate(streamOffset, LettuceConverters.toBytes(groupName))));
.newLettuceResult(getAsyncConnection().xgroupCreate(streamOffset, LettuceConverters.toBytes(groupName),
XGroupCreateArgs.Builder.mkstream(mkSteam))));
return null;
}
if (isQueueing()) {
transaction(connection
.newLettuceResult(getAsyncConnection().xgroupCreate(streamOffset, LettuceConverters.toBytes(groupName))));
.newLettuceResult(getAsyncConnection().xgroupCreate(streamOffset, LettuceConverters.toBytes(groupName),
XGroupCreateArgs.Builder.mkstream(mkSteam))));
return null;
}
return getConnection().xgroupCreate(streamOffset, LettuceConverters.toBytes(groupName));
return getConnection().xgroupCreate(streamOffset, LettuceConverters.toBytes(groupName), XGroupCreateArgs.Builder.mkstream(mkSteam));
} catch (Exception ex) {
throw convertLettuceAccessException(ex);
}

View File

@@ -55,6 +55,7 @@ import org.springframework.util.ClassUtils;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Tugdual Grall
* @since 2.2
*/
class DefaultReactiveStreamOperations<K, HK, HV> implements ReactiveStreamOperations<K, HK, HV> {
@@ -162,12 +163,17 @@ class DefaultReactiveStreamOperations<K, HK, HV> implements ReactiveStreamOperat
@Override
public Mono<String> createGroup(K key, ReadOffset readOffset, String group) {
return createMono(connection -> connection.xGroupCreate(rawKey(key), group, readOffset, false));
}
@Override
public Mono<String> createGroup(K key, ReadOffset readOffset, String group, boolean mkStream) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(readOffset, "ReadOffset must not be null!");
Assert.notNull(group, "Group must not be null!");
return createMono(connection -> connection.xGroupCreate(rawKey(key), group, readOffset));
return createMono(connection -> connection.xGroupCreate(rawKey(key), group, readOffset, mkStream));
}
@Override

View File

@@ -50,6 +50,7 @@ import org.springframework.util.ClassUtils;
*
* @author Mark Paluch
* @author Christoph Strobl
* @autor Tugdual Grall
* @since 2.2
*/
class DefaultStreamOperations<K, HK, HV> extends AbstractOperations<K, Object> implements StreamOperations<K, HK, HV> {
@@ -162,6 +163,20 @@ class DefaultStreamOperations<K, HK, HV> extends AbstractOperations<K, Object> i
return execute(connection -> connection.xGroupCreate(rawKey, group, readOffset), true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.StreamOperations#createGroup(java.lang.Object, org.springframework.data.redis.connection.RedisStreamCommands.ReadOffset, java.lang.String, boolean)
*/
@Override
public String createGroup(K key, ReadOffset readOffset, String group, boolean mkStream) {
byte[] rawKey = rawKey(key);
if (!mkStream) {
return createGroup(key, readOffset, group);
} else {
return execute(connection -> connection.xGroupCreate(rawKey, group, readOffset, true), true);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.StreamOperations#deleteConsumer(java.lang.Object, org.springframework.data.redis.connection.RedisStreamCommands.Consumer)

View File

@@ -37,6 +37,7 @@ import org.springframework.util.Assert;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Tugdual Grall
* @since 2.2
*/
public interface ReactiveStreamOperations<K, HK, HV> extends HashMapperProvider<HK, HV> {
@@ -169,6 +170,19 @@ public interface ReactiveStreamOperations<K, HK, HV> extends HashMapperProvider<
return createGroup(key, ReadOffset.latest(), group);
}
/**
* Create a consumer group at the {@link ReadOffset#latest() latest offset}.
*
* @param key the {@literal key} the stream is stored at.
* @param group name of the consumer group.
* @param mkStream if true the group will create the stream if not already present (MKSTREAM)
* @return the {@link Mono} emitting {@literal OK} if successful.. {@literal null} when used in pipeline /
* transaction.
*/
default Mono<String> createGroup(K key, String group, boolean mkStream) {
return createGroup(key, ReadOffset.latest(), group, mkStream);
}
/**
* Create a consumer group.
*
@@ -179,6 +193,16 @@ public interface ReactiveStreamOperations<K, HK, HV> extends HashMapperProvider<
*/
Mono<String> createGroup(K key, ReadOffset readOffset, String group);
/**
* Create a consumer group.
*
* @param key the {@literal key} the stream is stored at.
* @param readOffset the {@link ReadOffset} to apply.
* @param group name of the consumer group.
* @param mkStream if true the group will create the stream if needed (MKSTREAM)
* @return the {@link Mono} emitting {@literal OK} if successful.
*/
Mono<String> createGroup(K key, ReadOffset readOffset, String group, boolean mkStream);
/**
* Delete a consumer from a consumer group.
*

View File

@@ -36,6 +36,7 @@ import org.springframework.util.Assert;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Tugdual Grall
* @since 2.2
*/
public interface StreamOperations<K, HK, HV> extends HashMapperProvider<HK, HV> {
@@ -176,6 +177,18 @@ public interface StreamOperations<K, HK, HV> extends HashMapperProvider<HK, HV>
@Nullable
String createGroup(K key, ReadOffset readOffset, String group);
/**
* Create a consumer group.
*
* @param key the {@literal key} the stream is stored at.
* @param readOffset the {@link ReadOffset} to apply.
* @param group name of the consumer group.
* @param mkStream if true the group will create the stream if needed (MKSTREAM)
* @return {@literal OK} if successful. {@literal null} when used in pipeline / transaction.
*/
@Nullable
String createGroup(K key, ReadOffset readOffset, String group, boolean mkStream);
/**
* Delete a consumer from a consumer group.
*