From 0b03c7d809db16a3aaba9afc986ca305e1a02afc Mon Sep 17 00:00:00 2001 From: Niklas Petersen Date: Wed, 24 Apr 2024 22:12:04 +0200 Subject: [PATCH 01/13] Add ResumeAtRevision support to KV Watcher --- .../Internal/NatsKVWatcher.cs | 8 +- src/NATS.Client.KeyValueStore/NatsKVOpts.cs | 8 ++ .../NatsKVWatcherTest.cs | 85 +++++++++++++++++++ 3 files changed, 99 insertions(+), 2 deletions(-) diff --git a/src/NATS.Client.KeyValueStore/Internal/NatsKVWatcher.cs b/src/NATS.Client.KeyValueStore/Internal/NatsKVWatcher.cs index f86960994..a3bec33a0 100644 --- a/src/NATS.Client.KeyValueStore/Internal/NatsKVWatcher.cs +++ b/src/NATS.Client.KeyValueStore/Internal/NatsKVWatcher.cs @@ -383,10 +383,14 @@ private async ValueTask CreatePushConsumer(string origin) config.HeadersOnly = true; } - if (sequence > 0) + // Resume from a specific revision ? + if (sequence > 0 || _opts.ResumeAtRevision > 0) { config.DeliverPolicy = ConsumerConfigDeliverPolicy.ByStartSequence; - config.OptStartSeq = sequence + 1; + + // If Sequence is set, it means that the consumer is being recreated, and we should start + // from the next sequence, otherwise we should use the revision specified in the options. + config.OptStartSeq = sequence > 0 ? sequence + 1 : _opts.ResumeAtRevision; } var consumer = await _context.CreateOrUpdateConsumerAsync( diff --git a/src/NATS.Client.KeyValueStore/NatsKVOpts.cs b/src/NATS.Client.KeyValueStore/NatsKVOpts.cs index 88483e229..ebc2eeba8 100644 --- a/src/NATS.Client.KeyValueStore/NatsKVOpts.cs +++ b/src/NATS.Client.KeyValueStore/NatsKVOpts.cs @@ -41,6 +41,14 @@ public record NatsKVWatchOpts /// Async function called when the enumerator reaches the end of data. Return True to break the async enumeration, False to allow the enumeration to continue. /// public Func>? OnNoData { get; init; } + + /// + /// The revision to start from, if set to 0 (default) this will be ignored. + /// + /// Setting this to a non-zero value will cause the watcher to ignore the values for and . + /// + /// + public ulong ResumeAtRevision { get; set; } } public record NatsKVDeleteOpts diff --git a/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs b/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs index a6f097a4f..e47afb8af 100644 --- a/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs +++ b/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs @@ -347,4 +347,89 @@ public async Task Serialization_errors() break; } } + + // Test that watch can resume from a specific revision + [Fact] + public async Task Watch_resume_at_revision() + { + await using var server = NatsServer.StartJS(); + await using var nats = server.CreateClientConnection(); + + const string bucket = "Watch_resume_at_revision"; + var config = new NatsKVConfig(bucket) { History = 10 }; + + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var cancellationToken = cts.Token; + + var js = new NatsJSContext(nats); + var kv = new NatsKVContext(js); + var store = await kv.CreateStoreAsync(config, cancellationToken: cancellationToken); + + await store.PutAsync("k1", 1, cancellationToken: cancellationToken); + await store.PutAsync("k2", 2, cancellationToken: cancellationToken); + var revK3 = await store.PutAsync("k3", 3, cancellationToken: cancellationToken); + await store.PutAsync("k4", 3, cancellationToken: cancellationToken); + + // Watch all + var watchOps = new NatsKVWatchOpts() { MetaOnly = true, }; + var watchAll = store.WatchAsync(opts: watchOps, cancellationToken: cancellationToken); + + // Expect to see k1, k2, k3 and k4 + var allEntries = new List<(ulong Revision, string key)>(); + await foreach (var key in watchAll) + { + allEntries.Add((key.Revision, key.Key)); + if (key.Delta == 0) + { + break; + } + } + + // Expects k1, k2, k3 and k4 + allEntries.Should().HaveCount(4); + + // Watch from the revision of k3 + var watchOpsFromRevK3 = watchOps with { ResumeAtRevision = revK3, }; + + var watchFromRevision = store.WatchAsync(opts: watchOpsFromRevK3, cancellationToken: cancellationToken); + + // Expect to see k2 and k3, and k4 + var fromRevisionEntries = new List<(ulong Revision, string key)>(); + await foreach (var key in watchFromRevision) + { + fromRevisionEntries.Add((key.Revision, key.Key)); + if (key.Delta == 0) + { + break; + } + } + + // Expects k2, k3 and k4 + fromRevisionEntries.Should().HaveCount(2); + + + // Watch from none existing revision + var noData = false; + var watchOpsNoneExisting = watchOps with + { + ResumeAtRevision = 9999, + OnNoData = (_) => + { + noData = true; + return ValueTask.FromResult(true); + }, + }; + + var watchFromNoneExistingRevision = + store.WatchAsync(opts: watchOpsNoneExisting, cancellationToken: cancellationToken); + + // Expect to see no data + await foreach (var key in watchFromNoneExistingRevision) + { + // We should not see any entries, if we get here something is wrong + Assert.Fail("Should not return any entries, and OnNoData should have been called to bail out"); + } + + noData.Should().BeTrue(); + } } From a6e126938f22825e371e51e46e3f4b35d724e1a5 Mon Sep 17 00:00:00 2001 From: Niklas Petersen Date: Wed, 24 Apr 2024 22:17:30 +0200 Subject: [PATCH 02/13] Fix doc comments --- src/NATS.Client.KeyValueStore/NatsKVOpts.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NATS.Client.KeyValueStore/NatsKVOpts.cs b/src/NATS.Client.KeyValueStore/NatsKVOpts.cs index ebc2eeba8..d5ed6a06f 100644 --- a/src/NATS.Client.KeyValueStore/NatsKVOpts.cs +++ b/src/NATS.Client.KeyValueStore/NatsKVOpts.cs @@ -45,7 +45,7 @@ public record NatsKVWatchOpts /// /// The revision to start from, if set to 0 (default) this will be ignored. /// - /// Setting this to a non-zero value will cause the watcher to ignore the values for and . + /// Setting this to a non-zero value will cause the watcher to ignore the values for and . /// /// public ulong ResumeAtRevision { get; set; } From fbc94f07f4720f909fa49fd1bc464d8e5b6c0841 Mon Sep 17 00:00:00 2001 From: Niklas Petersen Date: Wed, 24 Apr 2024 22:20:13 +0200 Subject: [PATCH 03/13] Remove empty line in test --- tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs b/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs index e47afb8af..b35453c0d 100644 --- a/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs +++ b/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs @@ -407,7 +407,6 @@ public async Task Watch_resume_at_revision() // Expects k2, k3 and k4 fromRevisionEntries.Should().HaveCount(2); - // Watch from none existing revision var noData = false; var watchOpsNoneExisting = watchOps with From c545c9c59dcec5bcceb525e81af0cf1fd71d55e2 Mon Sep 17 00:00:00 2001 From: Niklas Petersen Date: Wed, 24 Apr 2024 22:39:58 +0200 Subject: [PATCH 04/13] Make ResumeAtRevision init only --- src/NATS.Client.KeyValueStore/NatsKVOpts.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NATS.Client.KeyValueStore/NatsKVOpts.cs b/src/NATS.Client.KeyValueStore/NatsKVOpts.cs index d5ed6a06f..6d8b53cc4 100644 --- a/src/NATS.Client.KeyValueStore/NatsKVOpts.cs +++ b/src/NATS.Client.KeyValueStore/NatsKVOpts.cs @@ -48,7 +48,7 @@ public record NatsKVWatchOpts /// Setting this to a non-zero value will cause the watcher to ignore the values for and . /// /// - public ulong ResumeAtRevision { get; set; } + public ulong ResumeAtRevision { get; init; } } public record NatsKVDeleteOpts From 38c229588cf04f9f04337fdb06a2ae6db6b138e7 Mon Sep 17 00:00:00 2001 From: Niklas Petersen Date: Thu, 25 Apr 2024 16:26:18 +0200 Subject: [PATCH 05/13] Simplify if statement --- .../Internal/NatsKVWatcher.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/NATS.Client.KeyValueStore/Internal/NatsKVWatcher.cs b/src/NATS.Client.KeyValueStore/Internal/NatsKVWatcher.cs index a3bec33a0..b7062929c 100644 --- a/src/NATS.Client.KeyValueStore/Internal/NatsKVWatcher.cs +++ b/src/NATS.Client.KeyValueStore/Internal/NatsKVWatcher.cs @@ -384,13 +384,15 @@ private async ValueTask CreatePushConsumer(string origin) } // Resume from a specific revision ? - if (sequence > 0 || _opts.ResumeAtRevision > 0) + if (sequence > 0) { config.DeliverPolicy = ConsumerConfigDeliverPolicy.ByStartSequence; - - // If Sequence is set, it means that the consumer is being recreated, and we should start - // from the next sequence, otherwise we should use the revision specified in the options. - config.OptStartSeq = sequence > 0 ? sequence + 1 : _opts.ResumeAtRevision; + config.OptStartSeq = sequence + 1; + } + else if (_opts.ResumeAtRevision > 0) + { + config.DeliverPolicy = ConsumerConfigDeliverPolicy.ByStartSequence; + config.OptStartSeq = _opts.ResumeAtRevision; } var consumer = await _context.CreateOrUpdateConsumerAsync( From 7e416b833928b80b0fc1ee0716d5ec1e7147d0c8 Mon Sep 17 00:00:00 2001 From: Ziya Suzen Date: Wed, 5 Jun 2024 00:17:07 +0100 Subject: [PATCH 06/13] Opts validation --- src/NATS.Client.KeyValueStore/INatsKVStore.cs | 4 + src/NATS.Client.KeyValueStore/NatsKVOpts.cs | 29 ++++++- src/NATS.Client.KeyValueStore/NatsKVStore.cs | 4 + .../NatsKVWatcherTest.cs | 75 +++++++++++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) diff --git a/src/NATS.Client.KeyValueStore/INatsKVStore.cs b/src/NATS.Client.KeyValueStore/INatsKVStore.cs index 959bc6cf7..6e25a92b7 100644 --- a/src/NATS.Client.KeyValueStore/INatsKVStore.cs +++ b/src/NATS.Client.KeyValueStore/INatsKVStore.cs @@ -91,6 +91,7 @@ public interface INatsKVStore /// A used to cancel the API call. /// Serialized value type /// An asynchronous enumerable which can be used in await foreach loops + /// There was a conflict in options, e.g. IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is set to a non-zero value. IAsyncEnumerable> WatchAsync(IEnumerable keys, INatsDeserialize? serializer = default, NatsKVWatchOpts? opts = default, CancellationToken cancellationToken = default); /// @@ -112,6 +113,7 @@ public interface INatsKVStore /// A used to cancel the API call. /// Serialized value type /// An async enumerable of entries to be used in an await foreach + /// There was a conflict in options, e.g. IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is set to a non-zero value. IAsyncEnumerable> HistoryAsync(string key, INatsDeserialize? serializer = default, NatsKVWatchOpts? opts = default, CancellationToken cancellationToken = default); /// @@ -126,6 +128,7 @@ public interface INatsKVStore /// /// Purge options /// A used to cancel the API call. + /// There was a conflict in options, e.g. IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is set to a non-zero value. ValueTask PurgeDeletesAsync(NatsKVPurgeOpts? opts = default, CancellationToken cancellationToken = default); /// @@ -134,5 +137,6 @@ public interface INatsKVStore /// Watch options /// A used to cancel the API call. /// An async enumerable of keys to be used in an await foreach + /// There was a conflict in options, e.g. IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is set to a non-zero value. IAsyncEnumerable GetKeysAsync(NatsKVWatchOpts? opts = default, CancellationToken cancellationToken = default); } diff --git a/src/NATS.Client.KeyValueStore/NatsKVOpts.cs b/src/NATS.Client.KeyValueStore/NatsKVOpts.cs index 6d8b53cc4..3763de0e1 100644 --- a/src/NATS.Client.KeyValueStore/NatsKVOpts.cs +++ b/src/NATS.Client.KeyValueStore/NatsKVOpts.cs @@ -25,11 +25,19 @@ public record NatsKVWatchOpts /// /// Include history of the entries /// + /// + /// Setting this will cause the watcher to throw + /// if the values for and/or are set. + /// public bool IncludeHistory { get; init; } = false; /// /// Only retrieve updates, not current values /// + /// + /// Setting this will cause the watcher to throw + /// if the values for and/or are set. + /// public bool UpdatesOnly { get; init; } = false; /// @@ -45,10 +53,29 @@ public record NatsKVWatchOpts /// /// The revision to start from, if set to 0 (default) this will be ignored. /// - /// Setting this to a non-zero value will cause the watcher to ignore the values for and . + /// Setting this to a non-zero value will cause the watcher to throw + /// if the values for and/or are set. /// /// public ulong ResumeAtRevision { get; init; } + + internal void ThrowIfInvalid() + { + if (ResumeAtRevision > 0) + { + if (IncludeHistory || UpdatesOnly) + { + throw new InvalidOperationException("IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is set to a non-zero value."); + } + } + else + { + if (IncludeHistory && UpdatesOnly) + { + throw new InvalidOperationException("IncludeHistory and UpdatesOnly are mutually exclusive."); + } + } + } } public record NatsKVDeleteOpts diff --git a/src/NATS.Client.KeyValueStore/NatsKVStore.cs b/src/NATS.Client.KeyValueStore/NatsKVStore.cs index ba8e26185..2c80ddc62 100644 --- a/src/NATS.Client.KeyValueStore/NatsKVStore.cs +++ b/src/NATS.Client.KeyValueStore/NatsKVStore.cs @@ -340,6 +340,7 @@ public async IAsyncEnumerable> WatchAsync(IEnumerable } } + /// public async IAsyncEnumerable> HistoryAsync(string key, INatsDeserialize? serializer = default, NatsKVWatchOpts? opts = default, [EnumeratorCancellation] CancellationToken cancellationToken = default) { try @@ -382,6 +383,7 @@ public async ValueTask GetStatusAsync(CancellationToken cancellati public IAsyncEnumerable> WatchAsync(INatsDeserialize? serializer = default, NatsKVWatchOpts? opts = default, CancellationToken cancellationToken = default) => WatchAsync([">"], serializer, opts, cancellationToken); + /// public async ValueTask PurgeDeletesAsync(NatsKVPurgeOpts? opts = default, CancellationToken cancellationToken = default) { opts ??= NatsKVPurgeOpts.Default; @@ -459,6 +461,8 @@ internal async ValueTask> WatchInternalAsync(IEnumerable(); + opts.ThrowIfInvalid(); + var watcher = new NatsKVWatcher( context: _context, bucket: Bucket, diff --git a/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs b/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs index fd47d5f08..b3b49b48f 100644 --- a/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs +++ b/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs @@ -544,4 +544,79 @@ public async Task Watch_resume_at_revision() noData.Should().BeTrue(); } + + [Fact] + public async Task Validate_watch_options() + { + await using var server = NatsServer.StartJS(); + await using var nats = server.CreateClientConnection(); + + const string bucket = nameof(Validate_watch_options); + var config = new NatsKVConfig(bucket) { History = 10 }; + + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var cancellationToken = cts.Token; + var js = new NatsJSContext(nats); + var kv = new NatsKVContext(js); + var store = await kv.CreateStoreAsync(config, cancellationToken: cancellationToken); + + for (int i = 0; i < 10; i++) + { + await store.PutAsync("x", i, cancellationToken: cancellationToken); + } + + // Valid options + foreach (var opts in new[] + { + new NatsKVWatchOpts { IncludeHistory = false, UpdatesOnly = false, ResumeAtRevision = 5 }, + new NatsKVWatchOpts { IncludeHistory = true, UpdatesOnly = false, ResumeAtRevision = 0 }, + new NatsKVWatchOpts { IncludeHistory = false, UpdatesOnly = true, ResumeAtRevision = 0 }, + }) + { + var count = 0; + var cts2 = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + + if (opts.UpdatesOnly) + { + cts2.Cancel(); + count++; + } + + try + { + await foreach (var entry in store.WatchAsync([">"], opts: opts, cancellationToken: cts2.Token)) + { + count++; + _output.WriteLine($"entry: {entry.Key} ({entry.Revision}): {entry.Value}"); + if (entry.Value == 9) + break; + } + } + catch (TaskCanceledException) + { + } + catch (OperationCanceledException) + { + } + + count.Should().BeGreaterThan(0); + } + + // Invalid options + foreach (var opts in new[] + { + new NatsKVWatchOpts { IncludeHistory = true, UpdatesOnly = false, ResumeAtRevision = 5 }, + new NatsKVWatchOpts { IncludeHistory = true, UpdatesOnly = true, ResumeAtRevision = 5 }, + new NatsKVWatchOpts { IncludeHistory = false, UpdatesOnly = true, ResumeAtRevision = 5 }, + new NatsKVWatchOpts { IncludeHistory = true, UpdatesOnly = true, ResumeAtRevision = 0 }, + }) + { + await Assert.ThrowsAsync(async () => + { + await foreach (var entry in store.WatchAsync([">"], opts: opts, cancellationToken: cancellationToken)) + { + } + }); + } + } } From f5e40fd30fd7378b44792eaeb003cfc3b9782ecd Mon Sep 17 00:00:00 2001 From: Ziya Suzen Date: Wed, 5 Jun 2024 00:21:36 +0100 Subject: [PATCH 07/13] dotnet format --- tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs b/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs index b3b49b48f..a66ca1f5f 100644 --- a/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs +++ b/tests/NATS.Client.KeyValueStore.Tests/NatsKVWatcherTest.cs @@ -560,7 +560,7 @@ public async Task Validate_watch_options() var kv = new NatsKVContext(js); var store = await kv.CreateStoreAsync(config, cancellationToken: cancellationToken); - for (int i = 0; i < 10; i++) + for (var i = 0; i < 10; i++) { await store.PutAsync("x", i, cancellationToken: cancellationToken); } From 6a48971a6057ebfd6895f7205231f0fc17937a7d Mon Sep 17 00:00:00 2001 From: Ziya Suzen Date: Thu, 6 Jun 2024 01:00:59 +0100 Subject: [PATCH 08/13] Update src/NATS.Client.KeyValueStore/INatsKVStore.cs Co-authored-by: Caleb Lloyd <2414837+caleblloyd@users.noreply.github.com> --- src/NATS.Client.KeyValueStore/INatsKVStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NATS.Client.KeyValueStore/INatsKVStore.cs b/src/NATS.Client.KeyValueStore/INatsKVStore.cs index 6e25a92b7..5d21c74cd 100644 --- a/src/NATS.Client.KeyValueStore/INatsKVStore.cs +++ b/src/NATS.Client.KeyValueStore/INatsKVStore.cs @@ -91,7 +91,7 @@ public interface INatsKVStore /// A used to cancel the API call. /// Serialized value type /// An asynchronous enumerable which can be used in await foreach loops - /// There was a conflict in options, e.g. IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is set to a non-zero value. + /// There was a conflict in options, e.g. IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is not set. IAsyncEnumerable> WatchAsync(IEnumerable keys, INatsDeserialize? serializer = default, NatsKVWatchOpts? opts = default, CancellationToken cancellationToken = default); /// From c8303a01a3054de90156e574723fb852596a27b3 Mon Sep 17 00:00:00 2001 From: Ziya Suzen Date: Thu, 6 Jun 2024 01:01:05 +0100 Subject: [PATCH 09/13] Update src/NATS.Client.KeyValueStore/INatsKVStore.cs Co-authored-by: Caleb Lloyd <2414837+caleblloyd@users.noreply.github.com> --- src/NATS.Client.KeyValueStore/INatsKVStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NATS.Client.KeyValueStore/INatsKVStore.cs b/src/NATS.Client.KeyValueStore/INatsKVStore.cs index 5d21c74cd..ce34f751a 100644 --- a/src/NATS.Client.KeyValueStore/INatsKVStore.cs +++ b/src/NATS.Client.KeyValueStore/INatsKVStore.cs @@ -137,6 +137,6 @@ public interface INatsKVStore /// Watch options /// A used to cancel the API call. /// An async enumerable of keys to be used in an await foreach - /// There was a conflict in options, e.g. IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is set to a non-zero value. + /// There was a conflict in options, e.g. IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is not set. IAsyncEnumerable GetKeysAsync(NatsKVWatchOpts? opts = default, CancellationToken cancellationToken = default); } From b1e04a9dd886e709eb1ee27d6030063ee8f9a793 Mon Sep 17 00:00:00 2001 From: Ziya Suzen Date: Thu, 6 Jun 2024 01:01:15 +0100 Subject: [PATCH 10/13] Update src/NATS.Client.KeyValueStore/INatsKVStore.cs Co-authored-by: Caleb Lloyd <2414837+caleblloyd@users.noreply.github.com> --- src/NATS.Client.KeyValueStore/INatsKVStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NATS.Client.KeyValueStore/INatsKVStore.cs b/src/NATS.Client.KeyValueStore/INatsKVStore.cs index ce34f751a..f0ea13d68 100644 --- a/src/NATS.Client.KeyValueStore/INatsKVStore.cs +++ b/src/NATS.Client.KeyValueStore/INatsKVStore.cs @@ -113,7 +113,7 @@ public interface INatsKVStore /// A used to cancel the API call. /// Serialized value type /// An async enumerable of entries to be used in an await foreach - /// There was a conflict in options, e.g. IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is set to a non-zero value. + /// There was a conflict in options, e.g. IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is not set. IAsyncEnumerable> HistoryAsync(string key, INatsDeserialize? serializer = default, NatsKVWatchOpts? opts = default, CancellationToken cancellationToken = default); /// From 7ed9e886b3ab84528311644f8f85c5bcb199ca23 Mon Sep 17 00:00:00 2001 From: Ziya Suzen Date: Thu, 6 Jun 2024 01:01:39 +0100 Subject: [PATCH 11/13] Update src/NATS.Client.KeyValueStore/NatsKVOpts.cs Co-authored-by: Niklas Petersen <7766483+niklasfp@users.noreply.github.com> --- src/NATS.Client.KeyValueStore/NatsKVOpts.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NATS.Client.KeyValueStore/NatsKVOpts.cs b/src/NATS.Client.KeyValueStore/NatsKVOpts.cs index 3763de0e1..894a27bba 100644 --- a/src/NATS.Client.KeyValueStore/NatsKVOpts.cs +++ b/src/NATS.Client.KeyValueStore/NatsKVOpts.cs @@ -65,7 +65,7 @@ internal void ThrowIfInvalid() { if (IncludeHistory || UpdatesOnly) { - throw new InvalidOperationException("IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is set to a non-zero value."); + throw new InvalidOperationException("IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is set to a zero value."); } } else From a424acefedcf79ad42cc46face3e20ca2238019a Mon Sep 17 00:00:00 2001 From: Ziya Suzen Date: Thu, 6 Jun 2024 01:03:56 +0100 Subject: [PATCH 12/13] Remove exception comment from PurgeDeletesAsync method --- src/NATS.Client.KeyValueStore/INatsKVStore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NATS.Client.KeyValueStore/INatsKVStore.cs b/src/NATS.Client.KeyValueStore/INatsKVStore.cs index f0ea13d68..58b8a48ee 100644 --- a/src/NATS.Client.KeyValueStore/INatsKVStore.cs +++ b/src/NATS.Client.KeyValueStore/INatsKVStore.cs @@ -128,7 +128,6 @@ public interface INatsKVStore /// /// Purge options /// A used to cancel the API call. - /// There was a conflict in options, e.g. IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is set to a non-zero value. ValueTask PurgeDeletesAsync(NatsKVPurgeOpts? opts = default, CancellationToken cancellationToken = default); /// From 2a72374b83b729d2133398c394e9f0979c6dd41b Mon Sep 17 00:00:00 2001 From: Ziya Suzen Date: Thu, 6 Jun 2024 01:07:00 +0100 Subject: [PATCH 13/13] Typo --- src/NATS.Client.KeyValueStore/NatsKVOpts.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NATS.Client.KeyValueStore/NatsKVOpts.cs b/src/NATS.Client.KeyValueStore/NatsKVOpts.cs index 894a27bba..6a915cfc5 100644 --- a/src/NATS.Client.KeyValueStore/NatsKVOpts.cs +++ b/src/NATS.Client.KeyValueStore/NatsKVOpts.cs @@ -65,7 +65,7 @@ internal void ThrowIfInvalid() { if (IncludeHistory || UpdatesOnly) { - throw new InvalidOperationException("IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is set to a zero value."); + throw new InvalidOperationException("IncludeHistory and UpdatesOnly are only valid when ResumeAtRevision is not set."); } } else