Commit ea85ae80 authored by Todd Tingen's avatar Todd Tingen Committed by Nick Craver

Eliminate the need for Stream magic strings & added additional XGROUP… (#878)

* Eliminate the need for Stream magic strings & added additional XGROUP subcommands.

- Replaced the need for magic strings with Range, ReadOffset, and GroupReadOffset structs.
- Condensed some of the method signatures due to the new structs.
- Added methods & tests for two of the XGROUP subcommands (DESTROY & DELCONSUMER).

* Updated tests with their original IDs used.

* Use a single struct to represent stream position for Group, Read, and ReadGroup commands.

- Consolidated GroupCreateOptions, GroupReadOffset, and ReadOffset into a single struct, Position.
- Removed the "Pair" structs and created StreamPosition, this will be used in the multi-stream read commands.
- Renamed RedisStreamEntry to StreamEntry.
- Also added the remaining subcommand for the XGROUP command (SETID) and an associated unit test.

* File cleanup, delete consolidated structs.

* Additional changes and unit tests for the Position struct.

* Update PositionKind enum with explicit zero value.
parent 1ea6c217
...@@ -828,6 +828,13 @@ public void StreamClaimMessagesReturningIds() ...@@ -828,6 +828,13 @@ public void StreamClaimMessagesReturningIds()
mock.Verify(_ => _.StreamClaimIdsOnly("prefix:key", "group", "consumer", 1000, messageIds, CommandFlags.HighPriority)); mock.Verify(_ => _.StreamClaimIdsOnly("prefix:key", "group", "consumer", 1000, messageIds, CommandFlags.HighPriority));
} }
[Fact]
public void StreamConsumerGroupSetPosition()
{
wrapper.StreamConsumerGroupSetPosition("key", "group", Position.Beginning, CommandFlags.HighPriority);
mock.Verify(_ => _.StreamConsumerGroupSetPosition("prefix:key", "group", Position.Beginning, CommandFlags.HighPriority));
}
[Fact] [Fact]
public void StreamConsumerInfoGet() public void StreamConsumerInfoGet()
{ {
...@@ -838,8 +845,8 @@ public void StreamConsumerInfoGet() ...@@ -838,8 +845,8 @@ public void StreamConsumerInfoGet()
[Fact] [Fact]
public void StreamCreateConsumerGroup() public void StreamCreateConsumerGroup()
{ {
wrapper.StreamCreateConsumerGroup("key", "group", "0-0", CommandFlags.HighPriority); wrapper.StreamCreateConsumerGroup("key", "group", Position.Beginning, CommandFlags.HighPriority);
mock.Verify(_ => _.StreamCreateConsumerGroup("prefix:key", "group", "0-0", CommandFlags.HighPriority)); mock.Verify(_ => _.StreamCreateConsumerGroup("prefix:key", "group", Position.Beginning, CommandFlags.HighPriority));
} }
[Fact] [Fact]
...@@ -871,6 +878,20 @@ public void StreamMessagesDelete() ...@@ -871,6 +878,20 @@ public void StreamMessagesDelete()
mock.Verify(_ => _.StreamDelete("prefix:key", messageIds, CommandFlags.HighPriority)); mock.Verify(_ => _.StreamDelete("prefix:key", messageIds, CommandFlags.HighPriority));
} }
[Fact]
public void StreamDeleteConsumer()
{
wrapper.StreamDeleteConsumer("key", "group", "consumer", CommandFlags.HighPriority);
mock.Verify(_ => _.StreamDeleteConsumer("prefix:key", "group", "consumer", CommandFlags.HighPriority));
}
[Fact]
public void StreamDeleteConsumerGroup()
{
wrapper.StreamDeleteConsumerGroup("key", "group", CommandFlags.HighPriority);
mock.Verify(_ => _.StreamDeleteConsumerGroup("prefix:key", "group", CommandFlags.HighPriority));
}
[Fact] [Fact]
public void StreamPendingInfoGet() public void StreamPendingInfoGet()
{ {
...@@ -881,37 +902,45 @@ public void StreamPendingInfoGet() ...@@ -881,37 +902,45 @@ public void StreamPendingInfoGet()
[Fact] [Fact]
public void StreamPendingMessageInfoGet() public void StreamPendingMessageInfoGet()
{ {
wrapper.StreamPendingMessages("key", "group", 10, RedisValue.Null, null, null, CommandFlags.HighPriority); wrapper.StreamPendingMessages("key", "group", 10, RedisValue.Null, "-", "+", CommandFlags.HighPriority);
mock.Verify(_ => _.StreamPendingMessages("prefix:key", "group", 10, RedisValue.Null, null, null, CommandFlags.HighPriority)); mock.Verify(_ => _.StreamPendingMessages("prefix:key", "group", 10, RedisValue.Null, "-", "+", CommandFlags.HighPriority));
} }
[Fact] [Fact]
public void StreamRange() public void StreamRange()
{ {
wrapper.StreamRange("key", "-", "+", null, Order.Ascending, CommandFlags.HighPriority); wrapper.StreamRange("key", "-", "+", null, Order.Ascending, CommandFlags.HighPriority);
mock.Verify(_ => _.StreamRange("prefix:key", "-", "+",null, Order.Ascending, CommandFlags.HighPriority)); mock.Verify(_ => _.StreamRange("prefix:key", "-", "+", null, Order.Ascending, CommandFlags.HighPriority));
} }
[Fact] [Fact]
public void StreamRead_1() public void StreamRead_1()
{ {
var keysAndIds = new StreamIdPair[0] { }; var streamPositions = new StreamPosition[0] { };
wrapper.StreamRead(keysAndIds, null, CommandFlags.HighPriority); wrapper.StreamRead(streamPositions, null, CommandFlags.HighPriority);
mock.Verify(_ => _.StreamRead(keysAndIds, null, CommandFlags.HighPriority)); mock.Verify(_ => _.StreamRead(streamPositions, null, CommandFlags.HighPriority));
} }
[Fact] [Fact]
public void StreamRead_2() public void StreamRead_2()
{ {
wrapper.StreamRead("key", "0-0", null, CommandFlags.HighPriority); wrapper.StreamRead("key", new Position("0-0"), null, CommandFlags.HighPriority);
mock.Verify(_ => _.StreamRead("prefix:key", "0-0", null, CommandFlags.HighPriority)); mock.Verify(_ => _.StreamRead("prefix:key", new Position("0-0"), null, CommandFlags.HighPriority));
}
[Fact]
public void StreamStreamReadGroup_1()
{
wrapper.StreamReadGroup("key", "group", "consumer", new Position("0-0"), 10, CommandFlags.HighPriority);
mock.Verify(_ => _.StreamReadGroup("prefix:key", "group", "consumer", new Position("0-0"), 10, CommandFlags.HighPriority));
} }
[Fact] [Fact]
public void StreamStreamReadGroup() public void StreamStreamReadGroup_2()
{ {
wrapper.StreamReadGroup("key", "group", "consumer", "0-0", 10, CommandFlags.HighPriority); var streamPositions = new StreamPosition[0] { };
mock.Verify(_ => _.StreamReadGroup("prefix:key", "group", "consumer", "0-0", 10, CommandFlags.HighPriority)); wrapper.StreamReadGroup(streamPositions, "group", "consumer", 10, CommandFlags.HighPriority);
mock.Verify(_ => _.StreamReadGroup(streamPositions, "group", "consumer", 10, CommandFlags.HighPriority));
} }
[Fact] [Fact]
......
This diff is collapsed.
...@@ -793,11 +793,18 @@ public void StreamConsumerInfoGetAsync() ...@@ -793,11 +793,18 @@ public void StreamConsumerInfoGetAsync()
mock.Verify(_ => _.StreamConsumerInfoAsync("prefix:key", "group", CommandFlags.HighPriority)); mock.Verify(_ => _.StreamConsumerInfoAsync("prefix:key", "group", CommandFlags.HighPriority));
} }
[Fact]
public void StreamConsumerGroupSetPositionAsync()
{
wrapper.StreamConsumerGroupSetPositionAsync("key", "group", Position.Beginning, CommandFlags.HighPriority);
mock.Verify(_ => _.StreamConsumerGroupSetPositionAsync("prefix:key", "group", Position.Beginning, CommandFlags.HighPriority));
}
[Fact] [Fact]
public void StreamCreateConsumerGroupAsync() public void StreamCreateConsumerGroupAsync()
{ {
wrapper.StreamCreateConsumerGroupAsync("key", "group", "0-0", CommandFlags.HighPriority); wrapper.StreamCreateConsumerGroupAsync("key", "group", new Position("0-0"), CommandFlags.HighPriority);
mock.Verify(_ => _.StreamCreateConsumerGroupAsync("prefix:key", "group", "0-0", CommandFlags.HighPriority)); mock.Verify(_ => _.StreamCreateConsumerGroupAsync("prefix:key", "group", new Position("0-0"), CommandFlags.HighPriority));
} }
[Fact] [Fact]
...@@ -829,6 +836,20 @@ public void StreamMessagesDeleteAsync() ...@@ -829,6 +836,20 @@ public void StreamMessagesDeleteAsync()
mock.Verify(_ => _.StreamDeleteAsync("prefix:key", messageIds, CommandFlags.HighPriority)); mock.Verify(_ => _.StreamDeleteAsync("prefix:key", messageIds, CommandFlags.HighPriority));
} }
[Fact]
public void StreamDeleteConsumerAsync()
{
wrapper.StreamDeleteConsumerAsync("key", "group", "consumer", CommandFlags.HighPriority);
mock.Verify(_ => _.StreamDeleteConsumerAsync("prefix:key", "group", "consumer", CommandFlags.HighPriority));
}
[Fact]
public void StreamDeleteConsumerGroupAsync()
{
wrapper.StreamDeleteConsumerGroupAsync("key", "group", CommandFlags.HighPriority);
mock.Verify(_ => _.StreamDeleteConsumerGroupAsync("prefix:key", "group", CommandFlags.HighPriority));
}
[Fact] [Fact]
public void StreamPendingInfoGetAsync() public void StreamPendingInfoGetAsync()
{ {
...@@ -853,23 +874,31 @@ public void StreamRangeAsync() ...@@ -853,23 +874,31 @@ public void StreamRangeAsync()
[Fact] [Fact]
public void StreamReadAsync_1() public void StreamReadAsync_1()
{ {
var keysAndIds = new StreamIdPair[0] { }; var streamPositions = new StreamPosition[0] { };
wrapper.StreamReadAsync(keysAndIds, null, CommandFlags.HighPriority); wrapper.StreamReadAsync(streamPositions, null, CommandFlags.HighPriority);
mock.Verify(_ => _.StreamReadAsync(keysAndIds, null, CommandFlags.HighPriority)); mock.Verify(_ => _.StreamReadAsync(streamPositions, null, CommandFlags.HighPriority));
} }
[Fact] [Fact]
public void StreamReadAsync_2() public void StreamReadAsync_2()
{ {
wrapper.StreamReadAsync("key", "0-0", null, CommandFlags.HighPriority); wrapper.StreamReadAsync("key", new Position("0-0"), null, CommandFlags.HighPriority);
mock.Verify(_ => _.StreamReadAsync("prefix:key", "0-0", null, CommandFlags.HighPriority)); mock.Verify(_ => _.StreamReadAsync("prefix:key", new Position("0-0"), null, CommandFlags.HighPriority));
}
[Fact]
public void StreamReadGroupAsync_1()
{
wrapper.StreamReadGroupAsync("key", "group", "consumer", Position.Beginning, 10, CommandFlags.HighPriority);
mock.Verify(_ => _.StreamReadGroupAsync("prefix:key", "group", "consumer", Position.Beginning, 10, CommandFlags.HighPriority));
} }
[Fact] [Fact]
public void StreamReadGroupAsync() public void StreamStreamReadGroupAsync_2()
{ {
wrapper.StreamReadGroupAsync("key", "group", "consumer", "0-0", 10, CommandFlags.HighPriority); var streamPositions = new StreamPosition[0] { };
mock.Verify(_ => _.StreamReadGroupAsync("prefix:key", "group", "consumer", "0-0", 10, CommandFlags.HighPriority)); wrapper.StreamReadGroupAsync(streamPositions, "group", "consumer", 10, CommandFlags.HighPriority);
mock.Verify(_ => _.StreamReadGroupAsync(streamPositions, "group", "consumer", 10, CommandFlags.HighPriority));
} }
[Fact] [Fact]
......
namespace StackExchange.Redis
{
internal enum PositionKind
{
Beginning = 0,
Explicit = 1,
New = 2
}
}
...@@ -607,7 +607,7 @@ public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisVal ...@@ -607,7 +607,7 @@ public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisVal
return Inner.StreamAdd(ToInner(key), streamPairs, messageId, maxLength, useApproximateMaxLength, flags); return Inner.StreamAdd(ToInner(key), streamPairs, messageId, maxLength, useApproximateMaxLength, flags);
} }
public RedisStreamEntry[] StreamClaim(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) public StreamEntry[] StreamClaim(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamClaim(ToInner(key), consumerGroup, claimingConsumer, minIdleTimeInMs, messageIds, flags); return Inner.StreamClaim(ToInner(key), consumerGroup, claimingConsumer, minIdleTimeInMs, messageIds, flags);
} }
...@@ -617,9 +617,14 @@ public RedisValue[] StreamClaimIdsOnly(RedisKey key, RedisValue consumerGroup, R ...@@ -617,9 +617,14 @@ public RedisValue[] StreamClaimIdsOnly(RedisKey key, RedisValue consumerGroup, R
return Inner.StreamClaimIdsOnly(ToInner(key), consumerGroup, claimingConsumer, minIdleTimeInMs, messageIds, flags); return Inner.StreamClaimIdsOnly(ToInner(key), consumerGroup, claimingConsumer, minIdleTimeInMs, messageIds, flags);
} }
public bool StreamCreateConsumerGroup(RedisKey key, RedisValue groupName, RedisValue? readFrom = null, CommandFlags flags = CommandFlags.None) public bool StreamConsumerGroupSetPosition(RedisKey key, RedisValue groupName, Position position, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamCreateConsumerGroup(ToInner(key), groupName, readFrom, flags); return Inner.StreamConsumerGroupSetPosition(ToInner(key), groupName, position, flags);
}
public bool StreamCreateConsumerGroup(RedisKey key, RedisValue groupName, Position? position = null, CommandFlags flags = CommandFlags.None)
{
return Inner.StreamCreateConsumerGroup(ToInner(key), groupName, position, flags);
} }
public StreamInfo StreamInfo(RedisKey key, CommandFlags flags = CommandFlags.None) public StreamInfo StreamInfo(RedisKey key, CommandFlags flags = CommandFlags.None)
...@@ -647,6 +652,16 @@ public long StreamDelete(RedisKey key, RedisValue[] messageIds, CommandFlags fla ...@@ -647,6 +652,16 @@ public long StreamDelete(RedisKey key, RedisValue[] messageIds, CommandFlags fla
return Inner.StreamDelete(ToInner(key), messageIds, flags); return Inner.StreamDelete(ToInner(key), messageIds, flags);
} }
public long StreamDeleteConsumer(RedisKey key, RedisValue groupName, RedisValue consumerName, CommandFlags flags = CommandFlags.None)
{
return Inner.StreamDeleteConsumer(ToInner(key), groupName, consumerName, flags);
}
public bool StreamDeleteConsumerGroup(RedisKey key, RedisValue groupName, CommandFlags flags = CommandFlags.None)
{
return Inner.StreamDeleteConsumerGroup(ToInner(key), groupName, flags);
}
public StreamPendingInfo StreamPending(RedisKey key, RedisValue groupName, CommandFlags flags = CommandFlags.None) public StreamPendingInfo StreamPending(RedisKey key, RedisValue groupName, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamPending(ToInner(key), groupName, flags); return Inner.StreamPending(ToInner(key), groupName, flags);
...@@ -657,24 +672,29 @@ public StreamPendingMessageInfo[] StreamPendingMessages(RedisKey key, RedisValue ...@@ -657,24 +672,29 @@ public StreamPendingMessageInfo[] StreamPendingMessages(RedisKey key, RedisValue
return Inner.StreamPendingMessages(ToInner(key), groupName, count, consumerName, minId, maxId, flags); return Inner.StreamPendingMessages(ToInner(key), groupName, count, consumerName, minId, maxId, flags);
} }
public RedisStreamEntry[] StreamRange(RedisKey key, RedisValue? minId = null, RedisValue? maxId = null, int? count = null, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) public StreamEntry[] StreamRange(RedisKey key, RedisValue? minId = null, RedisValue? maxId = null, int? count = null, Order messageOrder = Order.Ascending, CommandFlags flags = CommandFlags.None)
{
return Inner.StreamRange(ToInner(key), minId, maxId, count, messageOrder, flags);
}
public StreamEntry[] StreamRead(RedisKey key, Position position, int? count = null, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamRange(ToInner(key), minId, maxId, count, order, flags); return Inner.StreamRead(ToInner(key), position, count, flags);
} }
public RedisStreamEntry[] StreamRead(RedisKey key, RedisValue afterId, int? count = null, CommandFlags flags = CommandFlags.None) public RedisStream[] StreamRead(StreamPosition[] streamPositions, int? countPerStream = null, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamRead(ToInner(key), afterId, count, flags); return Inner.StreamRead(streamPositions, countPerStream, flags);
} }
public RedisStream[] StreamRead(StreamIdPair[] streamIdPairs, int? countPerStream = null, CommandFlags flags = CommandFlags.None) public StreamEntry[] StreamReadGroup(RedisKey key, RedisValue groupName, RedisValue consumerName, Position? position = null, int? count = null, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamRead(streamIdPairs, countPerStream, flags); return Inner.StreamReadGroup(ToInner(key), groupName, consumerName, position, count, flags);
} }
public RedisStreamEntry[] StreamReadGroup(RedisKey key, RedisValue groupName, RedisValue consumerName, RedisValue? readFromId = null, int? count = null, CommandFlags flags = CommandFlags.None) public RedisStream[] StreamReadGroup(StreamPosition[] streamPositions, RedisValue groupName, RedisValue consumerName, int? countPerStream = null, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamReadGroup(ToInner(key), groupName, consumerName, readFromId, count, flags); return Inner.StreamReadGroup(streamPositions, groupName, consumerName, countPerStream, flags);
} }
public long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) public long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None)
......
...@@ -586,7 +586,7 @@ public Task<RedisValue> StreamAddAsync(RedisKey key, NameValueEntry[] streamPair ...@@ -586,7 +586,7 @@ public Task<RedisValue> StreamAddAsync(RedisKey key, NameValueEntry[] streamPair
return Inner.StreamAddAsync(ToInner(key), streamPairs, messageId, maxLength, useApproximateMaxLength, flags); return Inner.StreamAddAsync(ToInner(key), streamPairs, messageId, maxLength, useApproximateMaxLength, flags);
} }
public Task<RedisStreamEntry[]> StreamClaimAsync(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) public Task<StreamEntry[]> StreamClaimAsync(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamClaimAsync(ToInner(key), consumerGroup, claimingConsumer, minIdleTimeInMs, messageIds, flags); return Inner.StreamClaimAsync(ToInner(key), consumerGroup, claimingConsumer, minIdleTimeInMs, messageIds, flags);
} }
...@@ -596,9 +596,14 @@ public Task<RedisValue[]> StreamClaimIdsOnlyAsync(RedisKey key, RedisValue consu ...@@ -596,9 +596,14 @@ public Task<RedisValue[]> StreamClaimIdsOnlyAsync(RedisKey key, RedisValue consu
return Inner.StreamClaimIdsOnlyAsync(ToInner(key), consumerGroup, claimingConsumer, minIdleTimeInMs, messageIds, flags); return Inner.StreamClaimIdsOnlyAsync(ToInner(key), consumerGroup, claimingConsumer, minIdleTimeInMs, messageIds, flags);
} }
public Task<bool> StreamCreateConsumerGroupAsync(RedisKey key, RedisValue groupName, RedisValue? readFrom = null, CommandFlags flags = CommandFlags.None) public Task<bool> StreamConsumerGroupSetPositionAsync(RedisKey key, RedisValue groupName, Position position, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamCreateConsumerGroupAsync(ToInner(key), groupName, readFrom, flags); return Inner.StreamConsumerGroupSetPositionAsync(ToInner(key), groupName, position, flags);
}
public Task<bool> StreamCreateConsumerGroupAsync(RedisKey key, RedisValue groupName, Position? position = null, CommandFlags flags = CommandFlags.None)
{
return Inner.StreamCreateConsumerGroupAsync(ToInner(key), groupName, position, flags);
} }
public Task<StreamInfo> StreamInfoAsync(RedisKey key, CommandFlags flags = CommandFlags.None) public Task<StreamInfo> StreamInfoAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
...@@ -626,6 +631,16 @@ public Task<long> StreamDeleteAsync(RedisKey key, RedisValue[] messageIds, Comma ...@@ -626,6 +631,16 @@ public Task<long> StreamDeleteAsync(RedisKey key, RedisValue[] messageIds, Comma
return Inner.StreamDeleteAsync(ToInner(key), messageIds, flags); return Inner.StreamDeleteAsync(ToInner(key), messageIds, flags);
} }
public Task<long> StreamDeleteConsumerAsync(RedisKey key, RedisValue groupName, RedisValue consumerName, CommandFlags flags = CommandFlags.None)
{
return Inner.StreamDeleteConsumerAsync(ToInner(key), groupName, consumerName, flags);
}
public Task<bool> StreamDeleteConsumerGroupAsync(RedisKey key, RedisValue groupName, CommandFlags flags = CommandFlags.None)
{
return Inner.StreamDeleteConsumerGroupAsync(ToInner(key), groupName, flags);
}
public Task<StreamPendingInfo> StreamPendingAsync(RedisKey key, RedisValue groupName, CommandFlags flags = CommandFlags.None) public Task<StreamPendingInfo> StreamPendingAsync(RedisKey key, RedisValue groupName, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamPendingAsync(ToInner(key), groupName, flags); return Inner.StreamPendingAsync(ToInner(key), groupName, flags);
...@@ -636,24 +651,29 @@ public Task<StreamPendingMessageInfo[]> StreamPendingMessagesAsync(RedisKey key, ...@@ -636,24 +651,29 @@ public Task<StreamPendingMessageInfo[]> StreamPendingMessagesAsync(RedisKey key,
return Inner.StreamPendingMessagesAsync(ToInner(key), groupName, count, consumerName, minId, maxId, flags); return Inner.StreamPendingMessagesAsync(ToInner(key), groupName, count, consumerName, minId, maxId, flags);
} }
public Task<RedisStreamEntry[]> StreamRangeAsync(RedisKey key, RedisValue? minId = null, RedisValue? maxId = null, int? count = null, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) public Task<StreamEntry[]> StreamRangeAsync(RedisKey key, RedisValue? minId = null, RedisValue? maxId = null, int? count = null, Order messageOrder = Order.Ascending, CommandFlags flags = CommandFlags.None)
{
return Inner.StreamRangeAsync(ToInner(key), minId, maxId, count, messageOrder, flags);
}
public Task<StreamEntry[]> StreamReadAsync(RedisKey key, Position position, int? count = null, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamRangeAsync(ToInner(key), minId, maxId, count, order, flags); return Inner.StreamReadAsync(ToInner(key), position, count, flags);
} }
public Task<RedisStreamEntry[]> StreamReadAsync(RedisKey key, RedisValue afterId, int? count = null, CommandFlags flags = CommandFlags.None) public Task<RedisStream[]> StreamReadAsync(StreamPosition[] streamPositions, int? countPerStream = null, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamReadAsync(ToInner(key), afterId, count, flags); return Inner.StreamReadAsync(streamPositions, countPerStream, flags);
} }
public Task<RedisStream[]> StreamReadAsync(StreamIdPair[] streamIdPairs, int? countPerStream = null, CommandFlags flags = CommandFlags.None) public Task<StreamEntry[]> StreamReadGroupAsync(RedisKey key, RedisValue groupName, RedisValue consumerName, Position? position = null, int? count = null, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamReadAsync(streamIdPairs, countPerStream, flags); return Inner.StreamReadGroupAsync(ToInner(key), groupName, consumerName, position, count, flags);
} }
public Task<RedisStreamEntry[]> StreamReadGroupAsync(RedisKey key, RedisValue groupName, RedisValue consumerName, RedisValue? readFromId = null, int? count = null, CommandFlags flags = CommandFlags.None) public Task<RedisStream[]> StreamReadGroupAsync(StreamPosition[] streamPositions, RedisValue groupName, RedisValue consumerName, int? countPerStream = null, CommandFlags flags = CommandFlags.None)
{ {
return Inner.StreamReadGroupAsync(ToInner(key), groupName, consumerName, readFromId, count, flags); return Inner.StreamReadGroupAsync(streamPositions, groupName, consumerName, countPerStream, flags);
} }
public Task<long> StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) public Task<long> StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None)
......
using System;
namespace StackExchange.Redis
{
/// <summary>
/// A position within a stream. Defaults to <see cref="Position.New"/>.
/// </summary>
public struct Position
{
/// <summary>
/// Indicate a position from which to read a stream.
/// </summary>
/// <param name="readAfter">The position from which to read a stream.</param>
public Position(RedisValue readAfter)
{
if (readAfter == RedisValue.Null) throw new ArgumentNullException(nameof(readAfter), "readAfter cannot be RedisValue.Null.");
Kind = PositionKind.Explicit;
ExplicitValue = readAfter;
}
private Position(PositionKind kind)
{
Kind = kind;
ExplicitValue = RedisValue.Null;
}
private PositionKind Kind { get; }
private RedisValue ExplicitValue { get; }
/// <summary>
/// Read new messages.
/// </summary>
public static Position New = new Position(PositionKind.New);
/// <summary>
/// Read from the beginning of a stream.
/// </summary>
public static Position Beginning = new Position(PositionKind.Beginning);
internal RedisValue ResolveForCommand(RedisCommand command)
{
if (Kind == PositionKind.Explicit) return ExplicitValue;
if (Kind == PositionKind.Beginning) return StreamConstants.ReadMinValue;
// PositionKind.New
if (command == RedisCommand.XREAD) throw new InvalidOperationException("Position.New cannot be used with StreamRead.");
if (command == RedisCommand.XREADGROUP) return StreamConstants.UndeliveredMessages;
if (command == RedisCommand.XGROUP) return StreamConstants.NewMessages;
throw new ArgumentException($"Unsupported command in ResolveForCommand: {command}.", nameof(command));
}
}
}
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
/// </summary> /// </summary>
public struct RedisStream public struct RedisStream
{ {
internal RedisStream(RedisKey key, RedisStreamEntry[] entries) internal RedisStream(RedisKey key, StreamEntry[] entries)
{ {
Key = key; Key = key;
Entries = entries; Entries = entries;
...@@ -19,6 +19,6 @@ internal RedisStream(RedisKey key, RedisStreamEntry[] entries) ...@@ -19,6 +19,6 @@ internal RedisStream(RedisKey key, RedisStreamEntry[] entries)
/// <summary> /// <summary>
/// An arry of entries contained within the stream. /// An arry of entries contained within the stream.
/// </summary> /// </summary>
public RedisStreamEntry[] Entries { get; } public StreamEntry[] Entries { get; }
} }
} }
...@@ -1325,7 +1325,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes ...@@ -1325,7 +1325,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
} }
} }
internal sealed class SingleStreamProcessor : StreamProcessorBase<RedisStreamEntry[]> internal sealed class SingleStreamProcessor : StreamProcessorBase<StreamEntry[]>
{ {
private bool skipStreamName; private bool skipStreamName;
...@@ -1339,7 +1339,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes ...@@ -1339,7 +1339,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
if (result.IsNull) if (result.IsNull)
{ {
// Server returns 'nil' if no entries are returned for the given stream. // Server returns 'nil' if no entries are returned for the given stream.
SetResult(message, new RedisStreamEntry[0]); SetResult(message, new StreamEntry[0]);
return true; return true;
} }
...@@ -1348,7 +1348,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes ...@@ -1348,7 +1348,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
return false; return false;
} }
RedisStreamEntry[] entries = null; StreamEntry[] entries = null;
if (skipStreamName) if (skipStreamName)
{ {
...@@ -1667,7 +1667,7 @@ internal abstract class StreamProcessorBase<T> : ResultProcessor<T> ...@@ -1667,7 +1667,7 @@ internal abstract class StreamProcessorBase<T> : ResultProcessor<T>
{ {
// For command response formats see https://redis.io/topics/streams-intro. // For command response formats see https://redis.io/topics/streams-intro.
protected RedisStreamEntry[] ParseRedisStreamEntries(RawResult result) protected StreamEntry[] ParseRedisStreamEntries(RawResult result)
{ {
if (result.Type != ResultType.MultiBulk) if (result.Type != ResultType.MultiBulk)
{ {
...@@ -1680,7 +1680,7 @@ protected RedisStreamEntry[] ParseRedisStreamEntries(RawResult result) ...@@ -1680,7 +1680,7 @@ protected RedisStreamEntry[] ParseRedisStreamEntries(RawResult result)
{ {
if (item.IsNull || item.Type != ResultType.MultiBulk) if (item.IsNull || item.Type != ResultType.MultiBulk)
{ {
return RedisStreamEntry.Null; return StreamEntry.Null;
} }
// Process the Multibulk array for each entry. The entry contains the following elements: // Process the Multibulk array for each entry. The entry contains the following elements:
...@@ -1688,7 +1688,7 @@ protected RedisStreamEntry[] ParseRedisStreamEntries(RawResult result) ...@@ -1688,7 +1688,7 @@ protected RedisStreamEntry[] ParseRedisStreamEntries(RawResult result)
// [1] = Multibulk array of the name/value pairs of the stream entry's data // [1] = Multibulk array of the name/value pairs of the stream entry's data
var entryDetails = item.GetItems(); var entryDetails = item.GetItems();
return new RedisStreamEntry(id: entryDetails[0].AsRedisValue(), return new StreamEntry(id: entryDetails[0].AsRedisValue(),
values: ParseStreamEntryValues(entryDetails[1])); values: ParseStreamEntryValues(entryDetails[1]));
}); });
} }
......
...@@ -42,12 +42,18 @@ internal static class StreamConstants ...@@ -42,12 +42,18 @@ internal static class StreamConstants
internal static readonly RedisValue Create = "CREATE"; internal static readonly RedisValue Create = "CREATE";
internal static readonly RedisValue DeleteConsumer = "DELCONSUMER";
internal static readonly RedisValue Destroy = "DESTROY";
internal static readonly RedisValue Group = "GROUP"; internal static readonly RedisValue Group = "GROUP";
internal static readonly RedisValue Groups = "GROUPS"; internal static readonly RedisValue Groups = "GROUPS";
internal static readonly RedisValue JustId = "JUSTID"; internal static readonly RedisValue JustId = "JUSTID";
internal static readonly RedisValue SetId = "SETID";
internal static readonly RedisValue MaxLen = "MAXLEN"; internal static readonly RedisValue MaxLen = "MAXLEN";
internal static readonly RedisValue Stream = "STREAM"; internal static readonly RedisValue Stream = "STREAM";
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
/// <summary> /// <summary>
/// Describes an entry contained in a Redis Stream. /// Describes an entry contained in a Redis Stream.
/// </summary> /// </summary>
public struct RedisStreamEntry public struct StreamEntry
{ {
internal RedisStreamEntry(RedisValue id, NameValueEntry[] values) internal StreamEntry(RedisValue id, NameValueEntry[] values)
{ {
Id = id; Id = id;
Values = values; Values = values;
...@@ -14,7 +14,7 @@ internal RedisStreamEntry(RedisValue id, NameValueEntry[] values) ...@@ -14,7 +14,7 @@ internal RedisStreamEntry(RedisValue id, NameValueEntry[] values)
/// <summary> /// <summary>
/// A null stream entry. /// A null stream entry.
/// </summary> /// </summary>
public static RedisStreamEntry Null { get; } = new RedisStreamEntry(RedisValue.Null, null); public static StreamEntry Null { get; } = new StreamEntry(RedisValue.Null, null);
/// <summary> /// <summary>
/// The ID assigned to the message. /// The ID assigned to the message.
......
...@@ -10,8 +10,8 @@ public struct StreamInfo ...@@ -10,8 +10,8 @@ public struct StreamInfo
int radixTreeKeys, int radixTreeKeys,
int radixTreeNodes, int radixTreeNodes,
int groups, int groups,
RedisStreamEntry firstEntry, StreamEntry firstEntry,
RedisStreamEntry lastEntry) StreamEntry lastEntry)
{ {
Length = length; Length = length;
RadixTreeKeys = radixTreeKeys; RadixTreeKeys = radixTreeKeys;
...@@ -44,11 +44,11 @@ public struct StreamInfo ...@@ -44,11 +44,11 @@ public struct StreamInfo
/// <summary> /// <summary>
/// The first entry in the stream. /// The first entry in the stream.
/// </summary> /// </summary>
public RedisStreamEntry FirstEntry { get; } public StreamEntry FirstEntry { get; }
/// <summary> /// <summary>
/// The last entry in the stream. /// The last entry in the stream.
/// </summary> /// </summary>
public RedisStreamEntry LastEntry { get; } public StreamEntry LastEntry { get; }
} }
} }
 namespace StackExchange.Redis
namespace StackExchange.Redis
{ {
/// <summary> /// <summary>
/// Describes a pair consisting of the Stream Key and the ID from which to read. /// Describes a pair consisting of the Stream Key and the <see cref="Position"/> from which to begin reading a stream.
/// </summary> /// </summary>
/// <remarks><see cref="IDatabase.StreamRead(StreamIdPair[], int?, CommandFlags)"/></remarks> public struct StreamPosition
public struct StreamIdPair
{ {
/// <summary> /// <summary>
/// Initializes a <see cref="StreamIdPair"/> value. /// Initializes a <see cref="StreamPosition"/> value.
/// </summary> /// </summary>
/// <param name="key">The key for the stream.</param> /// <param name="key">The key for the stream.</param>
/// <param name="id">The ID from which to begin reading the stream.</param> /// <param name="position">The position from which to begin reading the stream.</param>
public StreamIdPair(RedisKey key, RedisValue id) public StreamPosition(RedisKey key, Position position)
{ {
Key = key; Key = key;
Id = id; Position = position;
} }
/// <summary> /// <summary>
/// The key for the stream. /// The stream key.
/// </summary> /// </summary>
public RedisKey Key { get; } public RedisKey Key { get; }
/// <summary> /// <summary>
/// The ID from which to begin reading the stream. /// The offset at which to begin reading the stream.
/// </summary> /// </summary>
public RedisValue Id { get; } public Position Position { get; }
/// <summary>
/// See Object.ToString()
/// </summary>
public override string ToString() => $"{Key}: {Id}";
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment