From 093eb9bbf7b9fba6ad9a218739828494916115af Mon Sep 17 00:00:00 2001 From: Kijana Woodard Date: Tue, 21 Jan 2025 16:18:00 -0600 Subject: [PATCH 01/13] add minId overloads --- .../Interfaces/IDatabase.cs | 10 ++++++++++ .../Interfaces/IDatabaseAsync.cs | 3 +++ .../KeyspaceIsolation/KeyPrefixed.cs | 3 +++ .../KeyspaceIsolation/KeyPrefixedDatabase.cs | 3 +++ .../PublicAPI/PublicAPI.Unshipped.txt | 4 +++- src/StackExchange.Redis/RedisDatabase.cs | 14 +++++++++++++ src/StackExchange.Redis/StreamConstants.cs | 1 + .../KeyPrefixedDatabaseTests.cs | 7 +++++++ .../KeyPrefixedTests.cs | 7 +++++++ .../StackExchange.Redis.Tests/StreamTests.cs | 20 +++++++++++++++++++ 10 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 207c03326..fd4136b19 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2775,6 +2775,16 @@ IEnumerable SortedSetScan( /// long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None); + /// + /// Trim the stream to a specified minimum timestamp. + /// + /// The key of the stream. + /// All entries less than minId will be removed. + /// The flags to use for this operation. + /// The number of messages removed from the stream. + /// + long StreamTrim(RedisKey key, RedisValue minId, CommandFlags flags); + /// /// If key already exists and is a string, this command appends the value at the end of the string. /// If key does not exist it is created and set as an empty string, so APPEND will be similar to SET in this special case. diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 9852c131c..26a97c78e 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -672,6 +672,9 @@ IAsyncEnumerable SortedSetScanAsync( /// Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None); + /// + Task StreamTrimAsync(RedisKey key, RedisValue minId, CommandFlags flags); + /// Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs index f45c29886..6eac7c1d0 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs @@ -642,6 +642,9 @@ public Task StreamReadGroupAsync(StreamPosition[] streamPositions public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => Inner.StreamTrimAsync(ToInner(key), maxLength, useApproximateMaxLength, flags); + public Task StreamTrimAsync(RedisKey key, RedisValue minId, CommandFlags flags) => + Inner.StreamTrimAsync(ToInner(key), minId, flags); + public Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) => Inner.StringAppendAsync(ToInner(key), value, flags); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs index a19dd0b7a..72d84107d 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs @@ -624,6 +624,9 @@ public RedisStream[] StreamReadGroup(StreamPosition[] streamPositions, RedisValu public long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => Inner.StreamTrim(ToInner(key), maxLength, useApproximateMaxLength, flags); + public long StreamTrim(RedisKey key, RedisValue minId, CommandFlags flags) => + Inner.StreamTrim(ToInner(key), minId, flags); + public long StringAppend(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) => Inner.StringAppend(ToInner(key), value, flags); diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt index 91b0e1a43..1439e293b 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt @@ -1 +1,3 @@ -#nullable enable \ No newline at end of file +#nullable enable +StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, StackExchange.Redis.CommandFlags flags) -> long +StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! \ No newline at end of file diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 716176662..e7048fadb 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -3007,6 +3007,20 @@ public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproxima return ExecuteAsync(msg, ResultProcessor.Int64); } + public long StreamTrim(RedisKey key, RedisValue minId, CommandFlags flags = CommandFlags.None) + { + var values = new[] { StreamConstants.MinId, minId }; + var msg = Message.Create(Database, flags, RedisCommand.XTRIM, key, values); + return ExecuteSync(msg, ResultProcessor.Int64); + } + + public Task StreamTrimAsync(RedisKey key, RedisValue minId, CommandFlags flags = CommandFlags.None) + { + var values = new[] { StreamConstants.MinId, minId }; + var msg = Message.Create(Database, flags, RedisCommand.XTRIM, key, values); + return ExecuteAsync(msg, ResultProcessor.Int64); + } + public long StringAppend(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { var msg = Message.Create(Database, flags, RedisCommand.APPEND, key, value); diff --git a/src/StackExchange.Redis/StreamConstants.cs b/src/StackExchange.Redis/StreamConstants.cs index 74650e010..3f34c2e5e 100644 --- a/src/StackExchange.Redis/StreamConstants.cs +++ b/src/StackExchange.Redis/StreamConstants.cs @@ -59,6 +59,7 @@ internal static class StreamConstants internal static readonly RedisValue SetId = "SETID"; internal static readonly RedisValue MaxLen = "MAXLEN"; + internal static readonly RedisValue MinId = "MINID"; internal static readonly RedisValue MkStream = "MKSTREAM"; diff --git a/tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs b/tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs index 2552ac7aa..146ed4369 100644 --- a/tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs +++ b/tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs @@ -1202,6 +1202,13 @@ public void StreamTrim() mock.Received().StreamTrim("prefix:key", 1000, true, CommandFlags.None); } + [Fact] + public void StreamTrimMinId() + { + prefixed.StreamTrim("key", 1111111111, CommandFlags.None); + mock.Received().StreamTrim("prefix:key", 1111111111, CommandFlags.None); + } + [Fact] public void StringAppend() { diff --git a/tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs b/tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs index e768b9ec5..9446b771a 100644 --- a/tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs +++ b/tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs @@ -1118,6 +1118,13 @@ public async Task StreamTrimAsync() await mock.Received().StreamTrimAsync("prefix:key", 1000, true, CommandFlags.None); } + [Fact] + public async Task StreamTrimMinIdAsync() + { + await prefixed.StreamTrimAsync("key", 1111111111, CommandFlags.None); + await mock.Received().StreamTrimAsync("prefix:key", 1111111111, CommandFlags.None); + } + [Fact] public async Task StringAppendAsync() { diff --git a/tests/StackExchange.Redis.Tests/StreamTests.cs b/tests/StackExchange.Redis.Tests/StreamTests.cs index 0ea744848..73c9820c8 100644 --- a/tests/StackExchange.Redis.Tests/StreamTests.cs +++ b/tests/StackExchange.Redis.Tests/StreamTests.cs @@ -1916,6 +1916,26 @@ public void StreamTrimLength() Assert.Equal(1, len); } + [Fact] + public void StreamTrimMinId() + { + using var conn = Create(require: RedisFeatures.v5_0_0); + + var db = conn.GetDatabase(); + var key = Me(); + + // Add a couple items and check length. + db.StreamAdd(key, "field1", "value1", 1111111110); + db.StreamAdd(key, "field2", "value2", 1111111111); + db.StreamAdd(key, "field3", "value3", 1111111112); + + var numRemoved = db.StreamTrim(key, 1111111111, CommandFlags.None); + var len = db.StreamLength(key); + + Assert.Equal(2, numRemoved); + Assert.Equal(1, len); + } + [Fact] public void StreamVerifyLength() { From 09678d9706fecfee66fb21938e2b3556604ebc06 Mon Sep 17 00:00:00 2001 From: Kijana Woodard Date: Tue, 21 Jan 2025 16:30:17 -0600 Subject: [PATCH 02/13] fix test. clarify doc comment i now think the XTRIM documentation is saying that an entry at exactly MINID is kept. https://redis.io/docs/latest/commands/xtrim/ --- src/StackExchange.Redis/Interfaces/IDatabase.cs | 2 +- tests/StackExchange.Redis.Tests/StreamTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index fd4136b19..18ae15cd1 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2779,7 +2779,7 @@ IEnumerable SortedSetScan( /// Trim the stream to a specified minimum timestamp. /// /// The key of the stream. - /// All entries less than minId will be removed. + /// All entries with an id (timestamp) earlier minId will be removed. /// The flags to use for this operation. /// The number of messages removed from the stream. /// diff --git a/tests/StackExchange.Redis.Tests/StreamTests.cs b/tests/StackExchange.Redis.Tests/StreamTests.cs index 73c9820c8..c417bf7b4 100644 --- a/tests/StackExchange.Redis.Tests/StreamTests.cs +++ b/tests/StackExchange.Redis.Tests/StreamTests.cs @@ -1932,7 +1932,7 @@ public void StreamTrimMinId() var numRemoved = db.StreamTrim(key, 1111111111, CommandFlags.None); var len = db.StreamLength(key); - Assert.Equal(2, numRemoved); + Assert.Equal(1, numRemoved); Assert.Equal(1, len); } From 263be321dfec3bbb23aad29636154760b7b7baa5 Mon Sep 17 00:00:00 2001 From: Kijana Woodard Date: Tue, 21 Jan 2025 16:42:43 -0600 Subject: [PATCH 03/13] fix test. forgot to update the length check. --- tests/StackExchange.Redis.Tests/StreamTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/StackExchange.Redis.Tests/StreamTests.cs b/tests/StackExchange.Redis.Tests/StreamTests.cs index c417bf7b4..635c845f9 100644 --- a/tests/StackExchange.Redis.Tests/StreamTests.cs +++ b/tests/StackExchange.Redis.Tests/StreamTests.cs @@ -1933,7 +1933,7 @@ public void StreamTrimMinId() var len = db.StreamLength(key); Assert.Equal(1, numRemoved); - Assert.Equal(1, len); + Assert.Equal(2, len); } [Fact] From 606e30f9091d0abbe34e823e90e6e7b5875ad243 Mon Sep 17 00:00:00 2001 From: Kijana Woodard Date: Fri, 4 Jul 2025 12:54:22 -0500 Subject: [PATCH 04/13] change method name to StreamTrimByMinIdAsync implement useApproximateMaxLength and limit as per docs --- .../Interfaces/IDatabase.cs | 4 +- .../Interfaces/IDatabaseAsync.cs | 4 +- .../KeyspaceIsolation/KeyPrefixed.cs | 4 +- .../KeyspaceIsolation/KeyPrefixedDatabase.cs | 4 +- .../PublicAPI/PublicAPI.Unshipped.txt | 4 +- src/StackExchange.Redis/RedisDatabase.cs | 44 ++++++++++++++++--- .../KeyPrefixedDatabaseTests.cs | 20 +++++++-- .../KeyPrefixedTests.cs | 20 +++++++-- .../StackExchange.Redis.Tests/StreamTests.cs | 27 +++++++++++- 9 files changed, 108 insertions(+), 23 deletions(-) diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 18ae15cd1..ee50213f4 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2780,10 +2780,12 @@ IEnumerable SortedSetScan( /// /// The key of the stream. /// All entries with an id (timestamp) earlier minId will be removed. + /// If true, the "~" argument is used to allow the stream to exceed minId by a small number. This improves performance when removing messages. + /// The maximum number of entries to remove per call when useApproximateMaxLength = true. If 0, the limiting mechanism is disabled entirely. /// The flags to use for this operation. /// The number of messages removed from the stream. /// - long StreamTrim(RedisKey key, RedisValue minId, CommandFlags flags); + long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, CommandFlags flags = CommandFlags.None); /// /// If key already exists and is a string, this command appends the value at the end of the string. diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 26a97c78e..e81f1a464 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -672,8 +672,8 @@ IAsyncEnumerable SortedSetScanAsync( /// Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None); - /// - Task StreamTrimAsync(RedisKey key, RedisValue minId, CommandFlags flags); + /// + Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, CommandFlags flags = CommandFlags.None); /// Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs index 6eac7c1d0..dcb2c58ad 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs @@ -642,8 +642,8 @@ public Task StreamReadGroupAsync(StreamPosition[] streamPositions public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => Inner.StreamTrimAsync(ToInner(key), maxLength, useApproximateMaxLength, flags); - public Task StreamTrimAsync(RedisKey key, RedisValue minId, CommandFlags flags) => - Inner.StreamTrimAsync(ToInner(key), minId, flags); + public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, CommandFlags flags = CommandFlags.None) => + Inner.StreamTrimByMinIdAsync(ToInner(key), minId, useApproximateMaxLength, limit, flags); public Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) => Inner.StringAppendAsync(ToInner(key), value, flags); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs index 72d84107d..6999f0a41 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs @@ -624,8 +624,8 @@ public RedisStream[] StreamReadGroup(StreamPosition[] streamPositions, RedisValu public long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => Inner.StreamTrim(ToInner(key), maxLength, useApproximateMaxLength, flags); - public long StreamTrim(RedisKey key, RedisValue minId, CommandFlags flags) => - Inner.StreamTrim(ToInner(key), minId, flags); + public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, CommandFlags flags = CommandFlags.None) => + Inner.StreamTrimByMinId(ToInner(key), minId, useApproximateMaxLength, limit, flags); public long StringAppend(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) => Inner.StringAppend(ToInner(key), value, flags); diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt index 1439e293b..f7293cbe7 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt @@ -1,3 +1,3 @@ #nullable enable -StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, StackExchange.Redis.CommandFlags flags) -> long -StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! \ No newline at end of file +StackExchange.Redis.IDatabase.StreamTrimByMinId(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabaseAsync.StreamTrimByMinIdAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! \ No newline at end of file diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index e7048fadb..9c1c46d29 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -3007,17 +3007,15 @@ public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproxima return ExecuteAsync(msg, ResultProcessor.Int64); } - public long StreamTrim(RedisKey key, RedisValue minId, CommandFlags flags = CommandFlags.None) + public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, CommandFlags flags = CommandFlags.None) { - var values = new[] { StreamConstants.MinId, minId }; - var msg = Message.Create(Database, flags, RedisCommand.XTRIM, key, values); + var msg = GetStreamTrimByMinIdMessage(key, minId, useApproximateMaxLength, limit, flags); return ExecuteSync(msg, ResultProcessor.Int64); } - public Task StreamTrimAsync(RedisKey key, RedisValue minId, CommandFlags flags = CommandFlags.None) + public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, CommandFlags flags = CommandFlags.None) { - var values = new[] { StreamConstants.MinId, minId }; - var msg = Message.Create(Database, flags, RedisCommand.XTRIM, key, values); + var msg = GetStreamTrimByMinIdMessage(key, minId, useApproximateMaxLength, limit, flags); return ExecuteAsync(msg, ResultProcessor.Int64); } @@ -4508,6 +4506,40 @@ private Message GetStreamTrimMessage(RedisKey key, int maxLength, bool useApprox values); } + private Message GetStreamTrimByMinIdMessage(RedisKey key, RedisValue minId, bool useApproximateMaxLength, int? limit, CommandFlags flags) + { + if (limit.HasValue && limit.Value <= 0) + { + throw new ArgumentOutOfRangeException(nameof(limit), "limit must be greater than 0 when specified."); + } + + var values = new RedisValue[2 + (useApproximateMaxLength ? 1 : 0) + (useApproximateMaxLength && limit.HasValue ? 2 : 0)]; + + var offset = 0; + + values[offset++] = StreamConstants.MinId; + + if (useApproximateMaxLength) + { + values[offset++] = StreamConstants.ApproximateMaxLen; + } + + values[offset++] = minId; + + if (useApproximateMaxLength && limit.HasValue) + { + values[offset++] = RedisLiterals.LIMIT; + values[offset] = limit.Value; + } + + return Message.Create( + Database, + flags, + RedisCommand.XTRIM, + key, + values); + } + private Message? GetStringBitOperationMessage(Bitwise operation, RedisKey destination, RedisKey[] keys, CommandFlags flags) { if (keys == null) throw new ArgumentNullException(nameof(keys)); diff --git a/tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs b/tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs index 146ed4369..f25c22cc2 100644 --- a/tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs +++ b/tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs @@ -1203,10 +1203,24 @@ public void StreamTrim() } [Fact] - public void StreamTrimMinId() + public void StreamTrimByMinId() { - prefixed.StreamTrim("key", 1111111111, CommandFlags.None); - mock.Received().StreamTrim("prefix:key", 1111111111, CommandFlags.None); + prefixed.StreamTrimByMinId("key", 1111111111); + mock.Received().StreamTrimByMinId("prefix:key", 1111111111); + } + + [Fact] + public void StreamTrimByMinIdWithApproximate() + { + prefixed.StreamTrimByMinId("key", 1111111111, useApproximateMaxLength: true); + mock.Received().StreamTrimByMinId("prefix:key", 1111111111, useApproximateMaxLength: true); + } + + [Fact] + public void StreamTrimByMinIdWithApproximateAndLimit() + { + prefixed.StreamTrimByMinId("key", 1111111111, useApproximateMaxLength: true, limit: 100); + mock.Received().StreamTrimByMinId("prefix:key", 1111111111, useApproximateMaxLength: true, limit: 100); } [Fact] diff --git a/tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs b/tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs index 9446b771a..37c43ddb6 100644 --- a/tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs +++ b/tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs @@ -1119,10 +1119,24 @@ public async Task StreamTrimAsync() } [Fact] - public async Task StreamTrimMinIdAsync() + public async Task StreamTrimByMinIdAsync() { - await prefixed.StreamTrimAsync("key", 1111111111, CommandFlags.None); - await mock.Received().StreamTrimAsync("prefix:key", 1111111111, CommandFlags.None); + await prefixed.StreamTrimByMinIdAsync("key", 1111111111); + await mock.Received().StreamTrimByMinIdAsync("prefix:key", 1111111111); + } + + [Fact] + public async Task StreamTrimByMinIdAsyncWithApproximate() + { + await prefixed.StreamTrimByMinIdAsync("key", 1111111111, useApproximateMaxLength: true); + await mock.Received().StreamTrimByMinIdAsync("prefix:key", 1111111111, useApproximateMaxLength: true); + } + + [Fact] + public async Task StreamTrimByMinIdAsyncWithApproximateAndLimit() + { + await prefixed.StreamTrimByMinIdAsync("key", 1111111111, useApproximateMaxLength: true, limit: 100); + await mock.Received().StreamTrimByMinIdAsync("prefix:key", 1111111111, useApproximateMaxLength: true, limit: 100); } [Fact] diff --git a/tests/StackExchange.Redis.Tests/StreamTests.cs b/tests/StackExchange.Redis.Tests/StreamTests.cs index 635c845f9..c9f8955e7 100644 --- a/tests/StackExchange.Redis.Tests/StreamTests.cs +++ b/tests/StackExchange.Redis.Tests/StreamTests.cs @@ -1917,7 +1917,7 @@ public void StreamTrimLength() } [Fact] - public void StreamTrimMinId() + public void StreamTrimByMinId() { using var conn = Create(require: RedisFeatures.v5_0_0); @@ -1929,13 +1929,36 @@ public void StreamTrimMinId() db.StreamAdd(key, "field2", "value2", 1111111111); db.StreamAdd(key, "field3", "value3", 1111111112); - var numRemoved = db.StreamTrim(key, 1111111111, CommandFlags.None); + var numRemoved = db.StreamTrimByMinId(key, 1111111111); var len = db.StreamLength(key); Assert.Equal(1, numRemoved); Assert.Equal(2, len); } + [Fact] + public void StreamTrimByMinIdWithApproximateAndLimit() + { + using var conn = Create(require: RedisFeatures.v5_0_0); + + var db = conn.GetDatabase(); + var key = Me(); + + const int maxLength = 1000; + const int limit = 100; + + for (var i = 0; i < maxLength; i++) + { + db.StreamAdd(key, $"field", $"value", 1111111110 + i); + } + + var numRemoved = db.StreamTrimByMinId(key, 1111111110 + maxLength, useApproximateMaxLength: true, limit: limit); + var len = db.StreamLength(key); + + Assert.Equal(limit, numRemoved); + Assert.Equal(maxLength - limit, len); + } + [Fact] public void StreamVerifyLength() { From 2d3f747215c0d94419ec7a735ba8194bbf6ffc10 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 18 Jul 2025 13:12:51 +0100 Subject: [PATCH 05/13] add stream delete mode to minid api add xackdel --- StackExchange.Redis.sln | 1 + docs/ReleaseNotes.md | 1 + src/StackExchange.Redis/Enums/RedisCommand.cs | 4 + .../Enums/StreamDeleteMode.cs | 24 +++++ .../Enums/StreamDeleteResult.cs | 23 +++++ .../Interfaces/IDatabase.cs | 31 +++++- .../Interfaces/IDatabaseAsync.cs | 12 ++- .../KeyspaceIsolation/KeyPrefixed.cs | 10 +- .../KeyspaceIsolation/KeyPrefixedDatabase.cs | 10 +- .../PublicAPI/PublicAPI.Unshipped.txt | 16 +++- src/StackExchange.Redis/RedisDatabase.cs | 82 ++++++++++++---- src/StackExchange.Redis/ResultProcessor.cs | 72 ++++++++++++++ src/StackExchange.Redis/StreamConstants.cs | 16 +++- .../StackExchange.Redis.Tests/StreamTests.cs | 96 ++++++++++++++++--- 14 files changed, 355 insertions(+), 43 deletions(-) create mode 100644 src/StackExchange.Redis/Enums/StreamDeleteMode.cs create mode 100644 src/StackExchange.Redis/Enums/StreamDeleteResult.cs diff --git a/StackExchange.Redis.sln b/StackExchange.Redis.sln index 6e4416d7d..20b5e2f01 100644 --- a/StackExchange.Redis.sln +++ b/StackExchange.Redis.sln @@ -19,6 +19,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution docs\ReleaseNotes.md = docs\ReleaseNotes.md Shared.ruleset = Shared.ruleset version.json = version.json + tests\RedisConfigs\docker-compose.yml = tests\RedisConfigs\docker-compose.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RedisConfigs", "RedisConfigs", "{96E891CD-2ED7-4293-A7AB-4C6F5D8D2B05}" diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index b3a788286..8443357be 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -11,6 +11,7 @@ Current package versions: - Add support for new `BITOP` operations in CE 8.2 ([#2900 by atakavci](https://github.com/StackExchange/StackExchange.Redis/pull/2900)) - Package updates ([#2906 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2906)) - Fix handshake error with `CLIENT ID` ([#2909 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2909)) +- Add 8.2 stream commands ([#2842 by kijanawoodard](https://github.com/StackExchange/StackExchange.Redis/pull/2842), by [#xxxx by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/xxxx)) ## 2.8.41 diff --git a/src/StackExchange.Redis/Enums/RedisCommand.cs b/src/StackExchange.Redis/Enums/RedisCommand.cs index 3909be4c2..34e1eb296 100644 --- a/src/StackExchange.Redis/Enums/RedisCommand.cs +++ b/src/StackExchange.Redis/Enums/RedisCommand.cs @@ -206,10 +206,12 @@ internal enum RedisCommand WATCH, XACK, + XACKDEL, XADD, XAUTOCLAIM, XCLAIM, XDEL, + XDELEX, XGROUP, XINFO, XLEN, @@ -496,9 +498,11 @@ internal static bool IsPrimaryOnly(this RedisCommand command) case RedisCommand.GEOADD: case RedisCommand.SORT: case RedisCommand.XACK: + case RedisCommand.XACKDEL: case RedisCommand.XADD: case RedisCommand.XCLAIM: case RedisCommand.XDEL: + case RedisCommand.XDELEX: case RedisCommand.XGROUP: case RedisCommand.XREADGROUP: case RedisCommand.XTRIM: diff --git a/src/StackExchange.Redis/Enums/StreamDeleteMode.cs b/src/StackExchange.Redis/Enums/StreamDeleteMode.cs new file mode 100644 index 000000000..2909f1097 --- /dev/null +++ b/src/StackExchange.Redis/Enums/StreamDeleteMode.cs @@ -0,0 +1,24 @@ +namespace StackExchange.Redis; + +/// +/// Determines how stream trimming works. +/// +public enum StreamDeleteMode +{ + /// + /// Trims the stream according to the specified policy (MAXLEN or MINID) regardless of whether entries are referenced by any consumer groups, but preserves existing references to these entries in all consumer groups' PEL. + /// + KeepReferences = 0, + + /// + /// Trims the stream according to the specified policy and also removes all references to the trimmed entries from all consumer groups' PEL. + /// + /// Requires server 8.2 or above. + DeleteReferences = 1, + + /// + /// With ACKED: Only trims entries that were read and acknowledged by all consumer groups. + /// + /// Requires server 8.2 or above. + Acknowledged = 2, +} diff --git a/src/StackExchange.Redis/Enums/StreamDeleteResult.cs b/src/StackExchange.Redis/Enums/StreamDeleteResult.cs new file mode 100644 index 000000000..68ef92709 --- /dev/null +++ b/src/StackExchange.Redis/Enums/StreamDeleteResult.cs @@ -0,0 +1,23 @@ +namespace StackExchange.Redis; + +/// +/// Determines how stream trimming works. +/// +public enum StreamDeleteResult +{ + /// + /// No such id exists in the provided stream key. + /// + NotFound = -1, + + /// + /// Entry was deleted from the stream. + /// + Deleted = 1, + + /// + /// Entry was not deleted, but there are still dangling references. + /// + /// This response relates to the mode. + NotDeleted = 2, +} diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index ee50213f4..8dd60ffae 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2440,6 +2440,34 @@ IEnumerable SortedSetScan( /// long StreamAcknowledge(RedisKey key, RedisValue groupName, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None); + /// + /// Allow the consumer to mark a pending message as correctly processed. Returns the number of messages acknowledged. + /// + /// The key of the stream. + /// The name of the consumer group that received the message. + /// The delete mode to use when acknowledging the message. + /// The ID of the message to acknowledge. + /// The flags to use for this operation. + /// The outcome of the delete operation. + /// +#pragma warning disable RS0026 // similar overloads + StreamDeleteResult StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None); +#pragma warning restore RS0026 + + /// + /// Allow the consumer to mark a pending message as correctly processed. Returns the number of messages acknowledged. + /// + /// The key of the stream. + /// The name of the consumer group that received the message. + /// /// The delete mode to use when acknowledging the message. + /// The IDs of the messages to acknowledge. + /// The flags to use for this operation. + /// The outcome of each delete operation. + /// +#pragma warning disable RS0026 // similar overloads + StreamDeleteResult[] StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None); +#pragma warning restore RS0026 + /// /// Adds an entry using the specified values to the given stream key. /// If key does not exist, a new key holding a stream is created. @@ -2782,10 +2810,11 @@ IEnumerable SortedSetScan( /// All entries with an id (timestamp) earlier minId will be removed. /// If true, the "~" argument is used to allow the stream to exceed minId by a small number. This improves performance when removing messages. /// The maximum number of entries to remove per call when useApproximateMaxLength = true. If 0, the limiting mechanism is disabled entirely. + /// Determines how stream trimming should be performed. /// The flags to use for this operation. /// The number of messages removed from the stream. /// - long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, CommandFlags flags = CommandFlags.None); + long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); /// /// If key already exists and is a string, this command appends the value at the end of the string. diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index e81f1a464..8f29f97e7 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -594,6 +594,14 @@ IAsyncEnumerable SortedSetScanAsync( /// Task StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None); + /// +#pragma warning disable RS0026 // similar overloads + Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None); + + /// + Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None); +#pragma warning restore RS0026 + /// Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None); @@ -672,8 +680,8 @@ IAsyncEnumerable SortedSetScanAsync( /// Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None); - /// - Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, CommandFlags flags = CommandFlags.None); + /// + Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); /// Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs index dcb2c58ad..303ef0cbf 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs @@ -564,6 +564,12 @@ public Task StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, Red public Task StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => Inner.StreamAcknowledgeAsync(ToInner(key), groupName, messageIds, flags); + public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) => + Inner.StreamAcknowledgeAndDeleteAsync(ToInner(key), groupName, mode, messageId, flags); + + public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => + Inner.StreamAcknowledgeAndDeleteAsync(ToInner(key), groupName, mode, messageIds, flags); + public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => Inner.StreamAddAsync(ToInner(key), streamField, streamValue, messageId, maxLength, useApproximateMaxLength, flags); @@ -642,8 +648,8 @@ public Task StreamReadGroupAsync(StreamPosition[] streamPositions public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => Inner.StreamTrimAsync(ToInner(key), maxLength, useApproximateMaxLength, flags); - public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, CommandFlags flags = CommandFlags.None) => - Inner.StreamTrimByMinIdAsync(ToInner(key), minId, useApproximateMaxLength, limit, flags); + public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + Inner.StreamTrimByMinIdAsync(ToInner(key), minId, useApproximateMaxLength, limit, mode, flags); public Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) => Inner.StringAppendAsync(ToInner(key), value, flags); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs index 6999f0a41..83472ca38 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs @@ -546,6 +546,12 @@ public long StreamAcknowledge(RedisKey key, RedisValue groupName, RedisValue mes public long StreamAcknowledge(RedisKey key, RedisValue groupName, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => Inner.StreamAcknowledge(ToInner(key), groupName, messageIds, flags); + public StreamDeleteResult StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) => + Inner.StreamAcknowledgeAndDelete(ToInner(key), groupName, mode, messageId, flags); + + public StreamDeleteResult[] StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => + Inner.StreamAcknowledgeAndDelete(ToInner(key), groupName, mode, messageIds, flags); + public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => Inner.StreamAdd(ToInner(key), streamField, streamValue, messageId, maxLength, useApproximateMaxLength, flags); @@ -624,8 +630,8 @@ public RedisStream[] StreamReadGroup(StreamPosition[] streamPositions, RedisValu public long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => Inner.StreamTrim(ToInner(key), maxLength, useApproximateMaxLength, flags); - public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, CommandFlags flags = CommandFlags.None) => - Inner.StreamTrimByMinId(ToInner(key), minId, useApproximateMaxLength, limit, flags); + public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + Inner.StreamTrimByMinId(ToInner(key), minId, useApproximateMaxLength, limit, mode, flags); public long StringAppend(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) => Inner.StringAppend(ToInner(key), value, flags); diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt index f7293cbe7..562d855c9 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt @@ -1,3 +1,15 @@ #nullable enable -StackExchange.Redis.IDatabase.StreamTrimByMinId(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long -StackExchange.Redis.IDatabaseAsync.StreamTrimByMinIdAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! \ No newline at end of file +StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamDeleteResult +StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamDeleteResult[]! +StackExchange.Redis.IDatabase.StreamTrimByMinId(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamTrimByMinIdAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.StreamDeleteMode +StackExchange.Redis.StreamDeleteMode.Acknowledged = 2 -> StackExchange.Redis.StreamDeleteMode +StackExchange.Redis.StreamDeleteMode.DeleteReferences = 1 -> StackExchange.Redis.StreamDeleteMode +StackExchange.Redis.StreamDeleteMode.KeepReferences = 0 -> StackExchange.Redis.StreamDeleteMode +StackExchange.Redis.StreamDeleteResult +StackExchange.Redis.StreamDeleteResult.Deleted = 1 -> StackExchange.Redis.StreamDeleteResult +StackExchange.Redis.StreamDeleteResult.NotDeleted = 2 -> StackExchange.Redis.StreamDeleteResult +StackExchange.Redis.StreamDeleteResult.NotFound = -1 -> StackExchange.Redis.StreamDeleteResult \ No newline at end of file diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 9c1c46d29..531f7a82e 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -1,6 +1,7 @@ using System; using System.Buffers; using System.Collections.Generic; +using System.Diagnostics; using System.Net; using System.Threading.Tasks; using Pipelines.Sockets.Unofficial.Arenas; @@ -2423,6 +2424,30 @@ public Task StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, Red return ExecuteAsync(msg, ResultProcessor.Int64); } + public StreamDeleteResult StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) + { + var msg = GetStreamAcknowledgeAndDeleteMessage(key, groupName, mode, messageId, flags); + return ExecuteSync(msg, ResultProcessor.StreamDeleteResult); + } + + public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) + { + var msg = GetStreamAcknowledgeAndDeleteMessage(key, groupName, mode, messageId, flags); + return ExecuteAsync(msg, ResultProcessor.StreamDeleteResult); + } + + public StreamDeleteResult[] StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) + { + var msg = GetStreamAcknowledgeAndDeleteMessage(key, groupName, mode, messageIds, flags); + return ExecuteSync(msg, ResultProcessor.StreamDeleteResultArray)!; + } + + public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) + { + var msg = GetStreamAcknowledgeAndDeleteMessage(key, groupName, mode, messageIds, flags); + return ExecuteAsync(msg, ResultProcessor.StreamDeleteResultArray)!; + } + public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAddMessage( @@ -3007,15 +3032,15 @@ public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproxima return ExecuteAsync(msg, ResultProcessor.Int64); } - public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, CommandFlags flags = CommandFlags.None) + public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) { - var msg = GetStreamTrimByMinIdMessage(key, minId, useApproximateMaxLength, limit, flags); + var msg = GetStreamTrimByMinIdMessage(key, minId, useApproximateMaxLength, limit, mode, flags); return ExecuteSync(msg, ResultProcessor.Int64); } - public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, CommandFlags flags = CommandFlags.None) + public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) { - var msg = GetStreamTrimByMinIdMessage(key, minId, useApproximateMaxLength, limit, flags); + var msg = GetStreamTrimByMinIdMessage(key, minId, useApproximateMaxLength, limit, mode, flags); return ExecuteAsync(msg, ResultProcessor.Int64); } @@ -4121,13 +4146,7 @@ private Message GetSortedSetRemoveRangeByScoreMessage(RedisKey key, double start private Message GetStreamAcknowledgeMessage(RedisKey key, RedisValue groupName, RedisValue messageId, CommandFlags flags) { - var values = new RedisValue[] - { - groupName, - messageId, - }; - - return Message.Create(Database, flags, RedisCommand.XACK, key, values); + return Message.Create(Database, flags, RedisCommand.XACK, key, groupName, messageId); } private Message GetStreamAcknowledgeMessage(RedisKey key, RedisValue groupName, RedisValue[] messageIds, CommandFlags flags) @@ -4136,17 +4155,33 @@ private Message GetStreamAcknowledgeMessage(RedisKey key, RedisValue groupName, if (messageIds.Length == 0) throw new ArgumentOutOfRangeException(nameof(messageIds), "messageIds must contain at least one item."); var values = new RedisValue[messageIds.Length + 1]; + values[0] = groupName; + messageIds.AsSpan().CopyTo(values.AsSpan(1)); - var offset = 0; + return Message.Create(Database, flags, RedisCommand.XACK, key, values); + } - values[offset++] = groupName; + private Message GetStreamAcknowledgeAndDeleteMessage(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags) + { + return Message.Create(Database, flags, RedisCommand.XACKDEL, key, groupName, StreamConstants.GetMode(mode), StreamConstants.Ids, 1, messageId); + } - for (var i = 0; i < messageIds.Length; i++) - { - values[offset++] = messageIds[i]; - } + private Message GetStreamAcknowledgeAndDeleteMessage(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags) + { + if (messageIds == null) throw new ArgumentNullException(nameof(messageIds)); + if (messageIds.Length == 0) throw new ArgumentOutOfRangeException(nameof(messageIds), "messageIds must contain at least one item."); - return Message.Create(Database, flags, RedisCommand.XACK, key, values); + var values = new RedisValue[messageIds.Length + 4]; + + var offset = 0; + values[offset++] = groupName; + values[offset++] = StreamConstants.GetMode(mode); + values[offset++] = StreamConstants.Ids; + values[offset++] = messageIds.Length; + messageIds.AsSpan().CopyTo(values.AsSpan(offset)); + Debug.Assert(offset + messageIds.Length == values.Length); + + return Message.Create(Database, flags, RedisCommand.XACKDEL, key, values); } private Message GetStreamAddMessage(RedisKey key, RedisValue messageId, int? maxLength, bool useApproximateMaxLength, NameValueEntry streamPair, CommandFlags flags) @@ -4506,14 +4541,14 @@ private Message GetStreamTrimMessage(RedisKey key, int maxLength, bool useApprox values); } - private Message GetStreamTrimByMinIdMessage(RedisKey key, RedisValue minId, bool useApproximateMaxLength, int? limit, CommandFlags flags) + private Message GetStreamTrimByMinIdMessage(RedisKey key, RedisValue minId, bool useApproximateMaxLength, int? limit, StreamDeleteMode mode, CommandFlags flags) { if (limit.HasValue && limit.Value <= 0) { throw new ArgumentOutOfRangeException(nameof(limit), "limit must be greater than 0 when specified."); } - var values = new RedisValue[2 + (useApproximateMaxLength ? 1 : 0) + (useApproximateMaxLength && limit.HasValue ? 2 : 0)]; + var values = new RedisValue[2 + (useApproximateMaxLength ? 1 : 0) + (useApproximateMaxLength && limit.HasValue ? 2 : 0) + (mode == StreamDeleteMode.KeepReferences ? 0 : 1)]; var offset = 0; @@ -4532,6 +4567,13 @@ private Message GetStreamTrimByMinIdMessage(RedisKey key, RedisValue minId, bool values[offset] = limit.Value; } + if (mode != StreamDeleteMode.KeepReferences) // omit when not needed, for back-compat + { + values[offset++] = StreamConstants.GetMode(mode); + } + + Debug.Assert(offset == values.Length); + return Message.Create( Database, flags, diff --git a/src/StackExchange.Redis/ResultProcessor.cs b/src/StackExchange.Redis/ResultProcessor.cs index 06647212b..4b07fb8cf 100644 --- a/src/StackExchange.Redis/ResultProcessor.cs +++ b/src/StackExchange.Redis/ResultProcessor.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Net; +using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; @@ -1377,6 +1378,77 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes } } + internal static ResultProcessor StreamDeleteResult => + Int32EnumProcessor.Instance; + + internal static ResultProcessor StreamDeleteResultArray => + Int32EnumArrayProcessor.Instance; + + private class Int32EnumProcessor : ResultProcessor where T : unmanaged, Enum + { + private Int32EnumProcessor() { } + public static readonly Int32EnumProcessor Instance = new(); + + protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result) + { + switch (result.Resp2TypeBulkString) + { + case ResultType.Integer: + case ResultType.SimpleString: + case ResultType.BulkString: + if (result.TryGetInt64(out long i64)) + { + Debug.Assert(Unsafe.SizeOf() == sizeof(int)); + int i32 = (int)i64; + SetResult(message, Unsafe.As(ref i32)); + return true; + } + break; + case ResultType.Array when result.ItemsCount == 1: // pick a single element from a unit vector + if (result.GetItems()[0].TryGetInt64(out i64)) + { + Debug.Assert(Unsafe.SizeOf() == sizeof(int)); + int i32 = (int)i64; + SetResult(message, Unsafe.As(ref i32)); + return true; + } + break; + } + return false; + } + } + + private class Int32EnumArrayProcessor : ResultProcessor where T : unmanaged, Enum + { + private Int32EnumArrayProcessor() { } + public static readonly Int32EnumArrayProcessor Instance = new(); + + protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result) + { + switch (result.Resp2TypeArray) + { + case ResultType.Array: + T[] arr; + if (result.IsNull) + { + arr = null!; + } + else + { + Debug.Assert(Unsafe.SizeOf() == sizeof(int)); + arr = result.ToArray(static (in RawResult x) => + { + int i32 = (int)x.AsRedisValue(); + return Unsafe.As(ref i32); + })!; + } + SetResult(message, arr); + return true; + } + return false; + } + } + private class PubSubNumSubProcessor : Int64Processor { protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result) diff --git a/src/StackExchange.Redis/StreamConstants.cs b/src/StackExchange.Redis/StreamConstants.cs index 3f34c2e5e..6b0a67758 100644 --- a/src/StackExchange.Redis/StreamConstants.cs +++ b/src/StackExchange.Redis/StreamConstants.cs @@ -1,4 +1,6 @@ -namespace StackExchange.Redis +using System; + +namespace StackExchange.Redis { /// /// Constants representing values used in Redis Stream commands. @@ -68,5 +70,17 @@ internal static class StreamConstants internal static readonly RedisValue Stream = "STREAM"; internal static readonly RedisValue Streams = "STREAMS"; + + private static readonly RedisValue KeepRef = "KEEPREF", DelRef = "DELREF", Acked = "ACKED"; + + internal static readonly RedisValue Ids = "IDS"; + + internal static RedisValue GetMode(StreamDeleteMode mode) => mode switch + { + StreamDeleteMode.KeepReferences => KeepRef, + StreamDeleteMode.DeleteReferences => DelRef, + StreamDeleteMode.Acknowledged => Acked, + _ => throw new ArgumentOutOfRangeException(nameof(mode)), + }; } } diff --git a/tests/StackExchange.Redis.Tests/StreamTests.cs b/tests/StackExchange.Redis.Tests/StreamTests.cs index c9f8955e7..0a4bd8115 100644 --- a/tests/StackExchange.Redis.Tests/StreamTests.cs +++ b/tests/StackExchange.Redis.Tests/StreamTests.cs @@ -702,31 +702,85 @@ public void StreamConsumerGroupAcknowledgeMessage() var id1 = db.StreamAdd(key, "field1", "value1"); var id2 = db.StreamAdd(key, "field2", "value2"); var id3 = db.StreamAdd(key, "field3", "value3"); + RedisValue notexist = "0-0"; var id4 = db.StreamAdd(key, "field4", "value4"); db.StreamCreateConsumerGroup(key, groupName, StreamPosition.Beginning); // Read all 4 messages, they will be assigned to the consumer var entries = db.StreamReadGroup(key, groupName, consumer, StreamPosition.NewMessages); + Assert.Equal(4, entries.Length); // Send XACK for 3 of the messages // Single message Id overload. var oneAck = db.StreamAcknowledge(key, groupName, id1); + Assert.Equal(1, oneAck); + + var nack = db.StreamAcknowledge(key, groupName, notexist); + Assert.Equal(0, nack); // Multiple message Id overload. - var twoAck = db.StreamAcknowledge(key, groupName, new[] { id3, id4 }); + var twoAck = db.StreamAcknowledge(key, groupName, new[] { id3, notexist, id4 }); // Read the group again, it should only return the unacknowledged message. var notAcknowledged = db.StreamReadGroup(key, groupName, consumer, "0-0"); - Assert.Equal(4, entries.Length); - Assert.Equal(1, oneAck); Assert.Equal(2, twoAck); Assert.Single(notAcknowledged); Assert.Equal(id2, notAcknowledged[0].Id); } + [Theory] + [InlineData(StreamDeleteMode.KeepReferences)] + [InlineData(StreamDeleteMode.DeleteReferences)] + [InlineData(StreamDeleteMode.Acknowledged)] + public void StreamConsumerGroupAcknowledgeAndDeleteMessage(StreamDeleteMode mode) + { + using var conn = Create(require: RedisFeatures.v8_2_0_rc1); + + var db = conn.GetDatabase(); + var key = Me() + ":" + mode; + const string groupName = "test_group", + consumer = "test_consumer"; + + var id1 = db.StreamAdd(key, "field1", "value1"); + var id2 = db.StreamAdd(key, "field2", "value2"); + var id3 = db.StreamAdd(key, "field3", "value3"); + RedisValue notexist = "0-0"; + var id4 = db.StreamAdd(key, "field4", "value4"); + + db.StreamCreateConsumerGroup(key, groupName, StreamPosition.Beginning); + + // Read all 4 messages, they will be assigned to the consumer + var entries = db.StreamReadGroup(key, groupName, consumer, StreamPosition.NewMessages); + Assert.Equal(4, entries.Length); + + // Send XACK for 3 of the messages + + // Single message Id overload. + var oneAck = db.StreamAcknowledgeAndDelete(key, groupName, mode, id1); + Assert.Equal(StreamDeleteResult.Deleted, oneAck); + + StreamDeleteResult nack = db.StreamAcknowledgeAndDelete(key, groupName, mode, notexist); + Assert.Equal(StreamDeleteResult.NotFound, nack); + + // Multiple message Id overload. + RedisValue[] ids = new[] { id3, notexist, id4 }; + var twoAck = db.StreamAcknowledgeAndDelete(key, groupName, mode, ids); + + // Read the group again, it should only return the unacknowledged message. + var notAcknowledged = db.StreamReadGroup(key, groupName, consumer, "0-0"); + + Assert.Equal(3, twoAck.Length); + Assert.Equal(StreamDeleteResult.Deleted, twoAck[0]); + Assert.Equal(StreamDeleteResult.NotFound, twoAck[1]); + Assert.Equal(StreamDeleteResult.Deleted, twoAck[2]); + + Assert.Single(notAcknowledged); + Assert.Equal(id2, notAcknowledged[0].Id); + } + [Fact] public void StreamConsumerGroupClaimMessages() { @@ -1895,6 +1949,8 @@ public void StreamReadWithAfterIdAndCount_2() Assert.Equal(id3, entries[1].Id); } + protected override string GetConfiguration() => "127.0.0.1:6379"; + [Fact] public void StreamTrimLength() { @@ -1916,33 +1972,47 @@ public void StreamTrimLength() Assert.Equal(1, len); } - [Fact] - public void StreamTrimByMinId() + [Theory] + [InlineData(StreamDeleteMode.KeepReferences)] + [InlineData(StreamDeleteMode.DeleteReferences)] + [InlineData(StreamDeleteMode.Acknowledged)] + public void StreamTrimByMinId(StreamDeleteMode mode) { - using var conn = Create(require: RedisFeatures.v5_0_0); + using var conn = Create(require: mode switch + { + StreamDeleteMode.KeepReferences => RedisFeatures.v6_2_0, + _ => RedisFeatures.v8_2_0_rc1, + }); var db = conn.GetDatabase(); - var key = Me(); + var key = Me() + ":" + mode; // Add a couple items and check length. db.StreamAdd(key, "field1", "value1", 1111111110); db.StreamAdd(key, "field2", "value2", 1111111111); db.StreamAdd(key, "field3", "value3", 1111111112); - var numRemoved = db.StreamTrimByMinId(key, 1111111111); + var numRemoved = db.StreamTrimByMinId(key, 1111111111, mode: mode); var len = db.StreamLength(key); Assert.Equal(1, numRemoved); Assert.Equal(2, len); } - [Fact] - public void StreamTrimByMinIdWithApproximateAndLimit() + [Theory] + [InlineData(StreamDeleteMode.KeepReferences)] + [InlineData(StreamDeleteMode.DeleteReferences)] + [InlineData(StreamDeleteMode.Acknowledged)] + public void StreamTrimByMinIdWithApproximateAndLimit(StreamDeleteMode mode) { - using var conn = Create(require: RedisFeatures.v5_0_0); + using var conn = Create(require: mode switch + { + StreamDeleteMode.KeepReferences => RedisFeatures.v6_2_0, + _ => RedisFeatures.v8_2_0_rc1, + }); var db = conn.GetDatabase(); - var key = Me(); + var key = Me() + ":" + mode; const int maxLength = 1000; const int limit = 100; @@ -1952,7 +2022,7 @@ public void StreamTrimByMinIdWithApproximateAndLimit() db.StreamAdd(key, $"field", $"value", 1111111110 + i); } - var numRemoved = db.StreamTrimByMinId(key, 1111111110 + maxLength, useApproximateMaxLength: true, limit: limit); + var numRemoved = db.StreamTrimByMinId(key, 1111111110 + maxLength, useApproximateMaxLength: true, limit: limit, mode: mode); var len = db.StreamLength(key); Assert.Equal(limit, numRemoved); From d9f33b3fda57917300659d4ab1526a52f7a270ab Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 18 Jul 2025 13:20:43 +0100 Subject: [PATCH 06/13] xref new pr --- docs/ReleaseNotes.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 8443357be..6f119a4e7 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -11,7 +11,8 @@ Current package versions: - Add support for new `BITOP` operations in CE 8.2 ([#2900 by atakavci](https://github.com/StackExchange/StackExchange.Redis/pull/2900)) - Package updates ([#2906 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2906)) - Fix handshake error with `CLIENT ID` ([#2909 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2909)) -- Add 8.2 stream commands ([#2842 by kijanawoodard](https://github.com/StackExchange/StackExchange.Redis/pull/2842), by [#xxxx by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/xxxx)) +- Add `xack minid` support ([#2842 by kijanawoodard](https://github.com/StackExchange/StackExchange.Redis/pull/2842)) +- Add new 8.2 stream support - `xdelex`, `xackdel`, `xtrim [keepref|delref|acked]` ([#2912 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2912)) ## 2.8.41 From efe65abd502225228d2c14173b80c81837830301 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 18 Jul 2025 14:36:03 +0100 Subject: [PATCH 07/13] add limit to StreamTrim (maxlen) --- docs/ReleaseNotes.md | 4 +- .../Interfaces/IDatabase.cs | 17 ++++- .../Interfaces/IDatabaseAsync.cs | 9 ++- .../KeyspaceIsolation/KeyPrefixed.cs | 7 +- .../KeyspaceIsolation/KeyPrefixedDatabase.cs | 7 +- .../PublicAPI/PublicAPI.Shipped.txt | 2 - .../PublicAPI/PublicAPI.Unshipped.txt | 8 ++- src/StackExchange.Redis/RedisDatabase.cs | 67 +++++++------------ .../StackExchange.Redis.Tests/StreamTests.cs | 11 ++- 9 files changed, 73 insertions(+), 59 deletions(-) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 6f119a4e7..a9c0ad537 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -11,8 +11,8 @@ Current package versions: - Add support for new `BITOP` operations in CE 8.2 ([#2900 by atakavci](https://github.com/StackExchange/StackExchange.Redis/pull/2900)) - Package updates ([#2906 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2906)) - Fix handshake error with `CLIENT ID` ([#2909 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2909)) -- Add `xack minid` support ([#2842 by kijanawoodard](https://github.com/StackExchange/StackExchange.Redis/pull/2842)) -- Add new 8.2 stream support - `xdelex`, `xackdel`, `xtrim [keepref|delref|acked]` ([#2912 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2912)) +- Add `XTRIM MINID` support ([#2842 by kijanawoodard](https://github.com/StackExchange/StackExchange.Redis/pull/2842)) +- Add new CE 8.2 stream support - `XDELEX`, `XACKDEL`, `XTRIM [KEEPREF|DELREF|ACKED]` ([#2912 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2912)) ## 2.8.41 diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 8dd60ffae..55389e0e6 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2801,7 +2801,20 @@ IEnumerable SortedSetScan( /// The flags to use for this operation. /// The number of messages removed from the stream. /// - long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None); + long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength, CommandFlags flags); + + /// + /// Trim the stream to a specified maximum length. + /// + /// The key of the stream. + /// The maximum length of the stream. + /// If true, the "~" argument is used to allow the stream to exceed max length by a small number. This improves performance when removing messages. + /// Specifies the maximal count of entries that will be evicted. + /// Determines how stream trimming should be performed. + /// The flags to use for this operation. + /// The number of messages removed from the stream. + /// + long StreamTrim(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); /// /// Trim the stream to a specified minimum timestamp. @@ -2814,7 +2827,7 @@ IEnumerable SortedSetScan( /// The flags to use for this operation. /// The number of messages removed from the stream. /// - long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); + long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); /// /// If key already exists and is a string, this command appends the value at the end of the string. diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 8f29f97e7..b93dded2d 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -678,10 +678,13 @@ IAsyncEnumerable SortedSetScanAsync( Task StreamReadGroupAsync(StreamPosition[] streamPositions, RedisValue groupName, RedisValue consumerName, int? countPerStream = null, bool noAck = false, CommandFlags flags = CommandFlags.None); /// - Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None); + Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength, CommandFlags flags); - /// - Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); + /// + Task StreamTrimAsync(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); + + /// + Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); /// Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs index 303ef0cbf..b2a44d840 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs @@ -645,10 +645,13 @@ public Task StreamReadGroupAsync(StreamPosition[] streamPositions public Task StreamReadGroupAsync(StreamPosition[] streamPositions, RedisValue groupName, RedisValue consumerName, int? countPerStream = null, bool noAck = false, CommandFlags flags = CommandFlags.None) => Inner.StreamReadGroupAsync(streamPositions, groupName, consumerName, countPerStream, noAck, flags); - public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => + public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength, CommandFlags flags) => Inner.StreamTrimAsync(ToInner(key), maxLength, useApproximateMaxLength, flags); - public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + public Task StreamTrimAsync(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + Inner.StreamTrimAsync(ToInner(key), maxLength, useApproximateMaxLength, limit, mode, flags); + + public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => Inner.StreamTrimByMinIdAsync(ToInner(key), minId, useApproximateMaxLength, limit, mode, flags); public Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) => diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs index 83472ca38..04b79d8b4 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs @@ -627,10 +627,13 @@ public RedisStream[] StreamReadGroup(StreamPosition[] streamPositions, RedisValu public RedisStream[] StreamReadGroup(StreamPosition[] streamPositions, RedisValue groupName, RedisValue consumerName, int? countPerStream = null, bool noAck = false, CommandFlags flags = CommandFlags.None) => Inner.StreamReadGroup(streamPositions, groupName, consumerName, countPerStream, noAck, flags); - public long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => + public long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength, CommandFlags flags) => Inner.StreamTrim(ToInner(key), maxLength, useApproximateMaxLength, flags); - public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + public long StreamTrim(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + Inner.StreamTrim(ToInner(key), maxLength, useApproximateMaxLength, limit, mode, flags); + + public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => Inner.StreamTrimByMinId(ToInner(key), minId, useApproximateMaxLength, limit, mode, flags); public long StringAppend(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) => diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt index 00ae49025..30f096ec4 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt @@ -742,7 +742,6 @@ StackExchange.Redis.IDatabase.StreamReadGroup(StackExchange.Redis.RedisKey key, StackExchange.Redis.IDatabase.StreamReadGroup(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, StackExchange.Redis.RedisValue? position, int? count, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.StreamEntry[]! StackExchange.Redis.IDatabase.StreamReadGroup(StackExchange.Redis.StreamPosition[]! streamPositions, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, int? countPerStream = null, bool noAck = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisStream[]! StackExchange.Redis.IDatabase.StreamReadGroup(StackExchange.Redis.StreamPosition[]! streamPositions, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, int? countPerStream, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisStream[]! -StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringAppend(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringBitCount(StackExchange.Redis.RedisKey key, long start, long end, StackExchange.Redis.CommandFlags flags) -> long StackExchange.Redis.IDatabase.StringBitCount(StackExchange.Redis.RedisKey key, long start = 0, long end = -1, StackExchange.Redis.StringIndexType indexType = StackExchange.Redis.StringIndexType.Byte, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long @@ -979,7 +978,6 @@ StackExchange.Redis.IDatabaseAsync.StreamReadGroupAsync(StackExchange.Redis.Redi StackExchange.Redis.IDatabaseAsync.StreamReadGroupAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, StackExchange.Redis.RedisValue? position, int? count, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamReadGroupAsync(StackExchange.Redis.StreamPosition[]! streamPositions, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, int? countPerStream = null, bool noAck = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamReadGroupAsync(StackExchange.Redis.StreamPosition[]! streamPositions, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, int? countPerStream, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringAppendAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitCountAsync(StackExchange.Redis.RedisKey key, long start, long end, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitCountAsync(StackExchange.Redis.RedisKey key, long start = 0, long end = -1, StackExchange.Redis.StringIndexType indexType = StackExchange.Redis.StringIndexType.Byte, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt index 562d855c9..07118a368 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt @@ -1,10 +1,14 @@ #nullable enable StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamDeleteResult StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamDeleteResult[]! -StackExchange.Redis.IDatabase.StreamTrimByMinId(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> long +StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabase.StreamTrimByMinId(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamTrimByMinIdAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamTrimByMinIdAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.StreamDeleteMode StackExchange.Redis.StreamDeleteMode.Acknowledged = 2 -> StackExchange.Redis.StreamDeleteMode StackExchange.Redis.StreamDeleteMode.DeleteReferences = 1 -> StackExchange.Redis.StreamDeleteMode diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 531f7a82e..199994e85 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -3020,27 +3020,33 @@ public Task StreamReadGroupAsync(StreamPosition[] streamPositions return ExecuteAsync(msg, ResultProcessor.MultiStream, defaultValue: Array.Empty()); } - public long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) + public long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength, CommandFlags flags) + => StreamTrim(key, maxLength, useApproximateMaxLength, null, StreamDeleteMode.KeepReferences, flags); + + public long StreamTrim(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) { - var msg = GetStreamTrimMessage(key, maxLength, useApproximateMaxLength, flags); + var msg = GetStreamTrimMessage(true, key, maxLength, useApproximateMaxLength, limit, mode, flags); return ExecuteSync(msg, ResultProcessor.Int64); } - public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) + public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength, CommandFlags flags) + => StreamTrimAsync(key, maxLength, useApproximateMaxLength, null, StreamDeleteMode.KeepReferences, flags); + + public Task StreamTrimAsync(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) { - var msg = GetStreamTrimMessage(key, maxLength, useApproximateMaxLength, flags); + var msg = GetStreamTrimMessage(true, key, maxLength, useApproximateMaxLength, limit, mode, flags); return ExecuteAsync(msg, ResultProcessor.Int64); } - public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) + public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) { - var msg = GetStreamTrimByMinIdMessage(key, minId, useApproximateMaxLength, limit, mode, flags); + var msg = GetStreamTrimMessage(false, key, minId, useApproximateMaxLength, limit, mode, flags); return ExecuteSync(msg, ResultProcessor.Int64); } - public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, int? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) + public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) { - var msg = GetStreamTrimByMinIdMessage(key, minId, useApproximateMaxLength, limit, mode, flags); + var msg = GetStreamTrimMessage(false, key, minId, useApproximateMaxLength, limit, mode, flags); return ExecuteAsync(msg, ResultProcessor.Int64); } @@ -4512,59 +4518,36 @@ protected override void WriteImpl(PhysicalConnection physical) public override int ArgCount => argCount; } - private Message GetStreamTrimMessage(RedisKey key, int maxLength, bool useApproximateMaxLength, CommandFlags flags) + private Message GetStreamTrimMessage(bool maxLen, RedisKey key, RedisValue threshold, bool useApproximateMaxLength, long? limit, StreamDeleteMode mode, CommandFlags flags) { - if (maxLength < 0) - { - throw new ArgumentOutOfRangeException(nameof(maxLength), "maxLength must be equal to or greater than 0."); - } - - var values = new RedisValue[2 + (useApproximateMaxLength ? 1 : 0)]; - - values[0] = StreamConstants.MaxLen; - - if (useApproximateMaxLength) - { - values[1] = StreamConstants.ApproximateMaxLen; - values[2] = maxLength; - } - else + if (limit.HasValue && limit.GetValueOrDefault() <= 0) { - values[1] = maxLength; + throw new ArgumentOutOfRangeException(nameof(limit), "limit must be greater than 0 when specified."); } - return Message.Create( - Database, - flags, - RedisCommand.XTRIM, - key, - values); - } - - private Message GetStreamTrimByMinIdMessage(RedisKey key, RedisValue minId, bool useApproximateMaxLength, int? limit, StreamDeleteMode mode, CommandFlags flags) - { - if (limit.HasValue && limit.Value <= 0) + if (limit is null && !useApproximateMaxLength && mode == StreamDeleteMode.KeepReferences) { - throw new ArgumentOutOfRangeException(nameof(limit), "limit must be greater than 0 when specified."); + // avoid array alloc in simple case + return Message.Create(Database, flags, RedisCommand.XTRIM, key, maxLen ? StreamConstants.MaxLen : StreamConstants.MinId, threshold); } - var values = new RedisValue[2 + (useApproximateMaxLength ? 1 : 0) + (useApproximateMaxLength && limit.HasValue ? 2 : 0) + (mode == StreamDeleteMode.KeepReferences ? 0 : 1)]; + var values = new RedisValue[2 + (useApproximateMaxLength ? 1 : 0) + (limit.HasValue ? 2 : 0) + (mode == StreamDeleteMode.KeepReferences ? 0 : 1)]; var offset = 0; - values[offset++] = StreamConstants.MinId; + values[offset++] = maxLen ? StreamConstants.MaxLen : StreamConstants.MinId; if (useApproximateMaxLength) { values[offset++] = StreamConstants.ApproximateMaxLen; } - values[offset++] = minId; + values[offset++] = threshold; - if (useApproximateMaxLength && limit.HasValue) + if (limit.HasValue) { values[offset++] = RedisLiterals.LIMIT; - values[offset] = limit.Value; + values[offset++] = limit.GetValueOrDefault(); } if (mode != StreamDeleteMode.KeepReferences) // omit when not needed, for back-compat diff --git a/tests/StackExchange.Redis.Tests/StreamTests.cs b/tests/StackExchange.Redis.Tests/StreamTests.cs index 0a4bd8115..423c1acf2 100644 --- a/tests/StackExchange.Redis.Tests/StreamTests.cs +++ b/tests/StackExchange.Redis.Tests/StreamTests.cs @@ -2023,10 +2023,17 @@ public void StreamTrimByMinIdWithApproximateAndLimit(StreamDeleteMode mode) } var numRemoved = db.StreamTrimByMinId(key, 1111111110 + maxLength, useApproximateMaxLength: true, limit: limit, mode: mode); + var expectRemoved = mode switch + { + StreamDeleteMode.KeepReferences => limit, + StreamDeleteMode.DeleteReferences => 0, + StreamDeleteMode.Acknowledged => 0, + _ => throw new ArgumentOutOfRangeException(nameof(mode)), + }; var len = db.StreamLength(key); - Assert.Equal(limit, numRemoved); - Assert.Equal(maxLength - limit, len); + Assert.Equal(expectRemoved, numRemoved); + Assert.Equal(maxLength - expectRemoved, len); } [Fact] From 888d0a1086ff6434942b2c06bdbf453438cdf760 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 18 Jul 2025 15:17:41 +0100 Subject: [PATCH 08/13] XADD KEEPREF|DELREF|ACKED --- .../Interfaces/IDatabase.cs | 43 +++++++++++- .../Interfaces/IDatabaseAsync.cs | 14 +++- .../KeyspaceIsolation/KeyPrefixed.cs | 10 ++- .../KeyspaceIsolation/KeyPrefixedDatabase.cs | 10 ++- .../PublicAPI/PublicAPI.Shipped.txt | 4 -- .../PublicAPI/PublicAPI.Unshipped.txt | 8 +++ src/StackExchange.Redis/RedisDatabase.cs | 69 +++++++++++++++---- .../StackExchange.Redis.Tests/StreamTests.cs | 33 +++++---- 8 files changed, 150 insertions(+), 41 deletions(-) diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 55389e0e6..56f7b64d6 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2482,7 +2482,7 @@ IEnumerable SortedSetScan( /// The flags to use for this operation. /// The ID of the newly created message. /// - RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None); + RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags); /// /// Adds an entry using the specified values to the given stream key. @@ -2497,7 +2497,46 @@ IEnumerable SortedSetScan( /// The flags to use for this operation. /// The ID of the newly created message. /// - RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None); + RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags); + + /// + /// Adds an entry using the specified values to the given stream key. + /// If key does not exist, a new key holding a stream is created. + /// The command returns the ID of the newly created stream entry. + /// + /// The key of the stream. + /// The field name for the stream entry. + /// The value to set in the stream entry. + /// The ID to assign to the stream entry, defaults to an auto-generated ID ("*"). + /// The maximum length of the stream. + /// If true, the "~" argument is used to allow the stream to exceed max length by a small number. This improves performance when removing messages. + /// Specifies the maximal count of entries that will be evicted. + /// Determines how stream trimming should be performed. + /// The flags to use for this operation. + /// The ID of the newly created message. + /// +#pragma warning disable RS0026 // different shape + RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode deleteMode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); +#pragma warning restore RS0026 + + /// + /// Adds an entry using the specified values to the given stream key. + /// If key does not exist, a new key holding a stream is created. + /// The command returns the ID of the newly created stream entry. + /// + /// The key of the stream. + /// The fields and their associated values to set in the stream entry. + /// The ID to assign to the stream entry, defaults to an auto-generated ID ("*"). + /// The maximum length of the stream. + /// If true, the "~" argument is used to allow the stream to exceed max length by a small number. This improves performance when removing messages. + /// Specifies the maximal count of entries that will be evicted. + /// Determines how stream trimming should be performed. + /// The flags to use for this operation. + /// The ID of the newly created message. + /// +#pragma warning disable RS0026 // different shape + RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode deleteMode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); +#pragma warning restore RS0026 /// /// Change ownership of messages consumed, but not yet acknowledged, by a different consumer. diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index b93dded2d..58de79e4f 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -594,8 +594,8 @@ IAsyncEnumerable SortedSetScanAsync( /// Task StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None); - /// #pragma warning disable RS0026 // similar overloads + /// Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None); /// @@ -603,10 +603,18 @@ IAsyncEnumerable SortedSetScanAsync( #pragma warning restore RS0026 /// - Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None); + Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags); /// - Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None); + Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags); + +#pragma warning disable RS0026 // similar overloads + /// + Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode deleteMode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); + + /// + Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode deleteMode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); +#pragma warning restore RS0026 /// Task StreamAutoClaimAsync(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue startAtId, int? count = null, CommandFlags flags = CommandFlags.None); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs index b2a44d840..d6d6d7c7d 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs @@ -570,12 +570,18 @@ public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, Re public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => Inner.StreamAcknowledgeAndDeleteAsync(ToInner(key), groupName, mode, messageIds, flags); - public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => + public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) => Inner.StreamAddAsync(ToInner(key), streamField, streamValue, messageId, maxLength, useApproximateMaxLength, flags); - public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => + public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) => Inner.StreamAddAsync(ToInner(key), streamPairs, messageId, maxLength, useApproximateMaxLength, flags); + public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + Inner.StreamAddAsync(ToInner(key), streamField, streamValue, messageId, maxLength, useApproximateMaxLength, limit, mode, flags); + + public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + Inner.StreamAddAsync(ToInner(key), streamPairs, messageId, maxLength, useApproximateMaxLength, limit, mode, flags); + public Task StreamAutoClaimAsync(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue startAtId, int? count = null, CommandFlags flags = CommandFlags.None) => Inner.StreamAutoClaimAsync(ToInner(key), consumerGroup, claimingConsumer, minIdleTimeInMs, startAtId, count, flags); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs index 04b79d8b4..9eac6ac48 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs @@ -552,12 +552,18 @@ public StreamDeleteResult StreamAcknowledgeAndDelete(RedisKey key, RedisValue gr public StreamDeleteResult[] StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => Inner.StreamAcknowledgeAndDelete(ToInner(key), groupName, mode, messageIds, flags); - public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => + public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) => Inner.StreamAdd(ToInner(key), streamField, streamValue, messageId, maxLength, useApproximateMaxLength, flags); - public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) => + public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) => Inner.StreamAdd(ToInner(key), streamPairs, messageId, maxLength, useApproximateMaxLength, flags); + public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + Inner.StreamAdd(ToInner(key), streamField, streamValue, messageId, maxLength, useApproximateMaxLength, limit, mode, flags); + + public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + Inner.StreamAdd(ToInner(key), streamPairs, messageId, maxLength, useApproximateMaxLength, limit, mode, flags); + public StreamAutoClaimResult StreamAutoClaim(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue startAtId, int? count = null, CommandFlags flags = CommandFlags.None) => Inner.StreamAutoClaim(ToInner(key), consumerGroup, claimingConsumer, minIdleTimeInMs, startAtId, count, flags); diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt index 30f096ec4..7d1c2b982 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt @@ -717,8 +717,6 @@ StackExchange.Redis.IDatabase.SortedSetUpdate(StackExchange.Redis.RedisKey key, StackExchange.Redis.IDatabase.SortedSetUpdate(StackExchange.Redis.RedisKey key, StackExchange.Redis.SortedSetEntry[]! values, StackExchange.Redis.SortedSetWhen when = StackExchange.Redis.SortedSetWhen.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StreamAcknowledge(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StreamAcknowledge(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long -StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue -StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue StackExchange.Redis.IDatabase.StreamAutoClaim(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue consumerGroup, StackExchange.Redis.RedisValue claimingConsumer, long minIdleTimeInMs, StackExchange.Redis.RedisValue startAtId, int? count = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamAutoClaimResult StackExchange.Redis.IDatabase.StreamAutoClaimIdsOnly(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue consumerGroup, StackExchange.Redis.RedisValue claimingConsumer, long minIdleTimeInMs, StackExchange.Redis.RedisValue startAtId, int? count = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamAutoClaimIdsOnlyResult StackExchange.Redis.IDatabase.StreamClaim(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue consumerGroup, StackExchange.Redis.RedisValue claimingConsumer, long minIdleTimeInMs, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamEntry[]! @@ -953,8 +951,6 @@ StackExchange.Redis.IDatabaseAsync.SortedSetUpdateAsync(StackExchange.Redis.Redi StackExchange.Redis.IDatabaseAsync.SortedSetUpdateAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.SortedSetEntry[]! values, StackExchange.Redis.SortedSetWhen when = StackExchange.Redis.SortedSetWhen.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAutoClaimAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue consumerGroup, StackExchange.Redis.RedisValue claimingConsumer, long minIdleTimeInMs, StackExchange.Redis.RedisValue startAtId, int? count = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAutoClaimIdsOnlyAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue consumerGroup, StackExchange.Redis.RedisValue claimingConsumer, long minIdleTimeInMs, StackExchange.Redis.RedisValue startAtId, int? count = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamClaimAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue consumerGroup, StackExchange.Redis.RedisValue claimingConsumer, long minIdleTimeInMs, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt index 07118a368..2c7cf8df3 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt @@ -1,11 +1,19 @@ #nullable enable StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamDeleteResult StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamDeleteResult[]! +StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode deleteMode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue +StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisValue +StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode deleteMode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue +StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisValue StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> long StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StreamTrimByMinId(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode deleteMode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode deleteMode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamTrimByMinIdAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 199994e85..7651d2a8b 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -2448,7 +2448,10 @@ public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, return ExecuteAsync(msg, ResultProcessor.StreamDeleteResultArray)!; } - public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) + public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) + => StreamAdd(key, streamField, streamValue, messageId, maxLength, useApproximateMaxLength, null, StreamDeleteMode.KeepReferences, flags); + + public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAddMessage( key, @@ -2456,12 +2459,17 @@ public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue str maxLength, useApproximateMaxLength, new NameValueEntry(streamField, streamValue), + limit, + mode, flags); return ExecuteSync(msg, ResultProcessor.RedisValue); } - public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) + public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) + => StreamAddAsync(key, streamField, streamValue, messageId, maxLength, useApproximateMaxLength, null, StreamDeleteMode.KeepReferences, flags); + + public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAddMessage( key, @@ -2469,12 +2477,17 @@ public Task StreamAddAsync(RedisKey key, RedisValue streamField, Red maxLength, useApproximateMaxLength, new NameValueEntry(streamField, streamValue), + limit, + mode, flags); return ExecuteAsync(msg, ResultProcessor.RedisValue); } - public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) + public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) + => StreamAdd(key, streamPairs, messageId, maxLength, useApproximateMaxLength, null, StreamDeleteMode.KeepReferences, flags); + + public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAddMessage( key, @@ -2482,12 +2495,17 @@ public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisVal maxLength, useApproximateMaxLength, streamPairs, + limit, + mode, flags); return ExecuteSync(msg, ResultProcessor.RedisValue); } - public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None) + public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) + => StreamAddAsync(key, streamPairs, messageId, maxLength, useApproximateMaxLength, null, StreamDeleteMode.KeepReferences, flags); + + public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAddMessage( key, @@ -2495,6 +2513,8 @@ public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPair maxLength, useApproximateMaxLength, streamPairs, + limit, + mode, flags); return ExecuteAsync(msg, ResultProcessor.RedisValue); @@ -4190,14 +4210,16 @@ private Message GetStreamAcknowledgeAndDeleteMessage(RedisKey key, RedisValue gr return Message.Create(Database, flags, RedisCommand.XACKDEL, key, values); } - private Message GetStreamAddMessage(RedisKey key, RedisValue messageId, int? maxLength, bool useApproximateMaxLength, NameValueEntry streamPair, CommandFlags flags) + private Message GetStreamAddMessage(RedisKey key, RedisValue messageId, long? maxLength, bool useApproximateMaxLength, NameValueEntry streamPair, long? limit, StreamDeleteMode mode, CommandFlags flags) { // Calculate the correct number of arguments: // 3 array elements for Entry ID & NameValueEntry.Name & NameValueEntry.Value. // 2 elements if using MAXLEN (keyword & value), otherwise 0. // 1 element if using Approximate Length (~), otherwise 0. var totalLength = 3 + (maxLength.HasValue ? 2 : 0) - + (maxLength.HasValue && useApproximateMaxLength ? 1 : 0); + + (maxLength.HasValue && useApproximateMaxLength ? 1 : 0) + + (limit.HasValue ? 2 : 0) + + (mode != StreamDeleteMode.KeepReferences ? 1 : 0); var values = new RedisValue[totalLength]; var offset = 0; @@ -4209,26 +4231,35 @@ private Message GetStreamAddMessage(RedisKey key, RedisValue messageId, int? max if (useApproximateMaxLength) { values[offset++] = StreamConstants.ApproximateMaxLen; - values[offset++] = maxLength.Value; - } - else - { - values[offset++] = maxLength.Value; } + + values[offset++] = maxLength.Value; + } + + if (limit.HasValue) + { + values[offset++] = RedisLiterals.LIMIT; + values[offset++] = limit.Value; + } + + if (mode != StreamDeleteMode.KeepReferences) + { + values[offset++] = StreamConstants.GetMode(mode); } values[offset++] = messageId; values[offset++] = streamPair.Name; - values[offset] = streamPair.Value; + values[offset++] = streamPair.Value; + Debug.Assert(offset == totalLength); return Message.Create(Database, flags, RedisCommand.XADD, key, values); } /// /// Gets message for . /// - private Message GetStreamAddMessage(RedisKey key, RedisValue entryId, int? maxLength, bool useApproximateMaxLength, NameValueEntry[] streamPairs, CommandFlags flags) + private Message GetStreamAddMessage(RedisKey key, RedisValue entryId, long? maxLength, bool useApproximateMaxLength, NameValueEntry[] streamPairs, long? limit, StreamDeleteMode mode, CommandFlags flags) { if (streamPairs == null) throw new ArgumentNullException(nameof(streamPairs)); if (streamPairs.Length == 0) throw new ArgumentOutOfRangeException(nameof(streamPairs), "streamPairs must contain at least one item."); @@ -4262,6 +4293,17 @@ private Message GetStreamAddMessage(RedisKey key, RedisValue entryId, int? maxLe values[offset++] = maxLength.Value; } + if (limit.HasValue) + { + values[offset++] = RedisLiterals.LIMIT; + values[offset++] = limit.Value; + } + + if (mode != StreamDeleteMode.KeepReferences) + { + values[offset++] = StreamConstants.GetMode(mode); + } + values[offset++] = entryId; for (var i = 0; i < streamPairs.Length; i++) @@ -4270,6 +4312,7 @@ private Message GetStreamAddMessage(RedisKey key, RedisValue entryId, int? maxLe values[offset++] = streamPairs[i].Value; } + Debug.Assert(offset == totalLength); return Message.Create(Database, flags, RedisCommand.XADD, key, values); } diff --git a/tests/StackExchange.Redis.Tests/StreamTests.cs b/tests/StackExchange.Redis.Tests/StreamTests.cs index 423c1acf2..bc8fd8cc5 100644 --- a/tests/StackExchange.Redis.Tests/StreamTests.cs +++ b/tests/StackExchange.Redis.Tests/StreamTests.cs @@ -1972,17 +1972,21 @@ public void StreamTrimLength() Assert.Equal(1, len); } + private static Version ForMode(StreamDeleteMode mode, Version? defaultVersion = null) => mode switch + { + StreamDeleteMode.KeepReferences => defaultVersion ?? RedisFeatures.v5_0_0, + StreamDeleteMode.Acknowledged => RedisFeatures.v8_2_0_rc1, + StreamDeleteMode.DeleteReferences => RedisFeatures.v8_2_0_rc1, + _ => throw new ArgumentOutOfRangeException(nameof(mode)), + }; + [Theory] [InlineData(StreamDeleteMode.KeepReferences)] [InlineData(StreamDeleteMode.DeleteReferences)] [InlineData(StreamDeleteMode.Acknowledged)] public void StreamTrimByMinId(StreamDeleteMode mode) { - using var conn = Create(require: mode switch - { - StreamDeleteMode.KeepReferences => RedisFeatures.v6_2_0, - _ => RedisFeatures.v8_2_0_rc1, - }); + using var conn = Create(require: ForMode(mode, RedisFeatures.v6_2_0)); var db = conn.GetDatabase(); var key = Me() + ":" + mode; @@ -2005,11 +2009,7 @@ public void StreamTrimByMinId(StreamDeleteMode mode) [InlineData(StreamDeleteMode.Acknowledged)] public void StreamTrimByMinIdWithApproximateAndLimit(StreamDeleteMode mode) { - using var conn = Create(require: mode switch - { - StreamDeleteMode.KeepReferences => RedisFeatures.v6_2_0, - _ => RedisFeatures.v8_2_0_rc1, - }); + using var conn = Create(require: ForMode(mode, RedisFeatures.v6_2_0)); var db = conn.GetDatabase(); var key = Me() + ":" + mode; @@ -2063,14 +2063,17 @@ public async Task AddWithApproxCountAsync() await db.StreamAddAsync(key, "field", "value", maxLength: 10, useApproximateMaxLength: true, flags: CommandFlags.None).ConfigureAwait(false); } - [Fact] - public void AddWithApproxCount() + [Theory] + [InlineData(StreamDeleteMode.KeepReferences)] + [InlineData(StreamDeleteMode.DeleteReferences)] + [InlineData(StreamDeleteMode.Acknowledged)] + public void AddWithApproxCount(StreamDeleteMode mode) { - using var conn = Create(require: RedisFeatures.v5_0_0); + using var conn = Create(require: ForMode(mode)); var db = conn.GetDatabase(); - var key = Me(); - db.StreamAdd(key, "field", "value", maxLength: 10, useApproximateMaxLength: true, flags: CommandFlags.None); + var key = Me() + ":" + mode; + db.StreamAdd(key, "field", "value", maxLength: 10, useApproximateMaxLength: true, deleteMode: mode, flags: CommandFlags.None); } [Fact] From c7c88c54633cc7b2d3b0fa9b7438e569463329ea Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 18 Jul 2025 15:19:35 +0100 Subject: [PATCH 09/13] more release notes --- docs/ReleaseNotes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index a9c0ad537..4135281a2 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -12,7 +12,7 @@ Current package versions: - Package updates ([#2906 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2906)) - Fix handshake error with `CLIENT ID` ([#2909 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2909)) - Add `XTRIM MINID` support ([#2842 by kijanawoodard](https://github.com/StackExchange/StackExchange.Redis/pull/2842)) -- Add new CE 8.2 stream support - `XDELEX`, `XACKDEL`, `XTRIM [KEEPREF|DELREF|ACKED]` ([#2912 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2912)) +- Add new CE 8.2 stream support - `XDELEX`, `XACKDEL`, `{XADD|XTRIM} [KEEPREF|DELREF|ACKED]` ([#2912 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2912)) ## 2.8.41 From fd5c2fd91ab1d94f8729ed5cf5c38c7de878b5dc Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 18 Jul 2025 15:25:15 +0100 Subject: [PATCH 10/13] naming is hard --- ...{StreamDeleteMode.cs => StreamTrimMode.cs} | 2 +- ...eamDeleteResult.cs => StreamTrimResult.cs} | 4 +- .../Interfaces/IDatabase.cs | 16 ++--- .../Interfaces/IDatabaseAsync.cs | 24 ++++---- .../KeyspaceIsolation/KeyPrefixed.cs | 12 ++-- .../KeyspaceIsolation/KeyPrefixedDatabase.cs | 12 ++-- .../PublicAPI/PublicAPI.Unshipped.txt | 40 ++++++------- src/StackExchange.Redis/RedisDatabase.cs | 58 +++++++++--------- src/StackExchange.Redis/ResultProcessor.cs | 8 +-- src/StackExchange.Redis/StreamConstants.cs | 8 +-- .../StackExchange.Redis.Tests/StreamTests.cs | 60 +++++++++---------- 11 files changed, 122 insertions(+), 122 deletions(-) rename src/StackExchange.Redis/Enums/{StreamDeleteMode.cs => StreamTrimMode.cs} (96%) rename src/StackExchange.Redis/Enums/{StreamDeleteResult.cs => StreamTrimResult.cs} (76%) diff --git a/src/StackExchange.Redis/Enums/StreamDeleteMode.cs b/src/StackExchange.Redis/Enums/StreamTrimMode.cs similarity index 96% rename from src/StackExchange.Redis/Enums/StreamDeleteMode.cs rename to src/StackExchange.Redis/Enums/StreamTrimMode.cs index 2909f1097..2033e8414 100644 --- a/src/StackExchange.Redis/Enums/StreamDeleteMode.cs +++ b/src/StackExchange.Redis/Enums/StreamTrimMode.cs @@ -3,7 +3,7 @@ /// /// Determines how stream trimming works. /// -public enum StreamDeleteMode +public enum StreamTrimMode { /// /// Trims the stream according to the specified policy (MAXLEN or MINID) regardless of whether entries are referenced by any consumer groups, but preserves existing references to these entries in all consumer groups' PEL. diff --git a/src/StackExchange.Redis/Enums/StreamDeleteResult.cs b/src/StackExchange.Redis/Enums/StreamTrimResult.cs similarity index 76% rename from src/StackExchange.Redis/Enums/StreamDeleteResult.cs rename to src/StackExchange.Redis/Enums/StreamTrimResult.cs index 68ef92709..aa157a8a0 100644 --- a/src/StackExchange.Redis/Enums/StreamDeleteResult.cs +++ b/src/StackExchange.Redis/Enums/StreamTrimResult.cs @@ -3,7 +3,7 @@ /// /// Determines how stream trimming works. /// -public enum StreamDeleteResult +public enum StreamTrimResult { /// /// No such id exists in the provided stream key. @@ -18,6 +18,6 @@ public enum StreamDeleteResult /// /// Entry was not deleted, but there are still dangling references. /// - /// This response relates to the mode. + /// This response relates to the mode. NotDeleted = 2, } diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 56f7b64d6..3a0a7b521 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2451,7 +2451,7 @@ IEnumerable SortedSetScan( /// The outcome of the delete operation. /// #pragma warning disable RS0026 // similar overloads - StreamDeleteResult StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None); + StreamTrimResult StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None); #pragma warning restore RS0026 /// @@ -2465,7 +2465,7 @@ IEnumerable SortedSetScan( /// The outcome of each delete operation. /// #pragma warning disable RS0026 // similar overloads - StreamDeleteResult[] StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None); + StreamTrimResult[] StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None); #pragma warning restore RS0026 /// @@ -2511,12 +2511,12 @@ IEnumerable SortedSetScan( /// The maximum length of the stream. /// If true, the "~" argument is used to allow the stream to exceed max length by a small number. This improves performance when removing messages. /// Specifies the maximal count of entries that will be evicted. - /// Determines how stream trimming should be performed. + /// Determines how stream trimming should be performed. /// The flags to use for this operation. /// The ID of the newly created message. /// #pragma warning disable RS0026 // different shape - RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode deleteMode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); + RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None); #pragma warning restore RS0026 /// @@ -2530,12 +2530,12 @@ IEnumerable SortedSetScan( /// The maximum length of the stream. /// If true, the "~" argument is used to allow the stream to exceed max length by a small number. This improves performance when removing messages. /// Specifies the maximal count of entries that will be evicted. - /// Determines how stream trimming should be performed. + /// Determines how stream trimming should be performed. /// The flags to use for this operation. /// The ID of the newly created message. /// #pragma warning disable RS0026 // different shape - RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode deleteMode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); + RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None); #pragma warning restore RS0026 /// @@ -2853,7 +2853,7 @@ IEnumerable SortedSetScan( /// The flags to use for this operation. /// The number of messages removed from the stream. /// - long StreamTrim(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); + long StreamTrim(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None); /// /// Trim the stream to a specified minimum timestamp. @@ -2866,7 +2866,7 @@ IEnumerable SortedSetScan( /// The flags to use for this operation. /// The number of messages removed from the stream. /// - long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); + long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None); /// /// If key already exists and is a string, this command appends the value at the end of the string. diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 58de79e4f..7ea567bf5 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -595,11 +595,11 @@ IAsyncEnumerable SortedSetScanAsync( Task StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None); #pragma warning disable RS0026 // similar overloads - /// - Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None); + /// + Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None); - /// - Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None); + /// + Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None); #pragma warning restore RS0026 /// @@ -609,11 +609,11 @@ IAsyncEnumerable SortedSetScanAsync( Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags); #pragma warning disable RS0026 // similar overloads - /// - Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode deleteMode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); + /// + Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None); - /// - Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode deleteMode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); + /// + Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None); #pragma warning restore RS0026 /// @@ -688,11 +688,11 @@ IAsyncEnumerable SortedSetScanAsync( /// Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength, CommandFlags flags); - /// - Task StreamTrimAsync(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); + /// + Task StreamTrimAsync(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None); - /// - Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None); + /// + Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None); /// Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs index d6d6d7c7d..0b94ac4d0 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs @@ -564,10 +564,10 @@ public Task StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, Red public Task StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => Inner.StreamAcknowledgeAsync(ToInner(key), groupName, messageIds, flags); - public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) => + public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) => Inner.StreamAcknowledgeAndDeleteAsync(ToInner(key), groupName, mode, messageId, flags); - public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => + public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => Inner.StreamAcknowledgeAndDeleteAsync(ToInner(key), groupName, mode, messageIds, flags); public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) => @@ -576,10 +576,10 @@ public Task StreamAddAsync(RedisKey key, RedisValue streamField, Red public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) => Inner.StreamAddAsync(ToInner(key), streamPairs, messageId, maxLength, useApproximateMaxLength, flags); - public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) => Inner.StreamAddAsync(ToInner(key), streamField, streamValue, messageId, maxLength, useApproximateMaxLength, limit, mode, flags); - public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) => Inner.StreamAddAsync(ToInner(key), streamPairs, messageId, maxLength, useApproximateMaxLength, limit, mode, flags); public Task StreamAutoClaimAsync(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue startAtId, int? count = null, CommandFlags flags = CommandFlags.None) => @@ -654,10 +654,10 @@ public Task StreamReadGroupAsync(StreamPosition[] streamPositions public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength, CommandFlags flags) => Inner.StreamTrimAsync(ToInner(key), maxLength, useApproximateMaxLength, flags); - public Task StreamTrimAsync(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + public Task StreamTrimAsync(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) => Inner.StreamTrimAsync(ToInner(key), maxLength, useApproximateMaxLength, limit, mode, flags); - public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) => Inner.StreamTrimByMinIdAsync(ToInner(key), minId, useApproximateMaxLength, limit, mode, flags); public Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) => diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs index 9eac6ac48..858c9817c 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs @@ -546,10 +546,10 @@ public long StreamAcknowledge(RedisKey key, RedisValue groupName, RedisValue mes public long StreamAcknowledge(RedisKey key, RedisValue groupName, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => Inner.StreamAcknowledge(ToInner(key), groupName, messageIds, flags); - public StreamDeleteResult StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) => + public StreamTrimResult StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) => Inner.StreamAcknowledgeAndDelete(ToInner(key), groupName, mode, messageId, flags); - public StreamDeleteResult[] StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => + public StreamTrimResult[] StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => Inner.StreamAcknowledgeAndDelete(ToInner(key), groupName, mode, messageIds, flags); public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) => @@ -558,10 +558,10 @@ public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue str public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) => Inner.StreamAdd(ToInner(key), streamPairs, messageId, maxLength, useApproximateMaxLength, flags); - public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) => Inner.StreamAdd(ToInner(key), streamField, streamValue, messageId, maxLength, useApproximateMaxLength, limit, mode, flags); - public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) => Inner.StreamAdd(ToInner(key), streamPairs, messageId, maxLength, useApproximateMaxLength, limit, mode, flags); public StreamAutoClaimResult StreamAutoClaim(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue startAtId, int? count = null, CommandFlags flags = CommandFlags.None) => @@ -636,10 +636,10 @@ public RedisStream[] StreamReadGroup(StreamPosition[] streamPositions, RedisValu public long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength, CommandFlags flags) => Inner.StreamTrim(ToInner(key), maxLength, useApproximateMaxLength, flags); - public long StreamTrim(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + public long StreamTrim(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) => Inner.StreamTrim(ToInner(key), maxLength, useApproximateMaxLength, limit, mode, flags); - public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) => + public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) => Inner.StreamTrimByMinId(ToInner(key), minId, useApproximateMaxLength, limit, mode, flags); public long StringAppend(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) => diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt index 2c7cf8df3..dd1b6ad5a 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt @@ -1,27 +1,27 @@ #nullable enable -StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamDeleteResult -StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamDeleteResult[]! -StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode deleteMode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue +StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamTrimResult +StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamTrimResult[]! +StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisValue -StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode deleteMode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue +StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisValue StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> long -StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long -StackExchange.Redis.IDatabase.StreamTrimByMinId(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long -StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamDeleteMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode deleteMode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabase.StreamTrimByMinId(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode deleteMode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamTrimByMinIdAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamDeleteMode mode = StackExchange.Redis.StreamDeleteMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.StreamDeleteMode -StackExchange.Redis.StreamDeleteMode.Acknowledged = 2 -> StackExchange.Redis.StreamDeleteMode -StackExchange.Redis.StreamDeleteMode.DeleteReferences = 1 -> StackExchange.Redis.StreamDeleteMode -StackExchange.Redis.StreamDeleteMode.KeepReferences = 0 -> StackExchange.Redis.StreamDeleteMode -StackExchange.Redis.StreamDeleteResult -StackExchange.Redis.StreamDeleteResult.Deleted = 1 -> StackExchange.Redis.StreamDeleteResult -StackExchange.Redis.StreamDeleteResult.NotDeleted = 2 -> StackExchange.Redis.StreamDeleteResult -StackExchange.Redis.StreamDeleteResult.NotFound = -1 -> StackExchange.Redis.StreamDeleteResult \ No newline at end of file +StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamTrimByMinIdAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.StreamTrimMode +StackExchange.Redis.StreamTrimMode.Acknowledged = 2 -> StackExchange.Redis.StreamTrimMode +StackExchange.Redis.StreamTrimMode.DeleteReferences = 1 -> StackExchange.Redis.StreamTrimMode +StackExchange.Redis.StreamTrimMode.KeepReferences = 0 -> StackExchange.Redis.StreamTrimMode +StackExchange.Redis.StreamTrimResult +StackExchange.Redis.StreamTrimResult.Deleted = 1 -> StackExchange.Redis.StreamTrimResult +StackExchange.Redis.StreamTrimResult.NotDeleted = 2 -> StackExchange.Redis.StreamTrimResult +StackExchange.Redis.StreamTrimResult.NotFound = -1 -> StackExchange.Redis.StreamTrimResult \ No newline at end of file diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 7651d2a8b..0dcc62a21 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -2424,34 +2424,34 @@ public Task StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, Red return ExecuteAsync(msg, ResultProcessor.Int64); } - public StreamDeleteResult StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) + public StreamTrimResult StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAcknowledgeAndDeleteMessage(key, groupName, mode, messageId, flags); return ExecuteSync(msg, ResultProcessor.StreamDeleteResult); } - public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) + public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAcknowledgeAndDeleteMessage(key, groupName, mode, messageId, flags); return ExecuteAsync(msg, ResultProcessor.StreamDeleteResult); } - public StreamDeleteResult[] StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) + public StreamTrimResult[] StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAcknowledgeAndDeleteMessage(key, groupName, mode, messageIds, flags); return ExecuteSync(msg, ResultProcessor.StreamDeleteResultArray)!; } - public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) + public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAcknowledgeAndDeleteMessage(key, groupName, mode, messageIds, flags); return ExecuteAsync(msg, ResultProcessor.StreamDeleteResultArray)!; } public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) - => StreamAdd(key, streamField, streamValue, messageId, maxLength, useApproximateMaxLength, null, StreamDeleteMode.KeepReferences, flags); + => StreamAdd(key, streamField, streamValue, messageId, maxLength, useApproximateMaxLength, null, StreamTrimMode.KeepReferences, flags); - public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) + public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAddMessage( key, @@ -2467,9 +2467,9 @@ public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue str } public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) - => StreamAddAsync(key, streamField, streamValue, messageId, maxLength, useApproximateMaxLength, null, StreamDeleteMode.KeepReferences, flags); + => StreamAddAsync(key, streamField, streamValue, messageId, maxLength, useApproximateMaxLength, null, StreamTrimMode.KeepReferences, flags); - public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) + public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAddMessage( key, @@ -2485,9 +2485,9 @@ public Task StreamAddAsync(RedisKey key, RedisValue streamField, Red } public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) - => StreamAdd(key, streamPairs, messageId, maxLength, useApproximateMaxLength, null, StreamDeleteMode.KeepReferences, flags); + => StreamAdd(key, streamPairs, messageId, maxLength, useApproximateMaxLength, null, StreamTrimMode.KeepReferences, flags); - public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) + public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAddMessage( key, @@ -2503,9 +2503,9 @@ public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisVal } public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) - => StreamAddAsync(key, streamPairs, messageId, maxLength, useApproximateMaxLength, null, StreamDeleteMode.KeepReferences, flags); + => StreamAddAsync(key, streamPairs, messageId, maxLength, useApproximateMaxLength, null, StreamTrimMode.KeepReferences, flags); - public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) + public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAddMessage( key, @@ -3041,30 +3041,30 @@ public Task StreamReadGroupAsync(StreamPosition[] streamPositions } public long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength, CommandFlags flags) - => StreamTrim(key, maxLength, useApproximateMaxLength, null, StreamDeleteMode.KeepReferences, flags); + => StreamTrim(key, maxLength, useApproximateMaxLength, null, StreamTrimMode.KeepReferences, flags); - public long StreamTrim(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) + public long StreamTrim(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) { var msg = GetStreamTrimMessage(true, key, maxLength, useApproximateMaxLength, limit, mode, flags); return ExecuteSync(msg, ResultProcessor.Int64); } public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength, CommandFlags flags) - => StreamTrimAsync(key, maxLength, useApproximateMaxLength, null, StreamDeleteMode.KeepReferences, flags); + => StreamTrimAsync(key, maxLength, useApproximateMaxLength, null, StreamTrimMode.KeepReferences, flags); - public Task StreamTrimAsync(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) + public Task StreamTrimAsync(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) { var msg = GetStreamTrimMessage(true, key, maxLength, useApproximateMaxLength, limit, mode, flags); return ExecuteAsync(msg, ResultProcessor.Int64); } - public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) + public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) { var msg = GetStreamTrimMessage(false, key, minId, useApproximateMaxLength, limit, mode, flags); return ExecuteSync(msg, ResultProcessor.Int64); } - public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamDeleteMode mode = StreamDeleteMode.KeepReferences, CommandFlags flags = CommandFlags.None) + public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode mode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None) { var msg = GetStreamTrimMessage(false, key, minId, useApproximateMaxLength, limit, mode, flags); return ExecuteAsync(msg, ResultProcessor.Int64); @@ -4187,12 +4187,12 @@ private Message GetStreamAcknowledgeMessage(RedisKey key, RedisValue groupName, return Message.Create(Database, flags, RedisCommand.XACK, key, values); } - private Message GetStreamAcknowledgeAndDeleteMessage(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue messageId, CommandFlags flags) + private Message GetStreamAcknowledgeAndDeleteMessage(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue messageId, CommandFlags flags) { return Message.Create(Database, flags, RedisCommand.XACKDEL, key, groupName, StreamConstants.GetMode(mode), StreamConstants.Ids, 1, messageId); } - private Message GetStreamAcknowledgeAndDeleteMessage(RedisKey key, RedisValue groupName, StreamDeleteMode mode, RedisValue[] messageIds, CommandFlags flags) + private Message GetStreamAcknowledgeAndDeleteMessage(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue[] messageIds, CommandFlags flags) { if (messageIds == null) throw new ArgumentNullException(nameof(messageIds)); if (messageIds.Length == 0) throw new ArgumentOutOfRangeException(nameof(messageIds), "messageIds must contain at least one item."); @@ -4210,7 +4210,7 @@ private Message GetStreamAcknowledgeAndDeleteMessage(RedisKey key, RedisValue gr return Message.Create(Database, flags, RedisCommand.XACKDEL, key, values); } - private Message GetStreamAddMessage(RedisKey key, RedisValue messageId, long? maxLength, bool useApproximateMaxLength, NameValueEntry streamPair, long? limit, StreamDeleteMode mode, CommandFlags flags) + private Message GetStreamAddMessage(RedisKey key, RedisValue messageId, long? maxLength, bool useApproximateMaxLength, NameValueEntry streamPair, long? limit, StreamTrimMode mode, CommandFlags flags) { // Calculate the correct number of arguments: // 3 array elements for Entry ID & NameValueEntry.Name & NameValueEntry.Value. @@ -4219,7 +4219,7 @@ private Message GetStreamAddMessage(RedisKey key, RedisValue messageId, long? ma var totalLength = 3 + (maxLength.HasValue ? 2 : 0) + (maxLength.HasValue && useApproximateMaxLength ? 1 : 0) + (limit.HasValue ? 2 : 0) - + (mode != StreamDeleteMode.KeepReferences ? 1 : 0); + + (mode != StreamTrimMode.KeepReferences ? 1 : 0); var values = new RedisValue[totalLength]; var offset = 0; @@ -4242,7 +4242,7 @@ private Message GetStreamAddMessage(RedisKey key, RedisValue messageId, long? ma values[offset++] = limit.Value; } - if (mode != StreamDeleteMode.KeepReferences) + if (mode != StreamTrimMode.KeepReferences) { values[offset++] = StreamConstants.GetMode(mode); } @@ -4259,7 +4259,7 @@ private Message GetStreamAddMessage(RedisKey key, RedisValue messageId, long? ma /// /// Gets message for . /// - private Message GetStreamAddMessage(RedisKey key, RedisValue entryId, long? maxLength, bool useApproximateMaxLength, NameValueEntry[] streamPairs, long? limit, StreamDeleteMode mode, CommandFlags flags) + private Message GetStreamAddMessage(RedisKey key, RedisValue entryId, long? maxLength, bool useApproximateMaxLength, NameValueEntry[] streamPairs, long? limit, StreamTrimMode mode, CommandFlags flags) { if (streamPairs == null) throw new ArgumentNullException(nameof(streamPairs)); if (streamPairs.Length == 0) throw new ArgumentOutOfRangeException(nameof(streamPairs), "streamPairs must contain at least one item."); @@ -4299,7 +4299,7 @@ private Message GetStreamAddMessage(RedisKey key, RedisValue entryId, long? maxL values[offset++] = limit.Value; } - if (mode != StreamDeleteMode.KeepReferences) + if (mode != StreamTrimMode.KeepReferences) { values[offset++] = StreamConstants.GetMode(mode); } @@ -4561,20 +4561,20 @@ protected override void WriteImpl(PhysicalConnection physical) public override int ArgCount => argCount; } - private Message GetStreamTrimMessage(bool maxLen, RedisKey key, RedisValue threshold, bool useApproximateMaxLength, long? limit, StreamDeleteMode mode, CommandFlags flags) + private Message GetStreamTrimMessage(bool maxLen, RedisKey key, RedisValue threshold, bool useApproximateMaxLength, long? limit, StreamTrimMode mode, CommandFlags flags) { if (limit.HasValue && limit.GetValueOrDefault() <= 0) { throw new ArgumentOutOfRangeException(nameof(limit), "limit must be greater than 0 when specified."); } - if (limit is null && !useApproximateMaxLength && mode == StreamDeleteMode.KeepReferences) + if (limit is null && !useApproximateMaxLength && mode == StreamTrimMode.KeepReferences) { // avoid array alloc in simple case return Message.Create(Database, flags, RedisCommand.XTRIM, key, maxLen ? StreamConstants.MaxLen : StreamConstants.MinId, threshold); } - var values = new RedisValue[2 + (useApproximateMaxLength ? 1 : 0) + (limit.HasValue ? 2 : 0) + (mode == StreamDeleteMode.KeepReferences ? 0 : 1)]; + var values = new RedisValue[2 + (useApproximateMaxLength ? 1 : 0) + (limit.HasValue ? 2 : 0) + (mode == StreamTrimMode.KeepReferences ? 0 : 1)]; var offset = 0; @@ -4593,7 +4593,7 @@ private Message GetStreamTrimMessage(bool maxLen, RedisKey key, RedisValue thres values[offset++] = limit.GetValueOrDefault(); } - if (mode != StreamDeleteMode.KeepReferences) // omit when not needed, for back-compat + if (mode != StreamTrimMode.KeepReferences) // omit when not needed, for back-compat { values[offset++] = StreamConstants.GetMode(mode); } diff --git a/src/StackExchange.Redis/ResultProcessor.cs b/src/StackExchange.Redis/ResultProcessor.cs index 4b07fb8cf..ce9b41f83 100644 --- a/src/StackExchange.Redis/ResultProcessor.cs +++ b/src/StackExchange.Redis/ResultProcessor.cs @@ -1378,11 +1378,11 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes } } - internal static ResultProcessor StreamDeleteResult => - Int32EnumProcessor.Instance; + internal static ResultProcessor StreamDeleteResult => + Int32EnumProcessor.Instance; - internal static ResultProcessor StreamDeleteResultArray => - Int32EnumArrayProcessor.Instance; + internal static ResultProcessor StreamDeleteResultArray => + Int32EnumArrayProcessor.Instance; private class Int32EnumProcessor : ResultProcessor where T : unmanaged, Enum { diff --git a/src/StackExchange.Redis/StreamConstants.cs b/src/StackExchange.Redis/StreamConstants.cs index 6b0a67758..929398e4b 100644 --- a/src/StackExchange.Redis/StreamConstants.cs +++ b/src/StackExchange.Redis/StreamConstants.cs @@ -75,11 +75,11 @@ internal static class StreamConstants internal static readonly RedisValue Ids = "IDS"; - internal static RedisValue GetMode(StreamDeleteMode mode) => mode switch + internal static RedisValue GetMode(StreamTrimMode mode) => mode switch { - StreamDeleteMode.KeepReferences => KeepRef, - StreamDeleteMode.DeleteReferences => DelRef, - StreamDeleteMode.Acknowledged => Acked, + StreamTrimMode.KeepReferences => KeepRef, + StreamTrimMode.DeleteReferences => DelRef, + StreamTrimMode.Acknowledged => Acked, _ => throw new ArgumentOutOfRangeException(nameof(mode)), }; } diff --git a/tests/StackExchange.Redis.Tests/StreamTests.cs b/tests/StackExchange.Redis.Tests/StreamTests.cs index bc8fd8cc5..9a46c9763 100644 --- a/tests/StackExchange.Redis.Tests/StreamTests.cs +++ b/tests/StackExchange.Redis.Tests/StreamTests.cs @@ -732,10 +732,10 @@ public void StreamConsumerGroupAcknowledgeMessage() } [Theory] - [InlineData(StreamDeleteMode.KeepReferences)] - [InlineData(StreamDeleteMode.DeleteReferences)] - [InlineData(StreamDeleteMode.Acknowledged)] - public void StreamConsumerGroupAcknowledgeAndDeleteMessage(StreamDeleteMode mode) + [InlineData(StreamTrimMode.KeepReferences)] + [InlineData(StreamTrimMode.DeleteReferences)] + [InlineData(StreamTrimMode.Acknowledged)] + public void StreamConsumerGroupAcknowledgeAndDeleteMessage(StreamTrimMode mode) { using var conn = Create(require: RedisFeatures.v8_2_0_rc1); @@ -760,10 +760,10 @@ public void StreamConsumerGroupAcknowledgeAndDeleteMessage(StreamDeleteMode mode // Single message Id overload. var oneAck = db.StreamAcknowledgeAndDelete(key, groupName, mode, id1); - Assert.Equal(StreamDeleteResult.Deleted, oneAck); + Assert.Equal(StreamTrimResult.Deleted, oneAck); - StreamDeleteResult nack = db.StreamAcknowledgeAndDelete(key, groupName, mode, notexist); - Assert.Equal(StreamDeleteResult.NotFound, nack); + StreamTrimResult nack = db.StreamAcknowledgeAndDelete(key, groupName, mode, notexist); + Assert.Equal(StreamTrimResult.NotFound, nack); // Multiple message Id overload. RedisValue[] ids = new[] { id3, notexist, id4 }; @@ -773,9 +773,9 @@ public void StreamConsumerGroupAcknowledgeAndDeleteMessage(StreamDeleteMode mode var notAcknowledged = db.StreamReadGroup(key, groupName, consumer, "0-0"); Assert.Equal(3, twoAck.Length); - Assert.Equal(StreamDeleteResult.Deleted, twoAck[0]); - Assert.Equal(StreamDeleteResult.NotFound, twoAck[1]); - Assert.Equal(StreamDeleteResult.Deleted, twoAck[2]); + Assert.Equal(StreamTrimResult.Deleted, twoAck[0]); + Assert.Equal(StreamTrimResult.NotFound, twoAck[1]); + Assert.Equal(StreamTrimResult.Deleted, twoAck[2]); Assert.Single(notAcknowledged); Assert.Equal(id2, notAcknowledged[0].Id); @@ -1972,19 +1972,19 @@ public void StreamTrimLength() Assert.Equal(1, len); } - private static Version ForMode(StreamDeleteMode mode, Version? defaultVersion = null) => mode switch + private static Version ForMode(StreamTrimMode mode, Version? defaultVersion = null) => mode switch { - StreamDeleteMode.KeepReferences => defaultVersion ?? RedisFeatures.v5_0_0, - StreamDeleteMode.Acknowledged => RedisFeatures.v8_2_0_rc1, - StreamDeleteMode.DeleteReferences => RedisFeatures.v8_2_0_rc1, + StreamTrimMode.KeepReferences => defaultVersion ?? RedisFeatures.v5_0_0, + StreamTrimMode.Acknowledged => RedisFeatures.v8_2_0_rc1, + StreamTrimMode.DeleteReferences => RedisFeatures.v8_2_0_rc1, _ => throw new ArgumentOutOfRangeException(nameof(mode)), }; [Theory] - [InlineData(StreamDeleteMode.KeepReferences)] - [InlineData(StreamDeleteMode.DeleteReferences)] - [InlineData(StreamDeleteMode.Acknowledged)] - public void StreamTrimByMinId(StreamDeleteMode mode) + [InlineData(StreamTrimMode.KeepReferences)] + [InlineData(StreamTrimMode.DeleteReferences)] + [InlineData(StreamTrimMode.Acknowledged)] + public void StreamTrimByMinId(StreamTrimMode mode) { using var conn = Create(require: ForMode(mode, RedisFeatures.v6_2_0)); @@ -2004,10 +2004,10 @@ public void StreamTrimByMinId(StreamDeleteMode mode) } [Theory] - [InlineData(StreamDeleteMode.KeepReferences)] - [InlineData(StreamDeleteMode.DeleteReferences)] - [InlineData(StreamDeleteMode.Acknowledged)] - public void StreamTrimByMinIdWithApproximateAndLimit(StreamDeleteMode mode) + [InlineData(StreamTrimMode.KeepReferences)] + [InlineData(StreamTrimMode.DeleteReferences)] + [InlineData(StreamTrimMode.Acknowledged)] + public void StreamTrimByMinIdWithApproximateAndLimit(StreamTrimMode mode) { using var conn = Create(require: ForMode(mode, RedisFeatures.v6_2_0)); @@ -2025,9 +2025,9 @@ public void StreamTrimByMinIdWithApproximateAndLimit(StreamDeleteMode mode) var numRemoved = db.StreamTrimByMinId(key, 1111111110 + maxLength, useApproximateMaxLength: true, limit: limit, mode: mode); var expectRemoved = mode switch { - StreamDeleteMode.KeepReferences => limit, - StreamDeleteMode.DeleteReferences => 0, - StreamDeleteMode.Acknowledged => 0, + StreamTrimMode.KeepReferences => limit, + StreamTrimMode.DeleteReferences => 0, + StreamTrimMode.Acknowledged => 0, _ => throw new ArgumentOutOfRangeException(nameof(mode)), }; var len = db.StreamLength(key); @@ -2064,16 +2064,16 @@ public async Task AddWithApproxCountAsync() } [Theory] - [InlineData(StreamDeleteMode.KeepReferences)] - [InlineData(StreamDeleteMode.DeleteReferences)] - [InlineData(StreamDeleteMode.Acknowledged)] - public void AddWithApproxCount(StreamDeleteMode mode) + [InlineData(StreamTrimMode.KeepReferences)] + [InlineData(StreamTrimMode.DeleteReferences)] + [InlineData(StreamTrimMode.Acknowledged)] + public void AddWithApproxCount(StreamTrimMode mode) { using var conn = Create(require: ForMode(mode)); var db = conn.GetDatabase(); var key = Me() + ":" + mode; - db.StreamAdd(key, "field", "value", maxLength: 10, useApproximateMaxLength: true, deleteMode: mode, flags: CommandFlags.None); + db.StreamAdd(key, "field", "value", maxLength: 10, useApproximateMaxLength: true, trimMode: mode, flags: CommandFlags.None); } [Fact] From 72567628652df14c189e9a03a1b0e8e419c96087 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 18 Jul 2025 16:02:34 +0100 Subject: [PATCH 11/13] XDELEX --- .../Interfaces/IDatabase.cs | 15 +++++ .../Interfaces/IDatabaseAsync.cs | 5 ++ .../KeyspaceIsolation/KeyPrefixed.cs | 3 + .../KeyspaceIsolation/KeyPrefixedDatabase.cs | 3 + .../PublicAPI/PublicAPI.Unshipped.txt | 2 + src/StackExchange.Redis/RedisDatabase.cs | 58 +++++++++++++------ src/StackExchange.Redis/ResultProcessor.cs | 4 +- .../StackExchange.Redis.Tests/StreamTests.cs | 48 +++++++++++++++ 8 files changed, 118 insertions(+), 20 deletions(-) diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 3a0a7b521..c37d3ddb0 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2650,7 +2650,22 @@ IEnumerable SortedSetScan( /// The flags to use for this operation. /// Returns the number of messages successfully deleted from the stream. /// +#pragma warning disable RS0026 // similar overloads long StreamDelete(RedisKey key, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None); +#pragma warning restore RS0026 + + /// + /// Delete messages in the stream. This method does not delete the stream. + /// + /// The key of the stream. + /// The IDs of the messages to delete. + /// Determines how stream trimming should be performed. + /// The flags to use for this operation. + /// Returns the number of messages successfully deleted from the stream. + /// +#pragma warning disable RS0026 // similar overloads + StreamTrimResult[] StreamDelete(RedisKey key, RedisValue[] messageIds, StreamTrimMode mode, CommandFlags flags = CommandFlags.None); +#pragma warning restore RS0026 /// /// Delete a consumer from a consumer group. diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 7ea567bf5..4873c1069 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -640,9 +640,14 @@ IAsyncEnumerable SortedSetScanAsync( /// Task StreamCreateConsumerGroupAsync(RedisKey key, RedisValue groupName, RedisValue? position = null, bool createStream = true, CommandFlags flags = CommandFlags.None); +#pragma warning disable RS0026 /// Task StreamDeleteAsync(RedisKey key, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None); + /// + Task StreamDeleteAsync(RedisKey key, RedisValue[] messageIds, StreamTrimMode mode, CommandFlags flags = CommandFlags.None); +#pragma warning restore RS0026 + /// Task StreamDeleteConsumerAsync(RedisKey key, RedisValue groupName, RedisValue consumerName, CommandFlags flags = CommandFlags.None); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs index 0b94ac4d0..331d23ea7 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs @@ -618,6 +618,9 @@ public Task StreamLengthAsync(RedisKey key, CommandFlags flags = CommandFl public Task StreamDeleteAsync(RedisKey key, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => Inner.StreamDeleteAsync(ToInner(key), messageIds, flags); + public Task StreamDeleteAsync(RedisKey key, RedisValue[] messageIds, StreamTrimMode mode, CommandFlags flags = CommandFlags.None) => + Inner.StreamDeleteAsync(ToInner(key), messageIds, mode, flags); + public Task StreamDeleteConsumerAsync(RedisKey key, RedisValue groupName, RedisValue consumerName, CommandFlags flags = CommandFlags.None) => Inner.StreamDeleteConsumerAsync(ToInner(key), groupName, consumerName, flags); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs index 858c9817c..18406ba9f 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs @@ -600,6 +600,9 @@ public long StreamLength(RedisKey key, CommandFlags flags = CommandFlags.None) = public long StreamDelete(RedisKey key, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) => Inner.StreamDelete(ToInner(key), messageIds, flags); + public StreamTrimResult[] StreamDelete(RedisKey key, RedisValue[] messageIds, StreamTrimMode mode, CommandFlags flags = CommandFlags.None) => + Inner.StreamDelete(ToInner(key), messageIds, mode, flags); + public long StreamDeleteConsumer(RedisKey key, RedisValue groupName, RedisValue consumerName, CommandFlags flags = CommandFlags.None) => Inner.StreamDeleteConsumer(ToInner(key), groupName, consumerName, flags); diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt index dd1b6ad5a..49e1223da 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt @@ -5,6 +5,7 @@ StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackE StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisValue StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisValue +StackExchange.Redis.IDatabase.StreamDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamTrimResult[]! StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> long StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StreamTrimByMinId(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long @@ -14,6 +15,7 @@ StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey k StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamTrimByMinIdAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 0dcc62a21..5493f3ebd 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -2427,25 +2427,25 @@ public Task StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, Red public StreamTrimResult StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAcknowledgeAndDeleteMessage(key, groupName, mode, messageId, flags); - return ExecuteSync(msg, ResultProcessor.StreamDeleteResult); + return ExecuteSync(msg, ResultProcessor.StreamTrimResult); } public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAcknowledgeAndDeleteMessage(key, groupName, mode, messageId, flags); - return ExecuteAsync(msg, ResultProcessor.StreamDeleteResult); + return ExecuteAsync(msg, ResultProcessor.StreamTrimResult); } public StreamTrimResult[] StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAcknowledgeAndDeleteMessage(key, groupName, mode, messageIds, flags); - return ExecuteSync(msg, ResultProcessor.StreamDeleteResultArray)!; + return ExecuteSync(msg, ResultProcessor.StreamTrimResultArray)!; } public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) { var msg = GetStreamAcknowledgeAndDeleteMessage(key, groupName, mode, messageIds, flags); - return ExecuteAsync(msg, ResultProcessor.StreamDeleteResultArray)!; + return ExecuteAsync(msg, ResultProcessor.StreamTrimResultArray)!; } public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, CommandFlags flags) @@ -2748,28 +2748,50 @@ public Task StreamLengthAsync(RedisKey key, CommandFlags flags = CommandFl public long StreamDelete(RedisKey key, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) { - var msg = Message.Create( - Database, - flags, - RedisCommand.XDEL, - key, - messageIds); - + var msg = Message.Create(Database, flags, RedisCommand.XDEL, key, messageIds); return ExecuteSync(msg, ResultProcessor.Int64); } - public Task StreamDeleteAsync(RedisKey key, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) + public StreamTrimResult[] StreamDelete(RedisKey key, RedisValue[] messageIds, StreamTrimMode mode, CommandFlags flags) { - var msg = Message.Create( - Database, - flags, - RedisCommand.XDEL, - key, - messageIds); + var msg = GetStreamDeleteExMessage(key, messageIds, mode, flags); + return ExecuteSync(msg, ResultProcessor.StreamTrimResultArray)!; + } + + private Message GetStreamDeleteExMessage(RedisKey key, RedisValue[] messageIds, StreamTrimMode mode, CommandFlags flags) + { + if (messageIds == null) throw new ArgumentNullException(nameof(messageIds)); + if (messageIds.Length == 0) throw new ArgumentOutOfRangeException(nameof(messageIds), "messageIds must contain at least one item."); + + // avoid array for single message case + if (messageIds.Length == 1) + { + return Message.Create(Database, flags, RedisCommand.XDELEX, key, StreamConstants.GetMode(mode), StreamConstants.Ids, 1, messageIds[0]); + } + + var values = new RedisValue[messageIds.Length + 3]; + + var offset = 0; + values[offset++] = StreamConstants.GetMode(mode); + values[offset++] = StreamConstants.Ids; + values[offset++] = messageIds.Length; + messageIds.AsSpan().CopyTo(values.AsSpan(offset)); + Debug.Assert(offset + messageIds.Length == values.Length); + return Message.Create(Database, flags, RedisCommand.XDELEX, key, values); + } + public Task StreamDeleteAsync(RedisKey key, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.XDEL, key, messageIds); return ExecuteAsync(msg, ResultProcessor.Int64); } + public Task StreamDeleteAsync(RedisKey key, RedisValue[] messageIds, StreamTrimMode mode, CommandFlags flags = CommandFlags.None) + { + var msg = GetStreamDeleteExMessage(key, messageIds, mode, flags); + return ExecuteAsync(msg, ResultProcessor.StreamTrimResultArray)!; + } + public long StreamDeleteConsumer(RedisKey key, RedisValue groupName, RedisValue consumerName, CommandFlags flags = CommandFlags.None) { var msg = Message.Create( diff --git a/src/StackExchange.Redis/ResultProcessor.cs b/src/StackExchange.Redis/ResultProcessor.cs index ce9b41f83..58e23b35b 100644 --- a/src/StackExchange.Redis/ResultProcessor.cs +++ b/src/StackExchange.Redis/ResultProcessor.cs @@ -1378,10 +1378,10 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes } } - internal static ResultProcessor StreamDeleteResult => + internal static ResultProcessor StreamTrimResult => Int32EnumProcessor.Instance; - internal static ResultProcessor StreamDeleteResultArray => + internal static ResultProcessor StreamTrimResultArray => Int32EnumArrayProcessor.Instance; private class Int32EnumProcessor : ResultProcessor where T : unmanaged, Enum diff --git a/tests/StackExchange.Redis.Tests/StreamTests.cs b/tests/StackExchange.Redis.Tests/StreamTests.cs index 9a46c9763..3e187f342 100644 --- a/tests/StackExchange.Redis.Tests/StreamTests.cs +++ b/tests/StackExchange.Redis.Tests/StreamTests.cs @@ -1287,6 +1287,54 @@ public void StreamDeleteMessages() Assert.Equal(2, messages.Length); } + [Theory] + [InlineData(StreamTrimMode.KeepReferences)] + [InlineData(StreamTrimMode.DeleteReferences)] + [InlineData(StreamTrimMode.Acknowledged)] + public void StreamDeleteExMessage(StreamTrimMode mode) + { + using var conn = Create(require: ForMode(mode)); + + var db = conn.GetDatabase(); + var key = Me() + ":" + mode; + + db.StreamAdd(key, "field1", "value1"); + db.StreamAdd(key, "field2", "value2"); + var id3 = db.StreamAdd(key, "field3", "value3"); + db.StreamAdd(key, "field4", "value4"); + + var deleted = db.StreamDelete(key, new[] { id3 }, mode: mode); + var messages = db.StreamRange(key); + + Assert.Equal(StreamTrimResult.Deleted, Assert.Single(deleted)); + Assert.Equal(3, messages.Length); + } + + [Theory] + [InlineData(StreamTrimMode.KeepReferences)] + [InlineData(StreamTrimMode.DeleteReferences)] + [InlineData(StreamTrimMode.Acknowledged)] + public void StreamDeleteExMessages(StreamTrimMode mode) + { + using var conn = Create(require: ForMode(mode)); + + var db = conn.GetDatabase(); + var key = Me() + ":" + mode; + + db.StreamAdd(key, "field1", "value1"); + var id2 = db.StreamAdd(key, "field2", "value2"); + var id3 = db.StreamAdd(key, "field3", "value3"); + db.StreamAdd(key, "field4", "value4"); + + var deleted = db.StreamDelete(key, new[] { id2, id3 }, mode: mode); + var messages = db.StreamRange(key); + + Assert.Equal(2, deleted.Length); + Assert.Equal(StreamTrimResult.Deleted, deleted[0]); + Assert.Equal(StreamTrimResult.Deleted, deleted[1]); + Assert.Equal(2, messages.Length); + } + [Fact] public void StreamGroupInfoGet() { From e0e15a7c5ac5c3efdb00f70524a50e213e278fde Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 18 Jul 2025 16:08:08 +0100 Subject: [PATCH 12/13] merge shipped --- .../PublicAPI/PublicAPI.Shipped.txt | 30 ++++++++++++++++++- .../PublicAPI/PublicAPI.Unshipped.txt | 30 +------------------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt index 7d1c2b982..43b35ba58 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt @@ -717,6 +717,12 @@ StackExchange.Redis.IDatabase.SortedSetUpdate(StackExchange.Redis.RedisKey key, StackExchange.Redis.IDatabase.SortedSetUpdate(StackExchange.Redis.RedisKey key, StackExchange.Redis.SortedSetEntry[]! values, StackExchange.Redis.SortedSetWhen when = StackExchange.Redis.SortedSetWhen.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StreamAcknowledge(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StreamAcknowledge(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamTrimResult +StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamTrimResult[]! +StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue +StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisValue +StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue +StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisValue StackExchange.Redis.IDatabase.StreamAutoClaim(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue consumerGroup, StackExchange.Redis.RedisValue claimingConsumer, long minIdleTimeInMs, StackExchange.Redis.RedisValue startAtId, int? count = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamAutoClaimResult StackExchange.Redis.IDatabase.StreamAutoClaimIdsOnly(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue consumerGroup, StackExchange.Redis.RedisValue claimingConsumer, long minIdleTimeInMs, StackExchange.Redis.RedisValue startAtId, int? count = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamAutoClaimIdsOnlyResult StackExchange.Redis.IDatabase.StreamClaim(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue consumerGroup, StackExchange.Redis.RedisValue claimingConsumer, long minIdleTimeInMs, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamEntry[]! @@ -726,6 +732,7 @@ StackExchange.Redis.IDatabase.StreamConsumerInfo(StackExchange.Redis.RedisKey ke StackExchange.Redis.IDatabase.StreamCreateConsumerGroup(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue? position = null, bool createStream = true, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool StackExchange.Redis.IDatabase.StreamCreateConsumerGroup(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue? position, StackExchange.Redis.CommandFlags flags) -> bool StackExchange.Redis.IDatabase.StreamDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabase.StreamDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamTrimResult[]! StackExchange.Redis.IDatabase.StreamDeleteConsumer(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StreamDeleteConsumerGroup(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool StackExchange.Redis.IDatabase.StreamGroupInfo(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamGroupInfo[]! @@ -740,6 +747,9 @@ StackExchange.Redis.IDatabase.StreamReadGroup(StackExchange.Redis.RedisKey key, StackExchange.Redis.IDatabase.StreamReadGroup(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, StackExchange.Redis.RedisValue? position, int? count, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.StreamEntry[]! StackExchange.Redis.IDatabase.StreamReadGroup(StackExchange.Redis.StreamPosition[]! streamPositions, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, int? countPerStream = null, bool noAck = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisStream[]! StackExchange.Redis.IDatabase.StreamReadGroup(StackExchange.Redis.StreamPosition[]! streamPositions, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, int? countPerStream, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisStream[]! +StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> long +StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabase.StreamTrimByMinId(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringAppend(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringBitCount(StackExchange.Redis.RedisKey key, long start, long end, StackExchange.Redis.CommandFlags flags) -> long StackExchange.Redis.IDatabase.StringBitCount(StackExchange.Redis.RedisKey key, long start = 0, long end = -1, StackExchange.Redis.StringIndexType indexType = StackExchange.Redis.StringIndexType.Byte, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long @@ -951,6 +961,12 @@ StackExchange.Redis.IDatabaseAsync.SortedSetUpdateAsync(StackExchange.Redis.Redi StackExchange.Redis.IDatabaseAsync.SortedSetUpdateAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.SortedSetEntry[]! values, StackExchange.Redis.SortedSetWhen when = StackExchange.Redis.SortedSetWhen.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAutoClaimAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue consumerGroup, StackExchange.Redis.RedisValue claimingConsumer, long minIdleTimeInMs, StackExchange.Redis.RedisValue startAtId, int? count = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAutoClaimIdsOnlyAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue consumerGroup, StackExchange.Redis.RedisValue claimingConsumer, long minIdleTimeInMs, StackExchange.Redis.RedisValue startAtId, int? count = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamClaimAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue consumerGroup, StackExchange.Redis.RedisValue claimingConsumer, long minIdleTimeInMs, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! @@ -960,6 +976,7 @@ StackExchange.Redis.IDatabaseAsync.StreamConsumerInfoAsync(StackExchange.Redis.R StackExchange.Redis.IDatabaseAsync.StreamCreateConsumerGroupAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue? position = null, bool createStream = true, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamCreateConsumerGroupAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue? position, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamDeleteConsumerAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamDeleteConsumerGroupAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamGroupInfoAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! @@ -974,6 +991,9 @@ StackExchange.Redis.IDatabaseAsync.StreamReadGroupAsync(StackExchange.Redis.Redi StackExchange.Redis.IDatabaseAsync.StreamReadGroupAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, StackExchange.Redis.RedisValue? position, int? count, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamReadGroupAsync(StackExchange.Redis.StreamPosition[]! streamPositions, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, int? countPerStream = null, bool noAck = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamReadGroupAsync(StackExchange.Redis.StreamPosition[]! streamPositions, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue consumerName, int? countPerStream, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StreamTrimByMinIdAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringAppendAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitCountAsync(StackExchange.Redis.RedisKey key, long start, long end, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitCountAsync(StackExchange.Redis.RedisKey key, long start = 0, long end = -1, StackExchange.Redis.StringIndexType indexType = StackExchange.Redis.StringIndexType.Byte, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! @@ -1896,4 +1916,12 @@ StackExchange.Redis.ConfigurationOptions.SetUserPfxCertificate(string! userCerti StackExchange.Redis.Bitwise.AndOr = 6 -> StackExchange.Redis.Bitwise StackExchange.Redis.Bitwise.Diff = 4 -> StackExchange.Redis.Bitwise StackExchange.Redis.Bitwise.Diff1 = 5 -> StackExchange.Redis.Bitwise -StackExchange.Redis.Bitwise.One = 7 -> StackExchange.Redis.Bitwise \ No newline at end of file +StackExchange.Redis.Bitwise.One = 7 -> StackExchange.Redis.Bitwise +StackExchange.Redis.StreamTrimMode +StackExchange.Redis.StreamTrimMode.Acknowledged = 2 -> StackExchange.Redis.StreamTrimMode +StackExchange.Redis.StreamTrimMode.DeleteReferences = 1 -> StackExchange.Redis.StreamTrimMode +StackExchange.Redis.StreamTrimMode.KeepReferences = 0 -> StackExchange.Redis.StreamTrimMode +StackExchange.Redis.StreamTrimResult +StackExchange.Redis.StreamTrimResult.Deleted = 1 -> StackExchange.Redis.StreamTrimResult +StackExchange.Redis.StreamTrimResult.NotDeleted = 2 -> StackExchange.Redis.StreamTrimResult +StackExchange.Redis.StreamTrimResult.NotFound = -1 -> StackExchange.Redis.StreamTrimResult \ No newline at end of file diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt index 49e1223da..91b0e1a43 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt @@ -1,29 +1 @@ -#nullable enable -StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamTrimResult -StackExchange.Redis.IDatabase.StreamAcknowledgeAndDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamTrimResult[]! -StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue -StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisValue -StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue -StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisValue -StackExchange.Redis.IDatabase.StreamDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.StreamTrimResult[]! -StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> long -StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long -StackExchange.Redis.IDatabase.StreamTrimByMinId(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long -StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAndDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode trimMode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue streamField, StackExchange.Redis.RedisValue streamValue, StackExchange.Redis.RedisValue? messageId, int? maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.StreamTrimMode mode, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StreamTrimByMinIdAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StackExchange.Redis.StreamTrimMode mode = StackExchange.Redis.StreamTrimMode.KeepReferences, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.StreamTrimMode -StackExchange.Redis.StreamTrimMode.Acknowledged = 2 -> StackExchange.Redis.StreamTrimMode -StackExchange.Redis.StreamTrimMode.DeleteReferences = 1 -> StackExchange.Redis.StreamTrimMode -StackExchange.Redis.StreamTrimMode.KeepReferences = 0 -> StackExchange.Redis.StreamTrimMode -StackExchange.Redis.StreamTrimResult -StackExchange.Redis.StreamTrimResult.Deleted = 1 -> StackExchange.Redis.StreamTrimResult -StackExchange.Redis.StreamTrimResult.NotDeleted = 2 -> StackExchange.Redis.StreamTrimResult -StackExchange.Redis.StreamTrimResult.NotFound = -1 -> StackExchange.Redis.StreamTrimResult \ No newline at end of file +#nullable enable \ No newline at end of file From 855cd5cfec9c01ed049058f83612c29782c837f4 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Mon, 21 Jul 2025 09:39:26 +0100 Subject: [PATCH 13/13] Update StreamTests.cs --- tests/StackExchange.Redis.Tests/StreamTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/StackExchange.Redis.Tests/StreamTests.cs b/tests/StackExchange.Redis.Tests/StreamTests.cs index 2a0d113e7..ac7bdcdb0 100644 --- a/tests/StackExchange.Redis.Tests/StreamTests.cs +++ b/tests/StackExchange.Redis.Tests/StreamTests.cs @@ -1289,7 +1289,7 @@ public async Task StreamDeleteMessages() [InlineData(StreamTrimMode.Acknowledged)] public void StreamDeleteExMessage(StreamTrimMode mode) { - using var conn = Create(require: ForMode(mode)); + using var conn = Create(require: RedisFeatures.v8_2_0_rc1); // XDELEX var db = conn.GetDatabase(); var key = Me() + ":" + mode; @@ -1312,7 +1312,7 @@ public void StreamDeleteExMessage(StreamTrimMode mode) [InlineData(StreamTrimMode.Acknowledged)] public void StreamDeleteExMessages(StreamTrimMode mode) { - using var conn = Create(require: ForMode(mode)); + using var conn = Create(require: RedisFeatures.v8_2_0_rc1); // XDELEX var db = conn.GetDatabase(); var key = Me() + ":" + mode;