From 97903a1447d027e2d4cbec7227082cf8e4d53063 Mon Sep 17 00:00:00 2001 From: Kijana Woodard Date: Tue, 21 Jan 2025 16:18:00 -0600 Subject: [PATCH 1/5] 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 b97bba73b..816a8a078 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 75d93d0f9..1a2dc5d21 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 f467aca24..799cf30fb 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 aebc8fc6d..a59b8f607 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 d060bb011e2ddaa86202c31cb17f5444e0e3859b Mon Sep 17 00:00:00 2001 From: Kijana Woodard Date: Tue, 21 Jan 2025 16:30:17 -0600 Subject: [PATCH 2/5] 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 64c7b092b25d98f30d1efb1eeca7bc3b0db07c8a Mon Sep 17 00:00:00 2001 From: Kijana Woodard Date: Tue, 21 Jan 2025 16:42:43 -0600 Subject: [PATCH 3/5] 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 f8bc7c59b225860a26d5d5661d60dafb5139abd7 Mon Sep 17 00:00:00 2001 From: Kijana Woodard Date: Wed, 22 Jan 2025 12:55:32 -0600 Subject: [PATCH 4/5] re-run appveyor From cc7801892d407a060952aaf98295c0379ad716f2 Mon Sep 17 00:00:00 2001 From: Kijana Woodard Date: Fri, 4 Jul 2025 12:54:22 -0500 Subject: [PATCH 5/5] 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 816a8a078..f3d5ac151 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 1a2dc5d21..73319fadf 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 799cf30fb..5ed06745c 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 a59b8f607..045861f0a 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() {