From 450f0d4cb5038bab0c48028d801f7da158f3f2bd Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 4 Aug 2020 15:58:26 +0800 Subject: [PATCH 001/107] clean up code --- .../RedisClientManagerCacheClient.cs | 133 ++++++------------ src/ServiceStack.Redis/RedisManagerPool.cs | 8 +- 2 files changed, 49 insertions(+), 92 deletions(-) diff --git a/src/ServiceStack.Redis/RedisClientManagerCacheClient.cs b/src/ServiceStack.Redis/RedisClientManagerCacheClient.cs index eefa5f56..8da814ee 100644 --- a/src/ServiceStack.Redis/RedisClientManagerCacheClient.cs +++ b/src/ServiceStack.Redis/RedisClientManagerCacheClient.cs @@ -31,18 +31,14 @@ public void Dispose() { } public T Get(string key) { - using (var client = redisManager.GetReadOnlyClient()) - { - return client.Get(key); - } + using var client = redisManager.GetReadOnlyClient(); + return client.Get(key); } public IDictionary GetAll(IEnumerable keys) { - using (var client = redisManager.GetReadOnlyClient()) - { - return client.GetAll(keys); - } + using var client = redisManager.GetReadOnlyClient(); + return client.GetAll(keys); } private void AssertNotReadOnly() @@ -59,164 +55,127 @@ public ICacheClient GetClient() public bool Remove(string key) { - using (var client = GetClient()) - { - return client.Remove(key); - } + using var client = GetClient(); + return client.Remove(key); } public void RemoveAll(IEnumerable keys) { - using (var client = GetClient()) - { - client.RemoveAll(keys); - } + using var client = GetClient(); + client.RemoveAll(keys); } public long Increment(string key, uint amount) { - using (var client = GetClient()) - { - return client.Increment(key, amount); - } + using var client = GetClient(); + return client.Increment(key, amount); } public long Decrement(string key, uint amount) { - using (var client = GetClient()) - { - return client.Decrement(key, amount); - } + using var client = GetClient(); + return client.Decrement(key, amount); } public bool Add(string key, T value) { - using (var client = GetClient()) - { - return client.Add(key, value); - } + using var client = GetClient(); + return client.Add(key, value); } public bool Set(string key, T value) { - using (var client = GetClient()) - { - return client.Set(key, value); - } + using var client = GetClient(); + return client.Set(key, value); } public bool Replace(string key, T value) { - using (var client = GetClient()) - { - return client.Replace(key, value); - } + using var client = GetClient(); + return client.Replace(key, value); } public bool Add(string key, T value, DateTime expiresAt) { - using (var client = GetClient()) - { - return client.Add(key, value, expiresAt); - } + using var client = GetClient(); + return client.Add(key, value, expiresAt); } public bool Set(string key, T value, DateTime expiresAt) { - using (var client = GetClient()) - { - return client.Set(key, value, expiresAt); - } + using var client = GetClient(); + return client.Set(key, value, expiresAt); } public bool Replace(string key, T value, DateTime expiresAt) { - using (var client = GetClient()) - { - return client.Replace(key, value, expiresAt); - } + using var client = GetClient(); + return client.Replace(key, value, expiresAt); } public bool Add(string key, T value, TimeSpan expiresIn) { - using (var client = GetClient()) - { - return client.Set(key, value, expiresIn); - } + using var client = GetClient(); + return client.Set(key, value, expiresIn); } public bool Set(string key, T value, TimeSpan expiresIn) { - using (var client = GetClient()) - { - return client.Set(key, value, expiresIn); - } + using var client = GetClient(); + return client.Set(key, value, expiresIn); } public bool Replace(string key, T value, TimeSpan expiresIn) { - using (var client = GetClient()) - { - return client.Replace(key, value, expiresIn); - } + using var client = GetClient(); + return client.Replace(key, value, expiresIn); } public void FlushAll() { - using (var client = GetClient()) - { - client.FlushAll(); - } + using var client = GetClient(); + client.FlushAll(); } public void SetAll(IDictionary values) { - using (var client = GetClient()) - { - client.SetAll(values); - } + using var client = GetClient(); + client.SetAll(values); } public void RemoveByPattern(string pattern) { - using (var client = GetClient()) + using var client = GetClient(); + if (client is IRemoveByPattern redisClient) { - if (client is IRemoveByPattern redisClient) - { - redisClient.RemoveByPattern(pattern); - } + redisClient.RemoveByPattern(pattern); } } public void RemoveByRegex(string pattern) { - using (var client = GetClient()) + using var client = GetClient(); + if (client is IRemoveByPattern redisClient) { - if (client is IRemoveByPattern redisClient) - { - redisClient.RemoveByRegex(pattern); - } + redisClient.RemoveByRegex(pattern); } } public TimeSpan? GetTimeToLive(string key) { - using (var client = GetClient()) + using var client = GetClient(); + if (client is ICacheClientExtended redisClient) { - if (client is ICacheClientExtended redisClient) - { - return redisClient.GetTimeToLive(key); - } + return redisClient.GetTimeToLive(key); } + return null; } public IEnumerable GetKeysByPattern(string pattern) { - using (var client = (ICacheClientExtended)GetClient()) - { - return client.GetKeysByPattern(pattern).ToList(); - } + using var client = (ICacheClientExtended)GetClient(); + return client.GetKeysByPattern(pattern).ToList(); } public void RemoveExpiredEntries() diff --git a/src/ServiceStack.Redis/RedisManagerPool.cs b/src/ServiceStack.Redis/RedisManagerPool.cs index d50f2edd..73b66823 100644 --- a/src/ServiceStack.Redis/RedisManagerPool.cs +++ b/src/ServiceStack.Redis/RedisManagerPool.cs @@ -409,9 +409,9 @@ protected virtual void Dispose(bool disposing) try { // get rid of unmanaged resources - for (var i = 0; i < clients.Length; i++) + foreach (var client in clients) { - Dispose(clients[i]); + Dispose(client); } } catch (Exception ex) @@ -433,9 +433,7 @@ protected void Dispose(RedisClient redisClient) } catch (Exception ex) { - Log.Error(string.Format( - "Error when trying to dispose of RedisClient to host {0}:{1}", - redisClient.Host, redisClient.Port), ex); + Log.Error($"Error when trying to dispose of RedisClient to host {redisClient.Host}:{redisClient.Port}", ex); } } From 0de025a7cc105347c60fee1597b0c155f78d2b87 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 4 Aug 2020 15:58:36 +0800 Subject: [PATCH 002/107] bump to v5.9.2 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 07377b6c..deb81e2e 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.9.1 + 5.9.2 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 57967e92..bc78d984 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.9.1 + 5.9.2 latest false From ed2145a3581b3d5049bbd0ce02686038816b879e Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 5 Aug 2020 22:45:18 +0800 Subject: [PATCH 003/107] bump to v5.9.3 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index deb81e2e..058f372d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.9.2 + 5.9.3 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index bc78d984..fcf9af62 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.9.2 + 5.9.3 latest false From 4c43d7f9cab922d98348374d783bfa9c7e86f7bb Mon Sep 17 00:00:00 2001 From: Michael Malone Date: Mon, 17 Aug 2020 16:28:09 -0600 Subject: [PATCH 004/107] Fix inaccurate CurrentServerTime The `Stopwatch.ElapsedTicks` property is _timer_ ticks, which is not the same as the `DateTime.Ticks` according to the note in the remarks section of [the documentation](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch.elapsedticks?view=netcore-3.1#remarks) (and my testing that led to this discovery). Try the following simple c# sample to see the difference: ``` var serverTimeAtStart = DateTime.UtcNow; var startedAt = Stopwatch.StartNew(); Thread.Sleep(5000); var CurrentServerTime = new DateTime(serverTimeAtStart.Ticks + startedAt.ElapsedTicks, DateTimeKind.Utc); var CorrectServerTime = new DateTime(serverTimeAtStart.Ticks + startedAt.Elapsed.Ticks, DateTimeKind.Utc); Debug.WriteLine($"Current: {CurrentServerTime}\r\nCorrect: {CorrectServerTime}\r\nNow: {DateTime.UtcNow}"); ``` --- src/ServiceStack.Redis/RedisPubSubServer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ServiceStack.Redis/RedisPubSubServer.cs b/src/ServiceStack.Redis/RedisPubSubServer.cs index 5db5440c..728d4421 100644 --- a/src/ServiceStack.Redis/RedisPubSubServer.cs +++ b/src/ServiceStack.Redis/RedisPubSubServer.cs @@ -60,7 +60,7 @@ public bool AutoRestart set => Interlocked.CompareExchange(ref autoRestart, value ? YES : NO, autoRestart); } - public DateTime CurrentServerTime => new DateTime(serverTimeAtStart.Ticks + startedAt.ElapsedTicks, DateTimeKind.Utc); + public DateTime CurrentServerTime => new DateTime(serverTimeAtStart.Ticks + startedAt.Elapsed.Ticks, DateTimeKind.Utc); public long BgThreadCount => Interlocked.CompareExchange(ref bgThreadCount, 0, 0); @@ -582,4 +582,4 @@ public virtual void Dispose() DisposeHeartbeatTimer(); } } -} \ No newline at end of file +} From 8db6b2fdcdf9de5efd104403e40e42cb2decf7d0 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 25 Aug 2020 17:09:59 +0800 Subject: [PATCH 005/107] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c070ba7c..e5d9bdfc 100644 --- a/README.md +++ b/README.md @@ -802,7 +802,7 @@ What to look out for in your code-base to prevent against multiple concurrent us - Use `IRedisClient` redis instance client within a `using` statement - Never use a client instance after it has been disposed - - Never use (or return) a "server collection" (e.g. [Redis.Lists](#simple-example-using-redis-lists)) after the client has been disposed + - Never use (or return) a "server collection or lock" (e.g. [Redis.Lists](#simple-example-using-redis-lists)) after the client has been disposed - Never keep a Singleton or `static` instance to a redis client (just the `IRedisClientsManager` factory) - Never use the same redis client in multiple threads, i.e. have each thread resolve their own client from the factory From ec93f90cf44c78f4c0e7debb62ca495ef64255df Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 25 Aug 2020 17:10:56 +0800 Subject: [PATCH 006/107] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e5d9bdfc..ebdf8f15 100644 --- a/README.md +++ b/README.md @@ -802,7 +802,7 @@ What to look out for in your code-base to prevent against multiple concurrent us - Use `IRedisClient` redis instance client within a `using` statement - Never use a client instance after it has been disposed - - Never use (or return) a "server collection or lock" (e.g. [Redis.Lists](#simple-example-using-redis-lists)) after the client has been disposed + - Never use (or return) a "server collection or resource" (e.g. [Redis.Lists](#simple-example-using-redis-lists), lock) after the client has been disposed - Never keep a Singleton or `static` instance to a redis client (just the `IRedisClientsManager` factory) - Never use the same redis client in multiple threads, i.e. have each thread resolve their own client from the factory From b9b8a0ea1d97644fc826773d2f2b345b8c821c6d Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Sun, 30 Aug 2020 20:26:12 +0100 Subject: [PATCH 007/107] Async (#2) Implement async APIs for ServiceStack.Redis --- src/ServiceStack.Redis.sln | 24 +- .../AsyncInterfaces/IRedisClientAsync.cs | 395 +++++ .../IRedisClientsManagerAsync.cs | 46 + .../IRedisHashAsync.Generic.cs | 32 + .../AsyncInterfaces/IRedisHashAsync.cs | 23 + .../IRedisListAsync.Generic.cs | 64 + .../AsyncInterfaces/IRedisListAsync.cs | 58 + .../IRedisNativeClientAsync.cs | 297 ++++ .../AsyncInterfaces/IRedisPipelineAsync.cs | 9 + .../IRedisPipelineSharedAsync.cs | 15 + .../IRedisQueueCompletableOperationAsync.cs | 23 + .../IRedisQueueableOperationAsync.cs | 26 + .../AsyncInterfaces/IRedisSetAsync.Generic.cs | 42 + .../AsyncInterfaces/IRedisSetAsync.cs | 47 + .../IRedisSortedSetAsync.Generic.cs | 51 + .../AsyncInterfaces/IRedisSortedSetAsync.cs | 46 + .../IRedisSubscriptionAsync.cs | 61 + .../AsyncInterfaces/IRedisTransactionAsync.cs | 29 + .../IRedisTransactionBaseAsync.cs | 11 + .../AsyncInterfaces/IRedisTypedClientAsync.cs | 211 +++ .../IRedisTypedPipelineAsync.cs | 11 + .../IRedisTypedQueueableOperationAsync.cs | 26 + .../IRedisTypedTransactionAsync.cs | 28 + .../Caching/ICacheClientAsync.cs | 116 ++ .../Caching/ICacheClientExtendedAsync.cs | 19 + .../Caching/IRemoveByPatternAsync.cs | 19 + .../Data/IEntityStoreAsync.Generic.cs | 36 + .../Data/IEntityStoreAsync.cs | 30 + .../BasicRedisClientManager.Async.cs | 158 ++ .../BasicRedisClientManager.ICacheClient.cs | 2 +- .../BasicRedisClientManager.cs | 6 +- .../BufferedReader.Async.cs | 93 + src/ServiceStack.Redis/BufferedReader.cs | 76 + src/ServiceStack.Redis/BufferedStream.cs | 5 + .../Generic/QueuedRedisTypedCommand.Async.cs | 110 ++ .../Generic/QueuedRedisTypedCommand.cs | 10 +- .../Generic/RedisClientHash.Generic.Async.cs | 55 + .../Generic/RedisClientHash.Generic.cs | 2 +- .../Generic/RedisClientList.Generic.Async.cs | 184 ++ .../Generic/RedisClientList.Generic.cs | 2 +- .../Generic/RedisClientSet.Generic.Async.cs | 109 ++ .../Generic/RedisClientSet.Generic.cs | 2 +- .../RedisClientSortedSet.Generic.Async.cs | 136 ++ .../Generic/RedisClientSortedSet.Generic.cs | 2 +- .../Generic/RedisTypedClient.Async.cs | 755 ++++++++ .../Generic/RedisTypedClient.cs | 41 +- .../Generic/RedisTypedClient_List.Async.cs | 33 + .../Generic/RedisTypedClient_List.cs | 3 +- .../Generic/RedisTypedClient_Set.Async.cs | 30 + .../Generic/RedisTypedClient_Set.cs | 2 +- .../RedisTypedClient_SortedSet.Async.cs | 30 + .../Generic/RedisTypedClient_SortedSet.cs | 2 +- .../Generic/RedisTypedPipeline.Async.cs | 265 +++ .../Generic/RedisTypedPipeline.cs | 2 +- .../Generic/RedisTypedTransaction.Async.cs | 94 + .../Generic/RedisTypedTransaction.cs | 21 +- .../Pipeline/QueuedRedisCommand.Async.cs | 51 + .../Pipeline/QueuedRedisCommand.cs | 8 +- .../Pipeline/QueuedRedisOperation.Async.cs | 159 ++ .../Pipeline/QueuedRedisOperation.cs | 140 +- .../Pipeline/RedisAllPurposePipeline.Async.cs | 330 ++++ .../Pipeline/RedisAllPurposePipeline.cs | 4 +- .../Pipeline/RedisCommand.Async.cs | 118 ++ .../Pipeline/RedisCommand.cs | 13 +- .../Pipeline/RedisPipelineCommand.Async.cs | 32 + .../Pipeline/RedisPipelineCommand.cs | 2 +- .../RedisQueueCompletableOperation.cs | 2 +- .../PooledRedisClientManager.Async.cs | 43 + .../PooledRedisClientManager.cs | 12 +- .../Properties/AssemblyInfo.cs | 2 +- src/ServiceStack.Redis/RedisClient.Async.cs | 1524 +++++++++++++++++ .../RedisClient.ICacheClient.cs | 98 +- src/ServiceStack.Redis/RedisClient.cs | 152 +- .../RedisClientExtensions.cs | 2 +- .../RedisClientHash.Async.cs | 55 + src/ServiceStack.Redis/RedisClientHash.cs | 2 +- .../RedisClientList.Async.cs | 161 ++ src/ServiceStack.Redis/RedisClientList.cs | 2 +- .../RedisClientManagerCacheClient.Async.cs | 173 ++ .../RedisClientManagerCacheClient.cs | 2 +- .../RedisClientSet.Async.cs | 118 ++ src/ServiceStack.Redis/RedisClientSet.cs | 2 +- .../RedisClientSortedSet.Async.cs | 102 ++ .../RedisClientSortedSet.cs | 2 +- src/ServiceStack.Redis/RedisClient_Admin.cs | 18 +- .../RedisClient_Hash.Async.cs | 30 + src/ServiceStack.Redis/RedisClient_Hash.cs | 22 +- .../RedisClient_List.Async.cs | 30 + src/ServiceStack.Redis/RedisClient_List.cs | 31 +- .../RedisClient_Set.Async.cs | 30 + src/ServiceStack.Redis/RedisClient_Set.cs | 46 +- src/ServiceStack.Redis/RedisClient_Slowlog.cs | 5 + .../RedisClient_SortedSet.Async.cs | 30 + .../RedisClient_SortedSet.cs | 30 +- .../RedisClientsManagerExtensions.Async.cs | 120 ++ .../RedisClientsManagerExtensions.cs | 2 +- src/ServiceStack.Redis/RedisLock.Async.cs | 93 + src/ServiceStack.Redis/RedisLock.cs | 17 +- .../RedisManagerPool.Async.cs | 33 + src/ServiceStack.Redis/RedisManagerPool.cs | 9 +- .../RedisNativeClient.Async.cs | 1487 ++++++++++++++++ src/ServiceStack.Redis/RedisNativeClient.cs | 306 ++-- .../RedisNativeClient_Utils.Async.cs | 552 ++++++ .../RedisNativeClient_Utils.cs | 86 +- src/ServiceStack.Redis/RedisSentinel.cs | 1 + .../RedisSubscription.Async.cs | 184 ++ src/ServiceStack.Redis/RedisSubscription.cs | 36 +- .../ServiceStack.Redis.csproj | 19 +- .../Diagnostic/TrackingRedisClientProxy.cs | 4 +- .../Diagnostic/TrackingRedisClientsManager.cs | 4 +- .../Support/Locking/DistributedLock.Async.cs | 121 ++ .../Support/Locking/DistributedLock.cs | 2 +- .../Support/Locking/IDistributedLock.Async.cs | 36 + .../Implementation/SerializingRedisClient.cs | 2 +- .../Transaction/RedisTransaction.Async.cs | 129 ++ .../Transaction/RedisTransaction.cs | 23 +- .../ValueTask_Utils.Async.cs | 136 ++ .../IncrBenchmarks.cs | 281 +++ tests/ServiceStack.Redis.Benchmark/Program.cs | 39 + .../ServiceStack.Redis.Benchmark.csproj | 18 + .../AdhocClientTests.Async.cs | 25 + .../AsyncImplementationsTests.Async.cs | 865 ++++++++++ .../BasicRediscClientManagerTests.Async.cs | 42 + .../CultureInfoTests.Async.cs | 47 + .../CustomCommandTests.Async.cs | 140 ++ .../Generic/RedisClientHashTestsBase.Async.cs | 249 +++ .../RedisClientHashTestsModels.Async.cs | 80 + .../Generic/RedisClientListTestExtra.Async.cs | 58 + .../Generic/RedisClientListTestsBase.Async.cs | 334 ++++ .../RedisClientListTestsModels.Async.cs | 91 + .../Generic/RedisClientListTestsModels.cs | 1 + .../Generic/RedisClientSetTestsBase.Async.cs | 343 ++++ .../RedisClientSetTestsModels.Async.cs | 81 + .../Generic/RedisClientTests.Async.cs | 129 ++ ...RedisPersistenceProviderTestsBase.Async.cs | 96 ++ ...sPersistenceProviderTestsBaseImpl.Async.cs | 40 + .../Generic/RedisTypedClientTests.Async.cs | 126 ++ .../Generic/RedisTypedPipelineTests.Async.cs | 230 +++ .../RedisTypedTransactionTests.Async.cs | 240 +++ .../LexTests.Async.cs | 115 ++ .../LuaCachedScripts.Async.cs | 295 ++++ .../LuaCachedScripts.cs | 2 + .../PooledRedisClientManagerTests.Async.cs | 440 +++++ .../PooledRedisClientManagerTests.cs | 2 +- .../Properties/AssemblyInfo.cs | 2 +- ...edisBasicPersistenceProviderTests.Async.cs | 262 +++ .../RedisBatchTests.Async.cs | 46 + .../RedisCacheClientTests.Async.cs | 140 ++ .../RedisClientConfigTests.Async.cs | 108 ++ .../RedisClientConfigTests.cs | 3 +- .../RedisClientEvalTests.Async.cs | 200 +++ .../RedisClientHashTests.Async.cs | 351 ++++ .../RedisClientListTests.Async.cs | 502 ++++++ .../RedisClientSetTests.Async.cs | 335 ++++ .../RedisClientSortedSetTests.Async.cs | 454 +++++ .../RedisClientSortedSetTests.cs | 2 +- .../RedisClientTests.Async.cs | 669 ++++++++ .../RedisClientTests.cs | 23 +- .../RedisClientTestsBase.Async.cs | 126 ++ .../RedisClientTestsBase.cs | 4 +- .../RedisGeoNativeClientTests.Async.cs | 286 ++++ .../RedisGeoTests.Async.cs | 239 +++ .../RedisHyperLogTests.Async.cs | 35 + .../RedisPersistenceProviderTests.Async.cs | 69 + .../RedisPipelineCommonTests.Async.cs | 73 + .../RedisPipelineTests.Async.cs | 281 +++ .../RedisPubSubTests.Async.cs | 293 ++++ .../RedisScanTests.Async.cs | 173 ++ .../RedisStatsTests.Async.cs | 43 + .../RedisTransactionCommonTests.Async.cs | 68 + .../RedisTransactionTests.Async.cs | 417 +++++ .../RetryCommandTests.Async.cs | 143 ++ .../RetryCommandTests.cs | 5 +- .../ServiceStack.Redis.Tests.csproj | 16 +- .../ShippersExample.Async.cs | 128 ++ tests/ServiceStack.Redis.Tests/SslTests.cs | 2 +- .../TrackThreadTests.cs | 4 +- .../UserSessionRedisClientTests.Async.cs | 436 +++++ .../ValueTypeExamples.Async.cs | 138 ++ 179 files changed, 20986 insertions(+), 509 deletions(-) create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisClientAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisClientsManagerAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.Generic.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.Generic.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisNativeClientAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineSharedAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueCompletableOperationAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueableOperationAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.Generic.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.Generic.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisSubscriptionAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionBaseAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedClientAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedPipelineAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedQueueableOperationAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedTransactionAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientExtendedAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/IRemoveByPatternAsync.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.Generic.cs create mode 100644 src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.cs create mode 100644 src/ServiceStack.Redis/BasicRedisClientManager.Async.cs create mode 100644 src/ServiceStack.Redis/BufferedReader.Async.cs create mode 100644 src/ServiceStack.Redis/BufferedReader.cs create mode 100644 src/ServiceStack.Redis/Generic/QueuedRedisTypedCommand.Async.cs create mode 100644 src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs create mode 100644 src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs create mode 100644 src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs create mode 100644 src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs create mode 100644 src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs create mode 100644 src/ServiceStack.Redis/Generic/RedisTypedClient_List.Async.cs create mode 100644 src/ServiceStack.Redis/Generic/RedisTypedClient_Set.Async.cs create mode 100644 src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.Async.cs create mode 100644 src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs create mode 100644 src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs create mode 100644 src/ServiceStack.Redis/Pipeline/QueuedRedisCommand.Async.cs create mode 100644 src/ServiceStack.Redis/Pipeline/QueuedRedisOperation.Async.cs create mode 100644 src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.Async.cs create mode 100644 src/ServiceStack.Redis/Pipeline/RedisCommand.Async.cs create mode 100644 src/ServiceStack.Redis/Pipeline/RedisPipelineCommand.Async.cs create mode 100644 src/ServiceStack.Redis/PooledRedisClientManager.Async.cs create mode 100644 src/ServiceStack.Redis/RedisClient.Async.cs create mode 100644 src/ServiceStack.Redis/RedisClientHash.Async.cs create mode 100644 src/ServiceStack.Redis/RedisClientList.Async.cs create mode 100644 src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs create mode 100644 src/ServiceStack.Redis/RedisClientSet.Async.cs create mode 100644 src/ServiceStack.Redis/RedisClientSortedSet.Async.cs create mode 100644 src/ServiceStack.Redis/RedisClient_Hash.Async.cs create mode 100644 src/ServiceStack.Redis/RedisClient_List.Async.cs create mode 100644 src/ServiceStack.Redis/RedisClient_Set.Async.cs create mode 100644 src/ServiceStack.Redis/RedisClient_SortedSet.Async.cs create mode 100644 src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs create mode 100644 src/ServiceStack.Redis/RedisLock.Async.cs create mode 100644 src/ServiceStack.Redis/RedisManagerPool.Async.cs create mode 100644 src/ServiceStack.Redis/RedisNativeClient.Async.cs create mode 100644 src/ServiceStack.Redis/RedisNativeClient_Utils.Async.cs create mode 100644 src/ServiceStack.Redis/RedisSubscription.Async.cs create mode 100644 src/ServiceStack.Redis/Support/Locking/DistributedLock.Async.cs create mode 100644 src/ServiceStack.Redis/Support/Locking/IDistributedLock.Async.cs create mode 100644 src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs create mode 100644 src/ServiceStack.Redis/ValueTask_Utils.Async.cs create mode 100644 tests/ServiceStack.Redis.Benchmark/IncrBenchmarks.cs create mode 100644 tests/ServiceStack.Redis.Benchmark/Program.cs create mode 100644 tests/ServiceStack.Redis.Benchmark/ServiceStack.Redis.Benchmark.csproj create mode 100644 tests/ServiceStack.Redis.Tests/AdhocClientTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/BasicRediscClientManagerTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/CultureInfoTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/CustomCommandTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsBase.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsModels.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestExtra.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsBase.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsModels.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsBase.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsModels.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisClientTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBase.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBaseImpl.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisTypedPipelineTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/Generic/RedisTypedTransactionTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/LexTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/LuaCachedScripts.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisBasicPersistenceProviderTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisBatchTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisCacheClientTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisClientConfigTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisClientEvalTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisClientHashTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisClientListTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisClientSetTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisClientSortedSetTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisClientTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisGeoNativeClientTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisGeoTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisHyperLogTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisPersistenceProviderTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisPipelineCommonTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisPipelineTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisPubSubTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisScanTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisStatsTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisTransactionCommonTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RedisTransactionTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/RetryCommandTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/ShippersExample.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/UserSessionRedisClientTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/ValueTypeExamples.Async.cs diff --git a/src/ServiceStack.Redis.sln b/src/ServiceStack.Redis.sln index aa0193dd..3c500eee 100644 --- a/src/ServiceStack.Redis.sln +++ b/src/ServiceStack.Redis.sln @@ -1,18 +1,18 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2010 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29721.120 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{38F69F8F-9303-4BAF-B081-D28339163E07}" ProjectSection(SolutionItems) = preProject + ..\build\build-core.proj = ..\build\build-core.proj ..\build\build.bat = ..\build\build.bat ..\build\build.proj = ..\build\build.proj ..\build\build.tasks = ..\build\build.tasks - ..\README.md = ..\README.md Directory.Build.props = Directory.Build.props - ServiceStack.Redis\ServiceStack.Redis.Core.csproj = ServiceStack.Redis\ServiceStack.Redis.Core.csproj - ..\build\build-core.proj = ..\build\build-core.proj ..\tests\Directory.Build.props = ..\tests\Directory.Build.props + ..\README.md = ..\README.md + ServiceStack.Redis\ServiceStack.Redis.Core.csproj = ServiceStack.Redis\ServiceStack.Redis.Core.csproj EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceStack.Redis", "ServiceStack.Redis\ServiceStack.Redis.csproj", "{AF99F19B-4C04-4F58-81EF-B092F1FCC540}" @@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console.Tests", "..\tests\C EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceStack.Redis.Tests.Sentinel", "..\tests\ServiceStack.Redis.Tests.Sentinel\ServiceStack.Redis.Tests.Sentinel.csproj", "{91C55091-A946-49B5-9517-8794EBCC5784}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack.Redis.Benchmark", "..\tests\ServiceStack.Redis.Benchmark\ServiceStack.Redis.Benchmark.csproj", "{959CA5FE-6525-4EEF-86CA-F4978BEFF14F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -77,6 +79,18 @@ Global {91C55091-A946-49B5-9517-8794EBCC5784}.Release|x64.ActiveCfg = Release|Any CPU {91C55091-A946-49B5-9517-8794EBCC5784}.Release|x64.Build.0 = Release|Any CPU {91C55091-A946-49B5-9517-8794EBCC5784}.Release|x86.ActiveCfg = Release|Any CPU + {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Debug|x64.ActiveCfg = Debug|Any CPU + {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Debug|x64.Build.0 = Debug|Any CPU + {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Debug|x86.ActiveCfg = Debug|Any CPU + {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Debug|x86.Build.0 = Debug|Any CPU + {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Release|Any CPU.Build.0 = Release|Any CPU + {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Release|x64.ActiveCfg = Release|Any CPU + {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Release|x64.Build.0 = Release|Any CPU + {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Release|x86.ActiveCfg = Release|Any CPU + {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisClientAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisClientAsync.cs new file mode 100644 index 00000000..50aac66f --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisClientAsync.cs @@ -0,0 +1,395 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis/ +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Caching; +using ServiceStack.Data; +using ServiceStack.Model; +using ServiceStack.Redis.Generic; +using ServiceStack.Redis.Pipeline; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + public interface IRedisClientAsync + : IEntityStoreAsync, ICacheClientExtendedAsync, IRemoveByPatternAsync + { + /* non-obvious changes from IRedisClient: + - sync API is Save (foreground) and SaveAsync (background); renamed here to ForegroundSaveAsync and BackgroundSaveAsync + to avoid overload problems and accidental swaps from bg to fg when migrating to async API + - RewriteAppendOnlyFileAsync becomes BackgroundRewriteAppendOnlyFileAsync for consistency with the above + - AcquireLockAsync - timeout made an optional arg rather than an overload + - SetValueIf[Not]ExistsAsync - flatten overloads via optional expiry + - move all Dictionary<,> args to IDictionary<,> + - add SlowlogGet / Reset + */ + //Basic Redis Connection operations + + ////Basic Redis Connection Info + //ValueTask this[string key] { get; set; } + + IHasNamed Lists { get; } + IHasNamed Sets { get; } + IHasNamed SortedSets { get; } + IHasNamed Hashes { get; } + + long Db { get; } + ValueTask SelectAsync(long db, CancellationToken cancellationToken = default); + ValueTask DbSizeAsync(CancellationToken cancellationToken = default); + + ValueTask> InfoAsync(CancellationToken cancellationToken = default); + ValueTask GetServerTimeAsync(CancellationToken cancellationToken = default); + ValueTask LastSaveAsync(CancellationToken cancellationToken = default); + string Host { get; } + int Port { get; } + int ConnectTimeout { get; set; } + int RetryTimeout { get; set; } + int RetryCount { get; set; } + int SendTimeout { get; set; } + string Password { get; set; } + bool HadExceptions { get; } + + ValueTask PingAsync(CancellationToken cancellationToken = default); + ValueTask EchoAsync(string text, CancellationToken cancellationToken = default); + + ValueTask CustomAsync(object[] cmdWithArgs, CancellationToken cancellationToken = default); + ValueTask CustomAsync(params object[] cmdWithArgs); // convenience API + + ValueTask ForegroundSaveAsync(CancellationToken cancellationToken = default); + ValueTask BackgroundSaveAsync(CancellationToken cancellationToken = default); + ValueTask ShutdownAsync(CancellationToken cancellationToken = default); + ValueTask ShutdownNoSaveAsync(CancellationToken cancellationToken = default); + ValueTask BackgroundRewriteAppendOnlyFileAsync(CancellationToken cancellationToken = default); + ValueTask FlushDbAsync(CancellationToken cancellationToken = default); + + + ValueTask GetServerRoleAsync(CancellationToken cancellationToken = default); + ValueTask GetServerRoleInfoAsync(CancellationToken cancellationToken = default); + ValueTask GetConfigAsync(string item, CancellationToken cancellationToken = default); + ValueTask SetConfigAsync(string item, string value, CancellationToken cancellationToken = default); + ValueTask SaveConfigAsync(CancellationToken cancellationToken = default); + ValueTask ResetInfoStatsAsync(CancellationToken cancellationToken = default); + + ValueTask GetClientAsync(CancellationToken cancellationToken = default); + ValueTask SetClientAsync(string name, CancellationToken cancellationToken = default); + ValueTask KillClientAsync(string address, CancellationToken cancellationToken = default); + ValueTask KillClientsAsync(string fromAddress = null, string withId = null, RedisClientType? ofType = null, bool? skipMe = null, CancellationToken cancellationToken = default); + ValueTask>> GetClientsInfoAsync(CancellationToken cancellationToken = default); + ValueTask PauseAllClientsAsync(TimeSpan duration, CancellationToken cancellationToken = default); + + ValueTask> GetAllKeysAsync(CancellationToken cancellationToken = default); + + //Fetch fully qualified key for specific Type and Id + string UrnKey(T value); + string UrnKey(object id); + string UrnKey(Type type, object id); + + ValueTask SetAllAsync(IEnumerable keys, IEnumerable values, CancellationToken cancellationToken = default); + ValueTask SetAllAsync(IDictionary map, CancellationToken cancellationToken = default); + ValueTask SetValuesAsync(IDictionary map, CancellationToken cancellationToken = default); + + ValueTask SetValueAsync(string key, string value, CancellationToken cancellationToken = default); + ValueTask SetValueAsync(string key, string value, TimeSpan expireIn, CancellationToken cancellationToken = default); + ValueTask SetValueIfNotExistsAsync(string key, string value, TimeSpan? expireIn = default, CancellationToken cancellationToken = default); + ValueTask SetValueIfExistsAsync(string key, string value, TimeSpan? expireIn = default, CancellationToken cancellationToken = default); + + ValueTask GetValueAsync(string key, CancellationToken cancellationToken = default); + ValueTask GetAndSetValueAsync(string key, string value, CancellationToken cancellationToken = default); + + ValueTask> GetValuesAsync(List keys, CancellationToken cancellationToken = default); + ValueTask> GetValuesAsync(List keys, CancellationToken cancellationToken = default); + ValueTask> GetValuesMapAsync(List keys, CancellationToken cancellationToken = default); + ValueTask> GetValuesMapAsync(List keys, CancellationToken cancellationToken = default); + ValueTask AppendToValueAsync(string key, string value, CancellationToken cancellationToken = default); + ValueTask RenameKeyAsync(string fromName, string toName, CancellationToken cancellationToken = default); + + //store POCOs as hash + ValueTask GetFromHashAsync(object id, CancellationToken cancellationToken = default); + ValueTask StoreAsHashAsync(T entity, CancellationToken cancellationToken = default); + + ValueTask StoreObjectAsync(object entity, CancellationToken cancellationToken = default); + + ValueTask ContainsKeyAsync(string key, CancellationToken cancellationToken = default); + ValueTask RemoveEntryAsync(string[] args, CancellationToken cancellationToken = default); + ValueTask RemoveEntryAsync(params string[] args); // convenience API + ValueTask IncrementValueAsync(string key, CancellationToken cancellationToken = default); + ValueTask IncrementValueByAsync(string key, int count, CancellationToken cancellationToken = default); + ValueTask IncrementValueByAsync(string key, long count, CancellationToken cancellationToken = default); + ValueTask IncrementValueByAsync(string key, double count, CancellationToken cancellationToken = default); + ValueTask DecrementValueAsync(string key, CancellationToken cancellationToken = default); + ValueTask DecrementValueByAsync(string key, int count, CancellationToken cancellationToken = default); + ValueTask> SearchKeysAsync(string pattern, CancellationToken cancellationToken = default); + + ValueTask TypeAsync(string key, CancellationToken cancellationToken = default); + ValueTask GetEntryTypeAsync(string key, CancellationToken cancellationToken = default); + ValueTask GetStringCountAsync(string key, CancellationToken cancellationToken = default); + ValueTask GetRandomKeyAsync(CancellationToken cancellationToken = default); + ValueTask ExpireEntryInAsync(string key, TimeSpan expireIn, CancellationToken cancellationToken = default); + ValueTask ExpireEntryAtAsync(string key, DateTime expireAt, CancellationToken cancellationToken = default); + ValueTask> GetSortedEntryValuesAsync(string key, int startingFrom, int endingAt, CancellationToken cancellationToken = default); + + //Store entities without registering entity ids + ValueTask WriteAllAsync(IEnumerable entities, CancellationToken cancellationToken = default); + + //Scan APIs + IAsyncEnumerable ScanAllKeysAsync(string pattern = null, int pageSize = 1000, CancellationToken cancellationToken = default); + IAsyncEnumerable ScanAllSetItemsAsync(string setId, string pattern = null, int pageSize = 1000, CancellationToken cancellationToken = default); + IAsyncEnumerable> ScanAllSortedSetItemsAsync(string setId, string pattern = null, int pageSize = 1000, CancellationToken cancellationToken = default); + IAsyncEnumerable> ScanAllHashEntriesAsync(string hashId, string pattern = null, int pageSize = 1000, CancellationToken cancellationToken = default); + + //Hyperlog APIs + ValueTask AddToHyperLogAsync(string key, string[] elements, CancellationToken cancellationToken = default); + ValueTask AddToHyperLogAsync(string key, params string[] elements); // convenience API + ValueTask CountHyperLogAsync(string key, CancellationToken cancellationToken = default); + ValueTask MergeHyperLogsAsync(string toKey, string[] fromKeys, CancellationToken cancellationToken = default); + ValueTask MergeHyperLogsAsync(string toKey, params string[] fromKeys); // convenience API + + //GEO APIs + ValueTask AddGeoMemberAsync(string key, double longitude, double latitude, string member, CancellationToken cancellationToken = default); + ValueTask AddGeoMembersAsync(string key, RedisGeo[] geoPoints, CancellationToken cancellationToken = default); + ValueTask AddGeoMembersAsync(string key, params RedisGeo[] geoPoints); // convenience API + ValueTask CalculateDistanceBetweenGeoMembersAsync(string key, string fromMember, string toMember, string unit = null, CancellationToken cancellationToken = default); + ValueTask GetGeohashesAsync(string key, string[] members, CancellationToken cancellationToken = default); + ValueTask GetGeohashesAsync(string key, params string[] members); // convenience API + ValueTask> GetGeoCoordinatesAsync(string key, string[] members, CancellationToken cancellationToken = default); + ValueTask> GetGeoCoordinatesAsync(string key, params string[] members); // convenience API + ValueTask FindGeoMembersInRadiusAsync(string key, double longitude, double latitude, double radius, string unit, CancellationToken cancellationToken = default); + ValueTask> FindGeoResultsInRadiusAsync(string key, double longitude, double latitude, double radius, string unit, int? count = null, bool? sortByNearest = null, CancellationToken cancellationToken = default); + ValueTask FindGeoMembersInRadiusAsync(string key, string member, double radius, string unit, CancellationToken cancellationToken = default); + ValueTask> FindGeoResultsInRadiusAsync(string key, string member, double radius, string unit, int? count = null, bool? sortByNearest = null, CancellationToken cancellationToken = default); + + /// + /// Returns a high-level typed client API + /// + /// + IRedisTypedClientAsync As(); + + ValueTask CreateTransactionAsync(CancellationToken cancellationToken = default); + IRedisPipelineAsync CreatePipeline(); + + ValueTask AcquireLockAsync(string key, TimeSpan? timeOut = default, CancellationToken cancellationToken = default); + + #region Redis pubsub + + ValueTask WatchAsync(string[] keys, CancellationToken cancellationToken = default); + ValueTask WatchAsync(params string[] keys); // convenience API + ValueTask UnWatchAsync(CancellationToken cancellationToken = default); + ValueTask CreateSubscriptionAsync(CancellationToken cancellationToken = default); + ValueTask PublishMessageAsync(string toChannel, string message, CancellationToken cancellationToken = default); + + #endregion + + + #region Set operations + + ValueTask> GetAllItemsFromSetAsync(string setId, CancellationToken cancellationToken = default); + ValueTask AddItemToSetAsync(string setId, string item, CancellationToken cancellationToken = default); + ValueTask AddRangeToSetAsync(string setId, List items, CancellationToken cancellationToken = default); + ValueTask RemoveItemFromSetAsync(string setId, string item, CancellationToken cancellationToken = default); + ValueTask PopItemFromSetAsync(string setId, CancellationToken cancellationToken = default); + ValueTask> PopItemsFromSetAsync(string setId, int count, CancellationToken cancellationToken = default); + ValueTask MoveBetweenSetsAsync(string fromSetId, string toSetId, string item, CancellationToken cancellationToken = default); + ValueTask GetSetCountAsync(string setId, CancellationToken cancellationToken = default); + ValueTask SetContainsItemAsync(string setId, string item, CancellationToken cancellationToken = default); + ValueTask> GetIntersectFromSetsAsync(string[] setIds, CancellationToken cancellationToken = default); + ValueTask> GetIntersectFromSetsAsync(params string[] setIds); // convenience API + ValueTask StoreIntersectFromSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); + ValueTask StoreIntersectFromSetsAsync(string intoSetId, params string[] setIds); // convenience API + ValueTask> GetUnionFromSetsAsync(string[] setIds, CancellationToken cancellationToken = default); + ValueTask> GetUnionFromSetsAsync(params string[] setIds); // convenience API + ValueTask StoreUnionFromSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); + ValueTask StoreUnionFromSetsAsync(string intoSetId, params string[] setIds); // convenience API + ValueTask> GetDifferencesFromSetAsync(string fromSetId, string[] withSetIds, CancellationToken cancellationToken = default); + ValueTask> GetDifferencesFromSetAsync(string fromSetId, params string[] withSetIds); // convenience API + ValueTask StoreDifferencesFromSetAsync(string intoSetId, string fromSetId, string[] withSetIds, CancellationToken cancellationToken = default); + ValueTask StoreDifferencesFromSetAsync(string intoSetId, string fromSetId, params string[] withSetIds); // convenience API + ValueTask GetRandomItemFromSetAsync(string setId, CancellationToken cancellationToken = default); + + #endregion + + + #region List operations + + ValueTask> GetAllItemsFromListAsync(string listId, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromListAsync(string listId, int startingFrom, int endingAt, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedListAsync(string listId, int startingFrom, int endingAt, CancellationToken cancellationToken = default); + ValueTask> GetSortedItemsFromListAsync(string listId, SortOptions sortOptions, CancellationToken cancellationToken = default); + ValueTask AddItemToListAsync(string listId, string value, CancellationToken cancellationToken = default); + ValueTask AddRangeToListAsync(string listId, List values, CancellationToken cancellationToken = default); + ValueTask PrependItemToListAsync(string listId, string value, CancellationToken cancellationToken = default); + ValueTask PrependRangeToListAsync(string listId, List values, CancellationToken cancellationToken = default); + + ValueTask RemoveAllFromListAsync(string listId, CancellationToken cancellationToken = default); + ValueTask RemoveStartFromListAsync(string listId, CancellationToken cancellationToken = default); + ValueTask BlockingRemoveStartFromListAsync(string listId, TimeSpan? timeOut, CancellationToken cancellationToken = default); + ValueTask BlockingRemoveStartFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken cancellationToken = default); + ValueTask RemoveEndFromListAsync(string listId, CancellationToken cancellationToken = default); + ValueTask TrimListAsync(string listId, int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken = default); + ValueTask RemoveItemFromListAsync(string listId, string value, CancellationToken cancellationToken = default); + ValueTask RemoveItemFromListAsync(string listId, string value, int noOfMatches, CancellationToken cancellationToken = default); + ValueTask GetListCountAsync(string listId, CancellationToken cancellationToken = default); + ValueTask GetItemFromListAsync(string listId, int listIndex, CancellationToken cancellationToken = default); + ValueTask SetItemInListAsync(string listId, int listIndex, string value, CancellationToken cancellationToken = default); + + //Queue operations + ValueTask EnqueueItemOnListAsync(string listId, string value, CancellationToken cancellationToken = default); + ValueTask DequeueItemFromListAsync(string listId, CancellationToken cancellationToken = default); + ValueTask BlockingDequeueItemFromListAsync(string listId, TimeSpan? timeOut, CancellationToken cancellationToken = default); + ValueTask BlockingDequeueItemFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken cancellationToken = default); + + //Stack operations + ValueTask PushItemToListAsync(string listId, string value, CancellationToken cancellationToken = default); + ValueTask PopItemFromListAsync(string listId, CancellationToken cancellationToken = default); + ValueTask BlockingPopItemFromListAsync(string listId, TimeSpan? timeOut, CancellationToken cancellationToken = default); + ValueTask BlockingPopItemFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken cancellationToken = default); + ValueTask PopAndPushItemBetweenListsAsync(string fromListId, string toListId, CancellationToken cancellationToken = default); + ValueTask BlockingPopAndPushItemBetweenListsAsync(string fromListId, string toListId, TimeSpan? timeOut, CancellationToken cancellationToken = default); + + #endregion + + + #region Sorted Set operations + + ValueTask AddItemToSortedSetAsync(string setId, string value, CancellationToken cancellationToken = default); + ValueTask AddItemToSortedSetAsync(string setId, string value, double score, CancellationToken cancellationToken = default); + ValueTask AddRangeToSortedSetAsync(string setId, List values, double score, CancellationToken cancellationToken = default); + ValueTask AddRangeToSortedSetAsync(string setId, List values, long score, CancellationToken cancellationToken = default); + ValueTask RemoveItemFromSortedSetAsync(string setId, string value, CancellationToken cancellationToken = default); + ValueTask RemoveItemsFromSortedSetAsync(string setId, List values, CancellationToken cancellationToken = default); + ValueTask PopItemWithLowestScoreFromSortedSetAsync(string setId, CancellationToken cancellationToken = default); + ValueTask PopItemWithHighestScoreFromSortedSetAsync(string setId, CancellationToken cancellationToken = default); + ValueTask SortedSetContainsItemAsync(string setId, string value, CancellationToken cancellationToken = default); + ValueTask IncrementItemInSortedSetAsync(string setId, string value, double incrementBy, CancellationToken cancellationToken = default); + ValueTask IncrementItemInSortedSetAsync(string setId, string value, long incrementBy, CancellationToken cancellationToken = default); + ValueTask GetItemIndexInSortedSetAsync(string setId, string value, CancellationToken cancellationToken = default); + ValueTask GetItemIndexInSortedSetDescAsync(string setId, string value, CancellationToken cancellationToken = default); + ValueTask> GetAllItemsFromSortedSetAsync(string setId, CancellationToken cancellationToken = default); + ValueTask> GetAllItemsFromSortedSetDescAsync(string setId, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetDescAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken = default); + ValueTask> GetAllWithScoresFromSortedSetAsync(string setId, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetDescAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask RemoveRangeFromSortedSetAsync(string setId, int minRank, int maxRank, CancellationToken cancellationToken = default); + ValueTask RemoveRangeFromSortedSetByScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask RemoveRangeFromSortedSetByScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); + ValueTask GetSortedSetCountAsync(string setId, CancellationToken cancellationToken = default); + ValueTask GetSortedSetCountAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); + ValueTask GetSortedSetCountAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); + ValueTask GetSortedSetCountAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask GetItemScoreInSortedSetAsync(string setId, string value, CancellationToken cancellationToken = default); + ValueTask StoreIntersectFromSortedSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); + ValueTask StoreIntersectFromSortedSetsAsync(string intoSetId, params string[] setIds); // convenience API + ValueTask StoreIntersectFromSortedSetsAsync(string intoSetId, string[] setIds, string[] args, CancellationToken cancellationToken = default); + ValueTask StoreUnionFromSortedSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); + ValueTask StoreUnionFromSortedSetsAsync(string intoSetId, params string[] setIds); // convenience API + ValueTask StoreUnionFromSortedSetsAsync(string intoSetId, string[] setIds, string[] args, CancellationToken cancellationToken = default); + ValueTask> SearchSortedSetAsync(string setId, string start = null, string end = null, int? skip = null, int? take = null, CancellationToken cancellationToken = default); + ValueTask SearchSortedSetCountAsync(string setId, string start = null, string end = null, CancellationToken cancellationToken = default); + ValueTask RemoveRangeFromSortedSetBySearchAsync(string setId, string start = null, string end = null, CancellationToken cancellationToken = default); + + #endregion + + + #region Hash operations + + ValueTask HashContainsEntryAsync(string hashId, string key, CancellationToken cancellationToken = default); + ValueTask SetEntryInHashAsync(string hashId, string key, string value, CancellationToken cancellationToken = default); + ValueTask SetEntryInHashIfNotExistsAsync(string hashId, string key, string value, CancellationToken cancellationToken = default); + ValueTask SetRangeInHashAsync(string hashId, IEnumerable> keyValuePairs, CancellationToken cancellationToken = default); + ValueTask IncrementValueInHashAsync(string hashId, string key, int incrementBy, CancellationToken cancellationToken = default); + ValueTask IncrementValueInHashAsync(string hashId, string key, double incrementBy, CancellationToken cancellationToken = default); + ValueTask GetValueFromHashAsync(string hashId, string key, CancellationToken cancellationToken = default); + ValueTask> GetValuesFromHashAsync(string hashId, string[] keys, CancellationToken cancellationToken = default); + ValueTask> GetValuesFromHashAsync(string hashId, params string[] keys); // convenience API + ValueTask RemoveEntryFromHashAsync(string hashId, string key, CancellationToken cancellationToken = default); + ValueTask GetHashCountAsync(string hashId, CancellationToken cancellationToken = default); + ValueTask> GetHashKeysAsync(string hashId, CancellationToken cancellationToken = default); + ValueTask> GetHashValuesAsync(string hashId, CancellationToken cancellationToken = default); + ValueTask> GetAllEntriesFromHashAsync(string hashId, CancellationToken cancellationToken = default); + + #endregion + + + #region Eval/Lua operations + + ValueTask ExecCachedLuaAsync(string scriptBody, Func> scriptSha1, CancellationToken cancellationToken = default); + + ValueTask ExecLuaAsync(string body, string[] args, CancellationToken cancellationToken = default); + ValueTask ExecLuaAsync(string body, params string[] args); // conveinence API + ValueTask ExecLuaAsync(string luaBody, string[] keys, string[] args, CancellationToken cancellationToken = default); + ValueTask ExecLuaShaAsync(string sha1, string[] args, CancellationToken cancellationToken = default); + ValueTask ExecLuaShaAsync(string sha1, params string[] args); // convenience API + ValueTask ExecLuaShaAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken = default); + + ValueTask ExecLuaAsStringAsync(string luaBody, string[] args, CancellationToken cancellationToken = default); + ValueTask ExecLuaAsStringAsync(string luaBody, params string[] args); // convenience API + ValueTask ExecLuaAsStringAsync(string luaBody, string[] keys, string[] args, CancellationToken cancellationToken = default); + ValueTask ExecLuaShaAsStringAsync(string sha1, string[] args, CancellationToken cancellationToken = default); + ValueTask ExecLuaShaAsStringAsync(string sha1, params string[] args); // convenience API + ValueTask ExecLuaShaAsStringAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken = default); + + ValueTask ExecLuaAsIntAsync(string luaBody, string[] args, CancellationToken cancellationToken = default); + ValueTask ExecLuaAsIntAsync(string luaBody, params string[] args); // convenience API + ValueTask ExecLuaAsIntAsync(string luaBody, string[] keys, string[] args, CancellationToken cancellationToken = default); + ValueTask ExecLuaShaAsIntAsync(string sha1, string[] args, CancellationToken cancellationToken = default); + ValueTask ExecLuaShaAsIntAsync(string sha1, params string[] args); // convenience API + ValueTask ExecLuaShaAsIntAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken = default); + + ValueTask> ExecLuaAsListAsync(string luaBody, string[] args, CancellationToken cancellationToken = default); + ValueTask> ExecLuaAsListAsync(string luaBody, params string[] args); // convenience API + ValueTask> ExecLuaAsListAsync(string luaBody, string[] keys, string[] args, CancellationToken cancellationToken = default); + ValueTask> ExecLuaShaAsListAsync(string sha1, string[] args, CancellationToken cancellationToken = default); + ValueTask> ExecLuaShaAsListAsync(string sha1, params string[] args); // convenience API + ValueTask> ExecLuaShaAsListAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken = default); + + ValueTask CalculateSha1Async(string luaBody, CancellationToken cancellationToken = default); + + ValueTask HasLuaScriptAsync(string sha1Ref, CancellationToken cancellationToken = default); + ValueTask> WhichLuaScriptsExistsAsync(string[] sha1Refs, CancellationToken cancellationToken = default); + ValueTask> WhichLuaScriptsExistsAsync(params string[] sha1Refs); // convenience API + ValueTask RemoveAllLuaScriptsAsync(CancellationToken cancellationToken = default); + ValueTask KillRunningLuaScriptAsync(CancellationToken cancellationToken = default); + ValueTask LoadLuaScriptAsync(string body, CancellationToken cancellationToken = default); + + #endregion + + ValueTask SlowlogResetAsync(CancellationToken cancellationToken = default); + ValueTask GetSlowlogAsync(int? numberOfRecords = null, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisClientsManagerAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisClientsManagerAsync.cs new file mode 100644 index 00000000..f731af18 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisClientsManagerAsync.cs @@ -0,0 +1,46 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Caching; + +namespace ServiceStack.Redis +{ + public interface IRedisClientsManagerAsync : IAsyncDisposable + { + /// + /// Returns a Read/Write client (The default) using the hosts defined in ReadWriteHosts + /// + /// + ValueTask GetClientAsync(CancellationToken cancellationToken = default); + + /// + /// Returns a ReadOnly client using the hosts defined in ReadOnlyHosts. + /// + /// + ValueTask GetReadOnlyClientAsync(CancellationToken cancellationToken = default); + + /// + /// Returns a Read/Write ICacheClient (The default) using the hosts defined in ReadWriteHosts + /// + /// + ValueTask GetCacheClientAsync(CancellationToken cancellationToken = default); + + /// + /// Returns a ReadOnly ICacheClient using the hosts defined in ReadOnlyHosts. + /// + /// + ValueTask GetReadOnlyCacheClientAsync(CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.Generic.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.Generic.cs new file mode 100644 index 00000000..3e192f94 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.Generic.cs @@ -0,0 +1,32 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Model; + +namespace ServiceStack.Redis.Generic +{ + public interface IRedisHashAsync : IAsyncEnumerable>, IHasStringId + { + ValueTask> GetAllAsync(CancellationToken cancellationToken = default); + + ValueTask CountAsync(CancellationToken cancellationToken = default); + ValueTask AddAsync(KeyValuePair item, CancellationToken cancellationToken = default); + ValueTask AddAsync(TKey key, TValue value, CancellationToken cancellationToken = default); + ValueTask ClearAsync(CancellationToken cancellationToken = default); + ValueTask ContainsKeyAsync(TKey key, CancellationToken cancellationToken = default); + ValueTask RemoveAsync(TKey key, CancellationToken cancellationToken = default); + } + +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.cs new file mode 100644 index 00000000..3ff0e9e4 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Model; + +namespace ServiceStack.Redis +{ + public interface IRedisHashAsync + : IAsyncEnumerable>, IHasStringId + { + ValueTask AddIfNotExistsAsync(KeyValuePair item, CancellationToken cancellationToken = default); + ValueTask AddRangeAsync(IEnumerable> items, CancellationToken cancellationToken = default); + ValueTask IncrementValueAsync(string key, int incrementBy, CancellationToken cancellationToken = default); + + // shim the basic ICollection etc APIs + ValueTask CountAsync(CancellationToken cancellationToken = default); + ValueTask AddAsync(KeyValuePair item, CancellationToken cancellationToken = default); + ValueTask AddAsync(string key, string value, CancellationToken cancellationToken = default); + ValueTask ClearAsync(CancellationToken cancellationToken = default); + ValueTask ContainsKeyAsync(string key, CancellationToken cancellationToken = default); + ValueTask RemoveAsync(string key, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.Generic.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.Generic.cs new file mode 100644 index 00000000..1234b924 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.Generic.cs @@ -0,0 +1,64 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot Async(demis.bellot@gmail.com, CancellationToken cancellationToken = default) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Model; + +namespace ServiceStack.Redis.Generic +{ + /// + /// Wrap the common redis list operations under a IList[string] interface. + /// + + public interface IRedisListAsync + : IAsyncEnumerable, IHasStringId + { + ValueTask CountAsync(CancellationToken cancellationToken = default); + ValueTask> GetAllAsync(CancellationToken cancellationToken = default); + ValueTask> GetRangeAsync(int startingFrom, int endingAt, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedListAsync(int startingFrom, int endingAt, CancellationToken cancellationToken = default); + ValueTask RemoveAllAsync(CancellationToken cancellationToken = default); + ValueTask TrimAsync(int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken = default); + ValueTask RemoveValueAsync(T value, CancellationToken cancellationToken = default); + ValueTask RemoveValueAsync(T value, int noOfMatches, CancellationToken cancellationToken = default); + + ValueTask AddRangeAsync(IEnumerable values, CancellationToken cancellationToken = default); + ValueTask AppendAsync(T value, CancellationToken cancellationToken = default); + ValueTask PrependAsync(T value, CancellationToken cancellationToken = default); + ValueTask RemoveStartAsync(CancellationToken cancellationToken = default); + ValueTask BlockingRemoveStartAsync(TimeSpan? timeOut, CancellationToken cancellationToken = default); + ValueTask RemoveEndAsync(CancellationToken cancellationToken = default); + + ValueTask EnqueueAsync(T value, CancellationToken cancellationToken = default); + ValueTask DequeueAsync(CancellationToken cancellationToken = default); + ValueTask BlockingDequeueAsync(TimeSpan? timeOut, CancellationToken cancellationToken = default); + + ValueTask PushAsync(T value, CancellationToken cancellationToken = default); + ValueTask PopAsync(CancellationToken cancellationToken = default); + ValueTask BlockingPopAsync(TimeSpan? timeOut, CancellationToken cancellationToken = default); + ValueTask PopAndPushAsync(IRedisListAsync toList, CancellationToken cancellationToken = default); + + + ValueTask RemoveAsync(T item, CancellationToken cancellationToken = default); + ValueTask AddAsync(T item, CancellationToken cancellationToken = default); + ValueTask RemoveAtAsync(int index, CancellationToken cancellationToken = default); + ValueTask ContainsAsync(T item, CancellationToken cancellationToken = default); + ValueTask ClearAsync(CancellationToken cancellationToken = default); + ValueTask IndexOfAsync(T item, CancellationToken cancellationToken = default); + + ValueTask ElementAtAsync(int index, CancellationToken cancellationToken = default); + ValueTask SetValueAsync(int index, T value, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.cs new file mode 100644 index 00000000..9a9f98fc --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.cs @@ -0,0 +1,58 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot Async(demis.bellot@gmail.com) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Model; + +namespace ServiceStack.Redis +{ + public interface IRedisListAsync + : IAsyncEnumerable, IHasStringId + { + ValueTask CountAsync(CancellationToken cancellationToken = default); + ValueTask> GetAllAsync(CancellationToken cancellationToken = default); + ValueTask> GetRangeAsync(int startingFrom, int endingAt, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedListAsync(int startingFrom, int endingAt, CancellationToken cancellationToken = default); + ValueTask RemoveAllAsync(CancellationToken cancellationToken = default); + ValueTask TrimAsync(int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken = default); + ValueTask RemoveValueAsync(string value, CancellationToken cancellationToken = default); + ValueTask RemoveValueAsync(string value, int noOfMatches, CancellationToken cancellationToken = default); + + ValueTask PrependAsync(string value, CancellationToken cancellationToken = default); + ValueTask AppendAsync(string value, CancellationToken cancellationToken = default); + ValueTask RemoveStartAsync(CancellationToken cancellationToken = default); + ValueTask BlockingRemoveStartAsync(TimeSpan? timeOut, CancellationToken cancellationToken = default); + ValueTask RemoveEndAsync(CancellationToken cancellationToken = default); + + ValueTask EnqueueAsync(string value, CancellationToken cancellationToken = default); + ValueTask DequeueAsync(CancellationToken cancellationToken = default); + ValueTask BlockingDequeueAsync(TimeSpan? timeOut, CancellationToken cancellationToken = default); + + ValueTask PushAsync(string value, CancellationToken cancellationToken = default); + ValueTask PopAsync(CancellationToken cancellationToken = default); + ValueTask BlockingPopAsync(TimeSpan? timeOut, CancellationToken cancellationToken = default); + ValueTask PopAndPushAsync(IRedisListAsync toList, CancellationToken cancellationToken = default); + + ValueTask RemoveAsync(string item, CancellationToken cancellationToken = default); + ValueTask AddAsync(string item, CancellationToken cancellationToken = default); + ValueTask RemoveAtAsync(int index, CancellationToken cancellationToken = default); + ValueTask ContainsAsync(string item, CancellationToken cancellationToken = default); + ValueTask ClearAsync(CancellationToken cancellationToken = default); + ValueTask IndexOfAsync(string item, CancellationToken cancellationToken = default); + + ValueTask ElementAtAsync(int index, CancellationToken cancellationToken = default); + ValueTask SetValueAsync(int index, string value, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisNativeClientAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisNativeClientAsync.cs new file mode 100644 index 00000000..5c26b511 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisNativeClientAsync.cs @@ -0,0 +1,297 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis/ +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + public interface IRedisNativeClientAsync : IAsyncDisposable + { + /* + non-obvious changes: + - Db is get only; addition of SelectAsync + - LastSave is now a method + - shutdown now takes nosave arg + - expose the optional args on Set + - add SlowlogGet and SlowlogReset + - add ZCount + */ + + //Redis utility operations + ValueTask> InfoAsync(CancellationToken cancellationToken = default); + long Db { get; } + ValueTask SelectAsync(long db, CancellationToken cancellationToken = default); + + ValueTask DbSizeAsync(CancellationToken cancellationToken = default); + ValueTask LastSaveAsync(CancellationToken cancellationToken = default); + ValueTask SaveAsync(CancellationToken cancellationToken = default); + ValueTask BgSaveAsync(CancellationToken cancellationToken = default); + ValueTask ShutdownAsync(bool noSave = false, CancellationToken cancellationToken = default); + ValueTask BgRewriteAofAsync(CancellationToken cancellationToken = default); + ValueTask QuitAsync(CancellationToken cancellationToken = default); + ValueTask FlushDbAsync(CancellationToken cancellationToken = default); + ValueTask FlushAllAsync(CancellationToken cancellationToken = default); + ValueTask PingAsync(CancellationToken cancellationToken = default); + ValueTask EchoAsync(string text, CancellationToken cancellationToken = default); + ValueTask SlaveOfAsync(string hostname, int port, CancellationToken cancellationToken = default); + ValueTask SlaveOfNoOneAsync(CancellationToken cancellationToken = default); + ValueTask ConfigGetAsync(string pattern, CancellationToken cancellationToken = default); + ValueTask ConfigSetAsync(string item, byte[] value, CancellationToken cancellationToken = default); + ValueTask ConfigResetStatAsync(CancellationToken cancellationToken = default); + ValueTask ConfigRewriteAsync(CancellationToken cancellationToken = default); + ValueTask TimeAsync(CancellationToken cancellationToken = default); + ValueTask DebugSegfaultAsync(CancellationToken cancellationToken = default); + ValueTask DumpAsync(string key, CancellationToken cancellationToken = default); + ValueTask RestoreAsync(string key, long expireMs, byte[] dumpValue, CancellationToken cancellationToken = default); + ValueTask MigrateAsync(string host, int port, string key, int destinationDb, long timeoutMs, CancellationToken cancellationToken = default); + ValueTask MoveAsync(string key, int db, CancellationToken cancellationToken = default); + ValueTask ObjectIdleTimeAsync(string key, CancellationToken cancellationToken = default); + ValueTask RoleAsync(CancellationToken cancellationToken = default); + + ValueTask RawCommandAsync(object[] cmdWithArgs, CancellationToken cancellationToken = default); + ValueTask RawCommandAsync(params object[] cmdWithArgs); // convenience API + ValueTask RawCommandAsync(byte[][] cmdWithBinaryArgs, CancellationToken cancellationToken = default); + ValueTask RawCommandAsync(params byte[][] cmdWithBinaryArgs); // convenience API + + ValueTask ClientGetNameAsync(CancellationToken cancellationToken = default); + ValueTask ClientSetNameAsync(string client, CancellationToken cancellationToken = default); + ValueTask ClientKillAsync(string host, CancellationToken cancellationToken = default); + ValueTask ClientKillAsync(string addr = null, string id = null, string type = null, string skipMe = null, CancellationToken cancellationToken = default); + ValueTask ClientListAsync(CancellationToken cancellationToken = default); + ValueTask ClientPauseAsync(int timeOutMs, CancellationToken cancellationToken = default); + + //Common key-value Redis operations + ValueTask KeysAsync(string pattern, CancellationToken cancellationToken = default); + ValueTask TypeAsync(string key, CancellationToken cancellationToken = default); + ValueTask ExistsAsync(string key, CancellationToken cancellationToken = default); + ValueTask StrLenAsync(string key, CancellationToken cancellationToken = default); + ValueTask SetAsync(string key, byte[] value, bool exists, long expirySeconds = 0, long expiryMilliseconds = 0, CancellationToken cancellationToken = default); + ValueTask SetAsync(string key, byte[] value, long expirySeconds = 0, long expiryMilliseconds = 0, CancellationToken cancellationToken = default); + ValueTask SetExAsync(string key, int expireInSeconds, byte[] value, CancellationToken cancellationToken = default); + ValueTask PersistAsync(string key, CancellationToken cancellationToken = default); + ValueTask PSetExAsync(string key, long expireInMs, byte[] value, CancellationToken cancellationToken = default); + ValueTask SetNXAsync(string key, byte[] value, CancellationToken cancellationToken = default); + ValueTask MSetAsync(byte[][] keys, byte[][] values, CancellationToken cancellationToken = default); + ValueTask MSetAsync(string[] keys, byte[][] values, CancellationToken cancellationToken = default); + ValueTask MSetNxAsync(byte[][] keys, byte[][] values, CancellationToken cancellationToken = default); + ValueTask MSetNxAsync(string[] keys, byte[][] values, CancellationToken cancellationToken = default); + ValueTask GetAsync(string key, CancellationToken cancellationToken = default); + ValueTask GetSetAsync(string key, byte[] value, CancellationToken cancellationToken = default); + ValueTask MGetAsync(byte[][] keysAndArgs, CancellationToken cancellationToken = default); + ValueTask MGetAsync(params byte[][] keysAndArgs); // convenience API + ValueTask MGetAsync(string[] keys, CancellationToken cancellationToken = default); + ValueTask MGetAsync(params string[] keys); // convenience API + ValueTask DelAsync(string key, CancellationToken cancellationToken = default); + ValueTask DelAsync(string[] keys, CancellationToken cancellationToken = default); + ValueTask DelAsync(params string[] keys); // convenience API + ValueTask IncrAsync(string key, CancellationToken cancellationToken = default); + ValueTask IncrByAsync(string key, long incrBy, CancellationToken cancellationToken = default); + ValueTask IncrByFloatAsync(string key, double incrBy, CancellationToken cancellationToken = default); + ValueTask DecrAsync(string key, CancellationToken cancellationToken = default); + ValueTask DecrByAsync(string key, long decrBy, CancellationToken cancellationToken = default); + ValueTask AppendAsync(string key, byte[] value, CancellationToken cancellationToken = default); + ValueTask GetRangeAsync(string key, int fromIndex, int toIndex, CancellationToken cancellationToken = default); + ValueTask SetRangeAsync(string key, int offset, byte[] value, CancellationToken cancellationToken = default); + ValueTask GetBitAsync(string key, int offset, CancellationToken cancellationToken = default); + ValueTask SetBitAsync(string key, int offset, int value, CancellationToken cancellationToken = default); + ValueTask BitCountAsync(string key, CancellationToken cancellationToken = default); + ValueTask RandomKeyAsync(CancellationToken cancellationToken = default); + ValueTask RenameAsync(string oldKeyname, string newKeyname, CancellationToken cancellationToken = default); + ValueTask RenameNxAsync(string oldKeyname, string newKeyname, CancellationToken cancellationToken = default); + ValueTask ExpireAsync(string key, int seconds, CancellationToken cancellationToken = default); + ValueTask PExpireAsync(string key, long ttlMs, CancellationToken cancellationToken = default); + ValueTask ExpireAtAsync(string key, long unixTime, CancellationToken cancellationToken = default); + ValueTask PExpireAtAsync(string key, long unixTimeMs, CancellationToken cancellationToken = default); + ValueTask TtlAsync(string key, CancellationToken cancellationToken = default); + ValueTask PTtlAsync(string key, CancellationToken cancellationToken = default); + + //Scan APIs + ValueTask ScanAsync(ulong cursor, int count = 10, string match = null, CancellationToken cancellationToken = default); + ValueTask SScanAsync(string setId, ulong cursor, int count = 10, string match = null, CancellationToken cancellationToken = default); + ValueTask ZScanAsync(string setId, ulong cursor, int count = 10, string match = null, CancellationToken cancellationToken = default); + ValueTask HScanAsync(string hashId, ulong cursor, int count = 10, string match = null, CancellationToken cancellationToken = default); + + //Hyperlog + ValueTask PfAddAsync(string key, byte[][] elements, CancellationToken cancellationToken = default); + ValueTask PfAddAsync(string key, params byte[][] elements); // convenience API + ValueTask PfCountAsync(string key, CancellationToken cancellationToken = default); + ValueTask PfMergeAsync(string toKeyId, string[] fromKeys, CancellationToken cancellationToken = default); + ValueTask PfMergeAsync(string toKeyId, params string[] fromKeys); + + //Redis Sort operation Async(works on lists, sets or hashes) + ValueTask SortAsync(string listOrSetId, SortOptions sortOptions, CancellationToken cancellationToken = default); + + //Redis List operations + ValueTask LRangeAsync(string listId, int startingFrom, int endingAt, CancellationToken cancellationToken = default); + ValueTask RPushAsync(string listId, byte[] value, CancellationToken cancellationToken = default); + ValueTask RPushXAsync(string listId, byte[] value, CancellationToken cancellationToken = default); + ValueTask LPushAsync(string listId, byte[] value, CancellationToken cancellationToken = default); + ValueTask LPushXAsync(string listId, byte[] value, CancellationToken cancellationToken = default); + ValueTask LTrimAsync(string listId, int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken = default); + ValueTask LRemAsync(string listId, int removeNoOfMatches, byte[] value, CancellationToken cancellationToken = default); + ValueTask LLenAsync(string listId, CancellationToken cancellationToken = default); + ValueTask LIndexAsync(string listId, int listIndex, CancellationToken cancellationToken = default); + ValueTask LInsertAsync(string listId, bool insertBefore, byte[] pivot, byte[] value, CancellationToken cancellationToken = default); + ValueTask LSetAsync(string listId, int listIndex, byte[] value, CancellationToken cancellationToken = default); + ValueTask LPopAsync(string listId, CancellationToken cancellationToken = default); + ValueTask RPopAsync(string listId, CancellationToken cancellationToken = default); + ValueTask BLPopAsync(string listId, int timeOutSecs, CancellationToken cancellationToken = default); + ValueTask BLPopAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken = default); + ValueTask BLPopValueAsync(string listId, int timeOutSecs, CancellationToken cancellationToken = default); + ValueTask BLPopValueAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken = default); + ValueTask BRPopAsync(string listId, int timeOutSecs, CancellationToken cancellationToken = default); + ValueTask BRPopAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken = default); + ValueTask RPopLPushAsync(string fromListId, string toListId, CancellationToken cancellationToken = default); + ValueTask BRPopValueAsync(string listId, int timeOutSecs, CancellationToken cancellationToken = default); + ValueTask BRPopValueAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken = default); + ValueTask BRPopLPushAsync(string fromListId, string toListId, int timeOutSecs, CancellationToken cancellationToken = default); + + //Redis Set operations + ValueTask SMembersAsync(string setId, CancellationToken cancellationToken = default); + ValueTask SAddAsync(string setId, byte[] value, CancellationToken cancellationToken = default); + ValueTask SAddAsync(string setId, byte[][] value, CancellationToken cancellationToken = default); + ValueTask SRemAsync(string setId, byte[] value, CancellationToken cancellationToken = default); + ValueTask SPopAsync(string setId, CancellationToken cancellationToken = default); + ValueTask SPopAsync(string setId, int count, CancellationToken cancellationToken = default); + ValueTask SMoveAsync(string fromSetId, string toSetId, byte[] value, CancellationToken cancellationToken = default); + ValueTask SCardAsync(string setId, CancellationToken cancellationToken = default); + ValueTask SIsMemberAsync(string setId, byte[] value, CancellationToken cancellationToken = default); + ValueTask SInterAsync(string[] setIds, CancellationToken cancellationToken = default); + ValueTask SInterAsync(params string[] setIds); // convenience API + ValueTask SInterStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); + ValueTask SInterStoreAsync(string intoSetId, params string[] setIds); // convenience API + ValueTask SUnionAsync(string[] setIds, CancellationToken cancellationToken = default); + ValueTask SUnionAsync(params string[] setIds); // convenience API + ValueTask SUnionStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); + ValueTask SUnionStoreAsync(string intoSetId, params string[] setIds); // convenience API + ValueTask SDiffAsync(string fromSetId, string[] withSetIds, CancellationToken cancellationToken = default); + ValueTask SDiffAsync(string fromSetId, params string[] withSetIds); // convenience API + ValueTask SDiffStoreAsync(string intoSetId, string fromSetId, string[] withSetIds, CancellationToken cancellationToken = default); + ValueTask SDiffStoreAsync(string intoSetId, string fromSetId, params string[] withSetIds); // convenience API + ValueTask SRandMemberAsync(string setId, CancellationToken cancellationToken = default); + + + ////Redis Sorted Set operations + ValueTask ZAddAsync(string setId, double score, byte[] value, CancellationToken cancellationToken = default); + ValueTask ZAddAsync(string setId, long score, byte[] value, CancellationToken cancellationToken = default); + ValueTask ZRemAsync(string setId, byte[] value, CancellationToken cancellationToken = default); + ValueTask ZRemAsync(string setId, byte[][] values, CancellationToken cancellationToken = default); + ValueTask ZIncrByAsync(string setId, double incrBy, byte[] value, CancellationToken cancellationToken = default); + ValueTask ZIncrByAsync(string setId, long incrBy, byte[] value, CancellationToken cancellationToken = default); + ValueTask ZRankAsync(string setId, byte[] value, CancellationToken cancellationToken = default); + ValueTask ZRevRankAsync(string setId, byte[] value, CancellationToken cancellationToken = default); + ValueTask ZRangeAsync(string setId, int min, int max, CancellationToken cancellationToken = default); + ValueTask ZRangeWithScoresAsync(string setId, int min, int max, CancellationToken cancellationToken = default); + ValueTask ZRevRangeAsync(string setId, int min, int max, CancellationToken cancellationToken = default); + ValueTask ZRevRangeWithScoresAsync(string setId, int min, int max, CancellationToken cancellationToken = default); + ValueTask ZRangeByScoreAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask ZRangeByScoreAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask ZRangeByScoreWithScoresAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask ZRangeByScoreWithScoresAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask ZRevRangeByScoreAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask ZRevRangeByScoreAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask ZRevRangeByScoreWithScoresAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask ZRevRangeByScoreWithScoresAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask ZRemRangeByRankAsync(string setId, int min, int max, CancellationToken cancellationToken = default); + ValueTask ZRemRangeByScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask ZRemRangeByScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); + ValueTask ZCardAsync(string setId, CancellationToken cancellationToken = default); + ValueTask ZCountAsync(string setId, double min, double max, CancellationToken cancellationToken = default); + ValueTask ZScoreAsync(string setId, byte[] value, CancellationToken cancellationToken = default); + ValueTask ZUnionStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); + ValueTask ZUnionStoreAsync(string intoSetId, params string[] setIds); // convenience API + ValueTask ZInterStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); + ValueTask ZInterStoreAsync(string intoSetId, params string[] setIds); // convenience API + ValueTask ZRangeByLexAsync(string setId, string min, string max, int? skip = null, int? take = null, CancellationToken cancellationToken = default); + ValueTask ZLexCountAsync(string setId, string min, string max, CancellationToken cancellationToken = default); + ValueTask ZRemRangeByLexAsync(string setId, string min, string max, CancellationToken cancellationToken = default); + + ////Redis Hash operations + ValueTask HSetAsync(string hashId, byte[] key, byte[] value, CancellationToken cancellationToken = default); + ValueTask HMSetAsync(string hashId, byte[][] keys, byte[][] values, CancellationToken cancellationToken = default); + ValueTask HSetNXAsync(string hashId, byte[] key, byte[] value, CancellationToken cancellationToken = default); + ValueTask HIncrbyAsync(string hashId, byte[] key, int incrementBy, CancellationToken cancellationToken = default); + ValueTask HIncrbyFloatAsync(string hashId, byte[] key, double incrementBy, CancellationToken cancellationToken = default); + ValueTask HGetAsync(string hashId, byte[] key, CancellationToken cancellationToken = default); + ValueTask HMGetAsync(string hashId, byte[][] keysAndArgs, CancellationToken cancellationToken = default); + ValueTask HMGetAsync(string hashId, params byte[][] keysAndArgs); // convenience API + ValueTask HDelAsync(string hashId, byte[] key, CancellationToken cancellationToken = default); + ValueTask HExistsAsync(string hashId, byte[] key, CancellationToken cancellationToken = default); + ValueTask HLenAsync(string hashId, CancellationToken cancellationToken = default); + ValueTask HKeysAsync(string hashId, CancellationToken cancellationToken = default); + ValueTask HValsAsync(string hashId, CancellationToken cancellationToken = default); + ValueTask HGetAllAsync(string hashId, CancellationToken cancellationToken = default); + + //Redis GEO operations + ValueTask GeoAddAsync(string key, double longitude, double latitude, string member, CancellationToken cancellationToken = default); + ValueTask GeoAddAsync(string key, RedisGeo[] geoPoints, CancellationToken cancellationToken = default); + ValueTask GeoAddAsync(string key, params RedisGeo[] geoPoints); // convenience API + ValueTask GeoDistAsync(string key, string fromMember, string toMember, string unit = null, CancellationToken cancellationToken = default); + ValueTask GeoHashAsync(string key, string[] members, CancellationToken cancellationToken = default); + ValueTask GeoHashAsync(string key, params string[] members); // convenience API + ValueTask> GeoPosAsync(string key, string[] members, CancellationToken cancellationToken = default); + ValueTask> GeoPosAsync(string key, params string[] members); // convenience API + ValueTask> GeoRadiusAsync(string key, double longitude, double latitude, double radius, string unit, + bool withCoords = false, bool withDist = false, bool withHash = false, int? count = null, bool? asc = null, CancellationToken cancellationToken = default); + ValueTask> GeoRadiusByMemberAsync(string key, string member, double radius, string unit, + bool withCoords = false, bool withDist = false, bool withHash = false, int? count = null, bool? asc = null, CancellationToken cancellationToken = default); + + //Redis Pub/Sub operations + ValueTask WatchAsync(string[] keys, CancellationToken cancellationToken = default); + ValueTask WatchAsync(params string[] keys); // convenience API + ValueTask UnWatchAsync(CancellationToken cancellationToken = default); + ValueTask PublishAsync(string toChannel, byte[] message, CancellationToken cancellationToken = default); + ValueTask SubscribeAsync(string[] toChannels, CancellationToken cancellationToken = default); + ValueTask SubscribeAsync(params string[] toChannels); // convenience API + ValueTask UnSubscribeAsync(string[] toChannels, CancellationToken cancellationToken = default); + ValueTask UnSubscribeAsync(params string[] toChannels); // convenience API + ValueTask PSubscribeAsync(string[] toChannelsMatchingPatterns, CancellationToken cancellationToken = default); + ValueTask PSubscribeAsync(params string[] toChannelsMatchingPatterns); // convenience API + ValueTask PUnSubscribeAsync(string[] toChannelsMatchingPatterns, CancellationToken cancellationToken = default); + ValueTask PUnSubscribeAsync(params string[] toChannelsMatchingPatterns); // convenience API + ValueTask ReceiveMessagesAsync(CancellationToken cancellationToken = default); + ValueTask CreateSubscriptionAsync(CancellationToken cancellationToken = default); + + //Redis LUA support + ValueTask EvalCommandAsync(string luaBody, int numberKeysInArgs, byte[][] keys, CancellationToken cancellationToken = default); + ValueTask EvalCommandAsync(string luaBody, int numberKeysInArgs, params byte[][] keys); // convenience API + ValueTask EvalShaCommandAsync(string sha1, int numberKeysInArgs, byte[][] keys, CancellationToken cancellationToken = default); + ValueTask EvalShaCommandAsync(string sha1, int numberKeysInArgs, params byte[][] keys); // convenience API + + ValueTask EvalAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken = default); + ValueTask EvalAsync(string luaBody, int numberOfKeys, params byte[][] keysAndArgs); // convenience API + ValueTask EvalShaAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken = default); + ValueTask EvalShaAsync(string sha1, int numberOfKeys, params byte[][] keysAndArgs); // convenience API + + ValueTask EvalIntAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken = default); + ValueTask EvalIntAsync(string luaBody, int numberOfKeys, params byte[][] keysAndArgs); // convenience API + ValueTask EvalShaIntAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken = default); + ValueTask EvalShaIntAsync(string sha1, int numberOfKeys, params byte[][] keysAndArgs); // convenience API + ValueTask EvalStrAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken = default); + ValueTask EvalStrAsync(string luaBody, int numberOfKeys, params byte[][] keysAndArgs); // convenience API + ValueTask EvalShaStrAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken = default); + ValueTask EvalShaStrAsync(string sha1, int numberOfKeys, params byte[][] keysAndArgs); // convenience API + + ValueTask CalculateSha1Async(string luaBody, CancellationToken cancellationToken = default); + ValueTask ScriptExistsAsync(byte[][] sha1Refs, CancellationToken cancellationToken = default); + ValueTask ScriptExistsAsync(params byte[][] sha1Refs); // convenience API + ValueTask ScriptFlushAsync(CancellationToken cancellationToken = default); + ValueTask ScriptKillAsync(CancellationToken cancellationToken = default); + ValueTask ScriptLoadAsync(string body, CancellationToken cancellationToken = default); + + ValueTask SlowlogResetAsync(CancellationToken cancellationToken = default); + ValueTask SlowlogGetAsync(int? top = null, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineAsync.cs new file mode 100644 index 00000000..b703c4f0 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineAsync.cs @@ -0,0 +1,9 @@ +namespace ServiceStack.Redis.Pipeline +{ + /// + /// Interface to redis pipeline + /// + public interface IRedisPipelineAsync : IRedisPipelineSharedAsync, IRedisQueueableOperationAsync + { + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineSharedAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineSharedAsync.cs new file mode 100644 index 00000000..9e3e2292 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineSharedAsync.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Pipeline +{ + /// + /// Pipeline interface shared by typed and non-typed pipelines + /// + public interface IRedisPipelineSharedAsync : IAsyncDisposable, IRedisQueueCompletableOperationAsync + { + ValueTask FlushAsync(CancellationToken cancellationToken = default); + ValueTask ReplayAsync(CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueCompletableOperationAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueCompletableOperationAsync.cs new file mode 100644 index 00000000..f99bfb7d --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueCompletableOperationAsync.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Pipeline +{ + /// + /// Interface to operations that allow queued commands to be completed + /// + public interface IRedisQueueCompletableOperationAsync + { + void CompleteVoidQueuedCommandAsync(Func voidReadCommand); + void CompleteIntQueuedCommandAsync(Func> intReadCommand); + void CompleteLongQueuedCommandAsync(Func> longReadCommand); + void CompleteBytesQueuedCommandAsync(Func> bytesReadCommand); + void CompleteMultiBytesQueuedCommandAsync(Func> multiBytesReadCommand); + void CompleteStringQueuedCommandAsync(Func> stringReadCommand); + void CompleteMultiStringQueuedCommandAsync(Func>> multiStringReadCommand); + void CompleteDoubleQueuedCommandAsync(Func> doubleReadCommand); + void CompleteRedisDataQueuedCommandAsync(Func> redisDataReadCommand); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueableOperationAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueableOperationAsync.cs new file mode 100644 index 00000000..ec862703 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueableOperationAsync.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Pipeline +{ + /// + /// interface to operation that can queue commands + /// + public interface IRedisQueueableOperationAsync + { + void QueueCommand(Func command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func>> command, Action> onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func>> command, Action> onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func>> command, Action> onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.Generic.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.Generic.cs new file mode 100644 index 00000000..b3363eb8 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.Generic.cs @@ -0,0 +1,42 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot Async(demis.bellot@gmail.com, CancellationToken cancellationToken = default) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Model; + +namespace ServiceStack.Redis.Generic +{ + public interface IRedisSetAsync : IAsyncEnumerable, IHasStringId + { + ValueTask CountAsync(CancellationToken cancellationToken = default); + ValueTask> SortAsync(int startingFrom, int endingAt, CancellationToken cancellationToken = default); + ValueTask> GetAllAsync(CancellationToken cancellationToken = default); + ValueTask PopRandomItemAsync(CancellationToken cancellationToken = default); + ValueTask GetRandomItemAsync(CancellationToken cancellationToken = default); + ValueTask MoveToAsync(T item, IRedisSetAsync toSet, CancellationToken cancellationToken = default); + ValueTask PopulateWithIntersectOfAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken = default); + ValueTask PopulateWithIntersectOfAsync(params IRedisSetAsync[] sets); // convenience API + ValueTask PopulateWithUnionOfAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken = default); + ValueTask PopulateWithUnionOfAsync(params IRedisSetAsync[] sets); // convenience API + ValueTask GetDifferencesAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); + ValueTask GetDifferencesAsync(params IRedisSetAsync[] withSets); // convenience API + ValueTask PopulateWithDifferencesOfAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); + ValueTask PopulateWithDifferencesOfAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets); // convenience API + ValueTask ClearAsync(CancellationToken cancellationToken = default); + ValueTask ContainsAsync(T item, CancellationToken cancellationToken = default); + ValueTask RemoveAsync(T item, CancellationToken cancellationToken = default); + ValueTask AddAsync(T item, CancellationToken cancellationToken = default); + } + +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.cs new file mode 100644 index 00000000..bd8ac6c3 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.cs @@ -0,0 +1,47 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot Async(demis.bellot@gmail.com) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Model; + +namespace ServiceStack.Redis +{ + public interface IRedisSetAsync + : IAsyncEnumerable, IHasStringId + { + ValueTask CountAsync(CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetAsync(int startingFrom, int endingAt, CancellationToken cancellationToken = default); + ValueTask> GetAllAsync(CancellationToken cancellationToken = default); + ValueTask PopAsync(CancellationToken cancellationToken = default); + ValueTask MoveAsync(string value, IRedisSetAsync toSet, CancellationToken cancellationToken = default); + ValueTask> IntersectAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); + ValueTask> IntersectAsync(params IRedisSetAsync[] withSets); // conveinence API + ValueTask StoreIntersectAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); + ValueTask StoreIntersectAsync(params IRedisSetAsync[] withSets); // conveinence API + ValueTask> UnionAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); + ValueTask> UnionAsync(params IRedisSetAsync[] withSets); // conveinence API + ValueTask StoreUnionAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); + ValueTask StoreUnionAsync(params IRedisSetAsync[] withSets); // conveinence API + ValueTask> DiffAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); + ValueTask StoreDiffAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); + ValueTask StoreDiffAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets); // conveinence API + ValueTask GetRandomEntryAsync(CancellationToken cancellationToken = default); + + + ValueTask RemoveAsync(string item, CancellationToken cancellationToken = default); + ValueTask AddAsync(string item, CancellationToken cancellationToken = default); + ValueTask ContainsAsync(string item, CancellationToken cancellationToken = default); + ValueTask ClearAsync(CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.Generic.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.Generic.cs new file mode 100644 index 00000000..ec7927cb --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.Generic.cs @@ -0,0 +1,51 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot Async(demis.bellot@gmail.com, CancellationToken cancellationToken = default) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Model; + +namespace ServiceStack.Redis.Generic +{ + public interface IRedisSortedSetAsync : IAsyncEnumerable, IHasStringId + { + ValueTask CountAsync(CancellationToken cancellationToken = default); + ValueTask AddAsync(T item, double score, CancellationToken cancellationToken = default); + ValueTask PopItemWithHighestScoreAsync(CancellationToken cancellationToken = default); + ValueTask PopItemWithLowestScoreAsync(CancellationToken cancellationToken = default); + ValueTask IncrementItemAsync(T item, double incrementBy, CancellationToken cancellationToken = default); + ValueTask IndexOfAsync(T item, CancellationToken cancellationToken = default); + ValueTask IndexOfDescendingAsync(T item, CancellationToken cancellationToken = default); + ValueTask> GetAllAsync(CancellationToken cancellationToken = default); + ValueTask> GetAllDescendingAsync(CancellationToken cancellationToken = default); + ValueTask> GetRangeAsync(int fromRank, int toRank, CancellationToken cancellationToken = default); + ValueTask> GetRangeByLowestScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeByLowestScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeByHighestScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeByHighestScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask RemoveRangeAsync(int minRank, int maxRank, CancellationToken cancellationToken = default); + ValueTask RemoveRangeByScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask GetItemScoreAsync(T item, CancellationToken cancellationToken = default); + ValueTask PopulateWithIntersectOfAsync(IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken = default); + ValueTask PopulateWithIntersectOfAsync(params IRedisSortedSetAsync[] setIds); // convenience API + ValueTask PopulateWithIntersectOfAsync(IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken = default); + ValueTask PopulateWithUnionOfAsync(IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken = default); + ValueTask PopulateWithUnionOfAsync(params IRedisSortedSetAsync[] setIds); // convenience API + ValueTask PopulateWithUnionOfAsync(IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken = default); + ValueTask ClearAsync(CancellationToken cancellationToken = default); + + ValueTask ContainsAsync(T item, CancellationToken cancellationToken = default); + ValueTask AddAsync(T item, CancellationToken cancellationToken = default); + ValueTask RemoveAsync(T item, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.cs new file mode 100644 index 00000000..9e85d906 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.cs @@ -0,0 +1,46 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot Async(demis.bellot@gmail.com) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Model; + +namespace ServiceStack.Redis +{ + public interface IRedisSortedSetAsync + : IAsyncEnumerable, IHasStringId + { + ValueTask CountAsync(CancellationToken cancellationToken = default); + ValueTask> GetAllAsync(CancellationToken cancellationToken = default); + ValueTask> GetRangeAsync(int startingRank, int endingRank, CancellationToken cancellationToken = default); + ValueTask> GetRangeByScoreAsync(string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeByScoreAsync(string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeByScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeByScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask RemoveRangeAsync(int fromRank, int toRank, CancellationToken cancellationToken = default); + ValueTask RemoveRangeByScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask StoreFromIntersectAsync(IRedisSortedSetAsync[] ofSets, CancellationToken cancellationToken = default); + ValueTask StoreFromIntersectAsync(params IRedisSortedSetAsync[] ofSets); // convenience API + ValueTask StoreFromUnionAsync(IRedisSortedSetAsync[] ofSets, CancellationToken cancellationToken = default); + ValueTask StoreFromUnionAsync(params IRedisSortedSetAsync[] ofSets); // convenience API + ValueTask GetItemIndexAsync(string value, CancellationToken cancellationToken = default); + ValueTask GetItemScoreAsync(string value, CancellationToken cancellationToken = default); + ValueTask IncrementItemScoreAsync(string value, double incrementByScore, CancellationToken cancellationToken = default); + ValueTask PopItemWithHighestScoreAsync(CancellationToken cancellationToken = default); + ValueTask PopItemWithLowestScoreAsync(CancellationToken cancellationToken = default); + ValueTask ClearAsync(CancellationToken cancellationToken = default); + ValueTask ContainsAsync(string item, CancellationToken cancellationToken = default); + ValueTask AddAsync(string item, CancellationToken cancellationToken = default); + ValueTask RemoveAsync(string item, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSubscriptionAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSubscriptionAsync.cs new file mode 100644 index 00000000..6b06a032 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSubscriptionAsync.cs @@ -0,0 +1,61 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + public interface IRedisSubscriptionAsync + : IAsyncDisposable + { + /// + /// The number of active subscriptions this client has + /// + long SubscriptionCount { get; } + + /// + /// Registered handler called after client *Subscribes* to each new channel + /// + event Func OnSubscribeAsync; + + /// + /// Registered handler called when each message is received + /// + event Func OnMessageAsync; + + /// + /// Registered handler called when each message is received + /// + event Func OnMessageBytesAsync; + + /// + /// Registered handler called when each channel is unsubscribed + /// + event Func OnUnSubscribeAsync; + + /// + /// Subscribe to channels by name + /// + ValueTask SubscribeToChannelsAsync(string[] channels, CancellationToken cancellationToken = default); + + /// + /// Subscribe to channels by name + /// + ValueTask SubscribeToChannelsAsync(params string[] channels); // convenience API + + /// + /// Subscribe to channels matching the supplied patterns + /// + ValueTask SubscribeToChannelsMatchingAsync(string[] patterns, CancellationToken cancellationToken = default); + + /// + /// Subscribe to channels matching the supplied patterns + /// + ValueTask SubscribeToChannelsMatchingAsync(params string[] patterns); // convenience API + + ValueTask UnSubscribeFromAllChannelsAsync(CancellationToken cancellationToken = default); + ValueTask UnSubscribeFromChannelsAsync(string[] channels, CancellationToken cancellationToken = default); + ValueTask UnSubscribeFromChannelsAsync(params string[] channels); // convenience API + ValueTask UnSubscribeFromChannelsMatchingAsync(string[] patterns, CancellationToken cancellationToken = default); + ValueTask UnSubscribeFromChannelsMatchingAsync(params string[] patterns); // convenience API + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionAsync.cs new file mode 100644 index 00000000..5ee56047 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionAsync.cs @@ -0,0 +1,29 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Redis.Pipeline; + +namespace ServiceStack.Redis +{ + /// + /// Interface to redis transaction + /// + public interface IRedisTransactionAsync + : IRedisTransactionBaseAsync, IRedisQueueableOperationAsync, IAsyncDisposable + { + ValueTask CommitAsync(CancellationToken cancellationToken = default); + ValueTask RollbackAsync(CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionBaseAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionBaseAsync.cs new file mode 100644 index 00000000..fb2089e5 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionBaseAsync.cs @@ -0,0 +1,11 @@ +using ServiceStack.Redis.Pipeline; + +namespace ServiceStack.Redis +{ + /// + /// Base transaction interface, shared by typed and non-typed transactions + /// + public interface IRedisTransactionBaseAsync : IRedisPipelineSharedAsync + { + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedClientAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedClientAsync.cs new file mode 100644 index 00000000..3af3b6b0 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedClientAsync.cs @@ -0,0 +1,211 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Data; +using ServiceStack.Model; + +namespace ServiceStack.Redis.Generic +{ + public interface IRedisTypedClientAsync : IEntityStoreAsync + { + IHasNamed> Lists { get; } + IHasNamed> Sets { get; } + IHasNamed> SortedSets { get; } + IRedisHashAsync GetHash(string hashId); + IRedisSetAsync TypeIdsSet { get; } + + // not provided: use GetValueAsync/SetValueAsync instead + // T this[string key] { get; set; } + + ValueTask> CreateTransactionAsync(CancellationToken cancellationToken = default); + IRedisTypedPipelineAsync CreatePipeline(); + + IRedisClientAsync RedisClient { get; } + + ValueTask AcquireLockAsync(TimeSpan? timeOut = default, CancellationToken cancellationToken = default); + + long Db { get; } + ValueTask SelectAsync(long db, CancellationToken cancellationToken = default); + + ValueTask> GetAllKeysAsync(CancellationToken cancellationToken = default); + + string UrnKey(T value); + + string SequenceKey { get; set; } + ValueTask SetSequenceAsync(int value, CancellationToken cancellationToken = default); + ValueTask GetNextSequenceAsync(CancellationToken cancellationToken = default); + ValueTask GetNextSequenceAsync(int incrBy, CancellationToken cancellationToken = default); + ValueTask GetEntryTypeAsync(string key, CancellationToken cancellationToken = default); + ValueTask GetRandomKeyAsync(CancellationToken cancellationToken = default); + + ValueTask SetValueAsync(string key, T entity, CancellationToken cancellationToken = default); + ValueTask SetValueAsync(string key, T entity, TimeSpan expireIn, CancellationToken cancellationToken = default); + ValueTask SetValueIfNotExistsAsync(string key, T entity, CancellationToken cancellationToken = default); + ValueTask SetValueIfExistsAsync(string key, T entity, CancellationToken cancellationToken = default); + + ValueTask StoreAsync(T entity, TimeSpan expireIn, CancellationToken cancellationToken = default); + + ValueTask GetValueAsync(string key, CancellationToken cancellationToken = default); + ValueTask GetAndSetValueAsync(string key, T value, CancellationToken cancellationToken = default); + ValueTask ContainsKeyAsync(string key, CancellationToken cancellationToken = default); + ValueTask RemoveEntryAsync(string key, CancellationToken cancellationToken = default); + ValueTask RemoveEntryAsync(string[] args, CancellationToken cancellationToken = default); + ValueTask RemoveEntryAsync(params string[] args); // convenience API + ValueTask RemoveEntryAsync(IHasStringId[] entities, CancellationToken cancellationToken = default); + ValueTask RemoveEntryAsync(params IHasStringId[] entities); // convenience API + ValueTask IncrementValueAsync(string key, CancellationToken cancellationToken = default); + ValueTask IncrementValueByAsync(string key, int count, CancellationToken cancellationToken = default); + ValueTask DecrementValueAsync(string key, CancellationToken cancellationToken = default); + ValueTask DecrementValueByAsync(string key, int count, CancellationToken cancellationToken = default); + + ValueTask ExpireInAsync(object id, TimeSpan expiresAt, CancellationToken cancellationToken = default); + ValueTask ExpireAtAsync(object id, DateTime dateTime, CancellationToken cancellationToken = default); + ValueTask ExpireEntryInAsync(string key, TimeSpan expiresAt, CancellationToken cancellationToken = default); + ValueTask ExpireEntryAtAsync(string key, DateTime dateTime, CancellationToken cancellationToken = default); + + ValueTask GetTimeToLiveAsync(string key, CancellationToken cancellationToken = default); + ValueTask ForegroundSaveAsync(CancellationToken cancellationToken = default); + ValueTask BackgroundSaveAsync(CancellationToken cancellationToken = default); + ValueTask FlushDbAsync(CancellationToken cancellationToken = default); + ValueTask FlushAllAsync(CancellationToken cancellationToken = default); + ValueTask SearchKeysAsync(string pattern, CancellationToken cancellationToken = default); + ValueTask> GetValuesAsync(List keys, CancellationToken cancellationToken = default); + ValueTask> GetSortedEntryValuesAsync(IRedisSetAsync fromSet, int startingFrom, int endingAt, CancellationToken cancellationToken = default); + ValueTask StoreAsHashAsync(T entity, CancellationToken cancellationToken = default); + ValueTask GetFromHashAsync(object id, CancellationToken cancellationToken = default); + + //Set operations + ValueTask> GetAllItemsFromSetAsync(IRedisSetAsync fromSet, CancellationToken cancellationToken = default); + ValueTask AddItemToSetAsync(IRedisSetAsync toSet, T item, CancellationToken cancellationToken = default); + ValueTask RemoveItemFromSetAsync(IRedisSetAsync fromSet, T item, CancellationToken cancellationToken = default); + ValueTask PopItemFromSetAsync(IRedisSetAsync fromSet, CancellationToken cancellationToken = default); + ValueTask MoveBetweenSetsAsync(IRedisSetAsync fromSet, IRedisSetAsync toSet, T item, CancellationToken cancellationToken = default); + ValueTask GetSetCountAsync(IRedisSetAsync set, CancellationToken cancellationToken = default); + ValueTask SetContainsItemAsync(IRedisSetAsync set, T item, CancellationToken cancellationToken = default); + ValueTask> GetIntersectFromSetsAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken = default); + ValueTask> GetIntersectFromSetsAsync(params IRedisSetAsync[] sets); + ValueTask StoreIntersectFromSetsAsync(IRedisSetAsync intoSet, IRedisSetAsync[] sets, CancellationToken cancellationToken = default); + ValueTask StoreIntersectFromSetsAsync(IRedisSetAsync intoSet, params IRedisSetAsync[] sets); // convenience API + ValueTask> GetUnionFromSetsAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken = default); + ValueTask> GetUnionFromSetsAsync(params IRedisSetAsync[] sets); // convenience API + ValueTask StoreUnionFromSetsAsync(IRedisSetAsync intoSet, IRedisSetAsync[] sets, CancellationToken cancellationToken = default); + ValueTask StoreUnionFromSetsAsync(IRedisSetAsync intoSet, params IRedisSetAsync[] sets); // convenience API + ValueTask> GetDifferencesFromSetAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); + ValueTask> GetDifferencesFromSetAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets); // convenience API + ValueTask StoreDifferencesFromSetAsync(IRedisSetAsync intoSet, IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); + ValueTask StoreDifferencesFromSetAsync(IRedisSetAsync intoSet, IRedisSetAsync fromSet, params IRedisSetAsync[] withSets); // convenience API + ValueTask GetRandomItemFromSetAsync(IRedisSetAsync fromSet, CancellationToken cancellationToken = default); + + //List operations + ValueTask> GetAllItemsFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromListAsync(IRedisListAsync fromList, int startingFrom, int endingAt, CancellationToken cancellationToken = default); + ValueTask> SortListAsync(IRedisListAsync fromList, int startingFrom, int endingAt, CancellationToken cancellationToken = default); + ValueTask AddItemToListAsync(IRedisListAsync fromList, T value, CancellationToken cancellationToken = default); + ValueTask PrependItemToListAsync(IRedisListAsync fromList, T value, CancellationToken cancellationToken = default); + ValueTask RemoveStartFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); + ValueTask BlockingRemoveStartFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken cancellationToken = default); + ValueTask RemoveEndFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); + ValueTask RemoveAllFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); + ValueTask TrimListAsync(IRedisListAsync fromList, int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken = default); + ValueTask RemoveItemFromListAsync(IRedisListAsync fromList, T value, CancellationToken cancellationToken = default); + ValueTask RemoveItemFromListAsync(IRedisListAsync fromList, T value, int noOfMatches, CancellationToken cancellationToken = default); + ValueTask GetListCountAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); + ValueTask GetItemFromListAsync(IRedisListAsync fromList, int listIndex, CancellationToken cancellationToken = default); + ValueTask SetItemInListAsync(IRedisListAsync toList, int listIndex, T value, CancellationToken cancellationToken = default); + ValueTask InsertBeforeItemInListAsync(IRedisListAsync toList, T pivot, T value, CancellationToken cancellationToken = default); + ValueTask InsertAfterItemInListAsync(IRedisListAsync toList, T pivot, T value, CancellationToken cancellationToken = default); + + //Queue operations + ValueTask EnqueueItemOnListAsync(IRedisListAsync fromList, T item, CancellationToken cancellationToken = default); + ValueTask DequeueItemFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); + ValueTask BlockingDequeueItemFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken cancellationToken = default); + + //Stack operations + ValueTask PushItemToListAsync(IRedisListAsync fromList, T item, CancellationToken cancellationToken = default); + ValueTask PopItemFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); + ValueTask BlockingPopItemFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken cancellationToken = default); + ValueTask PopAndPushItemBetweenListsAsync(IRedisListAsync fromList, IRedisListAsync toList, CancellationToken cancellationToken = default); + ValueTask BlockingPopAndPushItemBetweenListsAsync(IRedisListAsync fromList, IRedisListAsync toList, TimeSpan? timeOut, CancellationToken cancellationToken = default); + + //Sorted Set operations + ValueTask AddItemToSortedSetAsync(IRedisSortedSetAsync toSet, T value, CancellationToken cancellationToken = default); + ValueTask AddItemToSortedSetAsync(IRedisSortedSetAsync toSet, T value, double score, CancellationToken cancellationToken = default); + ValueTask RemoveItemFromSortedSetAsync(IRedisSortedSetAsync fromSet, T value, CancellationToken cancellationToken = default); + ValueTask PopItemWithLowestScoreFromSortedSetAsync(IRedisSortedSetAsync fromSet, CancellationToken cancellationToken = default); + ValueTask PopItemWithHighestScoreFromSortedSetAsync(IRedisSortedSetAsync fromSet, CancellationToken cancellationToken = default); + ValueTask SortedSetContainsItemAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken = default); + ValueTask IncrementItemInSortedSetAsync(IRedisSortedSetAsync set, T value, double incrementBy, CancellationToken cancellationToken = default); + ValueTask GetItemIndexInSortedSetAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken = default); + ValueTask GetItemIndexInSortedSetDescAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken = default); + ValueTask> GetAllItemsFromSortedSetAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken = default); + ValueTask> GetAllItemsFromSortedSetDescAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetDescAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken = default); + ValueTask> GetAllWithScoresFromSortedSetAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetDescAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); + ValueTask RemoveRangeFromSortedSetAsync(IRedisSortedSetAsync set, int minRank, int maxRank, CancellationToken cancellationToken = default); + ValueTask RemoveRangeFromSortedSetByScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken = default); + ValueTask GetSortedSetCountAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken = default); + ValueTask GetItemScoreInSortedSetAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken = default); + ValueTask StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken = default); + ValueTask StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, params IRedisSortedSetAsync[] setIds); // convenience API + ValueTask StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken = default); + ValueTask StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken = default); + ValueTask StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, params IRedisSortedSetAsync[] setIds); // convenience API + ValueTask StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken = default); + + //Hash operations + ValueTask HashContainsEntryAsync(IRedisHashAsync hash, TKey key, CancellationToken cancellationToken = default); + ValueTask SetEntryInHashAsync(IRedisHashAsync hash, TKey key, T value, CancellationToken cancellationToken = default); + ValueTask SetEntryInHashIfNotExistsAsync(IRedisHashAsync hash, TKey key, T value, CancellationToken cancellationToken = default); + ValueTask SetRangeInHashAsync(IRedisHashAsync hash, IEnumerable> keyValuePairs, CancellationToken cancellationToken = default); + ValueTask GetValueFromHashAsync(IRedisHashAsync hash, TKey key, CancellationToken cancellationToken = default); + ValueTask RemoveEntryFromHashAsync(IRedisHashAsync hash, TKey key, CancellationToken cancellationToken = default); + ValueTask GetHashCountAsync(IRedisHashAsync hash, CancellationToken cancellationToken = default); + ValueTask> GetHashKeysAsync(IRedisHashAsync hash, CancellationToken cancellationToken = default); + ValueTask> GetHashValuesAsync(IRedisHashAsync hash, CancellationToken cancellationToken = default); + ValueTask> GetAllEntriesFromHashAsync(IRedisHashAsync hash, CancellationToken cancellationToken = default); + + //Useful common app-logic + ValueTask StoreRelatedEntitiesAsync(object parentId, List children, CancellationToken cancellationToken = default); + ValueTask StoreRelatedEntitiesAsync(object parentId, TChild[] children, CancellationToken cancellationToken = default); + ValueTask StoreRelatedEntitiesAsync(object parentId, params TChild[] children); // convenience API + ValueTask DeleteRelatedEntitiesAsync(object parentId, CancellationToken cancellationToken = default); + ValueTask DeleteRelatedEntityAsync(object parentId, object childId, CancellationToken cancellationToken = default); + ValueTask> GetRelatedEntitiesAsync(object parentId, CancellationToken cancellationToken = default); + ValueTask GetRelatedEntitiesCountAsync(object parentId, CancellationToken cancellationToken = default); + ValueTask AddToRecentsListAsync(T value, CancellationToken cancellationToken = default); + ValueTask> GetLatestFromRecentsListAsync(int skip, int take, CancellationToken cancellationToken = default); + ValueTask> GetEarliestFromRecentsListAsync(int skip, int take, CancellationToken cancellationToken = default); + } + +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedPipelineAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedPipelineAsync.cs new file mode 100644 index 00000000..1bc9db8a --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedPipelineAsync.cs @@ -0,0 +1,11 @@ +using ServiceStack.Redis.Pipeline; + +namespace ServiceStack.Redis.Generic +{ + /// + /// Interface to redis typed pipeline + /// + public interface IRedisTypedPipelineAsync : IRedisPipelineSharedAsync, IRedisTypedQueueableOperationAsync + { + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedQueueableOperationAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedQueueableOperationAsync.cs new file mode 100644 index 00000000..0fac7b59 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedQueueableOperationAsync.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Generic +{ + /// + /// interface to queueable operation using typed redis client + /// + /// + public interface IRedisTypedQueueableOperationAsync + { + void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func, ValueTask>> command, Action> onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func, ValueTask>> command, Action> onSuccessCallback = null, Action onErrorCallback = null); + void QueueCommand(Func, ValueTask>> command, Action> onSuccessCallback = null, Action onErrorCallback = null); + + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedTransactionAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedTransactionAsync.cs new file mode 100644 index 00000000..b89ef5a8 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedTransactionAsync.cs @@ -0,0 +1,28 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2017 ServiceStack, Inc. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Generic +{ + /// + /// Redis transaction for typed client + /// + /// + public interface IRedisTypedTransactionAsync : IRedisTypedQueueableOperationAsync, IAsyncDisposable + { + ValueTask CommitAsync(CancellationToken cancellationToken = default); + ValueTask RollbackAsync(CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientAsync.cs new file mode 100644 index 00000000..19d8ccf7 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientAsync.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Caching +{ + /// + /// A common interface implementation that is implemented by most cache providers + /// + public interface ICacheClientAsync + : IAsyncDisposable + { + /// + /// Removes the specified item from the cache. + /// + /// The identifier for the item to delete. + /// + /// true if the item was successfully removed from the cache; false otherwise. + /// + ValueTask RemoveAsync(string key, CancellationToken cancellationToken = default); + + /// + /// Removes the cache for all the keys provided. + /// + /// The keys. + ValueTask RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken = default); + + /// + /// Retrieves the specified item from the cache. + /// + /// + /// The identifier for the item to retrieve. + /// + /// The retrieved item, or null if the key was not found. + /// + ValueTask GetAsync(string key, CancellationToken cancellationToken = default); + + /// + /// Increments the value of the specified key by the given amount. + /// The operation is atomic and happens on the server. + /// A non existent value at key starts at 0 + /// + /// The identifier for the item to increment. + /// The amount by which the client wants to increase the item. + /// + /// The new value of the item or -1 if not found. + /// + /// The item must be inserted into the cache before it can be changed. The item must be inserted as a . The operation only works with values, so -1 always indicates that the item was not found. + ValueTask IncrementAsync(string key, uint amount, CancellationToken cancellationToken = default); + + /// + /// Increments the value of the specified key by the given amount. + /// The operation is atomic and happens on the server. + /// A non existent value at key starts at 0 + /// + /// The identifier for the item to increment. + /// The amount by which the client wants to decrease the item. + /// + /// The new value of the item or -1 if not found. + /// + /// The item must be inserted into the cache before it can be changed. The item must be inserted as a . The operation only works with values, so -1 always indicates that the item was not found. + ValueTask DecrementAsync(string key, uint amount, CancellationToken cancellationToken = default); + + /// + /// Adds a new item into the cache at the specified cache key only if the cache is empty. + /// + /// The key used to reference the item. + /// The object to be inserted into the cache. + /// + /// true if the item was successfully stored in the cache; false otherwise. + /// + /// The item does not expire unless it is removed due memory pressure. + ValueTask AddAsync(string key, T value, CancellationToken cancellationToken = default); + + /// + /// Sets an item into the cache at the cache key specified regardless if it already exists or not. + /// + ValueTask SetAsync(string key, T value, CancellationToken cancellationToken = default); + + /// + /// Replaces the item at the cachekey specified only if an items exists at the location already. + /// + ValueTask ReplaceAsync(string key, T value, CancellationToken cancellationToken = default); + + ValueTask AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken = default); + ValueTask SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken = default); + ValueTask ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken = default); + + ValueTask AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken = default); + ValueTask SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken = default); + ValueTask ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken = default); + + /// + /// Invalidates all data on the cache. + /// + ValueTask FlushAllAsync(CancellationToken cancellationToken = default); + + /// + /// Retrieves multiple items from the cache. + /// The default value of T is set for all keys that do not exist. + /// + /// The list of identifiers for the items to retrieve. + /// + /// a Dictionary holding all items indexed by their key. + /// + ValueTask> GetAllAsync(IEnumerable keys, CancellationToken cancellationToken = default); + + /// + /// Sets multiple items to the cache. + /// + /// + /// The values. + ValueTask SetAllAsync(IDictionary values, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientExtendedAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientExtendedAsync.cs new file mode 100644 index 00000000..b8270e2d --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientExtendedAsync.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Caching +{ + /// + /// Extend ICacheClient API with shared, non-core features + /// + public interface ICacheClientExtendedAsync : ICacheClientAsync + { + ValueTask GetTimeToLiveAsync(string key, CancellationToken cancellationToken = default); + + IAsyncEnumerable GetKeysByPatternAsync(string pattern, CancellationToken cancellationToken = default); + + ValueTask RemoveExpiredEntriesAsync(CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/IRemoveByPatternAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/IRemoveByPatternAsync.cs new file mode 100644 index 00000000..09e65f32 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/IRemoveByPatternAsync.cs @@ -0,0 +1,19 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Caching +{ + public interface IRemoveByPatternAsync + { + /// + /// Removes items from cache that have keys matching the specified wildcard pattern + /// + /// The wildcard, where "*" means any sequence of characters and "?" means any single character. + ValueTask RemoveByPatternAsync(string pattern, CancellationToken cancellationToken = default); + /// + /// Removes items from the cache based on the specified regular expression pattern + /// + /// Regular expression pattern to search cache keys + ValueTask RemoveByRegexAsync(string regex, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.Generic.cs b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.Generic.cs new file mode 100644 index 00000000..717cc0c6 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.Generic.cs @@ -0,0 +1,36 @@ +//Copyright (c) ServiceStack, Inc. All Rights Reserved. +//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Data +{ + /// + /// For providers that want a cleaner API with a little more perf + /// + /// + public interface IEntityStoreAsync + { + ValueTask GetByIdAsync(object id, CancellationToken cancellationToken = default); + + ValueTask> GetByIdsAsync(IEnumerable ids, CancellationToken cancellationToken = default); + + ValueTask> GetAllAsync(CancellationToken cancellationToken = default); + + ValueTask StoreAsync(T entity, CancellationToken cancellationToken = default); + + ValueTask StoreAllAsync(IEnumerable entities, CancellationToken cancellationToken = default); + + ValueTask DeleteAsync(T entity, CancellationToken cancellationToken = default); + + ValueTask DeleteByIdAsync(object id, CancellationToken cancellationToken = default); + + ValueTask DeleteByIdsAsync(IEnumerable ids, CancellationToken cancellationToken = default); + + ValueTask DeleteAllAsync(CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.cs new file mode 100644 index 00000000..41b04939 --- /dev/null +++ b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.cs @@ -0,0 +1,30 @@ +//Copyright (c) ServiceStack, Inc. All Rights Reserved. +//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Data +{ + public interface IEntityStoreAsync : IAsyncDisposable + { + ValueTask GetByIdAsync(object id, CancellationToken cancellationToken = default); + + ValueTask> GetByIdsAsync(ICollection ids, CancellationToken cancellationToken = default); + + ValueTask StoreAsync(T entity, CancellationToken cancellationToken = default); + + ValueTask StoreAllAsync(IEnumerable entities, CancellationToken cancellationToken = default); + + ValueTask DeleteAsync(T entity, CancellationToken cancellationToken = default); + + ValueTask DeleteByIdAsync(object id, CancellationToken cancellationToken = default); + + ValueTask DeleteByIdsAsync(ICollection ids, CancellationToken cancellationToken = default); + + ValueTask DeleteAllAsync(CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs b/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs new file mode 100644 index 00000000..5e0d2069 --- /dev/null +++ b/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs @@ -0,0 +1,158 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Caching; +using ServiceStack.Redis.Internal; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + /// + /// Provides thread-safe retrieval of redis clients since each client is a new one. + /// Allows the configuration of different ReadWrite and ReadOnly hosts + /// + public partial class BasicRedisClientManager + : IRedisClientsManagerAsync, ICacheClientAsync + { + private ValueTask GetCacheClientAsync(in CancellationToken _) + => new RedisClientManagerCacheClient(this).AsValueTask(); + + private ValueTask GetReadOnlyCacheClientAsync(in CancellationToken _) + => ConfigureRedisClientAsync(this.GetReadOnlyClientImpl()).AsValueTask(); + + private IRedisClientAsync ConfigureRedisClientAsync(IRedisClientAsync client) + => client; + + ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(CancellationToken cancellationToken) + => GetCacheClientAsync(cancellationToken); + + ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken cancellationToken) + => GetClientImpl().AsValueTask(); + + ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken cancellationToken) + => GetReadOnlyCacheClientAsync(cancellationToken); + + ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken cancellationToken) + => GetReadOnlyClientImpl().AsValueTask(); + + ValueTask IAsyncDisposable.DisposeAsync() + { + Dispose(); + return default; + } + + async ValueTask ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) + { + await using var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.GetAsync(key).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.SetAsync(key, value, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await client.FlushAllAsync(cancellationToken).ConfigureAwait(false); + } + + async ValueTask> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) + { + await using var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.GetAllAsync(keys, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await client.SetAllAsync(values, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.RemoveAsync(key, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await client.RemoveAllAsync(keys, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.IncrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.DecrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.AddAsync(key, value, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + { + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs b/src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs index 28aa6411..f38853c0 100644 --- a/src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs +++ b/src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs @@ -37,7 +37,7 @@ public ICacheClient GetCacheClient() public ICacheClient GetReadOnlyCacheClient() { - return ConfigureRedisClient(this.GetReadOnlyClient()); + return ConfigureRedisClient(this.GetReadOnlyClientImpl()); } private ICacheClient ConfigureRedisClient(IRedisClient client) diff --git a/src/ServiceStack.Redis/BasicRedisClientManager.cs b/src/ServiceStack.Redis/BasicRedisClientManager.cs index b1a18332..1c21691a 100644 --- a/src/ServiceStack.Redis/BasicRedisClientManager.cs +++ b/src/ServiceStack.Redis/BasicRedisClientManager.cs @@ -99,7 +99,8 @@ protected virtual void OnStart() /// Returns a Read/Write client (The default) using the hosts defined in ReadWriteHosts /// /// - public IRedisClient GetClient() + public IRedisClient GetClient() => GetClientImpl(); + private RedisClient GetClientImpl() { var client = InitNewClient(RedisResolver.CreateMasterClient(readWriteHostsIndex++)); return client; @@ -109,7 +110,8 @@ public IRedisClient GetClient() /// Returns a ReadOnly client using the hosts defined in ReadOnlyHosts. /// /// - public virtual IRedisClient GetReadOnlyClient() + public virtual IRedisClient GetReadOnlyClient() => GetReadOnlyClientImpl(); + private RedisClient GetReadOnlyClientImpl() { var client = InitNewClient(RedisResolver.CreateSlaveClient(readOnlyHostsIndex++)); return client; diff --git a/src/ServiceStack.Redis/BufferedReader.Async.cs b/src/ServiceStack.Redis/BufferedReader.Async.cs new file mode 100644 index 00000000..b722b82b --- /dev/null +++ b/src/ServiceStack.Redis/BufferedReader.Async.cs @@ -0,0 +1,93 @@ +using ServiceStack.Redis.Internal; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + internal sealed partial class BufferedReader + { + internal ValueTask ReadByteAsync(in CancellationToken cancellationToken = default) + => _available > 0 ? ReadByteFromBuffer().AsValueTask() : ReadByteSlowAsync(cancellationToken); + + private ValueTask ReadByteSlowAsync(in CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + _offset = 0; +#if ASYNC_MEMORY + var pending = _source.ReadAsync(new Memory(_buffer), cancellationToken); + if (!pending.IsCompletedSuccessfully) + return Awaited(this, pending); +#else + var pending = _source.ReadAsync(_buffer, 0, _buffer.Length, cancellationToken); + if (pending.Status != TaskStatus.RanToCompletion) + return Awaited(this, pending); +#endif + + _available = pending.Result; + return (_available > 0 ? ReadByteFromBuffer() : -1).AsValueTask(); + +#if ASYNC_MEMORY + static async ValueTask Awaited(BufferedReader @this, ValueTask pending) + { + @this._available = await pending.ConfigureAwait(false); + return @this._available > 0 ? @this.ReadByteFromBuffer() : -1; + } +#else + static async ValueTask Awaited(BufferedReader @this, Task pending) + { + @this._available = await pending.ConfigureAwait(false); + return @this._available > 0 ? @this.ReadByteFromBuffer() : -1; + } +#endif + } + + internal ValueTask ReadAsync(byte[] buffer, int offset, int count, in CancellationToken cancellationToken = default) + => _available > 0 + ? ReadFromBuffer(buffer, offset, count).AsValueTask() + : ReadSlowAsync(buffer, offset, count, cancellationToken); + + private ValueTask ReadSlowAsync(byte[] buffer, int offset, int count, in CancellationToken cancellationToken) + { + // if they're asking for more than we deal in, just step out of the way + if (count >= buffer.Length) + { +#if ASYNC_MEMORY + return _source.ReadAsync(new Memory(buffer, offset, count), cancellationToken); +#else + return new ValueTask(_source.ReadAsync(buffer, offset, count, cancellationToken)); +#endif + } + + // they're asking for less, so we could still have some left + _offset = 0; +#if ASYNC_MEMORY + var pending = _source.ReadAsync(new Memory(_buffer), cancellationToken); + if (!pending.IsCompletedSuccessfully) + return Awaited(this, pending, buffer, offset, count); + + _available = pending.Result; // already checked status, this is fine + return (_available > 0 ? ReadFromBuffer(buffer, offset, count) : 0).AsValueTask(); + + static async ValueTask Awaited(BufferedReader @this, ValueTask pending, byte[] buffer, int offset, int count) + { + @this._available = await pending.ConfigureAwait(false); + return @this._available > 0 ? @this.ReadFromBuffer(buffer, offset, count) : 0; + } +#else + var pending = _source.ReadAsync(_buffer, 0, _buffer.Length, cancellationToken); + if (pending.Status != TaskStatus.RanToCompletion) + return Awaited(this, pending, buffer, offset, count); + + _available = pending.Result; // already checked status, this is fine + return (_available > 0 ? ReadFromBuffer(buffer, offset, count) : 0).AsValueTask(); + + static async ValueTask Awaited(BufferedReader @this, Task pending, byte[] buffer, int offset, int count) + { + @this._available = await pending.ConfigureAwait(false); + return @this._available > 0 ? @this.ReadFromBuffer(buffer, offset, count) : 0; + } +#endif + } + } +} diff --git a/src/ServiceStack.Redis/BufferedReader.cs b/src/ServiceStack.Redis/BufferedReader.cs new file mode 100644 index 00000000..9bf2d573 --- /dev/null +++ b/src/ServiceStack.Redis/BufferedReader.cs @@ -0,0 +1,76 @@ +using System; +using System.IO; + +namespace ServiceStack.Redis +{ + /// + /// BufferedReader is a minimal buffer implementation that provides + /// efficient sync and async access for byte-by-byte consumption; + /// like BufferedStream, but with the async part + /// + internal sealed partial class BufferedReader : IDisposable + { + private readonly Stream _source; + readonly byte[] _buffer; + private int _offset, _available; + public void Dispose() + { + _available = 0; + _source.Dispose(); + } + internal void Close() + { + _available = 0; + _source.Close(); + } + + internal BufferedReader(Stream source, int bufferSize) + { + _source = source; + _buffer = new byte[bufferSize]; + _offset = _available = 0; + } + + internal int ReadByte() + => _available > 0 ? ReadByteFromBuffer() : ReadByteSlow(); + + private int ReadByteFromBuffer() + { + --_available; + return _buffer[_offset++]; + } + + private int ReadByteSlow() + { + _available = _source.Read(_buffer, _offset = 0, _buffer.Length); + return _available > 0 ? ReadByteFromBuffer() : -1; + } + + + private int ReadFromBuffer(byte[] buffer, int offset, int count) + { + // we have data in the buffer; hand it back + if (_available < count) count = _available; + Buffer.BlockCopy(_buffer, _offset, buffer, offset, count); + _available -= count; + _offset += count; + return count; + } + + internal int Read(byte[] buffer, int offset, int count) + => _available > 0 + ? ReadFromBuffer(buffer, offset, count) + : ReadSlow(buffer, offset, count); + + private int ReadSlow(byte[] buffer, int offset, int count) + { + // if they're asking for more than we deal in, just step out of the way + if (count >= buffer.Length) + return _source.Read(buffer, offset, count); + + // they're asking for less, so we could still have some left + _available = _source.Read(_buffer, _offset = 0, _buffer.Length); + return _available > 0 ? ReadFromBuffer(buffer, offset, count) : 0; + } + } +} diff --git a/src/ServiceStack.Redis/BufferedStream.cs b/src/ServiceStack.Redis/BufferedStream.cs index 78b8c870..322e5573 100644 --- a/src/ServiceStack.Redis/BufferedStream.cs +++ b/src/ServiceStack.Redis/BufferedStream.cs @@ -5,6 +5,11 @@ namespace ServiceStack.Redis { + // recommendation: mark this obsolete as it is incomplete, and no longer used; + // I've marked it obsolete in DEBUG to be sure +#if DEBUG + [Obsolete("Prefer System.IO.BufferedStream")] +#endif public sealed class BufferedStream : Stream { Stream networkStream; diff --git a/src/ServiceStack.Redis/Generic/QueuedRedisTypedCommand.Async.cs b/src/ServiceStack.Redis/Generic/QueuedRedisTypedCommand.Async.cs new file mode 100644 index 00000000..0c75d760 --- /dev/null +++ b/src/ServiceStack.Redis/Generic/QueuedRedisTypedCommand.Async.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using ServiceStack.Redis.Generic; +using ServiceStack.Redis.Internal; +using ServiceStack.Redis.Pipeline; + +namespace ServiceStack.Redis.Generic +{ + /// + /// A complete redis command, with method to send command, receive response, and run callback on success or failure + /// + internal partial class QueuedRedisTypedCommand : QueuedRedisOperation + { + private Delegate _asyncReturnCommand; + partial void OnExecuteThrowIfAsync() + { + if (_asyncReturnCommand is object) + { + throw new InvalidOperationException("An async return command was present, but the queued operation is being processed synchronously"); + } + } + private QueuedRedisTypedCommand SetAsyncReturnCommand(Delegate value) + { + if (_asyncReturnCommand is object && _asyncReturnCommand != value) + throw new InvalidOperationException("Only a single async return command can be assigned"); + _asyncReturnCommand = value; + return this; + } + + internal QueuedRedisTypedCommand WithAsyncReturnCommand(Func, ValueTask> VoidReturnCommandAsync) + => SetAsyncReturnCommand(VoidReturnCommandAsync); + internal QueuedRedisTypedCommand WithAsyncReturnCommand(Func, ValueTask> IntReturnCommandAsync) + => SetAsyncReturnCommand(IntReturnCommandAsync); + internal QueuedRedisTypedCommand WithAsyncReturnCommand(Func, ValueTask> LongReturnCommandAsync) + => SetAsyncReturnCommand(LongReturnCommandAsync); + internal QueuedRedisTypedCommand WithAsyncReturnCommand(Func, ValueTask> BoolReturnCommandAsync) + => SetAsyncReturnCommand(BoolReturnCommandAsync); + internal QueuedRedisTypedCommand WithAsyncReturnCommand(Func, ValueTask> BytesReturnCommandAsync) + => SetAsyncReturnCommand(BytesReturnCommandAsync); + internal QueuedRedisTypedCommand WithAsyncReturnCommand(Func, ValueTask> MultiBytesReturnCommandAsync) + => SetAsyncReturnCommand(MultiBytesReturnCommandAsync); + internal QueuedRedisTypedCommand WithAsyncReturnCommand(Func, ValueTask> StringReturnCommandAsync) + => SetAsyncReturnCommand(StringReturnCommandAsync); + internal QueuedRedisTypedCommand WithAsyncReturnCommand(Func, ValueTask>> MultiStringReturnCommandAsync) + => SetAsyncReturnCommand(MultiStringReturnCommandAsync); + internal QueuedRedisTypedCommand WithAsyncReturnCommand(Func, ValueTask> DoubleReturnCommandAsync) + => SetAsyncReturnCommand(DoubleReturnCommandAsync); + internal QueuedRedisTypedCommand WithAsyncReturnCommand(Func, ValueTask>> MultiObjectReturnCommandAsync) + => SetAsyncReturnCommand(MultiObjectReturnCommandAsync); + internal QueuedRedisTypedCommand WithAsyncReturnCommand(Func, ValueTask> ObjectReturnCommandAsync) + => SetAsyncReturnCommand(ObjectReturnCommandAsync); + + public ValueTask ExecuteAsync(IRedisTypedClientAsync client) + { + try + { + switch (_asyncReturnCommand) + { + case null: + ExecuteThrowIfSync(); + return default; + case Func, ValueTask> VoidReturnCommandAsync: + return VoidReturnCommandAsync(client); + case Func, ValueTask> IntReturnCommandAsync: + return IntReturnCommandAsync(client).Await(); + case Func, ValueTask> LongReturnCommandAsync: + return LongReturnCommandAsync(client).Await(); + case Func, ValueTask> DoubleReturnCommandAsync: + return DoubleReturnCommandAsync(client).Await(); + case Func, ValueTask> BytesReturnCommandAsync: + return BytesReturnCommandAsync(client).Await(); + case Func, ValueTask> StringReturnCommandAsync: + return StringReturnCommandAsync(client).Await(); + case Func, ValueTask> MultiBytesReturnCommandAsync: + return MultiBytesReturnCommandAsync(client).Await(); + case Func, ValueTask>> MultiStringReturnCommandAsync: + return MultiStringReturnCommandAsync(client).Await(); + case object obj: + ExecuteThrowIfSync(); + return default; + } + } + catch (Exception ex) + { + Log.Error(ex); + return default; // non-async version swallows + } + } + + protected void ExecuteThrowIfSync() + { + if (VoidReturnCommand is object + || IntReturnCommand is object + || LongReturnCommand is object + || BoolReturnCommand is object + || BytesReturnCommand is object + || MultiBytesReturnCommand is object + || StringReturnCommand is object + || MultiStringReturnCommand is object + || DoubleReturnCommand is object + || MultiObjectReturnCommand is object + || ObjectReturnCommand is object) + { + throw new InvalidOperationException("A sync return command was present, but the queued operation is being processed asynchronously"); + } + } + + } +} diff --git a/src/ServiceStack.Redis/Generic/QueuedRedisTypedCommand.cs b/src/ServiceStack.Redis/Generic/QueuedRedisTypedCommand.cs index ca8b2870..fe9c8b0b 100644 --- a/src/ServiceStack.Redis/Generic/QueuedRedisTypedCommand.cs +++ b/src/ServiceStack.Redis/Generic/QueuedRedisTypedCommand.cs @@ -1,14 +1,14 @@ -using System; +using ServiceStack.Redis.Pipeline; +using System; using System.Collections.Generic; -using ServiceStack.Redis.Generic; -using ServiceStack.Redis.Pipeline; +using System.Threading.Tasks; namespace ServiceStack.Redis.Generic { /// /// A complete redis command, with method to send command, receive response, and run callback on success or failure /// - internal class QueuedRedisTypedCommand : QueuedRedisOperation + internal partial class QueuedRedisTypedCommand : QueuedRedisOperation { public Action> VoidReturnCommand { get; set; } @@ -74,5 +74,7 @@ public void Execute(IRedisTypedClient client) } } + private void ExecuteThrowIfAsync() => OnExecuteThrowIfAsync(); + partial void OnExecuteThrowIfAsync(); } } diff --git a/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs new file mode 100644 index 00000000..1a214151 --- /dev/null +++ b/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs @@ -0,0 +1,55 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Redis.Internal; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Generic +{ + internal partial class RedisClientHash + : IRedisHashAsync + { + IRedisTypedClientAsync AsyncClient => client; + + ValueTask IRedisHashAsync.AddAsync(KeyValuePair item, CancellationToken cancellationToken) + => AsyncClient.SetEntryInHashAsync(this, item.Key, item.Value, cancellationToken).Await(); + + ValueTask IRedisHashAsync.AddAsync(TKey key, T value, CancellationToken cancellationToken) + => AsyncClient.SetEntryInHashAsync(this, key, value, cancellationToken).Await(); + + ValueTask IRedisHashAsync.ClearAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveEntryAsync(new[] { this }, cancellationToken).Await(); + + ValueTask IRedisHashAsync.ContainsKeyAsync(TKey key, CancellationToken cancellationToken) + => AsyncClient.HashContainsEntryAsync(this, key, cancellationToken); + + ValueTask IRedisHashAsync.CountAsync(CancellationToken cancellationToken) + => AsyncClient.GetHashCountAsync(this, cancellationToken).AsInt32(); + + ValueTask> IRedisHashAsync.GetAllAsync(CancellationToken cancellationToken) + => AsyncClient.GetAllEntriesFromHashAsync(this, cancellationToken); + + async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) + { + var all = await AsyncClient.GetAllEntriesFromHashAsync(this, cancellationToken).ConfigureAwait(false); + foreach (var pair in all) + { + yield return pair; + } + } + + ValueTask IRedisHashAsync.RemoveAsync(TKey key, CancellationToken cancellationToken) + => AsyncClient.RemoveEntryFromHashAsync(this, key, cancellationToken); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.cs b/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.cs index f128c895..795e14fa 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.cs @@ -19,7 +19,7 @@ namespace ServiceStack.Redis.Generic /// /// Wrap the common redis set operations under a ICollection[string] interface. /// - internal class RedisClientHash + internal partial class RedisClientHash : IRedisHash { private readonly RedisTypedClient client; diff --git a/src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs new file mode 100644 index 00000000..bf779689 --- /dev/null +++ b/src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs @@ -0,0 +1,184 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Redis.Internal; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Generic +{ + internal partial class RedisClientList + : IRedisListAsync + { + IRedisTypedClientAsync AsyncClient => client; + IRedisListAsync AsAsync() => this; + + async ValueTask IRedisListAsync.AddRangeAsync(IEnumerable values, CancellationToken cancellationToken) + { + //TODO: replace it with a pipeline implementation ala AddRangeToSet + foreach (var value in values) + { + await AsyncClient.AddItemToListAsync(this, value, cancellationToken).ConfigureAwait(false); + } + } + + ValueTask IRedisListAsync.AppendAsync(T value, CancellationToken cancellationToken) + => AsyncClient.AddItemToListAsync(this, value, cancellationToken); + + ValueTask IRedisListAsync.BlockingDequeueAsync(TimeSpan? timeOut, CancellationToken cancellationToken) + => AsyncClient.BlockingDequeueItemFromListAsync(this, timeOut, cancellationToken); + + ValueTask IRedisListAsync.BlockingPopAsync(TimeSpan? timeOut, CancellationToken cancellationToken) + => AsyncClient.BlockingPopItemFromListAsync(this, timeOut, cancellationToken); + + ValueTask IRedisListAsync.BlockingRemoveStartAsync(TimeSpan? timeOut, CancellationToken cancellationToken) + => AsyncClient.BlockingRemoveStartFromListAsync(this, timeOut, cancellationToken); + + ValueTask IRedisListAsync.CountAsync(CancellationToken cancellationToken) + => AsyncClient.GetListCountAsync(this, cancellationToken).AsInt32(); + + ValueTask IRedisListAsync.DequeueAsync(CancellationToken cancellationToken) + => AsyncClient.DequeueItemFromListAsync(this, cancellationToken); + + ValueTask IRedisListAsync.EnqueueAsync(T value, CancellationToken cancellationToken) + => AsyncClient.EnqueueItemOnListAsync(this, value, cancellationToken); + + ValueTask> IRedisListAsync.GetAllAsync(CancellationToken cancellationToken) + => AsyncClient.GetAllItemsFromListAsync(this, cancellationToken); + + async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) + { + var count = await AsAsync().CountAsync(cancellationToken).ConfigureAwait(false); + if (count <= PageLimit) + { + var all = await AsyncClient.GetAllItemsFromListAsync(this, cancellationToken).ConfigureAwait(false); + foreach (var item in all) + { + yield return item; + } + } + else + { + // from GetPagingEnumerator() + var skip = 0; + List pageResults; + do + { + pageResults = await AsyncClient.GetRangeFromListAsync(this, skip, PageLimit, cancellationToken).ConfigureAwait(false); + foreach (var result in pageResults) + { + yield return result; + } + skip += PageLimit; + } while (pageResults.Count == PageLimit); + } + } + + ValueTask> IRedisListAsync.GetRangeAsync(int startingFrom, int endingAt, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromListAsync(this, startingFrom, endingAt, cancellationToken); + + ValueTask> IRedisListAsync.GetRangeFromSortedListAsync(int startingFrom, int endingAt, CancellationToken cancellationToken) + => AsyncClient.SortListAsync(this, startingFrom, endingAt, cancellationToken); + + ValueTask IRedisListAsync.PopAndPushAsync(IRedisListAsync toList, CancellationToken cancellationToken) + => AsyncClient.PopAndPushItemBetweenListsAsync(this, toList, cancellationToken); + + ValueTask IRedisListAsync.PopAsync(CancellationToken cancellationToken) + => AsyncClient.PopItemFromListAsync(this, cancellationToken); + + ValueTask IRedisListAsync.PrependAsync(T value, CancellationToken cancellationToken) + => AsyncClient.PrependItemToListAsync(this, value, cancellationToken); + + ValueTask IRedisListAsync.PushAsync(T value, CancellationToken cancellationToken) + => AsyncClient.PushItemToListAsync(this, value, cancellationToken); + + ValueTask IRedisListAsync.RemoveAllAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveAllFromListAsync(this, cancellationToken); + + ValueTask IRedisListAsync.RemoveEndAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveEndFromListAsync(this, cancellationToken); + + ValueTask IRedisListAsync.RemoveStartAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveStartFromListAsync(this, cancellationToken); + + ValueTask IRedisListAsync.RemoveValueAsync(T value, CancellationToken cancellationToken) + => AsyncClient.RemoveItemFromListAsync(this, value, cancellationToken); + + ValueTask IRedisListAsync.RemoveValueAsync(T value, int noOfMatches, CancellationToken cancellationToken) + => AsyncClient.RemoveItemFromListAsync(this, value, noOfMatches, cancellationToken); + + ValueTask IRedisListAsync.TrimAsync(int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken) + => AsyncClient.TrimListAsync(this, keepStartingFrom, keepEndingAt, cancellationToken); + + async ValueTask IRedisListAsync.RemoveAsync(T value, CancellationToken cancellationToken) + { + var index = await AsAsync().IndexOfAsync(value, cancellationToken).ConfigureAwait(false); + if (index != -1) + { + await AsAsync().RemoveAtAsync(index, cancellationToken).ConfigureAwait(false); + return true; + } + return false; + } + + ValueTask IRedisListAsync.AddAsync(T value, CancellationToken cancellationToken) + => AsyncClient.AddItemToListAsync(this, value, cancellationToken); + + async ValueTask IRedisListAsync.RemoveAtAsync(int index, CancellationToken cancellationToken) + { + //TODO: replace with native implementation when one exists + + var nativeClient = client.NativeClient as IRedisNativeClientAsync ?? throw new NotSupportedException( + $"The native client ('{client.NativeClient.GetType().Name}') does not implement {nameof(IRedisNativeClientAsync)}"); + + var markForDelete = Guid.NewGuid().ToString(); + await nativeClient.LSetAsync(listId, index, Encoding.UTF8.GetBytes(markForDelete), cancellationToken).ConfigureAwait(false); + + const int removeAll = 0; + await nativeClient.LRemAsync(listId, removeAll, Encoding.UTF8.GetBytes(markForDelete), cancellationToken).ConfigureAwait(false); + } + + async ValueTask IRedisListAsync.ContainsAsync(T value, CancellationToken cancellationToken) + { + //TODO: replace with native implementation when exists + await foreach (var existingItem in this.ConfigureAwait(false).WithCancellation(cancellationToken)) + { + if (Equals(existingItem, value)) return true; + } + return false; + } + + ValueTask IRedisListAsync.ClearAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveAllFromListAsync(this, cancellationToken); + + async ValueTask IRedisListAsync.IndexOfAsync(T value, CancellationToken cancellationToken) + { + //TODO: replace with native implementation when exists + var i = 0; + await foreach (var existingItem in this.ConfigureAwait(false).WithCancellation(cancellationToken)) + { + if (Equals(existingItem, value)) return i; + i++; + } + return -1; + } + + ValueTask IRedisListAsync.ElementAtAsync(int index, CancellationToken cancellationToken) + => AsyncClient.GetItemFromListAsync(this, index, cancellationToken); + + ValueTask IRedisListAsync.SetValueAsync(int index, T value, CancellationToken cancellationToken) + => AsyncClient.SetItemInListAsync(this, index, value, cancellationToken); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisClientList.Generic.cs b/src/ServiceStack.Redis/Generic/RedisClientList.Generic.cs index 41eb1ae9..9ad4222f 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientList.Generic.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientList.Generic.cs @@ -17,7 +17,7 @@ namespace ServiceStack.Redis.Generic { - internal class RedisClientList + internal partial class RedisClientList : IRedisList { private readonly RedisTypedClient client; diff --git a/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs new file mode 100644 index 00000000..e8080193 --- /dev/null +++ b/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs @@ -0,0 +1,109 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Redis.Internal; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Generic +{ + internal partial class RedisClientSet + : IRedisSetAsync + { + IRedisTypedClientAsync AsyncClient => client; + + ValueTask IRedisSetAsync.AddAsync(T value, CancellationToken cancellationToken) + => AsyncClient.AddItemToSetAsync(this, value, cancellationToken); + + IRedisSetAsync AsAsync() => this; + + ValueTask IRedisSetAsync.ClearAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveEntryAsync(setId, cancellationToken).Await(); + + ValueTask IRedisSetAsync.ContainsAsync(T item, CancellationToken cancellationToken) + => AsyncClient.SetContainsItemAsync(this, item, cancellationToken); + + ValueTask IRedisSetAsync.CountAsync(CancellationToken cancellationToken) + => AsyncClient.GetSetCountAsync(this, cancellationToken).AsInt32(); + + ValueTask> IRedisSetAsync.GetAllAsync(CancellationToken cancellationToken) + => AsyncClient.GetAllItemsFromSetAsync(this, cancellationToken); + + async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) + { + var count = await AsAsync().CountAsync(cancellationToken).ConfigureAwait(false); + if (count <= PageLimit) + { + var all = await AsyncClient.GetAllItemsFromSetAsync(this, cancellationToken).ConfigureAwait(false); + foreach (var item in all) + { + yield return item; + } + } + else + { + // from GetPagingEnumerator + var skip = 0; + List pageResults; + do + { + pageResults = await AsyncClient.GetSortedEntryValuesAsync(this, skip, skip + PageLimit - 1).ConfigureAwait(false); + foreach (var result in pageResults) + { + yield return result; + } + skip += PageLimit; + } while (pageResults.Count == PageLimit); + } + } + + ValueTask IRedisSetAsync.GetDifferencesAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken) + => AsyncClient.StoreUnionFromSetsAsync(this, withSets, cancellationToken); + + ValueTask IRedisSetAsync.GetDifferencesAsync(params IRedisSetAsync[] withSets) + => AsAsync().GetDifferencesAsync(withSets, cancellationToken: default); + + ValueTask IRedisSetAsync.GetRandomItemAsync(CancellationToken cancellationToken) + => AsyncClient.GetRandomItemFromSetAsync(this, cancellationToken); + + ValueTask IRedisSetAsync.MoveToAsync(T item, IRedisSetAsync toSet, CancellationToken cancellationToken) + => AsyncClient.MoveBetweenSetsAsync(this, toSet, item, cancellationToken); + + ValueTask IRedisSetAsync.PopRandomItemAsync(CancellationToken cancellationToken) + => AsyncClient.PopItemFromSetAsync(this, cancellationToken); + + ValueTask IRedisSetAsync.PopulateWithDifferencesOfAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken) + => AsyncClient.StoreDifferencesFromSetAsync(this, fromSet, withSets, cancellationToken); + + ValueTask IRedisSetAsync.PopulateWithDifferencesOfAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets) + => AsAsync().PopulateWithDifferencesOfAsync(fromSet, withSets, cancellationToken: default); + + ValueTask IRedisSetAsync.PopulateWithIntersectOfAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken) + => AsyncClient.StoreIntersectFromSetsAsync(this, sets, cancellationToken); + + ValueTask IRedisSetAsync.PopulateWithIntersectOfAsync(params IRedisSetAsync[] sets) + => AsAsync().PopulateWithIntersectOfAsync(sets, cancellationToken: default); + + ValueTask IRedisSetAsync.PopulateWithUnionOfAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken) + => AsyncClient.StoreUnionFromSetsAsync(this, sets, cancellationToken); + + ValueTask IRedisSetAsync.PopulateWithUnionOfAsync(params IRedisSetAsync[] sets) + => AsAsync().PopulateWithUnionOfAsync(sets, cancellationToken: default); + + ValueTask IRedisSetAsync.RemoveAsync(T value, CancellationToken cancellationToken) + => AsyncClient.RemoveItemFromSetAsync(this, value, cancellationToken).AwaitAsTrue(); // see Remove for why "true" + + ValueTask> IRedisSetAsync.SortAsync(int startingFrom, int endingAt, CancellationToken cancellationToken) + => AsyncClient.GetSortedEntryValuesAsync(this, startingFrom, endingAt, cancellationToken); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.cs b/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.cs index c6a2514a..2ac86c8f 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.cs @@ -19,7 +19,7 @@ namespace ServiceStack.Redis.Generic /// /// Wrap the common redis set operations under a ICollection[string] interface. /// - internal class RedisClientSet + internal partial class RedisClientSet : IRedisSet { private readonly RedisTypedClient client; diff --git a/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs new file mode 100644 index 00000000..4e5d2cfe --- /dev/null +++ b/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs @@ -0,0 +1,136 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Redis.Internal; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Generic +{ + internal partial class RedisClientSortedSet + : IRedisSortedSetAsync + { + IRedisTypedClientAsync AsyncClient => client; + + IRedisSortedSetAsync AsAsync() => this; + + ValueTask IRedisSortedSetAsync.AddAsync(T item, double score, CancellationToken cancellationToken) + => AsyncClient.AddItemToSortedSetAsync(this, item, score); + + ValueTask IRedisSortedSetAsync.CountAsync(CancellationToken cancellationToken) + => AsyncClient.GetSortedSetCountAsync(this, cancellationToken).AsInt32(); + + ValueTask> IRedisSortedSetAsync.GetAllAsync(CancellationToken cancellationToken) + => AsyncClient.GetAllItemsFromSortedSetAsync(this); + + ValueTask> IRedisSortedSetAsync.GetAllDescendingAsync(CancellationToken cancellationToken) + => AsyncClient.GetAllItemsFromSortedSetDescAsync(this); + + async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) + { + var count = await AsAsync().CountAsync(cancellationToken).ConfigureAwait(false); + if (count <= PageLimit) + { + var all = await AsyncClient.GetAllItemsFromSortedSetAsync(this, cancellationToken).ConfigureAwait(false); + foreach (var item in all) + { + yield return item; + } + } + else + { + // from GetPagingEnumerator(); + var skip = 0; + List pageResults; + do + { + pageResults = await AsyncClient.GetRangeFromSortedSetAsync(this, skip, skip + PageLimit - 1, cancellationToken).ConfigureAwait(false); + foreach (var result in pageResults) + { + yield return result; + } + skip += PageLimit; + } while (pageResults.Count == PageLimit); + } + } + + ValueTask IRedisSortedSetAsync.GetItemScoreAsync(T item, CancellationToken cancellationToken) + => AsyncClient.GetItemScoreInSortedSetAsync(this, item, cancellationToken); + + ValueTask> IRedisSortedSetAsync.GetRangeAsync(int fromRank, int toRank, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetAsync(this, fromRank, toRank, cancellationToken); + + ValueTask> IRedisSortedSetAsync.GetRangeByHighestScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(this, fromScore, toScore, cancellationToken); + + ValueTask> IRedisSortedSetAsync.GetRangeByHighestScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(this, fromScore, toScore, skip, take, cancellationToken); + + ValueTask> IRedisSortedSetAsync.GetRangeByLowestScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(this, fromScore, toScore, cancellationToken); + + ValueTask> IRedisSortedSetAsync.GetRangeByLowestScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(this, fromScore, toScore, skip, take, cancellationToken); + + ValueTask IRedisSortedSetAsync.IncrementItemAsync(T item, double incrementBy, CancellationToken cancellationToken) + => AsyncClient.IncrementItemInSortedSetAsync(this, item, incrementBy, cancellationToken); + + ValueTask IRedisSortedSetAsync.IndexOfAsync(T item, CancellationToken cancellationToken) + => AsyncClient.GetItemIndexInSortedSetAsync(this, item, cancellationToken).AsInt32(); + + ValueTask IRedisSortedSetAsync.IndexOfDescendingAsync(T item, CancellationToken cancellationToken) + => AsyncClient.GetItemIndexInSortedSetDescAsync(this, item, cancellationToken); + + ValueTask IRedisSortedSetAsync.PopItemWithHighestScoreAsync(CancellationToken cancellationToken) + => AsyncClient.PopItemWithHighestScoreFromSortedSetAsync(this, cancellationToken); + + ValueTask IRedisSortedSetAsync.PopItemWithLowestScoreAsync(CancellationToken cancellationToken) + => AsyncClient.PopItemWithLowestScoreFromSortedSetAsync(this, cancellationToken); + + ValueTask IRedisSortedSetAsync.PopulateWithIntersectOfAsync(IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken) + => AsyncClient.StoreIntersectFromSortedSetsAsync(this, setIds, cancellationToken); + + ValueTask IRedisSortedSetAsync.PopulateWithIntersectOfAsync(IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken) + => AsyncClient.StoreIntersectFromSortedSetsAsync(this, setIds, args, cancellationToken); + + ValueTask IRedisSortedSetAsync.PopulateWithUnionOfAsync(IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken) + => AsyncClient.StoreUnionFromSortedSetsAsync(this, setIds, cancellationToken); + + ValueTask IRedisSortedSetAsync.PopulateWithUnionOfAsync(IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken) + => AsyncClient.StoreUnionFromSortedSetsAsync(this, setIds, args, cancellationToken); + + ValueTask IRedisSortedSetAsync.RemoveRangeAsync(int minRank, int maxRank, CancellationToken cancellationToken) + => AsyncClient.RemoveRangeFromSortedSetAsync(this, minRank, maxRank, cancellationToken); + + ValueTask IRedisSortedSetAsync.RemoveRangeByScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken) + => AsyncClient.RemoveRangeFromSortedSetByScoreAsync(this, fromScore, toScore, cancellationToken); + + ValueTask IRedisSortedSetAsync.ClearAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveEntryAsync(setId, cancellationToken).Await(); + + ValueTask IRedisSortedSetAsync.ContainsAsync(T value, CancellationToken cancellationToken) + => AsyncClient.SortedSetContainsItemAsync(this, value, cancellationToken); + + ValueTask IRedisSortedSetAsync.AddAsync(T value, CancellationToken cancellationToken) + => AsyncClient.AddItemToSortedSetAsync(this, value, cancellationToken); + + ValueTask IRedisSortedSetAsync.RemoveAsync(T value, CancellationToken cancellationToken) + => AsyncClient.RemoveItemFromSortedSetAsync(this, value, cancellationToken).AwaitAsTrue(); // see Remove for why "true" + + ValueTask IRedisSortedSetAsync.PopulateWithIntersectOfAsync(params IRedisSortedSetAsync[] setIds) + => AsAsync().PopulateWithIntersectOfAsync(setIds, cancellationToken: default); + + ValueTask IRedisSortedSetAsync.PopulateWithUnionOfAsync(params IRedisSortedSetAsync[] setIds) + => AsAsync().PopulateWithUnionOfAsync(setIds, cancellationToken: default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.cs b/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.cs index bda2f1eb..e7f53937 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.cs @@ -19,7 +19,7 @@ namespace ServiceStack.Redis.Generic /// /// Wrap the common redis set operations under a ICollection[string] interface. /// - internal class RedisClientSortedSet + internal partial class RedisClientSortedSet : IRedisSortedSet { private readonly RedisTypedClient client; diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs new file mode 100644 index 00000000..a6388731 --- /dev/null +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs @@ -0,0 +1,755 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Data; +using ServiceStack.Model; +using ServiceStack.Redis.Internal; +using ServiceStack.Text; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Generic +{ + partial class RedisTypedClient + : IRedisTypedClientAsync + { + public IRedisTypedClientAsync AsAsync() => this; + + private IRedisClientAsync AsyncClient => client; + private IRedisNativeClientAsync AsyncNative => client; + + IRedisSetAsync IRedisTypedClientAsync.TypeIdsSet => TypeIdsSetRaw; + + IRedisClientAsync IRedisTypedClientAsync.RedisClient => client; + + internal ValueTask ExpectQueuedAsync(CancellationToken cancellationToken) + => client.ExpectQueuedAsync(cancellationToken); + + internal ValueTask ExpectOkAsync(CancellationToken cancellationToken) + => client.ExpectOkAsync(cancellationToken); + + internal ValueTask ReadMultiDataResultCountAsync(CancellationToken cancellationToken) + => client.ReadMultiDataResultCountAsync(cancellationToken); + + ValueTask IRedisTypedClientAsync.GetValueAsync(string key, CancellationToken cancellationToken) + => DeserializeValueAsync(AsyncNative.GetAsync(key, cancellationToken)); + + async ValueTask IRedisTypedClientAsync.SetValueAsync(string key, T entity, CancellationToken cancellationToken) + { + AssertNotNull(key); + await AsyncClient.SetAsync(key, SerializeValue(entity), cancellationToken).ConfigureAwait(false); + await client.RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); + } + + ValueTask IEntityStoreAsync.GetByIdAsync(object id, CancellationToken cancellationToken) + { + var key = client.UrnKey(id); + return AsAsync().GetValueAsync(key, cancellationToken); + } + + internal ValueTask FlushSendBufferAsync(CancellationToken cancellationToken) + => client.FlushSendBufferAsync(cancellationToken); + + internal ValueTask AddTypeIdsRegisteredDuringPipelineAsync(CancellationToken cancellationToken) + => client.AddTypeIdsRegisteredDuringPipelineAsync(cancellationToken); + + async ValueTask> IEntityStoreAsync.GetByIdsAsync(IEnumerable ids, CancellationToken cancellationToken) + { + if (ids != null) + { + var urnKeys = ids.Map(x => client.UrnKey(x)); + if (urnKeys.Count != 0) + return await AsAsync().GetValuesAsync(urnKeys, cancellationToken).ConfigureAwait(false); + } + + return new List(); + } + + async ValueTask> IEntityStoreAsync.GetAllAsync(CancellationToken cancellationToken) + { + var allKeys = await AsyncClient.GetAllItemsFromSetAsync(this.TypeIdsSetKey, cancellationToken).ConfigureAwait(false); + return await AsAsync().GetByIdsAsync(allKeys.ToArray(), cancellationToken).ConfigureAwait(false); + } + + async ValueTask IEntityStoreAsync.StoreAsync(T entity, CancellationToken cancellationToken) + { + var urnKey = client.UrnKey(entity); + await AsAsync().SetValueAsync(urnKey, entity, cancellationToken).ConfigureAwait(false); + return entity; + } + + async ValueTask IEntityStoreAsync.StoreAllAsync(IEnumerable entities, CancellationToken cancellationToken) + { + if (PrepareStoreAll(entities, out var keys, out var values, out var entitiesList)) + { + await AsyncNative.MSetAsync(keys, values, cancellationToken).ConfigureAwait(false); + await client.RegisterTypeIdsAsync(entitiesList, cancellationToken).ConfigureAwait(false); + } + } + + async ValueTask IEntityStoreAsync.DeleteAsync(T entity, CancellationToken cancellationToken) + { + var urnKey = client.UrnKey(entity); + await AsyncClient.RemoveEntryAsync(new[] { urnKey }, cancellationToken).ConfigureAwait(false); + await client.RemoveTypeIdsAsync(new[] { entity }, cancellationToken).ConfigureAwait(false); + } + + async ValueTask IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken cancellationToken) + { + var urnKey = client.UrnKey(id); + + await AsyncClient.RemoveEntryAsync(new[] { urnKey }, cancellationToken).ConfigureAwait(false); + await client.RemoveTypeIdsAsync(new[] { id.ToString() }, cancellationToken).ConfigureAwait(false); + } + + async ValueTask IEntityStoreAsync.DeleteByIdsAsync(IEnumerable ids, CancellationToken cancellationToken) + { + if (ids == null) return; + + var urnKeys = ids.Map(t => client.UrnKey(t)); + if (urnKeys.Count > 0) + { + await AsyncClient.RemoveEntryAsync(urnKeys.ToArray(), cancellationToken).ConfigureAwait(false); + await client.RemoveTypeIdsAsync(ids.Map(x => x.ToString()).ToArray(), cancellationToken).ConfigureAwait(false); + } + } + + async ValueTask IEntityStoreAsync.DeleteAllAsync(CancellationToken cancellationToken) + { + var ids = await AsyncClient.GetAllItemsFromSetAsync(this.TypeIdsSetKey, cancellationToken).ConfigureAwait(false); + var urnKeys = ids.Map(t => client.UrnKey(t)); + if (urnKeys.Count > 0) + { + await AsyncClient.RemoveEntryAsync(urnKeys.ToArray(), cancellationToken).ConfigureAwait(false); + await AsyncClient.RemoveEntryAsync(new[] { this.TypeIdsSetKey }, cancellationToken).ConfigureAwait(false); + } + } + + async ValueTask> IRedisTypedClientAsync.GetValuesAsync(List keys, CancellationToken cancellationToken) + { + if (keys.IsNullOrEmpty()) return new List(); + + var resultBytesArray = await AsyncNative.MGetAsync(keys.ToArray(), cancellationToken).ConfigureAwait(false); + return ProcessGetValues(resultBytesArray); + } + + ValueTask> IRedisTypedClientAsync.CreateTransactionAsync(CancellationToken cancellationToken) + { + IRedisTypedTransactionAsync obj = new RedisTypedTransaction(this, true); + return obj.AsValueTask(); + } + + IRedisTypedPipelineAsync IRedisTypedClientAsync.CreatePipeline() + => new RedisTypedPipeline(this); + + + ValueTask IRedisTypedClientAsync.AcquireLockAsync(TimeSpan? timeOut, CancellationToken cancellationToken) + => AsyncClient.AcquireLockAsync(this.TypeLockKey, timeOut, cancellationToken); + + long IRedisTypedClientAsync.Db => AsyncClient.Db; + + IHasNamed> IRedisTypedClientAsync.Lists => Lists as IHasNamed> ?? throw new NotSupportedException("The provided Lists does not support IRedisListAsync"); + IHasNamed> IRedisTypedClientAsync.Sets => Sets as IHasNamed> ?? throw new NotSupportedException("The provided Sets does not support IRedisSetAsync"); + IHasNamed> IRedisTypedClientAsync.SortedSets => SortedSets as IHasNamed> ?? throw new NotSupportedException("The provided SortedSets does not support IRedisSortedSetAsync"); + + IRedisHashAsync IRedisTypedClientAsync.GetHash(string hashId) => GetHash(hashId) as IRedisHashAsync ?? throw new NotSupportedException("The provided Hash does not support IRedisHashAsync"); + + ValueTask IRedisTypedClientAsync.SelectAsync(long db, CancellationToken cancellationToken) + => AsyncClient.SelectAsync(db, cancellationToken); + + ValueTask> IRedisTypedClientAsync.GetAllKeysAsync(CancellationToken cancellationToken) + => AsyncClient.GetAllKeysAsync(cancellationToken); + + ValueTask IRedisTypedClientAsync.SetSequenceAsync(int value, CancellationToken cancellationToken) + => AsyncNative.GetSetAsync(SequenceKey, Encoding.UTF8.GetBytes(value.ToString()), cancellationToken).Await(); + + ValueTask IRedisTypedClientAsync.GetNextSequenceAsync(CancellationToken cancellationToken) + => AsAsync().IncrementValueAsync(SequenceKey, cancellationToken); + + ValueTask IRedisTypedClientAsync.GetNextSequenceAsync(int incrBy, CancellationToken cancellationToken) + => AsAsync().IncrementValueByAsync(SequenceKey, incrBy, cancellationToken); + + ValueTask IRedisTypedClientAsync.GetEntryTypeAsync(string key, CancellationToken cancellationToken) + => AsyncClient.GetEntryTypeAsync(key, cancellationToken); + + ValueTask IRedisTypedClientAsync.GetRandomKeyAsync(CancellationToken cancellationToken) + => AsyncClient.GetRandomKeyAsync(cancellationToken); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static void AssertNotNull(object obj, string name = "key") + { + if (obj is null) Throw(name); + static void Throw(string name) => throw new ArgumentNullException(name); + } + + async ValueTask IRedisTypedClientAsync.SetValueAsync(string key, T entity, TimeSpan expireIn, CancellationToken cancellationToken) + { + AssertNotNull(key); + await AsyncClient.SetAsync(key, SerializeValue(entity)).ConfigureAwait(false); + await client.RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); + } + + async ValueTask IRedisTypedClientAsync.SetValueIfNotExistsAsync(string key, T entity, CancellationToken cancellationToken) + { + var success = await AsyncNative.SetNXAsync(key, SerializeValue(entity)).IsSuccessAsync().ConfigureAwait(false); + if (success) await client.RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); + return success; + } + + async ValueTask IRedisTypedClientAsync.SetValueIfExistsAsync(string key, T entity, CancellationToken cancellationToken) + { + var success = await AsyncNative.SetAsync(key, SerializeValue(entity), exists: true).ConfigureAwait(false); + if (success) await client.RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); + return success; + } + + async ValueTask IRedisTypedClientAsync.StoreAsync(T entity, TimeSpan expireIn, CancellationToken cancellationToken) + { + var urnKey = client.UrnKey(entity); + await AsAsync().SetValueAsync(urnKey, entity, cancellationToken).ConfigureAwait(false); + return entity; + } + + ValueTask IRedisTypedClientAsync.GetAndSetValueAsync(string key, T value, CancellationToken cancellationToken) + => DeserializeValueAsync(AsyncNative.GetSetAsync(key, SerializeValue(value), cancellationToken)); + + ValueTask IRedisTypedClientAsync.ContainsKeyAsync(string key, CancellationToken cancellationToken) + => AsyncNative.ExistsAsync(key, cancellationToken).IsSuccessAsync(); + + ValueTask IRedisTypedClientAsync.RemoveEntryAsync(string key, CancellationToken cancellationToken) + => AsyncNative.DelAsync(key, cancellationToken).IsSuccessAsync(); + + ValueTask IRedisTypedClientAsync.RemoveEntryAsync(string[] keys, CancellationToken cancellationToken) + => AsyncNative.DelAsync(keys, cancellationToken).IsSuccessAsync(); + + async ValueTask IRedisTypedClientAsync.RemoveEntryAsync(IHasStringId[] entities, CancellationToken cancellationToken) + { + var ids = entities.Map(x => x.Id); + var success = await AsyncNative.DelAsync(ids.ToArray(), cancellationToken).IsSuccessAsync().ConfigureAwait(false); + if (success) await client.RemoveTypeIdsAsync(ids.ToArray(), cancellationToken).ConfigureAwait(false); + return success; + } + + ValueTask IRedisTypedClientAsync.IncrementValueAsync(string key, CancellationToken cancellationToken) + => AsyncNative.IncrAsync(key, cancellationToken); + + ValueTask IRedisTypedClientAsync.IncrementValueByAsync(string key, int count, CancellationToken cancellationToken) + => AsyncNative.IncrByAsync(key, count, cancellationToken); + + ValueTask IRedisTypedClientAsync.DecrementValueAsync(string key, CancellationToken cancellationToken) + => AsyncNative.DecrAsync(key, cancellationToken); + + ValueTask IRedisTypedClientAsync.DecrementValueByAsync(string key, int count, CancellationToken cancellationToken) + => AsyncNative.DecrByAsync(key, count, cancellationToken); + + ValueTask IRedisTypedClientAsync.ExpireInAsync(object id, TimeSpan expiresIn, CancellationToken cancellationToken) + { + var key = client.UrnKey(id); + return AsyncClient.ExpireEntryInAsync(key, expiresIn, cancellationToken); + } + + ValueTask IRedisTypedClientAsync.ExpireAtAsync(object id, DateTime expireAt, CancellationToken cancellationToken) + { + var key = client.UrnKey(id); + return AsyncClient.ExpireEntryAtAsync(key, expireAt, cancellationToken); + } + + ValueTask IRedisTypedClientAsync.ExpireEntryInAsync(string key, TimeSpan expireIn, CancellationToken cancellationToken) + => AsyncClient.ExpireEntryInAsync(key, expireIn, cancellationToken); + + ValueTask IRedisTypedClientAsync.ExpireEntryAtAsync(string key, DateTime expireAt, CancellationToken cancellationToken) + => AsyncClient.ExpireEntryAtAsync(key, expireAt, cancellationToken); + + async ValueTask IRedisTypedClientAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) + => TimeSpan.FromSeconds(await AsyncNative.TtlAsync(key, cancellationToken).ConfigureAwait(false)); + + ValueTask IRedisTypedClientAsync.ForegroundSaveAsync(CancellationToken cancellationToken) + => AsyncClient.ForegroundSaveAsync(cancellationToken); + + ValueTask IRedisTypedClientAsync.BackgroundSaveAsync(CancellationToken cancellationToken) + => AsyncClient.BackgroundSaveAsync(cancellationToken); + + ValueTask IRedisTypedClientAsync.FlushDbAsync(CancellationToken cancellationToken) + => AsyncClient.FlushDbAsync(cancellationToken); + + ValueTask IRedisTypedClientAsync.FlushAllAsync(CancellationToken cancellationToken) + => AsyncClient.FlushAllAsync(cancellationToken); + + async ValueTask IRedisTypedClientAsync.SearchKeysAsync(string pattern, CancellationToken cancellationToken) + { + var strKeys = await AsyncClient.SearchKeysAsync(pattern, cancellationToken).ConfigureAwait(false); + return SearchKeysParse(strKeys); + } + + private ValueTask> CreateList(ValueTask pending) + { + return pending.IsCompletedSuccessfully ? CreateList(pending.Result).AsValueTask() : Awaited(this, pending); + static async ValueTask> Awaited(RedisTypedClient obj, ValueTask pending) + => obj.CreateList(await pending.ConfigureAwait(false)); + } + private ValueTask DeserializeValueAsync(ValueTask pending) + { + return pending.IsCompletedSuccessfully ? DeserializeValue(pending.Result).AsValueTask() : Awaited(this, pending); + static async ValueTask Awaited(RedisTypedClient obj, ValueTask pending) + => obj.DeserializeValue(await pending.ConfigureAwait(false)); + } + + private static ValueTask DeserializeFromStringAsync(ValueTask pending) + { + return pending.IsCompletedSuccessfully ? DeserializeFromString(pending.Result).AsValueTask() : Awaited(pending); + static async ValueTask Awaited(ValueTask pending) + => DeserializeFromString(await pending.ConfigureAwait(false)); + } + + private static ValueTask> CreateGenericMapAsync(ValueTask> pending) + { + return pending.IsCompletedSuccessfully ? CreateGenericMap(pending.Result).AsValueTask() : Awaited(pending); + static async ValueTask> Awaited(ValueTask> pending) + => CreateGenericMap(await pending.ConfigureAwait(false)); + } + + private static ValueTask> ConvertEachToAsync(ValueTask> pending) + { + return pending.IsCompletedSuccessfully ? ConvertEachTo(pending.Result).AsValueTask() : Awaited(pending); + static async ValueTask> Awaited(ValueTask> pending) + => ConvertEachTo(await pending.ConfigureAwait(false)); + } + + ValueTask> IRedisTypedClientAsync.GetSortedEntryValuesAsync(IRedisSetAsync fromSet, int startingFrom, int endingAt, CancellationToken cancellationToken) + { + var sortOptions = new SortOptions { Skip = startingFrom, Take = endingAt, }; + var multiDataList = AsyncNative.SortAsync(fromSet.Id, sortOptions, cancellationToken); + return CreateList(multiDataList); + } + + ValueTask IRedisTypedClientAsync.StoreAsHashAsync(T entity, CancellationToken cancellationToken) + => AsyncClient.StoreAsHashAsync(entity, cancellationToken); + + ValueTask IRedisTypedClientAsync.GetFromHashAsync(object id, CancellationToken cancellationToken) + => AsyncClient.GetFromHashAsync(id, cancellationToken); + + async ValueTask> IRedisTypedClientAsync.GetAllItemsFromSetAsync(IRedisSetAsync fromSet, CancellationToken cancellationToken) + { + var multiDataList = await AsyncNative.SMembersAsync(fromSet.Id, cancellationToken).ConfigureAwait(false); + return CreateHashSet(multiDataList); + } + + ValueTask IRedisTypedClientAsync.AddItemToSetAsync(IRedisSetAsync toSet, T item, CancellationToken cancellationToken) + => AsyncNative.SAddAsync(toSet.Id, SerializeValue(item), cancellationToken).Await(); + + ValueTask IRedisTypedClientAsync.RemoveItemFromSetAsync(IRedisSetAsync fromSet, T item, CancellationToken cancellationToken) + => AsyncNative.SRemAsync(fromSet.Id, SerializeValue(item), cancellationToken).Await(); + + ValueTask IRedisTypedClientAsync.PopItemFromSetAsync(IRedisSetAsync fromSet, CancellationToken cancellationToken) + => DeserializeValueAsync(AsyncNative.SPopAsync(fromSet.Id, cancellationToken)); + + ValueTask IRedisTypedClientAsync.MoveBetweenSetsAsync(IRedisSetAsync fromSet, IRedisSetAsync toSet, T item, CancellationToken cancellationToken) + => AsyncNative.SMoveAsync(fromSet.Id, toSet.Id, SerializeValue(item), cancellationToken); + + ValueTask IRedisTypedClientAsync.GetSetCountAsync(IRedisSetAsync set, CancellationToken cancellationToken) + => AsyncNative.SCardAsync(set.Id, cancellationToken); + + ValueTask IRedisTypedClientAsync.SetContainsItemAsync(IRedisSetAsync set, T item, CancellationToken cancellationToken) + => AsyncNative.SIsMemberAsync(set.Id, SerializeValue(item)).IsSuccessAsync(); + + async ValueTask> IRedisTypedClientAsync.GetIntersectFromSetsAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken) + { + var multiDataList = await AsyncNative.SInterAsync(sets.Map(x => x.Id).ToArray(), cancellationToken).ConfigureAwait(false); + return CreateHashSet(multiDataList); + } + + ValueTask IRedisTypedClientAsync.StoreIntersectFromSetsAsync(IRedisSetAsync intoSet, IRedisSetAsync[] sets, CancellationToken cancellationToken) + => AsyncNative.SInterStoreAsync(intoSet.Id, sets.Map(x => x.Id).ToArray(), cancellationToken); + + async ValueTask> IRedisTypedClientAsync.GetUnionFromSetsAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken) + { + var multiDataList = await AsyncNative.SUnionAsync(sets.Map(x => x.Id).ToArray(), cancellationToken).ConfigureAwait(false); + return CreateHashSet(multiDataList); + } + + ValueTask IRedisTypedClientAsync.StoreUnionFromSetsAsync(IRedisSetAsync intoSet, IRedisSetAsync[] sets, CancellationToken cancellationToken) + => AsyncNative.SUnionStoreAsync(intoSet.Id, sets.Map(x => x.Id).ToArray(), cancellationToken); + + async ValueTask> IRedisTypedClientAsync.GetDifferencesFromSetAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken) + { + var multiDataList = await AsyncNative.SDiffAsync(fromSet.Id, withSets.Map(x => x.Id).ToArray(), cancellationToken).ConfigureAwait(false); + return CreateHashSet(multiDataList); + } + + ValueTask IRedisTypedClientAsync.StoreDifferencesFromSetAsync(IRedisSetAsync intoSet, IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken) + => AsyncNative.SDiffStoreAsync(intoSet.Id, fromSet.Id, withSets.Map(x => x.Id).ToArray(), cancellationToken); + + ValueTask IRedisTypedClientAsync.GetRandomItemFromSetAsync(IRedisSetAsync fromSet, CancellationToken cancellationToken) + => DeserializeValueAsync(AsyncNative.SRandMemberAsync(fromSet.Id, cancellationToken)); + + ValueTask> IRedisTypedClientAsync.GetAllItemsFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken) + { + var multiDataList = AsyncNative.LRangeAsync(fromList.Id, FirstElement, LastElement, cancellationToken); + return CreateList(multiDataList); + } + + ValueTask> IRedisTypedClientAsync.GetRangeFromListAsync(IRedisListAsync fromList, int startingFrom, int endingAt, CancellationToken cancellationToken) + { + var multiDataList = AsyncNative.LRangeAsync(fromList.Id, startingFrom, endingAt, cancellationToken); + return CreateList(multiDataList); + } + + ValueTask> IRedisTypedClientAsync.SortListAsync(IRedisListAsync fromList, int startingFrom, int endingAt, CancellationToken cancellationToken) + { + var sortOptions = new SortOptions { Skip = startingFrom, Take = endingAt, }; + var multiDataList = AsyncNative.SortAsync(fromList.Id, sortOptions, cancellationToken); + return CreateList(multiDataList); + } + + ValueTask IRedisTypedClientAsync.AddItemToListAsync(IRedisListAsync fromList, T value, CancellationToken cancellationToken) + => AsyncNative.RPushAsync(fromList.Id, SerializeValue(value), cancellationToken).Await(); + + ValueTask IRedisTypedClientAsync.PrependItemToListAsync(IRedisListAsync fromList, T value, CancellationToken cancellationToken) + => AsyncNative.LPushAsync(fromList.Id, SerializeValue(value), cancellationToken).Await(); + + ValueTask IRedisTypedClientAsync.RemoveStartFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken) + => DeserializeValueAsync(AsyncNative.LPopAsync(fromList.Id, cancellationToken)); + + async ValueTask IRedisTypedClientAsync.BlockingRemoveStartFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken cancellationToken) + { + var unblockingKeyAndValue = await AsyncNative.BLPopAsync(fromList.Id, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).ConfigureAwait(false); + return unblockingKeyAndValue.Length == 0 + ? default + : DeserializeValue(unblockingKeyAndValue[1]); + } + + ValueTask IRedisTypedClientAsync.RemoveEndFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken) + => DeserializeValueAsync(AsyncNative.RPopAsync(fromList.Id, cancellationToken)); + + ValueTask IRedisTypedClientAsync.RemoveAllFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken) + => AsyncNative.LTrimAsync(fromList.Id, int.MaxValue, FirstElement, cancellationToken); + + ValueTask IRedisTypedClientAsync.TrimListAsync(IRedisListAsync fromList, int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken) + => AsyncNative.LTrimAsync(fromList.Id, keepStartingFrom, keepEndingAt, cancellationToken); + + ValueTask IRedisTypedClientAsync.RemoveItemFromListAsync(IRedisListAsync fromList, T value, CancellationToken cancellationToken) + { + const int removeAll = 0; + return AsyncNative.LRemAsync(fromList.Id, removeAll, SerializeValue(value), cancellationToken); + } + + ValueTask IRedisTypedClientAsync.RemoveItemFromListAsync(IRedisListAsync fromList, T value, int noOfMatches, CancellationToken cancellationToken) + => AsyncNative.LRemAsync(fromList.Id, noOfMatches, SerializeValue(value), cancellationToken); + + ValueTask IRedisTypedClientAsync.GetListCountAsync(IRedisListAsync fromList, CancellationToken cancellationToken) + => AsyncNative.LLenAsync(fromList.Id, cancellationToken); + + ValueTask IRedisTypedClientAsync.GetItemFromListAsync(IRedisListAsync fromList, int listIndex, CancellationToken cancellationToken) + => DeserializeValueAsync(AsyncNative.LIndexAsync(fromList.Id, listIndex, cancellationToken)); + + ValueTask IRedisTypedClientAsync.SetItemInListAsync(IRedisListAsync toList, int listIndex, T value, CancellationToken cancellationToken) + => AsyncNative.LSetAsync(toList.Id, listIndex, SerializeValue(value), cancellationToken); + + ValueTask IRedisTypedClientAsync.InsertBeforeItemInListAsync(IRedisListAsync toList, T pivot, T value, CancellationToken cancellationToken) + => AsyncNative.LInsertAsync(toList.Id, insertBefore: true, pivot: SerializeValue(pivot), value: SerializeValue(value), cancellationToken: cancellationToken); + + ValueTask IRedisTypedClientAsync.InsertAfterItemInListAsync(IRedisListAsync toList, T pivot, T value, CancellationToken cancellationToken) + => AsyncNative.LInsertAsync(toList.Id, insertBefore: false, pivot: SerializeValue(pivot), value: SerializeValue(value), cancellationToken: cancellationToken); + + ValueTask IRedisTypedClientAsync.EnqueueItemOnListAsync(IRedisListAsync fromList, T item, CancellationToken cancellationToken) + => AsyncNative.LPushAsync(fromList.Id, SerializeValue(item), cancellationToken).Await(); + + ValueTask IRedisTypedClientAsync.DequeueItemFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken) + => DeserializeValueAsync(AsyncNative.RPopAsync(fromList.Id, cancellationToken)); + + async ValueTask IRedisTypedClientAsync.BlockingDequeueItemFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken cancellationToken) + { + var unblockingKeyAndValue = await AsyncNative.BRPopAsync(fromList.Id, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).ConfigureAwait(false); + return unblockingKeyAndValue.Length == 0 + ? default + : DeserializeValue(unblockingKeyAndValue[1]); + } + + ValueTask IRedisTypedClientAsync.PushItemToListAsync(IRedisListAsync fromList, T item, CancellationToken cancellationToken) + => AsyncNative.RPushAsync(fromList.Id, SerializeValue(item), cancellationToken).Await(); + + ValueTask IRedisTypedClientAsync.PopItemFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken) + => DeserializeValueAsync(AsyncNative.RPopAsync(fromList.Id, cancellationToken)); + + async ValueTask IRedisTypedClientAsync.BlockingPopItemFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken cancellationToken) + { + var unblockingKeyAndValue = await AsyncNative.BRPopAsync(fromList.Id, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).ConfigureAwait(false); + return unblockingKeyAndValue.Length == 0 + ? default + : DeserializeValue(unblockingKeyAndValue[1]); + } + + ValueTask IRedisTypedClientAsync.PopAndPushItemBetweenListsAsync(IRedisListAsync fromList, IRedisListAsync toList, CancellationToken cancellationToken) + => DeserializeValueAsync(AsyncNative.RPopLPushAsync(fromList.Id, toList.Id, cancellationToken)); + + ValueTask IRedisTypedClientAsync.BlockingPopAndPushItemBetweenListsAsync(IRedisListAsync fromList, IRedisListAsync toList, TimeSpan? timeOut, CancellationToken cancellationToken) + => DeserializeValueAsync(AsyncNative.BRPopLPushAsync(fromList.Id, toList.Id, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken)); + + ValueTask IRedisTypedClientAsync.AddItemToSortedSetAsync(IRedisSortedSetAsync toSet, T value, CancellationToken cancellationToken) + => AsyncClient.AddItemToSortedSetAsync(toSet.Id, value.SerializeToString(), cancellationToken).Await(); + + ValueTask IRedisTypedClientAsync.AddItemToSortedSetAsync(IRedisSortedSetAsync toSet, T value, double score, CancellationToken cancellationToken) + => AsyncClient.AddItemToSortedSetAsync(toSet.Id, value.SerializeToString(), score, cancellationToken).Await(); + + ValueTask IRedisTypedClientAsync.RemoveItemFromSortedSetAsync(IRedisSortedSetAsync fromSet, T value, CancellationToken cancellationToken) + => AsyncClient.RemoveItemFromSortedSetAsync(fromSet.Id, value.SerializeToString(), cancellationToken); + + ValueTask IRedisTypedClientAsync.PopItemWithLowestScoreFromSortedSetAsync(IRedisSortedSetAsync fromSet, CancellationToken cancellationToken) + => DeserializeFromStringAsync(AsyncClient.PopItemWithLowestScoreFromSortedSetAsync(fromSet.Id, cancellationToken)); + + ValueTask IRedisTypedClientAsync.PopItemWithHighestScoreFromSortedSetAsync(IRedisSortedSetAsync fromSet, CancellationToken cancellationToken) + => DeserializeFromStringAsync(AsyncClient.PopItemWithHighestScoreFromSortedSetAsync(fromSet.Id, cancellationToken)); + + ValueTask IRedisTypedClientAsync.SortedSetContainsItemAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken) + => AsyncClient.SortedSetContainsItemAsync(set.Id, value.SerializeToString(), cancellationToken); + + ValueTask IRedisTypedClientAsync.IncrementItemInSortedSetAsync(IRedisSortedSetAsync set, T value, double incrementBy, CancellationToken cancellationToken) + => AsyncClient.IncrementItemInSortedSetAsync(set.Id, value.SerializeToString(), incrementBy, cancellationToken); + + ValueTask IRedisTypedClientAsync.GetItemIndexInSortedSetAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken) + => AsyncClient.GetItemIndexInSortedSetAsync(set.Id, value.SerializeToString(), cancellationToken); + + ValueTask IRedisTypedClientAsync.GetItemIndexInSortedSetDescAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken) + => AsyncClient.GetItemIndexInSortedSetDescAsync(set.Id, value.SerializeToString(), cancellationToken); + + ValueTask> IRedisTypedClientAsync.GetAllItemsFromSortedSetAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken) + => AsyncClient.GetAllItemsFromSortedSetAsync(set.Id, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetAllItemsFromSortedSetDescAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken) + => AsyncClient.GetAllItemsFromSortedSetDescAsync(set.Id, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetAsync(set.Id, fromRank, toRank, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetDescAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetDescAsync(set.Id, fromRank, toRank, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetAllWithScoresFromSortedSetAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetAsync(set.Id, FirstElement, LastElement, cancellationToken)); + + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetAsync(set.Id, fromRank, toRank, cancellationToken)); + + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetDescAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetDescAsync(set.Id, fromRank, toRank, cancellationToken)); + + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(set.Id, fromStringScore, toStringScore, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(set.Id, fromStringScore, toStringScore, skip, take, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(set.Id, fromScore, toScore, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(set.Id, fromScore, toScore, skip, take, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByLowestScoreAsync(set.Id, fromStringScore, toStringScore, cancellationToken)); + + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByLowestScoreAsync(set.Id, fromStringScore, toStringScore, skip, take, cancellationToken)); + + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByLowestScoreAsync(set.Id, fromScore, toScore, cancellationToken)); + + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByLowestScoreAsync(set.Id, fromScore, toScore, skip, take, cancellationToken)); + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(set.Id, fromStringScore, toStringScore, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(set.Id, fromStringScore, toStringScore, skip, take, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(set.Id, fromScore, toScore, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(set.Id, fromScore, toScore, skip, take, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByHighestScoreAsync(set.Id, fromStringScore, toStringScore, cancellationToken)); + + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByHighestScoreAsync(set.Id, fromStringScore, toStringScore, skip, take, cancellationToken)); + + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByHighestScoreAsync(set.Id, fromScore, toScore, cancellationToken)); + + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByHighestScoreAsync(set.Id, fromScore, toScore, skip, take, cancellationToken)); + + ValueTask IRedisTypedClientAsync.RemoveRangeFromSortedSetAsync(IRedisSortedSetAsync set, int minRank, int maxRank, CancellationToken cancellationToken) + => AsyncClient.RemoveRangeFromSortedSetAsync(set.Id, minRank, maxRank, cancellationToken); + + ValueTask IRedisTypedClientAsync.RemoveRangeFromSortedSetByScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken) + => AsyncClient.RemoveRangeFromSortedSetByScoreAsync(set.Id, fromScore, toScore, cancellationToken); + + ValueTask IRedisTypedClientAsync.GetSortedSetCountAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken) + => AsyncClient.GetSortedSetCountAsync(set.Id, cancellationToken); + + ValueTask IRedisTypedClientAsync.GetItemScoreInSortedSetAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken) + => AsyncClient.GetItemScoreInSortedSetAsync(set.Id, value.SerializeToString(), cancellationToken); + + ValueTask IRedisTypedClientAsync.StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken) + => AsyncClient.StoreIntersectFromSortedSetsAsync(intoSetId.Id, setIds.Map(x => x.Id).ToArray(), cancellationToken); + + ValueTask IRedisTypedClientAsync.StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken) + => AsyncClient.StoreIntersectFromSortedSetsAsync(intoSetId.Id, setIds.Map(x => x.Id).ToArray(), args, cancellationToken); + + ValueTask IRedisTypedClientAsync.StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken) + => AsyncClient.StoreUnionFromSortedSetsAsync(intoSetId.Id, setIds.Map(x => x.Id).ToArray(), cancellationToken); + + ValueTask IRedisTypedClientAsync.StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken) + => AsyncClient.StoreUnionFromSortedSetsAsync(intoSetId.Id, setIds.Map(x => x.Id).ToArray(), args, cancellationToken); + + ValueTask IRedisTypedClientAsync.HashContainsEntryAsync(IRedisHashAsync hash, TKey key, CancellationToken cancellationToken) + => AsyncClient.HashContainsEntryAsync(hash.Id, key.SerializeToString(), cancellationToken); + + ValueTask IRedisTypedClientAsync.SetEntryInHashAsync(IRedisHashAsync hash, TKey key, T value, CancellationToken cancellationToken) + => AsyncClient.SetEntryInHashAsync(hash.Id, key.SerializeToString(), value.SerializeToString(), cancellationToken); + + ValueTask IRedisTypedClientAsync.SetEntryInHashIfNotExistsAsync(IRedisHashAsync hash, TKey key, T value, CancellationToken cancellationToken) + => AsyncClient.SetEntryInHashIfNotExistsAsync(hash.Id, key.SerializeToString(), value.SerializeToString(), cancellationToken); + + ValueTask IRedisTypedClientAsync.SetRangeInHashAsync(IRedisHashAsync hash, IEnumerable> keyValuePairs, CancellationToken cancellationToken) + { + var stringKeyValuePairs = keyValuePairs.ToList().ConvertAll( + x => new KeyValuePair(x.Key.SerializeToString(), x.Value.SerializeToString())); + + return AsyncClient.SetRangeInHashAsync(hash.Id, stringKeyValuePairs, cancellationToken); + } + + ValueTask IRedisTypedClientAsync.GetValueFromHashAsync(IRedisHashAsync hash, TKey key, CancellationToken cancellationToken) + => DeserializeFromStringAsync(AsyncClient.GetValueFromHashAsync(hash.Id, key.SerializeToString(), cancellationToken)); + + ValueTask IRedisTypedClientAsync.RemoveEntryFromHashAsync(IRedisHashAsync hash, TKey key, CancellationToken cancellationToken) + => AsyncClient.RemoveEntryFromHashAsync(hash.Id, key.SerializeToString(), cancellationToken); + + ValueTask IRedisTypedClientAsync.GetHashCountAsync(IRedisHashAsync hash, CancellationToken cancellationToken) + => AsyncClient.GetHashCountAsync(hash.Id, cancellationToken); + + ValueTask> IRedisTypedClientAsync.GetHashKeysAsync(IRedisHashAsync hash, CancellationToken cancellationToken) + => AsyncClient.GetHashKeysAsync(hash.Id, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetHashValuesAsync(IRedisHashAsync hash, CancellationToken cancellationToken) + => AsyncClient.GetHashValuesAsync(hash.Id, cancellationToken).ConvertEachToAsync(); + + ValueTask> IRedisTypedClientAsync.GetAllEntriesFromHashAsync(IRedisHashAsync hash, CancellationToken cancellationToken) + => ConvertEachToAsync(AsyncClient.GetAllEntriesFromHashAsync(hash.Id, cancellationToken)); + + async ValueTask IRedisTypedClientAsync.StoreRelatedEntitiesAsync(object parentId, List children, CancellationToken cancellationToken) + { + var childRefKey = GetChildReferenceSetKey(parentId); + var childKeys = children.ConvertAll(x => client.UrnKey(x)); + + await using var trans = await AsyncClient.CreateTransactionAsync(cancellationToken).ConfigureAwait(false); + //Ugly but need access to a generic constraint-free StoreAll method + trans.QueueCommand(c => ((RedisClient)c).StoreAllAsyncImpl(children, cancellationToken)); + trans.QueueCommand(c => c.AddRangeToSetAsync(childRefKey, childKeys, cancellationToken)); + + await trans.CommitAsync(cancellationToken).ConfigureAwait(false); + } + + ValueTask IRedisTypedClientAsync.StoreRelatedEntitiesAsync(object parentId, TChild[] children, CancellationToken cancellationToken) + => AsAsync().StoreRelatedEntitiesAsync(parentId, new List(children), cancellationToken); + + ValueTask IRedisTypedClientAsync.DeleteRelatedEntitiesAsync(object parentId, CancellationToken cancellationToken) + { + var childRefKey = GetChildReferenceSetKey(parentId); + return AsyncClient.RemoveAsync(childRefKey, cancellationToken).Await(); + } + + ValueTask IRedisTypedClientAsync.DeleteRelatedEntityAsync(object parentId, object childId, CancellationToken cancellationToken) + { + var childRefKey = GetChildReferenceSetKey(parentId); + return AsyncClient.RemoveItemFromSetAsync(childRefKey, TypeSerializer.SerializeToString(childId), cancellationToken); + } + + async ValueTask> IRedisTypedClientAsync.GetRelatedEntitiesAsync(object parentId, CancellationToken cancellationToken) + { + var childRefKey = GetChildReferenceSetKey(parentId); + var childKeys = (await AsyncClient.GetAllItemsFromSetAsync(childRefKey, cancellationToken).ConfigureAwait(false)).ToList(); + + return await AsyncClient.As().GetValuesAsync(childKeys, cancellationToken).ConfigureAwait(false); + } + + ValueTask IRedisTypedClientAsync.GetRelatedEntitiesCountAsync(object parentId, CancellationToken cancellationToken) + { + var childRefKey = GetChildReferenceSetKey(parentId); + return AsyncClient.GetSetCountAsync(childRefKey, cancellationToken); + } + + ValueTask IRedisTypedClientAsync.AddToRecentsListAsync(T value, CancellationToken cancellationToken) + { + var key = client.UrnKey(value); + var nowScore = DateTime.UtcNow.ToUnixTime(); + return AsyncClient.AddItemToSortedSetAsync(RecentSortedSetKey, key, nowScore, cancellationToken).Await(); + } + + async ValueTask> IRedisTypedClientAsync.GetLatestFromRecentsListAsync(int skip, int take, CancellationToken cancellationToken) + { + var toRank = take - 1; + var keys = await AsyncClient.GetRangeFromSortedSetDescAsync(RecentSortedSetKey, skip, toRank, cancellationToken).ConfigureAwait(false); + var values = await AsAsync().GetValuesAsync(keys, cancellationToken).ConfigureAwait(false); + return values; + } + + async ValueTask> IRedisTypedClientAsync.GetEarliestFromRecentsListAsync(int skip, int take, CancellationToken cancellationToken) + { + var toRank = take - 1; + var keys = await AsyncClient.GetRangeFromSortedSetAsync(RecentSortedSetKey, skip, toRank, cancellationToken).ConfigureAwait(false); + var values = await AsAsync().GetValuesAsync(keys, cancellationToken).ConfigureAwait(false); + return values; + } + + ValueTask IRedisTypedClientAsync.RemoveEntryAsync(params string[] args) + => AsAsync().RemoveEntryAsync(args, cancellationToken: default); + + ValueTask IRedisTypedClientAsync.RemoveEntryAsync(params IHasStringId[] entities) + => AsAsync().RemoveEntryAsync(entities, cancellationToken: default); + + ValueTask> IRedisTypedClientAsync.GetIntersectFromSetsAsync(params IRedisSetAsync[] sets) + => AsAsync().GetIntersectFromSetsAsync(sets, cancellationToken: default); + + ValueTask IRedisTypedClientAsync.StoreIntersectFromSetsAsync(IRedisSetAsync intoSet, params IRedisSetAsync[] sets) + => AsAsync().StoreIntersectFromSetsAsync(intoSet, sets, cancellationToken: default); + + ValueTask> IRedisTypedClientAsync.GetUnionFromSetsAsync(params IRedisSetAsync[] sets) + => AsAsync().GetUnionFromSetsAsync(sets, cancellationToken: default); + + ValueTask IRedisTypedClientAsync.StoreUnionFromSetsAsync(IRedisSetAsync intoSet, params IRedisSetAsync[] sets) + => AsAsync().StoreUnionFromSetsAsync(intoSet, sets, cancellationToken: default); + + ValueTask> IRedisTypedClientAsync.GetDifferencesFromSetAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets) + => AsAsync().GetDifferencesFromSetAsync(fromSet, withSets, cancellationToken: default); + + ValueTask IRedisTypedClientAsync.StoreDifferencesFromSetAsync(IRedisSetAsync intoSet, IRedisSetAsync fromSet, params IRedisSetAsync[] withSets) + => AsAsync().StoreDifferencesFromSetAsync(intoSet, fromSet, withSets, cancellationToken: default); + + ValueTask IRedisTypedClientAsync.StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, params IRedisSortedSetAsync[] setIds) + => AsAsync().StoreIntersectFromSortedSetsAsync(intoSetId, setIds, cancellationToken: default); + + ValueTask IRedisTypedClientAsync.StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, params IRedisSortedSetAsync[] setIds) + => AsAsync().StoreUnionFromSortedSetsAsync(intoSetId, setIds, cancellationToken: default); + + ValueTask IRedisTypedClientAsync.StoreRelatedEntitiesAsync(object parentId, params TChild[] children) + => AsAsync().StoreRelatedEntitiesAsync(parentId, children, cancellationToken: default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.cs index 069cd00d..04dde1d4 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.cs @@ -63,7 +63,7 @@ public RedisTypedClient(RedisClient client) public IRedisTypedTransaction CreateTransaction() { - return new RedisTypedTransaction(this); + return new RedisTypedTransaction(this, false); } public IRedisTypedPipeline CreatePipeline() @@ -137,7 +137,9 @@ public string UrnKey(T entity) return client.UrnKey(entity); } - public IRedisSet TypeIdsSet => new RedisClientSet(client, client.GetTypeIdsSetKey()); + public IRedisSet TypeIdsSet => TypeIdsSetRaw; + + private RedisClientSet TypeIdsSetRaw => new RedisClientSet(client, client.GetTypeIdsSetKey()); public T this[string key] { @@ -319,6 +321,10 @@ public void FlushAll() public T[] SearchKeys(string pattern) { var strKeys = client.SearchKeys(pattern); + return SearchKeysParse(strKeys); + } + private T[] SearchKeysParse(List strKeys) + { var keysCount = strKeys.Count; var keys = new T[keysCount]; @@ -334,7 +340,10 @@ public List GetValues(List keys) if (keys.IsNullOrEmpty()) return new List(); var resultBytesArray = client.MGet(keys.ToArray()); - + return ProcessGetValues(resultBytesArray); + } + private List ProcessGetValues(byte[][] resultBytesArray) + { var results = new List(); foreach (var resultBytes in resultBytesArray) { @@ -400,22 +409,34 @@ public T Store(T entity, TimeSpan expireIn) public void StoreAll(IEnumerable entities) { - if (entities == null) return; + if (PrepareStoreAll(entities, out var keys, out var values, out var entitiesList)) + { + client.MSet(keys, values); + client.RegisterTypeIds(entitiesList); + } + } + + private bool PrepareStoreAll(IEnumerable entities, out byte[][] keys, out byte[][] values, out List entitiesList) + { + if (entities == null) + { + keys = values = default; + entitiesList = default; + return false; + } - var entitiesList = entities.ToList(); + entitiesList = entities.ToList(); var len = entitiesList.Count; - var keys = new byte[len][]; - var values = new byte[len][]; + keys = new byte[len][]; + values = new byte[len][]; for (var i = 0; i < len; i++) { keys[i] = client.UrnKey(entitiesList[i]).ToUtf8Bytes(); values[i] = Redis.RedisClient.SerializeToUtf8Bytes(entitiesList[i]); } - - client.MSet(keys, values); - client.RegisterTypeIds(entitiesList); + return true; } public void Delete(T entity) diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_List.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_List.Async.cs new file mode 100644 index 00000000..e3c98d05 --- /dev/null +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_List.Async.cs @@ -0,0 +1,33 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Model; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Generic +{ + public partial class RedisTypedClient + { + internal partial class RedisClientLists + : IHasNamed> + { + IRedisListAsync IHasNamed>.this[string listId] + { + get => new RedisClientList(client, listId); + set => throw new NotSupportedException(); + } + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_List.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_List.cs index 56ed9592..3f4d15ce 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient_List.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_List.cs @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using ServiceStack.Model; namespace ServiceStack.Redis.Generic @@ -24,7 +25,7 @@ public partial class RedisTypedClient public IHasNamed> Lists { get; set; } - internal class RedisClientLists + internal partial class RedisClientLists : IHasNamed> { private readonly RedisTypedClient client; diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.Async.cs new file mode 100644 index 00000000..b5b9430f --- /dev/null +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.Async.cs @@ -0,0 +1,30 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Model; +using System; + +namespace ServiceStack.Redis.Generic +{ + public partial class RedisTypedClient + { + internal partial class RedisClientSets + : IHasNamed> + { + IRedisSetAsync IHasNamed>.this[string setId] + { + get => new RedisClientSet(client, setId); + set => throw new NotSupportedException(); + } + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.cs index 15e607ef..b723fa69 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.cs @@ -27,7 +27,7 @@ public long Db set { client.Db = value; } } - internal class RedisClientSets + internal partial class RedisClientSets : IHasNamed> { private readonly RedisTypedClient client; diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.Async.cs new file mode 100644 index 00000000..bd0cd64c --- /dev/null +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.Async.cs @@ -0,0 +1,30 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Model; +using System; + +namespace ServiceStack.Redis.Generic +{ + public partial class RedisTypedClient + { + internal partial class RedisClientSortedSets + : IHasNamed> + { + IRedisSortedSetAsync IHasNamed>.this[string setId] + { + get => new RedisClientSortedSet(client, setId); + set => throw new NotSupportedException(); + } + } + } +} diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.cs index 31113595..4c4e5d69 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.cs @@ -21,7 +21,7 @@ public partial class RedisTypedClient { public IHasNamed> SortedSets { get; set; } - internal class RedisClientSortedSets + internal partial class RedisClientSortedSets : IHasNamed> { private readonly RedisTypedClient client; diff --git a/src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs new file mode 100644 index 00000000..f20ce174 --- /dev/null +++ b/src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Redis.Generic; +using ServiceStack.Redis.Pipeline; +using ServiceStack.Text; + +namespace ServiceStack.Redis +{ + /// + /// Pipeline for redis typed client + /// + /// + public partial class RedisTypedPipeline + : IRedisTypedPipelineAsync + { + private IRedisTypedPipelineAsync AsAsync() => this; + void IRedisQueueCompletableOperationAsync.CompleteBytesQueuedCommandAsync(Func> bytesReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(bytesReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteDoubleQueuedCommandAsync(Func> doubleReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(doubleReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteIntQueuedCommandAsync(Func> intReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(intReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteLongQueuedCommandAsync(Func> longReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(longReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteMultiBytesQueuedCommandAsync(Func> multiBytesReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(multiBytesReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteMultiStringQueuedCommandAsync(Func>> multiStringReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(multiStringReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteRedisDataQueuedCommandAsync(Func> redisDataReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(redisDataReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteStringQueuedCommandAsync(Func> stringReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(stringReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteVoidQueuedCommandAsync(Func voidReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(voidReadCommand); + AddCurrentQueuedOperation(); + } + + ValueTask IAsyncDisposable.DisposeAsync() + { + Dispose(); + return default; + } + + async ValueTask IRedisPipelineSharedAsync.FlushAsync(CancellationToken cancellationToken) + { + try + { + // flush send buffers + await RedisClient.FlushSendBufferAsync(cancellationToken).ConfigureAwait(false); + RedisClient.ResetSendBuffer(); + + //receive expected results + foreach (var queuedCommand in QueuedCommands) + { + await queuedCommand.ProcessResultAsync(cancellationToken).ConfigureAwait(false); + } + + } + finally + { + ClosePipeline(); + await RedisClient.AddTypeIdsRegisteredDuringPipelineAsync(cancellationToken).ConfigureAwait(false); + } + } + + void IRedisTypedQueueableOperationAsync.QueueCommand(Func, ValueTask> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisTypedCommand + { + OnSuccessVoidCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + RedisAllPurposePipeline.AssertSync(command(RedisClient)); + } + + void IRedisTypedQueueableOperationAsync.QueueCommand(Func, ValueTask> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisTypedCommand + { + OnSuccessIntCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + RedisAllPurposePipeline.AssertSync(command(RedisClient)); + } + + void IRedisTypedQueueableOperationAsync.QueueCommand(Func, ValueTask> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisTypedCommand + { + OnSuccessLongCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + RedisAllPurposePipeline.AssertSync(command(RedisClient)); + } + + void IRedisTypedQueueableOperationAsync.QueueCommand(Func, ValueTask> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisTypedCommand + { + OnSuccessBoolCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + RedisAllPurposePipeline.AssertSync(command(RedisClient)); + } + + void IRedisTypedQueueableOperationAsync.QueueCommand(Func, ValueTask> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisTypedCommand + { + OnSuccessDoubleCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + RedisAllPurposePipeline.AssertSync(command(RedisClient)); + } + + void IRedisTypedQueueableOperationAsync.QueueCommand(Func, ValueTask> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisTypedCommand + { + OnSuccessBytesCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + RedisAllPurposePipeline.AssertSync(command(RedisClient)); + } + + void IRedisTypedQueueableOperationAsync.QueueCommand(Func, ValueTask> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisTypedCommand + { + OnSuccessStringCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + RedisAllPurposePipeline.AssertSync(command(RedisClient)); + } + + void IRedisTypedQueueableOperationAsync.QueueCommand(Func, ValueTask> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisTypedCommand + { + OnSuccessTypeCallback = x => onSuccessCallback(JsonSerializer.DeserializeFromString(x)), + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + RedisAllPurposePipeline.AssertSync(command(RedisClient)); + } + + void IRedisTypedQueueableOperationAsync.QueueCommand(Func, ValueTask>> command, Action> onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisTypedCommand + { + OnSuccessMultiStringCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + RedisAllPurposePipeline.AssertSync(command(RedisClient)); + } + + void IRedisTypedQueueableOperationAsync.QueueCommand(Func, ValueTask>> command, Action> onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisTypedCommand + { + OnSuccessMultiStringCallback = list => onSuccessCallback(list.ToHashSet()), + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(async r => + { + var result = await command(r).ConfigureAwait(false); + return result.ToList(); + })); + RedisAllPurposePipeline.AssertSync(command(RedisClient)); + } + + void IRedisTypedQueueableOperationAsync.QueueCommand(Func, ValueTask>> command, Action> onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisTypedCommand + { + OnSuccessMultiTypeCallback = x => onSuccessCallback(x.ConvertAll(y => JsonSerializer.DeserializeFromString(y))), + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + RedisAllPurposePipeline.AssertSync(command(RedisClient)); + } + + async ValueTask IRedisPipelineSharedAsync.ReplayAsync(CancellationToken cancellationToken) + { + RedisClient.Pipeline = this; + // execute + foreach (var queuedCommand in QueuedCommands) + { + if (queuedCommand is QueuedRedisTypedCommand cmd) + await cmd.ExecuteAsync(RedisClient).ConfigureAwait(false); + } + await AsAsync().FlushAsync(cancellationToken).ConfigureAwait(false); + return true; + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisTypedPipeline.cs b/src/ServiceStack.Redis/Generic/RedisTypedPipeline.cs index 61336d98..86e42af1 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedPipeline.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedPipeline.cs @@ -7,7 +7,7 @@ namespace ServiceStack.Redis /// Pipeline for redis typed client /// /// - public class RedisTypedPipeline : RedisTypedCommandQueue, IRedisTypedPipeline + public partial class RedisTypedPipeline : RedisTypedCommandQueue, IRedisTypedPipeline { internal RedisTypedPipeline(RedisTypedClient redisClient) : base(redisClient) diff --git a/src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs new file mode 100644 index 00000000..76709608 --- /dev/null +++ b/src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs @@ -0,0 +1,94 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Redis.Pipeline; + +namespace ServiceStack.Redis.Generic +{ + /// + /// Adds support for Redis Transactions (i.e. MULTI/EXEC/DISCARD operations). + /// + internal partial class RedisTypedTransaction + : IRedisTypedTransactionAsync, IRedisTransactionBaseAsync + { + async ValueTask IRedisTypedTransactionAsync.CommitAsync(CancellationToken cancellationToken) + { + bool rc = true; + try + { + _numCommands = QueuedCommands.Count / 2; + + //insert multi command at beginning + QueuedCommands.Insert(0, new QueuedRedisCommand() + { + }.WithAsyncReturnCommand(VoidReturnCommandAsync: r => { Init(); return default; }) + .WithAsyncReadCommand(RedisClient.ExpectOkAsync)); + + //the first half of the responses will be "QUEUED", + // so insert reading of multiline after these responses + QueuedCommands.Insert(_numCommands + 1, new QueuedRedisOperation() + { + OnSuccessIntCallback = handleMultiDataResultCount + }.WithAsyncReadCommand(RedisClient.ReadMultiDataResultCountAsync)); + + // add Exec command at end (not queued) + QueuedCommands.Add(new RedisCommand() + { + }.WithAsyncReturnCommand(r => ExecAsync(cancellationToken))); + + //execute transaction + await ExecAsync(cancellationToken).ConfigureAwait(false); + + ///////////////////////////// + //receive expected results + foreach (var queuedCommand in QueuedCommands) + { + await queuedCommand.ProcessResultAsync(cancellationToken).ConfigureAwait(false); + } + } + catch (RedisTransactionFailedException) + { + rc = false; + } + finally + { + RedisClient.Transaction = null; + ClosePipeline(); + await RedisClient.AddTypeIdsRegisteredDuringPipelineAsync(cancellationToken).ConfigureAwait(false); + } + return rc; + } + + private ValueTask ExecAsync(CancellationToken cancellationToken) + { + RedisClient.Exec(); + return RedisClient.FlushSendBufferAsync(cancellationToken); + } + + ValueTask IRedisTypedTransactionAsync.RollbackAsync(CancellationToken cancellationToken) + { + Rollback(); // no async bits needed + return default; + } + + partial void QueueExpectQueuedAsync() + { + QueuedCommands.Insert(0, new QueuedRedisOperation + { + }.WithAsyncReadCommand(RedisClient.ExpectQueuedAsync)); + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisTypedTransaction.cs b/src/ServiceStack.Redis/Generic/RedisTypedTransaction.cs index 94902650..b3ddb03d 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedTransaction.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedTransaction.cs @@ -19,14 +19,17 @@ namespace ServiceStack.Redis.Generic /// /// Adds support for Redis Transactions (i.e. MULTI/EXEC/DISCARD operations). /// - internal class RedisTypedTransaction + internal partial class RedisTypedTransaction : RedisTypedPipeline, IRedisTypedTransaction, IRedisTransactionBase { private int _numCommands = 0; - internal RedisTypedTransaction(RedisTypedClient redisClient) + private readonly bool _isAsync; + internal RedisTypedTransaction(RedisTypedClient redisClient, bool isAsync) : base(redisClient) { - + // if someone casts between sync/async: the sync-over-async or + // async-over-sync is entirely self-inflicted; I can't fix stupid + _isAsync = isAsync; } protected override void Init() @@ -59,7 +62,6 @@ private void Exec() { RedisClient.Exec(); RedisClient.FlushSendBuffer(); - } public bool Commit() @@ -76,7 +78,6 @@ public bool Commit() VoidReadCommand = RedisClient.ExpectOk, }); - //the first half of the responses will be "QUEUED", // so insert reading of multiline after these responses QueuedCommands.Insert(_numCommands + 1, new QueuedRedisOperation() @@ -174,8 +175,16 @@ public override void Dispose() protected override void AddCurrentQueuedOperation() { base.AddCurrentQueuedOperation(); - QueueExpectQueued(); + if (_isAsync) + { + QueueExpectQueuedAsync(); + } + else + { + QueueExpectQueued(); + } } #endregion + partial void QueueExpectQueuedAsync(); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/Pipeline/QueuedRedisCommand.Async.cs b/src/ServiceStack.Redis/Pipeline/QueuedRedisCommand.Async.cs new file mode 100644 index 00000000..fcadd82b --- /dev/null +++ b/src/ServiceStack.Redis/Pipeline/QueuedRedisCommand.Async.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using ServiceStack.Redis.Internal; + +namespace ServiceStack.Redis.Pipeline +{ + /// + /// A complete redis command, with method to send command, receive response, and run callback on success or failure + /// + internal partial class QueuedRedisCommand : RedisCommand + { + public override ValueTask ExecuteAsync(IRedisClientAsync client) + { + try + { + switch (AsyncReturnCommand) + { + case null: + ExecuteThrowIfSync(); + return default; + case Func VoidReturnCommandAsync: + return VoidReturnCommandAsync(client); + case Func> IntReturnCommandAsync: + return IntReturnCommandAsync(client).Await(); + case Func> LongReturnCommandAsync: + return LongReturnCommandAsync(client).Await(); + case Func> DoubleReturnCommandAsync: + return DoubleReturnCommandAsync(client).Await(); + case Func> BytesReturnCommandAsync: + return BytesReturnCommandAsync(client).Await(); + case Func> StringReturnCommandAsync: + return StringReturnCommandAsync(client).Await(); + case Func> MultiBytesReturnCommandAsync: + return MultiBytesReturnCommandAsync(client).Await(); + case Func>> MultiStringReturnCommandAsync: + return MultiStringReturnCommandAsync(client).Await(); + case object obj: + ExecuteThrowIfSync(); + // Execute only processes a limited number of patterns; we'll respect that here too + throw new InvalidOperationException("Command cannot be executed in this context: " + obj.GetType().FullName); + } + } + catch (Exception ex) + { + Log.Error(ex); + throw; + } + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Pipeline/QueuedRedisCommand.cs b/src/ServiceStack.Redis/Pipeline/QueuedRedisCommand.cs index 1acb90ec..238c9521 100644 --- a/src/ServiceStack.Redis/Pipeline/QueuedRedisCommand.cs +++ b/src/ServiceStack.Redis/Pipeline/QueuedRedisCommand.cs @@ -6,7 +6,7 @@ namespace ServiceStack.Redis.Pipeline /// /// A complete redis command, with method to send command, receive response, and run callback on success or failure /// - internal class QueuedRedisCommand : RedisCommand + internal partial class QueuedRedisCommand : RedisCommand { public override void Execute(IRedisClient client) { @@ -50,7 +50,10 @@ public override void Execute(IRedisClient client) else if (MultiStringReturnCommand != null) { MultiStringReturnCommand(client); - + } + else + { + ExecuteThrowIfAsync(); } } catch (Exception ex) @@ -58,7 +61,6 @@ public override void Execute(IRedisClient client) Log.Error(ex); throw; } - } } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/Pipeline/QueuedRedisOperation.Async.cs b/src/ServiceStack.Redis/Pipeline/QueuedRedisOperation.Async.cs new file mode 100644 index 00000000..8041e0b0 --- /dev/null +++ b/src/ServiceStack.Redis/Pipeline/QueuedRedisOperation.Async.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Pipeline +{ + internal partial class QueuedRedisOperation + { + public virtual ValueTask ExecuteAsync(IRedisClientAsync client) => default; + + private Delegate _asyncReadCommand; + private QueuedRedisOperation SetAsyncReadCommand(Delegate value) + { + if (_asyncReadCommand is object && _asyncReadCommand != value) + throw new InvalidOperationException("Only a single async read command can be assigned"); + _asyncReadCommand = value; + return this; + } + + internal QueuedRedisOperation WithAsyncReadCommand(Func VoidReadCommandAsync) + => SetAsyncReadCommand(VoidReadCommandAsync); + internal QueuedRedisOperation WithAsyncReadCommand(Func> IntReadCommandAsync) + => SetAsyncReadCommand(IntReadCommandAsync); + internal QueuedRedisOperation WithAsyncReadCommand(Func> LongReadCommandAsync) + => SetAsyncReadCommand(LongReadCommandAsync); + internal QueuedRedisOperation WithAsyncReadCommand(Func> BoolReadCommandAsync) + => SetAsyncReadCommand(BoolReadCommandAsync); + internal QueuedRedisOperation WithAsyncReadCommand(Func> BytesReadCommandAsync) + => SetAsyncReadCommand(BytesReadCommandAsync); + internal QueuedRedisOperation WithAsyncReadCommand(Func> MultiBytesReadCommandAsync) + => SetAsyncReadCommand(MultiBytesReadCommandAsync); + internal QueuedRedisOperation WithAsyncReadCommand(Func> StringReadCommandAsync) + => SetAsyncReadCommand(StringReadCommandAsync); + internal QueuedRedisOperation WithAsyncReadCommand(Func>> MultiStringReadCommandAsync) + => SetAsyncReadCommand(MultiStringReadCommandAsync); + internal QueuedRedisOperation WithAsyncReadCommand(Func>> DictionaryStringReadCommandAsync) + => SetAsyncReadCommand(DictionaryStringReadCommandAsync); + internal QueuedRedisOperation WithAsyncReadCommand(Func> DoubleReadCommandAsync) + => SetAsyncReadCommand(DoubleReadCommandAsync); + internal QueuedRedisOperation WithAsyncReadCommand(Func> RedisDataReadCommandAsync) + => SetAsyncReadCommand(RedisDataReadCommandAsync); + + public async ValueTask ProcessResultAsync(CancellationToken cancellationToken) + { + try + { + switch (_asyncReadCommand) + { + case null: + ProcessResultThrowIfSync(); + break; + case Func VoidReadCommandAsync: + await VoidReadCommandAsync(cancellationToken).ConfigureAwait(false); + OnSuccessVoidCallback?.Invoke(); + break; + case Func> IntReadCommandAsync: + var i32 = await IntReadCommandAsync(cancellationToken).ConfigureAwait(false); + OnSuccessIntCallback?.Invoke(i32); + OnSuccessLongCallback?.Invoke(i32); + OnSuccessBoolCallback?.Invoke(i32 == RedisNativeClient.Success); + OnSuccessVoidCallback?.Invoke(); + break; + case Func> LongReadCommandAsync: + var i64 = await LongReadCommandAsync(cancellationToken).ConfigureAwait(false); + OnSuccessIntCallback?.Invoke((int)i64); + OnSuccessLongCallback?.Invoke(i64); + OnSuccessBoolCallback?.Invoke(i64 == RedisNativeClient.Success); + OnSuccessVoidCallback?.Invoke(); + break; + case Func> DoubleReadCommandAsync: + var f64 = await DoubleReadCommandAsync(cancellationToken).ConfigureAwait(false); + OnSuccessDoubleCallback?.Invoke(f64); + break; + case Func> BytesReadCommandAsync: + var bytes = await BytesReadCommandAsync(cancellationToken).ConfigureAwait(false); + if (bytes != null && bytes.Length == 0) bytes = null; + OnSuccessBytesCallback?.Invoke(bytes); + OnSuccessStringCallback?.Invoke(bytes != null ? Encoding.UTF8.GetString(bytes) : null); + OnSuccessTypeCallback?.Invoke(bytes != null ? Encoding.UTF8.GetString(bytes) : null); + OnSuccessIntCallback?.Invoke(bytes != null ? int.Parse(Encoding.UTF8.GetString(bytes)) : 0); + OnSuccessBoolCallback?.Invoke(bytes != null && Encoding.UTF8.GetString(bytes) == "OK"); + break; + case Func> StringReadCommandAsync: + var s = await StringReadCommandAsync(cancellationToken).ConfigureAwait(false); + OnSuccessStringCallback?.Invoke(s); + OnSuccessTypeCallback?.Invoke(s); + break; + case Func> MultiBytesReadCommandAsync: + var multiBytes = await MultiBytesReadCommandAsync(cancellationToken).ConfigureAwait(false); + OnSuccessMultiBytesCallback?.Invoke(multiBytes); + OnSuccessMultiStringCallback?.Invoke(multiBytes?.ToStringList()); + OnSuccessMultiTypeCallback?.Invoke(multiBytes.ToStringList()); + OnSuccessDictionaryStringCallback?.Invoke(multiBytes.ToStringDictionary()); + break; + case Func>> MultiStringReadCommandAsync: + var multiString = await MultiStringReadCommandAsync(cancellationToken).ConfigureAwait(false); + OnSuccessMultiStringCallback?.Invoke(multiString); + break; + case Func> RedisDataReadCommandAsync: + var data = await RedisDataReadCommandAsync(cancellationToken).ConfigureAwait(false); + OnSuccessRedisTextCallback?.Invoke(data.ToRedisText()); + OnSuccessRedisDataCallback?.Invoke(data); + break; + case Func> BoolReadCommandAsync: + var b = await BoolReadCommandAsync(cancellationToken).ConfigureAwait(false); + OnSuccessBoolCallback?.Invoke(b); + break; + case Func>> DictionaryStringReadCommandAsync: + var dict = await DictionaryStringReadCommandAsync(cancellationToken).ConfigureAwait(false); + OnSuccessDictionaryStringCallback?.Invoke(dict); + break; + default: + ProcessResultThrowIfSync(); + break; + } + } + catch (Exception ex) + { + Log.Error(ex); + + if (OnErrorCallback != null) + { + OnErrorCallback(ex); + } + else + { + throw; + } + } + } + + partial void OnProcessResultThrowIfAsync() + { + if (_asyncReadCommand is object) + { + throw new InvalidOperationException("An async read command was present, but the queued operation is being processed synchronously"); + } + } + private void ProcessResultThrowIfSync() + { + if (VoidReadCommand is object + || IntReadCommand is object + || LongReadCommand is object + || BoolReadCommand is object + || BytesReadCommand is object + || MultiBytesReadCommand is object + || StringReadCommand is object + || MultiBytesReadCommand is object + || DictionaryStringReadCommand is object + || DoubleReadCommand is object + || RedisDataReadCommand is object) + { + throw new InvalidOperationException("A sync read command was present, but the queued operation is being processed asynchronously"); + } + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Pipeline/QueuedRedisOperation.cs b/src/ServiceStack.Redis/Pipeline/QueuedRedisOperation.cs index 58838a94..3a45d25b 100644 --- a/src/ServiceStack.Redis/Pipeline/QueuedRedisOperation.cs +++ b/src/ServiceStack.Redis/Pipeline/QueuedRedisOperation.cs @@ -5,7 +5,7 @@ namespace ServiceStack.Redis.Pipeline { - internal class QueuedRedisOperation + internal partial class QueuedRedisOperation { protected static readonly ILog Log = LogManager.GetLogger(typeof(QueuedRedisOperation)); @@ -51,60 +51,28 @@ public void ProcessResult() if (VoidReadCommand != null) { VoidReadCommand(); - if (OnSuccessVoidCallback != null) - { - OnSuccessVoidCallback(); - } + OnSuccessVoidCallback?.Invoke(); } else if (IntReadCommand != null) { var result = IntReadCommand(); - if (OnSuccessIntCallback != null) - { - OnSuccessIntCallback(result); - } - if (OnSuccessLongCallback != null) - { - OnSuccessLongCallback(result); - } - if (OnSuccessBoolCallback != null) - { - var success = result == RedisNativeClient.Success; - OnSuccessBoolCallback(success); - } - if (OnSuccessVoidCallback != null) - { - OnSuccessVoidCallback(); - } + OnSuccessIntCallback?.Invoke(result); + OnSuccessLongCallback?.Invoke(result); + OnSuccessBoolCallback?.Invoke(result == RedisNativeClient.Success); + OnSuccessVoidCallback?.Invoke(); } else if (LongReadCommand != null) { var result = LongReadCommand(); - if (OnSuccessIntCallback != null) - { - OnSuccessIntCallback((int)result); - } - if (OnSuccessLongCallback != null) - { - OnSuccessLongCallback(result); - } - if (OnSuccessBoolCallback != null) - { - var success = result == RedisNativeClient.Success; - OnSuccessBoolCallback(success); - } - if (OnSuccessVoidCallback != null) - { - OnSuccessVoidCallback(); - } + OnSuccessIntCallback?.Invoke((int)result); + OnSuccessLongCallback?.Invoke(result); + OnSuccessBoolCallback?.Invoke(result == RedisNativeClient.Success); + OnSuccessVoidCallback?.Invoke(); } else if (DoubleReadCommand != null) { var result = DoubleReadCommand(); - if (OnSuccessDoubleCallback != null) - { - OnSuccessDoubleCallback(result); - } + OnSuccessDoubleCallback?.Invoke(result); } else if (BytesReadCommand != null) { @@ -112,78 +80,50 @@ public void ProcessResult() if (result != null && result.Length == 0) result = null; - if (OnSuccessBytesCallback != null) - { - OnSuccessBytesCallback(result); - } - if (OnSuccessStringCallback != null) - { - OnSuccessStringCallback(result != null ? Encoding.UTF8.GetString(result) : null); - } - if (OnSuccessTypeCallback != null) - { - OnSuccessTypeCallback(result != null ? Encoding.UTF8.GetString(result) : null); - } - if (OnSuccessIntCallback != null) - { - OnSuccessIntCallback(result != null ? int.Parse(Encoding.UTF8.GetString(result)) : 0); - } - if (OnSuccessBoolCallback != null) - { - OnSuccessBoolCallback(result != null && Encoding.UTF8.GetString(result) == "OK"); - } + OnSuccessBytesCallback?.Invoke(result); + OnSuccessStringCallback?.Invoke(result != null ? Encoding.UTF8.GetString(result) : null); + OnSuccessTypeCallback?.Invoke(result != null ? Encoding.UTF8.GetString(result) : null); + OnSuccessIntCallback?.Invoke(result != null ? int.Parse(Encoding.UTF8.GetString(result)) : 0); + OnSuccessBoolCallback?.Invoke(result != null && Encoding.UTF8.GetString(result) == "OK"); } else if (StringReadCommand != null) { var result = StringReadCommand(); - if (OnSuccessStringCallback != null) - { - OnSuccessStringCallback(result); - } - if (OnSuccessTypeCallback != null) - { - OnSuccessTypeCallback(result); - } + OnSuccessStringCallback?.Invoke(result); + OnSuccessTypeCallback?.Invoke(result); } else if (MultiBytesReadCommand != null) { var result = MultiBytesReadCommand(); - if (OnSuccessMultiBytesCallback != null) - { - OnSuccessMultiBytesCallback(result); - } - if (OnSuccessMultiStringCallback != null) - { - OnSuccessMultiStringCallback(result != null ? result.ToStringList() : null); - } - if (OnSuccessMultiTypeCallback != null) - { - OnSuccessMultiTypeCallback(result.ToStringList()); - } - if (OnSuccessDictionaryStringCallback != null) - { - OnSuccessDictionaryStringCallback(result.ToStringDictionary()); - } + OnSuccessMultiBytesCallback?.Invoke(result); + OnSuccessMultiStringCallback?.Invoke(result != null ? result.ToStringList() : null); + OnSuccessMultiTypeCallback?.Invoke(result.ToStringList()); + OnSuccessDictionaryStringCallback?.Invoke(result.ToStringDictionary()); } else if (MultiStringReadCommand != null) { var result = MultiStringReadCommand(); - if (OnSuccessMultiStringCallback != null) - { - OnSuccessMultiStringCallback(result); - } + OnSuccessMultiStringCallback?.Invoke(result); } else if (RedisDataReadCommand != null) { var data = RedisDataReadCommand(); - if (OnSuccessRedisTextCallback != null) - { - OnSuccessRedisTextCallback(data.ToRedisText()); - } - if (OnSuccessRedisDataCallback != null) - { - OnSuccessRedisDataCallback(data); - } + OnSuccessRedisTextCallback?.Invoke(data.ToRedisText()); + OnSuccessRedisDataCallback?.Invoke(data); + } + else if (BoolReadCommand != null) + { + var result = BoolReadCommand(); + OnSuccessBoolCallback?.Invoke(result); + } + else if (DictionaryStringReadCommand != null) + { + var result = DictionaryStringReadCommand(); + OnSuccessDictionaryStringCallback?.Invoke(result); + } + else + { + ProcessResultThrowIfAsync(); } } catch (Exception ex) @@ -201,5 +141,7 @@ public void ProcessResult() } } + protected void ProcessResultThrowIfAsync() => OnProcessResultThrowIfAsync(); + partial void OnProcessResultThrowIfAsync(); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.Async.cs b/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.Async.cs new file mode 100644 index 00000000..17a30497 --- /dev/null +++ b/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.Async.cs @@ -0,0 +1,330 @@ +using ServiceStack.Redis.Pipeline; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + + public partial class RedisAllPurposePipeline : IRedisPipelineAsync + { + private IRedisPipelineAsync AsAsync() => this; + + private protected virtual async ValueTask ReplayAsync(CancellationToken cancellationToken) + { + Init(); + await ExecuteAsync().ConfigureAwait(false); + await AsAsync().FlushAsync(cancellationToken).ConfigureAwait(false); + return true; + } + + protected async ValueTask ExecuteAsync() + { + int count = QueuedCommands.Count; + for (int i = 0; i < count; ++i) + { + var op = QueuedCommands[0]; + QueuedCommands.RemoveAt(0); + await op.ExecuteAsync(RedisClient).ConfigureAwait(false); + QueuedCommands.Add(op); + } + } + + ValueTask IRedisPipelineSharedAsync.ReplayAsync(CancellationToken cancellationToken) + => ReplayAsync(cancellationToken); + + async ValueTask IRedisPipelineSharedAsync.FlushAsync(CancellationToken cancellationToken) + { + // flush send buffers + await RedisClient.FlushSendBufferAsync(cancellationToken).ConfigureAwait(false); + RedisClient.ResetSendBuffer(); + + try + { + //receive expected results + foreach (var queuedCommand in QueuedCommands) + { + await queuedCommand.ProcessResultAsync(cancellationToken).ConfigureAwait(false); + } + } + catch (Exception) + { + // The connection cannot be reused anymore. All queued commands have been sent to redis. Even if a new command is executed, the next response read from the + // network stream can be the response of one of the queued commands, depending on when the exception occurred. This response would be invalid for the new command. + RedisClient.DisposeConnection(); + throw; + } + + ClosePipeline(); + } + + private protected virtual ValueTask DisposeAsync() + { + // don't need to send anything; just clean up + Dispose(); + return default; + } + + ValueTask IAsyncDisposable.DisposeAsync() => DisposeAsync(); + + internal static void AssertSync(ValueTask command) + { + if (!command.IsCompleted) + { + _ = ObserveAsync(command.AsTask()); + throw new InvalidOperationException($"The operations provided to {nameof(IRedisQueueableOperationAsync.QueueCommand)} should not perform asynchronous operations internally"); + } + // this serves two purposes: 1) surface any fault, and + // 2) ensure that if pooled (IValueTaskSource), it is reclaimed + _ = command.Result; + } + + internal static void AssertSync(ValueTask command) + { + if (!command.IsCompleted) + { + _ = ObserveAsync(command.AsTask()); + throw new InvalidOperationException($"The operations provided to {nameof(IRedisQueueableOperationAsync.QueueCommand)} should not perform asynchronous operations internally"); + } + // this serves two purposes: 1) surface any fault, and + // 2) ensure that if pooled (IValueTaskSource), it is reclaimed + command.GetAwaiter().GetResult(); + } + + static async Task ObserveAsync(Task task) // semantically this is "async void", but: some sync-contexts explode on that + { + // we've already thrown an exception via AssertSync; this + // just ensures that an "unobserved exception" doesn't fire + // as well + try { await task.ConfigureAwait(false); } + catch { } + } + + void IRedisQueueableOperationAsync.QueueCommand(Func command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessVoidCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + AssertSync(command(RedisClient)); + } + + void IRedisQueueableOperationAsync.QueueCommand(Func> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessIntCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + AssertSync(command(RedisClient)); + } + + void IRedisQueueableOperationAsync.QueueCommand(Func> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessLongCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + AssertSync(command(RedisClient)); + } + + void IRedisQueueableOperationAsync.QueueCommand(Func> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessBoolCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + AssertSync(command(RedisClient)); + } + + void IRedisQueueableOperationAsync.QueueCommand(Func> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessDoubleCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + AssertSync(command(RedisClient)); + } + + void IRedisQueueableOperationAsync.QueueCommand(Func> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessBytesCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + AssertSync(command(RedisClient)); + } + + void IRedisQueueableOperationAsync.QueueCommand(Func> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessMultiBytesCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + AssertSync(command(RedisClient)); + } + + void IRedisQueueableOperationAsync.QueueCommand(Func> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessStringCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + AssertSync(command(RedisClient)); + } + + void IRedisQueueableOperationAsync.QueueCommand(Func>> command, Action> onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessMultiStringCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + AssertSync(command(RedisClient)); + } + + void IRedisQueueableOperationAsync.QueueCommand(Func>> command, Action> onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessMultiStringCallback = list => onSuccessCallback(list.ToHashSet()), + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(async r => + { + var result = await command(r).ConfigureAwait(false); + return result.ToList(); + })); + AssertSync(command(RedisClient)); + } + + void IRedisQueueableOperationAsync.QueueCommand(Func>> command, Action> onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessDictionaryStringCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + AssertSync(command(RedisClient)); + } + + void IRedisQueueableOperationAsync.QueueCommand(Func> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessRedisDataCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + AssertSync(command(RedisClient)); + } + + void IRedisQueueableOperationAsync.QueueCommand(Func> command, Action onSuccessCallback, Action onErrorCallback) + { + BeginQueuedCommand(new QueuedRedisCommand + { + OnSuccessRedisTextCallback = onSuccessCallback, + OnErrorCallback = onErrorCallback + }.WithAsyncReturnCommand(command)); + AssertSync(command(RedisClient)); + } + + void IRedisQueueCompletableOperationAsync.CompleteMultiBytesQueuedCommandAsync(Func> multiBytesReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(multiBytesReadCommand); + AddCurrentQueuedOperation(); + } + + + void IRedisQueueCompletableOperationAsync.CompleteLongQueuedCommandAsync(Func> longReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(longReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteBytesQueuedCommandAsync(Func> bytesReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(bytesReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteVoidQueuedCommandAsync(Func voidReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(voidReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteStringQueuedCommandAsync(Func> stringReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(stringReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteDoubleQueuedCommandAsync(Func> doubleReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(doubleReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteIntQueuedCommandAsync(Func> intReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(intReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteMultiStringQueuedCommandAsync(Func>> multiStringReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(multiStringReadCommand); + AddCurrentQueuedOperation(); + } + + void IRedisQueueCompletableOperationAsync.CompleteRedisDataQueuedCommandAsync(Func> redisDataReadCommand) + { + //AssertCurrentOperation(); + // this can happen when replaying pipeline/transaction + if (CurrentQueuedOperation == null) return; + + CurrentQueuedOperation.WithAsyncReadCommand(redisDataReadCommand); + AddCurrentQueuedOperation(); + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.cs b/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.cs index 9b08b858..19b8fd1b 100644 --- a/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.cs +++ b/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.cs @@ -1,10 +1,10 @@ -using System; using ServiceStack.Redis.Pipeline; +using System; namespace ServiceStack.Redis { - public class RedisAllPurposePipeline : RedisCommandQueue, IRedisPipeline + public partial class RedisAllPurposePipeline : RedisCommandQueue, IRedisPipeline { /// /// General purpose pipeline diff --git a/src/ServiceStack.Redis/Pipeline/RedisCommand.Async.cs b/src/ServiceStack.Redis/Pipeline/RedisCommand.Async.cs new file mode 100644 index 00000000..0485af6f --- /dev/null +++ b/src/ServiceStack.Redis/Pipeline/RedisCommand.Async.cs @@ -0,0 +1,118 @@ +using ServiceStack.Redis.Internal; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + /// + /// Redis command that does not get queued + /// + internal partial class RedisCommand + { + private Delegate _asyncReturnCommand; + protected Delegate AsyncReturnCommand => _asyncReturnCommand; + private RedisCommand SetAsyncReturnCommand(Delegate value) + { + if (_asyncReturnCommand is object && _asyncReturnCommand != value) + throw new InvalidOperationException("Only a single async return command can be assigned"); + _asyncReturnCommand = value; + return this; + } + internal RedisCommand WithAsyncReturnCommand(Func VoidReturnCommandAsync) + => SetAsyncReturnCommand(VoidReturnCommandAsync); + internal RedisCommand WithAsyncReturnCommand(Func> IntReturnCommandAsync) + => SetAsyncReturnCommand(IntReturnCommandAsync); + internal RedisCommand WithAsyncReturnCommand(Func> LongReturnCommandAsync) + => SetAsyncReturnCommand(LongReturnCommandAsync); + internal RedisCommand WithAsyncReturnCommand(Func> BoolReturnCommandAsync) + => SetAsyncReturnCommand(BoolReturnCommandAsync); + internal RedisCommand WithAsyncReturnCommand(Func> BytesReturnCommandAsync) + => SetAsyncReturnCommand(BytesReturnCommandAsync); + internal RedisCommand WithAsyncReturnCommand(Func> MultiBytesReturnCommandAsync) + => SetAsyncReturnCommand(MultiBytesReturnCommandAsync); + internal RedisCommand WithAsyncReturnCommand(Func> StringReturnCommandAsync) + => SetAsyncReturnCommand(StringReturnCommandAsync); + internal RedisCommand WithAsyncReturnCommand(Func>> MultiStringReturnCommandAsync) + => SetAsyncReturnCommand(MultiStringReturnCommandAsync); + internal RedisCommand WithAsyncReturnCommand(Func>> DictionaryStringReturnCommandAsync) + => SetAsyncReturnCommand(DictionaryStringReturnCommandAsync); + internal RedisCommand WithAsyncReturnCommand(Func> RedisDataReturnCommandAsync) + => SetAsyncReturnCommand(RedisDataReturnCommandAsync); + internal RedisCommand WithAsyncReturnCommand(Func> RedisTextReturnCommandAsync) + => SetAsyncReturnCommand(RedisTextReturnCommandAsync); + internal RedisCommand WithAsyncReturnCommand(Func> DoubleReturnCommandAsync) + => SetAsyncReturnCommand(DoubleReturnCommandAsync); + + public override ValueTask ExecuteAsync(IRedisClientAsync client) + { + try + { + switch (_asyncReturnCommand) + { + case null: + ExecuteThrowIfSync(); + return default; + case Func VoidReturnCommandAsync: + return VoidReturnCommandAsync(client); + case Func> IntReturnCommandAsync: + return IntReturnCommandAsync(client).Await(); + case Func> LongReturnCommandAsync: + return LongReturnCommandAsync(client).Await(); + case Func> DoubleReturnCommandAsync: + return DoubleReturnCommandAsync(client).Await(); + case Func> BytesReturnCommandAsync: + return BytesReturnCommandAsync(client).Await(); + case Func> StringReturnCommandAsync: + return StringReturnCommandAsync(client).Await(); + case Func> MultiBytesReturnCommandAsync: + return MultiBytesReturnCommandAsync(client).Await(); + case Func>> MultiStringReturnCommandAsync: + return MultiStringReturnCommandAsync(client).Await(); + case Func>> DictionaryStringReturnCommandAsync: + return DictionaryStringReturnCommandAsync(client).Await(); + case Func> RedisDataReturnCommandAsync: + return RedisDataReturnCommandAsync(client).Await(); + case Func> RedisTextReturnCommandAsync: + return RedisTextReturnCommandAsync(client).Await(); + case Func> BoolReturnCommandAsync: + return BoolReturnCommandAsync(client).Await(); + case object obj: + ExecuteThrowIfSync(); + return default; + } + } + catch (Exception ex) + { + Log.Error(ex); + return default; // RedisCommand.Execute swallows here; we'll do the same + } + } + + partial void OnExecuteThrowIfAsync() + { + if (_asyncReturnCommand is object) + { + throw new InvalidOperationException("An async return command was present, but the queued operation is being processed synchronously"); + } + } + protected void ExecuteThrowIfSync() + { + if (VoidReturnCommand is object + || IntReturnCommand is object + || LongReturnCommand is object + || BoolReturnCommand is object + || BytesReturnCommand is object + || MultiBytesReturnCommand is object + || StringReturnCommand is object + || MultiStringReturnCommand is object + || DictionaryStringReturnCommand is object + || RedisDataReturnCommand is object + || RedisTextReturnCommand is object + || DoubleReturnCommand is object) + { + throw new InvalidOperationException("A sync return command was present, but the queued operation is being processed asynchronously"); + } + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Pipeline/RedisCommand.cs b/src/ServiceStack.Redis/Pipeline/RedisCommand.cs index ab64a2c9..abe09a35 100644 --- a/src/ServiceStack.Redis/Pipeline/RedisCommand.cs +++ b/src/ServiceStack.Redis/Pipeline/RedisCommand.cs @@ -9,7 +9,7 @@ namespace ServiceStack.Redis /// /// Redis command that does not get queued /// - internal class RedisCommand : QueuedRedisOperation + internal partial class RedisCommand : QueuedRedisOperation { public Action VoidReturnCommand { get; set; } public Func IntReturnCommand { get; set; } @@ -79,11 +79,22 @@ public override void Execute(IRedisClient client) { RedisTextReturnCommand(client); } + else if (BoolReturnCommand != null) + { + BoolReturnCommand(client); + } + else + { + ExecuteThrowIfAsync(); + } } catch (Exception ex) { Log.Error(ex); } } + + protected void ExecuteThrowIfAsync() => OnExecuteThrowIfAsync(); + partial void OnExecuteThrowIfAsync(); } } diff --git a/src/ServiceStack.Redis/Pipeline/RedisPipelineCommand.Async.cs b/src/ServiceStack.Redis/Pipeline/RedisPipelineCommand.Async.cs new file mode 100644 index 00000000..19f71fdc --- /dev/null +++ b/src/ServiceStack.Redis/Pipeline/RedisPipelineCommand.Async.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Pipeline +{ + partial class RedisPipelineCommand + { + internal async ValueTask> ReadAllAsIntsAsync(CancellationToken cancellationToken) + { + var results = new List(); + while (cmdCount-- > 0) + { + results.Add(await client.ReadLongAsync(cancellationToken).ConfigureAwait(false)); + } + + return results; + } + internal async ValueTask ReadAllAsIntsHaveSuccessAsync(CancellationToken cancellationToken) + { + var allResults = await ReadAllAsIntsAsync(cancellationToken).ConfigureAwait(false); + return allResults.All(x => x == RedisNativeClient.Success); + } + + internal ValueTask FlushAsync(CancellationToken cancellationToken) + { + Flush(); + return default; + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Pipeline/RedisPipelineCommand.cs b/src/ServiceStack.Redis/Pipeline/RedisPipelineCommand.cs index 6c2830dd..67b4cb69 100644 --- a/src/ServiceStack.Redis/Pipeline/RedisPipelineCommand.cs +++ b/src/ServiceStack.Redis/Pipeline/RedisPipelineCommand.cs @@ -3,7 +3,7 @@ namespace ServiceStack.Redis.Pipeline { - public class RedisPipelineCommand + public partial class RedisPipelineCommand { private readonly RedisNativeClient client; private int cmdCount; diff --git a/src/ServiceStack.Redis/Pipeline/RedisQueueCompletableOperation.cs b/src/ServiceStack.Redis/Pipeline/RedisQueueCompletableOperation.cs index 8a399cac..92b10529 100644 --- a/src/ServiceStack.Redis/Pipeline/RedisQueueCompletableOperation.cs +++ b/src/ServiceStack.Redis/Pipeline/RedisQueueCompletableOperation.cs @@ -7,7 +7,7 @@ namespace ServiceStack.Redis /// /// Redis operation (transaction/pipeline) that allows queued commands to be completed /// - public class RedisQueueCompletableOperation + public partial class RedisQueueCompletableOperation { internal readonly List QueuedCommands = new List(); diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs b/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs new file mode 100644 index 00000000..d8d6dc62 --- /dev/null +++ b/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs @@ -0,0 +1,43 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Caching; +using ServiceStack.Redis.Internal; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + public partial class PooledRedisClientManager + : IRedisClientsManagerAsync + { + ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(CancellationToken cancellationToken) + => new RedisClientManagerCacheClient(this).AsValueTask(); + + ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken cancellationToken) + => GetClient(true).AsValueTask(); + + ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken cancellationToken) + => new RedisClientManagerCacheClient(this) { ReadOnly = true }.AsValueTask(); + + ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken cancellationToken) + => GetReadOnlyClient(true).AsValueTask(); + + ValueTask IAsyncDisposable.DisposeAsync() + { + Dispose(); + return default; + } + } + +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.cs b/src/ServiceStack.Redis/PooledRedisClientManager.cs index ea374a31..9635b4b6 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.cs @@ -216,7 +216,9 @@ protected virtual void OnStart() /// Returns a Read/Write client (The default) using the hosts defined in ReadWriteHosts /// /// - public IRedisClient GetClient() + public IRedisClient GetClient() => GetClient(false); + + private RedisClient GetClient(bool forAsync) { try { @@ -252,7 +254,7 @@ public IRedisClient GetClient() InitClient(inActiveClient); - return !AssertAccessOnlyOnSameThread + return (!AssertAccessOnlyOnSameThread || forAsync) ? inActiveClient : inActiveClient.LimitAccessToThread(Thread.CurrentThread.ManagedThreadId, Environment.StackTrace); } @@ -288,7 +290,7 @@ public IRedisClient GetClient() WritePoolIndex++; writeClients[inactivePoolIndex] = newClient; - return !AssertAccessOnlyOnSameThread + return (!AssertAccessOnlyOnSameThread || forAsync) ? newClient : newClient.LimitAccessToThread(Thread.CurrentThread.ManagedThreadId, Environment.StackTrace); } @@ -364,7 +366,9 @@ private int GetInActiveWriteClient(out RedisClient inactiveClient) /// Returns a ReadOnly client using the hosts defined in ReadOnlyHosts. /// /// - public virtual IRedisClient GetReadOnlyClient() + public virtual IRedisClient GetReadOnlyClient() => GetReadOnlyClient(false); + + private RedisClient GetReadOnlyClient(bool forAsync) { try { diff --git a/src/ServiceStack.Redis/Properties/AssemblyInfo.cs b/src/ServiceStack.Redis/Properties/AssemblyInfo.cs index 0085aaba..07aaea2f 100644 --- a/src/ServiceStack.Redis/Properties/AssemblyInfo.cs +++ b/src/ServiceStack.Redis/Properties/AssemblyInfo.cs @@ -2,4 +2,4 @@ [assembly: ComVisible(false)] [assembly: Guid("70a33fa7-9f81-418d-bb25-6a4be6648ae4")] -[assembly: System.Reflection.AssemblyVersion("5.0.0.0")] +[assembly: System.Reflection.AssemblyVersion("5.0.0.0")] \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClient.Async.cs b/src/ServiceStack.Redis/RedisClient.Async.cs new file mode 100644 index 00000000..0fb8414d --- /dev/null +++ b/src/ServiceStack.Redis/RedisClient.Async.cs @@ -0,0 +1,1524 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis/ +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Caching; +using ServiceStack.Data; +using ServiceStack.Model; +using ServiceStack.Redis.Generic; +using ServiceStack.Redis.Internal; +using ServiceStack.Redis.Pipeline; +using ServiceStack.Text; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + partial class RedisClient : IRedisClientAsync, IRemoveByPatternAsync, ICacheClientAsync + { + /// + /// Access this instance for async usage + /// + public IRedisClientAsync AsAsync() => this; + + // the typed client implements this for us + IRedisTypedClientAsync IRedisClientAsync.As() => (IRedisTypedClientAsync)As(); + + // convenience since we're not saturating the public API; this makes it easy to call + // the explicit interface implementations; the JIT should make this a direct call + private IRedisNativeClientAsync NativeAsync => this; + + IHasNamed IRedisClientAsync.Lists => Lists as IHasNamed ?? throw new NotSupportedException($"The provided Lists ({Lists?.GetType().FullName}) does not support IRedisListAsync"); + IHasNamed IRedisClientAsync.Sets => Sets as IHasNamed ?? throw new NotSupportedException($"The provided Sets ({Sets?.GetType().FullName})does not support IRedisSetAsync"); + IHasNamed IRedisClientAsync.SortedSets => SortedSets as IHasNamed ?? throw new NotSupportedException($"The provided SortedSets ({SortedSets?.GetType().FullName})does not support IRedisSortedSetAsync"); + IHasNamed IRedisClientAsync.Hashes => Hashes as IHasNamed ?? throw new NotSupportedException($"The provided Hashes ({Hashes?.GetType().FullName})does not support IRedisHashAsync"); + + internal ValueTask RegisterTypeIdAsync(T value, CancellationToken cancellationToken) + { + var typeIdsSetKey = GetTypeIdsSetKey(); + var id = value.GetId().ToString(); + + return RegisterTypeIdAsync(typeIdsSetKey, id, cancellationToken); + } + internal ValueTask RegisterTypeIdAsync(string typeIdsSetKey, string id, CancellationToken cancellationToken) + { + if (this.Pipeline != null) + { + var registeredTypeIdsWithinPipeline = GetRegisteredTypeIdsWithinPipeline(typeIdsSetKey); + registeredTypeIdsWithinPipeline.Add(id); + return default; + } + else + { + return AsAsync().AddItemToSetAsync(typeIdsSetKey, id, cancellationToken); + } + } + + // Called just after original Pipeline is closed. + internal async ValueTask AddTypeIdsRegisteredDuringPipelineAsync(CancellationToken cancellationToken) + { + foreach (var entry in registeredTypeIdsWithinPipelineMap) + { + await AsAsync().AddRangeToSetAsync(entry.Key, entry.Value.ToList(), cancellationToken).ConfigureAwait(false); + } + registeredTypeIdsWithinPipelineMap = new Dictionary>(); + } + + + ValueTask IRedisClientAsync.GetServerTimeAsync(CancellationToken cancellationToken) + => NativeAsync.TimeAsync(cancellationToken).Await(parts => ParseTimeResult(parts)); + + IRedisPipelineAsync IRedisClientAsync.CreatePipeline() + => new RedisAllPurposePipeline(this); + + ValueTask IRedisClientAsync.CreateTransactionAsync(CancellationToken cancellationToken) + { + AssertServerVersionNumber(); // pre-fetch call to INFO before transaction if needed + return new RedisTransaction(this, true).AsValueTask(); // note that the MULTI here will be held and flushed async + } + + ValueTask IRedisClientAsync.RemoveEntryAsync(string[] keys, CancellationToken cancellationToken) + => keys.Length == 0 ? default : NativeAsync.DelAsync(keys, cancellationToken).IsSuccessAsync(); + + private async ValueTask ExecAsync(Func action) + { + using (JsConfig.With(new Text.Config { ExcludeTypeInfo = false })) + { + await action(this).ConfigureAwait(false); + } + } + + private async ValueTask ExecAsync(Func> action) + { + using (JsConfig.With(new Text.Config { ExcludeTypeInfo = false })) + { + return await action(this).ConfigureAwait(false); + } + } + + ValueTask IRedisClientAsync.SetValueAsync(string key, string value, CancellationToken cancellationToken) + { + var bytesValue = value?.ToUtf8Bytes(); + return NativeAsync.SetAsync(key, bytesValue, cancellationToken: cancellationToken); + } + + ValueTask IRedisClientAsync.GetValueAsync(string key, CancellationToken cancellationToken) + => NativeAsync.GetAsync(key, cancellationToken).FromUtf8BytesAsync(); + + ValueTask ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) + { + return ExecAsync(r => + typeof(T) == typeof(byte[]) + ? ((IRedisNativeClientAsync)r).GetAsync(key, cancellationToken).Await(val => (T)(object)val) + : r.GetValueAsync(key, cancellationToken).Await(val => JsonSerializer.DeserializeFromString(val)) + ); + } + + async ValueTask> IRedisClientAsync.SearchKeysAsync(string pattern, CancellationToken cancellationToken) + { + var list = new List(); + await foreach (var value in ((IRedisClientAsync)this).ScanAllKeysAsync(pattern, cancellationToken: cancellationToken).WithCancellation(cancellationToken).ConfigureAwait(false)) + { + list.Add(value); + } + return list; + } + + async IAsyncEnumerable IRedisClientAsync.ScanAllKeysAsync(string pattern, int pageSize, [EnumeratorCancellation] CancellationToken cancellationToken) + { + ScanResult ret = default; + while (true) + { + ret = await (pattern != null // note ConfigureAwait is handled below + ? NativeAsync.ScanAsync(ret?.Cursor ?? 0, pageSize, match: pattern, cancellationToken: cancellationToken) + : NativeAsync.ScanAsync(ret?.Cursor ?? 0, pageSize, cancellationToken: cancellationToken) + ).ConfigureAwait(false); + + foreach (var key in ret.Results) + { + yield return key.FromUtf8Bytes(); + } + + if (ret.Cursor == 0) break; + } + } + + ValueTask IRedisClientAsync.GetEntryTypeAsync(string key, CancellationToken cancellationToken) + => NativeAsync.TypeAsync(key, cancellationToken).Await((val, state) => state.ParseEntryType(val), this); + + ValueTask IRedisClientAsync.AddItemToSetAsync(string setId, string item, CancellationToken cancellationToken) + => NativeAsync.SAddAsync(setId, item.ToUtf8Bytes(), cancellationToken).Await(); + + ValueTask IRedisClientAsync.AddItemToListAsync(string listId, string value, CancellationToken cancellationToken) + => NativeAsync.RPushAsync(listId, value.ToUtf8Bytes(), cancellationToken).Await(); + + ValueTask IRedisClientAsync.AddItemToSortedSetAsync(string setId, string value, CancellationToken cancellationToken) + => ((IRedisClientAsync)this).AddItemToSortedSetAsync(setId, value, GetLexicalScore(value), cancellationToken); + + ValueTask IRedisClientAsync.AddItemToSortedSetAsync(string setId, string value, double score, CancellationToken cancellationToken) + => NativeAsync.ZAddAsync(setId, score, value.ToUtf8Bytes()).IsSuccessAsync(); + + ValueTask IRedisClientAsync.SetEntryInHashAsync(string hashId, string key, string value, CancellationToken cancellationToken) + => NativeAsync.HSetAsync(hashId, key.ToUtf8Bytes(), value.ToUtf8Bytes()).IsSuccessAsync(); + + ValueTask IRedisClientAsync.SetAllAsync(IDictionary map, CancellationToken cancellationToken) + => GetSetAllBytes(map, out var keyBytes, out var valBytes) ? NativeAsync.MSetAsync(keyBytes, valBytes, cancellationToken) : default; + + ValueTask IRedisClientAsync.SetAllAsync(IEnumerable keys, IEnumerable values, CancellationToken cancellationToken) + => GetSetAllBytes(keys, values, out var keyBytes, out var valBytes) ? NativeAsync.MSetAsync(keyBytes, valBytes, cancellationToken) : default; + + ValueTask ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) + { + if (values.Count != 0) + { + return ExecAsync(r => + { + // need to do this inside Exec for the JSON config bits + GetSetAllBytesTyped(values, out var keys, out var valBytes); + return ((IRedisNativeClientAsync)r).MSetAsync(keys, valBytes, cancellationToken); + }); + } + else + { + return default; + } + } + + ValueTask IRedisClientAsync.RenameKeyAsync(string fromName, string toName, CancellationToken cancellationToken) + => NativeAsync.RenameAsync(fromName, toName, cancellationToken); + + ValueTask IRedisClientAsync.ContainsKeyAsync(string key, CancellationToken cancellationToken) + => NativeAsync.ExistsAsync(key, cancellationToken).IsSuccessAsync(); + + + ValueTask IRedisClientAsync.GetRandomKeyAsync(CancellationToken cancellationToken) + => NativeAsync.RandomKeyAsync(cancellationToken); + + ValueTask IRedisClientAsync.SelectAsync(long db, CancellationToken cancellationToken) + => NativeAsync.SelectAsync(db, cancellationToken); + + ValueTask IRedisClientAsync.ExpireEntryInAsync(string key, TimeSpan expireIn, CancellationToken cancellationToken) + => UseMillisecondExpiration(expireIn) + ? NativeAsync.PExpireAsync(key, (long)expireIn.TotalMilliseconds, cancellationToken) + : NativeAsync.ExpireAsync(key, (int)expireIn.TotalSeconds, cancellationToken); + + ValueTask IRedisClientAsync.ExpireEntryAtAsync(string key, DateTime expireAt, CancellationToken cancellationToken) + => AssertServerVersionNumber() >= 2600 + ? NativeAsync.PExpireAtAsync(key, ConvertToServerDate(expireAt).ToUnixTimeMs(), cancellationToken) + : NativeAsync.ExpireAtAsync(key, ConvertToServerDate(expireAt).ToUnixTime(), cancellationToken); + + ValueTask ICacheClientExtendedAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) + => NativeAsync.TtlAsync(key, cancellationToken).Await(ttlSecs => ParseTimeToLiveResult(ttlSecs)); + + ValueTask IRedisClientAsync.PingAsync(CancellationToken cancellationToken) + => NativeAsync.PingAsync(cancellationToken); + + ValueTask IRedisClientAsync.EchoAsync(string text, CancellationToken cancellationToken) + => NativeAsync.EchoAsync(text, cancellationToken); + + ValueTask IRedisClientAsync.ForegroundSaveAsync(CancellationToken cancellationToken) + => NativeAsync.SaveAsync(cancellationToken); + + ValueTask IRedisClientAsync.BackgroundSaveAsync(CancellationToken cancellationToken) + => NativeAsync.BgSaveAsync(cancellationToken); + + ValueTask IRedisClientAsync.ShutdownAsync(CancellationToken cancellationToken) + => NativeAsync.ShutdownAsync(false, cancellationToken); + + ValueTask IRedisClientAsync.ShutdownNoSaveAsync(CancellationToken cancellationToken) + => NativeAsync.ShutdownAsync(true, cancellationToken); + + ValueTask IRedisClientAsync.BackgroundRewriteAppendOnlyFileAsync(CancellationToken cancellationToken) + => NativeAsync.BgRewriteAofAsync(cancellationToken); + + ValueTask IRedisClientAsync.FlushDbAsync(CancellationToken cancellationToken) + => NativeAsync.FlushDbAsync(cancellationToken); + + ValueTask> IRedisClientAsync.GetValuesAsync(List keys, CancellationToken cancellationToken) + { + if (keys == null) throw new ArgumentNullException(nameof(keys)); + if (keys.Count == 0) return new List().AsValueTask(); + + return NativeAsync.MGetAsync(keys.ToArray(), cancellationToken).Await(val => ParseGetValuesResult(val)); + } + + ValueTask> IRedisClientAsync.GetValuesAsync(List keys, CancellationToken cancellationToken) + { + if (keys == null) throw new ArgumentNullException(nameof(keys)); + if (keys.Count == 0) return new List().AsValueTask(); + + return NativeAsync.MGetAsync(keys.ToArray(), cancellationToken).Await(value => ParseGetValuesResult(value)); + } + + ValueTask> IRedisClientAsync.GetValuesMapAsync(List keys, CancellationToken cancellationToken) + { + if (keys == null) throw new ArgumentNullException(nameof(keys)); + if (keys.Count == 0) return new Dictionary().AsValueTask(); + + var keysArray = keys.ToArray(); + return NativeAsync.MGetAsync(keysArray, cancellationToken).Await((resultBytesArray, state) => ParseGetValuesMapResult(state, resultBytesArray), keysArray); + } + + ValueTask> IRedisClientAsync.GetValuesMapAsync(List keys, CancellationToken cancellationToken) + { + if (keys == null) throw new ArgumentNullException(nameof(keys)); + if (keys.Count == 0) return new Dictionary().AsValueTask(); + + var keysArray = keys.ToArray(); + return NativeAsync.MGetAsync(keysArray, cancellationToken).Await((resultBytesArray, state) => ParseGetValuesMapResult(state, resultBytesArray), keysArray); + } + + ValueTask IRedisClientAsync.AcquireLockAsync(string key, TimeSpan? timeOut, CancellationToken cancellationToken) + => RedisLock.CreateAsync(this, key, timeOut, cancellationToken).Await(value => value); + + ValueTask IRedisClientAsync.SetValueAsync(string key, string value, TimeSpan expireIn, CancellationToken cancellationToken) + { + var bytesValue = value?.ToUtf8Bytes(); + + if (AssertServerVersionNumber() >= 2610) + { + PickTime(expireIn, out var seconds, out var milliseconds); + return NativeAsync.SetAsync(key, bytesValue, expirySeconds: seconds, + expiryMilliseconds: milliseconds, cancellationToken: cancellationToken); + } + else + { + return NativeAsync.SetExAsync(key, (int)expireIn.TotalSeconds, bytesValue, cancellationToken); + } + } + + static void PickTime(TimeSpan? value, out long expirySeconds, out long expiryMilliseconds) + { + expirySeconds = expiryMilliseconds = 0; + if (value.HasValue) + { + var expireIn = value.GetValueOrDefault(); + if (expireIn.Milliseconds > 0) + { + expiryMilliseconds = (long)expireIn.TotalMilliseconds; + } + else + { + expirySeconds = (long)expireIn.TotalSeconds; + } + } + } + ValueTask IRedisClientAsync.SetValueIfNotExistsAsync(string key, string value, TimeSpan? expireIn, CancellationToken cancellationToken) + { + var bytesValue = value?.ToUtf8Bytes(); + PickTime(expireIn, out var seconds, out var milliseconds); + return NativeAsync.SetAsync(key, bytesValue, false, seconds, milliseconds, cancellationToken); + } + + ValueTask IRedisClientAsync.SetValueIfExistsAsync(string key, string value, TimeSpan? expireIn, CancellationToken cancellationToken) + { + var bytesValue = value?.ToUtf8Bytes(); + PickTime(expireIn, out var seconds, out var milliseconds); + return NativeAsync.SetAsync(key, bytesValue, true, seconds, milliseconds, cancellationToken); + } + + ValueTask IRedisClientAsync.WatchAsync(string[] keys, CancellationToken cancellationToken) + => NativeAsync.WatchAsync(keys, cancellationToken); + + ValueTask IRedisClientAsync.UnWatchAsync(CancellationToken cancellationToken) + => NativeAsync.UnWatchAsync(cancellationToken); + + ValueTask IRedisClientAsync.AppendToValueAsync(string key, string value, CancellationToken cancellationToken) + => NativeAsync.AppendAsync(key, value.ToUtf8Bytes(), cancellationToken); + + async ValueTask IRedisClientAsync.StoreObjectAsync(object entity, CancellationToken cancellationToken) + { + if (entity == null) throw new ArgumentNullException(nameof(entity)); + + var id = entity.GetObjectId(); + var entityType = entity.GetType(); + var urnKey = UrnKey(entityType, id); + var valueString = JsonSerializer.SerializeToString(entity); + + await ((IRedisClientAsync)this).SetValueAsync(urnKey, valueString, cancellationToken).ConfigureAwait(false); + + await RegisterTypeIdAsync(GetTypeIdsSetKey(entityType), id.ToString(), cancellationToken).ConfigureAwait(false); + + return entity; + } + + ValueTask IRedisClientAsync.PopItemFromSetAsync(string setId, CancellationToken cancellationToken) + => NativeAsync.SPopAsync(setId, cancellationToken).FromUtf8BytesAsync(); + + ValueTask> IRedisClientAsync.PopItemsFromSetAsync(string setId, int count, CancellationToken cancellationToken) + => NativeAsync.SPopAsync(setId, count, cancellationToken).ToStringListAsync(); + + ValueTask IRedisClientAsync.SlowlogResetAsync(CancellationToken cancellationToken) + => NativeAsync.SlowlogResetAsync(cancellationToken); + + ValueTask IRedisClientAsync.GetSlowlogAsync(int? numberOfRecords, CancellationToken cancellationToken) + => NativeAsync.SlowlogGetAsync(numberOfRecords, cancellationToken).Await(data => ParseSlowlog(data)); + + + ValueTask ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), cancellationToken: cancellationToken)).AwaitAsTrue(); + + ValueTask IAsyncDisposable.DisposeAsync() + { + Dispose(); + return default; + } + + ValueTask IRedisClientAsync.GetSortedSetCountAsync(string setId, CancellationToken cancellationToken) + => NativeAsync.ZCardAsync(setId, cancellationToken); + + ValueTask IRedisClientAsync.GetSortedSetCountAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken) + { + var fromScore = GetLexicalScore(fromStringScore); + var toScore = GetLexicalScore(toStringScore); + return AsAsync().GetSortedSetCountAsync(setId, fromScore, toScore, cancellationToken); + } + + ValueTask IRedisClientAsync.GetSortedSetCountAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) + => NativeAsync.ZCountAsync(setId, fromScore, toScore, cancellationToken); + + ValueTask IRedisClientAsync.GetSortedSetCountAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) + => NativeAsync.ZCountAsync(setId, fromScore, toScore, cancellationToken); + + ValueTask IRedisClientAsync.GetItemScoreInSortedSetAsync(string setId, string value, CancellationToken cancellationToken) + => NativeAsync.ZScoreAsync(setId, value.ToUtf8Bytes(), cancellationToken); + + ValueTask IRedisClientAsync.CustomAsync(object[] cmdWithArgs, CancellationToken cancellationToken) + => RawCommandAsync(cancellationToken, cmdWithArgs).Await(result => result.ToRedisText()); + + ValueTask IRedisClientAsync.SetValuesAsync(IDictionary map, CancellationToken cancellationToken) + => ((IRedisClientAsync)this).SetAllAsync(map, cancellationToken); + + ValueTask ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + { + AssertNotInTransaction(); + return ExecAsync(async r => + { + await r.SetAsync(key, value).ConfigureAwait(false); + await r.ExpireEntryAtAsync(key, ConvertToServerDate(expiresAt)).ConfigureAwait(false); + }).AwaitAsTrue(); + } + ValueTask ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + { + if (AssertServerVersionNumber() >= 2600) + { + return ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), 0, expiryMilliseconds: (long)expiresIn.TotalMilliseconds)).AwaitAsTrue(); + } + else + { + return ExecAsync(r => ((IRedisNativeClientAsync)r).SetExAsync(key, (int)expiresIn.TotalSeconds, ToBytes(value))).AwaitAsTrue(); + } + } + + ValueTask ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) + => NativeAsync.FlushAllAsync(cancellationToken); + + ValueTask> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) + { + return ExecAsync(r => + { + var keysArray = keys.ToArray(); + + return ((IRedisNativeClientAsync)r).MGetAsync(keysArray, cancellationToken).Await((keyValues, state) => ProcessGetAllResult(state, keyValues), keysArray); + }); + } + + ValueTask ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) + => NativeAsync.DelAsync(key, cancellationToken).IsSuccessAsync(); + + IAsyncEnumerable ICacheClientExtendedAsync.GetKeysByPatternAsync(string pattern, CancellationToken cancellationToken) + => AsAsync().ScanAllKeysAsync(pattern, cancellationToken: cancellationToken); + + ValueTask ICacheClientExtendedAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationToken) + { + //Redis automatically removed expired Cache Entries + return default; + } + + async ValueTask IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, CancellationToken cancellationToken) + { + List buffer = null; + const int BATCH_SIZE = 1024; + await foreach (var key in AsAsync().ScanAllKeysAsync(pattern, cancellationToken: cancellationToken).WithCancellation(cancellationToken).ConfigureAwait(false)) + { + (buffer ??= new List()).Add(key); + if (buffer.Count == BATCH_SIZE) + { + await NativeAsync.DelAsync(buffer.ToArray(), cancellationToken).ConfigureAwait(false); + buffer.Clear(); + } + } + if (buffer is object && buffer.Count != 0) + { + await NativeAsync.DelAsync(buffer.ToArray(), cancellationToken).ConfigureAwait(false); + } + } + + ValueTask IRemoveByPatternAsync.RemoveByRegexAsync(string regex, CancellationToken cancellationToken) + => AsAsync().RemoveByPatternAsync(RegexToGlob(regex), cancellationToken); + + ValueTask ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) + => ExecAsync(r => r.RemoveEntryAsync(keys.ToArray(), cancellationToken)).Await(); + + ValueTask ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) + => ExecAsync(r => r.IncrementValueByAsync(key, (int)amount, cancellationToken)); + + ValueTask ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) + => ExecAsync(r => r.DecrementValueByAsync(key, (int)amount, cancellationToken)); + + + ValueTask ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: false, cancellationToken: cancellationToken)); + + ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: true, cancellationToken: cancellationToken)); + + ValueTask ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + { + AssertNotInTransaction(); + + return ExecAsync(async r => + { + if (await r.AddAsync(key, value, cancellationToken).ConfigureAwait(false)) + { + await r.ExpireEntryAtAsync(key, ConvertToServerDate(expiresAt), cancellationToken).ConfigureAwait(false); + return true; + } + return false; + }); + } + + ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + { + AssertNotInTransaction(); + + return ExecAsync(async r => + { + if (await r.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false)) + { + await r.ExpireEntryAtAsync(key, ConvertToServerDate(expiresAt), cancellationToken).ConfigureAwait(false); + return true; + } + return false; + }); + } + + ValueTask ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: false, cancellationToken: cancellationToken)); + + ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: true, cancellationToken: cancellationToken)); + + ValueTask IRedisClientAsync.DbSizeAsync(CancellationToken cancellationToken) + => NativeAsync.DbSizeAsync(cancellationToken); + + ValueTask> IRedisClientAsync.InfoAsync(CancellationToken cancellationToken) + => NativeAsync.InfoAsync(cancellationToken); + + ValueTask IRedisClientAsync.LastSaveAsync(CancellationToken cancellationToken) + => NativeAsync.LastSaveAsync(cancellationToken); + + async ValueTask IEntityStoreAsync.GetByIdAsync(object id, CancellationToken cancellationToken) + { + var key = UrnKey(id); + var valueString = await AsAsync().GetValueAsync(key, cancellationToken).ConfigureAwait(false); + var value = JsonSerializer.DeserializeFromString(valueString); + return value; + } + + async ValueTask> IEntityStoreAsync.GetByIdsAsync(ICollection ids, CancellationToken cancellationToken) + { + if (ids == null || ids.Count == 0) + return new List(); + + var urnKeys = ids.Cast().Map(UrnKey); + return await AsAsync().GetValuesAsync(urnKeys, cancellationToken).ConfigureAwait(false); + } + + async ValueTask IEntityStoreAsync.StoreAsync(T entity, CancellationToken cancellationToken) + { + var urnKey = UrnKey(entity); + var valueString = JsonSerializer.SerializeToString(entity); + + await AsAsync().SetValueAsync(urnKey, valueString, cancellationToken).ConfigureAwait(false); + await RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); + + return entity; + } + + ValueTask IEntityStoreAsync.StoreAllAsync(IEnumerable entities, CancellationToken cancellationToken) + => StoreAllAsyncImpl(entities, cancellationToken); + + internal async ValueTask StoreAllAsyncImpl(IEnumerable entities, CancellationToken cancellationToken) + { + if (PrepareStoreAll(entities, out var keys, out var values, out var entitiesList)) + { + await NativeAsync.MSetAsync(keys, values, cancellationToken).ConfigureAwait(false); + await RegisterTypeIdsAsync(entitiesList, cancellationToken).ConfigureAwait(false); + } + } + + internal ValueTask RegisterTypeIdsAsync(IEnumerable values, CancellationToken cancellationToken) + { + var typeIdsSetKey = GetTypeIdsSetKey(); + var ids = values.Map(x => x.GetId().ToString()); + + if (this.Pipeline != null) + { + var registeredTypeIdsWithinPipeline = GetRegisteredTypeIdsWithinPipeline(typeIdsSetKey); + ids.ForEach(x => registeredTypeIdsWithinPipeline.Add(x)); + return default; + } + else + { + return AsAsync().AddRangeToSetAsync(typeIdsSetKey, ids, cancellationToken); + } + } + + internal async ValueTask RemoveTypeIdsAsync(T[] values, CancellationToken cancellationToken) + { + var typeIdsSetKey = GetTypeIdsSetKey(); + if (this.Pipeline != null) + { + var registeredTypeIdsWithinPipeline = GetRegisteredTypeIdsWithinPipeline(typeIdsSetKey); + values.Each(x => registeredTypeIdsWithinPipeline.Remove(x.GetId().ToString())); + } + else + { + foreach (var x in values) + { + await AsAsync().RemoveItemFromSetAsync(typeIdsSetKey, x.GetId().ToString(), cancellationToken).ConfigureAwait(false); + } + } + } + + internal async ValueTask RemoveTypeIdsAsync(string[] ids, CancellationToken cancellationToken) + { + var typeIdsSetKey = GetTypeIdsSetKey(); + if (this.Pipeline != null) + { + var registeredTypeIdsWithinPipeline = GetRegisteredTypeIdsWithinPipeline(typeIdsSetKey); + ids.Each(x => registeredTypeIdsWithinPipeline.Remove(x)); + } + else + { + foreach (var x in ids) + { + await AsAsync().RemoveItemFromSetAsync(typeIdsSetKey, x, cancellationToken).ConfigureAwait(false); + } + } + } + + async ValueTask IEntityStoreAsync.DeleteAsync(T entity, CancellationToken cancellationToken) + { + var urnKey = UrnKey(entity); + await AsAsync().RemoveAsync(urnKey, cancellationToken).ConfigureAwait(false); + await this.RemoveTypeIdsAsync(new[] { entity }, cancellationToken).ConfigureAwait(false); + } + + async ValueTask IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken cancellationToken) + { + var urnKey = UrnKey(id); + await AsAsync().RemoveAsync(urnKey, cancellationToken).ConfigureAwait(false); + await this.RemoveTypeIdsAsync(new[] { id.ToString() }, cancellationToken).ConfigureAwait(false); + } + + async ValueTask IEntityStoreAsync.DeleteByIdsAsync(ICollection ids, CancellationToken cancellationToken) + { + if (ids == null || ids.Count == 0) return; + + var idsList = ids.Cast(); + var urnKeys = idsList.Map(UrnKey); + await AsAsync().RemoveEntryAsync(urnKeys.ToArray(), cancellationToken).ConfigureAwait(false); + await this.RemoveTypeIdsAsync(idsList.Map(x => x.ToString()).ToArray(), cancellationToken).ConfigureAwait(false); + } + + async ValueTask IEntityStoreAsync.DeleteAllAsync(CancellationToken cancellationToken) + { + var typeIdsSetKey = this.GetTypeIdsSetKey(); + var ids = await AsAsync().GetAllItemsFromSetAsync(typeIdsSetKey, cancellationToken).ConfigureAwait(false); + if (ids.Count > 0) + { + var urnKeys = ids.ToList().ConvertAll(UrnKey); + await AsAsync().RemoveEntryAsync(urnKeys.ToArray()).ConfigureAwait(false); + await AsAsync().RemoveAsync(typeIdsSetKey).ConfigureAwait(false); + } + } + + ValueTask> IRedisClientAsync.SearchSortedSetAsync(string setId, string start, string end, int? skip, int? take, CancellationToken cancellationToken) + { + start = GetSearchStart(start); + end = GetSearchEnd(end); + + return NativeAsync.ZRangeByLexAsync(setId, start, end, skip, take, cancellationToken).ToStringListAsync(); + } + + ValueTask IRedisClientAsync.SearchSortedSetCountAsync(string setId, string start, string end, CancellationToken cancellationToken) + => NativeAsync.ZLexCountAsync(setId, GetSearchStart(start), GetSearchEnd(end), cancellationToken); + + ValueTask IRedisClientAsync.RemoveRangeFromSortedSetBySearchAsync(string setId, string start, string end, CancellationToken cancellationToken) + => NativeAsync.ZRemRangeByLexAsync(setId, GetSearchStart(start), GetSearchEnd(end), cancellationToken); + + ValueTask IRedisClientAsync.TypeAsync(string key, CancellationToken cancellationToken) + => NativeAsync.TypeAsync(key, cancellationToken); + + ValueTask IRedisClientAsync.GetStringCountAsync(string key, CancellationToken cancellationToken) + => NativeAsync.StrLenAsync(key, cancellationToken); + + ValueTask IRedisClientAsync.GetSetCountAsync(string setId, CancellationToken cancellationToken) + => NativeAsync.SCardAsync(setId, cancellationToken); + + ValueTask IRedisClientAsync.GetListCountAsync(string listId, CancellationToken cancellationToken) + => NativeAsync.LLenAsync(listId, cancellationToken); + + ValueTask IRedisClientAsync.GetHashCountAsync(string hashId, CancellationToken cancellationToken) + => NativeAsync.HLenAsync(hashId, cancellationToken); + + async ValueTask IRedisClientAsync.ExecCachedLuaAsync(string scriptBody, Func> scriptSha1, CancellationToken cancellationToken) + { + if (!CachedLuaSha1Map.TryGetValue(scriptBody, out var sha1)) + CachedLuaSha1Map[scriptBody] = sha1 = await AsAsync().LoadLuaScriptAsync(scriptBody, cancellationToken).ConfigureAwait(false); + + try + { + return await scriptSha1(sha1).ConfigureAwait(false); + } + catch (RedisResponseException ex) + { + if (!ex.Message.StartsWith("NOSCRIPT")) + throw; + + CachedLuaSha1Map[scriptBody] = sha1 = await AsAsync().LoadLuaScriptAsync(scriptBody, cancellationToken).ConfigureAwait(false); + return await scriptSha1(sha1).ConfigureAwait(false); + } + } + + ValueTask IRedisClientAsync.ExecLuaAsync(string luaBody, string[] keys, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalCommandAsync(luaBody, keys?.Length ?? 0, MergeAndConvertToBytes(keys, args), cancellationToken).Await(data => data.ToRedisText()); + + ValueTask IRedisClientAsync.ExecLuaShaAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalShaCommandAsync(sha1, keys?.Length ?? 0, MergeAndConvertToBytes(keys, args), cancellationToken).Await(data => data.ToRedisText()); + + ValueTask IRedisClientAsync.ExecLuaAsStringAsync(string luaBody, string[] keys, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalStrAsync(luaBody, keys?.Length ?? 0, MergeAndConvertToBytes(keys, args), cancellationToken); + + ValueTask IRedisClientAsync.ExecLuaShaAsStringAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalShaStrAsync(sha1, keys?.Length ?? 0, MergeAndConvertToBytes(keys, args), cancellationToken); + + ValueTask IRedisClientAsync.LoadLuaScriptAsync(string body, CancellationToken cancellationToken) + => NativeAsync.ScriptLoadAsync(body, cancellationToken).FromUtf8BytesAsync(); + + ValueTask IRedisClientAsync.WriteAllAsync(IEnumerable entities, CancellationToken cancellationToken) + => PrepareWriteAll(entities, out var keys, out var values) ? NativeAsync.MSetAsync(keys, values, cancellationToken) : default; + + async ValueTask> IRedisClientAsync.GetAllItemsFromSetAsync(string setId, CancellationToken cancellationToken) + { + var multiDataList = await NativeAsync.SMembersAsync(setId, cancellationToken).ConfigureAwait(false); + return CreateHashSet(multiDataList); + } + + async ValueTask IRedisClientAsync.AddRangeToSetAsync(string setId, List items, CancellationToken cancellationToken) + { + if (await AddRangeToSetNeedsSendAsync(setId, items).ConfigureAwait(false)) + { + var uSetId = setId.ToUtf8Bytes(); + var pipeline = CreatePipelineCommand(); + foreach (var item in items) + { + pipeline.WriteCommand(Commands.SAdd, uSetId, item.ToUtf8Bytes()); + } + await pipeline.FlushAsync(cancellationToken).ConfigureAwait(false); + + //the number of items after + _ = await pipeline.ReadAllAsIntsAsync(cancellationToken).ConfigureAwait(false); + } + } + + async ValueTask AddRangeToSetNeedsSendAsync(string setId, List items) + { + if (setId.IsNullOrEmpty()) + throw new ArgumentNullException("setId"); + if (items == null) + throw new ArgumentNullException("items"); + if (items.Count == 0) + return false; + + if (this.Transaction is object || this.PipelineAsync is object) + { + var queueable = this.Transaction as IRedisQueueableOperationAsync + ?? this.Pipeline as IRedisQueueableOperationAsync; + + if (queueable == null) + throw new NotSupportedException("Cannot AddRangeToSetAsync() when Transaction is: " + this.Transaction.GetType().Name); + + //Complete the first QueuedCommand() + await AsAsync().AddItemToSetAsync(setId, items[0]).ConfigureAwait(false); + + //Add subsequent queued commands + for (var i = 1; i < items.Count; i++) + { + var item = items[i]; + queueable.QueueCommand(c => c.AddItemToSetAsync(setId, item)); + } + return false; + } + else + { + return true; + } + } + + ValueTask IRedisClientAsync.RemoveItemFromSetAsync(string setId, string item, CancellationToken cancellationToken) + => NativeAsync.SRemAsync(setId, item.ToUtf8Bytes(), cancellationToken).Await(); + + ValueTask IRedisClientAsync.IncrementValueByAsync(string key, int count, CancellationToken cancellationToken) + => NativeAsync.IncrByAsync(key, count, cancellationToken); + + ValueTask IRedisClientAsync.IncrementValueByAsync(string key, long count, CancellationToken cancellationToken) + => NativeAsync.IncrByAsync(key, count, cancellationToken); + + ValueTask IRedisClientAsync.IncrementValueByAsync(string key, double count, CancellationToken cancellationToken) + => NativeAsync.IncrByFloatAsync(key, count, cancellationToken); + ValueTask IRedisClientAsync.IncrementValueAsync(string key, CancellationToken cancellationToken) + => NativeAsync.IncrAsync(key, cancellationToken); + + ValueTask IRedisClientAsync.DecrementValueAsync(string key, CancellationToken cancellationToken) + => NativeAsync.DecrAsync(key, cancellationToken); + + ValueTask IRedisClientAsync.DecrementValueByAsync(string key, int count, CancellationToken cancellationToken) + => NativeAsync.DecrByAsync(key, count, cancellationToken); + + async ValueTask IRedisClientAsync.GetServerRoleAsync(CancellationToken cancellationToken) + { + if (AssertServerVersionNumber() >= 2812) + { + var text = await NativeAsync.RoleAsync(cancellationToken).ConfigureAwait(false); + var roleName = text.Children[0].Text; + return ToServerRole(roleName); + } + + var info = await AsAsync().InfoAsync(cancellationToken).ConfigureAwait(false); + info.TryGetValue("role", out var role); + return ToServerRole(role); + } + + ValueTask IRedisClientAsync.GetServerRoleInfoAsync(CancellationToken cancellationToken) + => NativeAsync.RoleAsync(cancellationToken); + + async ValueTask IRedisClientAsync.GetConfigAsync(string configItem, CancellationToken cancellationToken) + { + var byteArray = await NativeAsync.ConfigGetAsync(configItem, cancellationToken).ConfigureAwait(false); + return GetConfigParse(byteArray); + } + + ValueTask IRedisClientAsync.SetConfigAsync(string configItem, string value, CancellationToken cancellationToken) + => NativeAsync.ConfigSetAsync(configItem, value.ToUtf8Bytes(), cancellationToken); + + ValueTask IRedisClientAsync.SaveConfigAsync(CancellationToken cancellationToken) + => NativeAsync.ConfigRewriteAsync(cancellationToken); + + ValueTask IRedisClientAsync.ResetInfoStatsAsync(CancellationToken cancellationToken) + => NativeAsync.ConfigResetStatAsync(cancellationToken); + + ValueTask IRedisClientAsync.GetClientAsync(CancellationToken cancellationToken) + => NativeAsync.ClientGetNameAsync(cancellationToken); + + ValueTask IRedisClientAsync.SetClientAsync(string name, CancellationToken cancellationToken) + => NativeAsync.ClientSetNameAsync(name, cancellationToken); + + ValueTask IRedisClientAsync.KillClientAsync(string address, CancellationToken cancellationToken) + => NativeAsync.ClientKillAsync(address, cancellationToken); + + ValueTask IRedisClientAsync.KillClientsAsync(string fromAddress, string withId, RedisClientType? ofType, bool? skipMe, CancellationToken cancellationToken) + { + var typeString = ofType?.ToString().ToLower(); + var skipMeString = skipMe.HasValue ? (skipMe.Value ? "yes" : "no") : null; + return NativeAsync.ClientKillAsync(addr: fromAddress, id: withId, type: typeString, skipMe: skipMeString, cancellationToken); + } + + async ValueTask>> IRedisClientAsync.GetClientsInfoAsync(CancellationToken cancellationToken) + => GetClientsInfoParse(await NativeAsync.ClientListAsync(cancellationToken).ConfigureAwait(false)); + + ValueTask IRedisClientAsync.PauseAllClientsAsync(TimeSpan duration, CancellationToken cancellationToken) + => NativeAsync.ClientPauseAsync((int)duration.TotalMilliseconds, cancellationToken); + + ValueTask> IRedisClientAsync.GetAllKeysAsync(CancellationToken cancellationToken) + => AsAsync().SearchKeysAsync("*", cancellationToken); + + ValueTask IRedisClientAsync.GetAndSetValueAsync(string key, string value, CancellationToken cancellationToken) + => NativeAsync.GetSetAsync(key, value.ToUtf8Bytes(), cancellationToken).FromUtf8BytesAsync(); + + async ValueTask IRedisClientAsync.GetFromHashAsync(object id, CancellationToken cancellationToken) + { + var key = UrnKey(id); + return (await AsAsync().GetAllEntriesFromHashAsync(key, cancellationToken).ConfigureAwait(false)).ToJson().FromJson(); + } + + async ValueTask IRedisClientAsync.StoreAsHashAsync(T entity, CancellationToken cancellationToken) + { + var key = UrnKey(entity); + var hash = ConvertToHashFn(entity); + await AsAsync().SetRangeInHashAsync(key, hash, cancellationToken).ConfigureAwait(false); + await RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); + } + + ValueTask> IRedisClientAsync.GetSortedEntryValuesAsync(string setId, int startingFrom, int endingAt, CancellationToken cancellationToken) + { + var sortOptions = new SortOptions { Skip = startingFrom, Take = endingAt, }; + return NativeAsync.SortAsync(setId, sortOptions, cancellationToken).ToStringListAsync(); + } + + async IAsyncEnumerable IRedisClientAsync.ScanAllSetItemsAsync(string setId, string pattern, int pageSize, [EnumeratorCancellation] CancellationToken cancellationToken) + { + var ret = new ScanResult(); + while (true) + { + ret = await (pattern != null // note ConfigureAwait is handled below + ? NativeAsync.SScanAsync(setId, ret.Cursor, pageSize, match: pattern, cancellationToken: cancellationToken) + : NativeAsync.SScanAsync(setId, ret.Cursor, pageSize, cancellationToken: cancellationToken) + ).ConfigureAwait(false); + + foreach (var key in ret.Results) + { + yield return key.FromUtf8Bytes(); + } + + if (ret.Cursor == 0) break; + } + } + + async IAsyncEnumerable> IRedisClientAsync.ScanAllSortedSetItemsAsync(string setId, string pattern, int pageSize, [EnumeratorCancellation] CancellationToken cancellationToken) + { + var ret = new ScanResult(); + while (true) + { + ret = await (pattern != null // note ConfigureAwait is handled below + ? NativeAsync.ZScanAsync(setId, ret.Cursor, pageSize, match: pattern, cancellationToken: cancellationToken) + : NativeAsync.ZScanAsync(setId, ret.Cursor, pageSize, cancellationToken: cancellationToken) + ).ConfigureAwait(false); + + foreach (var entry in ret.AsItemsWithScores()) + { + yield return entry; + } + + if (ret.Cursor == 0) break; + } + } + + async IAsyncEnumerable> IRedisClientAsync.ScanAllHashEntriesAsync(string hashId, string pattern, int pageSize, [EnumeratorCancellation] CancellationToken cancellationToken) + { + var ret = new ScanResult(); + while (true) + { + ret = await (pattern != null // note ConfigureAwait is handled below + ? NativeAsync.HScanAsync(hashId, ret.Cursor, pageSize, match: pattern, cancellationToken: cancellationToken) + : NativeAsync.HScanAsync(hashId, ret.Cursor, pageSize, cancellationToken: cancellationToken) + ).ConfigureAwait(false); + + foreach (var entry in ret.AsKeyValues()) + { + yield return entry; + } + + if (ret.Cursor == 0) break; + } + } + + ValueTask IRedisClientAsync.AddToHyperLogAsync(string key, string[] elements, CancellationToken cancellationToken) + => NativeAsync.PfAddAsync(key, elements.Map(x => x.ToUtf8Bytes()).ToArray(), cancellationToken); + + ValueTask IRedisClientAsync.CountHyperLogAsync(string key, CancellationToken cancellationToken) + => NativeAsync.PfCountAsync(key, cancellationToken); + + ValueTask IRedisClientAsync.MergeHyperLogsAsync(string toKey, string[] fromKeys, CancellationToken cancellationToken) + => NativeAsync.PfMergeAsync(toKey, fromKeys, cancellationToken); + + ValueTask IRedisClientAsync.AddGeoMemberAsync(string key, double longitude, double latitude, string member, CancellationToken cancellationToken) + => NativeAsync.GeoAddAsync(key, longitude, latitude, member, cancellationToken); + + ValueTask IRedisClientAsync.AddGeoMembersAsync(string key, RedisGeo[] geoPoints, CancellationToken cancellationToken) + => NativeAsync.GeoAddAsync(key, geoPoints, cancellationToken); + + ValueTask IRedisClientAsync.CalculateDistanceBetweenGeoMembersAsync(string key, string fromMember, string toMember, string unit, CancellationToken cancellationToken) + => NativeAsync.GeoDistAsync(key, fromMember, toMember, unit, cancellationToken); + + ValueTask IRedisClientAsync.GetGeohashesAsync(string key, string[] members, CancellationToken cancellationToken) + => NativeAsync.GeoHashAsync(key, members, cancellationToken); + + ValueTask> IRedisClientAsync.GetGeoCoordinatesAsync(string key, string[] members, CancellationToken cancellationToken) + => NativeAsync.GeoPosAsync(key, members, cancellationToken); + + async ValueTask IRedisClientAsync.FindGeoMembersInRadiusAsync(string key, double longitude, double latitude, double radius, string unit, CancellationToken cancellationToken) + { + var results = await NativeAsync.GeoRadiusAsync(key, longitude, latitude, radius, unit, cancellationToken: cancellationToken).ConfigureAwait(false); + return ParseFindGeoMembersResult(results); + } + + ValueTask> IRedisClientAsync.FindGeoResultsInRadiusAsync(string key, double longitude, double latitude, double radius, string unit, int? count, bool? sortByNearest, CancellationToken cancellationToken) + => NativeAsync.GeoRadiusAsync(key, longitude, latitude, radius, unit, withCoords: true, withDist: true, withHash: true, count: count, asc: sortByNearest, cancellationToken: cancellationToken); + + async ValueTask IRedisClientAsync.FindGeoMembersInRadiusAsync(string key, string member, double radius, string unit, CancellationToken cancellationToken) + { + var results = await NativeAsync.GeoRadiusByMemberAsync(key, member, radius, unit, cancellationToken: cancellationToken).ConfigureAwait(false); + return ParseFindGeoMembersResult(results); + } + + ValueTask> IRedisClientAsync.FindGeoResultsInRadiusAsync(string key, string member, double radius, string unit, int? count, bool? sortByNearest, CancellationToken cancellationToken) + => NativeAsync.GeoRadiusByMemberAsync(key, member, radius, unit, withCoords: true, withDist: true, withHash: true, count: count, asc: sortByNearest, cancellationToken: cancellationToken); + + ValueTask IRedisClientAsync.CreateSubscriptionAsync(CancellationToken cancellationToken) + => new RedisSubscription(this).AsValueTask(); + + ValueTask IRedisClientAsync.PublishMessageAsync(string toChannel, string message, CancellationToken cancellationToken) + => NativeAsync.PublishAsync(toChannel, message.ToUtf8Bytes(), cancellationToken); + + ValueTask IRedisClientAsync.MoveBetweenSetsAsync(string fromSetId, string toSetId, string item, CancellationToken cancellationToken) + => NativeAsync.SMoveAsync(fromSetId, toSetId, item.ToUtf8Bytes(), cancellationToken); + + ValueTask IRedisClientAsync.SetContainsItemAsync(string setId, string item, CancellationToken cancellationToken) + => NativeAsync.SIsMemberAsync(setId, item.ToUtf8Bytes(), cancellationToken).IsSuccessAsync(); + + async ValueTask> IRedisClientAsync.GetIntersectFromSetsAsync(string[] setIds, CancellationToken cancellationToken) + { + if (setIds.Length == 0) + return new HashSet(); + + var multiDataList = await NativeAsync.SInterAsync(setIds, cancellationToken).ConfigureAwait(false); + return CreateHashSet(multiDataList); + } + + ValueTask IRedisClientAsync.StoreIntersectFromSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + { + if (setIds.Length == 0) return default; + + return NativeAsync.SInterStoreAsync(intoSetId, setIds, cancellationToken); + } + + async ValueTask> IRedisClientAsync.GetUnionFromSetsAsync(string[] setIds, CancellationToken cancellationToken) + { + if (setIds.Length == 0) + return new HashSet(); + + var multiDataList = await NativeAsync.SUnionAsync(setIds, cancellationToken).ConfigureAwait(false); + return CreateHashSet(multiDataList); + } + + ValueTask IRedisClientAsync.StoreUnionFromSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + { + if (setIds.Length == 0) return default; + + return NativeAsync.SUnionStoreAsync(intoSetId, setIds, cancellationToken); + } + + async ValueTask> IRedisClientAsync.GetDifferencesFromSetAsync(string fromSetId, string[] withSetIds, CancellationToken cancellationToken) + { + if (withSetIds.Length == 0) + return new HashSet(); + + var multiDataList = await NativeAsync.SDiffAsync(fromSetId, withSetIds, cancellationToken).ConfigureAwait(false); + return CreateHashSet(multiDataList); + } + + ValueTask IRedisClientAsync.StoreDifferencesFromSetAsync(string intoSetId, string fromSetId, string[] withSetIds, CancellationToken cancellationToken) + { + if (withSetIds.Length == 0) return default; + + return NativeAsync.SDiffStoreAsync(intoSetId, fromSetId, withSetIds, cancellationToken); + } + + ValueTask IRedisClientAsync.GetRandomItemFromSetAsync(string setId, CancellationToken cancellationToken) + => NativeAsync.SRandMemberAsync(setId, cancellationToken).FromUtf8BytesAsync(); + + ValueTask> IRedisClientAsync.GetAllItemsFromListAsync(string listId, CancellationToken cancellationToken) + => NativeAsync.LRangeAsync(listId, FirstElement, LastElement, cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.GetRangeFromListAsync(string listId, int startingFrom, int endingAt, CancellationToken cancellationToken) + => NativeAsync.LRangeAsync(listId, startingFrom, endingAt, cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.GetRangeFromSortedListAsync(string listId, int startingFrom, int endingAt, CancellationToken cancellationToken) + { + var sortOptions = new SortOptions { Skip = startingFrom, Take = endingAt, SortAlpha = true }; + return AsAsync().GetSortedItemsFromListAsync(listId, sortOptions, cancellationToken); + } + + ValueTask> IRedisClientAsync.GetSortedItemsFromListAsync(string listId, SortOptions sortOptions, CancellationToken cancellationToken) + => NativeAsync.SortAsync(listId, sortOptions, cancellationToken).ToStringListAsync(); + + async ValueTask IRedisClientAsync.AddRangeToListAsync(string listId, List values, CancellationToken cancellationToken) + { + var pipeline = AddRangeToListPrepareNonFlushed(listId, values); + await pipeline.FlushAsync(cancellationToken).ConfigureAwait(false); + + //the number of items after + _ = await pipeline.ReadAllAsIntsAsync(cancellationToken).ConfigureAwait(false); + } + + ValueTask IRedisClientAsync.PrependItemToListAsync(string listId, string value, CancellationToken cancellationToken) + => NativeAsync.LPushAsync(listId, value.ToUtf8Bytes(), cancellationToken).Await(); + + async ValueTask IRedisClientAsync.PrependRangeToListAsync(string listId, List values, CancellationToken cancellationToken) + { + var pipeline = PrependRangeToListPrepareNonFlushed(listId, values); + await pipeline.FlushAsync(cancellationToken).ConfigureAwait(false); + + //the number of items after + _ = await pipeline.ReadAllAsIntsAsync(cancellationToken).ConfigureAwait(false); + } + + ValueTask IRedisClientAsync.RemoveAllFromListAsync(string listId, CancellationToken cancellationToken) + => NativeAsync.LTrimAsync(listId, LastElement, FirstElement, cancellationToken); + + ValueTask IRedisClientAsync.RemoveStartFromListAsync(string listId, CancellationToken cancellationToken) + => NativeAsync.LPopAsync(listId, cancellationToken).FromUtf8BytesAsync(); + + ValueTask IRedisClientAsync.BlockingRemoveStartFromListAsync(string listId, TimeSpan? timeOut, CancellationToken cancellationToken) + => NativeAsync.BLPopValueAsync(listId, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).FromUtf8BytesAsync(); + + async ValueTask IRedisClientAsync.BlockingRemoveStartFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken cancellationToken) + { + var value = await NativeAsync.BLPopValueAsync(listIds, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).ConfigureAwait(false); + if (value == null) + return null; + return new ItemRef { Id = value[0].FromUtf8Bytes(), Item = value[1].FromUtf8Bytes() }; + } + + ValueTask IRedisClientAsync.RemoveEndFromListAsync(string listId, CancellationToken cancellationToken) + => NativeAsync.RPopAsync(listId, cancellationToken).FromUtf8BytesAsync(); + + ValueTask IRedisClientAsync.TrimListAsync(string listId, int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken) + => NativeAsync.LTrimAsync(listId, keepStartingFrom, keepEndingAt, cancellationToken); + + ValueTask IRedisClientAsync.RemoveItemFromListAsync(string listId, string value, CancellationToken cancellationToken) + => NativeAsync.LRemAsync(listId, 0, value.ToUtf8Bytes(), cancellationToken); + + ValueTask IRedisClientAsync.RemoveItemFromListAsync(string listId, string value, int noOfMatches, CancellationToken cancellationToken) + => NativeAsync.LRemAsync(listId, 0, value.ToUtf8Bytes(), cancellationToken); + + ValueTask IRedisClientAsync.GetItemFromListAsync(string listId, int listIndex, CancellationToken cancellationToken) + => NativeAsync.LIndexAsync(listId, listIndex, cancellationToken).FromUtf8BytesAsync(); + + ValueTask IRedisClientAsync.SetItemInListAsync(string listId, int listIndex, string value, CancellationToken cancellationToken) + => NativeAsync.LSetAsync(listId, listIndex, value.ToUtf8Bytes(), cancellationToken); + + ValueTask IRedisClientAsync.EnqueueItemOnListAsync(string listId, string value, CancellationToken cancellationToken) + => NativeAsync.LPushAsync(listId, value.ToUtf8Bytes(), cancellationToken).Await(); + + ValueTask IRedisClientAsync.DequeueItemFromListAsync(string listId, CancellationToken cancellationToken) + => NativeAsync.RPopAsync(listId, cancellationToken).FromUtf8BytesAsync(); + + ValueTask IRedisClientAsync.BlockingDequeueItemFromListAsync(string listId, TimeSpan? timeOut, CancellationToken cancellationToken) + => NativeAsync.BRPopValueAsync(listId, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).FromUtf8BytesAsync(); + + async ValueTask IRedisClientAsync.BlockingDequeueItemFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken cancellationToken) + { + var value = await NativeAsync.BRPopValueAsync(listIds, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).ConfigureAwait(false); + if (value == null) + return null; + return new ItemRef { Id = value[0].FromUtf8Bytes(), Item = value[1].FromUtf8Bytes() }; + } + + ValueTask IRedisClientAsync.PushItemToListAsync(string listId, string value, CancellationToken cancellationToken) + => NativeAsync.RPushAsync(listId, value.ToUtf8Bytes(), cancellationToken).Await(); + + ValueTask IRedisClientAsync.PopItemFromListAsync(string listId, CancellationToken cancellationToken) + => NativeAsync.RPopAsync(listId, cancellationToken).FromUtf8BytesAsync(); + + ValueTask IRedisClientAsync.BlockingPopItemFromListAsync(string listId, TimeSpan? timeOut, CancellationToken cancellationToken) + => NativeAsync.BRPopValueAsync(listId, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).FromUtf8BytesAsync(); + + async ValueTask IRedisClientAsync.BlockingPopItemFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken cancellationToken) + { + var value = await NativeAsync.BRPopValueAsync(listIds, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).ConfigureAwait(false); + if (value == null) + return null; + return new ItemRef { Id = value[0].FromUtf8Bytes(), Item = value[1].FromUtf8Bytes() }; + } + + ValueTask IRedisClientAsync.PopAndPushItemBetweenListsAsync(string fromListId, string toListId, CancellationToken cancellationToken) + => NativeAsync.RPopLPushAsync(fromListId, toListId, cancellationToken).FromUtf8BytesAsync(); + + ValueTask IRedisClientAsync.BlockingPopAndPushItemBetweenListsAsync(string fromListId, string toListId, TimeSpan? timeOut, CancellationToken cancellationToken) + => NativeAsync.BRPopLPushAsync(fromListId, toListId, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).FromUtf8BytesAsync(); + + async ValueTask IRedisClientAsync.AddRangeToSortedSetAsync(string setId, List values, double score, CancellationToken cancellationToken) + { + var pipeline = AddRangeToSortedSetPrepareNonFlushed(setId, values, score.ToFastUtf8Bytes()); + await pipeline.FlushAsync(cancellationToken).ConfigureAwait(false); + + return await pipeline.ReadAllAsIntsHaveSuccessAsync(cancellationToken).ConfigureAwait(false); + } + + async ValueTask IRedisClientAsync.AddRangeToSortedSetAsync(string setId, List values, long score, CancellationToken cancellationToken) + { + var pipeline = AddRangeToSortedSetPrepareNonFlushed(setId, values, score.ToUtf8Bytes()); + await pipeline.FlushAsync(cancellationToken).ConfigureAwait(false); + + return await pipeline.ReadAllAsIntsHaveSuccessAsync(cancellationToken).ConfigureAwait(false); + } + + ValueTask IRedisClientAsync.RemoveItemFromSortedSetAsync(string setId, string value, CancellationToken cancellationToken) + => NativeAsync.ZRemAsync(setId, value.ToUtf8Bytes(), cancellationToken).IsSuccessAsync(); + + ValueTask IRedisClientAsync.RemoveItemsFromSortedSetAsync(string setId, List values, CancellationToken cancellationToken) + => NativeAsync.ZRemAsync(setId, values.Map(x => x.ToUtf8Bytes()).ToArray(), cancellationToken); + + async ValueTask IRedisClientAsync.PopItemWithLowestScoreFromSortedSetAsync(string setId, CancellationToken cancellationToken) + { + //TODO: this should be atomic + var topScoreItemBytes = await NativeAsync.ZRangeAsync(setId, FirstElement, 1, cancellationToken).ConfigureAwait(false); + if (topScoreItemBytes.Length == 0) return null; + + await NativeAsync.ZRemAsync(setId, topScoreItemBytes[0], cancellationToken).ConfigureAwait(false); + return topScoreItemBytes[0].FromUtf8Bytes(); + } + + async ValueTask IRedisClientAsync.PopItemWithHighestScoreFromSortedSetAsync(string setId, CancellationToken cancellationToken) + { + //TODO: this should be atomic + var topScoreItemBytes = await NativeAsync.ZRevRangeAsync(setId, FirstElement, 1, cancellationToken).ConfigureAwait(false); + if (topScoreItemBytes.Length == 0) return null; + + await NativeAsync.ZRemAsync(setId, topScoreItemBytes[0], cancellationToken).ConfigureAwait(false); + return topScoreItemBytes[0].FromUtf8Bytes(); + } + + ValueTask IRedisClientAsync.SortedSetContainsItemAsync(string setId, string value, CancellationToken cancellationToken) + => NativeAsync.ZRankAsync(setId, value.ToUtf8Bytes(),cancellationToken).Await(val => val != -1); + + ValueTask IRedisClientAsync.IncrementItemInSortedSetAsync(string setId, string value, double incrementBy, CancellationToken cancellationToken) + => NativeAsync.ZIncrByAsync(setId, incrementBy, value.ToUtf8Bytes(), cancellationToken); + + ValueTask IRedisClientAsync.IncrementItemInSortedSetAsync(string setId, string value, long incrementBy, CancellationToken cancellationToken) + => NativeAsync.ZIncrByAsync(setId, incrementBy, value.ToUtf8Bytes(), cancellationToken); + + ValueTask IRedisClientAsync.GetItemIndexInSortedSetAsync(string setId, string value, CancellationToken cancellationToken) + => NativeAsync.ZRankAsync(setId, value.ToUtf8Bytes(), cancellationToken); + + ValueTask IRedisClientAsync.GetItemIndexInSortedSetDescAsync(string setId, string value, CancellationToken cancellationToken) + => NativeAsync.ZRevRankAsync(setId, value.ToUtf8Bytes(), cancellationToken); + + ValueTask> IRedisClientAsync.GetAllItemsFromSortedSetAsync(string setId, CancellationToken cancellationToken) + => NativeAsync.ZRangeAsync(setId, FirstElement, LastElement, cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.GetAllItemsFromSortedSetDescAsync(string setId, CancellationToken cancellationToken) + => NativeAsync.ZRevRangeAsync(setId, FirstElement, LastElement, cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken) + => NativeAsync.ZRangeAsync(setId, fromRank, toRank, cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetDescAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken) + => NativeAsync.ZRevRangeAsync(setId, fromRank, toRank, cancellationToken).ToStringListAsync(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ValueTask> CreateSortedScoreMapAsync(ValueTask pending) + { + return pending.IsCompletedSuccessfully ? CreateSortedScoreMap(pending.Result).AsValueTask() : Awaited(pending); + static async ValueTask> Awaited(ValueTask pending) + => CreateSortedScoreMap(await pending.ConfigureAwait(false)); + } + + ValueTask> IRedisClientAsync.GetAllWithScoresFromSortedSetAsync(string setId, CancellationToken cancellationToken) + => CreateSortedScoreMapAsync(NativeAsync.ZRangeWithScoresAsync(setId, FirstElement, LastElement, cancellationToken)); + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken) + => CreateSortedScoreMapAsync(NativeAsync.ZRangeWithScoresAsync(setId, fromRank, toRank, cancellationToken)); + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetDescAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken) + => CreateSortedScoreMapAsync(NativeAsync.ZRevRangeWithScoresAsync(setId, fromRank, toRank, cancellationToken)); + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken) + => AsAsync().GetRangeFromSortedSetByLowestScoreAsync(setId, fromStringScore, toStringScore, null, null, cancellationToken); + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + { + var fromScore = GetLexicalScore(fromStringScore); + var toScore = GetLexicalScore(toStringScore); + return AsAsync().GetRangeFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken); + } + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) + => AsAsync().GetRangeFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) + => AsAsync().GetRangeFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) + => NativeAsync.ZRangeByScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken) + => NativeAsync.ZRangeByScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken) + => AsAsync().GetRangeWithScoresFromSortedSetByLowestScoreAsync(setId, fromStringScore, toStringScore, null, null, cancellationToken); + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + { + var fromScore = GetLexicalScore(fromStringScore); + var toScore = GetLexicalScore(toStringScore); + return AsAsync().GetRangeWithScoresFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken); + } + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) + => AsAsync().GetRangeWithScoresFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) + => AsAsync().GetRangeWithScoresFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) + => CreateSortedScoreMapAsync(NativeAsync.ZRangeByScoreWithScoresAsync(setId, fromScore, toScore, skip, take, cancellationToken)); + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken) + => CreateSortedScoreMapAsync(NativeAsync.ZRangeByScoreWithScoresAsync(setId, fromScore, toScore, skip, take, cancellationToken)); + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken) + => AsAsync().GetRangeFromSortedSetByHighestScoreAsync(setId, fromStringScore, toStringScore, null, null, cancellationToken); + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + { + var fromScore = GetLexicalScore(fromStringScore); + var toScore = GetLexicalScore(toStringScore); + return AsAsync().GetRangeFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken); + } + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) + => AsAsync().GetRangeFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) + => AsAsync().GetRangeFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) + => NativeAsync.ZRevRangeByScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken) + => NativeAsync.ZRevRangeByScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken) + => AsAsync().GetRangeWithScoresFromSortedSetByHighestScoreAsync(setId, fromStringScore, toStringScore, null, null, cancellationToken); + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + { + var fromScore = GetLexicalScore(fromStringScore); + var toScore = GetLexicalScore(toStringScore); + return AsAsync().GetRangeWithScoresFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken); + } + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) + => AsAsync().GetRangeWithScoresFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) + => AsAsync().GetRangeWithScoresFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) + => CreateSortedScoreMapAsync(NativeAsync.ZRevRangeByScoreWithScoresAsync(setId, fromScore, toScore, skip, take, cancellationToken)); + + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken) + => CreateSortedScoreMapAsync(NativeAsync.ZRevRangeByScoreWithScoresAsync(setId, fromScore, toScore, skip, take, cancellationToken)); + + ValueTask IRedisClientAsync.RemoveRangeFromSortedSetAsync(string setId, int minRank, int maxRank, CancellationToken cancellationToken) + => NativeAsync.ZRemRangeByRankAsync(setId, minRank, maxRank, cancellationToken); + + ValueTask IRedisClientAsync.RemoveRangeFromSortedSetByScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) + => NativeAsync.ZRemRangeByScoreAsync(setId, fromScore, toScore, cancellationToken); + + ValueTask IRedisClientAsync.RemoveRangeFromSortedSetByScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) + => NativeAsync.ZRemRangeByScoreAsync(setId, fromScore, toScore, cancellationToken); + + ValueTask IRedisClientAsync.StoreIntersectFromSortedSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + => NativeAsync.ZInterStoreAsync(intoSetId, setIds, cancellationToken); + + ValueTask IRedisClientAsync.StoreIntersectFromSortedSetsAsync(string intoSetId, string[] setIds, string[] args, CancellationToken cancellationToken) + => base.ZInterStoreAsync(intoSetId, setIds, args, cancellationToken); + + ValueTask IRedisClientAsync.StoreUnionFromSortedSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + => NativeAsync.ZUnionStoreAsync(intoSetId, setIds, cancellationToken); + + ValueTask IRedisClientAsync.StoreUnionFromSortedSetsAsync(string intoSetId, string[] setIds, string[] args, CancellationToken cancellationToken) + => base.ZUnionStoreAsync(intoSetId, setIds, args, cancellationToken); + + ValueTask IRedisClientAsync.HashContainsEntryAsync(string hashId, string key, CancellationToken cancellationToken) + => NativeAsync.HExistsAsync(hashId, key.ToUtf8Bytes(), cancellationToken).IsSuccessAsync(); + + ValueTask IRedisClientAsync.SetEntryInHashIfNotExistsAsync(string hashId, string key, string value, CancellationToken cancellationToken) + => NativeAsync.HSetNXAsync(hashId, key.ToUtf8Bytes(), value.ToUtf8Bytes(), cancellationToken).IsSuccessAsync(); + + ValueTask IRedisClientAsync.SetRangeInHashAsync(string hashId, IEnumerable> keyValuePairs, CancellationToken cancellationToken) + => SetRangeInHashPrepare(keyValuePairs, out var keys, out var values) ? NativeAsync.HMSetAsync(hashId, keys, values, cancellationToken) : default; + + ValueTask IRedisClientAsync.IncrementValueInHashAsync(string hashId, string key, int incrementBy, CancellationToken cancellationToken) + => NativeAsync.HIncrbyAsync(hashId, key.ToUtf8Bytes(), incrementBy, cancellationToken); + + ValueTask IRedisClientAsync.IncrementValueInHashAsync(string hashId, string key, double incrementBy, CancellationToken cancellationToken) + => NativeAsync.HIncrbyFloatAsync(hashId, key.ToUtf8Bytes(), incrementBy, cancellationToken); + + ValueTask IRedisClientAsync.GetValueFromHashAsync(string hashId, string key, CancellationToken cancellationToken) + => NativeAsync.HGetAsync(hashId, key.ToUtf8Bytes(), cancellationToken).FromUtf8BytesAsync(); + + ValueTask> IRedisClientAsync.GetValuesFromHashAsync(string hashId, string[] keys, CancellationToken cancellationToken) + { + if (keys.Length == 0) return new List().AsValueTask(); + var keyBytes = ConvertToBytes(keys); + return NativeAsync.HMGetAsync(hashId, keyBytes, cancellationToken).ToStringListAsync(); + } + + ValueTask IRedisClientAsync.RemoveEntryFromHashAsync(string hashId, string key, CancellationToken cancellationToken) + => NativeAsync.HDelAsync(hashId, key.ToUtf8Bytes(), cancellationToken).IsSuccessAsync(); + + ValueTask> IRedisClientAsync.GetHashKeysAsync(string hashId, CancellationToken cancellationToken) + => NativeAsync.HKeysAsync(hashId, cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.GetHashValuesAsync(string hashId, CancellationToken cancellationToken) + => NativeAsync.HValsAsync(hashId, cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.GetAllEntriesFromHashAsync(string hashId, CancellationToken cancellationToken) + => NativeAsync.HGetAllAsync(hashId, cancellationToken).Await(ret => ret.ToStringDictionary()); + + ValueTask IRedisClientAsync.ExecLuaAsync(string body, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalCommandAsync(body, 0, args.ToMultiByteArray(), cancellationToken).Await(ret => ret.ToRedisText()); + + ValueTask IRedisClientAsync.ExecLuaShaAsync(string sha1, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalShaCommandAsync(sha1, 0, args.ToMultiByteArray(), cancellationToken).Await(ret => ret.ToRedisText()); + + ValueTask IRedisClientAsync.ExecLuaAsStringAsync(string body, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalStrAsync(body, 0, args.ToMultiByteArray(), cancellationToken); + + ValueTask IRedisClientAsync.ExecLuaShaAsStringAsync(string sha1, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalShaStrAsync(sha1, 0, args.ToMultiByteArray(), cancellationToken); + + ValueTask IRedisClientAsync.ExecLuaAsIntAsync(string body, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalIntAsync(body, 0, args.ToMultiByteArray(), cancellationToken); + + ValueTask IRedisClientAsync.ExecLuaAsIntAsync(string body, string[] keys, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalIntAsync(body, keys.Length, MergeAndConvertToBytes(keys, args), cancellationToken); + + ValueTask IRedisClientAsync.ExecLuaShaAsIntAsync(string sha1, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalShaIntAsync(sha1, 0, args.ToMultiByteArray(), cancellationToken); + + ValueTask IRedisClientAsync.ExecLuaShaAsIntAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalShaIntAsync(sha1, keys.Length, MergeAndConvertToBytes(keys, args), cancellationToken); + + ValueTask> IRedisClientAsync.ExecLuaAsListAsync(string body, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalAsync(body, 0, args.ToMultiByteArray(), cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.ExecLuaAsListAsync(string body, string[] keys, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalAsync(body, keys.Length, MergeAndConvertToBytes(keys, args), cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.ExecLuaShaAsListAsync(string sha1, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalShaAsync(sha1, 0, args.ToMultiByteArray(), cancellationToken).ToStringListAsync(); + + ValueTask> IRedisClientAsync.ExecLuaShaAsListAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken) + => NativeAsync.EvalShaAsync(sha1, keys.Length, MergeAndConvertToBytes(keys, args), cancellationToken).ToStringListAsync(); + + ValueTask IRedisClientAsync.CalculateSha1Async(string luaBody, CancellationToken cancellationToken) + => CalculateSha1(luaBody).AsValueTask(); + + async ValueTask IRedisClientAsync.HasLuaScriptAsync(string sha1Ref, CancellationToken cancellationToken) + { + var map = await AsAsync().WhichLuaScriptsExistsAsync(new[] { sha1Ref }, cancellationToken).ConfigureAwait(false); + return map[sha1Ref]; + } + + async ValueTask> IRedisClientAsync.WhichLuaScriptsExistsAsync(string[] sha1Refs, CancellationToken cancellationToken) + { + var intFlags = await NativeAsync.ScriptExistsAsync(sha1Refs.ToMultiByteArray()).ConfigureAwait(false); + return WhichLuaScriptsExistsParseResult(sha1Refs, intFlags); + } + + ValueTask IRedisClientAsync.RemoveAllLuaScriptsAsync(CancellationToken cancellationToken) + => NativeAsync.ScriptFlushAsync(cancellationToken); + + ValueTask IRedisClientAsync.KillRunningLuaScriptAsync(CancellationToken cancellationToken) + => NativeAsync.ScriptKillAsync(cancellationToken); + + ValueTask IRedisClientAsync.CustomAsync(params object[] cmdWithArgs) + => AsAsync().CustomAsync(cmdWithArgs, cancellationToken: default); + + ValueTask IRedisClientAsync.RemoveEntryAsync(params string[] args) + => AsAsync().RemoveEntryAsync(args, cancellationToken: default); + + ValueTask IRedisClientAsync.AddToHyperLogAsync(string key, params string[] elements) + => AsAsync().AddToHyperLogAsync(key, elements, cancellationToken: default); + + ValueTask IRedisClientAsync.MergeHyperLogsAsync(string toKey, params string[] fromKeys) + => AsAsync().MergeHyperLogsAsync(toKey, fromKeys, cancellationToken: default); + + ValueTask IRedisClientAsync.AddGeoMembersAsync(string key, params RedisGeo[] geoPoints) + => AsAsync().AddGeoMembersAsync(key, geoPoints, cancellationToken: default); + + ValueTask IRedisClientAsync.GetGeohashesAsync(string key, params string[] members) + => AsAsync().GetGeohashesAsync(key, members, cancellationToken: default); + + ValueTask> IRedisClientAsync.GetGeoCoordinatesAsync(string key, params string[] members) + => AsAsync().GetGeoCoordinatesAsync(key, members, cancellationToken: default); + + ValueTask IRedisClientAsync.WatchAsync(params string[] keys) + => AsAsync().WatchAsync(keys, cancellationToken: default); + + ValueTask> IRedisClientAsync.GetIntersectFromSetsAsync(params string[] setIds) + => AsAsync().GetIntersectFromSetsAsync(setIds, cancellationToken: default); + + ValueTask IRedisClientAsync.StoreIntersectFromSetsAsync(string intoSetId, params string[] setIds) + => AsAsync().StoreIntersectFromSetsAsync(intoSetId, setIds, cancellationToken: default); + + ValueTask> IRedisClientAsync.GetUnionFromSetsAsync(params string[] setIds) + => AsAsync().GetUnionFromSetsAsync(setIds, cancellationToken: default); + + ValueTask IRedisClientAsync.StoreUnionFromSetsAsync(string intoSetId, params string[] setIds) + => AsAsync().StoreUnionFromSetsAsync(intoSetId, setIds, cancellationToken: default); + + ValueTask> IRedisClientAsync.GetDifferencesFromSetAsync(string fromSetId, params string[] withSetIds) + => AsAsync().GetDifferencesFromSetAsync(fromSetId, withSetIds, cancellationToken: default); + + ValueTask IRedisClientAsync.StoreDifferencesFromSetAsync(string intoSetId, string fromSetId, params string[] withSetIds) + => AsAsync().StoreDifferencesFromSetAsync(intoSetId, fromSetId, withSetIds, cancellationToken: default); + + ValueTask IRedisClientAsync.StoreIntersectFromSortedSetsAsync(string intoSetId, params string[] setIds) + => AsAsync().StoreIntersectFromSortedSetsAsync(intoSetId, setIds, cancellationToken: default); + + ValueTask IRedisClientAsync.StoreUnionFromSortedSetsAsync(string intoSetId, params string[] setIds) + => AsAsync().StoreUnionFromSortedSetsAsync(intoSetId, setIds, cancellationToken: default); + + ValueTask> IRedisClientAsync.GetValuesFromHashAsync(string hashId, params string[] keys) + => AsAsync().GetValuesFromHashAsync(hashId, keys, cancellationToken: default); + + ValueTask IRedisClientAsync.ExecLuaAsync(string body, params string[] args) + => AsAsync().ExecLuaAsync(body, args, cancellationToken: default); + + ValueTask IRedisClientAsync.ExecLuaShaAsync(string sha1, params string[] args) + => AsAsync().ExecLuaShaAsync(sha1, args, cancellationToken: default); + + ValueTask IRedisClientAsync.ExecLuaAsStringAsync(string luaBody, params string[] args) + => AsAsync().ExecLuaAsStringAsync(luaBody, args, cancellationToken: default); + + ValueTask IRedisClientAsync.ExecLuaShaAsStringAsync(string sha1, params string[] args) + => AsAsync().ExecLuaShaAsStringAsync(sha1, args, cancellationToken: default); + + ValueTask IRedisClientAsync.ExecLuaAsIntAsync(string luaBody, params string[] args) + => AsAsync().ExecLuaAsIntAsync(luaBody, args, cancellationToken: default); + + ValueTask IRedisClientAsync.ExecLuaShaAsIntAsync(string sha1, params string[] args) + => AsAsync().ExecLuaShaAsIntAsync(sha1, args, cancellationToken: default); + + ValueTask> IRedisClientAsync.ExecLuaAsListAsync(string luaBody, params string[] args) + => AsAsync().ExecLuaAsListAsync(luaBody, args, cancellationToken: default); + + ValueTask> IRedisClientAsync.ExecLuaShaAsListAsync(string sha1, params string[] args) + => AsAsync().ExecLuaShaAsListAsync(sha1, args, cancellationToken: default); + + ValueTask> IRedisClientAsync.WhichLuaScriptsExistsAsync(params string[] sha1Refs) + => AsAsync().WhichLuaScriptsExistsAsync(sha1Refs, cancellationToken: default); + } +} diff --git a/src/ServiceStack.Redis/RedisClient.ICacheClient.cs b/src/ServiceStack.Redis/RedisClient.ICacheClient.cs index 15406272..9c974922 100644 --- a/src/ServiceStack.Redis/RedisClient.ICacheClient.cs +++ b/src/ServiceStack.Redis/RedisClient.ICacheClient.cs @@ -172,60 +172,74 @@ public IDictionary GetAll(IEnumerable keys) { var keysArray = keys.ToArray(); var keyValues = r.MGet(keysArray); - var results = new Dictionary(); - var isBytes = typeof(T) == typeof(byte[]); - var i = 0; - foreach (var keyValue in keyValues) + return ProcessGetAllResult(keysArray, keyValues); + }); + } + + private static IDictionary ProcessGetAllResult(string[] keysArray, byte[][] keyValues) + { + var results = new Dictionary(); + var isBytes = typeof(T) == typeof(byte[]); + + var i = 0; + foreach (var keyValue in keyValues) + { + var key = keysArray[i++]; + + if (keyValue == null) { - var key = keysArray[i++]; - - if (keyValue == null) - { - results[key] = default(T); - continue; - } - - if (isBytes) - { - results[key] = (T)(object)keyValue; - } - else - { - var keyValueString = Encoding.UTF8.GetString(keyValue); - results[key] = JsonSerializer.DeserializeFromString(keyValueString); - } + results[key] = default(T); + continue; } - return results; - }); + + if (isBytes) + { + results[key] = (T)(object)keyValue; + } + else + { + var keyValueString = Encoding.UTF8.GetString(keyValue); + results[key] = JsonSerializer.DeserializeFromString(keyValueString); + } + } + return results; } public void SetAll(IDictionary values) { - Exec(r => + if (values.Count != 0) { - var keys = values.Keys.ToArray(); - var valBytes = new byte[values.Count][]; - var isBytes = typeof(T) == typeof(byte[]); + Exec(r => + { + // need to do this inside Exec for the JSON config bits + GetSetAllBytesTyped(values, out var keys, out var valBytes); + r.MSet(keys, valBytes); + }); + } + } - var i = 0; - foreach (var value in values.Values) + private static void GetSetAllBytesTyped(IDictionary values, out string[] keys, out byte[][] valBytes) + { + keys = values.Keys.ToArray(); + valBytes = new byte[values.Count][]; + var isBytes = typeof(T) == typeof(byte[]); + + var i = 0; + foreach (var value in values.Values) + { + if (!isBytes) { - if (!isBytes) - { - var t = JsonSerializer.SerializeToString(value); - if (t != null) - valBytes[i] = t.ToUtf8Bytes(); - else - valBytes[i] = new byte[] { }; - } + var t = JsonSerializer.SerializeToString(value); + if (t != null) + valBytes[i] = t.ToUtf8Bytes(); else - valBytes[i] = (byte[])(object)value ?? new byte[] { }; - i++; + valBytes[i] = new byte[] { }; } - - r.MSet(keys, valBytes); - }); + else + valBytes[i] = (byte[])(object)value ?? new byte[] { }; + i++; + } } } diff --git a/src/ServiceStack.Redis/RedisClient.cs b/src/ServiceStack.Redis/RedisClient.cs index 938bb6a4..d1d0d7c4 100644 --- a/src/ServiceStack.Redis/RedisClient.cs +++ b/src/ServiceStack.Redis/RedisClient.cs @@ -197,32 +197,54 @@ public void SetValues(Dictionary map) public void SetAll(IEnumerable keys, IEnumerable values) { - if (keys == null || values == null) return; + if (GetSetAllBytes(keys, values, out var keyBytes, out var valBytes)) + { + base.MSet(keyBytes, valBytes); + } + } + + bool GetSetAllBytes(IEnumerable keys, IEnumerable values, out byte[][] keyBytes, out byte[][] valBytes) + { + keyBytes = valBytes = default; + if (keys == null || values == null) return false; var keyArray = keys.ToArray(); var valueArray = values.ToArray(); if (keyArray.Length != valueArray.Length) throw new Exception("Key length != Value Length. {0}/{1}".Fmt(keyArray.Length, valueArray.Length)); - if (keyArray.Length == 0) return; + if (keyArray.Length == 0) return false; - var keyBytes = new byte[keyArray.Length][]; - var valBytes = new byte[keyArray.Length][]; + keyBytes = new byte[keyArray.Length][]; + valBytes = new byte[keyArray.Length][]; for (int i = 0; i < keyArray.Length; i++) { keyBytes[i] = keyArray[i].ToUtf8Bytes(); valBytes[i] = valueArray[i].ToUtf8Bytes(); } - base.MSet(keyBytes, valBytes); + return true; } public void SetAll(Dictionary map) { - if (map == null || map.Count == 0) return; + if (GetSetAllBytes(map, out var keyBytes, out var valBytes)) + { + base.MSet(keyBytes, valBytes); + } + } + + private static bool GetSetAllBytes(IDictionary map, out byte[][] keyBytes, out byte[][] valBytes) + { + if (map == null || map.Count == 0) + { + keyBytes = null; + valBytes = null; + return false; + } - var keyBytes = new byte[map.Count][]; - var valBytes = new byte[map.Count][]; + keyBytes = new byte[map.Count][]; + valBytes = new byte[map.Count][]; var i = 0; foreach (var key in map.Keys) @@ -232,8 +254,7 @@ public void SetAll(Dictionary map) valBytes[i] = val.ToUtf8Bytes(); i++; } - - base.MSet(keyBytes, valBytes); + return true; } public string GetValue(string key) @@ -321,25 +342,23 @@ public string GetRandomKey() public bool ExpireEntryIn(string key, TimeSpan expireIn) { - if (AssertServerVersionNumber() >= 2600) + if (UseMillisecondExpiration(expireIn)) { - if (expireIn.Milliseconds > 0) - { - return PExpire(key, (long)expireIn.TotalMilliseconds); - } + return PExpire(key, (long)expireIn.TotalMilliseconds); } return Expire(key, (int)expireIn.TotalSeconds); } + private bool UseMillisecondExpiration(TimeSpan value) + + => AssertServerVersionNumber() >= 2600 && value.Milliseconds > 0; + public bool ExpireEntryIn(byte[] key, TimeSpan expireIn) { - if (AssertServerVersionNumber() >= 2600) + if (UseMillisecondExpiration(expireIn)) { - if (expireIn.Milliseconds > 0) - { - return PExpire(key, (long)expireIn.TotalMilliseconds); - } + return PExpire(key, (long)expireIn.TotalMilliseconds); } return Expire(key, (int)expireIn.TotalSeconds); @@ -358,8 +377,10 @@ public bool ExpireEntryAt(string key, DateTime expireAt) } public TimeSpan? GetTimeToLive(string key) + => ParseTimeToLiveResult(Ttl(key)); + + private static TimeSpan? ParseTimeToLiveResult(long ttlSecs) { - var ttlSecs = Ttl(key); if (ttlSecs == -1) return TimeSpan.MaxValue; //no expiry set @@ -401,7 +422,7 @@ public IDisposable AcquireLock(string key, TimeSpan timeOut) public IRedisTransaction CreateTransaction() { AssertServerVersionNumber(); // pre-fetch call to INFO before transaction if needed - return new RedisTransaction(this); + return new RedisTransaction(this, false); } public void AssertNotInTransaction() @@ -426,9 +447,11 @@ public List GetValues(List keys) if (keys == null) throw new ArgumentNullException(nameof(keys)); if (keys.Count == 0) return new List(); - var resultBytesArray = MGet(keys.ToArray()); - - var results = new List(); + return ParseGetValuesResult(MGet(keys.ToArray())); + } + private static List ParseGetValuesResult(byte[][] resultBytesArray) + { + var results = new List(resultBytesArray.Length); foreach (var resultBytes in resultBytesArray) { if (resultBytes == null) continue; @@ -445,9 +468,12 @@ public List GetValues(List keys) if (keys == null) throw new ArgumentNullException(nameof(keys)); if (keys.Count == 0) return new List(); - var resultBytesArray = MGet(keys.ToArray()); + return ParseGetValuesResult(MGet(keys.ToArray())); + } - var results = new List(); + private static List ParseGetValuesResult(byte[][] resultBytesArray) + { + var results = new List(resultBytesArray.Length); foreach (var resultBytes in resultBytesArray) { if (resultBytes == null) continue; @@ -468,6 +494,11 @@ public Dictionary GetValuesMap(List keys) var keysArray = keys.ToArray(); var resultBytesArray = MGet(keysArray); + return ParseGetValuesMapResult(keysArray, resultBytesArray); + } + + private static Dictionary ParseGetValuesMapResult(string[] keysArray, byte[][] resultBytesArray) + { var results = new Dictionary(); for (var i = 0; i < resultBytesArray.Length; i++) { @@ -496,6 +527,11 @@ public Dictionary GetValuesMap(List keys) var keysArray = keys.ToArray(); var resultBytesArray = MGet(keysArray); + return ParseGetValuesMapResult(keysArray, resultBytesArray); + } + + private static Dictionary ParseGetValuesMapResult(string[] keysArray, byte[][] resultBytesArray) + { var results = new Dictionary(); for (var i = 0; i < resultBytesArray.Length; i++) { @@ -699,42 +735,69 @@ public void StoreAsHash(T entity) //Without the Generic Constraints internal void _StoreAll(IEnumerable entities) { - if (entities == null) return; + if (PrepareStoreAll(entities, out var keys, out var values, out var entitiesList)) + { + base.MSet(keys, values); + RegisterTypeIds(entitiesList); + } + } - var entitiesList = entities.ToList(); + private bool PrepareStoreAll(IEnumerable entities, out byte[][] keys, out byte[][] values, out List entitiesList) + { + if (entities == null) + { + entitiesList = default; + keys = values = default; + return false; + } + + entitiesList = entities.ToList(); var len = entitiesList.Count; - if (len == 0) return; + if (len == 0) + { + keys = values = default; + return false; + } - var keys = new byte[len][]; - var values = new byte[len][]; + keys = new byte[len][]; + values = new byte[len][]; for (var i = 0; i < len; i++) { keys[i] = UrnKey(entitiesList[i]).ToUtf8Bytes(); values[i] = SerializeToUtf8Bytes(entitiesList[i]); } - - base.MSet(keys, values); - RegisterTypeIds(entitiesList); + return true; } public void WriteAll(IEnumerable entities) { - if (entities == null) return; + if (PrepareWriteAll(entities, out var keys, out var values)) + { + base.MSet(keys, values); + } + } + + private bool PrepareWriteAll(IEnumerable entities, out byte[][] keys, out byte[][] values) + { + if (entities == null) + { + keys = values = default; + return false; + } var entitiesList = entities.ToList(); var len = entitiesList.Count; - var keys = new byte[len][]; - var values = new byte[len][]; + keys = new byte[len][]; + values = new byte[len][]; for (var i = 0; i < len; i++) { keys[i] = UrnKey(entitiesList[i]).ToUtf8Bytes(); values[i] = SerializeToUtf8Bytes(entitiesList[i]); } - - base.MSet(keys, values); + return true; } public static byte[] SerializeToUtf8Bytes(T value) @@ -938,6 +1001,10 @@ public bool HasLuaScript(string sha1Ref) public Dictionary WhichLuaScriptsExists(params string[] sha1Refs) { var intFlags = base.ScriptExists(sha1Refs.ToMultiByteArray()); + return WhichLuaScriptsExistsParseResult(sha1Refs, intFlags); + } + static Dictionary WhichLuaScriptsExistsParseResult(string[] sha1Refs, byte[][] intFlags) + { var map = new Dictionary(); for (int i = 0; i < sha1Refs.Length; i++) { @@ -973,8 +1040,11 @@ public void RemoveByPattern(string pattern) public void RemoveByRegex(string pattern) { - RemoveByPattern(pattern.Replace(".*", "*").Replace(".+", "?")); + RemoveByPattern(RegexToGlob(pattern)); } + + private static string RegexToGlob(string regex) + => regex.Replace(".*", "*").Replace(".+", "?"); public IEnumerable ScanAllKeys(string pattern = null, int pageSize = 1000) { diff --git a/src/ServiceStack.Redis/RedisClientExtensions.cs b/src/ServiceStack.Redis/RedisClientExtensions.cs index 5f7ef45b..79e655ed 100644 --- a/src/ServiceStack.Redis/RedisClientExtensions.cs +++ b/src/ServiceStack.Redis/RedisClientExtensions.cs @@ -1,6 +1,6 @@ namespace ServiceStack.Redis { - public static class RedisClientExtensions + public static partial class RedisClientExtensions { public static string GetHostString(this IRedisClient redis) { diff --git a/src/ServiceStack.Redis/RedisClientHash.Async.cs b/src/ServiceStack.Redis/RedisClientHash.Async.cs new file mode 100644 index 00000000..1285572f --- /dev/null +++ b/src/ServiceStack.Redis/RedisClientHash.Async.cs @@ -0,0 +1,55 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Redis.Internal; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + internal partial class RedisClientHash + : IRedisHashAsync + { + private IRedisClientAsync AsyncClient => client; + + ValueTask IRedisHashAsync.AddAsync(KeyValuePair item, CancellationToken cancellationToken) + => AsyncClient.SetEntryInHashAsync(hashId, item.Key, item.Value, cancellationToken).Await(); + + ValueTask IRedisHashAsync.AddAsync(string key, string value, CancellationToken cancellationToken) + => AsyncClient.SetEntryInHashAsync(hashId, key, value, cancellationToken).Await(); + + ValueTask IRedisHashAsync.AddIfNotExistsAsync(KeyValuePair item, CancellationToken cancellationToken) + => AsyncClient.SetEntryInHashIfNotExistsAsync(hashId, item.Key, item.Value, cancellationToken); + + ValueTask IRedisHashAsync.AddRangeAsync(IEnumerable> items, CancellationToken cancellationToken) + => AsyncClient.SetRangeInHashAsync(hashId, items, cancellationToken); + + ValueTask IRedisHashAsync.ClearAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveAsync(hashId, cancellationToken).Await(); + + ValueTask IRedisHashAsync.ContainsKeyAsync(string key, CancellationToken cancellationToken) + => AsyncClient.HashContainsEntryAsync(hashId, key, cancellationToken); + + ValueTask IRedisHashAsync.CountAsync(CancellationToken cancellationToken) + => AsyncClient.GetHashCountAsync(hashId, cancellationToken).AsInt32(); + + IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) + => AsyncClient.ScanAllHashEntriesAsync(hashId).GetAsyncEnumerator(cancellationToken); // note: we're using HSCAN here, not HGETALL + + ValueTask IRedisHashAsync.IncrementValueAsync(string key, int incrementBy, CancellationToken cancellationToken) + => AsyncClient.IncrementValueInHashAsync(hashId, key, incrementBy, cancellationToken); + + ValueTask IRedisHashAsync.RemoveAsync(string key, CancellationToken cancellationToken) + => AsyncClient.RemoveEntryFromHashAsync(hashId, key, cancellationToken); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientHash.cs b/src/ServiceStack.Redis/RedisClientHash.cs index dd9efd76..bc9c877e 100644 --- a/src/ServiceStack.Redis/RedisClientHash.cs +++ b/src/ServiceStack.Redis/RedisClientHash.cs @@ -20,7 +20,7 @@ namespace ServiceStack.Redis /// /// Wrap the common redis set operations under a ICollection[string] interface. /// - internal class RedisClientHash + internal partial class RedisClientHash : IRedisHash { private readonly RedisClient client; diff --git a/src/ServiceStack.Redis/RedisClientList.Async.cs b/src/ServiceStack.Redis/RedisClientList.Async.cs new file mode 100644 index 00000000..a79b4bd4 --- /dev/null +++ b/src/ServiceStack.Redis/RedisClientList.Async.cs @@ -0,0 +1,161 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Redis.Internal; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + internal partial class RedisClientList + : IRedisListAsync + { + private IRedisClientAsync AsyncClient => client; + private IRedisListAsync AsAsync() => this; + + ValueTask IRedisListAsync.AppendAsync(string value, CancellationToken cancellationToken) + => AsyncClient.AddItemToListAsync(listId, value, cancellationToken); + + ValueTask IRedisListAsync.BlockingDequeueAsync(TimeSpan? timeOut, CancellationToken cancellationToken) + => AsyncClient.BlockingDequeueItemFromListAsync(listId, timeOut, cancellationToken); + + ValueTask IRedisListAsync.BlockingPopAsync(TimeSpan? timeOut, CancellationToken cancellationToken) + => AsyncClient.BlockingPopItemFromListAsync(listId, timeOut, cancellationToken); + + ValueTask IRedisListAsync.BlockingRemoveStartAsync(TimeSpan? timeOut, CancellationToken cancellationToken) + => AsyncClient.BlockingRemoveStartFromListAsync(listId, timeOut, cancellationToken); + + ValueTask IRedisListAsync.CountAsync(CancellationToken cancellationToken) + => AsyncClient.GetListCountAsync(listId, cancellationToken).AsInt32(); + + ValueTask IRedisListAsync.DequeueAsync(CancellationToken cancellationToken) + => AsyncClient.DequeueItemFromListAsync(listId); + + ValueTask IRedisListAsync.EnqueueAsync(string value, CancellationToken cancellationToken) + => AsyncClient.EnqueueItemOnListAsync(listId, value, cancellationToken); + + ValueTask> IRedisListAsync.GetAllAsync(CancellationToken cancellationToken) + => AsyncClient.GetAllItemsFromListAsync(listId, cancellationToken); + + + async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) + { + var count = await AsAsync().CountAsync(cancellationToken).ConfigureAwait(false); + if (count <= PageLimit) + { + var all = await AsyncClient.GetAllItemsFromListAsync(listId, cancellationToken).ConfigureAwait(false); + foreach (var item in all) + { + yield return item; + } + } + else + { + // from GetPagingEnumerator() + var skip = 0; + List pageResults; + do + { + pageResults = await AsyncClient.GetRangeFromListAsync(listId, skip, skip + PageLimit - 1, cancellationToken).ConfigureAwait(false); + foreach (var result in pageResults) + { + yield return result; + } + skip += PageLimit; + } while (pageResults.Count == PageLimit); + } + } + + ValueTask> IRedisListAsync.GetRangeAsync(int startingFrom, int endingAt, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromListAsync(listId, startingFrom, endingAt, cancellationToken); + + ValueTask> IRedisListAsync.GetRangeFromSortedListAsync(int startingFrom, int endingAt, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedListAsync(listId, startingFrom, endingAt, cancellationToken); + + ValueTask IRedisListAsync.PopAndPushAsync(IRedisListAsync toList, CancellationToken cancellationToken) + => AsyncClient.PopAndPushItemBetweenListsAsync(listId, toList.Id, cancellationToken); + + ValueTask IRedisListAsync.PopAsync(CancellationToken cancellationToken) + => AsyncClient.PopItemFromListAsync(listId, cancellationToken); + + ValueTask IRedisListAsync.PrependAsync(string value, CancellationToken cancellationToken) + => AsyncClient.PrependItemToListAsync(listId, value, cancellationToken); + + ValueTask IRedisListAsync.PushAsync(string value, CancellationToken cancellationToken) + => AsyncClient.PushItemToListAsync(listId, value, cancellationToken); + + ValueTask IRedisListAsync.RemoveAllAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveAllFromListAsync(listId, cancellationToken); + + ValueTask IRedisListAsync.RemoveEndAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveEndFromListAsync(listId, cancellationToken); + + ValueTask IRedisListAsync.RemoveStartAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveStartFromListAsync(listId, cancellationToken); + + ValueTask IRedisListAsync.RemoveValueAsync(string value, CancellationToken cancellationToken) + => AsyncClient.RemoveItemFromListAsync(listId, value, cancellationToken); + + ValueTask IRedisListAsync.RemoveValueAsync(string value, int noOfMatches, CancellationToken cancellationToken) + => AsyncClient.RemoveItemFromListAsync(listId, value, noOfMatches, cancellationToken); + + ValueTask IRedisListAsync.TrimAsync(int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken) + => AsyncClient.TrimListAsync(listId, keepStartingFrom, keepEndingAt, cancellationToken); + + async ValueTask IRedisListAsync.RemoveAsync(string value, CancellationToken cancellationToken) + => (await AsyncClient.RemoveItemFromListAsync(listId, value, cancellationToken).ConfigureAwait(false)) > 0; + + ValueTask IRedisListAsync.AddAsync(string value, CancellationToken cancellationToken) + => AsyncClient.AddItemToListAsync(listId, value, cancellationToken); + + async ValueTask IRedisListAsync.RemoveAtAsync(int index, CancellationToken cancellationToken) + { + //TODO: replace with native implementation when one exists + var markForDelete = Guid.NewGuid().ToString(); + await AsyncClient.SetItemInListAsync(listId, index, markForDelete, cancellationToken).ConfigureAwait(false); + await AsyncClient.RemoveItemFromListAsync(listId, markForDelete, cancellationToken).ConfigureAwait(false); + } + + async ValueTask IRedisListAsync.ContainsAsync(string value, CancellationToken cancellationToken) + { + //TODO: replace with native implementation when exists + await foreach (var existingItem in this.ConfigureAwait(false).WithCancellation(cancellationToken)) + { + if (existingItem == value) return true; + } + return false; + } + + ValueTask IRedisListAsync.ClearAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveAllFromListAsync(listId); + + async ValueTask IRedisListAsync.IndexOfAsync(string value, CancellationToken cancellationToken) + { + //TODO: replace with native implementation when exists + var i = 0; + await foreach (var existingItem in this.ConfigureAwait(false).WithCancellation(cancellationToken)) + { + if (existingItem == value) return i; + i++; + } + return -1; + } + + ValueTask IRedisListAsync.ElementAtAsync(int index, CancellationToken cancellationToken) + => AsyncClient.GetItemFromListAsync(listId, index); + + ValueTask IRedisListAsync.SetValueAsync(int index, string value, CancellationToken cancellationToken) + => AsyncClient.SetItemInListAsync(listId, index, value); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientList.cs b/src/ServiceStack.Redis/RedisClientList.cs index 8d80b101..88d0c855 100644 --- a/src/ServiceStack.Redis/RedisClientList.cs +++ b/src/ServiceStack.Redis/RedisClientList.cs @@ -19,7 +19,7 @@ namespace ServiceStack.Redis /// /// Wrap the common redis list operations under a IList[string] interface. /// - internal class RedisClientList + internal partial class RedisClientList : IRedisList { private readonly RedisClient client; diff --git a/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs b/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs new file mode 100644 index 00000000..aa4dfd44 --- /dev/null +++ b/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs @@ -0,0 +1,173 @@ +using ServiceStack.Caching; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + partial class RedisClientManagerCacheClient : ICacheClientAsync, IRemoveByPatternAsync, ICacheClientExtendedAsync + { + ValueTask IAsyncDisposable.DisposeAsync() + { + Dispose(); + return default; + } + + private ValueTask GetClientAsync(in CancellationToken cancellationToken) + { + AssertNotReadOnly(); + return redisManager.GetClientAsync(cancellationToken); + } + + async ValueTask ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) + { + await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.GetAsync(key).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.SetAsync(key, value, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await client.FlushAllAsync(cancellationToken).ConfigureAwait(false); + } + + async ValueTask> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) + { + await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.GetAllAsync(keys, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await client.SetAllAsync(values, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.RemoveAsync(key, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientExtendedAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) + { + await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + if (client is ICacheClientExtendedAsync extended) + { + return await extended.GetTimeToLiveAsync(key, cancellationToken).ConfigureAwait(false); + } + return null; + + } + + async IAsyncEnumerable ICacheClientExtendedAsync.GetKeysByPatternAsync(string pattern, [EnumeratorCancellation] CancellationToken cancellationToken) + { + await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + if (client is ICacheClientExtendedAsync extended) + { + await foreach (var key in extended.GetKeysByPatternAsync(pattern).WithCancellation(cancellationToken).ConfigureAwait(false)) + { + yield return key; + } + } + } + + ValueTask ICacheClientExtendedAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationToken) + { + //Redis automatically removed expired Cache Entries + return default; + } + + async ValueTask IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + if (client is IRemoveByPatternAsync redisClient) + { + await redisClient.RemoveByPatternAsync(pattern).ConfigureAwait(false); + } + } + + async ValueTask IRemoveByPatternAsync.RemoveByRegexAsync(string regex, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + if (client is IRemoveByPatternAsync redisClient) + { + await redisClient.RemoveByRegexAsync(regex).ConfigureAwait(false); + } + } + + async ValueTask ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await client.RemoveAllAsync(keys, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.IncrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.DecrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.AddAsync(key, value, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + } + + async ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + { + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientManagerCacheClient.cs b/src/ServiceStack.Redis/RedisClientManagerCacheClient.cs index 8da814ee..6557bb46 100644 --- a/src/ServiceStack.Redis/RedisClientManagerCacheClient.cs +++ b/src/ServiceStack.Redis/RedisClientManagerCacheClient.cs @@ -13,7 +13,7 @@ namespace ServiceStack.Redis /// This works well for master-replica replication scenarios where you have /// 1 master that replicates to multiple read replicas. /// - public class RedisClientManagerCacheClient : ICacheClient, IRemoveByPattern, ICacheClientExtended + public partial class RedisClientManagerCacheClient : ICacheClient, IRemoveByPattern, ICacheClientExtended { private readonly IRedisClientsManager redisManager; diff --git a/src/ServiceStack.Redis/RedisClientSet.Async.cs b/src/ServiceStack.Redis/RedisClientSet.Async.cs new file mode 100644 index 00000000..38476eed --- /dev/null +++ b/src/ServiceStack.Redis/RedisClientSet.Async.cs @@ -0,0 +1,118 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Redis.Internal; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + internal partial class RedisClientSet + : IRedisSetAsync + { + private IRedisSetAsync AsAsync() => this; + private IRedisClientAsync AsyncClient => client; + + ValueTask IRedisSetAsync.AddAsync(string item, CancellationToken cancellationToken) + => AsyncClient.AddItemToSetAsync(setId, item, cancellationToken); + + ValueTask IRedisSetAsync.ClearAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveAsync(setId, cancellationToken).Await(); + + ValueTask IRedisSetAsync.ContainsAsync(string item, CancellationToken cancellationToken) + => AsyncClient.SetContainsItemAsync(setId, item, cancellationToken); + + ValueTask IRedisSetAsync.CountAsync(CancellationToken cancellationToken) + => AsyncClient.GetSetCountAsync(setId, cancellationToken).AsInt32(); + + ValueTask> IRedisSetAsync.DiffAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken) + { + var withSetIds = withSets.ToList().ConvertAll(x => x.Id).ToArray(); + return AsyncClient.GetDifferencesFromSetAsync(setId, withSetIds, cancellationToken); + } + + ValueTask> IRedisSetAsync.GetAllAsync(CancellationToken cancellationToken) + => AsyncClient.GetAllItemsFromSetAsync(setId, cancellationToken); + + IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) + => AsyncClient.ScanAllSetItemsAsync(setId).GetAsyncEnumerator(cancellationToken); // uses SSCAN + + ValueTask IRedisSetAsync.GetRandomEntryAsync(CancellationToken cancellationToken) + => AsyncClient.GetRandomItemFromSetAsync(setId, cancellationToken); + + ValueTask> IRedisSetAsync.GetRangeFromSortedSetAsync(int startingFrom, int endingAt, CancellationToken cancellationToken) + => AsyncClient.GetSortedEntryValuesAsync(setId, startingFrom, endingAt, cancellationToken); + + ValueTask> IRedisSetAsync.IntersectAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken) + { + var allSetIds = MergeSetIds(withSets); + return AsyncClient.GetIntersectFromSetsAsync(allSetIds.ToArray(), cancellationToken); + } + + ValueTask> IRedisSetAsync.IntersectAsync(params IRedisSetAsync[] withSets) + => AsAsync().IntersectAsync(withSets, cancellationToken: default); + + private List MergeSetIds(IRedisSetAsync[] withSets) + { + var allSetIds = new List { setId }; + allSetIds.AddRange(withSets.ToList().ConvertAll(x => x.Id)); + return allSetIds; + } + + ValueTask IRedisSetAsync.MoveAsync(string value, IRedisSetAsync toSet, CancellationToken cancellationToken) + => AsyncClient.MoveBetweenSetsAsync(setId, toSet.Id, value, cancellationToken); + + ValueTask IRedisSetAsync.PopAsync(CancellationToken cancellationToken) + => AsyncClient.PopItemFromSetAsync(setId, cancellationToken); + + ValueTask IRedisSetAsync.RemoveAsync(string item, CancellationToken cancellationToken) + => AsyncClient.RemoveItemFromSetAsync(setId, item, cancellationToken).AwaitAsTrue(); // see Remove for why true + + ValueTask IRedisSetAsync.StoreDiffAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken) + { + var withSetIds = withSets.ToList().ConvertAll(x => x.Id).ToArray(); + return AsyncClient.StoreDifferencesFromSetAsync(setId, fromSet.Id, withSetIds, cancellationToken); + } + + ValueTask IRedisSetAsync.StoreDiffAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets) + => AsAsync().StoreDiffAsync(fromSet, withSets, cancellationToken: default); + + ValueTask IRedisSetAsync.StoreIntersectAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken) + { + var withSetIds = withSets.ToList().ConvertAll(x => x.Id).ToArray(); + return AsyncClient.StoreIntersectFromSetsAsync(setId, withSetIds, cancellationToken); + } + + ValueTask IRedisSetAsync.StoreIntersectAsync(params IRedisSetAsync[] withSets) + => AsAsync().StoreIntersectAsync(withSets, cancellationToken: default); + + ValueTask IRedisSetAsync.StoreUnionAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken) + { + var withSetIds = withSets.ToList().ConvertAll(x => x.Id).ToArray(); + return AsyncClient.StoreUnionFromSetsAsync(setId, withSetIds, cancellationToken); + } + + ValueTask IRedisSetAsync.StoreUnionAsync(params IRedisSetAsync[] withSets) + => AsAsync().StoreUnionAsync(withSets, cancellationToken: default); + + ValueTask> IRedisSetAsync.UnionAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken) + { + var allSetIds = MergeSetIds(withSets); + return AsyncClient.GetUnionFromSetsAsync(allSetIds.ToArray(), cancellationToken); + } + + ValueTask> IRedisSetAsync.UnionAsync(params IRedisSetAsync[] withSets) + => AsAsync().UnionAsync(withSets, cancellationToken: default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientSet.cs b/src/ServiceStack.Redis/RedisClientSet.cs index dc96b566..b923fa9b 100644 --- a/src/ServiceStack.Redis/RedisClientSet.cs +++ b/src/ServiceStack.Redis/RedisClientSet.cs @@ -20,7 +20,7 @@ namespace ServiceStack.Redis /// /// Wrap the common redis set operations under a ICollection[string] interface. /// - internal class RedisClientSet + internal partial class RedisClientSet : IRedisSet { private readonly RedisClient client; diff --git a/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs b/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs new file mode 100644 index 00000000..9c00c1ec --- /dev/null +++ b/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs @@ -0,0 +1,102 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Redis.Internal; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + internal partial class RedisClientSortedSet + : IRedisSortedSetAsync + { + private IRedisClientAsync AsyncClient => client; + + ValueTask IRedisSortedSetAsync.AddAsync(string value, CancellationToken cancellationToken) + => AsyncClient.AddItemToSortedSetAsync(setId, value, cancellationToken).Await(); + + private IRedisSortedSetAsync AsAsync() => this; + + ValueTask IRedisSortedSetAsync.ClearAsync(CancellationToken cancellationToken) + => AsyncClient.RemoveAsync(setId, cancellationToken).Await(); + + ValueTask IRedisSortedSetAsync.ContainsAsync(string value, CancellationToken cancellationToken) + => AsyncClient.SortedSetContainsItemAsync(setId, value, cancellationToken); + + ValueTask IRedisSortedSetAsync.CountAsync(CancellationToken cancellationToken) + => AsyncClient.GetSortedSetCountAsync(setId, cancellationToken).AsInt32(); + + ValueTask> IRedisSortedSetAsync.GetAllAsync(CancellationToken cancellationToken) + => AsyncClient.GetAllItemsFromSortedSetAsync(setId, cancellationToken); + + async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) + { + // uses ZSCAN + await foreach (var pair in AsyncClient.ScanAllSortedSetItemsAsync(setId, cancellationToken: cancellationToken).ConfigureAwait(false)) + { + yield return pair.Key; + } + } + + ValueTask IRedisSortedSetAsync.GetItemIndexAsync(string value, CancellationToken cancellationToken) + => AsyncClient.GetItemIndexInSortedSetAsync(setId, value, cancellationToken); + + ValueTask IRedisSortedSetAsync.GetItemScoreAsync(string value, CancellationToken cancellationToken) + => AsyncClient.GetItemScoreInSortedSetAsync(setId, value, cancellationToken); + + ValueTask> IRedisSortedSetAsync.GetRangeAsync(int startingRank, int endingRank, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetAsync(setId, startingRank, endingRank, cancellationToken); + + ValueTask> IRedisSortedSetAsync.GetRangeByScoreAsync(string fromStringScore, string toStringScore, CancellationToken cancellationToken) + => AsAsync().GetRangeByScoreAsync(fromStringScore, toStringScore, null, null, cancellationToken); + + ValueTask> IRedisSortedSetAsync.GetRangeByScoreAsync(string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(setId, fromStringScore, toStringScore, skip, take, cancellationToken); + + ValueTask> IRedisSortedSetAsync.GetRangeByScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken) + => AsAsync().GetRangeByScoreAsync(fromScore, toScore, null, null, cancellationToken); + + ValueTask> IRedisSortedSetAsync.GetRangeByScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken); + + ValueTask IRedisSortedSetAsync.IncrementItemScoreAsync(string value, double incrementByScore, CancellationToken cancellationToken) + => AsyncClient.IncrementItemInSortedSetAsync(setId, value, incrementByScore, cancellationToken).Await(); + + ValueTask IRedisSortedSetAsync.PopItemWithHighestScoreAsync(CancellationToken cancellationToken) + => AsyncClient.PopItemWithHighestScoreFromSortedSetAsync(setId, cancellationToken); + + ValueTask IRedisSortedSetAsync.PopItemWithLowestScoreAsync(CancellationToken cancellationToken) + => AsyncClient.PopItemWithLowestScoreFromSortedSetAsync(setId, cancellationToken); + + ValueTask IRedisSortedSetAsync.RemoveAsync(string value, CancellationToken cancellationToken) + => AsyncClient.RemoveItemFromSortedSetAsync(setId, value, cancellationToken).AwaitAsTrue(); // see Remove() for why "true" + + ValueTask IRedisSortedSetAsync.RemoveRangeAsync(int fromRank, int toRank, CancellationToken cancellationToken) + => AsyncClient.RemoveRangeFromSortedSetAsync(setId, fromRank, toRank, cancellationToken).Await(); + + ValueTask IRedisSortedSetAsync.RemoveRangeByScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken) + => AsyncClient.RemoveRangeFromSortedSetByScoreAsync(setId, fromScore, toScore, cancellationToken).Await(); + + ValueTask IRedisSortedSetAsync.StoreFromIntersectAsync(IRedisSortedSetAsync[] ofSets, CancellationToken cancellationToken) + => AsyncClient.StoreIntersectFromSortedSetsAsync(setId, ofSets.GetIds(), cancellationToken).Await(); + + ValueTask IRedisSortedSetAsync.StoreFromIntersectAsync(params IRedisSortedSetAsync[] ofSets) + => AsAsync().StoreFromIntersectAsync(ofSets, cancellationToken: default); + + ValueTask IRedisSortedSetAsync.StoreFromUnionAsync(IRedisSortedSetAsync[] ofSets, CancellationToken cancellationToken) + => AsyncClient.StoreUnionFromSortedSetsAsync(setId, ofSets.GetIds(), cancellationToken).Await(); + + ValueTask IRedisSortedSetAsync.StoreFromUnionAsync(params IRedisSortedSetAsync[] ofSets) + => AsAsync().StoreFromUnionAsync(ofSets, cancellationToken: default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientSortedSet.cs b/src/ServiceStack.Redis/RedisClientSortedSet.cs index 78086aec..118bb2fb 100644 --- a/src/ServiceStack.Redis/RedisClientSortedSet.cs +++ b/src/ServiceStack.Redis/RedisClientSortedSet.cs @@ -20,7 +20,7 @@ namespace ServiceStack.Redis /// /// Wrap the common redis set operations under a ICollection[string] interface. /// - internal class RedisClientSortedSet + internal partial class RedisClientSortedSet : IRedisSortedSet { private readonly RedisClient client; diff --git a/src/ServiceStack.Redis/RedisClient_Admin.cs b/src/ServiceStack.Redis/RedisClient_Admin.cs index 0dc7e3f1..dfa5fa5c 100644 --- a/src/ServiceStack.Redis/RedisClient_Admin.cs +++ b/src/ServiceStack.Redis/RedisClient_Admin.cs @@ -32,8 +32,13 @@ public RedisText GetServerRoleInfo() public string GetConfig(string configItem) { - var sb = StringBuilderCache.Allocate(); var byteArray = base.ConfigGet(configItem); + return GetConfigParse(byteArray); + } + + static string GetConfigParse(byte[][] byteArray) + { + var sb = StringBuilderCache.Allocate(); const int startAt = 1; //skip repeating config name for (var i = startAt; i < byteArray.Length; i++) { @@ -80,7 +85,11 @@ public long KillClients(string fromAddress = null, string withId = null, RedisCl public List> GetClientsInfo() { - var clientList = base.ClientList().FromUtf8Bytes(); + return GetClientsInfoParse(ClientList()); + } + private static List> GetClientsInfoParse(byte[] rawResult) + { + var clientList = rawResult.FromUtf8Bytes(); var results = new List>(); var lines = clientList.Split('\n'); @@ -108,6 +117,11 @@ public void PauseAllClients(TimeSpan duration) public DateTime GetServerTime() { var parts = base.Time(); + return ParseTimeResult(parts); + } + + private static DateTime ParseTimeResult(byte[][] parts) + { var unixTime = long.Parse(parts[0].FromUtf8Bytes()); var microSecs = long.Parse(parts[1].FromUtf8Bytes()); var ticks = microSecs / 1000 * TimeSpan.TicksPerMillisecond; diff --git a/src/ServiceStack.Redis/RedisClient_Hash.Async.cs b/src/ServiceStack.Redis/RedisClient_Hash.Async.cs new file mode 100644 index 00000000..f4a93100 --- /dev/null +++ b/src/ServiceStack.Redis/RedisClient_Hash.Async.cs @@ -0,0 +1,30 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Model; +using System; + +namespace ServiceStack.Redis +{ + public partial class RedisClient + { + internal partial class RedisClientHashes + : IHasNamed + { + IRedisHashAsync IHasNamed.this[string hashId] + { + get => new RedisClientHash(client, hashId); + set => throw new NotSupportedException(); + } + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClient_Hash.cs b/src/ServiceStack.Redis/RedisClient_Hash.cs index 66ea80fd..c2cd0a91 100644 --- a/src/ServiceStack.Redis/RedisClient_Hash.cs +++ b/src/ServiceStack.Redis/RedisClient_Hash.cs @@ -23,7 +23,7 @@ public partial class RedisClient { public IHasNamed Hashes { get; set; } - internal class RedisClientHashes + internal partial class RedisClientHashes : IHasNamed { private readonly RedisClient client; @@ -59,12 +59,23 @@ public bool SetEntryInHashIfNotExists(string hashId, string key, string value) } public void SetRangeInHash(string hashId, IEnumerable> keyValuePairs) + { + if (SetRangeInHashPrepare(keyValuePairs, out var keys, out var values)) + { + base.HMSet(hashId, keys, values); + } + } + bool SetRangeInHashPrepare(IEnumerable> keyValuePairs, out byte[][] keys, out byte[][] values) { var keyValuePairsList = keyValuePairs.ToList(); - if (keyValuePairsList.Count == 0) return; + if (keyValuePairsList.Count == 0) + { + keys = values = default; + return false; + } - var keys = new byte[keyValuePairsList.Count][]; - var values = new byte[keyValuePairsList.Count][]; + keys = new byte[keyValuePairsList.Count][]; + values = new byte[keyValuePairsList.Count][]; for (var i = 0; i < keyValuePairsList.Count; i++) { @@ -72,8 +83,7 @@ public void SetRangeInHash(string hashId, IEnumerable + { + IRedisListAsync IHasNamed.this[string listId] + { + get => new RedisClientList(client, listId); + set => throw new NotSupportedException(); + } + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClient_List.cs b/src/ServiceStack.Redis/RedisClient_List.cs index 77f7a0fd..067705c7 100644 --- a/src/ServiceStack.Redis/RedisClient_List.cs +++ b/src/ServiceStack.Redis/RedisClient_List.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Linq; using ServiceStack.Model; +using ServiceStack.Redis.Pipeline; using ServiceStack.Text; namespace ServiceStack.Redis @@ -26,7 +27,7 @@ public partial class RedisClient public IHasNamed Lists { get; set; } - internal class RedisClientLists + internal partial class RedisClientLists : IHasNamed { private readonly RedisClient client; @@ -81,6 +82,15 @@ public void AddItemToList(string listId, string value) } public void AddRangeToList(string listId, List values) + { + var pipeline = AddRangeToListPrepareNonFlushed(listId, values); + pipeline.Flush(); + + //the number of items after + var intResults = pipeline.ReadAllAsInts(); + } + + private RedisPipelineCommand AddRangeToListPrepareNonFlushed(string listId, List values) { var uListId = listId.ToUtf8Bytes(); @@ -89,10 +99,7 @@ public void AddRangeToList(string listId, List values) { pipeline.WriteCommand(Commands.RPush, uListId, value.ToUtf8Bytes()); } - pipeline.Flush(); - - //the number of items after - var intResults = pipeline.ReadAllAsInts(); + return pipeline; } public void PrependItemToList(string listId, string value) @@ -101,6 +108,15 @@ public void PrependItemToList(string listId, string value) } public void PrependRangeToList(string listId, List values) + { + var pipeline = PrependRangeToListPrepareNonFlushed(listId, values); + pipeline.Flush(); + + //the number of items after + var intResults = pipeline.ReadAllAsInts(); + } + + private RedisPipelineCommand PrependRangeToListPrepareNonFlushed(string listId, List values) { var uListId = listId.ToUtf8Bytes(); @@ -111,10 +127,7 @@ public void PrependRangeToList(string listId, List values) var value = values[i]; pipeline.WriteCommand(Commands.LPush, uListId, value.ToUtf8Bytes()); } - pipeline.Flush(); - - //the number of items after - var intResults = pipeline.ReadAllAsInts(); + return pipeline; } public void RemoveAllFromList(string listId) diff --git a/src/ServiceStack.Redis/RedisClient_Set.Async.cs b/src/ServiceStack.Redis/RedisClient_Set.Async.cs new file mode 100644 index 00000000..698d131f --- /dev/null +++ b/src/ServiceStack.Redis/RedisClient_Set.Async.cs @@ -0,0 +1,30 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Model; +using System; + +namespace ServiceStack.Redis +{ + public partial class RedisClient + { + internal partial class RedisClientSets + : IHasNamed + { + IRedisSetAsync IHasNamed.this[string setId] + { + get => new RedisClientSet(client, setId); + set => throw new NotSupportedException(); + } + } + } +} diff --git a/src/ServiceStack.Redis/RedisClient_Set.cs b/src/ServiceStack.Redis/RedisClient_Set.cs index 8b18f169..3a671a8d 100644 --- a/src/ServiceStack.Redis/RedisClient_Set.cs +++ b/src/ServiceStack.Redis/RedisClient_Set.cs @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using ServiceStack.Common; using ServiceStack.Model; using ServiceStack.Redis.Generic; @@ -26,7 +27,7 @@ public partial class RedisClient { public IHasNamed Sets { get; set; } - internal class RedisClientSets + internal partial class RedisClientSets : IHasNamed { private readonly RedisClient client; @@ -96,6 +97,11 @@ public List GetGeoCoordinates(string key, params string[] members) public string[] FindGeoMembersInRadius(string key, double longitude, double latitude, double radius, string unit) { var results = base.GeoRadius(key, longitude, latitude, radius, unit); + return ParseFindGeoMembersResult(results); + } + + private static string[] ParseFindGeoMembersResult(List results) + { var to = new string[results.Count]; for (var i = 0; i < results.Count; i++) { @@ -113,12 +119,7 @@ public List FindGeoResultsInRadius(string key, double longitude, public string[] FindGeoMembersInRadius(string key, string member, double radius, string unit) { var results = base.GeoRadiusByMember(key, member, radius, unit); - var to = new string[results.Count]; - for (var i = 0; i < results.Count; i++) - { - to[i] = results[i].Member; - } - return to; + return ParseFindGeoMembersResult(results); } public List FindGeoResultsInRadius(string key, string member, double radius, string unit, int? count = null, bool? sortByNearest = null) @@ -138,13 +139,30 @@ public void AddItemToSet(string setId, string item) } public void AddRangeToSet(string setId, List items) + { + if (AddRangeToSetNeedsSend(setId, items)) + { + var uSetId = setId.ToUtf8Bytes(); + var pipeline = CreatePipelineCommand(); + foreach (var item in items) + { + pipeline.WriteCommand(Commands.SAdd, uSetId, item.ToUtf8Bytes()); + } + pipeline.Flush(); + + //the number of items after + _ = pipeline.ReadAllAsInts(); + } + } + + bool AddRangeToSetNeedsSend(string setId, List items) { if (setId.IsNullOrEmpty()) throw new ArgumentNullException("setId"); if (items == null) throw new ArgumentNullException("items"); if (items.Count == 0) - return; + return false; if (this.Transaction != null || this.Pipeline != null) { @@ -163,19 +181,11 @@ public void AddRangeToSet(string setId, List items) var item = items[i]; queueable.QueueCommand(c => c.AddItemToSet(setId, item)); } + return false; } else { - var uSetId = setId.ToUtf8Bytes(); - var pipeline = CreatePipelineCommand(); - foreach (var item in items) - { - pipeline.WriteCommand(Commands.SAdd, uSetId, item.ToUtf8Bytes()); - } - pipeline.Flush(); - - //the number of items after - var intResults = pipeline.ReadAllAsInts(); + return true; } } diff --git a/src/ServiceStack.Redis/RedisClient_Slowlog.cs b/src/ServiceStack.Redis/RedisClient_Slowlog.cs index eb36f3b9..c84a3ea2 100644 --- a/src/ServiceStack.Redis/RedisClient_Slowlog.cs +++ b/src/ServiceStack.Redis/RedisClient_Slowlog.cs @@ -24,6 +24,11 @@ public partial class RedisClient public IEnumerable GetSlowlog(int? numberOfRecords = null) { var data = Slowlog(numberOfRecords); + return ParseSlowlog(data); + } + + private static SlowlogItem[] ParseSlowlog(object[] data) + { var list = new SlowlogItem[data.Length]; for (int i = 0; i < data.Length; i++) { diff --git a/src/ServiceStack.Redis/RedisClient_SortedSet.Async.cs b/src/ServiceStack.Redis/RedisClient_SortedSet.Async.cs new file mode 100644 index 00000000..72350cde --- /dev/null +++ b/src/ServiceStack.Redis/RedisClient_SortedSet.Async.cs @@ -0,0 +1,30 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using ServiceStack.Model; +using System; + +namespace ServiceStack.Redis +{ + public partial class RedisClient : IRedisClient + { + internal partial class RedisClientSortedSets + : IHasNamed + { + IRedisSortedSetAsync IHasNamed.this[string setId] + { + get => new RedisClientSortedSet(client, setId); + set => throw new NotSupportedException(); + } + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClient_SortedSet.cs b/src/ServiceStack.Redis/RedisClient_SortedSet.cs index fd909c57..00686295 100644 --- a/src/ServiceStack.Redis/RedisClient_SortedSet.cs +++ b/src/ServiceStack.Redis/RedisClient_SortedSet.cs @@ -15,6 +15,7 @@ using System.Globalization; using System.Linq; using ServiceStack.Model; +using ServiceStack.Redis.Pipeline; using ServiceStack.Redis.Support; using ServiceStack.Text; @@ -24,7 +25,7 @@ public partial class RedisClient : IRedisClient { public IHasNamed SortedSets { get; set; } - internal class RedisClientSortedSets + internal partial class RedisClientSortedSets : IHasNamed { private readonly RedisClient client; @@ -87,36 +88,29 @@ public bool AddItemToSortedSet(string setId, string value, long score) public bool AddRangeToSortedSet(string setId, List values, double score) { - var pipeline = CreatePipelineCommand(); - var uSetId = setId.ToUtf8Bytes(); - var uScore = score.ToFastUtf8Bytes(); - - foreach (var value in values) - { - pipeline.WriteCommand(Commands.ZAdd, uSetId, uScore, value.ToUtf8Bytes()); - } - + var pipeline = AddRangeToSortedSetPrepareNonFlushed(setId, values, score.ToFastUtf8Bytes()); pipeline.Flush(); - var success = pipeline.ReadAllAsIntsHaveSuccess(); - return success; + return pipeline.ReadAllAsIntsHaveSuccess(); } public bool AddRangeToSortedSet(string setId, List values, long score) + { + var pipeline = AddRangeToSortedSetPrepareNonFlushed(setId, values, score.ToUtf8Bytes()); + pipeline.Flush(); + + return pipeline.ReadAllAsIntsHaveSuccess(); + } + RedisPipelineCommand AddRangeToSortedSetPrepareNonFlushed(string setId, List values, byte[] uScore) { var pipeline = CreatePipelineCommand(); var uSetId = setId.ToUtf8Bytes(); - var uScore = score.ToUtf8Bytes(); foreach (var value in values) { pipeline.WriteCommand(Commands.ZAdd, uSetId, uScore, value.ToUtf8Bytes()); } - - pipeline.Flush(); - - var success = pipeline.ReadAllAsIntsHaveSuccess(); - return success; + return pipeline; } public bool RemoveItemFromSortedSet(string setId, string value) diff --git a/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs b/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs new file mode 100644 index 00000000..d57ae1a0 --- /dev/null +++ b/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs @@ -0,0 +1,120 @@ +using ServiceStack.Caching; +using ServiceStack.Redis.Generic; +using ServiceStack.Redis.Internal; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + /// + /// Useful wrapper IRedisClientsManager to cut down the boiler plate of most IRedisClient access + /// + public static partial class RedisClientsManagerExtensions + { + ///// + ///// Creates a PubSubServer that uses a background thread to listen and process for + ///// Redis Pub/Sub messages published to the specified channel. + ///// Use optional callbacks to listen for message, error and life-cycle events. + ///// Callbacks can be assigned later, then call Start() for PubSubServer to start listening for messages + ///// + //public static IRedisPubSubServer CreatePubSubServer(this IRedisClientsManager redisManager, + // string channel, + // Action onMessage = null, + // Action onError = null, + // Action onInit = null, + // Action onStart = null, + // Action onStop = null) + //{ + // return new RedisPubSubServer(redisManager, channel) + // { + // OnMessage = onMessage, + // OnError = onError, + // OnInit = onInit, + // OnStart = onStart, + // OnStop = onStop, + // }; + //} + + private static T InvalidAsyncClient(IRedisClientsManager manager, string method) where T : class + => throw new NotSupportedException($"The client returned from '{manager?.GetType().FullName ?? "(null)"}.{method}()' does not implement {typeof(T).Name}"); + + public static ValueTask GetClientAsync(this IRedisClientsManager redisManager, CancellationToken cancellationToken = default) + { + return redisManager is IRedisClientsManagerAsync asyncManager + ? asyncManager.GetClientAsync(cancellationToken) + : (redisManager.GetClient() as IRedisClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetClient))).AsValueTask(); + } + + public static ValueTask GetReadOnlyClientAsync(this IRedisClientsManager redisManager, CancellationToken cancellationToken = default) + { + return redisManager is IRedisClientsManagerAsync asyncManager + ? asyncManager.GetReadOnlyClientAsync(cancellationToken) + : (redisManager.GetReadOnlyClient() as IRedisClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetReadOnlyClient))).AsValueTask(); + } + + public static ValueTask GetCacheClientAsync(this IRedisClientsManager redisManager, CancellationToken cancellationToken = default) + { + return redisManager is IRedisClientsManagerAsync asyncManager + ? asyncManager.GetCacheClientAsync(cancellationToken) + : (redisManager.GetCacheClient() as ICacheClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetCacheClient))).AsValueTask(); + } + + public static ValueTask GetReadOnlyCacheClientAsync(this IRedisClientsManager redisManager, CancellationToken cancellationToken = default) + { + return redisManager is IRedisClientsManagerAsync asyncManager + ? asyncManager.GetReadOnlyCacheClientAsync(cancellationToken) + : (redisManager.GetReadOnlyCacheClient() as ICacheClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetCacheClient))).AsValueTask(); + } + + + public static async ValueTask ExecAsync(this IRedisClientsManager redisManager, Func lambda) + { + await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + await lambda(redis).ConfigureAwait(false); + } + + public static async ValueTask ExecAsync(this IRedisClientsManager redisManager, Func> lambda) + { + await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + return await lambda(redis).ConfigureAwait(false); + } + + //public static void ExecTrans(this IRedisClientsManager redisManager, Action lambda) + //{ + // using (var redis = redisManager.GetClient()) + // using (var trans = redis.CreateTransaction()) + // { + // lambda(trans); + + // trans.Commit(); + // } + //} + + public static async ValueTask ExecAsAsync(this IRedisClientsManager redisManager, Func, ValueTask> lambda) + { + await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + await lambda(redis.As()).ConfigureAwait(false); + } + + public static async ValueTask ExecAsAsync(this IRedisClientsManager redisManager, Func, ValueTask> lambda) + { + await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + return await lambda(redis.As()).ConfigureAwait(false); + } + + public static async ValueTask> ExecAsAsync(this IRedisClientsManager redisManager, Func, ValueTask>> lambda) + { + await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + return await lambda(redis.As()).ConfigureAwait(false); + } + + public static async ValueTask> ExecAsAsync(this IRedisClientsManager redisManager, Func, ValueTask>> lambda) + { + await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + return await lambda(redis.As()).ConfigureAwait(false); + } + } + +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientsManagerExtensions.cs b/src/ServiceStack.Redis/RedisClientsManagerExtensions.cs index 9d4f8ce6..6f62b84b 100644 --- a/src/ServiceStack.Redis/RedisClientsManagerExtensions.cs +++ b/src/ServiceStack.Redis/RedisClientsManagerExtensions.cs @@ -7,7 +7,7 @@ namespace ServiceStack.Redis /// /// Useful wrapper IRedisClientsManager to cut down the boiler plate of most IRedisClient access /// - public static class RedisClientsManagerExtensions + public static partial class RedisClientsManagerExtensions { /// /// Creates a PubSubServer that uses a background thread to listen and process for diff --git a/src/ServiceStack.Redis/RedisLock.Async.cs b/src/ServiceStack.Redis/RedisLock.Async.cs new file mode 100644 index 00000000..20013da9 --- /dev/null +++ b/src/ServiceStack.Redis/RedisLock.Async.cs @@ -0,0 +1,93 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Redis.Internal; +using ServiceStack.Text; + +namespace ServiceStack.Redis +{ + public partial class RedisLock + : IAsyncDisposable + { + internal static ValueTask CreateAsync(IRedisClientAsync redisClient, string key, + TimeSpan? timeOut = default, CancellationToken cancellationToken = default) + { + var obj = new RedisLock(redisClient, key); + return obj.AcquireAsync(timeOut, cancellationToken).Await(obj); + } + + // async version of ExecUtils.RetryUntilTrue + private static async ValueTask RetryUntilTrue(Func> action, + TimeSpan? timeOut = null, CancellationToken cancellationToken = default) + { + var i = 0; + var firstAttempt = DateTime.UtcNow; + + while (timeOut == null || DateTime.UtcNow - firstAttempt < timeOut.Value) + { + cancellationToken.ThrowIfCancellationRequested(); + i++; + if (await action(cancellationToken).ConfigureAwait(false)) + { + return; + } + await Task.Delay(ExecUtils.CalculateFullJitterBackOffDelay(i)).ConfigureAwait(false); + } + + throw new TimeoutException($"Exceeded timeout of {timeOut.Value}"); + } + + + private async ValueTask AcquireAsync(TimeSpan? timeOut, CancellationToken cancellationToken) + { + var redisClient = (IRedisClientAsync)untypedClient; + await RetryUntilTrue( // .ConfigureAwait(false) is below + async ct => + { + //This pattern is taken from the redis command for SETNX http://redis.io/commands/setnx + + //Calculate a unix time for when the lock should expire + var realSpan = timeOut ?? new TimeSpan(365, 0, 0, 0); //if nothing is passed in the timeout hold for a year + var expireTime = DateTime.UtcNow.Add(realSpan); + var lockString = (expireTime.ToUnixTimeMs() + 1).ToString(); + + //Try to set the lock, if it does not exist this will succeed and the lock is obtained + var nx = await redisClient.SetValueIfNotExistsAsync(key, lockString, cancellationToken: ct).ConfigureAwait(false); + if (nx) + return true; + + //If we've gotten here then a key for the lock is present. This could be because the lock is + //correctly acquired or it could be because a client that had acquired the lock crashed (or didn't release it properly). + //Therefore we need to get the value of the lock to see when it should expire + + await redisClient.WatchAsync(new[] { key }, ct).ConfigureAwait(false); + var lockExpireString = await redisClient.GetValueAsync(key, ct).ConfigureAwait(false); + if (!long.TryParse(lockExpireString, out var lockExpireTime)) + { + await redisClient.UnWatchAsync(ct).ConfigureAwait(false); // since the client is scoped externally + return false; + } + + //If the expire time is greater than the current time then we can't let the lock go yet + if (lockExpireTime > DateTime.UtcNow.ToUnixTimeMs()) + { + await redisClient.UnWatchAsync(ct).ConfigureAwait(false); // since the client is scoped externally + return false; + } + + //If the expire time is less than the current time then it wasn't released properly and we can attempt to + //acquire the lock. The above call to Watch(_lockKey) enrolled the key in monitoring, so if it changes + //before we call Commit() below, the Commit will fail and return false, which means that another thread + //was able to acquire the lock before we finished processing. + await using var trans = await redisClient.CreateTransactionAsync(ct).ConfigureAwait(false); + trans.QueueCommand(r => r.SetValueAsync(key, lockString)); + return await trans.CommitAsync(ct).ConfigureAwait(false); //returns false if Transaction failed + }, + timeOut, cancellationToken + ).ConfigureAwait(false); + } + + ValueTask IAsyncDisposable.DisposeAsync() + => ((IRedisClientAsync)untypedClient).RemoveAsync(key).Await(); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisLock.cs b/src/ServiceStack.Redis/RedisLock.cs index 021aba20..abf3a9d9 100644 --- a/src/ServiceStack.Redis/RedisLock.cs +++ b/src/ServiceStack.Redis/RedisLock.cs @@ -1,20 +1,23 @@ -using System; -using ServiceStack.Common; using ServiceStack.Text; +using System; namespace ServiceStack.Redis { - public class RedisLock + public partial class RedisLock : IDisposable { - private readonly IRedisClient redisClient; + private readonly object untypedClient; private readonly string key; - public RedisLock(IRedisClient redisClient, string key, TimeSpan? timeOut) + private RedisLock(object redisClient, string key) { - this.redisClient = redisClient; + this.untypedClient = redisClient; this.key = key; + } + public RedisLock(IRedisClient redisClient, string key, TimeSpan? timeOut) + : this(redisClient, key) + { ExecUtils.RetryUntilTrue( () => { @@ -65,7 +68,7 @@ public RedisLock(IRedisClient redisClient, string key, TimeSpan? timeOut) public void Dispose() { - redisClient.Remove(key); + ((IRedisClient)untypedClient).Remove(key); } } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisManagerPool.Async.cs b/src/ServiceStack.Redis/RedisManagerPool.Async.cs new file mode 100644 index 00000000..f677dc10 --- /dev/null +++ b/src/ServiceStack.Redis/RedisManagerPool.Async.cs @@ -0,0 +1,33 @@ +//Copyright (c) Service Stack LLC. All Rights Reserved. +//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt + +using ServiceStack.Caching; +using ServiceStack.Redis.Internal; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + public partial class RedisManagerPool + : IRedisClientsManagerAsync + { + ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(CancellationToken cancellationToken) + => new RedisClientManagerCacheClient(this).AsValueTask(); + + ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken cancellationToken) + => GetClient(true).AsValueTask(); + + ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken cancellationToken) + => new RedisClientManagerCacheClient(this) { ReadOnly = true }.AsValueTask(); + + ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken cancellationToken) + => GetClient(true).AsValueTask(); + + ValueTask IAsyncDisposable.DisposeAsync() + { + Dispose(); + return default; + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisManagerPool.cs b/src/ServiceStack.Redis/RedisManagerPool.cs index 73b66823..43141801 100644 --- a/src/ServiceStack.Redis/RedisManagerPool.cs +++ b/src/ServiceStack.Redis/RedisManagerPool.cs @@ -129,12 +129,13 @@ public void FailoverTo(IEnumerable readWriteHosts, IEnumerable r { FailoverTo(readWriteHosts.ToArray()); //only use readWriteHosts } - + /// /// Returns a Read/Write client (The default) using the hosts defined in ReadWriteHosts /// /// - public IRedisClient GetClient() + public IRedisClient GetClient() => GetClient(false); + private RedisClient GetClient(bool forAsync) { try { @@ -196,7 +197,7 @@ public IRedisClient GetClient() poolIndex++; clients[inactivePoolIndex] = newClient; - return !AssertAccessOnlyOnSameThread + return (!AssertAccessOnlyOnSameThread || forAsync) ? newClient : newClient.LimitAccessToThread(Thread.CurrentThread.ManagedThreadId, Environment.StackTrace); } @@ -222,7 +223,7 @@ public IRedisClient GetClient() public IRedisClient GetReadOnlyClient() { - return GetClient(); + return GetClient(false); } class ReservedClient : RedisClient diff --git a/src/ServiceStack.Redis/RedisNativeClient.Async.cs b/src/ServiceStack.Redis/RedisNativeClient.Async.cs new file mode 100644 index 00000000..3a28d701 --- /dev/null +++ b/src/ServiceStack.Redis/RedisNativeClient.Async.cs @@ -0,0 +1,1487 @@ +using ServiceStack.Redis.Internal; +using ServiceStack.Redis.Pipeline; +using ServiceStack.Text; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + partial class RedisNativeClient + : IRedisNativeClientAsync + { + internal IRedisPipelineSharedAsync PipelineAsync + => (IRedisPipelineSharedAsync)pipeline; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static void AssertNotNull(object obj, string name = "key") + { + if (obj is null) Throw(name); + static void Throw(string name) => throw new ArgumentNullException(name); + } + + private IRedisNativeClientAsync AsAsync() => this; + + ValueTask IAsyncDisposable.DisposeAsync() + { + Dispose(); + return default; + } + + ValueTask IRedisNativeClientAsync.TimeAsync(CancellationToken cancellationToken) + => SendExpectMultiDataAsync(cancellationToken, Commands.Time); + + ValueTask IRedisNativeClientAsync.ExistsAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.Exists, key.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.SetAsync(string key, byte[] value, bool exists, long expirySeconds, long expiryMilliseconds, CancellationToken cancellationToken) + { + AssertNotNull(key); + value ??= TypeConstants.EmptyByteArray; + + if (value.Length > OneGb) + throw new ArgumentException("value exceeds 1G", "value"); + + var entryExists = exists ? Commands.Xx : Commands.Nx; + byte[][] args; + if (expiryMilliseconds != 0) + { + args = new[] { Commands.Set, key.ToUtf8Bytes(), value, Commands.Px, expiryMilliseconds.ToUtf8Bytes(), entryExists }; + } + else if (expirySeconds != 0) + { + args = new[] { Commands.Set, key.ToUtf8Bytes(), value, Commands.Ex, expirySeconds.ToUtf8Bytes(), entryExists }; + } + else + { + args = new[] { Commands.Set, key.ToUtf8Bytes(), value, entryExists }; + } + + return IsString(SendExpectStringAsync(cancellationToken, args), OK); + } + ValueTask IRedisNativeClientAsync.SetAsync(string key, byte[] value, long expirySeconds, long expiryMilliseconds, CancellationToken cancellationToken) + { + AssertNotNull(key); + value ??= TypeConstants.EmptyByteArray; + + if (value.Length > OneGb) + throw new ArgumentException("value exceeds 1G", "value"); + + byte[][] args; + if (expiryMilliseconds != 0) + { + args = new[] { Commands.Set, key.ToUtf8Bytes(), value, Commands.Px, expiryMilliseconds.ToUtf8Bytes() }; + } + else if (expirySeconds != 0) + { + args = new[] { Commands.Set, key.ToUtf8Bytes(), value, Commands.Ex, expirySeconds.ToUtf8Bytes() }; + } + else + { + args = new[] { Commands.Set, key.ToUtf8Bytes(), value }; + } + + return SendExpectSuccessAsync(cancellationToken, args); + } + + ValueTask IRedisNativeClientAsync.GetAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectDataAsync(cancellationToken, Commands.Get, key.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.DelAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.Del, key.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.ScanAsync(ulong cursor, int count, string match, CancellationToken cancellationToken) + { + if (match == null) + return SendExpectScanResultAsync(cancellationToken, Commands.Scan, cursor.ToUtf8Bytes(), + Commands.Count, count.ToUtf8Bytes()); + + return SendExpectScanResultAsync(cancellationToken, Commands.Scan, cursor.ToUtf8Bytes(), + Commands.Match, match.ToUtf8Bytes(), + Commands.Count, count.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.TypeAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectCodeAsync(cancellationToken, Commands.Type, key.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.RPushAsync(string listId, byte[] value, CancellationToken cancellationToken) + { + AssertListIdAndValue(listId, value); + + return SendExpectLongAsync(cancellationToken, Commands.RPush, listId.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.SAddAsync(string setId, byte[] value, CancellationToken cancellationToken) + { + AssertSetIdAndValue(setId, value); + + return SendExpectLongAsync(cancellationToken, Commands.SAdd, setId.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.ZAddAsync(string setId, double score, byte[] value, CancellationToken cancellationToken) + { + AssertSetIdAndValue(setId, value); + + return SendExpectLongAsync(cancellationToken, Commands.ZAdd, setId.ToUtf8Bytes(), score.ToFastUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.ZAddAsync(string setId, long score, byte[] value, CancellationToken cancellationToken) + { + AssertSetIdAndValue(setId, value); + + return SendExpectLongAsync(cancellationToken, Commands.ZAdd, setId.ToUtf8Bytes(), score.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.HSetAsync(string hashId, byte[] key, byte[] value, CancellationToken cancellationToken) + => HSetAsync(hashId.ToUtf8Bytes(), key, value, cancellationToken); + + internal ValueTask HSetAsync(byte[] hashId, byte[] key, byte[] value, CancellationToken cancellationToken = default) + { + AssertHashIdAndKey(hashId, key); + + return SendExpectLongAsync(cancellationToken, Commands.HSet, hashId, key, value); + } + + ValueTask IRedisNativeClientAsync.RandomKeyAsync(CancellationToken cancellationToken) + => SendExpectDataAsync(cancellationToken, Commands.RandomKey).FromUtf8BytesAsync(); + + ValueTask IRedisNativeClientAsync.RenameAsync(string oldKeyname, string newKeyname, CancellationToken cancellationToken) + { + CheckRenameKeys(oldKeyname, newKeyname); + return SendExpectSuccessAsync(cancellationToken, Commands.Rename, oldKeyname.ToUtf8Bytes(), newKeyname.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.RenameNxAsync(string oldKeyname, string newKeyname, CancellationToken cancellationToken) + { + CheckRenameKeys(oldKeyname, newKeyname); + return SendExpectLongAsync(cancellationToken, Commands.RenameNx, oldKeyname.ToUtf8Bytes(), newKeyname.ToUtf8Bytes()).IsSuccessAsync(); + } + + ValueTask IRedisNativeClientAsync.MSetAsync(byte[][] keys, byte[][] values, CancellationToken cancellationToken) + { + var keysAndValues = MergeCommandWithKeysAndValues(Commands.MSet, keys, values); + + return SendExpectSuccessAsync(cancellationToken, keysAndValues); + } + + + ValueTask IRedisNativeClientAsync.MSetAsync(string[] keys, byte[][] values, CancellationToken cancellationToken) + => ((IRedisNativeClientAsync)this).MSetAsync(keys.ToMultiByteArray(), values, cancellationToken); + + ValueTask IRedisNativeClientAsync.SelectAsync(long db, CancellationToken cancellationToken) + { + this.db = db; + return SendExpectSuccessAsync(cancellationToken, Commands.Select, db.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.DelAsync(string[] keys, CancellationToken cancellationToken) + { + AssertNotNull(keys, nameof(keys)); + + var cmdWithArgs = MergeCommandWithArgs(Commands.Del, keys); + return SendExpectLongAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.ExpireAsync(string key, int seconds, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.Expire, key.ToUtf8Bytes(), seconds.ToUtf8Bytes()).IsSuccessAsync(); + } + + ValueTask IRedisNativeClientAsync.PExpireAsync(string key, long ttlMs, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.PExpire, key.ToUtf8Bytes(), ttlMs.ToUtf8Bytes()).IsSuccessAsync(); + } + + ValueTask IRedisNativeClientAsync.ExpireAtAsync(string key, long unixTime, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.ExpireAt, key.ToUtf8Bytes(), unixTime.ToUtf8Bytes()).IsSuccessAsync(); + } + + ValueTask IRedisNativeClientAsync.PExpireAtAsync(string key, long unixTimeMs, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.PExpireAt, key.ToUtf8Bytes(), unixTimeMs.ToUtf8Bytes()).IsSuccessAsync(); + } + + ValueTask IRedisNativeClientAsync.TtlAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.Ttl, key.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.PTtlAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.PTtl, key.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.PingAsync(CancellationToken cancellationToken) + => IsString(SendExpectCodeAsync(cancellationToken, Commands.Ping), "PONG"); + + private static ValueTask IsString(ValueTask pending, string expected) + { + return pending.IsCompletedSuccessfully ? (pending.Result == expected).AsValueTask() + : Awaited(pending, expected); + + static async ValueTask Awaited(ValueTask pending, string expected) + => await pending.ConfigureAwait(false) == expected; + } + + ValueTask IRedisNativeClientAsync.EchoAsync(string text, CancellationToken cancellationToken) + => SendExpectDataAsync(cancellationToken, Commands.Echo, text.ToUtf8Bytes()).FromUtf8BytesAsync(); + + ValueTask IRedisNativeClientAsync.DbSizeAsync(CancellationToken cancellationToken) + => SendExpectLongAsync(cancellationToken, Commands.DbSize); + + ValueTask IRedisNativeClientAsync.LastSaveAsync(CancellationToken cancellationToken) + => SendExpectLongAsync(cancellationToken, Commands.LastSave).Await(t => t.FromUnixTime()); + + ValueTask IRedisNativeClientAsync.SaveAsync(CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.Save); + + ValueTask IRedisNativeClientAsync.BgSaveAsync(CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.BgSave); + + ValueTask IRedisNativeClientAsync.ShutdownAsync(bool noSave, CancellationToken cancellationToken) + => noSave + ? SendWithoutReadAsync(cancellationToken, Commands.Shutdown, Commands.NoSave) + : SendWithoutReadAsync(cancellationToken, Commands.Shutdown); + + ValueTask IRedisNativeClientAsync.BgRewriteAofAsync(CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.BgRewriteAof); + + ValueTask IRedisNativeClientAsync.QuitAsync(CancellationToken cancellationToken) + => SendWithoutReadAsync(cancellationToken, Commands.Quit); + + ValueTask IRedisNativeClientAsync.FlushDbAsync(CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.FlushDb); + + ValueTask IRedisNativeClientAsync.FlushAllAsync(CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.FlushAll); + + ValueTask IRedisNativeClientAsync.SlaveOfAsync(string hostname, int port, CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.SlaveOf, hostname.ToUtf8Bytes(), port.ToUtf8Bytes()); + + ValueTask IRedisNativeClientAsync.SlaveOfNoOneAsync(CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.SlaveOf, Commands.No, Commands.One); + + ValueTask IRedisNativeClientAsync.KeysAsync(string pattern, CancellationToken cancellationToken) + { + AssertNotNull(pattern, nameof(pattern)); + return SendExpectMultiDataAsync(cancellationToken, Commands.Keys, pattern.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.MGetAsync(string[] keys, CancellationToken cancellationToken) + { + AssertNotNull(keys, nameof(keys)); + if (keys.Length == 0) + throw new ArgumentException("keys"); + + var cmdWithArgs = MergeCommandWithArgs(Commands.MGet, keys); + + return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.SetExAsync(string key, int expireInSeconds, byte[] value, CancellationToken cancellationToken) + { + AssertNotNull(key); + value ??= TypeConstants.EmptyByteArray; + + if (value.Length > OneGb) + throw new ArgumentException("value exceeds 1G", "value"); + + return SendExpectSuccessAsync(cancellationToken, Commands.SetEx, key.ToUtf8Bytes(), expireInSeconds.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.WatchAsync(string[] keys, CancellationToken cancellationToken) + { + AssertNotNull(keys, nameof(keys)); + if (keys.Length == 0) + throw new ArgumentException("keys"); + + var cmdWithArgs = MergeCommandWithArgs(Commands.Watch, keys); + + return SendExpectCodeAsync(cancellationToken, cmdWithArgs).Await(); + } + + ValueTask IRedisNativeClientAsync.UnWatchAsync(CancellationToken cancellationToken) + => SendExpectCodeAsync(cancellationToken, Commands.UnWatch).Await(); + + ValueTask IRedisNativeClientAsync.AppendAsync(string key, byte[] value, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.Append, key.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.GetRangeAsync(string key, int fromIndex, int toIndex, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectDataAsync(cancellationToken, Commands.GetRange, key.ToUtf8Bytes(), fromIndex.ToUtf8Bytes(), toIndex.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.SetRangeAsync(string key, int offset, byte[] value, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.SetRange, key.ToUtf8Bytes(), offset.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.GetBitAsync(string key, int offset, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.GetBit, key.ToUtf8Bytes(), offset.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.SetBitAsync(string key, int offset, int value, CancellationToken cancellationToken) + { + AssertNotNull(key); + if (value > 1 || value < 0) + throw new ArgumentOutOfRangeException(nameof(value), "value is out of range"); + return SendExpectLongAsync(cancellationToken, Commands.SetBit, key.ToUtf8Bytes(), offset.ToUtf8Bytes(), value.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.PersistAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.Persist, key.ToUtf8Bytes()).IsSuccessAsync(); + } + + ValueTask IRedisNativeClientAsync.PSetExAsync(string key, long expireInMs, byte[] value, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectSuccessAsync(cancellationToken, Commands.PSetEx, key.ToUtf8Bytes(), expireInMs.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.SetNXAsync(string key, byte[] value, CancellationToken cancellationToken) + { + AssertNotNull(key); + value ??= TypeConstants.EmptyByteArray; + + if (value.Length > OneGb) + throw new ArgumentException("value exceeds 1G", "value"); + + return SendExpectLongAsync(cancellationToken, Commands.SetNx, key.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.SPopAsync(string setId, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + return SendExpectDataAsync(cancellationToken, Commands.SPop, setId.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.SPopAsync(string setId, int count, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + return SendExpectMultiDataAsync(cancellationToken, Commands.SPop, setId.ToUtf8Bytes(), count.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.SlowlogResetAsync(CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.Slowlog, "RESET".ToUtf8Bytes()); + + ValueTask IRedisNativeClientAsync.SlowlogGetAsync(int? top, CancellationToken cancellationToken) + { + if (top.HasValue) + return SendExpectDeeplyNestedMultiDataAsync(cancellationToken, Commands.Slowlog, Commands.Get, top.Value.ToUtf8Bytes()); + else + return SendExpectDeeplyNestedMultiDataAsync(cancellationToken, Commands.Slowlog, Commands.Get); + } + + ValueTask IRedisNativeClientAsync.ZCardAsync(string setId, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + return SendExpectLongAsync(cancellationToken, Commands.ZCard, setId.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.ZCountAsync(string setId, double min, double max, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + return SendExpectLongAsync(cancellationToken, Commands.ZCount, setId.ToUtf8Bytes(), min.ToUtf8Bytes(), max.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.ZScoreAsync(string setId, byte[] value, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + return SendExpectDoubleAsync(cancellationToken, Commands.ZScore, setId.ToUtf8Bytes(), value); + } + + protected ValueTask RawCommandAsync(CancellationToken cancellationToken, params object[] cmdWithArgs) + { + var byteArgs = new List(); + + foreach (var arg in cmdWithArgs) + { + if (arg == null) + { + byteArgs.Add(TypeConstants.EmptyByteArray); + continue; + } + + if (arg is byte[] bytes) + { + byteArgs.Add(bytes); + } + else if (arg.GetType().IsUserType()) + { + var json = arg.ToJson(); + byteArgs.Add(json.ToUtf8Bytes()); + } + else + { + var str = arg.ToString(); + byteArgs.Add(str.ToUtf8Bytes()); + } + } + + return SendExpectComplexResponseAsync(cancellationToken, byteArgs.ToArray()); + } + + ValueTask> IRedisNativeClientAsync.InfoAsync(CancellationToken cancellationToken) + => SendExpectStringAsync(cancellationToken, Commands.Info).Await(info => ParseInfoResult(info)); + + ValueTask IRedisNativeClientAsync.ZRangeByLexAsync(string setId, string min, string max, int? skip, int? take, CancellationToken cancellationToken) + => SendExpectMultiDataAsync(cancellationToken, GetZRangeByLexArgs(setId, min, max, skip, take)); + + ValueTask IRedisNativeClientAsync.ZLexCountAsync(string setId, string min, string max, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + + return SendExpectLongAsync(cancellationToken, + Commands.ZLexCount, setId.ToUtf8Bytes(), min.ToUtf8Bytes(), max.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.ZRemRangeByLexAsync(string setId, string min, string max, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + + return SendExpectLongAsync(cancellationToken, + Commands.ZRemRangeByLex, setId.ToUtf8Bytes(), min.ToUtf8Bytes(), max.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.CalculateSha1Async(string luaBody, CancellationToken cancellationToken) + { + AssertNotNull(luaBody, nameof(luaBody)); + + byte[] buffer = Encoding.UTF8.GetBytes(luaBody); + return BitConverter.ToString(buffer.ToSha1Hash()).Replace("-", "").AsValueTask(); + } + + ValueTask IRedisNativeClientAsync.ScriptExistsAsync(byte[][] sha1Refs, CancellationToken cancellationToken) + { + var keysAndValues = MergeCommandWithArgs(Commands.Script, Commands.Exists, sha1Refs); + return SendExpectMultiDataAsync(cancellationToken, keysAndValues); + } + + ValueTask IRedisNativeClientAsync.ScriptFlushAsync(CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.Script, Commands.Flush); + + ValueTask IRedisNativeClientAsync.ScriptKillAsync(CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.Script, Commands.Kill); + + ValueTask IRedisNativeClientAsync.ScriptLoadAsync(string body, CancellationToken cancellationToken) + { + AssertNotNull(body, nameof(body)); + + var cmdArgs = MergeCommandWithArgs(Commands.Script, Commands.Load, body.ToUtf8Bytes()); + return SendExpectDataAsync(cancellationToken, cmdArgs); + } + + ValueTask IRedisNativeClientAsync.StrLenAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.StrLen, key.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.LLenAsync(string listId, CancellationToken cancellationToken) + { + AssertNotNull(listId, nameof(listId)); + return SendExpectLongAsync(cancellationToken, Commands.LLen, listId.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.SCardAsync(string setId, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + return SendExpectLongAsync(cancellationToken, Commands.SCard, setId.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.HLenAsync(string hashId, CancellationToken cancellationToken) + { + AssertNotNull(hashId, nameof(hashId)); + return SendExpectLongAsync(cancellationToken, Commands.HLen, hashId.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.EvalCommandAsync(string luaBody, int numberKeysInArgs, byte[][] keys, CancellationToken cancellationToken) + { + AssertNotNull(luaBody, nameof(luaBody)); + + var cmdArgs = MergeCommandWithArgs(Commands.Eval, luaBody.ToUtf8Bytes(), keys.PrependInt(numberKeysInArgs)); + return RawCommandAsync(cancellationToken, cmdArgs); + } + + ValueTask IRedisNativeClientAsync.EvalShaCommandAsync(string sha1, int numberKeysInArgs, byte[][] keys, CancellationToken cancellationToken) + { + AssertNotNull(sha1, nameof(sha1)); + + var cmdArgs = MergeCommandWithArgs(Commands.EvalSha, sha1.ToUtf8Bytes(), keys.PrependInt(numberKeysInArgs)); + return RawCommandAsync(cancellationToken, cmdArgs); + } + + ValueTask IRedisNativeClientAsync.EvalAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken) + { + AssertNotNull(luaBody, nameof(luaBody)); + + var cmdArgs = MergeCommandWithArgs(Commands.Eval, luaBody.ToUtf8Bytes(), keysAndArgs.PrependInt(numberOfKeys)); + return SendExpectMultiDataAsync(cancellationToken, cmdArgs); + } + + ValueTask IRedisNativeClientAsync.EvalShaAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken) + { + AssertNotNull(sha1, nameof(sha1)); + + var cmdArgs = MergeCommandWithArgs(Commands.EvalSha, sha1.ToUtf8Bytes(), keysAndArgs.PrependInt(numberOfKeys)); + return SendExpectMultiDataAsync(cancellationToken, cmdArgs); + } + + ValueTask IRedisNativeClientAsync.EvalIntAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken) + { + AssertNotNull(luaBody, nameof(luaBody)); + + var cmdArgs = MergeCommandWithArgs(Commands.Eval, luaBody.ToUtf8Bytes(), keysAndArgs.PrependInt(numberOfKeys)); + return SendExpectLongAsync(cancellationToken, cmdArgs); + } + + ValueTask IRedisNativeClientAsync.EvalShaIntAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken) + { + AssertNotNull(sha1, nameof(sha1)); + + var cmdArgs = MergeCommandWithArgs(Commands.EvalSha, sha1.ToUtf8Bytes(), keysAndArgs.PrependInt(numberOfKeys)); + return SendExpectLongAsync(cancellationToken, cmdArgs); + } + + ValueTask IRedisNativeClientAsync.EvalStrAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken) + { + AssertNotNull(luaBody, nameof(luaBody)); + + var cmdArgs = MergeCommandWithArgs(Commands.Eval, luaBody.ToUtf8Bytes(), keysAndArgs.PrependInt(numberOfKeys)); + return SendExpectDataAsync(cancellationToken, cmdArgs).FromUtf8BytesAsync(); + } + + ValueTask IRedisNativeClientAsync.EvalShaStrAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken) + { + AssertNotNull(sha1, nameof(sha1)); + + var cmdArgs = MergeCommandWithArgs(Commands.EvalSha, sha1.ToUtf8Bytes(), keysAndArgs.PrependInt(numberOfKeys)); + return SendExpectDataAsync(cancellationToken, cmdArgs).FromUtf8BytesAsync(); + } + + ValueTask IRedisNativeClientAsync.SMembersAsync(string setId, CancellationToken cancellationToken) + => SendExpectMultiDataAsync(cancellationToken, Commands.SMembers, setId.ToUtf8Bytes()); + + ValueTask IRedisNativeClientAsync.SAddAsync(string setId, byte[][] values, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + AssertNotNull(values, nameof(values)); + if (values.Length == 0) + throw new ArgumentException(nameof(values)); + + var cmdWithArgs = MergeCommandWithArgs(Commands.SAdd, setId.ToUtf8Bytes(), values); + return SendExpectLongAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.SRemAsync(string setId, byte[] value, CancellationToken cancellationToken) + { + AssertSetIdAndValue(setId, value); + return SendExpectLongAsync(cancellationToken, Commands.SRem, setId.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.IncrByAsync(string key, long count, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.IncrBy, key.ToUtf8Bytes(), count.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.IncrByFloatAsync(string key, double incrBy, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectDoubleAsync(cancellationToken, Commands.IncrByFloat, key.ToUtf8Bytes(), incrBy.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.IncrAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.Incr, key.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.DecrAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.Decr, key.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.DecrByAsync(string key, long count, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.DecrBy, key.ToUtf8Bytes(), count.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.ConfigGetAsync(string pattern, CancellationToken cancellationToken) + => SendExpectMultiDataAsync(cancellationToken, Commands.Config, Commands.Get, pattern.ToUtf8Bytes()); + + ValueTask IRedisNativeClientAsync.ConfigSetAsync(string item, byte[] value, CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.Config, Commands.Set, item.ToUtf8Bytes(), value); + + ValueTask IRedisNativeClientAsync.ConfigResetStatAsync(CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.Config, Commands.ResetStat); + + ValueTask IRedisNativeClientAsync.ConfigRewriteAsync(CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.Config, Commands.Rewrite); + + ValueTask IRedisNativeClientAsync.DebugSegfaultAsync(CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.Debug, Commands.Segfault); + + ValueTask IRedisNativeClientAsync.DumpAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectDataAsync(cancellationToken, Commands.Dump, key.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.RestoreAsync(string key, long expireMs, byte[] dumpValue, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectDataAsync(cancellationToken, Commands.Restore, key.ToUtf8Bytes(), expireMs.ToUtf8Bytes(), dumpValue); + } + + ValueTask IRedisNativeClientAsync.MigrateAsync(string host, int port, string key, int destinationDb, long timeoutMs, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectSuccessAsync(cancellationToken, Commands.Migrate, host.ToUtf8Bytes(), port.ToUtf8Bytes(), key.ToUtf8Bytes(), destinationDb.ToUtf8Bytes(), timeoutMs.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.MoveAsync(string key, int db, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.Move, key.ToUtf8Bytes(), db.ToUtf8Bytes()).IsSuccessAsync(); + } + + ValueTask IRedisNativeClientAsync.ObjectIdleTimeAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.Object, Commands.IdleTime, key.ToUtf8Bytes()); + } + + async ValueTask IRedisNativeClientAsync.RoleAsync(CancellationToken cancellationToken) + => (await SendExpectComplexResponseAsync(cancellationToken, Commands.Role).ConfigureAwait(false)).ToRedisText(); + + ValueTask IRedisNativeClientAsync.RawCommandAsync(object[] cmdWithArgs, CancellationToken cancellationToken) + => SendExpectComplexResponseAsync(cancellationToken, PrepareRawCommand(cmdWithArgs)); + + ValueTask IRedisNativeClientAsync.RawCommandAsync(byte[][] cmdWithBinaryArgs, CancellationToken cancellationToken) + => SendExpectComplexResponseAsync(cancellationToken, cmdWithBinaryArgs); + + ValueTask IRedisNativeClientAsync.ClientGetNameAsync(CancellationToken cancellationToken) + => SendExpectStringAsync(cancellationToken, Commands.Client, Commands.GetName); + + ValueTask IRedisNativeClientAsync.ClientSetNameAsync(string name, CancellationToken cancellationToken) + { + ClientValidateName(name); + return SendExpectSuccessAsync(cancellationToken, Commands.Client, Commands.SetName, name.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.ClientKillAsync(string clientAddr, CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.Client, Commands.Kill, clientAddr.ToUtf8Bytes()); + + ValueTask IRedisNativeClientAsync.ClientKillAsync(string addr, string id, string type, string skipMe, CancellationToken cancellationToken) + => SendExpectLongAsync(cancellationToken, ClientKillPerpareArgs(addr, id, type, skipMe)); + + ValueTask IRedisNativeClientAsync.ClientListAsync(CancellationToken cancellationToken) + => SendExpectDataAsync(cancellationToken, Commands.Client, Commands.List); + + ValueTask IRedisNativeClientAsync.ClientPauseAsync(int timeOutMs, CancellationToken cancellationToken) + => SendExpectSuccessAsync(cancellationToken, Commands.Client, Commands.Pause, timeOutMs.ToUtf8Bytes()); + + ValueTask IRedisNativeClientAsync.MSetNxAsync(byte[][] keys, byte[][] values, CancellationToken cancellationToken) + { + var keysAndValues = MergeCommandWithKeysAndValues(Commands.MSet, keys, values); + return SendExpectLongAsync(cancellationToken, keysAndValues).IsSuccessAsync(); + } + + ValueTask IRedisNativeClientAsync.MSetNxAsync(string[] keys, byte[][] values, CancellationToken cancellationToken) + => AsAsync().MSetNxAsync(keys.ToMultiByteArray(), values, cancellationToken); + + ValueTask IRedisNativeClientAsync.GetSetAsync(string key, byte[] value, CancellationToken cancellationToken) + { + GetSetAssertArgs(key, ref value); + return SendExpectDataAsync(cancellationToken, Commands.GetSet, key.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.MGetAsync(byte[][] keys, CancellationToken cancellationToken) + => SendExpectMultiDataAsync(cancellationToken, MGetPrepareArgs(keys)); + + ValueTask IRedisNativeClientAsync.SScanAsync(string setId, ulong cursor, int count, string match, CancellationToken cancellationToken) + { + if (match == null) + { + return SendExpectScanResultAsync(cancellationToken, Commands.SScan, + setId.ToUtf8Bytes(), cursor.ToUtf8Bytes(), + Commands.Count, count.ToUtf8Bytes()); + } + + return SendExpectScanResultAsync(cancellationToken, Commands.SScan, + setId.ToUtf8Bytes(), cursor.ToUtf8Bytes(), + Commands.Match, match.ToUtf8Bytes(), + Commands.Count, count.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.ZScanAsync(string setId, ulong cursor, int count, string match, CancellationToken cancellationToken) + { + if (match == null) + { + return SendExpectScanResultAsync(cancellationToken, Commands.ZScan, + setId.ToUtf8Bytes(), cursor.ToUtf8Bytes(), + Commands.Count, count.ToUtf8Bytes()); + } + + return SendExpectScanResultAsync(cancellationToken, Commands.ZScan, + setId.ToUtf8Bytes(), cursor.ToUtf8Bytes(), + Commands.Match, match.ToUtf8Bytes(), + Commands.Count, count.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.HScanAsync(string hashId, ulong cursor, int count, string match, CancellationToken cancellationToken) + { + if (match == null) + { + return SendExpectScanResultAsync(cancellationToken, Commands.HScan, + hashId.ToUtf8Bytes(), cursor.ToUtf8Bytes(), + Commands.Count, count.ToUtf8Bytes()); + } + + return SendExpectScanResultAsync(cancellationToken, Commands.HScan, + hashId.ToUtf8Bytes(), cursor.ToUtf8Bytes(), + Commands.Match, match.ToUtf8Bytes(), + Commands.Count, count.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.PfAddAsync(string key, byte[][] elements, CancellationToken cancellationToken) + { + var cmdWithArgs = MergeCommandWithArgs(Commands.PfAdd, key.ToUtf8Bytes(), elements); + return SendExpectLongAsync(cancellationToken, cmdWithArgs).IsSuccessAsync(); + } + + ValueTask IRedisNativeClientAsync.PfCountAsync(string key, CancellationToken cancellationToken) + { + var cmdWithArgs = MergeCommandWithArgs(Commands.PfCount, key.ToUtf8Bytes()); + return SendExpectLongAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.PfMergeAsync(string toKeyId, string[] fromKeys, CancellationToken cancellationToken) + { + var fromKeyBytes = fromKeys.Map(x => x.ToUtf8Bytes()).ToArray(); + var cmdWithArgs = MergeCommandWithArgs(Commands.PfMerge, toKeyId.ToUtf8Bytes(), fromKeyBytes); + return SendExpectSuccessAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.SortAsync(string listOrSetId, SortOptions sortOptions, CancellationToken cancellationToken) + => SendExpectMultiDataAsync(cancellationToken, SortPrepareArgs(listOrSetId, sortOptions)); + + ValueTask IRedisNativeClientAsync.LRangeAsync(string listId, int startingFrom, int endingAt, CancellationToken cancellationToken) + { + AssertNotNull(listId, nameof(listId)); + return SendExpectMultiDataAsync(cancellationToken, Commands.LRange, listId.ToUtf8Bytes(), startingFrom.ToUtf8Bytes(), endingAt.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.RPushXAsync(string listId, byte[] value, CancellationToken cancellationToken) + { + AssertListIdAndValue(listId, value); + return SendExpectLongAsync(cancellationToken, Commands.RPush, listId.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.LPushAsync(string listId, byte[] value, CancellationToken cancellationToken) + { + AssertListIdAndValue(listId, value); + return SendExpectLongAsync(cancellationToken, Commands.LPush, listId.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.LPushXAsync(string listId, byte[] value, CancellationToken cancellationToken) + { + AssertListIdAndValue(listId, value); + return SendExpectLongAsync(cancellationToken, Commands.LPushX, listId.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.LTrimAsync(string listId, int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken) + { + AssertNotNull(listId, nameof(listId)); + return SendExpectSuccessAsync(cancellationToken, Commands.LTrim, listId.ToUtf8Bytes(), keepStartingFrom.ToUtf8Bytes(), keepEndingAt.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.LRemAsync(string listId, int removeNoOfMatches, byte[] value, CancellationToken cancellationToken) + { + AssertNotNull(listId, nameof(listId)); + return SendExpectLongAsync(cancellationToken, Commands.LRem, listId.ToUtf8Bytes(), removeNoOfMatches.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.LIndexAsync(string listId, int listIndex, CancellationToken cancellationToken) + { + AssertNotNull(listId, nameof(listId)); + return SendExpectDataAsync(cancellationToken, Commands.LIndex, listId.ToUtf8Bytes(), listIndex.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.LInsertAsync(string listId, bool insertBefore, byte[] pivot, byte[] value, CancellationToken cancellationToken) + { + AssertNotNull(listId, nameof(listId)); + var position = insertBefore ? Commands.Before : Commands.After; + return SendExpectSuccessAsync(cancellationToken, Commands.LInsert, listId.ToUtf8Bytes(), position, pivot, value); + } + + ValueTask IRedisNativeClientAsync.LSetAsync(string listId, int listIndex, byte[] value, CancellationToken cancellationToken) + { + AssertNotNull(listId, nameof(listId)); + return SendExpectSuccessAsync(cancellationToken, Commands.LSet, listId.ToUtf8Bytes(), listIndex.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.LPopAsync(string listId, CancellationToken cancellationToken) + { + AssertNotNull(listId, nameof(listId)); + return SendExpectDataAsync(cancellationToken, Commands.LPop, listId.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.RPopAsync(string listId, CancellationToken cancellationToken) + { + AssertNotNull(listId, nameof(listId)); + return SendExpectDataAsync(cancellationToken, Commands.RPop, listId.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.BLPopAsync(string listId, int timeOutSecs, CancellationToken cancellationToken) + { + AssertNotNull(listId, nameof(listId)); + return SendExpectMultiDataAsync(cancellationToken, Commands.BLPop, listId.ToUtf8Bytes(), timeOutSecs.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.BLPopAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken) + { + AssertNotNull(listIds, nameof(listIds)); + var args = new List { Commands.BLPop }; + args.AddRange(listIds.Select(listId => listId.ToUtf8Bytes())); + args.Add(timeOutSecs.ToUtf8Bytes()); + return SendExpectMultiDataAsync(cancellationToken, args.ToArray()); + } + + async ValueTask IRedisNativeClientAsync.BLPopValueAsync(string listId, int timeOutSecs, CancellationToken cancellationToken) + { + var blockingResponse = await AsAsync().BLPopAsync(new[] { listId }, timeOutSecs, cancellationToken).ConfigureAwait(false); + return blockingResponse.Length == 0 + ? null + : blockingResponse[1]; + } + + async ValueTask IRedisNativeClientAsync.BLPopValueAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken) + { + var blockingResponse = await AsAsync().BLPopAsync(listIds, timeOutSecs, cancellationToken).ConfigureAwait(false); + return blockingResponse.Length == 0 + ? null + : blockingResponse; + } + + ValueTask IRedisNativeClientAsync.BRPopAsync(string listId, int timeOutSecs, CancellationToken cancellationToken) + { + AssertNotNull(listId, nameof(listId)); + return SendExpectMultiDataAsync(cancellationToken, Commands.BRPop, listId.ToUtf8Bytes(), timeOutSecs.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.BRPopAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken) + { + AssertNotNull(listIds, nameof(listIds)); + var args = new List { Commands.BRPop }; + args.AddRange(listIds.Select(listId => listId.ToUtf8Bytes())); + args.Add(timeOutSecs.ToUtf8Bytes()); + return SendExpectMultiDataAsync(cancellationToken, args.ToArray()); + } + + ValueTask IRedisNativeClientAsync.RPopLPushAsync(string fromListId, string toListId, CancellationToken cancellationToken) + { + AssertNotNull(fromListId, nameof(fromListId)); + AssertNotNull(toListId, nameof(toListId)); + return SendExpectDataAsync(cancellationToken, Commands.RPopLPush, fromListId.ToUtf8Bytes(), toListId.ToUtf8Bytes()); + } + + async ValueTask IRedisNativeClientAsync.BRPopValueAsync(string listId, int timeOutSecs, CancellationToken cancellationToken) + { + var blockingResponse = await AsAsync().BRPopAsync(new[] { listId }, timeOutSecs, cancellationToken).ConfigureAwait(false); + return blockingResponse.Length == 0 + ? null + : blockingResponse[1]; + } + + async ValueTask IRedisNativeClientAsync.BRPopValueAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken) + { + var blockingResponse = await AsAsync().BRPopAsync(listIds, timeOutSecs, cancellationToken).ConfigureAwait(false); + return blockingResponse.Length == 0 + ? null + : blockingResponse; + } + + async ValueTask IRedisNativeClientAsync.BRPopLPushAsync(string fromListId, string toListId, int timeOutSecs, CancellationToken cancellationToken) + { + AssertNotNull(fromListId, nameof(fromListId)); + AssertNotNull(toListId, nameof(toListId)); + byte[][] result = await SendExpectMultiDataAsync(cancellationToken, Commands.BRPopLPush, fromListId.ToUtf8Bytes(), toListId.ToUtf8Bytes(), timeOutSecs.ToUtf8Bytes()); + return result.Length == 0 ? null : result[1]; + } + + ValueTask IRedisNativeClientAsync.SMoveAsync(string fromSetId, string toSetId, byte[] value, CancellationToken cancellationToken) + { + AssertNotNull(fromSetId, nameof(fromSetId)); + AssertNotNull(toSetId, nameof(toSetId)); + return SendExpectSuccessAsync(cancellationToken, Commands.SMove, fromSetId.ToUtf8Bytes(), toSetId.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.SIsMemberAsync(string setId, byte[] value, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + return SendExpectLongAsync(cancellationToken, Commands.SIsMember, setId.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.SInterAsync(string[] setIds, CancellationToken cancellationToken) + { + var cmdWithArgs = MergeCommandWithArgs(Commands.SInter, setIds); + return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.SInterStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + { + var setIdsList = new List(setIds); + setIdsList.Insert(0, intoSetId); + + var cmdWithArgs = MergeCommandWithArgs(Commands.SInterStore, setIdsList.ToArray()); + return SendExpectSuccessAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.SUnionAsync(string[] setIds, CancellationToken cancellationToken) + { + var cmdWithArgs = MergeCommandWithArgs(Commands.SUnion, setIds); + return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.SUnionStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + { + var setIdsList = new List(setIds); + setIdsList.Insert(0, intoSetId); + + var cmdWithArgs = MergeCommandWithArgs(Commands.SUnionStore, setIdsList.ToArray()); + return SendExpectSuccessAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.SDiffAsync(string fromSetId, string[] withSetIds, CancellationToken cancellationToken) + { + var setIdsList = new List(withSetIds); + setIdsList.Insert(0, fromSetId); + + var cmdWithArgs = MergeCommandWithArgs(Commands.SDiff, setIdsList.ToArray()); + return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.SDiffStoreAsync(string intoSetId, string fromSetId, string[] withSetIds, CancellationToken cancellationToken) + { + var setIdsList = new List(withSetIds); + setIdsList.Insert(0, fromSetId); + setIdsList.Insert(0, intoSetId); + + var cmdWithArgs = MergeCommandWithArgs(Commands.SDiffStore, setIdsList.ToArray()); + return SendExpectSuccessAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.SRandMemberAsync(string setId, CancellationToken cancellationToken) + => SendExpectDataAsync(cancellationToken, Commands.SRandMember, setId.ToUtf8Bytes()); + + ValueTask IRedisNativeClientAsync.ZRemAsync(string setId, byte[] value, CancellationToken cancellationToken) + { + AssertSetIdAndValue(setId, value); + return SendExpectLongAsync(cancellationToken, Commands.ZRem, setId.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.ZRemAsync(string setId, byte[][] values, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + AssertNotNull(values, nameof(values)); + if (values.Length == 0) + throw new ArgumentException("values"); + + var cmdWithArgs = MergeCommandWithArgs(Commands.ZRem, setId.ToUtf8Bytes(), values); + return SendExpectLongAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.ZIncrByAsync(string setId, double incrBy, byte[] value, CancellationToken cancellationToken) + { + AssertSetIdAndValue(setId, value); + return SendExpectDoubleAsync(cancellationToken, Commands.ZIncrBy, setId.ToUtf8Bytes(), incrBy.ToFastUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.ZIncrByAsync(string setId, long incrBy, byte[] value, CancellationToken cancellationToken) + { + AssertSetIdAndValue(setId, value); + return SendExpectDoubleAsync(cancellationToken, Commands.ZIncrBy, setId.ToUtf8Bytes(), incrBy.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.ZRankAsync(string setId, byte[] value, CancellationToken cancellationToken) + { + AssertSetIdAndValue(setId, value); + return SendExpectLongAsync(cancellationToken, Commands.ZRank, setId.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.ZRevRankAsync(string setId, byte[] value, CancellationToken cancellationToken) + { + AssertSetIdAndValue(setId, value); + return SendExpectLongAsync(cancellationToken, Commands.ZRevRank, setId.ToUtf8Bytes(), value); + } + + ValueTask IRedisNativeClientAsync.ZRangeAsync(string setId, int min, int max, CancellationToken cancellationToken) + => SendExpectMultiDataAsync(cancellationToken, Commands.ZRange, setId.ToUtf8Bytes(), min.ToUtf8Bytes(), max.ToUtf8Bytes()); + + private ValueTask GetRangeAsync(byte[] commandBytes, string setId, int min, int max, bool withScores, CancellationToken cancellationToken) + { + var args = GetRangeArgs(commandBytes, setId, min, max, withScores); + return SendExpectMultiDataAsync(cancellationToken, args); + } + + ValueTask IRedisNativeClientAsync.ZRangeWithScoresAsync(string setId, int min, int max, CancellationToken cancellationToken) + => GetRangeAsync(Commands.ZRange, setId, min, max, true, cancellationToken); + + ValueTask IRedisNativeClientAsync.ZRevRangeAsync(string setId, int min, int max, CancellationToken cancellationToken) + => GetRangeAsync(Commands.ZRevRange, setId, min, max, false, cancellationToken); + + ValueTask IRedisNativeClientAsync.ZRevRangeWithScoresAsync(string setId, int min, int max, CancellationToken cancellationToken) + => GetRangeAsync(Commands.ZRevRange, setId, min, max, true, cancellationToken); + + private ValueTask GetRangeByScoreAsync(byte[] commandBytes, + string setId, double min, double max, int? skip, int? take, bool withScores, CancellationToken cancellationToken) + { + var args = GetRangeByScoreArgs(commandBytes, setId, min, max, skip, take, withScores); + return SendExpectMultiDataAsync(cancellationToken, args); + } + + ValueTask IRedisNativeClientAsync.ZRangeByScoreAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken) + => GetRangeByScoreAsync(Commands.ZRangeByScore, setId, min, max, skip, take, false, cancellationToken); + + ValueTask IRedisNativeClientAsync.ZRangeByScoreAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken) + => GetRangeByScoreAsync(Commands.ZRangeByScore, setId, min, max, skip, take, false, cancellationToken); + + ValueTask IRedisNativeClientAsync.ZRangeByScoreWithScoresAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken) + => GetRangeByScoreAsync(Commands.ZRangeByScore, setId, min, max, skip, take, true, cancellationToken); + + ValueTask IRedisNativeClientAsync.ZRangeByScoreWithScoresAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken) + => GetRangeByScoreAsync(Commands.ZRangeByScore, setId, min, max, skip, take, true, cancellationToken); + + ValueTask IRedisNativeClientAsync.ZRevRangeByScoreAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken) + { + //Note: http://redis.io/commands/zrevrangebyscore has max, min in the wrong other + return GetRangeByScoreAsync(Commands.ZRevRangeByScore, setId, max, min, skip, take, false, cancellationToken); + } + + ValueTask IRedisNativeClientAsync.ZRevRangeByScoreAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken) + { + //Note: http://redis.io/commands/zrevrangebyscore has max, min in the wrong other + return GetRangeByScoreAsync(Commands.ZRevRangeByScore, setId, max, min, skip, take, false, cancellationToken); + } + + ValueTask IRedisNativeClientAsync.ZRevRangeByScoreWithScoresAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken) + { + //Note: http://redis.io/commands/zrevrangebyscore has max, min in the wrong other + return GetRangeByScoreAsync(Commands.ZRevRangeByScore, setId, max, min, skip, take, true, cancellationToken); + } + + ValueTask IRedisNativeClientAsync.ZRevRangeByScoreWithScoresAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken) + { + //Note: http://redis.io/commands/zrevrangebyscore has max, min in the wrong other + return GetRangeByScoreAsync(Commands.ZRevRangeByScore, setId, max, min, skip, take, true, cancellationToken); + } + + ValueTask IRedisNativeClientAsync.ZRemRangeByRankAsync(string setId, int min, int max, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + return SendExpectLongAsync(cancellationToken, Commands.ZRemRangeByRank, setId.ToUtf8Bytes(), + min.ToUtf8Bytes(), max.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.ZRemRangeByScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + return SendExpectLongAsync(cancellationToken, Commands.ZRemRangeByScore, setId.ToUtf8Bytes(), + fromScore.ToFastUtf8Bytes(), toScore.ToFastUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.ZRemRangeByScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) + { + AssertNotNull(setId, nameof(setId)); + return SendExpectLongAsync(cancellationToken, Commands.ZRemRangeByScore, setId.ToUtf8Bytes(), + fromScore.ToUtf8Bytes(), toScore.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.ZUnionStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + { + var setIdsList = new List(setIds); + setIdsList.Insert(0, setIds.Length.ToString()); + setIdsList.Insert(0, intoSetId); + + var cmdWithArgs = MergeCommandWithArgs(Commands.ZUnionStore, setIdsList.ToArray()); + return SendExpectLongAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.ZInterStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + { + var setIdsList = new List(setIds); + setIdsList.Insert(0, setIds.Length.ToString()); + setIdsList.Insert(0, intoSetId); + + var cmdWithArgs = MergeCommandWithArgs(Commands.ZInterStore, setIdsList.ToArray()); + return SendExpectLongAsync(cancellationToken, cmdWithArgs); + } + + internal ValueTask ZInterStoreAsync(string intoSetId, string[] setIds, string[] args, CancellationToken cancellationToken) + { + var totalArgs = new List(setIds); + totalArgs.Insert(0, setIds.Length.ToString()); + totalArgs.Insert(0, intoSetId); + totalArgs.AddRange(args); + + var cmdWithArgs = MergeCommandWithArgs(Commands.ZInterStore, totalArgs.ToArray()); + return SendExpectLongAsync(cancellationToken, cmdWithArgs); + } + + internal ValueTask ZUnionStoreAsync(string intoSetId, string[] setIds, string[] args, CancellationToken cancellationToken) + { + var totalArgs = new List(setIds); + totalArgs.Insert(0, setIds.Length.ToString()); + totalArgs.Insert(0, intoSetId); + totalArgs.AddRange(args); + + var cmdWithArgs = MergeCommandWithArgs(Commands.ZUnionStore, totalArgs.ToArray()); + return SendExpectLongAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.HMSetAsync(string hashId, byte[][] keys, byte[][] values, CancellationToken cancellationToken) + { + AssertNotNull(hashId, nameof(hashId)); + var cmdArgs = MergeCommandWithKeysAndValues(Commands.HMSet, hashId.ToUtf8Bytes(), keys, values); + return SendExpectSuccessAsync(cancellationToken, cmdArgs); + } + + ValueTask IRedisNativeClientAsync.HSetNXAsync(string hashId, byte[] key, byte[] value, CancellationToken cancellationToken) + { + AssertHashIdAndKey(hashId, key); + return SendExpectLongAsync(cancellationToken, Commands.HSetNx, hashId.ToUtf8Bytes(), key, value); + } + + ValueTask IRedisNativeClientAsync.HIncrbyAsync(string hashId, byte[] key, int incrementBy, CancellationToken cancellationToken) + { + AssertHashIdAndKey(hashId, key); + return SendExpectLongAsync(cancellationToken, Commands.HIncrBy, hashId.ToUtf8Bytes(), key, incrementBy.ToString().ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.HIncrbyFloatAsync(string hashId, byte[] key, double incrementBy, CancellationToken cancellationToken) + { + AssertHashIdAndKey(hashId, key); + return SendExpectDoubleAsync(cancellationToken, Commands.HIncrByFloat, hashId.ToUtf8Bytes(), key, incrementBy.ToString(CultureInfo.InvariantCulture).ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.HGetAsync(string hashId, byte[] key, CancellationToken cancellationToken) + => HGetAsync(hashId.ToUtf8Bytes(), key, cancellationToken); + + private ValueTask HGetAsync(byte[] hashId, byte[] key, CancellationToken cancellationToken) + { + AssertHashIdAndKey(hashId, key); + return SendExpectDataAsync(cancellationToken, Commands.HGet, hashId, key); + } + + ValueTask IRedisNativeClientAsync.HMGetAsync(string hashId, byte[][] keys, CancellationToken cancellationToken) + { + AssertNotNull(hashId, nameof(hashId)); + if (keys.Length == 0) + throw new ArgumentNullException(nameof(keys)); + + var cmdArgs = MergeCommandWithArgs(Commands.HMGet, hashId.ToUtf8Bytes(), keys); + return SendExpectMultiDataAsync(cancellationToken, cmdArgs); + } + + ValueTask IRedisNativeClientAsync.HDelAsync(string hashId, byte[] key, CancellationToken cancellationToken) + => HDelAsync(hashId.ToUtf8Bytes(), key, cancellationToken); + + private ValueTask HDelAsync(byte[] hashId, byte[] key, CancellationToken cancellationToken) + { + AssertHashIdAndKey(hashId, key); + return SendExpectLongAsync(cancellationToken, Commands.HDel, hashId, key); + } + + ValueTask IRedisNativeClientAsync.HExistsAsync(string hashId, byte[] key, CancellationToken cancellationToken) + { + AssertHashIdAndKey(hashId, key); + return SendExpectLongAsync(cancellationToken, Commands.HExists, hashId.ToUtf8Bytes(), key); + } + + ValueTask IRedisNativeClientAsync.HKeysAsync(string hashId, CancellationToken cancellationToken) + { + AssertNotNull(hashId, nameof(hashId)); + return SendExpectMultiDataAsync(cancellationToken, Commands.HKeys, hashId.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.HValsAsync(string hashId, CancellationToken cancellationToken) + { + AssertNotNull(hashId, nameof(hashId)); + return SendExpectMultiDataAsync(cancellationToken, Commands.HVals, hashId.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.HGetAllAsync(string hashId, CancellationToken cancellationToken) + { + AssertNotNull(hashId, nameof(hashId)); + return SendExpectMultiDataAsync(cancellationToken, Commands.HGetAll, hashId.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.GeoAddAsync(string key, double longitude, double latitude, string member, CancellationToken cancellationToken) + { + AssertNotNull(key, nameof(key)); + AssertNotNull(member, nameof(member)); + return SendExpectLongAsync(cancellationToken, Commands.GeoAdd, key.ToUtf8Bytes(), longitude.ToUtf8Bytes(), latitude.ToUtf8Bytes(), member.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.GeoAddAsync(string key, RedisGeo[] geoPoints, CancellationToken cancellationToken) + { + var cmdWithArgs = GeoAddPrepareArgs(key, geoPoints); + return SendExpectLongAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.GeoDistAsync(string key, string fromMember, string toMember, string unit, CancellationToken cancellationToken) + { + AssertNotNull(key, nameof(key)); + + return unit == null + ? SendExpectDoubleAsync(cancellationToken, Commands.GeoDist, key.ToUtf8Bytes(), fromMember.ToUtf8Bytes(), toMember.ToUtf8Bytes()) + : SendExpectDoubleAsync(cancellationToken, Commands.GeoDist, key.ToUtf8Bytes(), fromMember.ToUtf8Bytes(), toMember.ToUtf8Bytes(), unit.ToUtf8Bytes()); + } + + async ValueTask IRedisNativeClientAsync.GeoHashAsync(string key, string[] members, CancellationToken cancellationToken) + { + AssertNotNull(key, nameof(key)); + + var cmdWithArgs = MergeCommandWithArgs(Commands.GeoHash, key.ToUtf8Bytes(), members.Map(x => x.ToUtf8Bytes()).ToArray()); + var result = await SendExpectMultiDataAsync(cancellationToken, cmdWithArgs).ConfigureAwait(false); + return result.ToStringArray(); + } + + async ValueTask> IRedisNativeClientAsync.GeoPosAsync(string key, string[] members, CancellationToken cancellationToken) + { + AssertNotNull(key, nameof(key)); + + var cmdWithArgs = MergeCommandWithArgs(Commands.GeoPos, key.ToUtf8Bytes(), members.Map(x => x.ToUtf8Bytes()).ToArray()); + var data = await SendExpectComplexResponseAsync(cancellationToken, cmdWithArgs).ConfigureAwait(false); + return GeoPosParseResult(members, data); + } + + async ValueTask> IRedisNativeClientAsync.GeoRadiusAsync(string key, double longitude, double latitude, double radius, string unit, bool withCoords, bool withDist, bool withHash, int? count, bool? asc, CancellationToken cancellationToken) + { + var cmdWithArgs = GeoRadiusPrepareArgs(key, longitude, latitude, radius, unit, + withCoords, withDist, withHash, count, asc); + + var to = new List(); + + if (!(withCoords || withDist || withHash)) + { + var members = (await SendExpectMultiDataAsync(cancellationToken, cmdWithArgs).ConfigureAwait(false)).ToStringArray(); + foreach (var member in members) + { + to.Add(new RedisGeoResult { Member = member }); + } + } + else + { + var data = await SendExpectComplexResponseAsync(cancellationToken, cmdWithArgs).ConfigureAwait(false); + GetRadiusParseResult(unit, withCoords, withDist, withHash, to, data); + } + + return to; + } + + async ValueTask> IRedisNativeClientAsync.GeoRadiusByMemberAsync(string key, string member, double radius, string unit, bool withCoords, bool withDist, bool withHash, int? count, bool? asc, CancellationToken cancellationToken) + { + var cmdWithArgs = GeoRadiusByMemberPrepareArgs(key, member, radius, unit, withCoords, withDist, withHash, count, asc); + + var to = new List(); + + if (!(withCoords || withDist || withHash)) + { + var members = (await SendExpectMultiDataAsync(cancellationToken, cmdWithArgs).ConfigureAwait(false)).ToStringArray(); + foreach (var x in members) + { + to.Add(new RedisGeoResult { Member = x }); + } + } + else + { + var data = await SendExpectComplexResponseAsync(cancellationToken, cmdWithArgs).ConfigureAwait(false); + GeoRadiusByMemberParseResult(unit, withCoords, withDist, withHash, to, data); + } + + return to; + } + + ValueTask IRedisNativeClientAsync.PublishAsync(string toChannel, byte[] message, CancellationToken cancellationToken) + => SendExpectLongAsync(cancellationToken, Commands.Publish, toChannel.ToUtf8Bytes(), message); + + ValueTask IRedisNativeClientAsync.SubscribeAsync(string[] toChannels, CancellationToken cancellationToken) + { + if (toChannels.Length == 0) + throw new ArgumentNullException(nameof(toChannels)); + + var cmdWithArgs = MergeCommandWithArgs(Commands.Subscribe, toChannels); + return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.UnSubscribeAsync(string[] fromChannels, CancellationToken cancellationToken) + { + var cmdWithArgs = MergeCommandWithArgs(Commands.UnSubscribe, fromChannels); + return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.PSubscribeAsync(string[] toChannelsMatchingPatterns, CancellationToken cancellationToken) + { + if (toChannelsMatchingPatterns.Length == 0) + throw new ArgumentNullException(nameof(toChannelsMatchingPatterns)); + + var cmdWithArgs = MergeCommandWithArgs(Commands.PSubscribe, toChannelsMatchingPatterns); + return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.PUnSubscribeAsync(string[] fromChannelsMatchingPatterns, CancellationToken cancellationToken) + { + var cmdWithArgs = MergeCommandWithArgs(Commands.PUnSubscribe, fromChannelsMatchingPatterns); + return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + } + + ValueTask IRedisNativeClientAsync.ReceiveMessagesAsync(CancellationToken cancellationToken) + => ReadMultiDataAsync(cancellationToken); + + ValueTask IRedisNativeClientAsync.CreateSubscriptionAsync(CancellationToken cancellationToken) + => new RedisSubscription(this).AsValueTask(); + + ValueTask IRedisNativeClientAsync.BitCountAsync(string key, CancellationToken cancellationToken) + { + AssertNotNull(key); + return SendExpectLongAsync(cancellationToken, Commands.BitCount, key.ToUtf8Bytes()); + } + + ValueTask IRedisNativeClientAsync.DelAsync(params string[] keys) + => AsAsync().DelAsync(keys, default); + + ValueTask IRedisNativeClientAsync.SInterStoreAsync(string intoSetId, params string[] setIds) + => AsAsync().SInterStoreAsync(intoSetId, setIds, default); + + ValueTask IRedisNativeClientAsync.SUnionAsync(params string[] setIds) + => AsAsync().SUnionAsync(setIds, default); + + ValueTask IRedisNativeClientAsync.WatchAsync(params string[] keys) + => AsAsync().WatchAsync(keys, default); + + ValueTask IRedisNativeClientAsync.SubscribeAsync(params string[] toChannels) + => AsAsync().SubscribeAsync(toChannels, default); + + ValueTask IRedisNativeClientAsync.UnSubscribeAsync(params string[] toChannels) + => AsAsync().UnSubscribeAsync(toChannels, default); + + ValueTask IRedisNativeClientAsync.PSubscribeAsync(params string[] toChannelsMatchingPatterns) + => AsAsync().PSubscribeAsync(toChannelsMatchingPatterns, default); + + ValueTask IRedisNativeClientAsync.PUnSubscribeAsync(params string[] toChannelsMatchingPatterns) + => AsAsync().PUnSubscribeAsync(toChannelsMatchingPatterns, default); + + ValueTask IRedisNativeClientAsync.SInterAsync(params string[] setIds) + => AsAsync().SInterAsync(setIds, default); + + ValueTask IRedisNativeClientAsync.SDiffAsync(string fromSetId, params string[] withSetIds) + => AsAsync().SDiffAsync(fromSetId, withSetIds, default); + + ValueTask IRedisNativeClientAsync.SDiffStoreAsync(string intoSetId, string fromSetId, params string[] withSetIds) + => AsAsync().SDiffStoreAsync(intoSetId, fromSetId, withSetIds, default); + + ValueTask IRedisNativeClientAsync.ZUnionStoreAsync(string intoSetId, params string[] setIds) + => AsAsync().ZUnionStoreAsync(intoSetId, setIds, default); + + ValueTask IRedisNativeClientAsync.ZInterStoreAsync(string intoSetId, params string[] setIds) + => AsAsync().ZInterStoreAsync(intoSetId, setIds, default); + + ValueTask IRedisNativeClientAsync.EvalCommandAsync(string luaBody, int numberKeysInArgs, params byte[][] keys) + => AsAsync().EvalCommandAsync(luaBody, numberKeysInArgs, keys, default); + + ValueTask IRedisNativeClientAsync.EvalShaCommandAsync(string sha1, int numberKeysInArgs, params byte[][] keys) + => AsAsync().EvalShaCommandAsync(sha1, numberKeysInArgs, keys, default); + + ValueTask IRedisNativeClientAsync.EvalAsync(string luaBody, int numberOfKeys, params byte[][] keysAndArgs) + => AsAsync().EvalAsync(luaBody, numberOfKeys, keysAndArgs, default); + + ValueTask IRedisNativeClientAsync.EvalShaAsync(string sha1, int numberOfKeys, params byte[][] keysAndArgs) + => AsAsync().EvalShaAsync(sha1, numberOfKeys, keysAndArgs, default); + + ValueTask IRedisNativeClientAsync.EvalIntAsync(string luaBody, int numberOfKeys, params byte[][] keysAndArgs) + => AsAsync().EvalIntAsync(luaBody, numberOfKeys, keysAndArgs, default); + + ValueTask IRedisNativeClientAsync.EvalShaIntAsync(string sha1, int numberOfKeys, params byte[][] keysAndArgs) + => AsAsync().EvalShaIntAsync(sha1, numberOfKeys, keysAndArgs, default); + + ValueTask IRedisNativeClientAsync.EvalStrAsync(string luaBody, int numberOfKeys, params byte[][] keysAndArgs) + => AsAsync().EvalStrAsync(luaBody, numberOfKeys, keysAndArgs, default); + + ValueTask IRedisNativeClientAsync.EvalShaStrAsync(string sha1, int numberOfKeys, params byte[][] keysAndArgs) + => AsAsync().EvalShaStrAsync(sha1, numberOfKeys, keysAndArgs, default); + + ValueTask IRedisNativeClientAsync.RawCommandAsync(params object[] cmdWithArgs) + => AsAsync().RawCommandAsync(cmdWithArgs, default); + + ValueTask IRedisNativeClientAsync.RawCommandAsync(params byte[][] cmdWithBinaryArgs) + => AsAsync().RawCommandAsync(cmdWithBinaryArgs, default); + + ValueTask IRedisNativeClientAsync.MGetAsync(params string[] keys) + => AsAsync().MGetAsync(keys, default); + + ValueTask IRedisNativeClientAsync.PfAddAsync(string key, params byte[][] elements) + => AsAsync().PfAddAsync(key, elements, default); + + ValueTask IRedisNativeClientAsync.HMGetAsync(string hashId, params byte[][] keysAndArgs) + => AsAsync().HMGetAsync(hashId, keysAndArgs, default); + + ValueTask IRedisNativeClientAsync.MGetAsync(params byte[][] keysAndArgs) + => AsAsync().MGetAsync(keysAndArgs, default); + + ValueTask IRedisNativeClientAsync.SUnionStoreAsync(string intoSetId, params string[] setIds) + => AsAsync().SUnionStoreAsync(intoSetId, setIds, default); + + ValueTask IRedisNativeClientAsync.ScriptExistsAsync(params byte[][] sha1Refs) + => AsAsync().ScriptExistsAsync(sha1Refs, default); + + ValueTask IRedisNativeClientAsync.PfMergeAsync(string toKeyId, params string[] fromKeys) + => AsAsync().PfMergeAsync(toKeyId, fromKeys, default); + + ValueTask IRedisNativeClientAsync.GeoAddAsync(string key, params RedisGeo[] geoPoints) + => AsAsync().GeoAddAsync(key, geoPoints, default); + + ValueTask IRedisNativeClientAsync.GeoHashAsync(string key, params string[] members) + => AsAsync().GeoHashAsync(key, members, default); + + ValueTask> IRedisNativeClientAsync.GeoPosAsync(string key, params string[] members) + => AsAsync().GeoPosAsync(key, members, default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisNativeClient.cs b/src/ServiceStack.Redis/RedisNativeClient.cs index 33265854..d4fce91f 100644 --- a/src/ServiceStack.Redis/RedisNativeClient.cs +++ b/src/ServiceStack.Redis/RedisNativeClient.cs @@ -64,9 +64,12 @@ public DateTime? DeactivatedAt public bool HadExceptions => deactivatedAtTicks > 0; protected Socket socket; + [Obsolete("The direct stream is no longer directly available", true)] // API BREAKING CHANGE since exposed protected BufferedStream Bstream; protected SslStream sslStream; + private BufferedReader bufferedReader; + private IRedisTransactionBase transaction; private IRedisPipelineShared pipeline; @@ -207,6 +210,7 @@ public long Db } } + public void ChangeDb(long db) { this.db = db; @@ -226,22 +230,23 @@ public DateTime LastSave public Dictionary Info { - get - { - var lines = SendExpectString(Commands.Info); - var info = new Dictionary(); + get => ParseInfoResult(SendExpectString(Commands.Info)); + } - foreach (var line in lines - .Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)) - { - var p = line.IndexOf(':'); - if (p == -1) continue; + private static Dictionary ParseInfoResult(string lines) + { + var info = new Dictionary(); - info[line.Substring(0, p)] = line.Substring(p + 1); - } + foreach (var line in lines + .Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)) + { + var p = line.IndexOf(':'); + if (p == -1) continue; - return info; + info[line.Substring(0, p)] = line.Substring(p + 1); } + + return info; } public string ServerVersion @@ -254,6 +259,11 @@ public string ServerVersion } public RedisData RawCommand(params object[] cmdWithArgs) + { + return SendExpectComplexResponse(PrepareRawCommand(cmdWithArgs)); + } + + private static byte[][] PrepareRawCommand(object[] cmdWithArgs) { var byteArgs = new List(); @@ -280,9 +290,7 @@ public RedisData RawCommand(params object[] cmdWithArgs) byteArgs.Add(str.ToUtf8Bytes()); } } - - var data = SendExpectComplexResponse(byteArgs.ToArray()); - return data; + return byteArgs.ToArray(); } public RedisData RawCommand(params byte[][] cmdWithBinaryArgs) @@ -394,8 +402,10 @@ public string Type(string key) } public RedisKeyType GetEntryType(string key) + => ParseEntryType(Type(key)); + + private protected RedisKeyType ParseEntryType(string type) { - var type = Type(key); switch (type) { case "none": @@ -573,6 +583,12 @@ public byte[] GetBytes(string key) } public byte[] GetSet(string key, byte[] value) + { + GetSetAssertArgs(key, ref value); + return SendExpectData(Commands.GetSet, key.ToUtf8Bytes(), value); + } + + private static void GetSetAssertArgs(string key, ref byte[] value) { if (key == null) throw new ArgumentNullException("key"); @@ -581,8 +597,6 @@ public byte[] GetSet(string key, byte[] value) if (value.Length > OneGb) throw new ArgumentException("value exceeds 1G", "value"); - - return SendExpectData(Commands.GetSet, key.ToUtf8Bytes(), value); } public long Exists(string key) @@ -720,21 +734,21 @@ public string RandomKey() public void Rename(string oldKeyname, string newKeyname) { - if (oldKeyname == null) - throw new ArgumentNullException("oldKeyname"); - if (newKeyname == null) - throw new ArgumentNullException("newKeyname"); - + CheckRenameKeys(oldKeyname, newKeyname); SendExpectSuccess(Commands.Rename, oldKeyname.ToUtf8Bytes(), newKeyname.ToUtf8Bytes()); } - public bool RenameNx(string oldKeyname, string newKeyname) + private protected static void CheckRenameKeys(string oldKeyname, string newKeyname) { if (oldKeyname == null) throw new ArgumentNullException("oldKeyname"); if (newKeyname == null) throw new ArgumentNullException("newKeyname"); + } + public bool RenameNx(string oldKeyname, string newKeyname) + { + CheckRenameKeys(oldKeyname, newKeyname); return SendExpectLong(Commands.RenameNx, oldKeyname.ToUtf8Bytes(), newKeyname.ToUtf8Bytes()) == Success; } @@ -852,14 +866,18 @@ public string ClientGetName() } public void ClientSetName(string name) + { + ClientValidateName(name); + SendExpectSuccess(Commands.Client, Commands.SetName, name.ToUtf8Bytes()); + } + + private static void ClientValidateName(string name) { if (string.IsNullOrEmpty(name)) throw new ArgumentException("Name cannot be null or empty"); if (name.Contains(" ")) throw new ArgumentException("Name cannot contain spaces"); - - SendExpectSuccess(Commands.Client, Commands.SetName, name.ToUtf8Bytes()); } public void ClientPause(int timeOutMs) @@ -878,6 +896,11 @@ public void ClientKill(string clientAddr) } public long ClientKill(string addr = null, string id = null, string type = null, string skipMe = null) + { + return SendExpectLong(ClientKillPerpareArgs(addr, id, type, skipMe)); + } + + static byte[][] ClientKillPerpareArgs(string addr, string id, string type, string skipMe) { var cmdWithArgs = new List { @@ -907,8 +930,7 @@ public long ClientKill(string addr = null, string id = null, string type = null, cmdWithArgs.Add(Commands.SkipMe); cmdWithArgs.Add(skipMe.ToUtf8Bytes()); } - - return SendExpectLong(cmdWithArgs.ToArray()); + return cmdWithArgs.ToArray(); } public byte[][] Keys(string pattern) @@ -920,15 +942,18 @@ public byte[][] Keys(string pattern) } public byte[][] MGet(params byte[][] keys) + { + return SendExpectMultiData(MGetPrepareArgs(keys)); + } + + private static byte[][] MGetPrepareArgs(byte[][] keys) { if (keys == null) throw new ArgumentNullException("keys"); if (keys.Length == 0) throw new ArgumentException("keys"); - var cmdWithArgs = MergeCommandWithArgs(Commands.MGet, keys); - - return SendExpectMultiData(cmdWithArgs); + return MergeCommandWithArgs(Commands.MGet, keys); } public byte[][] MGet(params string[] keys) @@ -1038,6 +1063,10 @@ internal ScanResult SendExpectScanResult(byte[] cmd, params byte[][] args) { var cmdWithArgs = MergeCommandWithArgs(cmd, args); var multiData = SendExpectDeeplyNestedMultiData(cmdWithArgs); + return ParseScanResult(multiData); + } + internal static ScanResult ParseScanResult(object[] multiData) + { var counterBytes = (byte[])multiData[0]; var ret = new ScanResult @@ -1239,11 +1268,16 @@ public byte[][] LRange(string listId, int startingFrom, int endingAt) } public byte[][] Sort(string listOrSetId, SortOptions sortOptions) + { + return SendExpectMultiData(SortPrepareArgs(listOrSetId, sortOptions)); + } + + private static byte[][] SortPrepareArgs(string listOrSetId, SortOptions sortOptions) { var cmdWithArgs = new List - { - Commands.Sort, listOrSetId.ToUtf8Bytes() - }; + { + Commands.Sort, listOrSetId.ToUtf8Bytes() + }; if (sortOptions.SortPattern != null) { @@ -1279,8 +1313,7 @@ public byte[][] Sort(string listOrSetId, SortOptions sortOptions) cmdWithArgs.Add(Commands.Store); cmdWithArgs.Add(sortOptions.StoreAtKey.ToUtf8Bytes()); } - - return SendExpectMultiData(cmdWithArgs.ToArray()); + return cmdWithArgs.ToArray(); } public long RPush(string listId, byte[] value) @@ -1678,6 +1711,12 @@ public long ZRevRank(string setId, byte[] value) } private byte[][] GetRange(byte[] commandBytes, string setId, int min, int max, bool withScores) + { + var args = GetRangeArgs(commandBytes, setId, min, max, withScores); + return SendExpectMultiData(args); + } + + private static byte[][] GetRangeArgs(byte[] commandBytes, string setId, int min, int max, bool withScores) { if (string.IsNullOrEmpty(setId)) throw new ArgumentNullException("setId"); @@ -1691,8 +1730,7 @@ private byte[][] GetRange(byte[] commandBytes, string setId, int min, int max, b { cmdWithArgs.Add(Commands.WithScores); } - - return SendExpectMultiData(cmdWithArgs.ToArray()); + return cmdWithArgs.ToArray(); } public byte[][] ZRange(string setId, int min, int max) @@ -1717,6 +1755,13 @@ public byte[][] ZRevRangeWithScores(string setId, int min, int max) private byte[][] GetRangeByScore(byte[] commandBytes, string setId, double min, double max, int? skip, int? take, bool withScores) + { + var args = GetRangeByScoreArgs(commandBytes, setId, min, max, skip, take, withScores); + return SendExpectMultiData(); + } + + private static byte[][] GetRangeByScoreArgs(byte[] commandBytes, + string setId, double min, double max, int? skip, int? take, bool withScores) { if (setId == null) throw new ArgumentNullException("setId"); @@ -1737,34 +1782,14 @@ private byte[][] GetRangeByScore(byte[] commandBytes, { cmdWithArgs.Add(Commands.WithScores); } - - return SendExpectMultiData(cmdWithArgs.ToArray()); + return cmdWithArgs.ToArray(); } private byte[][] GetRangeByScore(byte[] commandBytes, string setId, long min, long max, int? skip, int? take, bool withScores) { - if (setId == null) - throw new ArgumentNullException("setId"); - - var cmdWithArgs = new List - { - commandBytes, setId.ToUtf8Bytes(), min.ToUtf8Bytes(), max.ToUtf8Bytes() - }; - - if (skip.HasValue || take.HasValue) - { - cmdWithArgs.Add(Commands.Limit); - cmdWithArgs.Add(skip.GetValueOrDefault(0).ToUtf8Bytes()); - cmdWithArgs.Add(take.GetValueOrDefault(0).ToUtf8Bytes()); - } - - if (withScores) - { - cmdWithArgs.Add(Commands.WithScores); - } - - return SendExpectMultiData(cmdWithArgs.ToArray()); + var args = GetRangeByScoreArgs(commandBytes, setId, min, max, skip, take, withScores); + return SendExpectMultiData(args); } public byte[][] ZRangeByScore(string setId, double min, double max, int? skip, int? take) @@ -1912,7 +1937,7 @@ public long ZInterStore(string intoSetId, string[] setIds, string[] args) return SendExpectLong(cmdWithArgs); } - public byte[][] ZRangeByLex(string setId, string min, string max, int? skip = null, int? take = null) + static byte[][] GetZRangeByLexArgs(string setId, string min, string max, int? skip, int? take) { if (setId == null) throw new ArgumentNullException("setId"); @@ -1928,9 +1953,10 @@ public byte[][] ZRangeByLex(string setId, string min, string max, int? skip = nu cmdWithArgs.Add(skip.GetValueOrDefault(0).ToUtf8Bytes()); cmdWithArgs.Add(take.GetValueOrDefault(0).ToUtf8Bytes()); } - - return SendExpectMultiData(cmdWithArgs.ToArray()); + return cmdWithArgs.ToArray(); } + public byte[][] ZRangeByLex(string setId, string min, string max, int? skip = null, int? take = null) + => SendExpectMultiData(GetZRangeByLexArgs(setId, min, max, skip, take)); public long ZLexCount(string setId, string min, string max) { @@ -2166,6 +2192,12 @@ public long GeoAdd(string key, double longitude, double latitude, string member) } public long GeoAdd(string key, params RedisGeo[] geoPoints) + { + var cmdWithArgs = GeoAddPrepareArgs(key, geoPoints); + return SendExpectLong(cmdWithArgs); + } + + private static byte[][] GeoAddPrepareArgs(string key, RedisGeo[] geoPoints) { if (key == null) throw new ArgumentNullException(nameof(key)); @@ -2179,8 +2211,7 @@ public long GeoAdd(string key, params RedisGeo[] geoPoints) members[i * 3 + 2] = geoPoint.Member.ToUtf8Bytes(); } - var cmdWithArgs = MergeCommandWithArgs(Commands.GeoAdd, key.ToUtf8Bytes(), members); - return SendExpectLong(cmdWithArgs); + return MergeCommandWithArgs(Commands.GeoAdd, key.ToUtf8Bytes(), members); } public double GeoDist(string key, string fromMember, string toMember, string unit = null) @@ -2209,6 +2240,10 @@ public List GeoPos(string key, params string[] members) var cmdWithArgs = MergeCommandWithArgs(Commands.GeoPos, key.ToUtf8Bytes(), members.Map(x => x.ToUtf8Bytes()).ToArray()); var data = SendExpectComplexResponse(cmdWithArgs); + return GeoPosParseResult(members, data); + } + private static List GeoPosParseResult(string[] members, RedisData data) + { var to = new List(); for (var i = 0; i < members.Length; i++) @@ -2235,6 +2270,53 @@ public List GeoPos(string key, params string[] members) public List GeoRadius(string key, double longitude, double latitude, double radius, string unit, bool withCoords = false, bool withDist = false, bool withHash = false, int? count = null, bool? asc = null) + { + var cmdWithArgs = GeoRadiusPrepareArgs(key, longitude, latitude, radius, unit, + withCoords, withDist, withHash, count, asc); + + var to = new List(); + + if (!(withCoords || withDist || withHash)) + { + var members = SendExpectMultiData(cmdWithArgs).ToStringArray(); + foreach (var member in members) + { + to.Add(new RedisGeoResult { Member = member }); + } + } + else + { + var data = SendExpectComplexResponse(cmdWithArgs); + GetRadiusParseResult(unit, withCoords, withDist, withHash, to, data); + } + + return to; + } + + private static void GetRadiusParseResult(string unit, bool withCoords, bool withDist, bool withHash, List to, RedisData data) + { + foreach (var child in data.Children) + { + var i = 0; + var result = new RedisGeoResult { Unit = unit, Member = child.Children[i++].Data.FromUtf8Bytes() }; + + if (withDist) result.Distance = child.Children[i++].ToDouble(); + + if (withHash) result.Hash = child.Children[i++].ToInt64(); + + if (withCoords) + { + var children = child.Children[i].Children; + result.Longitude = children[0].ToDouble(); + result.Latitude = children[1].ToDouble(); + } + + to.Add(result); + } + } + + private static byte[][] GeoRadiusPrepareArgs(string key, double longitude, double latitude, double radius, string unit, + bool withCoords, bool withDist, bool withHash, int? count, bool? asc) { if (key == null) throw new ArgumentNullException(nameof(key)); @@ -2265,47 +2347,57 @@ public List GeoRadius(string key, double longitude, double latit else if (asc == false) args.Add(Commands.Desc); - var cmdWithArgs = MergeCommandWithArgs(Commands.GeoRadius, key.ToUtf8Bytes(), args.ToArray()); + return MergeCommandWithArgs(Commands.GeoRadius, key.ToUtf8Bytes(), args.ToArray()); + } + + public List GeoRadiusByMember(string key, string member, double radius, string unit, + bool withCoords = false, bool withDist = false, bool withHash = false, int? count = null, bool? asc = null) + { + var cmdWithArgs = GeoRadiusByMemberPrepareArgs(key, member, radius, unit, withCoords, withDist, withHash, count, asc); var to = new List(); if (!(withCoords || withDist || withHash)) { var members = SendExpectMultiData(cmdWithArgs).ToStringArray(); - foreach (var member in members) + foreach (var x in members) { - to.Add(new RedisGeoResult { Member = member }); + to.Add(new RedisGeoResult { Member = x }); } } else { var data = SendExpectComplexResponse(cmdWithArgs); + GeoRadiusByMemberParseResult(unit, withCoords, withDist, withHash, to, data); + } - foreach (var child in data.Children) - { - var i = 0; - var result = new RedisGeoResult { Unit = unit, Member = child.Children[i++].Data.FromUtf8Bytes() }; + return to; + } - if (withDist) result.Distance = child.Children[i++].ToDouble(); + private static void GeoRadiusByMemberParseResult(string unit, bool withCoords, bool withDist, bool withHash, List to, RedisData data) + { + foreach (var child in data.Children) + { + var i = 0; + var result = new RedisGeoResult { Unit = unit, Member = child.Children[i++].Data.FromUtf8Bytes() }; - if (withHash) result.Hash = child.Children[i++].ToInt64(); + if (withDist) result.Distance = child.Children[i++].ToDouble(); - if (withCoords) - { - var children = child.Children[i].Children; - result.Longitude = children[0].ToDouble(); - result.Latitude = children[1].ToDouble(); - } + if (withHash) result.Hash = child.Children[i++].ToInt64(); - to.Add(result); + if (withCoords) + { + var children = child.Children[i].Children; + result.Longitude = children[0].ToDouble(); + result.Latitude = children[1].ToDouble(); } - } - return to; + to.Add(result); + } } - public List GeoRadiusByMember(string key, string member, double radius, string unit, - bool withCoords = false, bool withDist = false, bool withHash = false, int? count = null, bool? asc = null) + static byte[][] GeoRadiusByMemberPrepareArgs(string key, string member, double radius, string unit, + bool withCoords, bool withDist, bool withHash, int? count, bool? asc) { if (key == null) throw new ArgumentNullException(nameof(key)); @@ -2335,43 +2427,7 @@ public List GeoRadiusByMember(string key, string member, double else if (asc == false) args.Add(Commands.Desc); - var cmdWithArgs = MergeCommandWithArgs(Commands.GeoRadiusByMember, key.ToUtf8Bytes(), args.ToArray()); - - var to = new List(); - - if (!(withCoords || withDist || withHash)) - { - var members = SendExpectMultiData(cmdWithArgs).ToStringArray(); - foreach (var x in members) - { - to.Add(new RedisGeoResult { Member = x }); - } - } - else - { - var data = SendExpectComplexResponse(cmdWithArgs); - - foreach (var child in data.Children) - { - var i = 0; - var result = new RedisGeoResult { Unit = unit, Member = child.Children[i++].Data.FromUtf8Bytes() }; - - if (withDist) result.Distance = child.Children[i++].ToDouble(); - - if (withHash) result.Hash = child.Children[i++].ToInt64(); - - if (withCoords) - { - var children = child.Children[i].Children; - result.Longitude = children[0].ToDouble(); - result.Latitude = children[1].ToDouble(); - } - - to.Add(result); - } - } - - return to; + return MergeCommandWithArgs(Commands.GeoRadiusByMember, key.ToUtf8Bytes(), args.ToArray()); } #endregion @@ -2432,7 +2488,7 @@ private void SafeConnectionClose() try { // workaround for a .net bug: http://support.microsoft.com/kb/821625 - Bstream?.Close(); + bufferedReader?.Close(); } catch { } try @@ -2446,7 +2502,7 @@ private void SafeConnectionClose() } catch { } - Bstream = null; + bufferedReader = null; sslStream = null; socket = null; } diff --git a/src/ServiceStack.Redis/RedisNativeClient_Utils.Async.cs b/src/ServiceStack.Redis/RedisNativeClient_Utils.Async.cs new file mode 100644 index 00000000..969d6167 --- /dev/null +++ b/src/ServiceStack.Redis/RedisNativeClient_Utils.Async.cs @@ -0,0 +1,552 @@ +using ServiceStack.Redis.Internal; +using ServiceStack.Redis.Pipeline; +using ServiceStack.Text; +using ServiceStack.Text.Pools; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + partial class RedisNativeClient + { + private async ValueTask SendExpectMultiDataAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + { + return (await SendReceiveAsync(cmdWithBinaryArgs, ReadMultiDataAsync, cancellationToken, + PipelineAsync != null ? PipelineAsync.CompleteMultiBytesQueuedCommandAsync : (Action>>)null).ConfigureAwait(false)) + ?? TypeConstants.EmptyByteArrayArray; + } + + protected ValueTask SendWithoutReadAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + => SendReceiveAsync(cmdWithBinaryArgs, null, cancellationToken, null, sendWithoutRead: true).Await(); + + private ValueTask SendExpectLongAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + { + return SendReceiveAsync(cmdWithBinaryArgs, ReadLongAsync, cancellationToken, + PipelineAsync != null ? PipelineAsync.CompleteLongQueuedCommandAsync : (Action>>)null); + } + + private ValueTask SendExpectDoubleAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + { + return SendReceiveAsync(cmdWithBinaryArgs, ReadDoubleAsync, cancellationToken, + PipelineAsync != null ? PipelineAsync.CompleteDoubleQueuedCommandAsync : (Action>>)null); + } + protected ValueTask SendExpectStringAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + => SendExpectDataAsync(cancellationToken, cmdWithBinaryArgs).FromUtf8BytesAsync(); + + private ValueTask SendExpectSuccessAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + { + //Turn Action into Func Hack + Action>> completePipelineFn = null; + if (PipelineAsync != null) completePipelineFn = f => { PipelineAsync.CompleteVoidQueuedCommandAsync(ct => f(ct).Await()); }; + + return SendReceiveAsync(cmdWithBinaryArgs, ExpectSuccessFnAsync, cancellationToken, completePipelineFn).Await(); + } + + private ValueTask SendExpectDataAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + { + return SendReceiveAsync(cmdWithBinaryArgs, ReadDataAsync, cancellationToken, PipelineAsync != null ? PipelineAsync.CompleteBytesQueuedCommandAsync : (Action>>)null); + } + + private ValueTask SendExpectCodeAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + { + return SendReceiveAsync(cmdWithBinaryArgs, ExpectCodeAsync, cancellationToken, PipelineAsync != null ? PipelineAsync.CompleteStringQueuedCommandAsync : (Action>>)null); + } + + private ValueTask SendExpectScanResultAsync(CancellationToken cancellationToken, byte[] cmd, params byte[][] args) + { + var cmdWithArgs = MergeCommandWithArgs(cmd, args); + return SendExpectDeeplyNestedMultiDataAsync(cancellationToken, cmdWithArgs).Await(multiData => ParseScanResult(multiData)); + } + + private ValueTask SendExpectDeeplyNestedMultiDataAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + => SendReceiveAsync(cmdWithBinaryArgs, ReadDeeplyNestedMultiDataAsync, cancellationToken); + + private ValueTask ReadDeeplyNestedMultiDataAsync(CancellationToken cancellationToken) + => ReadDeeplyNestedMultiDataItemAsync(cancellationToken).Await(result => (object[])result); + + private async ValueTask ReadDeeplyNestedMultiDataItemAsync(CancellationToken cancellationToken) + { + int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + if (c == -1) + throw CreateNoMoreDataError(); + + var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + if (log.IsDebugEnabled) + Log("R: {0}", s); + + switch (c) + { + case '$': + return await ParseSingleLineAsync(string.Concat(char.ToString((char)c), s), cancellationToken).ConfigureAwait(false); + + case '-': + throw CreateResponseError(s.StartsWith("ERR") ? s.Substring(4) : s); + + case '*': + if (int.TryParse(s, out var count)) + { + var array = new object[count]; + for (int i = 0; i < count; i++) + { + array[i] = await ReadDeeplyNestedMultiDataItemAsync(cancellationToken).ConfigureAwait(false); + } + + return array; + } + break; + + default: + return s; + } + + throw CreateResponseError("Unknown reply on multi-request: " + ((char)c) + s); // c here is the protocol prefix + } + + protected ValueTask SendExpectComplexResponseAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + { + return SendReceiveAsync(cmdWithBinaryArgs, ReadComplexResponseAsync, cancellationToken, + PipelineAsync != null ? PipelineAsync.CompleteRedisDataQueuedCommandAsync : (Action>>)null); + } + + private async ValueTask ReadComplexResponseAsync(CancellationToken cancellationToken) + { + int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + if (c == -1) + throw CreateNoMoreDataError(); + + var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + if (log.IsDebugEnabled) + Log("R: {0}", s); + + switch (c) + { + case '$': + return new RedisData + { + Data = await ParseSingleLineAsync(string.Concat(char.ToString((char)c), s), cancellationToken).ConfigureAwait(false) + }; + + case '-': + throw CreateResponseError(s.StartsWith("ERR") ? s.Substring(4) : s); + + case '*': + if (int.TryParse(s, out var count)) + { + var ret = new RedisData { Children = new List() }; + for (var i = 0; i < count; i++) + { + ret.Children.Add(await ReadComplexResponseAsync(cancellationToken).ConfigureAwait(false)); + } + + return ret; + } + break; + + default: + return new RedisData { Data = s.ToUtf8Bytes() }; + } + + throw CreateResponseError("Unknown reply on multi-request: " + ((char)c) + s); // c here is the protocol prefix + } + + private async ValueTask SendReceiveAsync(byte[][] cmdWithBinaryArgs, + Func> fn, + CancellationToken cancellationToken, + Action>> completePipelineFn = null, + bool sendWithoutRead = false) + { + //if (TrackThread != null) + //{ + // if (TrackThread.Value.ThreadId != Thread.CurrentThread.ManagedThreadId) + // throw new InvalidAccessException(TrackThread.Value.ThreadId, TrackThread.Value.StackTrace); + //} + + var i = 0; + var didWriteToBuffer = false; + Exception originalEx = null; + + var firstAttempt = DateTime.UtcNow; + + while (true) + { + // this is deliberately *before* the try, so we never retry + // if we've been cancelled + cancellationToken.ThrowIfCancellationRequested(); + try + { + if (TryConnectIfNeeded()) // TODO: asyncify + didWriteToBuffer = false; + + if (socket == null) + throw new RedisRetryableException("Socket is not connected"); + + if (!didWriteToBuffer) //only write to buffer once + { + WriteCommandToSendBuffer(cmdWithBinaryArgs); + didWriteToBuffer = true; + } + + if (PipelineAsync == null) //pipeline will handle flush if in pipeline + { + await FlushSendBufferAsync(cancellationToken).ConfigureAwait(false); + } + else if (!sendWithoutRead) + { + if (completePipelineFn == null) + throw new NotSupportedException("Pipeline is not supported."); + + completePipelineFn(fn); + return default; + } + + var result = default(T); + if (fn != null) + result = await fn(cancellationToken).ConfigureAwait(false); + + if (Pipeline == null) + ResetSendBuffer(); + + if (i > 0) + Interlocked.Increment(ref RedisState.TotalRetrySuccess); + + Interlocked.Increment(ref RedisState.TotalCommandsSent); + + return result; + } + catch (Exception outerEx) + { + if (log.IsDebugEnabled) + logDebug("SendReceive Exception: " + outerEx.Message); + + var retryableEx = outerEx as RedisRetryableException; + if (retryableEx == null && outerEx is RedisException + || outerEx is LicenseException) + { + ResetSendBuffer(); + throw; + } + + var ex = retryableEx ?? GetRetryableException(outerEx); + if (ex == null) + throw CreateConnectionError(originalEx ?? outerEx); + + if (originalEx == null) + originalEx = ex; + + var retry = DateTime.UtcNow - firstAttempt < retryTimeout; + if (!retry) + { + if (Pipeline == null) + ResetSendBuffer(); + + Interlocked.Increment(ref RedisState.TotalRetryTimedout); + throw CreateRetryTimeoutException(retryTimeout, originalEx); + } + + Interlocked.Increment(ref RedisState.TotalRetryCount); + await Task.Delay(GetBackOffMultiplier(++i)).ConfigureAwait(false); + } + } + } + + internal ValueTask FlushSendBufferAsync(CancellationToken cancellationToken) + { + if (currentBufferIndex > 0) + PushCurrentBuffer(); + + if (cmdBuffer.Count > 0) + { + OnBeforeFlush?.Invoke(); + + if (!Env.IsMono && sslStream == null) + { + if (log.IsDebugEnabled && RedisConfig.EnableVerboseLogging) + { + var sb = StringBuilderCache.Allocate(); + foreach (var cmd in cmdBuffer) + { + if (sb.Length > 50) + break; + + sb.Append(Encoding.UTF8.GetString(cmd.Array, cmd.Offset, cmd.Count)); + } + logDebug("socket.Send: " + StringBuilderCache.ReturnAndFree(sb.Replace("\r\n", " ")).SafeSubstring(0, 50)); + } + + return new ValueTask(socket.SendAsync(cmdBuffer, SocketFlags.None)); + } + else + { + //Sending IList Throws 'Message to Large' SocketException in Mono + if (sslStream == null) + { + foreach (var segment in cmdBuffer) + { // TODO: what is modern Mono behavior here? + socket.Send(segment.Array, segment.Offset, segment.Count, SocketFlags.None); + } + } + else + { + return WriteAsync(sslStream, cmdBuffer, cancellationToken); + } + } + } + + return default; + + static async ValueTask WriteAsync(Stream destination, List> buffer, CancellationToken cancellationToken) + { + foreach (var segment in buffer) + { +#if ASYNC_MEMORY + await destination.WriteAsync(new ReadOnlyMemory(segment.Array, segment.Offset, segment.Count), cancellationToken).ConfigureAwait(false); +#else + await destination.WriteAsync(segment.Array, segment.Offset, segment.Count, cancellationToken).ConfigureAwait(false); +#endif + } + } + } + + + private ValueTask SafeReadByteAsync(in CancellationToken cancellationToken, [CallerMemberName]string name = null) + { + AssertNotDisposed(); + + if (log.IsDebugEnabled && RedisConfig.EnableVerboseLogging) + logDebug(name + "()"); + + return bufferedReader.ReadByteAsync(cancellationToken); + } + + private async ValueTask ReadLineAsync(CancellationToken cancellationToken) + { + AssertNotDisposed(); + + var sb = StringBuilderCache.Allocate(); + + int c; + while ((c = await bufferedReader.ReadByteAsync(cancellationToken).ConfigureAwait(false)) != -1) + { + if (c == '\r') + continue; + if (c == '\n') + break; + sb.Append((char)c); + } + return StringBuilderCache.ReturnAndFree(sb); + } + + private async ValueTask ParseSingleLineAsync(string r, CancellationToken cancellationToken) + { + if (log.IsDebugEnabled) + Log("R: {0}", r); + if (r.Length == 0) + throw CreateResponseError("Zero length response"); + + char c = r[0]; + if (c == '-') + throw CreateResponseError(r.StartsWith("-ERR") ? r.Substring(5) : r.Substring(1)); + + if (c == '$') + { + if (r == "$-1") + return null; + + if (int.TryParse(r.Substring(1), out var count)) + { + var retbuf = new byte[count]; + + var offset = 0; + while (count > 0) + { + var readCount = await bufferedReader.ReadAsync(retbuf, offset, count, cancellationToken).ConfigureAwait(false); + if (readCount <= 0) + throw CreateResponseError("Unexpected end of Stream"); + + offset += readCount; + count -= readCount; + } + + if (await bufferedReader.ReadByteAsync(cancellationToken).ConfigureAwait(false) != '\r' + || await bufferedReader.ReadByteAsync(cancellationToken).ConfigureAwait(false) != '\n') + throw CreateResponseError("Invalid termination"); + + return retbuf; + } + throw CreateResponseError("Invalid length"); + } + + if (c == ':' || c == '+') + { + //match the return value + return r.Substring(1).ToUtf8Bytes(); + } + throw CreateResponseError("Unexpected reply: " + r); + } + + private ValueTask ReadDataAsync(CancellationToken cancellationToken) + { + var pending = ReadLineAsync(cancellationToken); + return pending.IsCompletedSuccessfully + ? ParseSingleLineAsync(pending.Result, cancellationToken) + : Awaited(this, pending, cancellationToken); + + static async ValueTask Awaited(RedisNativeClient @this, ValueTask pending, CancellationToken cancellationToken) + { + var r = await pending.ConfigureAwait(false); + return await @this.ParseSingleLineAsync(r, cancellationToken).ConfigureAwait(false); + } + } + + private async ValueTask ExpectCodeAsync(CancellationToken cancellationToken) + { + int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + if (c == -1) + throw CreateNoMoreDataError(); + + var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + + if (log.IsDebugEnabled) + Log((char)c + s); + + if (c == '-') + throw CreateResponseError(s.StartsWith("ERR") ? s.Substring(4) : s); + + return s; + } + + private async ValueTask ReadMultiDataAsync(CancellationToken cancellationToken) + { + int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + if (c == -1) + throw CreateNoMoreDataError(); + + var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + if (log.IsDebugEnabled) + Log("R: {0}", s); + + switch (c) + { + // Some commands like BRPOPLPUSH may return Bulk Reply instead of Multi-bulk + case '$': + var t = new byte[2][]; + t[1] = await ParseSingleLineAsync(string.Concat(char.ToString((char)c), s), cancellationToken).ConfigureAwait(false); + return t; + + case '-': + throw CreateResponseError(s.StartsWith("ERR") ? s.Substring(4) : s); + + case '*': + if (int.TryParse(s, out var count)) + { + if (count == -1) + { + //redis is in an invalid state + return TypeConstants.EmptyByteArrayArray; + } + + var result = new byte[count][]; + + for (int i = 0; i < count; i++) + result[i] = await ReadDataAsync(cancellationToken).ConfigureAwait(false); + + return result; + } + break; + } + + throw CreateResponseError("Unknown reply on multi-request: " + ((char)c) + s); // c here is the protocol prefix + } + + internal async ValueTask ReadLongAsync(CancellationToken cancellationToken) + { + int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + if (c == -1) + throw CreateNoMoreDataError(); + + return ParseLong(c, await ReadLineAsync(cancellationToken).ConfigureAwait(false)); + } + + private ValueTask ReadDoubleAsync(CancellationToken cancellationToken) + => ReadDataAsync(cancellationToken).Await(bytes => bytes == null ? double.NaN : ParseDouble(bytes)); + + internal ValueTask ExpectOkAsync(CancellationToken cancellationToken) + => ExpectWordAsync(OK, cancellationToken); + + internal ValueTask ExpectQueuedAsync(CancellationToken cancellationToken) + => ExpectWordAsync(QUEUED, cancellationToken); + + internal ValueTask ExpectSuccessFnAsync(CancellationToken cancellationToken) + { + var pending = ExpectSuccessAsync(cancellationToken); + return pending.IsCompletedSuccessfully ? default : Awaited(pending); + + static async ValueTask Awaited(ValueTask pending) + { + await pending.ConfigureAwait(false); + return 0; + } + } + + internal async ValueTask ExpectSuccessAsync(CancellationToken cancellationToken) + { + int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + if (c == -1) + throw CreateNoMoreDataError(); + + var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + + if (log.IsDebugEnabled) + Log((char)c + s); + + if (c == '-') + throw CreateResponseError(s.StartsWith("ERR") && s.Length >= 4 ? s.Substring(4) : s); + } + + + private async ValueTask ExpectWordAsync(string word, CancellationToken cancellationToken) + { + int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + if (c == -1) + throw CreateNoMoreDataError(); + + var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + + if (log.IsDebugEnabled) + Log((char)c + s); + + if (c == '-') + throw CreateResponseError(s.StartsWith("ERR") ? s.Substring(4) : s); + + if (s != word) + throw CreateResponseError($"Expected '{word}' got '{s}'"); + } + + internal async ValueTask ReadMultiDataResultCountAsync(CancellationToken cancellationToken) + { + int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + if (c == -1) + throw CreateNoMoreDataError(); + + var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + if (log.IsDebugEnabled) + Log("R: {0}", s); + if (c == '-') + throw CreateResponseError(s.StartsWith("ERR") ? s.Substring(4) : s); + if (c == '*') + { + if (int.TryParse(s, out var count)) + { + return count; + } + } + throw CreateResponseError("Unknown reply on multi-request: " + ((char)c) + s); // c here is the protocol prefix + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisNativeClient_Utils.cs b/src/ServiceStack.Redis/RedisNativeClient_Utils.cs index 0edc7918..5ebb894d 100644 --- a/src/ServiceStack.Redis/RedisNativeClient_Utils.cs +++ b/src/ServiceStack.Redis/RedisNativeClient_Utils.cs @@ -90,6 +90,12 @@ private void Connect() SendTimeout = SendTimeout, ReceiveTimeout = ReceiveTimeout }; +#if DEBUG + // allow sync commands during connect (we're OK with sync for connect; the + // DebugAllowSync feature being used here only impacts tests) + var oldDebugAllowSync = DebugAllowSync; + DebugAllowSync = true; +#endif try { if (log.IsDebugEnabled) @@ -180,7 +186,7 @@ private void Connect() networkStream = sslStream; } - Bstream = new BufferedStream(networkStream, 16 * 1024); + bufferedReader = new BufferedReader(networkStream, 16 * 1024); if (!string.IsNullOrEmpty(Password)) SendUnmanagedExpectSuccess(Commands.Auth, Password.ToUtf8Bytes()); @@ -233,6 +239,12 @@ private void Connect() logError(ErrorConnect.Fmt(Host, Port)); throw; } + finally + { +#if DEBUG + DebugAllowSync = oldDebugAllowSync; +#endif + } } public static string ErrorConnect = "Could not connect to redis Instance at {0}:{1}"; @@ -244,11 +256,12 @@ public virtual void OnConnected() protected string ReadLine() { AssertNotDisposed(); + AssertNotAsyncOnly(); var sb = StringBuilderCache.Allocate(); int c; - while ((c = Bstream.ReadByte()) != -1) + while ((c = bufferedReader.ReadByte()) != -1) { if (c == '\r') continue; @@ -426,7 +439,7 @@ protected void SendUnmanagedExpectSuccess(params byte[][] cmdWithBinaryArgs) if (log.IsDebugEnabled && RedisConfig.EnableVerboseLogging) logDebug("stream.Write: " + Encoding.UTF8.GetString(bytes, 0, Math.Min(bytes.Length, 50)).Replace("\r\n"," ").SafeSubstring(0,50)); - Bstream.Write(bytes, 0, bytes.Length); + SendDirectToSocket(new ArraySegment(bytes, 0, bytes.Length)); ExpectSuccess(); } @@ -443,7 +456,9 @@ public void WriteAllToSendBuffer(params byte[][] cmdWithBinaryArgs) } } - readonly IList> cmdBuffer = new List>(); + // trated as List rather than IList to avoid allocs during foreach + readonly List> cmdBuffer = new List>(); + byte[] currentBuffer = BufferPool.GetBuffer(); int currentBufferIndex; @@ -524,20 +539,24 @@ internal void FlushSendBuffer() //Sending IList Throws 'Message to Large' SocketException in Mono foreach (var segment in cmdBuffer) { - var buffer = segment.Array; - if (sslStream == null) - { - socket.Send(buffer, segment.Offset, segment.Count, SocketFlags.None); - } - else - { - sslStream.Write(buffer, segment.Offset, segment.Count); - } + SendDirectToSocket(segment); } } } } + private void SendDirectToSocket(ArraySegment segment) + { + if (sslStream == null) + { + socket.Send(segment.Array, segment.Offset, segment.Count, SocketFlags.None); + } + else + { + sslStream.Write(segment.Array, segment.Offset, segment.Count); + } + } + /// /// reset buffer index in send buffer /// @@ -555,27 +574,41 @@ public void ResetSendBuffer() [MethodImpl(MethodImplOptions.AggressiveInlining)] void AssertNotDisposed() { - if (Bstream == null) + if (bufferedReader == null) throw new ObjectDisposedException($"Redis Client {ClientId} is Disposed"); } private int SafeReadByte(string name) { AssertNotDisposed(); - + AssertNotAsyncOnly(); + if (log.IsDebugEnabled && RedisConfig.EnableVerboseLogging) logDebug(name + "()"); - return Bstream.ReadByte(); + return bufferedReader.ReadByte(); } internal TrackThread? TrackThread; - + + partial void AssertNotAsyncOnly([CallerMemberName] string caller = default); +#if DEBUG + public bool DebugAllowSync { get; set; } = true; + partial void AssertNotAsyncOnly(string caller) + { + // for unit tests only; asserts that we're not meant to be in an async context + if (!DebugAllowSync) + throw new InvalidOperationException("Unexpected synchronous operation detected from '" + caller + "'"); + } +#endif + + protected T SendReceive(byte[][] cmdWithBinaryArgs, Func fn, Action> completePipelineFn = null, bool sendWithoutRead = false) { + if (Pipeline is null) AssertNotAsyncOnly(); if (TrackThread != null) { if (TrackThread.Value.ThreadId != Thread.CurrentThread.ManagedThreadId) @@ -904,8 +937,11 @@ public long ReadLong() if (c == -1) throw CreateNoMoreDataError(); - var s = ReadLine(); + return ParseLong(c, ReadLine()); + } + private long ParseLong(int c, string s) + { if (log.IsDebugEnabled) Log("R: {0}", s); @@ -918,7 +954,7 @@ public long ReadLong() if (long.TryParse(s, out i)) return i; } - throw CreateResponseError("Unknown reply on integer response: " + c + s); + throw CreateResponseError("Unknown reply on integer response: " + ((char)c) + s); // c here is the protocol prefix } public double ReadDouble() @@ -963,7 +999,7 @@ private byte[] ParseSingleLine(string r) var offset = 0; while (count > 0) { - var readCount = Bstream.Read(retbuf, offset, count); + var readCount = bufferedReader.Read(retbuf, offset, count); if (readCount <= 0) throw CreateResponseError("Unexpected end of Stream"); @@ -971,7 +1007,7 @@ private byte[] ParseSingleLine(string r) count -= readCount; } - if (Bstream.ReadByte() != '\r' || Bstream.ReadByte() != '\n') + if (bufferedReader.ReadByte() != '\r' || bufferedReader.ReadByte() != '\n') throw CreateResponseError("Invalid termination"); return retbuf; @@ -1027,7 +1063,7 @@ private byte[][] ReadMultiData() break; } - throw CreateResponseError("Unknown reply on multi-request: " + c + s); + throw CreateResponseError("Unknown reply on multi-request: " + ((char)c) + s); // c here is the protocol prefix } private object[] ReadDeeplyNestedMultiData() @@ -1071,7 +1107,7 @@ private object ReadDeeplyNestedMultiDataItem() return s; } - throw CreateResponseError("Unknown reply on multi-request: " + c + s); + throw CreateResponseError("Unknown reply on multi-request: " + ((char)c) + s); // c here is the protocol prefix } internal RedisData ReadComplexResponse() @@ -1112,7 +1148,7 @@ internal RedisData ReadComplexResponse() return new RedisData { Data = s.ToUtf8Bytes() }; } - throw CreateResponseError("Unknown reply on multi-request: " + c + s); + throw CreateResponseError("Unknown reply on multi-request: " + ((char)c) + s); // c here is the protocol prefix } internal int ReadMultiDataResultCount() @@ -1133,7 +1169,7 @@ internal int ReadMultiDataResultCount() return count; } } - throw CreateResponseError("Unknown reply on multi-request: " + c + s); + throw CreateResponseError("Unknown reply on multi-request: " + ((char)c) + s); // c here is the protocol prefix } private static void AssertListIdAndValue(string listId, byte[] value) diff --git a/src/ServiceStack.Redis/RedisSentinel.cs b/src/ServiceStack.Redis/RedisSentinel.cs index 286c1965..25965ef2 100644 --- a/src/ServiceStack.Redis/RedisSentinel.cs +++ b/src/ServiceStack.Redis/RedisSentinel.cs @@ -314,6 +314,7 @@ private RedisSentinelWorker GetValidSentinelWorker() 0 => "GetNextSentinel()", 1 => "GetRedisManager()", 2 => "BeginListeningForConfigurationChanges()", + _ => $"Step {step}", }; Log.Debug($"Failed to {name}: {ex.Message}"); } diff --git a/src/ServiceStack.Redis/RedisSubscription.Async.cs b/src/ServiceStack.Redis/RedisSubscription.Async.cs new file mode 100644 index 00000000..ae0027e8 --- /dev/null +++ b/src/ServiceStack.Redis/RedisSubscription.Async.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis +{ + partial class RedisSubscription + : IRedisSubscriptionAsync + { + // private events here for +/- semantics + private event Func OnSubscribeAsync; + private event Func OnMessageAsync; + private event Func OnMessageBytesAsync; + private event Func OnUnSubscribeAsync; + + event Func IRedisSubscriptionAsync.OnSubscribeAsync + { + add => OnSubscribeAsync += value; + remove => OnSubscribeAsync -= value; + } + event Func IRedisSubscriptionAsync.OnMessageAsync + { + add => OnMessageAsync += value; + remove => OnMessageAsync -= value; + } + event Func IRedisSubscriptionAsync.OnMessageBytesAsync + { + add => OnMessageBytesAsync += value; + remove => OnMessageBytesAsync -= value; + } + event Func IRedisSubscriptionAsync.OnUnSubscribeAsync + { + add => OnUnSubscribeAsync += value; + remove => OnUnSubscribeAsync -= value; + } + + private IRedisSubscriptionAsync AsAsync() => this; + private IRedisNativeClientAsync NativeAsync + { + get + { + return redisClient as IRedisNativeClientAsync ?? NotAsync(); + static IRedisNativeClientAsync NotAsync() => throw new InvalidOperationException("The underlying client is not async"); + } + } + + private async ValueTask UnSubscribeFromAllChannelsMatchingAnyPatternsAsync(CancellationToken cancellationToken = default) + { + if (activeChannels.Count == 0) return; + + var multiBytes = await NativeAsync.PUnSubscribeAsync(Array.Empty(), cancellationToken).ConfigureAwait(false); + await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); + + this.activeChannels = new List(); + } + + ValueTask IAsyncDisposable.DisposeAsync() => IsPSubscription + ? UnSubscribeFromAllChannelsMatchingAnyPatternsAsync() + : AsAsync().UnSubscribeFromAllChannelsAsync(); + + async ValueTask IRedisSubscriptionAsync.SubscribeToChannelsAsync(string[] channels, CancellationToken cancellationToken) + { + var multiBytes = await NativeAsync.SubscribeAsync(channels, cancellationToken).ConfigureAwait(false); + await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); + + while (this.SubscriptionCount > 0) + { + multiBytes = await NativeAsync.ReceiveMessagesAsync(cancellationToken).ConfigureAwait(false); + await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); + } + } + + async ValueTask IRedisSubscriptionAsync.SubscribeToChannelsMatchingAsync(string[] patterns, CancellationToken cancellationToken) + { + var multiBytes = await NativeAsync.PSubscribeAsync(patterns, cancellationToken).ConfigureAwait(false); + await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); + + while (this.SubscriptionCount > 0) + { + multiBytes = await NativeAsync.ReceiveMessagesAsync(cancellationToken).ConfigureAwait(false); + await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); + } + } + + async ValueTask IRedisSubscriptionAsync.UnSubscribeFromAllChannelsAsync(CancellationToken cancellationToken) + { + if (activeChannels.Count == 0) return; + + var multiBytes = await NativeAsync.UnSubscribeAsync(Array.Empty(), cancellationToken).ConfigureAwait(false); + await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); + + this.activeChannels = new List(); + } + + async ValueTask IRedisSubscriptionAsync.UnSubscribeFromChannelsAsync(string[] channels, CancellationToken cancellationToken) + { + var multiBytes = await NativeAsync.UnSubscribeAsync(channels, cancellationToken).ConfigureAwait(false); + await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); + } + + async ValueTask IRedisSubscriptionAsync.UnSubscribeFromChannelsMatchingAsync(string[] patterns, CancellationToken cancellationToken) + { + var multiBytes = await NativeAsync.PUnSubscribeAsync(patterns, cancellationToken).ConfigureAwait(false); + await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); + } + + private async ValueTask ParseSubscriptionResultsAsync(byte[][] multiBytes) + { + int componentsPerMsg = IsPSubscription ? 4 : 3; + for (var i = 0; i < multiBytes.Length; i += componentsPerMsg) + { + var messageType = multiBytes[i]; + var channel = multiBytes[i + 1].FromUtf8Bytes(); + if (SubscribeWord.AreEqual(messageType) + || PSubscribeWord.AreEqual(messageType)) + { + IsPSubscription = PSubscribeWord.AreEqual(messageType); + + this.SubscriptionCount = int.Parse(multiBytes[i + MsgIndex].FromUtf8Bytes()); + + activeChannels.Add(channel); + + var tmp = OnSubscribeAsync; + if (tmp is object) await tmp.Invoke(channel).ConfigureAwait(false); + } + else if (UnSubscribeWord.AreEqual(messageType) + || PUnSubscribeWord.AreEqual(messageType)) + { + this.SubscriptionCount = int.Parse(multiBytes[i + 2].FromUtf8Bytes()); + + activeChannels.Remove(channel); + + var tmp = OnUnSubscribeAsync; + if (tmp is object) await tmp.Invoke(channel).ConfigureAwait(false); + } + else if (MessageWord.AreEqual(messageType)) + { + var msgBytes = multiBytes[i + MsgIndex]; + var tmp1 = OnMessageBytesAsync; + if (tmp1 is object) await tmp1.Invoke(channel, msgBytes).ConfigureAwait(false); + + var tmp2 = OnMessageAsync; + if (tmp2 is object) + { + var message = msgBytes.FromUtf8Bytes(); + await tmp2.Invoke(channel, message).ConfigureAwait(false); + } + } + else if (PMessageWord.AreEqual(messageType)) + { + channel = multiBytes[i + 2].FromUtf8Bytes(); + var msgBytes = multiBytes[i + MsgIndex + 1]; + var tmp1 = OnMessageBytesAsync; + if (tmp1 is object) await tmp1.Invoke(channel, msgBytes).ConfigureAwait(false); + + var tmp2 = OnMessageAsync; + if (tmp2 is object) + { + var message = msgBytes.FromUtf8Bytes(); + await tmp2.Invoke(channel, message).ConfigureAwait(false); + } + } + else + { + throw new RedisException( + "Invalid state. Expected [[p]subscribe|[p]unsubscribe|message] got: " + messageType.FromUtf8Bytes()); + } + } + } + + ValueTask IRedisSubscriptionAsync.SubscribeToChannelsAsync(params string[] channels) + => AsAsync().SubscribeToChannelsAsync(channels, cancellationToken: default); + + ValueTask IRedisSubscriptionAsync.SubscribeToChannelsMatchingAsync(params string[] patterns) + => AsAsync().SubscribeToChannelsMatchingAsync(patterns, cancellationToken: default); + + ValueTask IRedisSubscriptionAsync.UnSubscribeFromChannelsAsync(params string[] channels) + => AsAsync().UnSubscribeFromChannelsAsync(channels, cancellationToken: default); + + ValueTask IRedisSubscriptionAsync.UnSubscribeFromChannelsMatchingAsync(params string[] patterns) + => AsAsync().UnSubscribeFromChannelsMatchingAsync(patterns, cancellationToken: default); + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisSubscription.cs b/src/ServiceStack.Redis/RedisSubscription.cs index 88e753cc..298b1bf4 100644 --- a/src/ServiceStack.Redis/RedisSubscription.cs +++ b/src/ServiceStack.Redis/RedisSubscription.cs @@ -4,7 +4,7 @@ namespace ServiceStack.Redis { - public class RedisSubscription + public partial class RedisSubscription : IRedisSubscription { private readonly IRedisNativeClient redisClient; @@ -73,10 +73,7 @@ private void ParseSubscriptionResults(byte[][] multiBytes) activeChannels.Add(channel); - if (this.OnSubscribe != null) - { - this.OnSubscribe(channel); - } + this.OnSubscribe?.Invoke(channel); } else if (UnSubscribeWord.AreEqual(messageType) || PUnSubscribeWord.AreEqual(messageType)) @@ -85,39 +82,24 @@ private void ParseSubscriptionResults(byte[][] multiBytes) activeChannels.Remove(channel); - if (this.OnUnSubscribe != null) - { - this.OnUnSubscribe(channel); - } + this.OnUnSubscribe?.Invoke(channel); } else if (MessageWord.AreEqual(messageType)) { var msgBytes = multiBytes[i + MsgIndex]; - if (this.OnMessageBytes != null) - { - this.OnMessageBytes(channel, msgBytes); - } - + this.OnMessageBytes?.Invoke(channel, msgBytes); + var message = msgBytes.FromUtf8Bytes(); - if (this.OnMessage != null) - { - this.OnMessage(channel, message); - } + this.OnMessage?.Invoke(channel, message); } else if (PMessageWord.AreEqual(messageType)) { channel = multiBytes[i + 2].FromUtf8Bytes(); var msgBytes = multiBytes[i + MsgIndex + 1]; - if (this.OnMessageBytes != null) - { - this.OnMessageBytes(channel, msgBytes); - } - + this.OnMessageBytes?.Invoke(channel, msgBytes); + var message = msgBytes.FromUtf8Bytes(); - if (this.OnMessage != null) - { - this.OnMessage(channel, message); - } + this.OnMessage?.Invoke(channel, message); } else { diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.csproj index e7b6503e..4808134c 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.csproj @@ -2,7 +2,7 @@ ServiceStack.Redis ServiceStack.Redis - net45;netstandard2.0 + net45;net472;netstandard2.0;netstandard2.1 C# Redis client for the Redis NoSQL DB C# Redis Client for the worlds fastest distributed NoSQL datastore. @@ -11,11 +11,25 @@ Redis;NoSQL;Client;Distributed;Cache;PubSub;Messaging;Transactions + + + $(DefineConstants);ASYNC_MEMORY + + + + + + + + + + + @@ -24,6 +38,9 @@ + + + \ No newline at end of file diff --git a/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientProxy.cs b/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientProxy.cs index 777e3dbc..e2c73350 100644 --- a/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientProxy.cs +++ b/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientProxy.cs @@ -1,4 +1,4 @@ -#if !NETSTANDARD2_0 +#if !(NETSTANDARD2_0 || NETSTANDARD2_1) using System; using System.Reflection; using System.Runtime.Remoting.Messaging; @@ -7,7 +7,7 @@ namespace ServiceStack.Redis.Support.Diagnostic { /// - /// Dynamically proxies access to the IRedisClient providing events for before & after each method invocation + /// Dynamically proxies access to the IRedisClient providing events for before & after each method invocation /// public class TrackingRedisClientProxy : System.Runtime.Remoting.Proxies.RealProxy { diff --git a/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientsManager.cs b/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientsManager.cs index 7aca6db2..d2408905 100644 --- a/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientsManager.cs +++ b/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientsManager.cs @@ -1,4 +1,4 @@ -#if !NETSTANDARD2_0 +#if !(NETSTANDARD2_0 || NETSTANDARD2_1) using System; using System.Collections.Generic; using System.Diagnostics; @@ -15,7 +15,7 @@ namespace ServiceStack.Redis.Support.Diagnostic /// Tracks each IRedisClient instance allocated from the IRedisClientsManager logging when they are allocated and disposed. /// Periodically writes the allocated instances to the log for diagnostic purposes. /// - public class TrackingRedisClientsManager : IRedisClientsManager + public partial class TrackingRedisClientsManager : IRedisClientsManager { private static readonly ILog Logger = LogManager.GetLogger(typeof(TrackingRedisClientsManager)); diff --git a/src/ServiceStack.Redis/Support/Locking/DistributedLock.Async.cs b/src/ServiceStack.Redis/Support/Locking/DistributedLock.Async.cs new file mode 100644 index 00000000..7c32db0c --- /dev/null +++ b/src/ServiceStack.Redis/Support/Locking/DistributedLock.Async.cs @@ -0,0 +1,121 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Support.Locking +{ + partial class DistributedLock : IDistributedLockAsync + { + public IDistributedLockAsync AsAsync() => this; + + async ValueTask IDistributedLockAsync.LockAsync(string key, int acquisitionTimeout, int lockTimeout, IRedisClientAsync client, CancellationToken cancellationToken) + { + long lockExpire = 0; + + // cannot lock on a null key + if (key == null) + return new LockState(LOCK_NOT_ACQUIRED, lockExpire); + + const int sleepIfLockSet = 200; + acquisitionTimeout *= 1000; //convert to ms + int tryCount = (acquisitionTimeout / sleepIfLockSet) + 1; + + var ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)); + var newLockExpire = CalculateLockExpire(ts, lockTimeout); + + var nativeClient = (IRedisNativeClientAsync)client; + long wasSet = await nativeClient.SetNXAsync(key, BitConverter.GetBytes(newLockExpire), cancellationToken).ConfigureAwait(false); + int totalTime = 0; + while (wasSet == LOCK_NOT_ACQUIRED && totalTime < acquisitionTimeout) + { + int count = 0; + while (wasSet == 0 && count < tryCount && totalTime < acquisitionTimeout) + { + await Task.Delay(sleepIfLockSet).ConfigureAwait(false); + totalTime += sleepIfLockSet; + ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)); + newLockExpire = CalculateLockExpire(ts, lockTimeout); + wasSet = await nativeClient.SetNXAsync(key, BitConverter.GetBytes(newLockExpire), cancellationToken).ConfigureAwait(false); + count++; + } + // acquired lock! + if (wasSet != LOCK_NOT_ACQUIRED) break; + + // handle possibliity of crashed client still holding the lock + var pipe = client.CreatePipeline(); + await using (pipe.ConfigureAwait(false)) + { + long lockValue = 0; + pipe.QueueCommand(r => ((IRedisNativeClientAsync)r).WatchAsync(new[] { key }, cancellationToken)); + pipe.QueueCommand(r => ((IRedisNativeClientAsync)r).GetAsync(key, cancellationToken), x => lockValue = (x != null) ? BitConverter.ToInt64(x, 0) : 0); + await pipe.FlushAsync(cancellationToken).ConfigureAwait(false); + + // if lock value is 0 (key is empty), or expired, then we can try to acquire it + ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)); + if (lockValue < ts.TotalSeconds) + { + ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)); + newLockExpire = CalculateLockExpire(ts, lockTimeout); + var trans = await client.CreateTransactionAsync(cancellationToken).ConfigureAwait(false); + await using (trans.ConfigureAwait(false)) + { + var expire = newLockExpire; + trans.QueueCommand(r => ((IRedisNativeClientAsync)r).SetAsync(key, BitConverter.GetBytes(expire), cancellationToken: cancellationToken)); + if (await trans.CommitAsync(cancellationToken).ConfigureAwait(false)) + wasSet = LOCK_RECOVERED; //recovered lock! + } + } + else + { + await nativeClient.UnWatchAsync(cancellationToken).ConfigureAwait(false); + } + } + if (wasSet != LOCK_NOT_ACQUIRED) break; + await Task.Delay(sleepIfLockSet).ConfigureAwait(false); + totalTime += sleepIfLockSet; + } + if (wasSet != LOCK_NOT_ACQUIRED) + { + lockExpire = newLockExpire; + } + return new LockState(wasSet, lockExpire); + } + + async ValueTask IDistributedLockAsync.UnlockAsync(string key, long lockExpire, IRedisClientAsync client, CancellationToken cancellationToken) + { + if (lockExpire <= 0) + return false; + long lockVal = 0; + var nativeClient = (IRedisNativeClientAsync)client; + var pipe = client.CreatePipeline(); + await using (pipe.ConfigureAwait(false)) + { + pipe.QueueCommand(r => ((IRedisNativeClientAsync)r).WatchAsync(new[] { key }, cancellationToken)); + pipe.QueueCommand(r => ((IRedisNativeClientAsync)r).GetAsync(key, cancellationToken), + x => lockVal = (x != null) ? BitConverter.ToInt64(x, 0) : 0); + await pipe.FlushAsync(cancellationToken).ConfigureAwait(false); + } + + if (lockVal != lockExpire) + { + if (lockVal != 0) + Debug.WriteLine($"Unlock(): Failed to unlock key {key}; lock has been acquired by another client "); + else + Debug.WriteLine($"Unlock(): Failed to unlock key {key}; lock has been identifed as a zombie and harvested "); + await nativeClient.UnWatchAsync(cancellationToken).ConfigureAwait(false); + return false; + } + + var trans = await client.CreateTransactionAsync(cancellationToken).ConfigureAwait(false); + await using (trans.ConfigureAwait(false)) + { + trans.QueueCommand(r => ((IRedisNativeClientAsync)r).DelAsync(key, cancellationToken)); + var rc = await trans.CommitAsync(cancellationToken).ConfigureAwait(false); + if (!rc) + Debug.WriteLine($"Unlock(): Failed to delete key {key}; lock has been acquired by another client "); + return rc; + } + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Support/Locking/DistributedLock.cs b/src/ServiceStack.Redis/Support/Locking/DistributedLock.cs index 296ee02a..84d1be68 100644 --- a/src/ServiceStack.Redis/Support/Locking/DistributedLock.cs +++ b/src/ServiceStack.Redis/Support/Locking/DistributedLock.cs @@ -3,7 +3,7 @@ namespace ServiceStack.Redis.Support.Locking { - public class DistributedLock : IDistributedLock + public partial class DistributedLock : IDistributedLock { public const int LOCK_NOT_ACQUIRED = 0; public const int LOCK_ACQUIRED = 1; diff --git a/src/ServiceStack.Redis/Support/Locking/IDistributedLock.Async.cs b/src/ServiceStack.Redis/Support/Locking/IDistributedLock.Async.cs new file mode 100644 index 00000000..c658a00e --- /dev/null +++ b/src/ServiceStack.Redis/Support/Locking/IDistributedLock.Async.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Support.Locking +{ + /// + /// Distributed lock interface + /// + public interface IDistributedLockAsync + { + // note: can't use "out" with async, so return LockState instead + ValueTask LockAsync(string key, int acquisitionTimeout, int lockTimeout, IRedisClientAsync client, CancellationToken cancellationToken = default); + ValueTask UnlockAsync(string key, long lockExpire, IRedisClientAsync client, CancellationToken cancellationToken = default); + } + + public readonly struct LockState + { + public long Result { get; } // kinda feels like this should be an enum; leaving alone for API parity (sync vs async) + public long Expiration { get; } + public LockState(long result, long expiration) + { + Result = result; + Expiration = expiration; + } + public override bool Equals(object obj) => throw new NotSupportedException(); + public override int GetHashCode() => throw new NotSupportedException(); + public override string ToString() => nameof(LockState); + + public void Deconstruct(out long result, out long expiration) + { + result = Result; + expiration = Expiration; + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Support/Queue/Implementation/SerializingRedisClient.cs b/src/ServiceStack.Redis/Support/Queue/Implementation/SerializingRedisClient.cs index 2217e173..e53c28cf 100644 --- a/src/ServiceStack.Redis/Support/Queue/Implementation/SerializingRedisClient.cs +++ b/src/ServiceStack.Redis/Support/Queue/Implementation/SerializingRedisClient.cs @@ -61,10 +61,10 @@ public object Deserialize(byte[] someBytes) return serializer.Deserialize(someBytes); } + /// /// deserialize an array of byte arrays /// /// - /// public IList Deserialize(byte[][] byteArray) { IList rc = new ArrayList(); diff --git a/src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs b/src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs new file mode 100644 index 00000000..2ae1858c --- /dev/null +++ b/src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs @@ -0,0 +1,129 @@ +// +// https://github.com/ServiceStack/ServiceStack.Redis +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of ServiceStack. +// + +using System; +using System.Threading; +using System.Threading.Tasks; +using ServiceStack.Redis.Pipeline; + +namespace ServiceStack.Redis +{ + /// + /// Adds support for Redis Transactions (i.e. MULTI/EXEC/DISCARD operations). + /// + public partial class RedisTransaction + : IRedisTransactionAsync, IRedisQueueCompletableOperationAsync + { + /// + /// Issue exec command (not queued) + /// + private async ValueTask ExecAsync(CancellationToken cancellationToken) + { + RedisClient.Exec(); + await RedisClient.FlushSendBufferAsync(cancellationToken).ConfigureAwait(false); + RedisClient.ResetSendBuffer(); + } + + + /// + /// Put "QUEUED" messages at back of queue + /// + partial void QueueExpectQueuedAsync() + { + QueuedCommands.Insert(0, new QueuedRedisOperation + { + }.WithAsyncReadCommand(RedisClient.ExpectQueuedAsync)); + } + + async ValueTask IRedisTransactionAsync.CommitAsync(CancellationToken cancellationToken) + { + bool rc = true; + try + { + numCommands = QueuedCommands.Count / 2; + + //insert multi command at beginning + QueuedCommands.Insert(0, new QueuedRedisCommand + { + }.WithAsyncReturnCommand(VoidReturnCommandAsync: r => { Init(); return default; }) + .WithAsyncReadCommand(RedisClient.ExpectOkAsync)); + + //the first half of the responses will be "QUEUED", + // so insert reading of multiline after these responses + QueuedCommands.Insert(numCommands + 1, new QueuedRedisOperation + { + OnSuccessIntCallback = handleMultiDataResultCount + }.WithAsyncReadCommand(RedisClient.ReadMultiDataResultCountAsync)); + + // add Exec command at end (not queued) + QueuedCommands.Add(new RedisCommand + { + }.WithAsyncReturnCommand(r => ExecAsync(cancellationToken))); + + //execute transaction + await ExecAsync(cancellationToken).ConfigureAwait(false); + + //receive expected results + foreach (var queuedCommand in QueuedCommands) + { + await queuedCommand.ProcessResultAsync(cancellationToken).ConfigureAwait(false); + } + } + catch (RedisTransactionFailedException) + { + rc = false; + } + finally + { + RedisClient.Transaction = null; + ClosePipeline(); + await RedisClient.AddTypeIdsRegisteredDuringPipelineAsync(cancellationToken).ConfigureAwait(false); + } + return rc; + } + + ValueTask IRedisTransactionAsync.RollbackAsync(CancellationToken cancellationToken) + { + Rollback(); // not currently anything different to do on the async path + return default; + } + // note: this also means that Dispose doesn't need to be complex; if Rollback needed + // splitting, we would need to override DisposeAsync and split the code, too + + + private protected override async ValueTask ReplayAsync(CancellationToken cancellationToken) + { + bool rc = true; + try + { + await ExecuteAsync().ConfigureAwait(false); + + //receive expected results + foreach (var queuedCommand in QueuedCommands) + { + await queuedCommand.ProcessResultAsync(cancellationToken).ConfigureAwait(false); + } + } + catch (RedisTransactionFailedException) + { + rc = false; + } + finally + { + RedisClient.Transaction = null; + ClosePipeline(); + await RedisClient.AddTypeIdsRegisteredDuringPipelineAsync(cancellationToken).ConfigureAwait(false); + } + return rc; + } + } +} \ No newline at end of file diff --git a/src/ServiceStack.Redis/Transaction/RedisTransaction.cs b/src/ServiceStack.Redis/Transaction/RedisTransaction.cs index 346759cb..baedbe36 100644 --- a/src/ServiceStack.Redis/Transaction/RedisTransaction.cs +++ b/src/ServiceStack.Redis/Transaction/RedisTransaction.cs @@ -18,12 +18,20 @@ namespace ServiceStack.Redis /// /// Adds support for Redis Transactions (i.e. MULTI/EXEC/DISCARD operations). /// - public class RedisTransaction + public partial class RedisTransaction : RedisAllPurposePipeline, IRedisTransaction, IRedisQueueCompletableOperation { private int numCommands = 0; public RedisTransaction(RedisClient redisClient) - : base(redisClient) {} + : this(redisClient, false) {} + + internal RedisTransaction(RedisClient redisClient, bool isAsync) + : base(redisClient) + { + // if someone casts between sync/async: the sync-over-async or + // async-over-sync is entirely self-inflicted; I can't fix stupid + _isAsync = isAsync; + } protected override void Init() { @@ -161,10 +169,19 @@ public override void Dispose() Rollback(); } + private readonly bool _isAsync; protected override void AddCurrentQueuedOperation() { base.AddCurrentQueuedOperation(); - QueueExpectQueued(); + if (_isAsync) + { + QueueExpectQueuedAsync(); + } + else + { + QueueExpectQueued(); + } } + partial void QueueExpectQueuedAsync(); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/ValueTask_Utils.Async.cs b/src/ServiceStack.Redis/ValueTask_Utils.Async.cs new file mode 100644 index 00000000..cc48d28e --- /dev/null +++ b/src/ServiceStack.Redis/ValueTask_Utils.Async.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Internal +{ + internal static class ValueTask_Utils + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ValueTask Await(this ValueTask pending) + { + if (pending.IsCompletedSuccessfully) + { + _ = pending.Result; // for IValueTaskSource reasons + return default; + } + else + { + return Awaited(pending); + } + async static ValueTask Awaited(ValueTask pending) + => await pending.ConfigureAwait(false); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ValueTask Await(this ValueTask pending, Func projection) + { + return pending.IsCompletedSuccessfully ? projection(pending.Result).AsValueTask() : Awaited(pending, projection); + async static ValueTask Awaited(ValueTask pending, Func projection) + => projection(await pending.ConfigureAwait(false)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ValueTask AsInt32(this ValueTask pending) + { + return pending.IsCompletedSuccessfully ? (checked((int)pending.Result)).AsValueTask() : Awaited(pending); + async static ValueTask Awaited(ValueTask pending) + => checked((int)await pending.ConfigureAwait(false)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ValueTask Await(this ValueTask pending, Func projection, TState state) + { + return pending.IsCompletedSuccessfully ? projection(pending.Result, state).AsValueTask() : Awaited(pending, projection, state); + async static ValueTask Awaited(ValueTask pending, Func projection, TState state) + => projection(await pending.ConfigureAwait(false), state); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ValueTask AwaitAsTrue(this ValueTask pending) + { + if (pending.IsCompletedSuccessfully) + { + pending.GetAwaiter().GetResult(); // for IValueTaskSource reasons + return s_ValueTaskTrue; + } + else + { + return Awaited(pending); + } + async static ValueTask Awaited(ValueTask pending) + { + await pending.ConfigureAwait(false); + return true; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ValueTask AwaitAsTrue(this ValueTask pending) + { + if (pending.IsCompletedSuccessfully) + { + _ = pending.Result; // for IValueTaskSource reasons + return s_ValueTaskTrue; + } + else + { + return Awaited(pending); + } + async static ValueTask Awaited(ValueTask pending) + { + await pending.ConfigureAwait(false); + return true; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ValueTask IsSuccessAsync(this ValueTask pending) + { + return pending.IsCompletedSuccessfully ? (pending.Result == RedisNativeClient.Success).AsValueTask() : Awaited(pending); + async static ValueTask Awaited(ValueTask pending) + => (await pending.ConfigureAwait(false)) == RedisNativeClient.Success; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ValueTask> ConvertEachToAsync(this ValueTask> pending) + { + return pending.IsCompletedSuccessfully ? pending.Result.ConvertEachTo().AsValueTask() : Awaited(pending); + static async ValueTask> Awaited(ValueTask> pending) + => (await pending.ConfigureAwait(false)).ConvertEachTo(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ValueTask> ToStringListAsync(this ValueTask pending) + { + return pending.IsCompletedSuccessfully ? pending.Result.ToStringList().AsValueTask() : Awaited(pending); + static async ValueTask> Awaited(ValueTask pending) + => (await pending.ConfigureAwait(false)).ToStringList(); + } + + private static readonly ValueTask s_ValueTaskTrue = true.AsValueTask(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ValueTask Await(this ValueTask pending, T result) + { + return pending.IsCompletedSuccessfully ? result.AsValueTask() : Awaited(pending, result); + async static ValueTask Awaited(ValueTask pending, T result) + { + await pending.ConfigureAwait(false); + return result; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ValueTask AsValueTask(this T value) => new ValueTask(value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ValueTask FromUtf8BytesAsync(this ValueTask pending) + { + return pending.IsCompletedSuccessfully ? pending.Result.FromUtf8Bytes().AsValueTask() : Awaited(pending); + static async ValueTask Awaited(ValueTask pending) + => (await pending.ConfigureAwait(false)).FromUtf8Bytes(); + } + } +} diff --git a/tests/ServiceStack.Redis.Benchmark/IncrBenchmarks.cs b/tests/ServiceStack.Redis.Benchmark/IncrBenchmarks.cs new file mode 100644 index 00000000..b39aa825 --- /dev/null +++ b/tests/ServiceStack.Redis.Benchmark/IncrBenchmarks.cs @@ -0,0 +1,281 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Order; +using Pipelines.Sockets.Unofficial; +using Respite; +using StackExchange.Redis; +using System.Linq; +using System.Net.Sockets; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Benchmark +{ + [SimpleJob(RuntimeMoniker.Net472)] + [SimpleJob(RuntimeMoniker.NetCoreApp31)] + [MemoryDiagnoser] + [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] + [Orderer(SummaryOrderPolicy.Method, MethodOrderPolicy.Alphabetical)] + [CategoriesColumn] + public class IncrBenchmarks + { + ConnectionMultiplexer _seredis; + IServer _seredis_server; + IDatabase _seredis_db; + RedisClient _ssredis; + IRedisClientAsync _ssAsync; + RespConnection _respite; + + static IncrBenchmarks() + { + RedisClient.NewFactoryFn = () => new RedisClient("127.0.0.1", 6379); + } + + [GlobalSetup] + public Task Setup() => Setup(false); + internal async Task Setup(bool minimal) + { + _ssredis = RedisClient.New(); + _ssAsync = _ssredis; + + if (!minimal) + { + _seredis = await ConnectionMultiplexer.ConnectAsync("127.0.0.1:6379"); + _seredis_server = _seredis.GetServer(_seredis.GetEndPoints().Single()); + _seredis_db = _seredis.GetDatabase(); + + var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + SocketConnection.SetRecommendedClientOptions(socket); + socket.Connect("127.0.0.1", 6379); + + _respite = RespConnection.Create(socket); + } + } + + [GlobalCleanup] + public async Task Teardown() + { + _seredis?.Dispose(); + _ssredis?.Dispose(); + if (_respite != null) await _respite.DisposeAsync(); + + _seredis_server = null; + _seredis_db = null; + _seredis = null; + _ssredis = null; + _respite = null; + _ssAsync = null; + } + + const string Key = "my_key"; +#if DEBUG + const int PER_TEST = 10; +#else + const int PER_TEST = 1000; +#endif + + [BenchmarkCategory("IncrAsync")] + [Benchmark(Description = "SERedis", OperationsPerInvoke = PER_TEST)] + public async Task SERedisIncrAsync() + { + long last = default; + await _seredis_db.KeyDeleteAsync(Key); + for (int i = 0; i < PER_TEST; i++) + { + last = await _seredis_db.StringIncrementAsync(Key); + } + return last; + } + + [BenchmarkCategory("IncrSync")] + [Benchmark(Description = "SERedis", OperationsPerInvoke = PER_TEST)] + public long SERedisIncrSync() + { + long last = default; + _seredis_db.KeyDelete(Key); + for (int i = 0; i < PER_TEST; i++) + { + last = _seredis_db.StringIncrement(Key); + } + return last; + } + + [BenchmarkCategory("PipelineIncrAsync")] + [Benchmark(Description = "SERedis", OperationsPerInvoke = PER_TEST)] + public async Task SERedisPipelineIncrAsync() + { + var last = Task.FromResult(0L); + await _seredis_db.KeyDeleteAsync(Key); + var batch = _seredis_db.CreateBatch(); + for (int i = 0; i < PER_TEST; i++) + { + last = batch.StringIncrementAsync(Key); + } + batch.Execute(); + return await last; + } + + [BenchmarkCategory("TransactionIncrAsync")] + [Benchmark(Description = "SERedis", OperationsPerInvoke = PER_TEST)] + public async Task SERedisTransactionIncrAsync() + { + var last = Task.FromResult(0L); + await _seredis_db.KeyDeleteAsync(Key); + var batch = _seredis_db.CreateTransaction(); + for (int i = 0; i < PER_TEST; i++) + { + last = batch.StringIncrementAsync(Key); + } + await batch.ExecuteAsync(); + return await last; + } + + [BenchmarkCategory("TransactionIncrSync")] + [Benchmark(Description = "SERedis", OperationsPerInvoke = PER_TEST)] + public async Task SERedisTransactionIncrSync() + { + var last = Task.FromResult(0L); + _seredis_db.KeyDelete(Key); + var batch = _seredis_db.CreateTransaction(); + for (int i = 0; i < PER_TEST; i++) + { + last = batch.StringIncrementAsync(Key); + } + batch.Execute(); + return await last; + } + + [BenchmarkCategory("IncrAsync")] + [Benchmark(Description = "SSRedis", OperationsPerInvoke = PER_TEST)] + public async Task SSRedisIncrAsync() + { + long last = default; + _ssredis.Del(Key); // todo: asyncify + for (int i = 0; i < PER_TEST; i++) + { + last = await _ssAsync.IncrementValueAsync(Key); + } + return last; + } + + + [BenchmarkCategory("IncrSync")] + [Benchmark(Description = "SSRedis", OperationsPerInvoke = PER_TEST)] + public long SSRedisIncrSync() + { + long last = default; + _ssredis.Del(Key); + for (int i = 0; i < PER_TEST; i++) + { + last = _ssredis.IncrementValue(Key); + } + return last; + } + + [BenchmarkCategory("PipelineIncrSync")] + [Benchmark(Description = "SSRedis", OperationsPerInvoke = PER_TEST)] + public long SSRedisPipelineIncrSync() + { + long last = default; + _ssredis.Del(Key); + using var trans = _ssredis.CreatePipeline(); + for (int i = 0; i < PER_TEST; i++) + { + trans.QueueCommand(r => r.IncrementValue(Key), l => last = l); + } + trans.Flush(); + return last; + } + + [BenchmarkCategory("TransactionIncrSync")] + [Benchmark(Description = "SSRedis", OperationsPerInvoke = PER_TEST)] + public long SSRedisTransactionIncrSync() + { + long last = default; + _ssredis.Del(Key); + using var trans = _ssredis.CreateTransaction(); + for (int i = 0; i < PER_TEST; i++) + { + trans.QueueCommand(r => r.IncrementValue(Key), l => last = l); + } + trans.Commit(); + return last; + } + + [BenchmarkCategory("PipelineIncrAsync")] + [Benchmark(Description = "SSRedis", OperationsPerInvoke = PER_TEST)] + public async Task SSRedisPipelineIncrAsync() + { + long last = default; + _ssredis.Del(Key); // todo: asyncify + await using var trans = _ssAsync.CreatePipeline(); + for (int i = 0; i < PER_TEST; i++) + { + trans.QueueCommand(r => r.IncrementValueAsync(Key), l => last = l); + } + await trans.FlushAsync(); + return last; + } + + [BenchmarkCategory("TransactionIncrAsync")] + [Benchmark(Description = "SSRedis", OperationsPerInvoke = PER_TEST)] + public async Task SSRedisTransactionIncrAsync() + { + long last = default; + _ssredis.Del(Key); // todo: asyncify + await using var trans = await _ssAsync.CreateTransactionAsync(); + for (int i = 0; i < PER_TEST; i++) + { + trans.QueueCommand(r => r.IncrementValueAsync(Key), l => last = l); + } + await trans.CommitAsync(); + return last; + } + + + //static readonly RespValue s_Time = RespValue.CreateAggregate( + // RespType.Array, RespValue.Create(RespType.BlobString, "time")); + + //static DateTime ParseTime(in RespValue value) + //{ + // var parts = value.SubItems; + // if (parts.TryGetSingleSpan(out var span)) + // return Parse(span[0], span[1]); + // return Slow(parts); + // static DateTime Slow(in ReadOnlyBlock parts) + // { + // var iter = parts.GetEnumerator(); + // if (!iter.MoveNext()) Throw(); + // var seconds = iter.Current; + // if (!iter.MoveNext()) Throw(); + // var microseconds = iter.Current; + // return Parse(seconds, microseconds); + // static void Throw() => throw new InvalidOperationException(); + // } + + // static DateTime Parse(in RespValue seconds, in RespValue microseconds) + // => Epoch.AddSeconds(seconds.ToInt64()).AddMilliseconds(microseconds.ToInt64() / 1000.0); + //} + //static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + //[BenchmarkCategory("IncrSync")] + //[Benchmark(Description = "Respite", OperationsPerInvoke = PER_TEST)] + //public void RespiteTimeSync() + //{ + // for (int i = 0; i < PER_TEST; i++) + // { + // _respite.Call(s_Time, val => ParseTime(val)); + // } + //} + + //[BenchmarkCategory("IncrAsync")] + //[Benchmark(Description = "Respite", OperationsPerInvoke = PER_TEST)] + //public async Task RespiteTimeAsync() + //{ + // for (int i = 0; i < PER_TEST; i++) + // { + // await _respite.CallAsync(s_Time, val => ParseTime(val)); + // } + //} + } +} diff --git a/tests/ServiceStack.Redis.Benchmark/Program.cs b/tests/ServiceStack.Redis.Benchmark/Program.cs new file mode 100644 index 00000000..84c48100 --- /dev/null +++ b/tests/ServiceStack.Redis.Benchmark/Program.cs @@ -0,0 +1,39 @@ +using BenchmarkDotNet.Running; +using System.Threading.Tasks; +using System; +namespace ServiceStack.Redis.Benchmark +{ + class Program + { +#if DEBUG + static async Task Main() + { + var obj = new IncrBenchmarks(); + try + { + await obj.Setup(false); + + Console.WriteLine(obj.SERedisIncrSync()); + Console.WriteLine(await obj.SERedisIncrAsync()); + Console.WriteLine(await obj.SERedisPipelineIncrAsync()); + Console.WriteLine(await obj.SERedisTransactionIncrAsync()); + Console.WriteLine(await obj.SERedisTransactionIncrSync()); + + Console.WriteLine(obj.SSRedisIncrSync()); + Console.WriteLine(obj.SSRedisPipelineIncrSync()); + Console.WriteLine(obj.SSRedisTransactionIncrSync()); + Console.WriteLine(await obj.SSRedisIncrAsync()); + Console.WriteLine(await obj.SSRedisPipelineIncrAsync()); + Console.WriteLine(await obj.SSRedisTransactionIncrAsync()); + } + finally + { + await obj.Teardown(); + } + } +#else + static void Main(string[] args) + => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); +#endif + } +} diff --git a/tests/ServiceStack.Redis.Benchmark/ServiceStack.Redis.Benchmark.csproj b/tests/ServiceStack.Redis.Benchmark/ServiceStack.Redis.Benchmark.csproj new file mode 100644 index 00000000..3c0feb32 --- /dev/null +++ b/tests/ServiceStack.Redis.Benchmark/ServiceStack.Redis.Benchmark.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp3.1;net472 + 8 + + + + + + + + + + + + diff --git a/tests/ServiceStack.Redis.Tests/AdhocClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/AdhocClientTests.Async.cs new file mode 100644 index 00000000..ebd418d9 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/AdhocClientTests.Async.cs @@ -0,0 +1,25 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Integration")] + public class AdhocClientTestsAsync + { + [Test] + public async Task Search_Test() + { + await using (var client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + { + const string cacheKey = "urn+metadata:All:SearchProProfiles?SwanShinichi Osawa /0/8,0,0,0"; + const long value = 1L; + await client.SetAsync(cacheKey, value); + var result = await client.GetAsync(cacheKey); + + Assert.That(result, Is.EqualTo(value)); + } + } + + // remaining tests from parent do not touch redis + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs b/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs new file mode 100644 index 00000000..52cd5c27 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs @@ -0,0 +1,865 @@ +// Copyright (c) Service Stack LLC. All Rights Reserved. +// License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt + +using NUnit.Framework; +using ServiceStack.Caching; +using ServiceStack.Data; +using ServiceStack.Model; +using ServiceStack.Redis.Generic; +using ServiceStack.Redis.Pipeline; +using ServiceStack.Redis.Support.Locking; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing.Text; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + // verify that anything that implements IFoo also implements IFooAsync + [Category("Async")] + public class AsyncImplementationTests + { + private static readonly Type[] AllTypes + = typeof(RedisClient).Assembly.GetTypes() + .Concat(typeof(AsyncImplementationTests).Assembly.GetTypes()) + .Where(x => x.IsClass) + .OrderBy(x => x.FullName).ToArray(); + + private string Log(string message) + { + TestContext.Out.WriteLine(message); + return message; + } + + [TestCase(typeof(ICacheClient), typeof(ICacheClientAsync))] + [TestCase(typeof(ICacheClientExtended), typeof(ICacheClientExtendedAsync))] + [TestCase(typeof(IEntityStore), typeof(IEntityStoreAsync))] + [TestCase(typeof(IEntityStore<>), typeof(IEntityStoreAsync<>))] + [TestCase(typeof(IRedisClient), typeof(IRedisClientAsync))] + + [TestCase(typeof(IRedisClientsManager), typeof(IRedisClientsManagerAsync))] + [TestCase(typeof(IRedisNativeClient), typeof(IRedisNativeClientAsync))] + [TestCase(typeof(IRedisPipeline), typeof(IRedisPipelineAsync))] + [TestCase(typeof(IRedisPipelineShared), typeof(IRedisPipelineSharedAsync))] + [TestCase(typeof(IRedisQueueableOperation), typeof(IRedisQueueableOperationAsync))] + + [TestCase(typeof(IRedisQueueCompletableOperation), typeof(IRedisQueueCompletableOperationAsync))] + [TestCase(typeof(IRedisTransaction), typeof(IRedisTransactionAsync))] + [TestCase(typeof(IRedisTransactionBase), typeof(IRedisTransactionBaseAsync))] + [TestCase(typeof(IRedisTypedClient<>), typeof(IRedisTypedClientAsync<>))] + [TestCase(typeof(IRemoveByPattern), typeof(IRemoveByPatternAsync))] + + [TestCase(typeof(IDistributedLock), typeof(IDistributedLockAsync))] + [TestCase(typeof(IRedisSubscription), typeof(IRedisSubscriptionAsync))] + [TestCase(typeof(IRedisHash), typeof(IRedisHashAsync))] + [TestCase(typeof(IRedisSortedSet), typeof(IRedisSortedSetAsync))] + [TestCase(typeof(IRedisSet), typeof(IRedisSetAsync))] + + [TestCase(typeof(IRedisList), typeof(IRedisListAsync))] + [TestCase(typeof(IRedisHash<,>), typeof(IRedisHashAsync<,>))] + [TestCase(typeof(IRedisSortedSet<>), typeof(IRedisSortedSetAsync<>))] + [TestCase(typeof(IRedisSet<>), typeof(IRedisSetAsync<>))] + [TestCase(typeof(IRedisList<>), typeof(IRedisListAsync<>))] + + [TestCase(typeof(IRedisTypedPipeline<>), typeof(IRedisTypedPipelineAsync<>))] + [TestCase(typeof(IRedisTypedQueueableOperation<>), typeof(IRedisTypedQueueableOperationAsync<>))] + [TestCase(typeof(IRedisTypedTransaction<>), typeof(IRedisTypedTransactionAsync<>))] + + public void TestSameAPI(Type syncInterface, Type asyncInterface) + { + TestContext.Out.WriteLine($"Comparing '{GetCSharpTypeName(syncInterface)}' and '{GetCSharpTypeName(asyncInterface)}'..."); + + var actual = new List(); + foreach (var method in asyncInterface.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + var tok = new MethodToken(method); + actual.Add(GetSignature(tok)); + } + + var expected = new List(); + ParameterToken cancellationToken = new ParameterToken("cancellationToken", typeof(CancellationToken), ParameterAttributes.Optional); + foreach (var method in syncInterface.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + AddExpected(method); + } + if (asyncInterface == typeof(IRedisSortedSetAsync) + || asyncInterface == typeof(IRedisSetAsync) + || asyncInterface == typeof(IRedisListAsync)) + { + AddFrom(typeof(ICollection), nameof(ICollection.Clear)); + AddFrom(typeof(ICollection), nameof(ICollection.Add)); + AddFrom(typeof(ICollection), nameof(ICollection.Remove)); + AddFrom(typeof(ICollection), nameof(ICollection.Contains)); + AddFrom(typeof(ICollection), "get_" + nameof(ICollection.Count), true); + + if (asyncInterface == typeof(IRedisListAsync)) + { + AddFrom(typeof(IList), nameof(IList.IndexOf)); + AddFrom(typeof(IList), nameof(IList.RemoveAt)); + AddFrom(typeof(IList), "set_Item", true); + AddFrom(typeof(IList), "get_Item", true); + } + } + else if (asyncInterface == typeof(IRedisSortedSetAsync<>) + || asyncInterface == typeof(IRedisSetAsync<>) + || asyncInterface == typeof(IRedisListAsync<>)) + { + AddFrom(typeof(ICollection<>), nameof(ICollection.Clear)); + AddFrom(typeof(ICollection<>), nameof(ICollection.Add)); + AddFrom(typeof(ICollection<>), nameof(ICollection.Remove)); + AddFrom(typeof(ICollection<>), nameof(ICollection.Contains)); + AddFrom(typeof(ICollection<>), "get_" + nameof(ICollection.Count), true); + + if (asyncInterface == typeof(IRedisListAsync<>)) + { + AddFrom(typeof(IList<>), nameof(IList.IndexOf)); + AddFrom(typeof(IList<>), nameof(IList.RemoveAt)); + AddFrom(typeof(IList<>), "set_Item", true); + AddFrom(typeof(IList<>), "get_Item", true); + } + } + else if (asyncInterface == typeof(IRedisHashAsync<,>)) + { + AddFrom(typeof(ICollection<>).MakeGenericType(typeof(KeyValuePair<,>).MakeGenericType(asyncInterface.GetGenericArguments())), nameof(IDictionary.Add)); + AddFrom(typeof(IDictionary<,>), nameof(IDictionary.Add)); + AddFrom(typeof(ICollection<>), nameof(IDictionary.Clear)); + AddFrom(typeof(IDictionary<,>), nameof(IDictionary.ContainsKey)); + AddFrom(typeof(IDictionary<,>), nameof(IDictionary.Remove)); + AddFrom(typeof(ICollection<>), "get_" + nameof(IDictionary.Count), true); + } + else if (asyncInterface == typeof(IRedisHashAsync)) + { + AddFrom(typeof(ICollection>), nameof(IDictionary.Add)); + AddFrom(typeof(IDictionary), nameof(IDictionary.Add)); + AddFrom(typeof(ICollection), nameof(IDictionary.Clear)); + AddFrom(typeof(IDictionary), nameof(IDictionary.ContainsKey)); + AddFrom(typeof(IDictionary), nameof(IDictionary.Remove)); + AddFrom(typeof(ICollection), "get_" + nameof(IDictionary.Count), true); + } + else if (asyncInterface == typeof(IRedisNativeClientAsync)) + { + AddFrom(typeof(RedisClient), nameof(RedisClient.SlowlogReset)); + AddFrom(typeof(RedisClient), nameof(RedisClient.BitCount)); + AddFromTyped(typeof(RedisClient), nameof(RedisClient.ZCount), typeof(string), typeof(double), typeof(double)); + // can't expose as SlowlogItem because of interface locations + expected.Add("ValueTask SlowlogGetAsync(int? top = default, CancellationToken cancellationToken = default)"); + // adding missing "exists" capability + expected.Add("ValueTask SetAsync(string key, byte[] value, bool exists, long expirySeconds = 0, long expiryMilliseconds = 0, CancellationToken cancellationToken = default)"); + } + else if (asyncInterface == typeof(IRedisClientAsync)) + { + expected.Add("ValueTask GetSlowlogAsync(int? numberOfRecords = default, CancellationToken cancellationToken = default)"); + expected.Add("ValueTask SlowlogResetAsync(CancellationToken cancellationToken = default)"); + } + + void AddFrom(Type syncInterface, string name, bool fromPropertyToMethod = false) + => AddExpected(syncInterface.GetMethod(name), fromPropertyToMethod); + void AddFromTyped(Type syncInterface, string name, params Type[] types) + => AddExpected(syncInterface.GetMethod(name, types), false); + + void AddExpected(MethodInfo method, bool fromPropertyToMethod = false) + { + if (method is null) return; + var tok = new MethodToken(method); + + ParameterToken[] parameters = tok.GetParameters(); + + // think about the return type + Type returnType; + if (tok.ReturnType == typeof(void)) + { + returnType = typeof(ValueTask); + } + else if (tok.ReturnType == typeof(IDisposable)) + { + returnType = typeof(IAsyncDisposable); + } + else if (tok.ReturnType.IsGenericType && tok.ReturnType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + returnType = typeof(IAsyncEnumerable<>).MakeGenericType(tok.ReturnType.GetGenericArguments()); + } + else + { + returnType = typeof(ValueTask<>).MakeGenericType(SwapForAsyncIfNeedeed(tok.ReturnType)); + } + string name = tok.Name + "Async"; + bool addCancellation = true; + // sniff to see if this is a delegate hook + if (parameters.Length == 0 && typeof(Delegate).IsAssignableFrom(tok.ReturnType) && name.StartsWith("get_")) + { + // property getter; replace with event add + returnType = typeof(void); + name = "add_" + name.Substring(4); + parameters = new[] { new ParameterToken("value", ActionDelegateToFunc(tok.ReturnType), default) }; + + } + else if (parameters.Length == 1 && tok.ReturnType == typeof(void) && name.StartsWith("set_") + && typeof(Delegate).IsAssignableFrom(parameters[0].ParameterType)) + { + // property setter; replace with event remove + returnType = typeof(void); + name = "remove_" + name.Substring(4); + ref ParameterToken p = ref parameters[0]; + p = p.WithParameterType(ActionDelegateToFunc(p.ParameterType)); + } + + if (name.StartsWith("get_") || name.StartsWith("set_") || name.StartsWith("add_") || name.StartsWith("remove_")) + { + bool preserve = (name.StartsWith("get_") || name.StartsWith("set_")), fullyHandled = false; + if (asyncInterface == typeof(IRedisNativeClientAsync) || asyncInterface == typeof(IRedisClientAsync) + || asyncInterface == typeof(IRedisTypedClientAsync<>)) + { + switch (tok.Name) + { + case "get_" + nameof(IRedisNativeClient.DbSize): + case "get_" + nameof(IRedisNativeClient.LastSave): + case "get_" + nameof(IRedisNativeClient.Info): + fromPropertyToMethod = true; + preserve = false; + break; + case "set_" + nameof(IRedisNativeClient.Db): + name = nameof(IRedisNativeClientAsync.SelectAsync); + parameters[0] = parameters[0].WithName("db"); + fullyHandled = true; + break; + case "set_" + nameof(IRedisClientAsync.Hashes): + case "set_" + nameof(IRedisClientAsync.Lists): + case "set_" + nameof(IRedisClientAsync.Sets): + case "set_" + nameof(IRedisClientAsync.SortedSets): + return; // no "set" included + case "get_Item": + case "set_Item": + return; // no indexer + } + } + + if (fromPropertyToMethod) + { + name = name switch + { + "get_ItemAsync" => "ElementAtAsync", + "set_ItemAsync" => "SetValueAsync", + _ => name.Substring(4), // don't worry about the remove, that isn't in this catchment + }; + } + else if (preserve && !fullyHandled) + { // just keep it the same by default + name = tok.Name; + returnType = SwapForAsyncIfNeedeed(tok.ReturnType); + addCancellation = false; + } + + else if (fullyHandled) { } + else + { + addCancellation = false; + } + } + + static Type ActionDelegateToFunc(Type type) + { + if (type.IsGenericType) + { + var genDef = type.GetGenericTypeDefinition(); + var targs = type.GetGenericArguments(); + Array.Resize(ref targs, targs.Length + 1); + targs[targs.Length - 1] = typeof(ValueTask); + return Expression.GetFuncType(targs); + } + return type; + } + + if (asyncInterface == typeof(IRedisQueueCompletableOperationAsync) && parameters.Length == 1) + { + // very unusual case; Func => Func> + returnType = typeof(void); + ref ParameterToken p = ref parameters[0]; + if (p.ParameterType == typeof(Action)) + { + p = p.WithParameterType(typeof(Func)); + } + else + { + p = p.WithParameterType(typeof(Func<,>).MakeGenericType( + typeof(CancellationToken), typeof(ValueTask<>).MakeGenericType(p.ParameterType.GetGenericArguments()))); + } + tok = new MethodToken(name, returnType, parameters, tok.IsGenericMethod, tok.IsGenericMethodDefinition, tok.GetGenericArguments(), tok.AllAttributes()); + expected.Add(GetSignature(tok)); + } + else if (asyncInterface == typeof(IRedisQueueableOperationAsync) || asyncInterface == typeof(IRedisTypedQueueableOperationAsync<>)) + { + // very unusual case; Func => Func> + if (parameters.Length != 3) return; // move to optionals rather than overloads + ref ParameterToken p = ref parameters[0]; // fixup the delegate type + if (p.ParameterType.IsGenericType) + { + var genDef = p.ParameterType.GetGenericTypeDefinition(); + Type[] funcTypes = p.ParameterType.GetGenericArguments(); + funcTypes[0] = SwapForAsyncIfNeedeed(funcTypes[0]); + + if (genDef == typeof(Action<>)) + { + Array.Resize(ref funcTypes, funcTypes.Length + 1); + funcTypes[funcTypes.Length - 1] = typeof(ValueTask); + } + else + { + funcTypes[funcTypes.Length - 1] = typeof(ValueTask<>) + .MakeGenericType(funcTypes[funcTypes.Length - 1]); + } + + p = p.WithParameterType(typeof(Func<,>).MakeGenericType(funcTypes)); + } + + // make the other parameters optional + p = ref parameters[1]; + p = p.WithAttributes(p.Attributes | ParameterAttributes.Optional); + p = ref parameters[2]; + p = p.WithAttributes(p.Attributes | ParameterAttributes.Optional); + returnType = typeof(void); + name = method.Name; // retain the original name + + tok = new MethodToken(name, returnType, parameters, tok.IsGenericMethod, tok.IsGenericMethodDefinition, tok.GetGenericArguments(), tok.AllAttributes()); + expected.Add(GetSignature(tok)); + } + else + { + for (int i = 0; i < parameters.Length; i++) + { + ref ParameterToken p = ref parameters[i]; + Type type = p.ParameterType, swapped = SwapForAsyncIfNeedeed(type); + if (type != swapped) + { + p = p.WithParameterType(swapped); + } + } + + static bool IsParams(in MethodToken tok) + { + var ps = tok.GetParameters(); + if (ps is null || ps.Length == 0) return false; + return ps.Last().IsDefined(typeof(ParamArrayAttribute)); + } + + if (IsParams(tok)) + { + // include it with params but without cancellationToken + tok = new MethodToken(name, returnType, parameters, tok.IsGenericMethod, tok.IsGenericMethodDefinition, tok.GetGenericArguments(), tok.AllAttributes()); + expected.Add(GetSignature(tok)); + + // and now remove the params so we can get with cancellationToken + ref ParameterToken p = ref parameters[parameters.Length - 1]; + p = p.WithAllAttributes(p.AllAttributes().Where(a => !(a is ParamArrayAttribute)).ToArray()); + } + + if (asyncInterface == typeof(IDistributedLockAsync) && name == nameof(IDistributedLockAsync.LockAsync)) + { + // can't use "out", so uses a new LockState type instead + returnType = typeof(ValueTask); + parameters = RemoveByRef(parameters); + + static ParameterToken[] RemoveByRef(ParameterToken[] parameters) + { + if (parameters.Any(x => x.ParameterType.IsByRef)) + { + parameters = parameters.Where(x => !x.ParameterType.IsByRef).ToArray(); + } + return parameters; + } + } + if (asyncInterface == typeof(IRedisNativeClientAsync)) + { + switch (tok.Name) + { + case nameof(IRedisNativeClient.DecrBy): + case nameof(IRedisNativeClient.IncrBy): + parameters[1] = parameters[1].WithParameterType(typeof(long)); + returnType = typeof(ValueTask); + break; + case nameof(IRedisNativeClient.Shutdown): + Insert(ref parameters, 0, new ParameterToken("noSave", typeof(bool), ParameterAttributes.Optional, false)); + break; + case nameof(IRedisNativeClient.Set): + Insert(ref parameters, 2, new ParameterToken("expirySeconds", typeof(long), ParameterAttributes.Optional, 0)); + Insert(ref parameters, 3, new ParameterToken("expiryMilliseconds", typeof(long), ParameterAttributes.Optional, 0)); + break; + } + + static void Insert(ref ParameterToken[] parameters, int index, ParameterToken value) + { + // don't try to be clever; this is inefficient but correct + var list = parameters.ToList(); + list.Insert(index, value); + parameters = list.ToArray(); + } + } + + if (asyncInterface == typeof(IRedisSubscriptionAsync) && tok.Name == "get_" + nameof(IRedisSubscription.SubscriptionCount)) + { + // this is a purely client value; don't treat as async + name = tok.Name; + returnType = tok.ReturnType; + } + + if (asyncInterface == typeof(IRedisClientAsync) || asyncInterface == typeof(IRedisTypedClientAsync<>)) + { + switch (tok.Name) + { + case nameof(IRedisClient.UrnKey): + case nameof(IRedisClient.As): + addCancellation = false; + name = tok.Name; + returnType = SwapForAsyncIfNeedeed(tok.ReturnType); + break; + case nameof(IRedisClient.Save): // to avoid AsyncAsync and overloaded meaning of Async + name = nameof(IRedisClientAsync.ForegroundSaveAsync); + break; + case nameof(IRedisClient.SaveAsync): // to avoid AsyncAsync and overloaded meaning of Async + name = nameof(IRedisClientAsync.BackgroundSaveAsync); + break; + case nameof(IRedisClient.RewriteAppendOnlyFileAsync): // for consistency + name = nameof(IRedisClientAsync.BackgroundRewriteAppendOnlyFileAsync); + break; + case nameof(IRedisClient.ExecCachedLua): + // Func scriptSha1 => Func> scriptSha1 + parameters[1] = parameters[1].WithParameterType(typeof(Func<,>).MakeGenericType(typeof(string), typeof(ValueTask<>).MakeGenericType(method.GetGenericArguments()))); + break; + case nameof(IRedisClient.AcquireLock) when asyncInterface == typeof(IRedisClientAsync): + if (parameters.Length != 2) return; // 2 overloads combined into 1 + parameters[1] = parameters[1].AsNullable().AsOptional(); + returnType = typeof(ValueTask<>).MakeGenericType(returnType); // add await for acquisition + break; + case nameof(IRedisClient.AcquireLock) when asyncInterface == typeof(IRedisTypedClientAsync<>): + if (parameters.Length != 1) return; // 2 overloads combined into 1 + parameters[0] = parameters[0].AsNullable().AsOptional(); + returnType = typeof(ValueTask<>).MakeGenericType(returnType); // add await for acquisition + break; + case nameof(IRedisClient.SetValueIfExists) when asyncInterface == typeof(IRedisClientAsync): + case nameof(IRedisClient.SetValueIfNotExists) when asyncInterface == typeof(IRedisClientAsync): + if (parameters.Length != 3) return; // 2 overloads combined into 1 + parameters[2] = parameters[2].AsNullable().AsOptional(); + break; + case nameof(IRedisClient.CreatePipeline): + case nameof(IRedisTypedClient.GetHash): + addCancellation = false; + name = tok.Name; + returnType = SwapForAsyncIfNeedeed(tok.ReturnType); + break; + } + } + + for (int i = 0; i < parameters.Length; i++) + { + ref ParameterToken p = ref parameters[i]; + var type = p.ParameterType; + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)) + { + // prefer IDictionary<,> to Dictionary<,> + p = p.WithParameterType(typeof(IDictionary<,>).MakeGenericType(type.GetGenericArguments())); + } + } + + // append optional cancellationToken + if (addCancellation) + { + Array.Resize(ref parameters, parameters.Length + 1); + parameters[parameters.Length - 1] = cancellationToken; + } + tok = new MethodToken(name, returnType, parameters, tok.IsGenericMethod, tok.IsGenericMethodDefinition, tok.GetGenericArguments(), tok.AllAttributes()); + expected.Add(GetSignature(tok)); + } + } + + actual.Sort(); + expected.Sort(); + int extra = 0, missing = 0, match = 0; + Log($"actual: {actual.Count}, expected: {expected.Count}"); + foreach (var method in actual.Except(expected)) + { + Log($"+ {method}"); + extra++; + } + foreach (var method in expected.Except(actual)) + { + Log($"- {method}"); + missing++; + } + foreach (var method in expected.Intersect(actual)) + { + Log($"= {method}"); + match++; + } + Assert.True(extra == 0 && missing == 0, $"signature mismatch on {GetCSharpTypeName(asyncInterface)}; missing: {missing}, extra: {extra}, match: {match}"); + + + static Type SwapForAsyncIfNeedeed(Type type) + { + if (type.IsArray) + { + var t = type.GetElementType(); + var swapped = SwapForAsyncIfNeedeed(t); + if (t != swapped) + { + var rank = type.GetArrayRank(); + return swapped.MakeArrayType(rank); + } + return type; + } + if (type == typeof(IRedisClient)) return typeof(IRedisClientAsync); + if (type == typeof(ICacheClient)) return typeof(ICacheClientAsync); + if (type == typeof(IRedisPipeline)) return typeof(IRedisPipelineAsync); + if (type == typeof(IRedisPipelineShared)) return typeof(IRedisPipelineSharedAsync); + if (type == typeof(IDisposable)) return typeof(IAsyncDisposable); + if (type == typeof(IRedisList)) return typeof(IRedisListAsync); + if (type == typeof(IRedisSet)) return typeof(IRedisSetAsync); + if (type == typeof(IRedisSortedSet)) return typeof(IRedisSortedSetAsync); + if (type == typeof(IRedisHash)) return typeof(IRedisHashAsync); + if (type == typeof(IRedisSubscription)) return typeof(IRedisSubscriptionAsync); + if (type == typeof(IRedisTransaction)) return typeof(IRedisTransactionAsync); + + if (type.IsGenericType) + { + var genDef = type.GetGenericTypeDefinition(); + var targs = type.GetGenericArguments(); + for (int i = 0; i < targs.Length; i++) + targs[i] = SwapForAsyncIfNeedeed(targs[i]); + + if (genDef == typeof(IRedisTypedClient<>)) return typeof(IRedisTypedClientAsync<>).MakeGenericType(targs); + if (genDef == typeof(IRedisList<>)) return typeof(IRedisListAsync<>).MakeGenericType(targs); + if (genDef == typeof(IRedisSet<>)) return typeof(IRedisSetAsync<>).MakeGenericType(targs); + if (genDef == typeof(IRedisSortedSet<>)) return typeof(IRedisSortedSetAsync<>).MakeGenericType(targs); + if (genDef == typeof(IRedisTypedTransaction<>)) return typeof(IRedisTypedTransactionAsync<>).MakeGenericType(targs); + if (genDef == typeof(IRedisHash<,>)) return typeof(IRedisHashAsync<,>).MakeGenericType(targs); + if (genDef == typeof(IRedisTypedPipeline<>)) return typeof(IRedisTypedPipelineAsync<>).MakeGenericType(targs); + + return genDef.MakeGenericType(targs); + } + + return type; + } + } + + static string GetCSharpTypeName(Type type) + { + if (!(type.IsGenericType || type.IsArray)) + { + return GetSimpleCSharpTypeName(type); + } + var sb = new StringBuilder(); + AppendCSharpTypeName(type, sb); + return sb.ToString(); + } + static string GetSimpleCSharpTypeName(Type type) + { + if (type == typeof(void)) return "void"; + if (type == typeof(bool)) return "bool"; + if (type == typeof(sbyte)) return "sbyte"; + if (type == typeof(short)) return "short"; + if (type == typeof(int)) return "int"; + if (type == typeof(long)) return "long"; + if (type == typeof(byte)) return "byte"; + if (type == typeof(ushort)) return "ushort"; + if (type == typeof(uint)) return "uint"; + if (type == typeof(ulong)) return "ulong"; + if (type == typeof(string)) return "string"; + if (type == typeof(double)) return "double"; + if (type == typeof(float)) return "float"; + if (type == typeof(object)) return "object"; + + return type.Name; + } + + static void AppendCSharpTypeName(Type type, StringBuilder sb) + { + if (type.IsArray) + { + // we won't worry about the difference between vector and non-vector rank zero arrays + AppendCSharpTypeName(type.GetElementType(), sb); + sb.Append("[").Append(',', type.GetArrayRank() - 1).Append("]"); + } + else if (type.IsGenericParameter) + { + sb.Append(type.Name); + } + else if (type.IsGenericType) + { + var nullable = Nullable.GetUnderlyingType(type); + if (nullable is object) + { + AppendCSharpTypeName(nullable, sb); + sb.Append("?"); + } + else + { + var name = type.Name; + int i = name.IndexOf('`'); + if (i < 0) + { + sb.Append(name); + } + else + { + sb.Append(name, 0, i); + } + sb.Append("<"); + var targs = type.GetGenericArguments(); + for (i = 0; i < targs.Length; i++) + { + if (i != 0) sb.Append(", "); + sb.Append(GetCSharpTypeName(targs[i])); + } + sb.Append(">"); + } + } + else + { + sb.Append(GetSimpleCSharpTypeName(type)); + } + } + static string GetSignature(MethodToken method) + { + var sb = new StringBuilder(); + AppendCSharpTypeName(method.ReturnType, sb); + sb.Append(' ').Append(method.Name); + if (method.IsGenericMethodDefinition) + { + sb.Append('<'); + var args = method.GetGenericArguments(); + for (int i = 0; i < args.Length; i++) + { + if (i != 0) sb.Append(", "); + sb.Append(args[i].Name); + } + sb.Append('>'); + } + sb.Append('('); + var ps = method.GetParameters(); + for (int i = 0; i < ps.Length; i++) + { + var p = ps[i]; + if (i != 0) sb.Append(", "); + if (p.IsDefined(typeof(ParamArrayAttribute))) + { + sb.Append("params "); + } + if (p.ParameterType.IsByRef) + { + const ParameterAttributes InOut = ParameterAttributes.In | ParameterAttributes.Out; + sb.Append((p.Attributes & InOut) switch + { + ParameterAttributes.In => "in", + ParameterAttributes.Out => "out", + _ => "ref" + }).Append(' '); + AppendCSharpTypeName(p.ParameterType.GetElementType(), sb); + } + else + { + AppendCSharpTypeName(p.ParameterType, sb); + } + sb.Append(' ').Append(p.Name); + if ((p.Attributes & ParameterAttributes.Optional) == ParameterAttributes.Optional) + { + sb.Append(" = "); + switch (p.DefaultValue) + { + case null: + case DBNull _: // used for delegates, honest! + sb.Append("default"); + break; + case string s: + sb.Append(@"""").Append(s.Replace(@"""", @"""""")).Append(@""""); + break; + case object o: + sb.Append(Convert.ToString(o, CultureInfo.InvariantCulture)); + break; + } + } + } + return sb.Append(')').ToString(); + } + + readonly struct ParameterToken + { + public bool IsDefined(Type attributeType) + => _allAttributes.Any(a => attributeType.IsAssignableFrom(a.GetType())); + public object DefaultValue { get; } + public ParameterAttributes Attributes { get; } + public string Name { get; } + public Type ParameterType { get; } + private readonly object[] _allAttributes; + public object[] AllAttributes() => MethodToken.Clone(_allAttributes); + + internal ParameterToken WithAllAttributes(params object[] allAttributes) + => new ParameterToken(Name, ParameterType, Attributes, DefaultValue, allAttributes); + + internal ParameterToken WithParameterType(Type parameterType) + => new ParameterToken(Name, parameterType, Attributes, DefaultValue, _allAttributes); + + internal ParameterToken AsNullable() + { + if (!ParameterType.IsValueType) return this; // already nullable (ish) + var existing = Nullable.GetUnderlyingType(ParameterType); + if (existing is object) return this; // already nullable + return WithParameterType(typeof(Nullable<>).MakeGenericType(ParameterType)); + } + + internal ParameterToken AsOptional() + => WithAttributes(Attributes | ParameterAttributes.Optional); + + internal ParameterToken WithAttributes(ParameterAttributes attributes) + => new ParameterToken(Name, ParameterType, attributes, DefaultValue, _allAttributes); + + internal ParameterToken WithName(string name) + => new ParameterToken(name, ParameterType, Attributes, DefaultValue, _allAttributes); + + public ParameterToken(ParameterInfo source) + { + Name = source.Name; + ParameterType = source.ParameterType; + Attributes = source.Attributes; + DefaultValue = source.DefaultValue; + _allAttributes = source.AllAttributes(); + } + + public ParameterToken(string name, Type parameterType, ParameterAttributes attributes, object defaultValue = default, params object[] allAttributes) + { + Name = name; + ParameterType = parameterType; + Attributes = attributes; + DefaultValue = defaultValue; + _allAttributes = allAttributes ?? Array.Empty(); + } + } + + readonly struct MethodToken + { + private readonly ParameterToken[] _parameters; + private readonly Type[] _genericArguments; + private readonly object[] _allAttributes; + public bool IsDefined(Type attributeType) + => _allAttributes.Any(a => attributeType.IsAssignableFrom(a.GetType())); + internal static T[] Clone(T[] source) + { + if (source is null) return null; + var result = new T[source.Length]; + source.CopyTo(result, 0); + return result; + } + public ParameterToken[] GetParameters() => Clone(_parameters); + public Type[] GetGenericArguments() => Clone(_genericArguments); + public string Name { get; } + public bool IsGenericMethodDefinition { get; } + public bool IsGenericMethod { get; } + public Type ReturnType { get; } + public object[] AllAttributes() => Clone(_allAttributes); + public MethodToken(MethodInfo source) + { + Name = source.Name; + IsGenericMethod = source.IsGenericMethod; + IsGenericMethodDefinition = source.IsGenericMethodDefinition; + ReturnType = source.ReturnType; + _genericArguments = (source.IsGenericMethod || source.IsGenericMethodDefinition) + ? source.GetGenericArguments() : null; + var ps = source.GetParameters(); + _parameters = ps is null ? null : Array.ConvertAll(ps, p => new ParameterToken(p)); + _allAttributes = source.AllAttributes(); + } + + public MethodToken(string name, Type returnType, ParameterToken[] parameters, + bool isGenericMethod, bool isGenericMethodDefinition, Type[] genericArguments, + params object[] allAttributes) + { + Name = name; + ReturnType = returnType; + IsGenericMethod = isGenericMethod; + IsGenericMethodDefinition = isGenericMethodDefinition; + _genericArguments = genericArguments; + _parameters = parameters ?? Array.Empty(); + _allAttributes = allAttributes ?? Array.Empty(); + } + } + + [TestCase(typeof(ICacheClient), typeof(ICacheClientAsync))] + [TestCase(typeof(ICacheClientExtended), typeof(ICacheClientExtendedAsync))] + [TestCase(typeof(IEntityStore), typeof(IEntityStoreAsync))] + [TestCase(typeof(IEntityStore<>), typeof(IEntityStoreAsync<>))] + [TestCase(typeof(IRedisClient), typeof(IRedisClientAsync))] + + [TestCase(typeof(IRedisClientsManager), typeof(IRedisClientsManagerAsync))] + [TestCase(typeof(IRedisNativeClient), typeof(IRedisNativeClientAsync))] + [TestCase(typeof(IRedisPipeline), typeof(IRedisPipelineAsync))] + [TestCase(typeof(IRedisPipelineShared), typeof(IRedisPipelineSharedAsync))] + [TestCase(typeof(IRedisQueueableOperation), typeof(IRedisQueueableOperationAsync))] + + [TestCase(typeof(IRedisQueueCompletableOperation), typeof(IRedisQueueCompletableOperationAsync))] + [TestCase(typeof(IRedisTransaction), typeof(IRedisTransactionAsync))] + [TestCase(typeof(IRedisTransactionBase), typeof(IRedisTransactionBaseAsync))] + [TestCase(typeof(IRedisTypedClient<>), typeof(IRedisTypedClientAsync<>))] + [TestCase(typeof(IRemoveByPattern), typeof(IRemoveByPatternAsync))] + + [TestCase(typeof(IDistributedLock), typeof(IDistributedLockAsync))] + [TestCase(typeof(IRedisSubscription), typeof(IRedisSubscriptionAsync))] + [TestCase(typeof(IRedisHash), typeof(IRedisHashAsync))] + [TestCase(typeof(IRedisSortedSet), typeof(IRedisSortedSetAsync))] + [TestCase(typeof(IRedisSet), typeof(IRedisSetAsync))] + + [TestCase(typeof(IRedisList), typeof(IRedisListAsync))] + [TestCase(typeof(IRedisHash<,>), typeof(IRedisHashAsync<,>))] + [TestCase(typeof(IRedisSortedSet<>), typeof(IRedisSortedSetAsync<>))] + [TestCase(typeof(IRedisSet<>), typeof(IRedisSetAsync<>))] + [TestCase(typeof(IRedisList<>), typeof(IRedisListAsync<>))] + + [TestCase(typeof(IRedisTypedPipeline<>), typeof(IRedisTypedPipelineAsync<>))] + [TestCase(typeof(IRedisTypedQueueableOperation<>), typeof(IRedisTypedQueueableOperationAsync<>))] + [TestCase(typeof(IRedisTypedTransaction<>), typeof(IRedisTypedTransactionAsync<>))] + public void TestFullyImplemented(Type syncInterface, Type asyncInterface) + { + HashSet except = new HashSet(); +#if NET472 // only exists there! + if (syncInterface == typeof(IRedisClientsManager)) + { + except.Add(typeof(ServiceStack.Redis.Support.Diagnostic.TrackingRedisClientsManager)); + } +#endif + + var syncTypes = AllTypes.Except(except).Where(x => Implements(x, syncInterface)).ToArray(); + DumpTypes(syncInterface, syncTypes); + + var asyncTypes = AllTypes.Except(except).Where(x => Implements(x, asyncInterface)).ToArray(); + DumpTypes(asyncInterface, asyncTypes); + Assert.AreEqual(syncTypes, asyncTypes); + } + + static void DumpTypes(Type @interface, Type[] classes) + { + TestContext.Out.WriteLine($"Classes that implement {@interface.Name}: {classes.Length}:"); + foreach (var @class in classes) + { + TestContext.Out.WriteLine($" {@class.FullName}"); + } + TestContext.Out.WriteLine(); + } + + static bool Implements(Type @class, Type @interface) + { + if (@interface.IsGenericTypeDefinition) + { + var found = (from iType in @class.GetInterfaces() + where iType.IsGenericType + && iType.GetGenericTypeDefinition() == @interface + select iType).SingleOrDefault(); + return found != null && found.IsAssignableFrom(@class); + } + return @interface.IsAssignableFrom(@class); + } + + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/BasicRediscClientManagerTests.Async.cs b/tests/ServiceStack.Redis.Tests/BasicRediscClientManagerTests.Async.cs new file mode 100644 index 00000000..57e01a70 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/BasicRediscClientManagerTests.Async.cs @@ -0,0 +1,42 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + public class BasicRediscClientManagerTestsAsync + : RedisClientTestsBaseAsync + { + [Test] + public async Task Can_select_db() + { + var redisManager = new BasicRedisClientManager("127.0.0.1"); + + await using (var client = await redisManager.GetClientAsync()) + { + await client.SelectAsync(2); + await client.SetAsync("db", 2); + } + + await using (var client = await redisManager.GetClientAsync()) + { + await client.SelectAsync(3); + await client.SetAsync("db", 3); + } + + await using (var client = await redisManager.GetClientAsync()) + { + await client.SelectAsync(2); + //((RedisClient)client).ChangeDb(2); + var db = await client.GetAsync("db"); + Assert.That(db, Is.EqualTo(2)); + } + + redisManager = new BasicRedisClientManager("127.0.0.1?db=3"); + await using (var client = await redisManager.GetClientAsync()) + { + var db = await client.GetAsync("db"); + Assert.That(db, Is.EqualTo(3)); + } + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/CultureInfoTests.Async.cs b/tests/ServiceStack.Redis.Tests/CultureInfoTests.Async.cs new file mode 100644 index 00000000..12cc63f6 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/CultureInfoTests.Async.cs @@ -0,0 +1,47 @@ +using NUnit.Framework; +using System.Globalization; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class CultureInfoTestsAsync + : RedisClientTestsBaseAsync + { + private CultureInfo previousCulture = CultureInfo.InvariantCulture; + + [OneTimeSetUp] + public void OneTimeSetUp() + { +#if NETCORE + previousCulture = CultureInfo.CurrentCulture; + CultureInfo.CurrentCulture = new CultureInfo("fr-FR"); +#else + previousCulture = Thread.CurrentThread.CurrentCulture; + Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); + Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR"); +#endif + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { +#if NETCORE + CultureInfo.CurrentCulture = previousCulture; +#else + Thread.CurrentThread.CurrentCulture = previousCulture; +#endif + } + + [Test] + public async Task Can_AddItemToSortedSet_in_different_Culture() + { + await RedisAsync.AddItemToSortedSetAsync("somekey1", "somevalue", 66121.202); + var score = await RedisAsync.GetItemScoreInSortedSetAsync("somekey1", "somevalue"); + + Assert.That(score, Is.EqualTo(66121.202)); + } + + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/CustomCommandTests.Async.cs b/tests/ServiceStack.Redis.Tests/CustomCommandTests.Async.cs new file mode 100644 index 00000000..3033ed5c --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/CustomCommandTests.Async.cs @@ -0,0 +1,140 @@ +using NUnit.Framework; +using ServiceStack.Common.Tests.Models; +using ServiceStack.Text; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class CustomCommandTestsAsync + : RedisClientTestsBaseAsync + { + [Test] + public async Task Can_send_custom_commands() + { + await RedisAsync.FlushAllAsync(); + + RedisText ret; + + ret = await RedisAsync.CustomAsync("SET", "foo", 1); + Assert.That(ret.Text, Is.EqualTo("OK")); + _ = await RedisAsync.CustomAsync(Commands.Set, "bar", "b"); + + ret = await RedisAsync.CustomAsync("GET", "foo"); + Assert.That(ret.Text, Is.EqualTo("1")); + ret = await RedisAsync.CustomAsync(Commands.Get, "bar"); + Assert.That(ret.Text, Is.EqualTo("b")); + + ret = await RedisAsync.CustomAsync(Commands.Keys, "*"); + var keys = ret.GetResults(); + Assert.That(keys, Is.EquivalentTo(new[] { "foo", "bar" })); + + ret = await RedisAsync.CustomAsync("MGET", "foo", "bar"); + var values = ret.GetResults(); + Assert.That(values, Is.EquivalentTo(new[] { "1", "b" })); + + foreach (var x in Enum.GetNames(typeof(DayOfWeek))) + { + await RedisAsync.CustomAsync("RPUSH", "DaysOfWeek", x); + } + + ret = await RedisAsync.CustomAsync("LRANGE", "DaysOfWeek", 1, -2); + + var weekDays = ret.GetResults(); + Assert.That(weekDays, Is.EquivalentTo( + new[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" })); + + ret.PrintDump(); + } + + [Test] + public async Task Can_send_custom_commands_longhand() + { + await RedisAsync.FlushAllAsync(); + + RedisText ret; + + ret = await RedisAsync.CustomAsync(new object[] { "SET", "foo", 1 }); + Assert.That(ret.Text, Is.EqualTo("OK")); + _ = await RedisAsync.CustomAsync(new object[] { Commands.Set, "bar", "b" }); + + ret = await RedisAsync.CustomAsync(new object[] { "GET", "foo" }); + Assert.That(ret.Text, Is.EqualTo("1")); + ret = await RedisAsync.CustomAsync(new object[] { Commands.Get, "bar" }); + Assert.That(ret.Text, Is.EqualTo("b")); + + ret = await RedisAsync.CustomAsync(new object[] { Commands.Keys, "*" }); + var keys = ret.GetResults(); + Assert.That(keys, Is.EquivalentTo(new[] { "foo", "bar" })); + + ret = await RedisAsync.CustomAsync(new object[] { "MGET", "foo", "bar" }); + var values = ret.GetResults(); + Assert.That(values, Is.EquivalentTo(new[] { "1", "b" })); + + foreach (var x in Enum.GetNames(typeof(DayOfWeek))) + { + await RedisAsync.CustomAsync(new object[] { "RPUSH", "DaysOfWeek", x }); + } + + ret = await RedisAsync.CustomAsync(new object[] { "LRANGE", "DaysOfWeek", 1, -2 }); + + var weekDays = ret.GetResults(); + Assert.That(weekDays, Is.EquivalentTo( + new[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" })); + + ret.PrintDump(); + } + + [Test] + public async Task Can_send_complex_types_in_Custom_Commands() + { + await RedisAsync.FlushAllAsync(); + + RedisText ret; + + ret = await RedisAsync.CustomAsync("SET", "foo", new Poco { Name = "Bar" }); + Assert.That(ret.Text, Is.EqualTo("OK")); + + ret = await RedisAsync.CustomAsync("GET", "foo"); + var dto = ret.GetResult(); + Assert.That(dto.Name, Is.EqualTo("Bar")); + + foreach (var x in Enum.GetNames(typeof(DayOfWeek))) + await RedisAsync.CustomAsync("RPUSH", "DaysOfWeek", new Poco { Name = x }); + + ret = await RedisAsync.CustomAsync("LRANGE", "DaysOfWeek", 1, -2); + var weekDays = ret.GetResults(); + + Assert.That(weekDays.First().Name, Is.EqualTo("Monday")); + + ret.PrintDump(); + } + + [Test] + public async Task Can_send_complex_types_in_Custom_Commands_longhand() + { + await RedisAsync.FlushAllAsync(); + + RedisText ret; + + ret = await RedisAsync.CustomAsync(new object[] { "SET", "foo", new Poco { Name = "Bar" } }); + Assert.That(ret.Text, Is.EqualTo("OK")); + + ret = await RedisAsync.CustomAsync(new object[] { "GET", "foo" }); + var dto = ret.GetResult(); + Assert.That(dto.Name, Is.EqualTo("Bar")); + + foreach (var x in Enum.GetNames(typeof(DayOfWeek))) + await RedisAsync.CustomAsync(new object[] { "RPUSH", "DaysOfWeek", new Poco { Name = x } }); + + ret = await RedisAsync.CustomAsync(new object[] { "LRANGE", "DaysOfWeek", 1, -2 }); + var weekDays = ret.GetResults(); + + Assert.That(weekDays.First().Name, Is.EqualTo("Monday")); + + ret.PrintDump(); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsBase.Async.cs new file mode 100644 index 00000000..91171dad --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsBase.Async.cs @@ -0,0 +1,249 @@ +using System.Collections.Generic; +using NUnit.Framework; +using ServiceStack.Common; +using ServiceStack.Common.Tests.Models; +using ServiceStack.Redis.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests.Generic +{ + [TestFixture, Category("Async")] + public abstract class RedisClientHashTestsBaseAsync + { + private const string HashId = "testhash"; + + protected abstract IModelFactory Factory { get; } + + private IRedisClientAsync client; + private IRedisTypedClientAsync redis; + private IRedisHashAsync Hash; + + [SetUp] + public async Task SetUp() + { + if (client != null) + { + await client.DisposeAsync(); + client = null; + } + client = new RedisClient(TestConfig.SingleHost); + await client.FlushAllAsync(); + + redis = client.As(); + + Hash = redis.GetHash(HashId); + } + + private Dictionary CreateMap() + { + var listValues = Factory.CreateList(); + var map = new Dictionary(); + listValues.ForEach(x => map[x.ToString()] = x); + return map; + } + + private Dictionary CreateMap2() + { + var listValues = Factory.CreateList2(); + var map = new Dictionary(); + listValues.ForEach(x => map[x.ToString()] = x); + return map; + } + + [Test] + public async Task Can_SetItemInHash_and_GetAllFromHash() + { + var mapValues = CreateMap(); + await mapValues.ForEachAsync(async (k, v) => await redis.SetEntryInHashAsync(Hash, k, v)); + + var members = await redis.GetAllEntriesFromHashAsync(Hash); + Assert.That(members, Is.EquivalentTo(mapValues)); + } + + [Test] + public async Task Can_RemoveFromHash() + { + var mapValues = CreateMap(); + await mapValues.ForEachAsync(async (k, v) => await redis.SetEntryInHashAsync(Hash, k, v)); + + var firstKey = mapValues.First().Key; + + await redis.RemoveEntryFromHashAsync(Hash, firstKey); + + mapValues.Remove(firstKey); + + var members = await redis.GetAllEntriesFromHashAsync(Hash); + Assert.That(members, Is.EquivalentTo(mapValues)); + } + + [Test] + public async Task Can_GetItemFromHash() + { + var mapValues = CreateMap(); + await mapValues.ForEachAsync(async (k, v) => await redis.SetEntryInHashAsync(Hash, k, v)); + + var firstKey = mapValues.First().Key; + + var hashValue = await redis.GetValueFromHashAsync(Hash, firstKey); + + Assert.That(hashValue, Is.EqualTo(mapValues[firstKey])); + } + + [Test] + public async Task Can_GetHashCount() + { + var mapValues = CreateMap(); + await mapValues.ForEachAsync(async (k, v) => await redis.SetEntryInHashAsync(Hash, k, v)); + + var hashCount = await redis.GetHashCountAsync(Hash); + + Assert.That(hashCount, Is.EqualTo(mapValues.Count)); + } + + [Test] + public async Task Does_HashContainsKey() + { + var mapValues = CreateMap(); + await mapValues.ForEachAsync(async (k, v) => await redis.SetEntryInHashAsync(Hash, k, v)); + + var existingMember = mapValues.First().Key; + var nonExistingMember = existingMember + "notexists"; + + Assert.That(await redis.HashContainsEntryAsync(Hash, existingMember), Is.True); + Assert.That(await redis.HashContainsEntryAsync(Hash, nonExistingMember), Is.False); + } + + [Test] + public async Task Can_GetHashKeys() + { + var mapValues = CreateMap(); + await mapValues.ForEachAsync(async (k, v) => await redis.SetEntryInHashAsync(Hash, k, v)); + + var expectedKeys = mapValues.Map(x => x.Key); + + var hashKeys = await redis.GetHashKeysAsync(Hash); + + Assert.That(hashKeys, Is.EquivalentTo(expectedKeys)); + } + + [Test] + public async Task Can_GetHashValues() + { + var mapValues = CreateMap(); + await mapValues.ForEachAsync(async (k, v) => await redis.SetEntryInHashAsync(Hash, k, v)); + + var expectedValues = mapValues.Map(x => x.Value); + + var hashValues = await redis.GetHashValuesAsync(Hash); + + Assert.That(hashValues, Is.EquivalentTo(expectedValues)); + } + + [Test] + public async Task Can_enumerate_small_IDictionary_Hash() + { + var mapValues = CreateMap(); + await mapValues.ForEachAsync(async (k, v) => await redis.SetEntryInHashAsync(Hash, k, v)); + + var members = new List(); + await foreach (var item in redis.GetHash(HashId)) + { + Assert.That(mapValues.ContainsKey(item.Key), Is.True); + members.Add(item.Key); + } + Assert.That(members.Count, Is.EqualTo(mapValues.Count)); + } + + [Test] + public async Task Can_Add_to_IDictionary_Hash() + { + var hash = redis.GetHash(HashId); + var mapValues = CreateMap(); + await mapValues.ForEachAsync((k, v) => hash.AddAsync(k, v)); + + var members = await redis.GetAllEntriesFromHashAsync(Hash); + Assert.That(members, Is.EquivalentTo(mapValues)); + } + + [Test] + public async Task Can_Clear_IDictionary_Hash() + { + var hash = redis.GetHash(HashId); + var mapValues = CreateMap(); + await mapValues.ForEachAsync((k, v) => hash.AddAsync(k, v)); + + Assert.That(await hash.CountAsync(), Is.EqualTo(mapValues.Count)); + + await hash.ClearAsync(); + + Assert.That(await hash.CountAsync(), Is.EqualTo(0)); + } + + [Test] + public async Task Can_Test_Contains_in_IDictionary_Hash() + { + var hash = redis.GetHash(HashId); + var mapValues = CreateMap(); + await mapValues.ForEachAsync((k, v) => hash.AddAsync(k, v)); + + var existingMember = mapValues.First().Key; + var nonExistingMember = existingMember + "notexists"; + + Assert.That(await hash.ContainsKeyAsync(existingMember), Is.True); + Assert.That(await hash.ContainsKeyAsync(nonExistingMember), Is.False); + } + + [Test] + public async Task Can_Remove_value_from_IDictionary_Hash() + { + var hash = redis.GetHash(HashId); + var mapValues = CreateMap(); + await mapValues.ForEachAsync((k, v) => hash.AddAsync(k, v)); + + var firstKey = mapValues.First().Key; + mapValues.Remove(firstKey); + await hash.RemoveAsync(firstKey); + + var members = await redis.GetAllEntriesFromHashAsync(Hash); + Assert.That(members, Is.EquivalentTo(mapValues)); + } + + [Test] + public async Task Can_SetItemInHashIfNotExists() + { + var mapValues = CreateMap(); + await mapValues.ForEachAsync(async (k, v) => await redis.SetEntryInHashAsync(Hash, k, v)); + + var existingMember = mapValues.First().Key; + var nonExistingMember = existingMember + "notexists"; + + var lastValue = mapValues.Last().Value; + + await redis.SetEntryInHashIfNotExistsAsync(Hash, existingMember, lastValue); + await redis.SetEntryInHashIfNotExistsAsync(Hash, nonExistingMember, lastValue); + + mapValues[nonExistingMember] = lastValue; + + var members = await redis.GetAllEntriesFromHashAsync(Hash); + Assert.That(members, Is.EquivalentTo(mapValues)); + } + + [Test] + public async Task Can_SetRangeInHash() + { + var mapValues = CreateMap(); + await mapValues.ForEachAsync(async (k, v) => await redis.SetEntryInHashAsync(Hash, k, v)); + + var newMapValues = CreateMap2(); + + await redis.SetRangeInHashAsync(Hash, newMapValues); + + newMapValues.Each(x => mapValues[x.Key] = x.Value); + + var members = await redis.GetAllEntriesFromHashAsync(Hash); + Assert.That(members, Is.EquivalentTo(mapValues)); + } + } + +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsModels.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsModels.Async.cs new file mode 100644 index 00000000..0e7d7216 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsModels.Async.cs @@ -0,0 +1,80 @@ +using System; +using NUnit.Framework; +using ServiceStack.Common.Tests.Models; +using ServiceStack.Redis.Tests.Support; + +namespace ServiceStack.Redis.Tests.Generic +{ + [TestFixture] + public class RedisClientHashTestsModelWithFieldsOfDifferentTypesAsync + : RedisClientHashTestsBaseAsync + { + private readonly IModelFactory factory = + new ModelWithFieldsOfDifferentTypesFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientHashTestsStringAsync + : RedisClientHashTestsBaseAsync + { + private readonly IModelFactory factory = new BuiltInsFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientHashTestsShipperAsync + : RedisClientHashTestsBaseAsync + { + private readonly IModelFactory factory = new ShipperFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientHashTestsIntAsync + : RedisClientHashTestsBaseAsync + { + private readonly IModelFactory factory = new IntFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientHashTestsCustomTypeAsync + : RedisClientSetTestsBaseAsync + { + private readonly IModelFactory factory = new CustomTypeFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + //public class RedisClientHashTestsDateTimeAsync + // : RedisClientHashTestsBaseAsync + //{ + // private readonly IModelFactory factory = new DateTimeFactory(); + + // protected override IModelFactory Factory + // { + // get { return factory; } + // } + //} + +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestExtra.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestExtra.Async.cs new file mode 100644 index 00000000..6e03d56c --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestExtra.Async.cs @@ -0,0 +1,58 @@ +using NUnit.Framework; +using ServiceStack.Common.Tests.Models; +using ServiceStack.Redis.Generic; +using ServiceStack.Redis.Tests.Support; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests.Generic +{ + [TestFixture, Category("Async")] + public class RedisClientListTestExtraAsync + { + const string ListId = "testlist"; + // const string ListId2 = "testlist2"; + private IRedisListAsync List; + // private IRedisListAsync List2; + + + private readonly IModelFactory factory = new CustomTypeFactory(); + + protected IModelFactory Factory { get { return factory; } } + + private IRedisClientAsync client; + private IRedisTypedClientAsync redis; + + [SetUp] + public async Task SetUp() + { + if (client != null) + { + await client.DisposeAsync(); + client = null; + } + client = new RedisClient(TestConfig.SingleHost); + await client.FlushAllAsync(); + + redis = client.As(); + + List = redis.Lists[ListId]; + // List2 = redis.Lists[ListId2]; + } + + [Test] + public async Task Can_Remove_value_from_IList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => List.AddAsync(x)); + + var equalItem = new CustomType() { CustomId = 4 }; + storeMembers.Remove(equalItem); + await List.RemoveAsync(equalItem); + + var members = await List.ToListAsync(); + + Factory.AssertListsAreEqual(members, storeMembers); + } + + } +} diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsBase.Async.cs new file mode 100644 index 00000000..662cc87c --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsBase.Async.cs @@ -0,0 +1,334 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using ServiceStack.Common; +using ServiceStack.Common.Tests.Models; +using ServiceStack.Redis.Generic; +using ServiceStack.Text; +using ServiceStack.Redis.Tests.Support; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests.Generic +{ + [TestFixture, Category("Async")] + public abstract class RedisClientListTestsBaseAsync + { + const string ListId = "testlist"; + const string ListId2 = "testlist2"; + private IRedisListAsync List; + private IRedisListAsync List2; + + protected abstract IModelFactory Factory { get; } + + private IRedisClientAsync client; + private IRedisTypedClientAsync redis; + + [SetUp] + public async Task SetUp() + { + if (client != null) + { + await client.DisposeAsync(); + client = null; + } + client = new RedisClient(TestConfig.SingleHost); + await client.FlushAllAsync(); + + redis = client.As(); + + List = redis.Lists[ListId]; + List2 = redis.Lists[ListId2]; + } + + [Test] + public async Task Can_AddToList_and_GetAllFromList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => redis.AddItemToListAsync(List, x)); + + var members = await redis.GetAllItemsFromListAsync(List); + + Factory.AssertListsAreEqual(members, storeMembers); + } + + [Test] + public async Task Can_GetListCount() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => redis.AddItemToListAsync(List, x)); + + var listCount = await redis.GetListCountAsync(List); + + Assert.That(listCount, Is.EqualTo(storeMembers.Count)); + } + + [Test] + public async Task Can_GetItemFromList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => redis.AddItemToListAsync(List, x)); + + var storeMember3 = storeMembers[2]; + var item3 = await redis.GetItemFromListAsync(List, 2); + + Factory.AssertIsEqual(item3, storeMember3); + } + + [Test] + public async Task Can_SetItemInList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => redis.AddItemToListAsync(List, x)); + + storeMembers[2] = Factory.NonExistingValue; + await redis.SetItemInListAsync(List, 2, Factory.NonExistingValue); + + var members = await redis.GetAllItemsFromListAsync(List); + + Factory.AssertListsAreEqual(members, storeMembers); + } + + [Test] + public async Task Can_PopFromList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => redis.AddItemToListAsync(List, x)); + + var lastValue = await redis.PopItemFromListAsync(List); + + Factory.AssertIsEqual(lastValue, storeMembers[storeMembers.Count - 1]); + } + + [Test] + public async Task Can_BlockingDequeueItemFromList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => redis.EnqueueItemOnListAsync(List, x)); + + var item1 = await redis.BlockingDequeueItemFromListAsync(List, new TimeSpan(0, 0, 1)); + + Factory.AssertIsEqual(item1, (T)storeMembers.First()); + } + + [Test] + public async Task Can_BlockingDequeueItemFromList_Timeout() + { + var item1 = await redis.BlockingDequeueItemFromListAsync(List, new TimeSpan(0, 0, 1)); + Assert.AreEqual(item1, default(T)); + } + + [Test] + public async Task Can_DequeueFromList() + { + + var queue = new Queue(); + var storeMembers = Factory.CreateList(); + storeMembers.ForEach(x => queue.Enqueue(x)); + await storeMembers.ForEachAsync(x => redis.EnqueueItemOnListAsync(List, x)); + + var item1 = await redis.DequeueItemFromListAsync(List); + + Factory.AssertIsEqual(item1, queue.Dequeue()); + } + + [Test] + public async Task PopAndPushSameAsDequeue() + { + var queue = new Queue(); + var storeMembers = Factory.CreateList(); + storeMembers.ForEach(x => queue.Enqueue(x)); + await storeMembers.ForEachAsync(x => redis.EnqueueItemOnListAsync(List, x)); + + var item1 = await redis.PopAndPushItemBetweenListsAsync(List, List2); + Assert.That(item1, Is.EqualTo(queue.Dequeue())); + } + + [Test] + public async Task Can_ClearList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => redis.EnqueueItemOnListAsync(List, x)); + + var count = (await redis.GetAllItemsFromListAsync(List)).Count; + Assert.That(count, Is.EqualTo(storeMembers.Count)); + + await redis.RemoveAllFromListAsync(List); + count = (await redis.GetAllItemsFromListAsync(List)).Count; + Assert.That(count, Is.EqualTo(0)); + + } + + [Test] + public async Task Can_ClearListWithOneItem() + { + var storeMembers = Factory.CreateList(); + await redis.EnqueueItemOnListAsync(List, storeMembers[0]); + + var count = (await redis.GetAllItemsFromListAsync(List)).Count; + Assert.That(count, Is.EqualTo(1)); + + await redis.RemoveAllFromListAsync(List); + count = (await redis.GetAllItemsFromListAsync(List)).Count; + Assert.That(count, Is.EqualTo(0)); + } + + [Test] + public async Task Can_MoveBetweenLists() + { + var list1Members = Factory.CreateList(); + var list2Members = Factory.CreateList2(); + var lastItem = list1Members[list1Members.Count - 1]; + + await list1Members.ForEachAsync(x => redis.AddItemToListAsync(List, x)); + await list2Members.ForEachAsync(x => redis.AddItemToListAsync(List2, x)); + + list1Members.Remove(lastItem); + list2Members.Insert(0, lastItem); + await redis.PopAndPushItemBetweenListsAsync(List, List2); + + var readList1 = await redis.GetAllItemsFromListAsync(List); + var readList2 = await redis.GetAllItemsFromListAsync(List2); + + Factory.AssertListsAreEqual(readList1, list1Members); + Factory.AssertListsAreEqual(readList2, list2Members); + } + + + [Test] + public async Task Can_enumerate_small_list() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => redis.AddItemToListAsync(List, x)); + + var readMembers = new List(); + await foreach (var item in redis.Lists[ListId]) + { + readMembers.Add(item); + } + Factory.AssertListsAreEqual(readMembers, storeMembers); + } + + [Test] + public async Task Can_enumerate_large_list() + { + if (TestConfig.IgnoreLongTests) return; + + const int listSize = 2500; + + await listSize.TimesAsync(x => redis.AddItemToListAsync(List, Factory.CreateInstance(x))); + + var i = 0; + await foreach (var item in List) + { + Factory.AssertIsEqual(item, Factory.CreateInstance(i++)); + } + } + + [Test] + public async Task Can_Add_to_IList() + { + var storeMembers = Factory.CreateList(); + var list = redis.Lists[ListId]; + await storeMembers.ForEachAsync(x => list.AddAsync(x)); + + var members = await list.ToListAsync(); + Factory.AssertListsAreEqual(members, storeMembers); + } + + [Test] + public async Task Can_Clear_IList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => List.AddAsync(x)); + + Assert.That(await List.CountAsync(), Is.EqualTo(storeMembers.Count)); + + await List.ClearAsync(); + + Assert.That(await List.CountAsync(), Is.EqualTo(0)); + } + + [Test] + public async Task Can_Test_Contains_in_IList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => List.AddAsync(x)); + + Assert.That(await List.ContainsAsync(Factory.ExistingValue), Is.True); + Assert.That(await List.ContainsAsync(Factory.NonExistingValue), Is.False); + } + + [Test] + public async Task Can_Remove_value_from_IList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => List.AddAsync(x)); + + storeMembers.Remove(Factory.ExistingValue); + await List.RemoveAsync(Factory.ExistingValue); + + var members = await List.ToListAsync(); + + Factory.AssertListsAreEqual(members, storeMembers); + } + + [Test] + public async Task Can_RemoveAt_value_from_IList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => List.AddAsync(x)); + + storeMembers.RemoveAt(2); + await List.RemoveAtAsync(2); + + var members = await List.ToListAsync(); + + Factory.AssertListsAreEqual(members, storeMembers); + } + + [Test] + public async Task Can_get_default_index_from_IList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => List.AddAsync(x)); + + for (var i = 0; i < storeMembers.Count; i++) + { + Factory.AssertIsEqual(await List.ElementAtAsync(i), storeMembers[i]); + } + } + + [Test] + public async Task Can_test_for_IndexOf_in_IList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => List.AddAsync(x)); + + foreach (var item in storeMembers) + { + Assert.That(await List.IndexOfAsync(item), Is.EqualTo(storeMembers.IndexOf(item))); + } + } + + + [Test] + public async Task Can_GetRangeFromList() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(x => redis.AddItemToListAsync(List, x)); + + //in SetUp(): List = redis.Lists["testlist"]; + //alias for: redis.GetRangeFromList(redis.Lists["testlist"], 1, 3); + var range = await List.GetRangeAsync(1, 3); + var expected = storeMembers.Skip(1).Take(3).ToList(); + + //Uncomment to view list contents + //Debug.WriteLine(range.Dump()); + //Debug.WriteLine(expected.Dump()); + + Factory.AssertListsAreEqual(range, expected); + } + + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsModels.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsModels.Async.cs new file mode 100644 index 00000000..00d6e80f --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsModels.Async.cs @@ -0,0 +1,91 @@ +using System; +using NUnit.Framework; +using ServiceStack.Common.Tests.Models; +using ServiceStack.Redis.Tests.Support; + +namespace ServiceStack.Redis.Tests.Generic +{ + [TestFixture] + public class RedisClientListTestsModelWithFieldsOfDifferentTypesAsync + : RedisClientListTestsBaseAsync + { + private readonly IModelFactory factory = + new ModelWithFieldsOfDifferentTypesFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientListTestsStringAsync + : RedisClientListTestsBaseAsync + { + private readonly IModelFactory factory = new BuiltInsFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientListTestsShipperAsync + : RedisClientListTestsBaseAsync + { + private readonly IModelFactory factory = new ShipperFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientListTestsIntAsync + : RedisClientListTestsBaseAsync + { + private readonly IModelFactory factory = new IntFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientListTestsCustomTypeAsync + : RedisClientSetTestsBaseAsync + { + private readonly IModelFactory factory = new CustomTypeFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientlistTestCustomType_FailingAsync + : RedisClientListTestsBaseAsync + { + private readonly IModelFactory factory = new CustomTypeFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + //public class RedisClientListTestsDateTimeAsync + // : RedisClientListTestsBaseAsync + //{ + // private readonly IModelFactory factory = new DateTimeFactory(); + + // protected override IModelFactory Factory + // { + // get { return factory; } + // } + //} +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsModels.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsModels.cs index d8c0be73..5d8e0474 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsModels.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsModels.cs @@ -5,6 +5,7 @@ namespace ServiceStack.Redis.Tests.Generic { + // TODO: error, missing fixture? public class RedisClientListTestsModelWithFieldsOfDifferentTypes : RedisClientListTestsBase { diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsBase.Async.cs new file mode 100644 index 00000000..3e1729fe --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsBase.Async.cs @@ -0,0 +1,343 @@ +using System.Collections.Generic; +using NUnit.Framework; +using ServiceStack.Common.Tests.Models; +using ServiceStack.Redis.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests.Generic +{ + [Category("Async")] + public abstract class RedisClientSetTestsBaseAsync + { + private const string SetId = "testset"; + private const string SetId2 = "testset2"; + private const string SetId3 = "testset3"; + protected abstract IModelFactory Factory { get; } + + private IRedisClientAsync client; + private IRedisTypedClientAsync redis; + private IRedisSetAsync Set; + private IRedisSetAsync Set2; + private IRedisSetAsync Set3; + + [SetUp] + public async Task SetUp() + { + if (client != null) + { + await client.DisposeAsync(); + client = null; + } + client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await client.FlushAllAsync(); + + redis = client.As(); + + Set = redis.Sets[SetId]; + Set2 = redis.Sets[SetId2]; + Set3 = redis.Sets[SetId3]; + } + + [Test] + public async Task Can_AddToSet_and_GetAllFromSet() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + + var members = await redis.GetAllItemsFromSetAsync(Set); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_RemoveFromSet() + { + var storeMembers = Factory.CreateList(); + + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + + await redis.RemoveItemFromSetAsync(Set, Factory.ExistingValue); + + storeMembers.Remove(Factory.ExistingValue); + + var members = await redis.GetAllItemsFromSetAsync(Set); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_PopFromSet() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + + var member = await redis.PopItemFromSetAsync(Set); + + Assert.That(storeMembers.Contains(member), Is.True); + } + + [Test] + public async Task Can_MoveBetweenSets() + { + var fromSetMembers = Factory.CreateList(); + var toSetMembers = Factory.CreateList2(); + + await fromSetMembers.ForEachAsync(x => redis.AddItemToSetAsync(Set, x)); + await toSetMembers.ForEachAsync(x => redis.AddItemToSetAsync(Set2, x)); + + await redis.MoveBetweenSetsAsync(Set, Set2, Factory.ExistingValue); + + fromSetMembers.Remove(Factory.ExistingValue); + toSetMembers.Add(Factory.ExistingValue); + + var readFromSetId = await redis.GetAllItemsFromSetAsync(Set); + var readToSetId = await redis.GetAllItemsFromSetAsync(Set2); + + Assert.That(readFromSetId, Is.EquivalentTo(fromSetMembers)); + Assert.That(readToSetId, Is.EquivalentTo(toSetMembers)); + } + + [Test] + public async Task Can_GetSetCount() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + + var setCount = await redis.GetSetCountAsync(Set); + + Assert.That(setCount, Is.EqualTo(storeMembers.Count)); + } + + [Test] + public async Task Does_SetContainsValue() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + + Assert.That(await redis.SetContainsItemAsync(Set, Factory.ExistingValue), Is.True); + Assert.That(await redis.SetContainsItemAsync(Set, Factory.NonExistingValue), Is.False); + } + + [Test] + public async Task Can_IntersectBetweenSets() + { + var storeMembers = Factory.CreateList(); + var storeMembers2 = Factory.CreateList2(); + + storeMembers.Add(storeMembers2.First()); + storeMembers2.Add(storeMembers.First()); + + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + await storeMembers2.ForEachAsync(x => redis.AddItemToSetAsync(Set2, x)); + + var intersectingMembers = await redis.GetIntersectFromSetsAsync(new[] { Set, Set2 }); + + var intersect = (await Set.ToListAsync()).Intersect((await Set2.ToListAsync())).ToList(); + + Assert.That(intersectingMembers, Is.EquivalentTo(intersect)); + } + + [Test] + public async Task Can_Store_IntersectBetweenSets() + { + var storeMembers = Factory.CreateList(); + var storeMembers2 = Factory.CreateList2(); + + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + await storeMembers2.ForEachAsync(x => redis.AddItemToSetAsync(Set2, x)); + + await redis.StoreIntersectFromSetsAsync(Set3, new[] { Set, Set2 }); + + var intersect = (await Set.ToListAsync()).Intersect(await Set2.ToListAsync()).ToList(); + + Assert.That(await Set3.ToListAsync(), Is.EquivalentTo(intersect)); + } + + [Test] + public async Task Can_UnionBetweenSets() + { + var storeMembers = Factory.CreateList(); + var storeMembers2 = Factory.CreateList2(); + + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + await storeMembers2.ForEachAsync(x => redis.AddItemToSetAsync(Set2, x)); + + var unionMembers = await redis.GetUnionFromSetsAsync(new[] { Set, Set2 }); + + var union = (await Set.ToListAsync()).Union(await Set2.ToListAsync()).ToList(); + + Assert.That(unionMembers, Is.EquivalentTo(union)); + } + + [Test] + public async Task Can_Store_UnionBetweenSets() + { + var storeMembers = Factory.CreateList(); + var storeMembers2 = Factory.CreateList2(); + + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + await storeMembers2.ForEachAsync(x => redis.AddItemToSetAsync(Set2, x)); + + await redis.StoreUnionFromSetsAsync(Set3, new[] { Set, Set2 }); + + var union = (await Set.ToListAsync()).Union((await Set2.ToListAsync())).ToList(); + + Assert.That(await Set3.ToListAsync(), Is.EquivalentTo(union)); + } + + [Test] + public async Task Can_DiffBetweenSets() + { + var storeMembers = Factory.CreateList(); + storeMembers.Add(Factory.CreateInstance(1)); + + var storeMembers2 = Factory.CreateList2(); + storeMembers2.Insert(0, Factory.CreateInstance(4)); + + var storeMembers3 = new List { + Factory.CreateInstance(1), + Factory.CreateInstance(5), + Factory.CreateInstance(7), + Factory.CreateInstance(11), + }; + + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + await storeMembers2.ForEachAsync(x => redis.AddItemToSetAsync(Set2, x)); + await storeMembers3.ForEachAsync(x => redis.AddItemToSetAsync(Set3, x)); + + var diffMembers = await redis.GetDifferencesFromSetAsync(Set, new[] { Set2, Set3 }); + + Assert.That(diffMembers, Is.EquivalentTo( + new List { Factory.CreateInstance(2), Factory.CreateInstance(3) })); + } + + [Test] + public async Task Can_Store_DiffBetweenSets() + { + var storeMembers = Factory.CreateList(); + storeMembers.Add(Factory.CreateInstance(1)); + + var storeMembers2 = Factory.CreateList2(); + storeMembers2.Insert(0, Factory.CreateInstance(4)); + + var storeMembers3 = new List { + Factory.CreateInstance(1), + Factory.CreateInstance(5), + Factory.CreateInstance(7), + Factory.CreateInstance(11), + }; + + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + await storeMembers2.ForEachAsync(x => redis.AddItemToSetAsync(Set2, x)); + await storeMembers3.ForEachAsync(x => redis.AddItemToSetAsync(Set3, x)); + + var storeSet = redis.Sets["testdiffsetstore"]; + + await redis.StoreDifferencesFromSetAsync(storeSet, Set, new[] { Set2, Set3 }); + + Assert.That(await storeSet.ToListAsync(), Is.EquivalentTo( + new List { Factory.CreateInstance(2), Factory.CreateInstance(3) })); + + } + + [Test] + public async Task Can_GetRandomEntryFromSet() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + + var randomEntry = await redis.GetRandomItemFromSetAsync(Set); + + Assert.That(storeMembers.Contains(randomEntry), Is.True); + } + + + [Test] + public async Task Can_enumerate_small_ICollection_Set() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + + var members = new List(); + await foreach (var item in Set) + { + members.Add(item); + } + + Assert.That(members.Count, Is.EqualTo(storeMembers.Count)); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_enumerate_large_ICollection_Set() + { + if (TestConfig.IgnoreLongTests) return; + + const int setSize = 2500; + + var storeMembers = new List(); + await setSize.TimesAsync(async x => + { + await redis.AddItemToSetAsync(Set, Factory.CreateInstance(x)); + storeMembers.Add(Factory.CreateInstance(x)); + }); + + var members = new List(); + await foreach (var item in Set) + { + members.Add(item); + } + members.Sort((x, y) => x.GetId().ToString().CompareTo(y.GetId().ToString())); + Assert.That(members.Count, Is.EqualTo(storeMembers.Count)); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_Add_to_ICollection_Set() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + + var members = await Set.ToListAsync(); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_Clear_ICollection_Set() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + + Assert.That(await Set.CountAsync(), Is.EqualTo(storeMembers.Count)); + + await Set.ClearAsync(); + + Assert.That(await Set.CountAsync(), Is.EqualTo(0)); + } + + [Test] + public async Task Can_Test_Contains_in_ICollection_Set() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + + Assert.That(await Set.ContainsAsync(Factory.ExistingValue), Is.True); + Assert.That(await Set.ContainsAsync(Factory.NonExistingValue), Is.False); + } + + [Test] + public async Task Can_Remove_value_from_ICollection_Set() + { + var storeMembers = Factory.CreateList(); + await storeMembers.ForEachAsync(async x => await redis.AddItemToSetAsync(Set, x)); + + storeMembers.Remove(Factory.ExistingValue); + await Set.RemoveAsync(Factory.ExistingValue); + + var members = await Set.ToListAsync(); + + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + } + +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsModels.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsModels.Async.cs new file mode 100644 index 00000000..9eb33a51 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsModels.Async.cs @@ -0,0 +1,81 @@ +using System; +using NUnit.Framework; +using ServiceStack.Common.Tests.Models; +using ServiceStack.Redis.Tests.Support; + +namespace ServiceStack.Redis.Tests.Generic +{ + [TestFixture] + public class RedisClientSetTestsModelWithFieldsOfDifferentTypesAsync + : RedisClientSetTestsBaseAsync + { + private readonly IModelFactory factory = + new ModelWithFieldsOfDifferentTypesFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientSetTestsStringAsync + : RedisClientSetTestsBaseAsync + { + private readonly IModelFactory factory = new BuiltInsFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientSetTestsShipperAsync + : RedisClientSetTestsBaseAsync + { + private readonly IModelFactory factory = new ShipperFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientSetTestsIntAsync + : RedisClientSetTestsBaseAsync + { + private readonly IModelFactory factory = new IntFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + [TestFixture] + public class RedisClientSetTestsCustomTypeAsync + : RedisClientSetTestsBaseAsync + { + private readonly IModelFactory factory = new CustomTypeFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + + //public class RedisClientSetTestsDateTimeAsync + // : RedisClientSetTestsBaseAsync + //{ + // private readonly IModelFactory factory = new DateTimeFactory(); + + // protected override IModelFactory Factory + // { + // get { return factory; } + // } + //} + +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientTests.Async.cs new file mode 100644 index 00000000..a553c572 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientTests.Async.cs @@ -0,0 +1,129 @@ +using NUnit.Framework; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests.Generic +{ + [TestFixture, Category("Integration"), Category("Async")] + public class RedisClientTestsAsync : RedisClientTestsBaseAsync + { + [OneTimeSetUp] + public void TestFixture() + { + } + + public override void OnBeforeEachTest() + { + base.OnBeforeEachTest(); + RedisRaw.NamespacePrefix = "GenericRedisClientTests"; + } + + [Test] + public async Task Can_Set_and_Get_string() + { + const string value = "value"; + await RedisAsync.SetValueAsync("key", value); + var valueString = await RedisAsync.GetValueAsync("key"); + + Assert.That(valueString, Is.EqualTo(value)); + } + + [Test] + public async Task Can_Set_and_Get_key_with_all_byte_values() + { + const string key = "bytesKey"; + + var value = new byte[256]; + for (var i = 0; i < value.Length; i++) + { + value[i] = (byte)i; + } + + await NativeAsync.SetAsync(key, value); + var resultValue = await NativeAsync.GetAsync(key); + + Assert.That(resultValue, Is.EquivalentTo(value)); + } + + public List Sort(IEnumerable list) + { + var sortedList = list.ToList(); + sortedList.Sort((x, y) => + x.GetId().ToString().CompareTo(y.GetId().ToString())); + + return sortedList; + } + + [Test] + public async Task Can_SetBit_And_GetBit_And_BitCount() + { + const string key = "BitKey"; + const int offset = 100; + await NativeAsync.SetBitAsync(key, offset, 1); + Assert.AreEqual(1, await NativeAsync.GetBitAsync(key, offset)); + Assert.AreEqual(1, await NativeAsync.BitCountAsync(key)); + } + + public class Dummy + { + public int Id { get; set; } + public string Name { get; set; } + } + + [Test] + public async Task Can_Delete() + { + var dto = new Dummy { Id = 1, Name = "Name" }; + + await RedisAsync.StoreAsync(dto); + + Assert.That((await RedisAsync.GetAllItemsFromSetAsync(RedisRaw.NamespacePrefix + "ids:Dummy")).ToArray()[0], Is.EqualTo("1")); + Assert.That(await RedisAsync.GetByIdAsync(1), Is.Not.Null); + + await RedisAsync.DeleteAsync(dto); + + Assert.That((await RedisAsync.GetAllItemsFromSetAsync(RedisRaw.NamespacePrefix + "ids:Dummy")).Count, Is.EqualTo(0)); + Assert.That(await RedisAsync.GetByIdAsync(1), Is.Null); + } + + [Test] + public async Task Can_DeleteById() + { + var dto = new Dummy { Id = 1, Name = "Name" }; + await RedisAsync.StoreAsync(dto); + + Assert.That((await RedisAsync.GetAllItemsFromSetAsync(RedisRaw.NamespacePrefix + "ids:Dummy")).ToArray()[0], Is.EqualTo("1")); + Assert.That(await RedisAsync.GetByIdAsync(1), Is.Not.Null); + + await RedisAsync.DeleteByIdAsync(dto.Id); + + Assert.That((await RedisAsync.GetAllItemsFromSetAsync(RedisRaw.NamespacePrefix + "ids:Dummy")).Count, Is.EqualTo(0)); + Assert.That(await RedisAsync.GetByIdAsync(1), Is.Null); + } + + [Test] + public async Task Can_save_via_string() + { + var dtos = 10.Times(i => new Dummy { Id = i, Name = "Name" + i }); + + await RedisAsync.SetValueAsync("dummy:strings", dtos.ToJson()); + + var fromDtos = (await RedisAsync.GetValueAsync("dummy:strings")).FromJson>(); + + Assert.That(fromDtos.Count, Is.EqualTo(10)); + } + + [Test] + public async Task Can_save_via_types() + { + var dtos = 10.Times(i => new Dummy { Id = i, Name = "Name" + i }); + + await RedisAsync.SetAsync("dummy:strings", dtos); + + var fromDtos = await RedisAsync.GetAsync>("dummy:strings"); + + Assert.That(fromDtos.Count, Is.EqualTo(10)); + } + } +} diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBase.Async.cs new file mode 100644 index 00000000..7bf630a9 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBase.Async.cs @@ -0,0 +1,96 @@ +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Common.Tests.Models; +using ServiceStack.Redis.Generic; + +namespace ServiceStack.Redis.Tests.Generic +{ + [TestFixture, Category("Async")] + public abstract class RedisPersistenceProviderTestsBaseAsync + { + protected abstract IModelFactory Factory { get; } + + private IRedisClientAsync client; + private IRedisTypedClientAsync redis; + + [SetUp] + public async Task SetUp() + { + if (client != null) + { + await client.DisposeAsync(); + client = null; + } + client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await client.FlushAllAsync(); + + redis = client.As(); + } + + [Test] + public async Task Can_Store_and_GetById_ModelWithIdAndName() + { + const int modelId = 1; + var to = Factory.CreateInstance(modelId); + await redis.StoreAsync(to); + + var from = await redis.GetByIdAsync(to.GetId().ToString()); + + Factory.AssertIsEqual(to, from); + } + + [Test] + public async Task Can_StoreAll_and_GetByIds_ModelWithIdAndName() + { + var tos = Factory.CreateList(); + var ids = tos.ConvertAll(x => x.GetId().ToString()); + + await redis.StoreAllAsync(tos); + + var froms = await redis.GetByIdsAsync(ids); + var fromIds = froms.Map(x => x.GetId().ToString()); + + Assert.That(fromIds, Is.EquivalentTo(ids)); + } + + [Test] + public async Task Can_Delete_ModelWithIdAndName() + { + var tos = Factory.CreateList(); + var ids = tos.ConvertAll(x => x.GetId().ToString()); + + await redis.StoreAllAsync(tos); + + var deleteIds = new[] { ids[1], ids[3] }; + + await redis.DeleteByIdsAsync(deleteIds); + + var froms = await redis.GetByIdsAsync(ids); + var fromIds = froms.Map(x => x.GetId().ToString()); + + var expectedIds = ids.Where(x => !deleteIds.Contains(x)) + .ToList().ConvertAll(x => x.ToString()); + + Assert.That(fromIds, Is.EquivalentTo(expectedIds)); + } + + [Test] + public async Task Can_DeleteAll() + { + var tos = Factory.CreateList(); + await redis.StoreAllAsync(tos); + + var all = await redis.GetAllAsync(); + + Assert.That(all.Count, Is.EqualTo(tos.Count)); + + await redis.DeleteAllAsync(); + + all = await redis.GetAllAsync(); + + Assert.That(all.Count, Is.EqualTo(0)); + } + + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBaseImpl.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBaseImpl.Async.cs new file mode 100644 index 00000000..3f7c00cf --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBaseImpl.Async.cs @@ -0,0 +1,40 @@ +using System; +using ServiceStack.Common.Tests.Models; + +namespace ServiceStack.Redis.Tests.Generic +{ + public class RedisPersistenceProviderTestsModelWithFieldsOfDifferentTypesAsync + : RedisPersistenceProviderTestsBaseAsync + { + private readonly IModelFactory factory = + new ModelWithFieldsOfDifferentTypesFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + public class RedisPersistenceProviderTestsStringFactoryAsync + : RedisPersistenceProviderTestsBaseAsync + { + private readonly IModelFactory factory = new BuiltInsFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + + public class RedisPersistenceProviderTestsShipperAsync + : RedisPersistenceProviderTestsBaseAsync + { + private readonly IModelFactory factory = new ShipperFactory(); + + protected override IModelFactory Factory + { + get { return factory; } + } + } + +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.Async.cs new file mode 100644 index 00000000..91c57d9d --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.Async.cs @@ -0,0 +1,126 @@ +using NUnit.Framework; +using ServiceStack.Redis.Generic; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests.Generic +{ + [TestFixture, Category("Integration")] + public class RedisTypedClientTestsAsync + : RedisClientTestsBaseAsync + { + public class CacheRecord + { + public CacheRecord() + { + this.Children = new List(); + } + + public string Id { get; set; } + public List Children { get; set; } + } + + public class CacheRecordChild + { + public string Id { get; set; } + public string Data { get; set; } + } + + protected IRedisTypedClientAsync RedisTyped; + + [SetUp] + public override void OnBeforeEachTest() + { + base.OnBeforeEachTest(); + + RedisRaw?.Dispose(); + RedisRaw = new RedisClient(TestConfig.SingleHost) + { + NamespacePrefix = "RedisTypedClientTests:" + }; + RedisTyped = RedisAsync.As(); + } + + [TearDown] + public virtual async Task TearDown() + { + foreach (var t in await RedisAsync.SearchKeysAsync(RedisRaw.NamespacePrefix + "*")) + { + await NativeAsync.DelAsync(t); + } + } + + [Test] + public async Task Can_Store_with_Prefix() + { + var expected = new CacheRecord() { Id = "123" }; + await RedisTyped.StoreAsync(expected); + var current = await RedisAsync.GetAsync("RedisTypedClientTests:urn:cacherecord:123"); + Assert.AreEqual(expected.Id, current.Id); + } + + [Test] + public async Task Can_Expire() + { + var cachedRecord = new CacheRecord + { + Id = "key", + Children = { + new CacheRecordChild { Id = "childKey", Data = "data" } + } + }; + + await RedisTyped.StoreAsync(cachedRecord); + await RedisTyped.ExpireInAsync("key", TimeSpan.FromSeconds(1)); + Assert.That(await RedisTyped.GetByIdAsync("key"), Is.Not.Null); + await Task.Delay(2000); + Assert.That(await RedisTyped.GetByIdAsync("key"), Is.Null); + } + + [Ignore("Changes in system clock can break test")] + [Test] + public async Task Can_ExpireAt() + { + var cachedRecord = new CacheRecord + { + Id = "key", + Children = { + new CacheRecordChild { Id = "childKey", Data = "data" } + } + }; + + await RedisTyped.StoreAsync(cachedRecord); + + var in2Secs = DateTime.UtcNow.AddSeconds(2); + + await RedisTyped.ExpireAtAsync("key", in2Secs); + + Assert.That(await RedisTyped.GetByIdAsync("key"), Is.Not.Null); + await Task.Delay(3000); + Assert.That(await RedisTyped.GetByIdAsync("key"), Is.Null); + } + + [Test] + public async Task Can_Delete_All_Items() + { + var cachedRecord = new CacheRecord + { + Id = "key", + Children = { + new CacheRecordChild { Id = "childKey", Data = "data" } + } + }; + + await RedisTyped.StoreAsync(cachedRecord); + + Assert.That(await RedisTyped.GetByIdAsync("key"), Is.Not.Null); + + await RedisTyped.DeleteAllAsync(); + + Assert.That(await RedisTyped.GetByIdAsync("key"), Is.Null); + + } + } + +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisTypedPipelineTests.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedPipelineTests.Async.cs new file mode 100644 index 00000000..663fdf97 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedPipelineTests.Async.cs @@ -0,0 +1,230 @@ +using NUnit.Framework; +using ServiceStack.Common.Tests.Models; +using ServiceStack.Redis.Generic; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests.Generic +{ + [TestFixture] + public class RedisTypedPipelineTestsAsync + : RedisClientTestsBaseAsync + { + public RedisTypedPipelineTestsAsync() + { + CleanMask = "gmultitest*"; + } + + private const string Key = "gmultitest"; + private const string ListKey = "gmultitest-list"; + private const string SetKey = "gmultitest-set"; + private const string SortedSetKey = "gmultitest-sortedset"; + + readonly ShipperFactory modelFactory = new ShipperFactory(); + private IRedisTypedClientAsync typedClient; + private Shipper model; + + public override void OnBeforeEachTest() + { + base.OnBeforeEachTest(); + + typedClient = RedisAsync.As(); + model = modelFactory.CreateInstance(1); + } + + + [Test] + public async Task Can_call_single_operation_in_pipeline() + { + Assert.That(await typedClient.GetValueAsync(Key), Is.Null); + + await using (var pipeline = typedClient.CreatePipeline()) + { + pipeline.QueueCommand(r => r.SetValueAsync(Key, model)); + + await pipeline.FlushAsync(); + } + + modelFactory.AssertIsEqual(await typedClient.GetValueAsync(Key), model); + } + + [Test] + public async Task No_commit_of_atomic_pipelines_discards_all_commands() + { + Assert.That(await typedClient.GetValueAsync(Key), Is.Null); + + await using (var pipeline = typedClient.CreatePipeline()) + { + pipeline.QueueCommand(r => r.SetValueAsync(Key, model)); + } + + Assert.That(await typedClient.GetValueAsync(Key), Is.Null); + } + + [Test] + public async Task Exception_in_atomic_pipelines_discards_all_commands() + { + Assert.That(await typedClient.GetValueAsync(Key), Is.Null); + try + { + await using var pipeline = typedClient.CreatePipeline(); + pipeline.QueueCommand(r => r.SetValueAsync(Key, model)); + throw new NotSupportedException(); + } + catch (NotSupportedException) + { + Assert.That(await typedClient.GetValueAsync(Key), Is.Null); + } + } + + [Test] + public async Task Can_call_single_operation_3_Times_in_pipeline() + { + var typedList = typedClient.Lists[ListKey]; + Assert.That(await typedList.CountAsync(), Is.EqualTo(0)); + + await using (var pipeline = typedClient.CreatePipeline()) + { + pipeline.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(1))); + pipeline.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(2))); + pipeline.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(3))); + + await pipeline.FlushAsync(); + } + + Assert.That(await typedList.CountAsync(), Is.EqualTo(3)); + } + + [Test] + public async Task Can_call_single_operation_with_callback_3_Times_in_pipeline() + { + var results = new List(); + + var typedList = typedClient.Lists[ListKey]; + Assert.That(await typedList.CountAsync(), Is.EqualTo(0)); + + await using (var pipeline = typedClient.CreatePipeline()) + { + pipeline.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(1)), () => results.Add(1)); + pipeline.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(2)), () => results.Add(2)); + pipeline.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(3)), () => results.Add(3)); + + await pipeline.FlushAsync(); + } + + Assert.That(await typedList.CountAsync(), Is.EqualTo(3)); + Assert.That(results, Is.EquivalentTo(new List { 1, 2, 3 })); + } + + [Test] + public async Task Supports_different_operation_types_in_same_pipeline() + { + var incrementResults = new List(); + var collectionCounts = new List(); + var containsItem = false; + + var typedList = typedClient.Lists[ListKey]; + var typedSet = typedClient.Sets[SetKey]; + var typedSortedSet = typedClient.SortedSets[SortedSetKey]; + + Assert.That(await typedClient.GetValueAsync(Key), Is.Null); + await using (var pipeline = typedClient.CreatePipeline()) + { + pipeline.QueueCommand(r => r.IncrementValueAsync(Key), intResult => incrementResults.Add(intResult)); + pipeline.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(1))); + pipeline.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(2))); + pipeline.QueueCommand(r => r.AddItemToSetAsync(typedSet, modelFactory.CreateInstance(3))); + pipeline.QueueCommand(r => r.SetContainsItemAsync(typedSet, modelFactory.CreateInstance(3)), b => containsItem = b); + pipeline.QueueCommand(r => r.AddItemToSortedSetAsync(typedSortedSet, modelFactory.CreateInstance(4))); + pipeline.QueueCommand(r => r.AddItemToSortedSetAsync(typedSortedSet, modelFactory.CreateInstance(5))); + pipeline.QueueCommand(r => r.AddItemToSortedSetAsync(typedSortedSet, modelFactory.CreateInstance(6))); + pipeline.QueueCommand(r => r.GetListCountAsync(typedList), intResult => collectionCounts.Add(intResult)); + pipeline.QueueCommand(r => r.GetSetCountAsync(typedSet), intResult => collectionCounts.Add(intResult)); + pipeline.QueueCommand(r => r.GetSortedSetCountAsync(typedSortedSet), intResult => collectionCounts.Add(intResult)); + pipeline.QueueCommand(r => r.IncrementValueAsync(Key), intResult => incrementResults.Add(intResult)); + + await pipeline.FlushAsync(); + } + + Assert.That(containsItem, Is.True); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("2")); + Assert.That(incrementResults, Is.EquivalentTo(new List { 1, 2 })); + Assert.That(collectionCounts, Is.EquivalentTo(new List { 2, 1, 3 })); + + modelFactory.AssertListsAreEqual(await typedList.GetAllAsync(), new List + { + modelFactory.CreateInstance(1), modelFactory.CreateInstance(2) + }); + + Assert.That(await typedSet.GetAllAsync(), Is.EquivalentTo(new List + { + modelFactory.CreateInstance(3) + })); + + modelFactory.AssertListsAreEqual(await typedSortedSet.GetAllAsync(), new List + { + modelFactory.CreateInstance(4), modelFactory.CreateInstance(5), modelFactory.CreateInstance(6) + }); + } + + [Test] + public async Task Can_call_multi_string_operations_in_pipeline() + { + Shipper item1 = null; + Shipper item4 = null; + + var results = new List(); + + var typedList = typedClient.Lists[ListKey]; + Assert.That(await typedList.CountAsync(), Is.EqualTo(0)); + + await using (var pipeline = typedClient.CreatePipeline()) + { + pipeline.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(1))); + pipeline.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(2))); + pipeline.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(3))); + pipeline.QueueCommand(r => r.GetAllItemsFromListAsync(typedList), x => results = x); + pipeline.QueueCommand(r => r.GetItemFromListAsync(typedList, 0), x => item1 = x); + pipeline.QueueCommand(r => r.GetItemFromListAsync(typedList, 4), x => item4 = x); + + await pipeline.FlushAsync(); + } + + Assert.That(await typedList.CountAsync(), Is.EqualTo(3)); + + modelFactory.AssertListsAreEqual(results, new List + { + modelFactory.CreateInstance(1), modelFactory.CreateInstance(2), modelFactory.CreateInstance(3) + }); + + modelFactory.AssertIsEqual(item1, modelFactory.CreateInstance(1)); + Assert.That(item4, Is.Null); + } + [Test] + public async Task Pipeline_can_be_replayed() + { + const string keySquared = Key + Key; + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + Assert.That(await RedisAsync.GetValueAsync(keySquared), Is.Null); + await using var pipeline = typedClient.CreatePipeline(); + pipeline.QueueCommand(r => r.IncrementValueAsync(Key)); + pipeline.QueueCommand(r => r.IncrementValueAsync(keySquared)); + await pipeline.FlushAsync(); + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("1")); + Assert.That(await RedisAsync.GetValueAsync(keySquared), Is.EqualTo("1")); + await typedClient.RemoveEntryAsync(Key); + await typedClient.RemoveEntryAsync(keySquared); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + Assert.That(await RedisAsync.GetValueAsync(keySquared), Is.Null); + + await pipeline.ReplayAsync(); + await pipeline.DisposeAsync(); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("1")); + Assert.That(await RedisAsync.GetValueAsync(keySquared), Is.EqualTo("1")); + + } + + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisTypedTransactionTests.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedTransactionTests.Async.cs new file mode 100644 index 00000000..9d096db7 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedTransactionTests.Async.cs @@ -0,0 +1,240 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Common.Tests.Models; +using ServiceStack.Redis.Generic; + +namespace ServiceStack.Redis.Tests.Generic +{ + [TestFixture] + public class RedisTypedTransactionTestsAsync + : RedisClientTestsBaseAsync + { + private const string Key = "multitest"; + private const string ListKey = "multitest-list"; + private const string SetKey = "multitest-set"; + private const string SortedSetKey = "multitest-sortedset"; + + readonly ShipperFactory modelFactory = new ShipperFactory(); + private IRedisTypedClientAsync typedClient; + private Shipper model; + + public RedisTypedTransactionTestsAsync() + { + CleanMask = "multitest*"; + } + + public override void OnBeforeEachTest() + { + base.OnBeforeEachTest(); + + typedClient = RedisAsync.As(); + model = modelFactory.CreateInstance(1); + } + + [Test] + public async Task Can_call_single_operation_in_transaction() + { + Assert.That(await typedClient.GetValueAsync(Key), Is.Null); + + await using (var trans = await typedClient.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.SetValueAsync(Key, model)); + + await trans.CommitAsync(); + } + + modelFactory.AssertIsEqual(await typedClient.GetValueAsync(Key), model); + } + + [Test] + public async Task No_commit_of_atomic_transactions_discards_all_commands() + { + Assert.That(await typedClient.GetValueAsync(Key), Is.Null); + + await using (var trans = await typedClient.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.SetValueAsync(Key, model)); + } + + Assert.That(await typedClient.GetValueAsync(Key), Is.Null); + } + + [Test] + public async Task Exception_in_atomic_transactions_discards_all_commands() + { + Assert.That(await typedClient.GetValueAsync(Key), Is.Null); + try + { + await using var trans = await typedClient.CreateTransactionAsync(); + trans.QueueCommand(r => r.SetValueAsync(Key, model)); + throw new NotSupportedException(); + } + catch (NotSupportedException) + { + Assert.That(await typedClient.GetValueAsync(Key), Is.Null); + } + } + + [Test] + public async Task Can_call_single_operation_3_Times_in_transaction() + { + var typedList = typedClient.Lists[ListKey]; + Assert.That(await typedList.CountAsync(), Is.EqualTo(0)); + + await using (var trans = await typedClient.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(1))); + trans.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(2))); + trans.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(3))); + + await trans.CommitAsync(); + } + + Assert.That(await typedList.CountAsync(), Is.EqualTo(3)); + } + + [Test] + public async Task Can_call_single_operation_with_callback_3_Times_in_transaction() + { + var results = new List(); + + var typedList = typedClient.Lists[ListKey]; + Assert.That(await typedList.CountAsync(), Is.EqualTo(0)); + + await using (var trans = await typedClient.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(1)), () => results.Add(1)); + trans.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(2)), () => results.Add(2)); + trans.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(3)), () => results.Add(3)); + + await trans.CommitAsync(); + } + + Assert.That(await typedList.CountAsync(), Is.EqualTo(3)); + Assert.That(results, Is.EquivalentTo(new List { 1, 2, 3 })); + } + + [Test] + public async Task Supports_different_operation_types_in_same_transaction() + { + var incrementResults = new List(); + var collectionCounts = new List(); + var containsItem = false; + + var typedList = typedClient.Lists[ListKey]; + var typedSet = typedClient.Sets[SetKey]; + var typedSortedSet = typedClient.SortedSets[SortedSetKey]; + + Assert.That(await typedClient.GetValueAsync(Key), Is.Null); + await using (var trans = await typedClient.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.IncrementValueAsync(Key), intResult => incrementResults.Add(intResult)); + trans.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(1))); + trans.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(2))); + trans.QueueCommand(r => r.AddItemToSetAsync(typedSet, modelFactory.CreateInstance(3))); + trans.QueueCommand(r => r.SetContainsItemAsync(typedSet, modelFactory.CreateInstance(3)), b => containsItem = b); + trans.QueueCommand(r => r.AddItemToSortedSetAsync(typedSortedSet, modelFactory.CreateInstance(4))); + trans.QueueCommand(r => r.AddItemToSortedSetAsync(typedSortedSet, modelFactory.CreateInstance(5))); + trans.QueueCommand(r => r.AddItemToSortedSetAsync(typedSortedSet, modelFactory.CreateInstance(6))); + trans.QueueCommand(r => r.GetListCountAsync(typedList), intResult => collectionCounts.Add(intResult)); + trans.QueueCommand(r => r.GetSetCountAsync(typedSet), intResult => collectionCounts.Add(intResult)); + trans.QueueCommand(r => r.GetSortedSetCountAsync(typedSortedSet), intResult => collectionCounts.Add(intResult)); + trans.QueueCommand(r => r.IncrementValueAsync(Key), intResult => incrementResults.Add(intResult)); + + await trans.CommitAsync(); + } + + Assert.That(containsItem, Is.True); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("2")); + Assert.That(incrementResults, Is.EquivalentTo(new List { 1, 2 })); + Assert.That(collectionCounts, Is.EquivalentTo(new List { 2, 1, 3 })); + + modelFactory.AssertListsAreEqual(await typedList.GetAllAsync(), new List + { + modelFactory.CreateInstance(1), modelFactory.CreateInstance(2) + }); + + Assert.That(await typedSet.GetAllAsync(), Is.EquivalentTo(new List + { + modelFactory.CreateInstance(3) + })); + + modelFactory.AssertListsAreEqual(await typedSortedSet.GetAllAsync(), new List + { + modelFactory.CreateInstance(4), modelFactory.CreateInstance(5), modelFactory.CreateInstance(6) + }); + } + + [Test] + public async Task Can_call_multi_string_operations_in_transaction() + { + Shipper item1 = null; + Shipper item4 = null; + + var results = new List(); + + var typedList = typedClient.Lists[ListKey]; + Assert.That(await typedList.CountAsync(), Is.EqualTo(0)); + + await using (var trans = await typedClient.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(1))); + trans.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(2))); + trans.QueueCommand(r => r.AddItemToListAsync(typedList, modelFactory.CreateInstance(3))); + trans.QueueCommand(r => r.GetAllItemsFromListAsync(typedList), x => results = x); + trans.QueueCommand(r => r.GetItemFromListAsync(typedList, 0), x => item1 = x); + trans.QueueCommand(r => r.GetItemFromListAsync(typedList, 4), x => item4 = x); + + await trans.CommitAsync(); + } + + Assert.That(await typedList.CountAsync(), Is.EqualTo(3)); + + modelFactory.AssertListsAreEqual(results, new List + { + modelFactory.CreateInstance(1), modelFactory.CreateInstance(2), modelFactory.CreateInstance(3) + }); + + modelFactory.AssertIsEqual(item1, modelFactory.CreateInstance(1)); + Assert.That(item4, Is.Null); + } + [Test] + // Operations that are not supported in older versions will look at server info to determine what to do. + // If server info is fetched each time, then it will interfer with transaction + public async Task Can_call_operation_not_supported_on_older_servers_in_transaction() + { + var temp = new byte[1]; + await using var trans = await RedisAsync.CreateTransactionAsync(); + trans.QueueCommand(r => ((IRedisNativeClientAsync)r).SetExAsync("key", 5, temp)); + await trans.CommitAsync(); + } + + + [Test] + public async Task Transaction_can_be_replayed() + { + string KeySquared = Key + Key; + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.Null); + await using var trans = await RedisAsync.CreateTransactionAsync(); + trans.QueueCommand(r => r.IncrementValueAsync(Key)); + trans.QueueCommand(r => r.IncrementValueAsync(KeySquared)); + await trans.CommitAsync(); + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("1")); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.EqualTo("1")); + await NativeAsync.DelAsync(Key); + await NativeAsync.DelAsync(KeySquared); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.Null); + + await trans.ReplayAsync(); + await trans.DisposeAsync(); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("1")); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.EqualTo("1")); + } + + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/LexTests.Async.cs b/tests/ServiceStack.Redis.Tests/LexTests.Async.cs new file mode 100644 index 00000000..d00e3f0f --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/LexTests.Async.cs @@ -0,0 +1,115 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class LexTestsAsync + : RedisClientTestsBaseAsync + { + readonly string[] values = "a,b,c,d,e,f,g".Split(','); + + [SetUp] + public async Task SetUp() + { + await RedisAsync.FlushAllAsync(); + foreach(var x in values) + { + await NativeAsync.ZAddAsync("zset", 0, x.ToUtf8Bytes()); + } + } + + [Test] + public async Task Can_ZRangeByLex_all_entries() + { + var results = await NativeAsync.ZRangeByLexAsync("zset", "-", "+"); + + Assert.That(results.Map(x => x.FromUtf8Bytes()), Is.EquivalentTo(values)); + + results = await NativeAsync.ZRangeByLexAsync("zset", "-", "+", 1, 3); + Assert.That(results.Map(x => x.FromUtf8Bytes()), Is.EquivalentTo(new[] { "b", "c", "d" })); + } + + [Test] + public async Task Can_ZRangeByLex_Desc() + { + var descInclusive = await NativeAsync.ZRangeByLexAsync("zset", "-", "[c"); + Assert.That(descInclusive.Map(x => x.FromUtf8Bytes()), Is.EquivalentTo(new[] { "a", "b", "c" })); + + var descExclusive = await NativeAsync.ZRangeByLexAsync("zset", "-", "(c"); + Assert.That(descExclusive.Map(x => x.FromUtf8Bytes()), Is.EquivalentTo(new[] { "a", "b" })); + } + + [Test] + public async Task Can_ZRangeByLex_Min_and_Max() + { + var range = await NativeAsync.ZRangeByLexAsync("zset", "[aaa", "(g"); + Assert.That(range.Map(x => x.FromUtf8Bytes()), + Is.EquivalentTo(new[] { "b", "c", "d", "e", "f" })); + } + + [Test] + public async Task Can_ZlexCount() + { + var total = await NativeAsync.ZLexCountAsync("zset", "-", "+"); + Assert.That(total, Is.EqualTo(values.Length)); + + Assert.That(await NativeAsync.ZLexCountAsync("zset", "-", "[c"), Is.EqualTo(3)); + Assert.That(await NativeAsync.ZLexCountAsync("zset", "-", "(c"), Is.EqualTo(2)); + } + + [Test] + public async Task Can_ZRemRangeByLex() + { + var removed = await NativeAsync.ZRemRangeByLexAsync("zset", "[aaa", "(g"); + Assert.That(removed, Is.EqualTo(5)); + + var remainder = await NativeAsync.ZRangeByLexAsync("zset", "-", "+"); + Assert.That(remainder.Map(x => x.FromUtf8Bytes()), Is.EqualTo(new[] { "a", "g" })); + } + + [Test] + public async Task Can_SearchSortedSet() + { + Assert.That(await RedisAsync.SearchSortedSetAsync("zset"), Is.EquivalentTo(values)); + Assert.That(await RedisAsync.SearchSortedSetAsync("zset", start: "-"), Is.EquivalentTo(values)); + Assert.That(await RedisAsync.SearchSortedSetAsync("zset", end: "+"), Is.EquivalentTo(values)); + + Assert.That((await RedisAsync.SearchSortedSetAsync("zset", start: "[aaa")).Count, Is.EqualTo(values.Length - 1)); + Assert.That((await RedisAsync.SearchSortedSetAsync("zset", end: "(g")).Count, Is.EqualTo(values.Length - 1)); + Assert.That((await RedisAsync.SearchSortedSetAsync("zset", "[aaa", "(g")).Count, Is.EqualTo(values.Length - 2)); + + Assert.That((await RedisAsync.SearchSortedSetAsync("zset", "a", "c")).Count, Is.EqualTo(3)); + Assert.That((await RedisAsync.SearchSortedSetAsync("zset", "[a", "[c")).Count, Is.EqualTo(3)); + Assert.That((await RedisAsync.SearchSortedSetAsync("zset", "a", "(c")).Count, Is.EqualTo(2)); + Assert.That((await RedisAsync.SearchSortedSetAsync("zset", "(a", "(c")).Count, Is.EqualTo(1)); + } + + [Test] + public async Task Can_SearchSortedSetCount() + { + Assert.That(await RedisAsync.SearchSortedSetAsync("zset"), Is.EquivalentTo(values)); + Assert.That(await RedisAsync.SearchSortedSetCountAsync("zset", start: "-"), Is.EqualTo(values.Length)); + Assert.That(await RedisAsync.SearchSortedSetCountAsync("zset", end: "+"), Is.EqualTo(values.Length)); + + Assert.That(await RedisAsync.SearchSortedSetCountAsync("zset", start: "[aaa"), Is.EqualTo(values.Length - 1)); + Assert.That(await RedisAsync.SearchSortedSetCountAsync("zset", end: "(g"), Is.EqualTo(values.Length - 1)); + Assert.That(await RedisAsync.SearchSortedSetCountAsync("zset", "[aaa", "(g"), Is.EqualTo(values.Length - 2)); + + Assert.That(await RedisAsync.SearchSortedSetCountAsync("zset", "a", "c"), Is.EqualTo(3)); + Assert.That(await RedisAsync.SearchSortedSetCountAsync("zset", "[a", "[c"), Is.EqualTo(3)); + Assert.That(await RedisAsync.SearchSortedSetCountAsync("zset", "a", "(c"), Is.EqualTo(2)); + Assert.That(await RedisAsync.SearchSortedSetCountAsync("zset", "(a", "(c"), Is.EqualTo(1)); + } + + [Test] + public async Task Can_RemoveRangeFromSortedSetBySearch() + { + var removed = await RedisAsync.RemoveRangeFromSortedSetBySearchAsync("zset", "[aaa", "(g"); + Assert.That(removed, Is.EqualTo(5)); + + var remainder = await RedisAsync.SearchSortedSetAsync("zset"); + Assert.That(remainder, Is.EqualTo(new[] { "a", "g" })); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/LuaCachedScripts.Async.cs b/tests/ServiceStack.Redis.Tests/LuaCachedScripts.Async.cs new file mode 100644 index 00000000..aca93239 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/LuaCachedScripts.Async.cs @@ -0,0 +1,295 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Text; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + [Category("Async")] + public class LuaCachedScriptsAsync + { + private const string LuaScript = @" +local limit = tonumber(ARGV[2]) +local pattern = ARGV[1] +local cursor = 0 +local len = 0 +local results = {} + +repeat + local r = redis.call('scan', cursor, 'MATCH', pattern, 'COUNT', limit) + cursor = tonumber(r[1]) + for k,v in ipairs(r[2]) do + table.insert(results, v) + len = len + 1 + if len == limit then break end + end +until cursor == 0 or len == limit + +return results +"; + + private static async Task AddTestKeysAsync(IRedisClientAsync redis, int count) + { + for (int i = 0; i < count; i++) + await redis.SetValueAsync("key:" + i, "value:" + i); + } + + [Test] + public async Task Can_call_repeated_scans_in_LUA() + { + await using var redis = new RedisClient().ForAsyncOnly(); + await AddTestKeysAsync(redis, 20); + + var r = await redis.ExecLuaAsync(LuaScript, "key:*", "10"); + Assert.That(r.Children.Count, Is.EqualTo(10)); + + r = await redis.ExecLuaAsync(LuaScript, "key:*", "40"); + Assert.That(r.Children.Count, Is.EqualTo(20)); + } + + [Test] + public async Task Can_call_Cached_Lua() + { + await using var redis = new RedisClient().ForAsyncOnly(); + await AddTestKeysAsync(redis, 20); + + var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, "key:*", "10")); + Assert.That(r.Children.Count, Is.EqualTo(10)); + + r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, "key:*", "10")); + Assert.That(r.Children.Count, Is.EqualTo(10)); + } + + [Test] + public async Task Can_call_Cached_Lua_even_after_script_is_flushed() + { + await using var redis = new RedisClient().ForAsyncOnly(); + await AddTestKeysAsync(redis, 20); + + var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, "key:*", "10")); + Assert.That(r.Children.Count, Is.EqualTo(10)); + + await ((IRedisNativeClientAsync)redis).ScriptFlushAsync(); + + r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, "key:*", "10")); + Assert.That(r.Children.Count, Is.EqualTo(10)); + } + + [Test] + public async Task Can_call_repeated_scans_in_LUA_longhand() + { + await using var redis = new RedisClient().ForAsyncOnly(); + await AddTestKeysAsync(redis, 20); + + var r = await redis.ExecLuaAsync(LuaScript, null, new[] { "key:*", "10" }); + Assert.That(r.Children.Count, Is.EqualTo(10)); + + r = await redis.ExecLuaAsync(LuaScript, null, new[] { "key:*", "40" }); + Assert.That(r.Children.Count, Is.EqualTo(20)); + } + + [Test] + public async Task Can_call_Cached_Lua_longhand() + { + await using var redis = new RedisClient().ForAsyncOnly(); + await AddTestKeysAsync(redis, 20); + + var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); + Assert.That(r.Children.Count, Is.EqualTo(10)); + + r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); + Assert.That(r.Children.Count, Is.EqualTo(10)); + } + + [Test] + public async Task Can_call_Cached_Lua_even_after_script_is_flushed_longhand() + { + await using var redis = new RedisClient().ForAsyncOnly(); + await AddTestKeysAsync(redis, 20); + + var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); + Assert.That(r.Children.Count, Is.EqualTo(10)); + + await ((IRedisNativeClientAsync)redis).ScriptFlushAsync(); + + r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); + Assert.That(r.Children.Count, Is.EqualTo(10)); + } + + private const string KeyAttributesScript = @" +local limit = tonumber(ARGV[2]) +local pattern = ARGV[1] +local cursor = 0 +local len = 0 +local keys = {} + +repeat + local r = redis.call('scan', cursor, 'MATCH', pattern, 'COUNT', limit) + cursor = tonumber(r[1]) + for k,v in ipairs(r[2]) do + table.insert(keys, v) + len = len + 1 + if len == limit then break end + end +until cursor == 0 or len == limit + +local keyAttrs = {} +for i,key in ipairs(keys) do + local type = redis.call('type', key)['ok'] + local pttl = redis.call('pttl', key) + local size = 0 + if type == 'string' then + size = redis.call('strlen', key) + elseif type == 'list' then + size = redis.call('llen', key) + elseif type == 'set' then + size = redis.call('scard', key) + elseif type == 'zset' then + size = redis.call('zcard', key) + elseif type == 'hash' then + size = redis.call('hlen', key) + end + + local attrs = {['id'] = key, ['type'] = type, ['ttl'] = pttl, ['size'] = size} + + table.insert(keyAttrs, attrs) +end + +return cjson.encode(keyAttrs)"; + + [Test] + public async Task Can_call_script_with_complex_response() + { + await using var redis = new RedisClient().ForAsyncOnly(); + var r = await redis.ExecCachedLuaAsync(KeyAttributesScript, sha1 => + redis.ExecLuaShaAsStringAsync(sha1, "key:*", "10")); + + r.Print(); + + var results = r.FromJson>(); + + Assert.That(results.Count, Is.EqualTo(10)); + + var result = results[0]; + Assert.That(result.Id.StartsWith("key:")); + Assert.That(result.Type, Is.EqualTo("string")); + Assert.That(result.Size, Is.GreaterThan("value:".Length)); + Assert.That(result.Ttl, Is.EqualTo(-1)); + } + + [Test] + public async Task Can_call_script_with_complex_response_longhand() + { + await using var redis = new RedisClient().ForAsyncOnly(); + var r = await redis.ExecCachedLuaAsync(KeyAttributesScript, sha1 => + redis.ExecLuaShaAsStringAsync(sha1, null, new[] { "key:*", "10" })); + + r.Print(); + + var results = r.FromJson>(); + + Assert.That(results.Count, Is.EqualTo(10)); + + var result = results[0]; + Assert.That(result.Id.StartsWith("key:")); + Assert.That(result.Type, Is.EqualTo("string")); + Assert.That(result.Size, Is.GreaterThan("value:".Length)); + Assert.That(result.Ttl, Is.EqualTo(-1)); + } + + public class SearchResult + { + public string Id { get; set; } + public string Type { get; set; } + public long Ttl { get; set; } + public long Size { get; set; } + } + + [Test] + public async Task Can_merge_multiple_SearchResults() + { + await using var Redis = new RedisClient().ForAsyncOnly(); + var limit = 10; + var query = "key:*"; + + List keys = new List(limit); + await foreach (var key in Redis.ScanAllKeysAsync(pattern: query, pageSize: limit)) + { + keys.Add(key); + if (keys.Count == limit) break; + } + + var keyTypes = new Dictionary(); + var keyTtls = new Dictionary(); + var keySizes = new Dictionary(); + + if (keys.Count > 0) + { + await using (var pipeline = Redis.CreatePipeline()) + { + foreach (var key in keys) + pipeline.QueueCommand(r => r.TypeAsync(key), x => keyTypes[key] = x); + + foreach (var key in keys) + pipeline.QueueCommand(r => ((IRedisNativeClientAsync)r).PTtlAsync(key), x => keyTtls[key] = x); + + await pipeline.FlushAsync(); + } + + await using (var pipeline = Redis.CreatePipeline()) + { + foreach (var entry in keyTypes) + { + var key = entry.Key; + switch (entry.Value) + { + case "string": + pipeline.QueueCommand(r => r.GetStringCountAsync(key), x => keySizes[key] = x); + break; + case "list": + pipeline.QueueCommand(r => r.GetListCountAsync(key), x => keySizes[key] = x); + break; + case "set": + pipeline.QueueCommand(r => r.GetSetCountAsync(key), x => keySizes[key] = x); + break; + case "zset": + pipeline.QueueCommand(r => r.GetSortedSetCountAsync(key), x => keySizes[key] = x); + break; + case "hash": + pipeline.QueueCommand(r => r.GetHashCountAsync(key), x => keySizes[key] = x); + break; + } + } + + await pipeline.FlushAsync(); + } + } + + var results = keys.Map(x => new SearchResult + { + Id = x, + Type = keyTypes.GetValueOrDefault(x), + Ttl = keyTtls.GetValueOrDefault(x), + Size = keySizes.GetValueOrDefault(x), + }); + + Assert.That(results.Count, Is.EqualTo(limit)); + + var result = results[0]; + Assert.That(result.Id.StartsWith("key:")); + Assert.That(result.Type, Is.EqualTo("string")); + Assert.That(result.Size, Is.GreaterThan("value:".Length)); + Assert.That(result.Ttl, Is.EqualTo(-1)); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/LuaCachedScripts.cs b/tests/ServiceStack.Redis.Tests/LuaCachedScripts.cs index fd764dfe..80491fec 100644 --- a/tests/ServiceStack.Redis.Tests/LuaCachedScripts.cs +++ b/tests/ServiceStack.Redis.Tests/LuaCachedScripts.cs @@ -71,6 +71,8 @@ public void Can_call_Cached_Lua_even_after_script_is_flushed() { using (var redis = new RedisClient()) { + AddTestKeys(redis, 20); + var r = redis.ExecCachedLua(LuaScript, sha1 => redis.ExecLuaSha(sha1, "key:*", "10")); Assert.That(r.Children.Count, Is.EqualTo(10)); diff --git a/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.Async.cs b/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.Async.cs new file mode 100644 index 00000000..fdf442f6 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.Async.cs @@ -0,0 +1,440 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Text; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Integration"), Category("Async")] + public class PooledRedisClientManagerTestsAsync + { + [OneTimeSetUp] + public void OneTimeSetUp() + { + RedisConfig.VerifyMasterConnections = false; + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + RedisConfig.VerifyMasterConnections = true; + } + + readonly string[] testReadWriteHosts = new[] { + "readwrite1", "readwrite2:6000", "192.168.0.1", "localhost" + }; + + readonly string[] testReadOnlyHosts = new[] { + "read1", "read2:7000", "127.0.0.1" + }; + + private string firstReadWriteHost; + private string firstReadOnlyHost; + + [SetUp] + public void OnBeforeEachTest() + { + firstReadWriteHost = testReadWriteHosts[0]; + firstReadOnlyHost = testReadOnlyHosts[0]; + } + + public IRedisClientsManagerAsync CreateManager(string[] readWriteHosts, string[] readOnlyHosts, int? defaultDb = null) + { + return new PooledRedisClientManager(readWriteHosts, readOnlyHosts, + new RedisClientManagerConfig + { + MaxWritePoolSize = readWriteHosts.Length, + MaxReadPoolSize = readOnlyHosts.Length, + AutoStart = false, + DefaultDb = defaultDb + }); + } + public IRedisClientsManagerAsync CreateManager(params string[] readWriteHosts) + { + return CreateManager(readWriteHosts, readWriteHosts); + } + + public IRedisClientsManagerAsync CreateManager() + { + return CreateManager(testReadWriteHosts, testReadOnlyHosts); + } + + public IRedisClientsManagerAsync CreateAndStartManager() + { + var manager = CreateManager(); + ((PooledRedisClientManager)manager).Start(); + return manager; + } + + [Test] + public async Task Cant_get_client_without_calling_Start() + { + await using var manager = CreateManager(); + try + { + var client = await manager.GetClientAsync(); + } + catch (InvalidOperationException) + { + return; + } + Assert.Fail("Should throw"); + } + + [Test] + public async Task Can_change_db_for_client_PooledRedisClientManager() + { + await using IRedisClientsManagerAsync db1 = new PooledRedisClientManager(1, new string[] { TestConfig.SingleHost }); + await using IRedisClientsManagerAsync db2 = new PooledRedisClientManager(2, new string[] { TestConfig.SingleHost }); + var val = Environment.TickCount; + var key = "test" + val; + var db1c = await db1.GetClientAsync(); + var db2c = await db2.GetClientAsync(); + try + { + await db1c.SetAsync(key, val); + Assert.That(await db2c.GetAsync(key), Is.EqualTo(0)); + Assert.That(await db1c.GetAsync(key), Is.EqualTo(val)); + } + finally + { + await db1c.RemoveAsync(key); + } + } + + [Test] + public async Task Can_change_db_for_client_RedisManagerPool() + { + await using IRedisClientsManagerAsync db1 = new RedisManagerPool(TestConfig.SingleHost + "?db=1"); + await using IRedisClientsManagerAsync db2 = new RedisManagerPool(TestConfig.SingleHost + "?db=2"); + var val = Environment.TickCount; + var key = "test" + val; + var db1c = await db1.GetClientAsync(); + var db2c = await db2.GetClientAsync(); + try + { + await db1c.SetAsync(key, val); + Assert.That(await db2c.GetAsync(key), Is.EqualTo(0)); + Assert.That(await db1c.GetAsync(key), Is.EqualTo(val)); + } + finally + { + await db1c.RemoveAsync(key); + } + } + + [Test] + public async Task Can_change_db_for_client_BasicRedisClientManager() + { + await using IRedisClientsManagerAsync db1 = new BasicRedisClientManager(1, new string[] { TestConfig.SingleHost }); + await using IRedisClientsManagerAsync db2 = new BasicRedisClientManager(2, new string[] { TestConfig.SingleHost }); + var val = Environment.TickCount; + var key = "test" + val; + var db1c = await db1.GetClientAsync(); + var db2c = await db2.GetClientAsync(); + try + { + await db1c.SetAsync(key, val); + Assert.That(await db2c.GetAsync(key), Is.EqualTo(0)); + Assert.That(await db1c.GetAsync(key), Is.EqualTo(val)); + } + finally + { + await db1c.RemoveAsync(key); + } + } + + [Test] + public async Task Can_get_client_after_calling_Start() + { + await using var manager = CreateAndStartManager(); + var client = await manager.GetClientAsync(); + } + + [Test] + public async Task Can_get_ReadWrite_client() + { + await using var manager = CreateAndStartManager(); + var client = await manager.GetClientAsync(); + + AssertClientHasHost(client, firstReadWriteHost); + } + + private static void AssertClientHasHost(IRedisClientAsync client, string hostWithOptionalPort) + { + var parts = hostWithOptionalPort.Split(':'); + var port = parts.Length > 1 ? int.Parse(parts[1]) : RedisConfig.DefaultPort; + + Assert.That(client.Host, Is.EqualTo(parts[0])); + Assert.That(client.Port, Is.EqualTo(port)); + } + + [Test] + public async Task Can_get_ReadOnly_client() + { + await using var manager = CreateAndStartManager(); + var client = await manager.GetReadOnlyClientAsync(); + + AssertClientHasHost(client, firstReadOnlyHost); + } + + [Test] + public async Task Does_loop_through_ReadWrite_hosts() + { + await using var manager = CreateAndStartManager(); + var client1 = await manager.GetClientAsync(); + await client1.DisposeAsync(); + var client2 = await manager.GetClientAsync(); + var client3 = await manager.GetClientAsync(); + var client4 = await manager.GetClientAsync(); + var client5 = await manager.GetClientAsync(); + + AssertClientHasHost(client1, testReadWriteHosts[0]); + AssertClientHasHost(client2, testReadWriteHosts[1]); + AssertClientHasHost(client3, testReadWriteHosts[2]); + AssertClientHasHost(client4, testReadWriteHosts[3]); + AssertClientHasHost(client5, testReadWriteHosts[0]); + } + + [Test] + public async Task Does_loop_through_ReadOnly_hosts() + { + await using var manager = CreateAndStartManager(); + var client1 = await manager.GetReadOnlyClientAsync(); + await client1.DisposeAsync(); + var client2 = await manager.GetReadOnlyClientAsync(); + await client2.DisposeAsync(); + var client3 = await manager.GetReadOnlyClientAsync(); + var client4 = await manager.GetReadOnlyClientAsync(); + var client5 = await manager.GetReadOnlyClientAsync(); + + AssertClientHasHost(client1, testReadOnlyHosts[0]); + AssertClientHasHost(client2, testReadOnlyHosts[1]); + AssertClientHasHost(client3, testReadOnlyHosts[2]); + AssertClientHasHost(client4, testReadOnlyHosts[0]); + AssertClientHasHost(client5, testReadOnlyHosts[1]); + } + + [Test] + public async Task Can_have_different_pool_size_and_host_configurations() + { + var writeHosts = new[] { "readwrite1" }; + var readHosts = new[] { "read1", "read2" }; + + const int poolSizeMultiplier = 4; + + await using IRedisClientsManagerAsync manager = new PooledRedisClientManager(writeHosts, readHosts, + new RedisClientManagerConfig + { + MaxWritePoolSize = writeHosts.Length * poolSizeMultiplier, + MaxReadPoolSize = readHosts.Length * poolSizeMultiplier, + AutoStart = true, + } + ); + //A poolsize of 4 will not block getting 4 clients + await using (var client1 = await manager.GetClientAsync()) + await using (var client2 = await manager.GetClientAsync()) + await using (var client3 = await manager.GetClientAsync()) + await using (var client4 = await manager.GetClientAsync()) + { + AssertClientHasHost(client1, writeHosts[0]); + AssertClientHasHost(client2, writeHosts[0]); + AssertClientHasHost(client3, writeHosts[0]); + AssertClientHasHost(client4, writeHosts[0]); + } + + //A poolsize of 8 will not block getting 8 clients + await using (var client1 = await manager.GetReadOnlyClientAsync()) + await using (var client2 = await manager.GetReadOnlyClientAsync()) + await using (var client3 = await manager.GetReadOnlyClientAsync()) + await using (var client4 = await manager.GetReadOnlyClientAsync()) + await using (var client5 = await manager.GetReadOnlyClientAsync()) + await using (var client6 = await manager.GetReadOnlyClientAsync()) + await using (var client7 = await manager.GetReadOnlyClientAsync()) + await using (var client8 = await manager.GetReadOnlyClientAsync()) + { + AssertClientHasHost(client1, readHosts[0]); + AssertClientHasHost(client2, readHosts[1]); + AssertClientHasHost(client3, readHosts[0]); + AssertClientHasHost(client4, readHosts[1]); + AssertClientHasHost(client5, readHosts[0]); + AssertClientHasHost(client6, readHosts[1]); + AssertClientHasHost(client7, readHosts[0]); + AssertClientHasHost(client8, readHosts[1]); + } + } + + [Test] + public async Task Does_block_ReadWrite_clients_pool() + { + await using IRedisClientsManagerAsync manager = CreateAndStartManager(); + var delay = TimeSpan.FromSeconds(1); + var client1 = await manager.GetClientAsync(); + var client2 = await manager.GetClientAsync(); + var client3 = await manager.GetClientAsync(); + var client4 = await manager.GetClientAsync(); + +#pragma warning disable IDE0039 // Use local function + Action func = async delegate +#pragma warning restore IDE0039 // Use local function + { + await Task.Delay(delay + TimeSpan.FromSeconds(0.5)); + await client4.DisposeAsync(); + }; + +#if NETCORE + _ = Task.Run(func); +#else + func.BeginInvoke(null, null); +#endif + + var start = DateTime.Now; + + var client5 = await manager.GetClientAsync(); + + Assert.That(DateTime.Now - start, Is.GreaterThanOrEqualTo(delay)); + + AssertClientHasHost(client1, testReadWriteHosts[0]); + AssertClientHasHost(client2, testReadWriteHosts[1]); + AssertClientHasHost(client3, testReadWriteHosts[2]); + AssertClientHasHost(client4, testReadWriteHosts[3]); + AssertClientHasHost(client5, testReadWriteHosts[3]); + } + + [Test] + public async Task Does_block_ReadOnly_clients_pool() + { + var delay = TimeSpan.FromSeconds(1); + + await using var manager = CreateAndStartManager(); + var client1 = await manager.GetReadOnlyClientAsync(); + var client2 = await manager.GetReadOnlyClientAsync(); + var client3 = await manager.GetReadOnlyClientAsync(); + +#pragma warning disable IDE0039 // Use local function + Action func = async delegate +#pragma warning restore IDE0039 // Use local function + { + await Task.Delay(delay + TimeSpan.FromSeconds(0.5)); + await client3.DisposeAsync(); + }; +#if NETCORE + _ =Task.Run(func); +#else + func.BeginInvoke(null, null); +#endif + var start = DateTime.Now; + + var client4 = await manager.GetReadOnlyClientAsync(); + + Assert.That(DateTime.Now - start, Is.GreaterThanOrEqualTo(delay)); + + AssertClientHasHost(client1, testReadOnlyHosts[0]); + AssertClientHasHost(client2, testReadOnlyHosts[1]); + AssertClientHasHost(client3, testReadOnlyHosts[2]); + AssertClientHasHost(client4, testReadOnlyHosts[2]); + } + + [Test] + public async Task Does_throw_TimeoutException_when_PoolTimeout_exceeded() + { + await using IRedisClientsManagerAsync manager = new PooledRedisClientManager(testReadWriteHosts, testReadOnlyHosts, + new RedisClientManagerConfig + { + MaxWritePoolSize = 4, + MaxReadPoolSize = 4, + AutoStart = false, + }); + ((PooledRedisClientManager)manager).PoolTimeout = 100; + + ((PooledRedisClientManager)manager).Start(); + + var masters = 4.Times(i => manager.GetClientAsync()); + + try + { + await manager.GetClientAsync(); + Assert.Fail("Should throw TimeoutException"); + } + catch (TimeoutException ex) + { + Assert.That(ex.Message, Does.StartWith("Redis Timeout expired.")); + } + + for (int i = 0; i < 4; i++) + { + await manager.GetReadOnlyClientAsync(); + } + + try + { + await manager.GetReadOnlyClientAsync(); + Assert.Fail("Should throw TimeoutException"); + } + catch (TimeoutException ex) + { + Assert.That(ex.Message, Does.StartWith("Redis Timeout expired.")); + } + } + + //[Ignore("tempromental integration test")] + //[Test] + //public void Can_support_64_threads_using_the_client_simultaneously() + //{ + // const int noOfConcurrentClients = 64; //WaitHandle.WaitAll limit is <= 64 + // var clientUsageMap = new Dictionary(); + + // var clientAsyncResults = new List(); + // using (var manager = CreateAndStartManager()) + // { + // for (var i = 0; i < noOfConcurrentClients; i++) + // { + // var clientNo = i; + // var action = (Action)(() => UseClient(manager, clientNo, clientUsageMap)); + // clientAsyncResults.Add(action.BeginInvoke(null, null)); + // } + // } + + // WaitHandle.WaitAll(clientAsyncResults.ConvertAll(x => x.AsyncWaitHandle).ToArray()); + + // RedisStats.ToDictionary().PrintDump(); + + // Debug.WriteLine(TypeSerializer.SerializeToString(clientUsageMap)); + + // var hostCount = 0; + // foreach (var entry in clientUsageMap) + // { + // Assert.That(entry.Value, Is.GreaterThanOrEqualTo(2), "Host has unproportionate distribution: " + entry.Value); + // Assert.That(entry.Value, Is.LessThanOrEqualTo(30), "Host has unproportionate distribution: " + entry.Value); + // hostCount += entry.Value; + // } + + // Assert.That(hostCount, Is.EqualTo(noOfConcurrentClients), "Invalid no of clients used"); + //} + + //private static void UseClient(IRedisClientsManager manager, int clientNo, Dictionary hostCountMap) + //{ + // using (var client = manager.GetClient()) + // { + // lock (hostCountMap) + // { + // int hostCount; + // if (!hostCountMap.TryGetValue(client.Host, out hostCount)) + // { + // hostCount = 0; + // } + + // hostCountMap[client.Host] = ++hostCount; + // } + + // Debug.WriteLine(String.Format("Client '{0}' is using '{1}'", clientNo, client.Host)); + // } + //} + + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.cs b/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.cs index 80191485..d1a9f7cb 100644 --- a/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.cs +++ b/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.cs @@ -345,7 +345,7 @@ public void Does_block_ReadOnly_clients_pool() Thread.Sleep(delay + TimeSpan.FromSeconds(0.5)); client3.Dispose(); }; -#if NETCORE +#if NETCORE Task.Run(func); #else func.BeginInvoke(null, null); diff --git a/tests/ServiceStack.Redis.Tests/Properties/AssemblyInfo.cs b/tests/ServiceStack.Redis.Tests/Properties/AssemblyInfo.cs index da860cde..157ff9b9 100644 --- a/tests/ServiceStack.Redis.Tests/Properties/AssemblyInfo.cs +++ b/tests/ServiceStack.Redis.Tests/Properties/AssemblyInfo.cs @@ -33,4 +33,4 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisBasicPersistenceProviderTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisBasicPersistenceProviderTests.Async.cs new file mode 100644 index 00000000..0f4e0b7b --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisBasicPersistenceProviderTests.Async.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Data; +using ServiceStack.Model; +using ServiceStack.Redis.Generic; +using ServiceStack.Script; +using ServiceStack.Text; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Integration")] + public class RedisBasicPersistenceProviderTestsAsync + : RedisClientTestsBaseAsync + { + List testModels; + + public static string TestModelIdsSetKey = "ids:" + typeof(TestModel).Name; + + public class TestModel + : IHasId + { + public Guid Id { get; set; } + public string Name { get; set; } + public int Age { get; set; } + + //Thanking R# for the timesaver + public bool Equals(TestModel other) + { + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + return other.Id.Equals(Id) && Equals(other.Name, Name) && other.Age == Age; + } + + public override bool Equals(object obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof(TestModel)) return false; + return Equals((TestModel)obj); + } + + [SuppressMessage("Style", "IDE0070:Use 'System.HashCode'", Justification = "not in netfx")] + public override int GetHashCode() + { + unchecked + { + int result = Id.GetHashCode(); + result = (result * 397) ^ (Name != null ? Name.GetHashCode() : 0); + result = (result * 397) ^ Age; + return result; + } + } + } + + public override void OnBeforeEachTest() + { + base.OnBeforeEachTest(); + + RedisRaw.NamespacePrefix = "RedisBasicPersistenceProviderTests"; + testModels = new List(); + 5.Times(i => testModels.Add( + new TestModel { Id = Guid.NewGuid(), Name = "Name" + i, Age = 20 + i })); + } + + [Test] + public async Task Can_Store() + { + foreach (var x in testModels) + { + await RedisAsync.StoreAsync(x); + } + + var allModels = (await RedisAsync.As().GetAllAsync()).OrderBy(x => x.Age).ToList(); + + Assert.That(allModels, Is.EquivalentTo(testModels)); + } + + [Test] + public async Task Can_StoreAll() + { + await RedisAsync.StoreAllAsync(testModels); + + var allModels = (await RedisAsync.As().GetAllAsync()).OrderBy(x => x.Age).ToList(); + + Assert.That(allModels, Is.EquivalentTo(testModels)); + } + + [Test] + public async Task Can_WriteAll() + { + await RedisAsync.WriteAllAsync(testModels); + + var testModelIds = testModels.ConvertAll(x => x.Id); + + var allModels = (await RedisAsync.GetByIdsAsync(testModelIds)) + .OrderBy(x => x.Age).ToList(); + + Assert.That(allModels, Is.EquivalentTo(testModels)); + } + + [Test] + public async Task Can_GetById() + { + await RedisAsync.StoreAllAsync(testModels); + + var last = testModels.Last(); + var lastById = await RedisAsync.GetByIdAsync(last.Id); + + Assert.That(lastById, Is.EqualTo(last)); + } + + [Test] + public async Task Can_GetByIds() + { + await RedisAsync.StoreAllAsync(testModels); + + var evenTestModels = testModels.Where(x => x.Age % 2 == 0) + .OrderBy(x => x.Id).ToList(); + var evenTestModelIds = evenTestModels.Select(x => x.Id).ToList(); + + var selectedModels = (await RedisAsync.GetByIdsAsync(evenTestModelIds)) + .OrderBy(x => x.Id).ToList(); + + Assert.That(selectedModels, Is.EqualTo(evenTestModels)); + } + + [Test] + public async Task Can_Delete() + { + await RedisAsync.StoreAllAsync(testModels); + + var last = testModels.Last(); + await RedisAsync.DeleteAsync(last); + + testModels.Remove(last); + + var allModels = (await RedisAsync.As().GetAllAsync()).OrderBy(x => x.Age).ToList(); + + Assert.That(allModels, Is.EquivalentTo(testModels)); + + //Test internal TestModelIdsSetKey state + var idsRemaining = (await RedisAsync.GetAllItemsFromSetAsync(RedisRaw.NamespacePrefix + TestModelIdsSetKey)) + .OrderBy(x => x).Map(x => new Guid(x)); + + var testModelIds = testModels.OrderBy(x => x.Id).Map(x => x.Id); + + Assert.That(idsRemaining, Is.EquivalentTo(testModelIds)); + } + + [Test] + public async Task Can_DeleteAll() + { + await RedisAsync.StoreAllAsync(testModels); + + await RedisAsync.DeleteAllAsync(); + + var allModels = await RedisAsync.As().GetAllAsync(); + + Assert.That(allModels, Is.Empty); + + //Test internal TestModelIdsSetKey state + var idsRemaining = await RedisAsync.GetAllItemsFromSetAsync(TestModelIdsSetKey); + Assert.That(idsRemaining, Is.Empty); + } + + [Test] + public async Task Can_DeleteAll_with_runtime_type() + { + await RedisAsync.StoreAllAsync(testModels); + + var mi = typeof(IEntityStoreAsync).GetMethod(nameof(IEntityStoreAsync.DeleteAllAsync)); + var genericMi = mi.MakeGenericMethod(typeof(TestModel)); + await (ValueTask)genericMi.Invoke(RedisAsync, new object[] { CancellationToken.None }); + + var allModels = await RedisAsync.As().GetAllAsync(); + Assert.That(allModels, Is.Empty); + var idsRemaining = await RedisAsync.GetAllItemsFromSetAsync(TestModelIdsSetKey); + Assert.That(idsRemaining, Is.Empty); + } + + [Test] + public async Task Can_As_DeleteAll_with_runtime_type() + { + await RedisAsync.StoreAllAsync(testModels); + + var mi = typeof(IRedisClientAsync).GetMethod(nameof(IRedisClientAsync.As)); + var genericMi = mi.MakeGenericMethod(typeof(TestModel)); + var typedClient = genericMi.Invoke(RedisAsync, TypeConstants.EmptyObjectArray); + var deleteMi = typeof(IEntityStoreAsync).GetMethod(nameof(IEntityStoreAsync.DeleteAllAsync)); + await (ValueTask)deleteMi.Invoke(typedClient, new object[] { CancellationToken.None }); + + var allModels = await RedisAsync.As().GetAllAsync(); + Assert.That(allModels, Is.Empty); + var idsRemaining = await RedisAsync.GetAllItemsFromSetAsync(TestModelIdsSetKey); + Assert.That(idsRemaining, Is.Empty); + } + + [Test] + public async Task Can_As_DeleteAll_with_script() + { + await RedisAsync.StoreAllAsync(testModels); + + var context = new ScriptContext + { + ScriptLanguages = { ScriptLisp.Language }, + AllowScriptingOfAllTypes = true, + ScriptMethods = { + new ProtectedScripts() + }, + Args = { + ["redis"] = RedisAsync + } + }.Init(); + + var type = typeof(TestModel).FullName; + RedisRaw.DebugAllowSync = true; // not reasonable to allow async from Lisp + context.EvaluateCode($"redis.call('DeleteAll<{type}>') |> return"); + context.EvaluateCode($"redis.call('As<{type}>').call('DeleteAll') |> return"); + context.RenderLisp($"(call redis \"DeleteAll<{type}>\")"); + context.RenderLisp($"(call (call redis \"As<{type}>\") \"DeleteAll\")"); + RedisRaw.DebugAllowSync = false; + + var allModels = await RedisAsync.As().GetAllAsync(); + Assert.That(allModels, Is.Empty); + var idsRemaining = await RedisAsync.GetAllItemsFromSetAsync(TestModelIdsSetKey); + Assert.That(idsRemaining, Is.Empty); + } + + [Test] + public async Task Can_DeleteByIds() + { + await RedisAsync.StoreAllAsync(testModels); + + var evenTestModels = testModels.Where(x => x.Age % 2 == 0) + .OrderBy(x => x.Id).ToList(); + var evenTestModelIds = evenTestModels.Select(x => x.Id).ToList(); + + await RedisAsync.DeleteByIdsAsync(evenTestModelIds); + + evenTestModels.ForEach(x => testModels.Remove(x)); + + var allModels = (await RedisAsync.As().GetAllAsync()).OrderBy(x => x.Age).ToList(); + + Assert.That(allModels, Is.EqualTo(testModels)); + + + //Test internal TestModelIdsSetKey state + var idsRemaining = (await RedisAsync.GetAllItemsFromSetAsync(RedisRaw.NamespacePrefix + TestModelIdsSetKey)) + .OrderBy(x => x).Map(x => new Guid(x)); + + var testModelIds = testModels.OrderBy(x => x.Id).Map(x => x.Id); + + Assert.That(idsRemaining, Is.EquivalentTo(testModelIds)); + } + + } +} diff --git a/tests/ServiceStack.Redis.Tests/RedisBatchTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisBatchTests.Async.cs new file mode 100644 index 00000000..3a3e1e82 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisBatchTests.Async.cs @@ -0,0 +1,46 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Text; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class RedisBatchTestsAsync + : RedisClientTestsBaseAsync + { + public class Message + { + public long Id { get; set; } + public string Key { get; set; } + public string Value { get; set; } + public string Description { get; set; } + } + + [Test] + public async Task Store_batch_items_in_List() + { + var redisMessages = RedisAsync.As(); + const int batchSize = 500; + var nextIds = await redisMessages.GetNextSequenceAsync(batchSize); + + var msgBatch = batchSize.Times(i => + new Message + { + Id = nextIds - (batchSize - i) + 1, + Key = i.ToString(), + Value = Guid.NewGuid().ToString(), + Description = "Description" + }); + + await redisMessages.Lists["listName"].AddRangeAsync(msgBatch); + + var msgs = await redisMessages.Lists["listName"].GetAllAsync(); + Assert.That(msgs.Count, Is.EqualTo(batchSize)); + + Assert.That(msgs.First().Id, Is.EqualTo(1)); + Assert.That(msgs.Last().Id, Is.EqualTo(500)); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisCacheClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisCacheClientTests.Async.cs new file mode 100644 index 00000000..fcbde83d --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisCacheClientTests.Async.cs @@ -0,0 +1,140 @@ +using System; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Caching; +using ServiceStack.Common.Tests.Models; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + [Category("Async")] + public class RedisCacheClientTestsAsync + { + private ICacheClientExtendedAsync cacheClient; + + [SetUp] + public async Task OnBeforeEachTest() + { + if (cacheClient != null) + await cacheClient.DisposeAsync(); + + cacheClient = new RedisClient(TestConfig.SingleHost); + await cacheClient.FlushAllAsync(); + } + + [Test] + public async Task Get_non_existant_value_returns_null() + { + var model = ModelWithIdAndName.Create(1); + var cacheKey = model.CreateUrn(); + var existingModel = await cacheClient.GetAsync(cacheKey); + Assert.That(existingModel, Is.Null); + } + + [Test] + public async Task Get_non_existant_generic_value_returns_null() + { + var model = ModelWithIdAndName.Create(1); + var cacheKey = model.CreateUrn(); + var existingModel = await cacheClient.GetAsync(cacheKey); + Assert.That(existingModel, Is.Null); + } + + [Test] + public async Task Can_store_and_get_model() + { + var model = ModelWithIdAndName.Create(1); + var cacheKey = model.CreateUrn(); + await cacheClient.SetAsync(cacheKey, model); + + var existingModel = await cacheClient.GetAsync(cacheKey); + ModelWithIdAndName.AssertIsEqual(existingModel, model); + } + + [Test] + public async Task Can_store_null_model() + { + await cacheClient.SetAsync("test-key", null); + } + + [Test] + public async Task Can_Set_and_Get_key_with_all_byte_values() + { + const string key = "bytesKey"; + + var value = new byte[256]; + for (var i = 0; i < value.Length; i++) + { + value[i] = (byte)i; + } + + await cacheClient.SetAsync(key, value); + var resultValue = await cacheClient.GetAsync(key); + + Assert.That(resultValue, Is.EquivalentTo(value)); + } + + [Test] + public async Task Can_Replace_By_Pattern() + { + var model = ModelWithIdAndName.Create(1); + string modelKey = "model:" + model.CreateUrn(); + await cacheClient.AddAsync(modelKey, model); + + model = ModelWithIdAndName.Create(2); + string modelKey2 = "xxmodelxx:" + model.CreateUrn(); + await cacheClient.AddAsync(modelKey2, model); + + string s = "this is a string"; + await cacheClient.AddAsync("string1", s); + + var removable = (IRemoveByPatternAsync)cacheClient; + await removable.RemoveByPatternAsync("*model*"); + + ModelWithIdAndName result = await cacheClient.GetAsync(modelKey); + Assert.That(result, Is.Null); + + result = await cacheClient.GetAsync(modelKey2); + Assert.That(result, Is.Null); + + string result2 = await cacheClient.GetAsync("string1"); + Assert.That(result2, Is.EqualTo(s)); + + await removable.RemoveByPatternAsync("string*"); + + result2 = await cacheClient.GetAsync("string1"); + Assert.That(result2, Is.Null); + } + + [Test] + public async Task Can_GetTimeToLive() + { + var model = ModelWithIdAndName.Create(1); + string key = "model:" + model.CreateUrn(); + await cacheClient.AddAsync(key, model); + + var ttl = await cacheClient.GetTimeToLiveAsync(key); + Assert.That(ttl, Is.EqualTo(TimeSpan.MaxValue)); + + await cacheClient.SetAsync(key, model, expiresIn: TimeSpan.FromSeconds(10)); + ttl = await cacheClient.GetTimeToLiveAsync(key); + Assert.That(ttl.Value, Is.GreaterThanOrEqualTo(TimeSpan.FromSeconds(9))); + Assert.That(ttl.Value, Is.LessThanOrEqualTo(TimeSpan.FromSeconds(10))); + + await cacheClient.RemoveAsync(key); + ttl = await cacheClient.GetTimeToLiveAsync(key); + Assert.That(ttl, Is.Null); + } + + [Test] + public async Task Can_increment_and_reset_values() + { + await using (var client = await new RedisManagerPool(TestConfig.SingleHost).GetCacheClientAsync()) + { + Assert.That(await client.IncrementAsync("incr:counter", 10), Is.EqualTo(10)); + await client.SetAsync("incr:counter", 0); + Assert.That(await client.IncrementAsync("incr:counter", 10), Is.EqualTo(10)); + } + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisClientConfigTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientConfigTests.Async.cs new file mode 100644 index 00000000..8dfe12ec --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisClientConfigTests.Async.cs @@ -0,0 +1,108 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Text; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class RedisClientConfigTestsAsync + : RedisClientTestsBaseAsync + { + [Ignore("Hurts MSOpenTech Redis Server")] + [Test] + public async Task Can_Set_and_Get_Config() + { + var orig = await RedisAsync.GetConfigAsync("maxmemory"); + var newMaxMemory = (long.Parse(orig) + 1).ToString(); + await RedisAsync.SetConfigAsync("maxmemory", newMaxMemory); + var current = await RedisAsync.GetConfigAsync("maxmemory"); + Assert.That(current, Is.EqualTo(newMaxMemory)); + } + + [Test] + public async Task Can_Rewrite_Redis_Config() + { + try + { + await RedisAsync.SaveConfigAsync(); + } + catch (RedisResponseException ex) + { + if (ex.Message.StartsWith("Rewriting config file: Permission denied") + || ex.Message.StartsWith("The server is running without a config file")) + return; + throw; + } + } + + [Test] + public async Task Can_Rewrite_Info_Stats() + { + await RedisAsync.ResetInfoStatsAsync(); + } + + [Test] + public async Task Can_set_and_Get_Client_Name() + { + var clientName = "CLIENT-" + Environment.TickCount; + await RedisAsync.SetClientAsync(clientName); + var client = await RedisAsync.GetClientAsync(); + + Assert.That(client, Is.EqualTo(clientName)); + } + + [Test] + public async Task Can_GetClientsInfo() + { + var clientList = await RedisAsync.GetClientsInfoAsync(); + clientList.PrintDump(); + } + + [Test] + public async Task Can_Kill_Client() + { + var clientList = await RedisAsync.GetClientsInfoAsync(); + var firstAddr = clientList.First()["addr"]; + await RedisAsync.KillClientAsync(firstAddr); + } + + [Test] + public async Task Can_Kill_Clients() + { + await RedisAsync.KillClientsAsync(fromAddress: "192.168.0.1:6379"); + await RedisAsync.KillClientsAsync(withId: "1"); + await RedisAsync.KillClientsAsync(ofType: RedisClientType.Normal); + await RedisAsync.KillClientsAsync(ofType: RedisClientType.PubSub); + await RedisAsync.KillClientsAsync(ofType: RedisClientType.Slave); + await RedisAsync.KillClientsAsync(skipMe: true); + await RedisAsync.KillClientsAsync(fromAddress: "192.168.0.1:6379", withId: "1", ofType: RedisClientType.Normal); + await RedisAsync.KillClientsAsync(skipMe: false); + } + + [Test] + public async Task Can_get_Role_Info() + { + var result = await NativeAsync.RoleAsync(); + result.PrintDump(); + Assert.That(result.Children[0].Text, Is.EqualTo("master")); + Assert.That(await RedisAsync.GetServerRoleAsync(), Is.EqualTo(RedisServerRole.Master)); + + //needs redis-server v3.0 + //var replica = new RedisClient("10.0.0.9:6380"); + //result = replica.Role(); + //result.PrintDump(); + } + + [Test] + public Task Can_PauseAllClients() + { + //needs redis-server v3.0 + //var replica = new RedisClient("10.0.0.9:6380"); + //replica.PauseAllClients(TimeSpan.FromSeconds(2)); + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisClientConfigTests.cs b/tests/ServiceStack.Redis.Tests/RedisClientConfigTests.cs index 307c9460..44b6b4f1 100644 --- a/tests/ServiceStack.Redis.Tests/RedisClientConfigTests.cs +++ b/tests/ServiceStack.Redis.Tests/RedisClientConfigTests.cs @@ -29,7 +29,8 @@ public void Can_Rewrite_Redis_Config() } catch (RedisResponseException ex) { - if (ex.Message.StartsWith("Rewriting config file: Permission denied")) + if (ex.Message.StartsWith("Rewriting config file: Permission denied") + || ex.Message.StartsWith("The server is running without a config file")) return; throw; } diff --git a/tests/ServiceStack.Redis.Tests/RedisClientEvalTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientEvalTests.Async.cs new file mode 100644 index 00000000..8a92f078 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisClientEvalTests.Async.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Common; +using ServiceStack.Text; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Integration")] + public class RedisClientEvalTestsAsync : RedisClientTestsBaseAsync + { + public override void OnBeforeEachTest() + { + //base.OnBeforeEachTest(); + + //Run on local build server + RedisRaw = new RedisClient(TestConfig.SingleHost); + RedisRaw.FlushAll(); + } + + [Test] + public async Task Can_Eval_int() + { + var intVal = await RedisAsync.ExecLuaAsIntAsync("return 3141591", Array.Empty()); + Assert.That(intVal, Is.EqualTo(3141591)); + } + + [Test] + public async Task Can_EvalSha_int() + { + var luaBody = "return 3141591"; + await RedisAsync.ExecLuaAsIntAsync(luaBody, Array.Empty()); + var sha1 = await RedisAsync.CalculateSha1Async(luaBody); + var intVal = await RedisAsync.ExecLuaShaAsIntAsync(sha1, Array.Empty()); + Assert.That(intVal, Is.EqualTo(3141591)); + } + + [Test] + public async Task Can_Eval_int_with_args() + { + var intVal = await RedisAsync.ExecLuaAsIntAsync("return 3141591", new[] { "20", "30", "40" }); + Assert.That(intVal, Is.EqualTo(3141591)); + } + + [Test] + public async Task Can_Eval_int_with_keys_and_args() + { + var intVal = await RedisAsync.ExecLuaAsIntAsync("return KEYS[1] + ARGV[1]", new[] { "20" }, new[] { "30", "40" }); + Assert.That(intVal, Is.EqualTo(50)); + } + + [Test] + public async Task Can_Eval_int2() + { + var intVal = await RedisAsync.ExecLuaAsIntAsync("return ARGV[1] + ARGV[2]", new[] { "10", "20" }); + Assert.That(intVal, Is.EqualTo(30)); + } + + [Test] + public async Task Can_Eval_string() + { + var strVal = await RedisAsync.ExecLuaAsStringAsync(@"return 'abc'", new string[0]); + Assert.That(strVal, Is.EqualTo("abc")); + } + + [Test] + public async Task Can_Eval_HelloWorld_string() + { + var strVal = await RedisAsync.ExecLuaAsStringAsync(@"return 'Hello, ' .. ARGV[1] .. '!'", new[] { "Redis Lua" }); + Assert.That(strVal, Is.EqualTo("Hello, Redis Lua!")); + } + + [Test] + public async Task Can_Eval_string_with_args() + { + var strVal = await RedisAsync.ExecLuaAsStringAsync(@"return 'abc'", new[] { "at", "dot", "com" }); + Assert.That(strVal, Is.EqualTo("abc")); + } + + [Test] + public async Task Can_Eval_string_with_keys_an_args() + { + var strVal = await RedisAsync.ExecLuaAsStringAsync(@"return KEYS[1] .. ARGV[1]", new[] { "at" }, new[] { "dot", "com" }); + Assert.That(strVal, Is.EqualTo("atdot")); + } + + [Test] + public async Task Can_Eval_multidata_with_args() + { + var strVals = await RedisAsync.ExecLuaAsListAsync(@"return {ARGV[1],ARGV[2],ARGV[3]}", new[] { "at", "dot", "com" }); + Assert.That(strVals, Is.EquivalentTo(new List { "at", "dot", "com" })); + } + + [Test] + public async Task Can_Eval_multidata_with_keys_and_args() + { + var strVals = await RedisAsync.ExecLuaAsListAsync(@"return {KEYS[1],ARGV[1],ARGV[2]}", new[] { "at" }, new[] { "dot", "com" }); + Assert.That(strVals, Is.EquivalentTo(new List { "at", "dot", "com" })); + } + + [Test] + public async Task Can_Load_and_Exec_script() + { + var luaBody = "return 'load script and exec'"; + var sha1 = await RedisAsync.LoadLuaScriptAsync(luaBody); + var result = await RedisAsync.ExecLuaShaAsStringAsync(sha1, new string[0]); + Assert.That(result, Is.EqualTo("load script and exec")); + } + + [Test] + public async Task Does_flush_all_scripts() + { + var luaBody = "return 'load script and exec'"; + var sha1 = await RedisAsync.LoadLuaScriptAsync(luaBody); + var result = await RedisAsync.ExecLuaShaAsStringAsync(sha1, new string[0]); + Assert.That(result, Is.EqualTo("load script and exec")); + + await RedisAsync.RemoveAllLuaScriptsAsync(); + + try + { + result = await RedisAsync.ExecLuaShaAsStringAsync(sha1, new string[0]); + Assert.Fail("script shouldn't exist"); + } + catch (RedisResponseException ex) + { + Assert.That(ex.Message, Does.Contain("NOSCRIPT")); + } + } + + [Test] + public async Task Can_detect_which_scripts_exist() + { + var sha1 = await RedisAsync.LoadLuaScriptAsync("return 'script1'"); + var sha2 = await RedisAsync.CalculateSha1Async("return 'script2'"); + var sha3 = await RedisAsync.LoadLuaScriptAsync("return 'script3'"); + + Assert.That(await RedisAsync.HasLuaScriptAsync(sha1)); + + var existsMap = await RedisAsync.WhichLuaScriptsExistsAsync(new[] { sha1, sha2, sha3 }); + Assert.That(existsMap[sha1]); + Assert.That(!existsMap[sha2]); + Assert.That(existsMap[sha3]); + } + + [Test] + public async Task Can_create_ZPop_with_lua() + { + var luaBody = @" + local val = redis.call('zrange', KEYS[1], 0, ARGV[1]-1) + if val then redis.call('zremrangebyrank', KEYS[1], 0, ARGV[1]-1) end + return val"; + + var i = 0; + var alphabet = 26.Times(c => ((char)('A' + c)).ToString()); + foreach (var x in alphabet) + { + await RedisAsync.AddItemToSortedSetAsync("zalphabet", x, i++); + } + + var letters = await RedisAsync.ExecLuaAsListAsync(luaBody, keys: new[] { "zalphabet" }, args: new[] { "3" }); + + letters.PrintDump(); + Assert.That(letters, Is.EquivalentTo(new[] { "A", "B", "C" })); + } + + [Test] + public async Task Can_create_ZRevPop_with_lua() + { + var luaBody = @" + local val = redis.call('zrange', KEYS[1], -ARGV[1], -1) + if val then redis.call('zremrangebyrank', KEYS[1], -ARGV[1], -1) end + return val"; + + var i = 0; + var alphabet = 26.Times(c => ((char)('A' + c)).ToString()); + foreach(var x in alphabet) + { + await RedisAsync.AddItemToSortedSetAsync("zalphabet", x, i++); + } + + var letters = await RedisAsync.ExecLuaAsListAsync(luaBody, keys: new[] { "zalphabet" }, args: new[] { "3" }); + + letters.PrintDump(); + Assert.That(letters, Is.EquivalentTo(new[] { "X", "Y", "Z" })); + } + + [Test] + public async Task Can_return_DaysOfWeek_as_list() + { + foreach(var x in Enum.GetNames(typeof(DayOfWeek)).ToList()) + { + await RedisAsync.AddItemToListAsync("DaysOfWeek", x); + } + (await RedisAsync.ExecLuaAsListAsync("return redis.call('LRANGE', 'DaysOfWeek', 0, -1)", new string[0])).PrintDump(); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisClientHashTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientHashTests.Async.cs new file mode 100644 index 00000000..f95f9376 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisClientHashTests.Async.cs @@ -0,0 +1,351 @@ +using NUnit.Framework; +using ServiceStack.Text; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class RedisClientHashTestsAsync + : RedisClientTestsBaseAsync + { + private const string HashId = "rchtesthash"; + + Dictionary stringMap; + Dictionary stringIntMap; + + public override void OnBeforeEachTest() + { + base.OnBeforeEachTest(); + stringMap = new Dictionary { + {"one","a"}, {"two","b"}, {"three","c"}, {"four","d"} + }; + stringIntMap = new Dictionary { + {"one",1}, {"two",2}, {"three",3}, {"four",4} + }; + } + + public override void OnAfterEachTest() + { + CleanMask = HashId + "*"; + base.OnAfterEachTest(); + } + + [Test] + public async Task Can_SetItemInHash_and_GetAllFromHash() + { + foreach (var x in stringMap) + { + await RedisAsync.SetEntryInHashAsync(HashId, x.Key, x.Value); + } + + var members = await RedisAsync.GetAllEntriesFromHashAsync(HashId); + Assert.That(members, Is.EquivalentTo(stringMap)); + } + + [Test] + public async Task Can_RemoveFromHash() + { + const string removeMember = "two"; + + foreach (var x in stringMap) + { + await RedisAsync.SetEntryInHashAsync(HashId, x.Key, x.Value); + } + + await RedisAsync.RemoveEntryFromHashAsync(HashId, removeMember); + + stringMap.Remove(removeMember); + + var members = await RedisAsync.GetAllEntriesFromHashAsync(HashId); + Assert.That(members, Is.EquivalentTo(stringMap)); + } + + [Test] + public async Task Can_GetItemFromHash() + { + foreach (var x in stringMap) + { + await RedisAsync.SetEntryInHashAsync(HashId, x.Key, x.Value); + } + + var hashValue = await RedisAsync.GetValueFromHashAsync(HashId, "two"); + + Assert.That(hashValue, Is.EqualTo(stringMap["two"])); + } + + [Test] + public async Task Can_GetHashCount() + { + foreach (var x in stringMap) + { + await RedisAsync.SetEntryInHashAsync(HashId, x.Key, x.Value); + } + + var hashCount = await RedisAsync.GetHashCountAsync(HashId); + + Assert.That(hashCount, Is.EqualTo(stringMap.Count)); + } + + [Test] + public async Task Does_HashContainsKey() + { + const string existingMember = "two"; + const string nonExistingMember = "five"; + + foreach (var x in stringMap) + { + await RedisAsync.SetEntryInHashAsync(HashId, x.Key, x.Value); + } + + Assert.That(await RedisAsync.HashContainsEntryAsync(HashId, existingMember), Is.True); + Assert.That(await RedisAsync.HashContainsEntryAsync(HashId, nonExistingMember), Is.False); + } + + [Test] + public async Task Can_GetHashKeys() + { + foreach (var x in stringMap) + { + await RedisAsync.SetEntryInHashAsync(HashId, x.Key, x.Value); + } + var expectedKeys = stringMap.Map(x => x.Key); + + var hashKeys = await RedisAsync.GetHashKeysAsync(HashId); + + Assert.That(hashKeys, Is.EquivalentTo(expectedKeys)); + } + + [Test] + public async Task Can_GetHashValues() + { + foreach (var x in stringMap) + { + await RedisAsync.SetEntryInHashAsync(HashId, x.Key, x.Value); + } + var expectedValues = stringMap.Map(x => x.Value); + + var hashValues = await RedisAsync.GetHashValuesAsync(HashId); + + Assert.That(hashValues, Is.EquivalentTo(expectedValues)); + } + + [Test] + public async Task Can_enumerate_small_IDictionary_Hash() + { + foreach (var x in stringMap) + { + await RedisAsync.SetEntryInHashAsync(HashId, x.Key, x.Value); + } + + var members = new List(); + await foreach (var item in RedisAsync.Hashes[HashId]) + { + Assert.That(stringMap.ContainsKey(item.Key), Is.True); + members.Add(item.Key); + } + Assert.That(members.Count, Is.EqualTo(stringMap.Count)); + } + + [Test] + public async Task Can_Add_to_IDictionary_Hash() + { + var hash = RedisAsync.Hashes[HashId]; + foreach (var x in stringMap) + { + await hash.AddAsync(x); + } + + var members = await RedisAsync.GetAllEntriesFromHashAsync(HashId); + Assert.That(members, Is.EquivalentTo(stringMap)); + } + + [Test] + public async Task Can_Clear_IDictionary_Hash() + { + var hash = RedisAsync.Hashes[HashId]; + foreach (var x in stringMap) + { + await hash.AddAsync(x); + } + + Assert.That(await hash.CountAsync(), Is.EqualTo(stringMap.Count)); + + await hash.ClearAsync(); + + Assert.That(await hash.CountAsync(), Is.EqualTo(0)); + } + + [Test] + public async Task Can_Test_Contains_in_IDictionary_Hash() + { + var hash = RedisAsync.Hashes[HashId]; + foreach (var x in stringMap) + { + await hash.AddAsync(x); + } + + Assert.That(await hash.ContainsKeyAsync("two"), Is.True); + Assert.That(await hash.ContainsKeyAsync("five"), Is.False); + } + + [Test] + public async Task Can_Remove_value_from_IDictionary_Hash() + { + var hash = RedisAsync.Hashes[HashId]; + foreach (var x in stringMap) + { + await hash.AddAsync(x); + } + + stringMap.Remove("two"); + await hash.RemoveAsync("two"); + + var members = await RedisAsync.GetAllEntriesFromHashAsync(HashId); + Assert.That(members, Is.EquivalentTo(stringMap)); + } + + private static Dictionary ToStringMap(Dictionary stringIntMap) + { + var map = new Dictionary(); + foreach (var kvp in stringIntMap) + { + map[kvp.Key] = kvp.Value.ToString(); + } + return map; + } + + [Test] + public async Task Can_increment_Hash_field() + { + var hash = RedisAsync.Hashes[HashId]; + foreach (var x in stringIntMap) + { + await hash.AddAsync(x.Key, x.Value.ToString()); + } + + stringIntMap["two"] += 10; + await RedisAsync.IncrementValueInHashAsync(HashId, "two", 10); + + var members = await RedisAsync.GetAllEntriesFromHashAsync(HashId); + Assert.That(members, Is.EquivalentTo(ToStringMap(stringIntMap))); + } + + [Test] + public async Task Can_increment_Hash_field_beyond_32_bits() + { + await RedisAsync.SetEntryInHashAsync(HashId, "int", Int32.MaxValue.ToString()); + await RedisAsync.IncrementValueInHashAsync(HashId, "int", 1); + long actual = Int64.Parse(await RedisAsync.GetValueFromHashAsync(HashId, "int")); + long expected = Int32.MaxValue + 1L; + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public async Task Can_SetItemInHashIfNotExists() + { + foreach (var x in stringMap) + { + await RedisAsync.SetEntryInHashAsync(HashId, x.Key, x.Value); + } + + await RedisAsync.SetEntryInHashIfNotExistsAsync(HashId, "two", "did not change existing item"); + await RedisAsync.SetEntryInHashIfNotExistsAsync(HashId, "five", "changed non existing item"); + stringMap["five"] = "changed non existing item"; + + var members = await RedisAsync.GetAllEntriesFromHashAsync(HashId); + Assert.That(members, Is.EquivalentTo(stringMap)); + } + + [Test] + public async Task Can_SetRangeInHash() + { + var newStringMap = new Dictionary { + {"five","e"}, {"six","f"}, {"seven","g"} + }; + foreach (var x in stringMap) + { + await RedisAsync.SetEntryInHashAsync(HashId, x.Key, x.Value); + } + + await RedisAsync.SetRangeInHashAsync(HashId, newStringMap); + + newStringMap.Each(x => stringMap.Add(x.Key, x.Value)); + + var members = await RedisAsync.GetAllEntriesFromHashAsync(HashId); + Assert.That(members, Is.EquivalentTo(stringMap)); + } + + [Test] + public async Task Can_GetItemsFromHash() + { + foreach (var x in stringMap) + { + await RedisAsync.SetEntryInHashAsync(HashId, x.Key, x.Value); + } + + var expectedValues = new List { stringMap["one"], stringMap["two"], null }; + var hashValues = await RedisAsync.GetValuesFromHashAsync(HashId, new[] { "one", "two", "not-exists" }); + + Assert.That(hashValues.EquivalentTo(expectedValues), Is.True); + } + [Test] + public async Task Can_hash_set() + { + var key = HashId + "key"; + var field = GetBytes("foo"); + var value = GetBytes("value"); + Assert.AreEqual(await NativeAsync.HDelAsync(key, field), 0); + Assert.AreEqual(await NativeAsync.HSetAsync(key, field, value), 1); + Assert.AreEqual(await NativeAsync.HDelAsync(key, field), 1); + } + + [Test] + public async Task Can_hash_multi_set_and_get() + { + const string Key = HashId + "multitest"; + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + var fields = new Dictionary { { "field1", "1" }, { "field2", "2" }, { "field3", "3" } }; + + await RedisAsync.SetRangeInHashAsync(Key, fields); + var members = await RedisAsync.GetAllEntriesFromHashAsync(Key); + foreach (var member in members) + { + Assert.IsTrue(fields.ContainsKey(member.Key)); + Assert.AreEqual(fields[member.Key], member.Value); + } + } + + public class HashTest + { + public int Id { get; set; } + public string Name { get; set; } + } + + [Test] + public async Task Can_store_as_Hash() + { + var dto = new HashTest { Id = 1 }; + await RedisAsync.StoreAsHashAsync(dto); + + var storedHash = await RedisAsync.GetHashKeysAsync(dto.ToUrn()); + Assert.That(storedHash, Is.EquivalentTo(new[] { "Id" })); + + var hold = RedisClient.ConvertToHashFn; + RedisClient.ConvertToHashFn = o => + { + var map = new Dictionary(); + o.ToObjectDictionary().Each(x => map[x.Key] = (x.Value ?? "").ToJsv()); + return map; + }; + + await RedisAsync.StoreAsHashAsync(dto); + storedHash = await RedisAsync.GetHashKeysAsync(dto.ToUrn()); + Assert.That(storedHash, Is.EquivalentTo(new[] { "Id", "Name" })); + + RedisClient.ConvertToHashFn = hold; + } + } + +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisClientListTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientListTests.Async.cs new file mode 100644 index 00000000..b68445a2 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisClientListTests.Async.cs @@ -0,0 +1,502 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using System.Linq; +using ServiceStack.Text; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class RedisClientListTestsAsync + : RedisClientTestsBaseAsync + { + const string ListId = "rcl_testlist"; + const string ListId2 = "rcl_testlist2"; + private List storeMembers; + + public RedisClientListTestsAsync() + { + CleanMask = "rcl_testlist*"; + } + + public override void OnBeforeEachTest() + { + base.OnBeforeEachTest(); + storeMembers = new List { "one", "two", "three", "four" }; + } + + private static void AssertAreEqual(List actualList, List expectedList) + { + Assert.That(actualList, Has.Count.EqualTo(expectedList.Count)); + var i = 0; + actualList.ForEach(x => Assert.That(x, Is.EqualTo(expectedList[i++]))); + } + + private static void AssertAreEqual(List actualList, Queue expectedList) + { + Assert.That(actualList, Has.Count.EqualTo(expectedList.Count)); + actualList.ForEach(x => Assert.That(x, Is.EqualTo(expectedList.Dequeue()))); + } + + [Test] + public async Task Can_PopAndPushItemBetweenLists() + { + await RedisAsync.AddItemToListAsync(ListId, "1"); + await RedisAsync.PopAndPushItemBetweenListsAsync(ListId, ListId2); + } + + [Test] + public async Task Can_BlockingPopAndPushItemBetweenLists() + { + await RedisAsync.AddItemToListAsync(ListId, "A"); + await RedisAsync.AddItemToListAsync(ListId, "B"); + var r = await RedisAsync.BlockingPopAndPushItemBetweenListsAsync(ListId, ListId2, new TimeSpan(0, 0, 1)); + + Assert.That(r, Is.EqualTo("B")); + } + + [Test] + public async Task Can_Timeout_BlockingPopAndPushItemBetweenLists() + { + var r = await RedisAsync.BlockingPopAndPushItemBetweenListsAsync(ListId, ListId2, new TimeSpan(0, 0, 1)); + Assert.That(r, Is.Null); + } + + [Test] + public async Task Can_AddToList_and_GetAllFromList() + { + foreach (var x in storeMembers) + { + await RedisAsync.AddItemToListAsync(ListId, x); + } + + var members = await RedisAsync.GetAllItemsFromListAsync(ListId); + + AssertAreEqual(members, storeMembers); + } + + [Test] + public async Task Can_AddRangeToList_and_GetAllFromList() + { + await RedisAsync.AddRangeToListAsync(ListId, storeMembers); + + var members = await RedisAsync.GetAllItemsFromListAsync(ListId); + AssertAreEqual(members, storeMembers); + } + + [Test] + public async Task Can_PrependRangeToList_and_GetAllFromList() + { + await RedisAsync.PrependRangeToListAsync(ListId, storeMembers); + + var members = await RedisAsync.GetAllItemsFromListAsync(ListId); + AssertAreEqual(members, storeMembers); + } + + [Test] + public async Task Can_GetListCount() + { + foreach (var x in storeMembers) + { + await RedisAsync.AddItemToListAsync(ListId, x); + } + + var listCount = await RedisAsync.GetListCountAsync(ListId); + + Assert.That(listCount, Is.EqualTo(storeMembers.Count)); + } + + [Test] + public async Task Can_GetItemFromList() + { + foreach (var x in storeMembers) + { + await RedisAsync.AddItemToListAsync(ListId, x); + } + + var storeMember3 = storeMembers[2]; + var item3 = await RedisAsync.GetItemFromListAsync(ListId, 2); + + Assert.That(item3, Is.EqualTo(storeMember3)); + } + + [Test] + public async Task Can_SetItemInList() + { + foreach (var x in storeMembers) + { + await RedisAsync.AddItemToListAsync(ListId, x); + } + + storeMembers[2] = "five"; + await RedisAsync.SetItemInListAsync(ListId, 2, "five"); + + var members = await RedisAsync.GetAllItemsFromListAsync(ListId); + AssertAreEqual(members, storeMembers); + } + + [Test] + public async Task Can_PopFromList() + { + foreach (var x in storeMembers) + { + await RedisAsync.AddItemToListAsync(ListId, x); + } + + var item4 = await RedisAsync.PopItemFromListAsync(ListId); + + Assert.That(item4, Is.EqualTo("four")); + } + + [Test] + public async Task Can_EnqueueOnList() + { + var queue = new Queue(); + storeMembers.ForEach(queue.Enqueue); + foreach (var x in storeMembers) + { + await RedisAsync.EnqueueItemOnListAsync(ListId, x); + } + + while (queue.Count > 0) + { + var actual = await RedisAsync.DequeueItemFromListAsync(ListId); + Assert.That(actual, Is.EqualTo(queue.Dequeue())); + } + } + + [Test] + public async Task Can_DequeueFromList() + { + var queue = new Queue(); + storeMembers.ForEach(queue.Enqueue); + foreach (var x in storeMembers) + { + await RedisAsync.EnqueueItemOnListAsync(ListId, x); + } + + var item1 = await RedisAsync.DequeueItemFromListAsync(ListId); + + Assert.That(item1, Is.EqualTo(queue.Dequeue())); + } + + [Test] + public async Task PopAndPushSameAsDequeue() + { + var queue = new Queue(); + storeMembers.ForEach(queue.Enqueue); + foreach (var x in storeMembers) + { + await RedisAsync.EnqueueItemOnListAsync(ListId, x); + } + + var item1 = await RedisAsync.PopAndPushItemBetweenListsAsync(ListId, ListId2); + Assert.That(item1, Is.EqualTo(queue.Dequeue())); + } + + [Test] + public async Task Can_BlockingDequeueFromList() + { + var queue = new Queue(); + storeMembers.ForEach(queue.Enqueue); + foreach (var x in storeMembers) + { + await RedisAsync.EnqueueItemOnListAsync(ListId, x); + } + + var item1 = await RedisAsync.BlockingDequeueItemFromListAsync(ListId, null); + + Assert.That(item1, Is.EqualTo(queue.Dequeue())); + } + + [Test] + public async Task BlockingDequeueFromList_Can_TimeOut() + { + var item1 = await RedisAsync.BlockingDequeueItemFromListAsync(ListId, TimeSpan.FromSeconds(1)); + Assert.That(item1, Is.Null); + } + + [Test] + public async Task Can_PushToList() + { + var stack = new Stack(); + storeMembers.ForEach(stack.Push); + foreach (var x in storeMembers) + { + await RedisAsync.PushItemToListAsync(ListId, x); + } + + while (stack.Count > 0) + { + var actual = await RedisAsync.PopItemFromListAsync(ListId); + Assert.That(actual, Is.EqualTo(stack.Pop())); + } + } + + [Test] + public async Task Can_BlockingPopFromList() + { + var stack = new Stack(); + storeMembers.ForEach(stack.Push); + foreach (var x in storeMembers) + { + await RedisAsync.PushItemToListAsync(ListId, x); + } + + var item1 = await RedisAsync.BlockingPopItemFromListAsync(ListId, null); + + Assert.That(item1, Is.EqualTo(stack.Pop())); + } + + [Test] + public async Task BlockingPopFromList_Can_TimeOut() + { + var item1 = await RedisAsync.BlockingPopItemFromListAsync(ListId, TimeSpan.FromSeconds(1)); + Assert.That(item1, Is.Null); + } + + [Test] + public async Task Can_RemoveStartFromList() + { + foreach (var x in storeMembers) + { + await RedisAsync.AddItemToListAsync(ListId, x); + } + + var item1 = await RedisAsync.RemoveStartFromListAsync(ListId); + + Assert.That(item1, Is.EqualTo(storeMembers.First())); + } + + [Test] + public async Task Can_RemoveEndFromList() + { + foreach (var x in storeMembers) + { + await RedisAsync.AddItemToListAsync(ListId, x); + } + + var item1 = await RedisAsync.RemoveEndFromListAsync(ListId); + + Assert.That(item1, Is.EqualTo(storeMembers.Last())); + } + + [Test] + public async Task Can_BlockingRemoveStartFromList() + { + foreach (var x in storeMembers) + { + await RedisAsync.AddItemToListAsync(ListId, x); + } + + var item1 = await RedisAsync.BlockingRemoveStartFromListAsync(ListId, null); + + Assert.That(item1, Is.EqualTo(storeMembers.First())); + } + + [Test] + public async Task Can_MoveBetweenLists() + { + var list1Members = new List { "one", "two", "three", "four" }; + var list2Members = new List { "five", "six", "seven" }; + const string item4 = "four"; + + foreach (var x in list1Members) + { + await RedisAsync.AddItemToListAsync(ListId, x); + } + foreach (var x in list2Members) + { + await RedisAsync.AddItemToListAsync(ListId2, x); + } + + list1Members.Remove(item4); + list2Members.Insert(0, item4); + await RedisAsync.PopAndPushItemBetweenListsAsync(ListId, ListId2); + + var readList1 = await RedisAsync.GetAllItemsFromListAsync(ListId); + var readList2 = await RedisAsync.GetAllItemsFromListAsync(ListId2); + + AssertAreEqual(readList1, list1Members); + AssertAreEqual(readList2, list2Members); + } + + + [Test] + public async Task Can_enumerate_small_list() + { + foreach (var x in storeMembers) + { + await RedisAsync.AddItemToListAsync(ListId, x); + } + + var readMembers = new List(); + await foreach (var item in RedisAsync.Lists[ListId]) + { + readMembers.Add(item); + } + AssertAreEqual(readMembers, storeMembers); + } + + [Test] + public async Task Can_enumerate_large_list() + { + if (TestConfig.IgnoreLongTests) return; + + const int listSize = 2500; + + storeMembers = new List(); + for (int x = 0; x < listSize; x++) + { + await RedisAsync.AddItemToListAsync(ListId, x.ToString()); + storeMembers.Add(x.ToString()); + } + + var members = new List(); + await foreach (var item in RedisAsync.Lists[ListId]) + { + members.Add(item); + } + members.Sort((x, y) => int.Parse(x).CompareTo(int.Parse(y))); + Assert.That(members.Count, Is.EqualTo(storeMembers.Count)); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_Add_to_IList() + { + var list = RedisAsync.Lists[ListId]; + foreach (var x in storeMembers) + { + await list.AddAsync(x); + } + + var members = await ToListAsync(list); + AssertAreEqual(members, storeMembers); + } + + [Test] + public async Task Can_Clear_IList() + { + var list = RedisAsync.Lists[ListId]; + foreach (var x in storeMembers) + { + await list.AddAsync(x); + } + + Assert.That(await list.CountAsync(), Is.EqualTo(storeMembers.Count)); + + await list.ClearAsync(); + + Assert.That(await list.CountAsync(), Is.EqualTo(0)); + } + + [Test] + public async Task Can_Test_Contains_in_IList() + { + var list = RedisAsync.Lists[ListId]; + foreach (var x in storeMembers) + { + await list.AddAsync(x); + } + + Assert.That(await list.ContainsAsync("two"), Is.True); + Assert.That(await list.ContainsAsync("five"), Is.False); + } + + [Test] + public async Task Can_Remove_value_from_IList() + { + var list = RedisAsync.Lists[ListId]; + foreach (var x in storeMembers) + { + await list.AddAsync(x); + } + + storeMembers.Remove("two"); + await list.RemoveAsync("two"); + + var members = await ToListAsync(list); + + AssertAreEqual(members, storeMembers); + } + + [Test] + public async Task Can_RemoveAt_value_from_IList() + { + var list = RedisAsync.Lists[ListId]; + foreach (var x in storeMembers) + { + await list.AddAsync(x); + } + + storeMembers.RemoveAt(2); + await list.RemoveAtAsync(2); + + var members = await ToListAsync(list); + + AssertAreEqual(members, storeMembers); + } + + [Test] + public async Task Can_get_default_index_from_IList() + { + var list = RedisAsync.Lists[ListId]; + foreach (var x in storeMembers) + { + await list.AddAsync(x); + } + + for (var i = 0; i < storeMembers.Count; i++) + { + Assert.That(await list.ElementAtAsync(i), Is.EqualTo(storeMembers[i])); + } + } + + [Test] + public async Task Can_test_for_IndexOf_in_IList() + { + var list = RedisAsync.Lists[ListId]; + foreach (var x in storeMembers) + { + await list.AddAsync(x); + } + + foreach (var item in storeMembers) + { + Assert.That(await list.IndexOfAsync(item), Is.EqualTo(storeMembers.IndexOf(item))); + } + } + + [Test] + public async Task Can_AddRangeToList_and_GetSortedItems() + { + await RedisAsync.PrependRangeToListAsync(ListId, storeMembers); + + var members = await RedisAsync.GetSortedItemsFromListAsync(ListId, new SortOptions { SortAlpha = true, SortDesc = true, Skip = 1, Take = 2 }); + AssertAreEqual(members, storeMembers.OrderByDescending(s => s).Skip(1).Take(2).ToList()); + } + + public class Test + { + public string A { get; set; } + } + + [Test] + public async Task RemoveAll_removes_all_items_from_Named_List() + { + var redis = RedisAsync.As(); + + var clientesRepo = redis.Lists["repo:Client:Test"]; + + Assert.IsTrue(await clientesRepo.CountAsync() == 0, "Count 1 = " + await clientesRepo.CountAsync()); + await clientesRepo.AddAsync(new Test() { A = "Test" }); + Assert.IsTrue(await clientesRepo.CountAsync() == 1, "Count 2 = " + await clientesRepo.CountAsync()); + await clientesRepo.RemoveAllAsync(); + Assert.IsTrue(await clientesRepo.CountAsync() == 0, "Count 3 = " + await clientesRepo.CountAsync()); + } + + } + +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisClientSetTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientSetTests.Async.cs new file mode 100644 index 00000000..ad23cc5a --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisClientSetTests.Async.cs @@ -0,0 +1,335 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Text; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class RedisClientSetTestsAsync + : RedisClientTestsBaseAsync + { + private const string SetIdSuffix = "testset"; + private List storeMembers; + + private string SetId + { + get + { + return this.PrefixedKey(SetIdSuffix); + } + } + + [SetUp] + public override void OnBeforeEachTest() + { + base.OnBeforeEachTest(); + RedisRaw.NamespacePrefix = "RedisClientSetTests"; + storeMembers = new List { "one", "two", "three", "four" }; + } + + [Test] + public async Task Can_AddToSet_and_GetAllFromSet() + { + await storeMembers.ForEachAsync(x => RedisAsync.AddItemToSetAsync(SetId, x)); + + var members = await RedisAsync.GetAllItemsFromSetAsync(SetId); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_AddRangeToSet_and_GetAllFromSet() + { + await RedisAsync.AddRangeToSetAsync(SetId, storeMembers); + + var members = await RedisAsync.GetAllItemsFromSetAsync(SetId); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_RemoveFromSet() + { + const string removeMember = "two"; + + await storeMembers.ForEachAsync(x => RedisAsync.AddItemToSetAsync(SetId, x)); + + await RedisAsync.RemoveItemFromSetAsync(SetId, removeMember); + + storeMembers.Remove(removeMember); + + var members = await RedisAsync.GetAllItemsFromSetAsync(SetId); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_PopFromSet() + { + await storeMembers.ForEachAsync(x => RedisAsync.AddItemToSetAsync(SetId, x)); + + var member = await RedisAsync.PopItemFromSetAsync(SetId); + + Assert.That(storeMembers.Contains(member), Is.True); + } + + [Test] + public async Task Can_MoveBetweenSets() + { + string fromSetId = PrefixedKey("testmovefromset"); + string toSetId = PrefixedKey("testmovetoset"); + const string moveMember = "four"; + var fromSetIdMembers = new List { "one", "two", "three", "four" }; + var toSetIdMembers = new List { "five", "six", "seven" }; + + await fromSetIdMembers.ForEachAsync(x => RedisAsync.AddItemToSetAsync(fromSetId, x)); + await toSetIdMembers.ForEachAsync(x => RedisAsync.AddItemToSetAsync(toSetId, x)); + + await RedisAsync.MoveBetweenSetsAsync(fromSetId, toSetId, moveMember); + + fromSetIdMembers.Remove(moveMember); + toSetIdMembers.Add(moveMember); + + var readFromSetId = await RedisAsync.GetAllItemsFromSetAsync(fromSetId); + var readToSetId = await RedisAsync.GetAllItemsFromSetAsync(toSetId); + + Assert.That(readFromSetId, Is.EquivalentTo(fromSetIdMembers)); + Assert.That(readToSetId, Is.EquivalentTo(toSetIdMembers)); + } + + [Test] + public async Task Can_GetSetCount() + { + await storeMembers.ForEachAsync(x => RedisAsync.AddItemToSetAsync(SetId, x)); + + var setCount = await RedisAsync.GetSetCountAsync(SetId); + + Assert.That(setCount, Is.EqualTo(storeMembers.Count)); + } + + [Test] + public async Task Does_SetContainsValue() + { + const string existingMember = "two"; + const string nonExistingMember = "five"; + + await storeMembers.ForEachAsync(x => RedisAsync.AddItemToSetAsync(SetId, x)); + + Assert.That(await RedisAsync.SetContainsItemAsync(SetId, existingMember), Is.True); + Assert.That(await RedisAsync.SetContainsItemAsync(SetId, nonExistingMember), Is.False); + } + + [Test] + public async Task Can_IntersectBetweenSets() + { + string set1Name = PrefixedKey("testintersectset1"); + string set2Name = PrefixedKey("testintersectset2"); + var set1Members = new List { "one", "two", "three", "four", "five" }; + var set2Members = new List { "four", "five", "six", "seven" }; + + await set1Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set1Name, x)); + await set2Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set2Name, x)); + + var intersectingMembers = await RedisAsync.GetIntersectFromSetsAsync(new[] { set1Name, set2Name }); + + Assert.That(intersectingMembers, Is.EquivalentTo(new List { "four", "five" })); + } + + [Test] + public async Task Can_Store_IntersectBetweenSets() + { + string set1Name = PrefixedKey("testintersectset1"); + string set2Name = PrefixedKey("testintersectset2"); + string storeSetName = PrefixedKey("testintersectsetstore"); + var set1Members = new List { "one", "two", "three", "four", "five" }; + var set2Members = new List { "four", "five", "six", "seven" }; + + await set1Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set1Name, x)); + await set2Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set2Name, x)); + + await RedisAsync.StoreIntersectFromSetsAsync(storeSetName, new[] { set1Name, set2Name }); + + var intersectingMembers = await RedisAsync.GetAllItemsFromSetAsync(storeSetName); + + Assert.That(intersectingMembers, Is.EquivalentTo(new List { "four", "five" })); + } + + [Test] + public async Task Can_UnionBetweenSets() + { + string set1Name = PrefixedKey("testunionset1"); + string set2Name = PrefixedKey("testunionset2"); + var set1Members = new List { "one", "two", "three", "four", "five" }; + var set2Members = new List { "four", "five", "six", "seven" }; + + await set1Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set1Name, x)); + await set2Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set2Name, x)); + + var unionMembers = await RedisAsync.GetUnionFromSetsAsync(new[] { set1Name, set2Name }); + + Assert.That(unionMembers, Is.EquivalentTo( + new List { "one", "two", "three", "four", "five", "six", "seven" })); + } + + [Test] + public async Task Can_Store_UnionBetweenSets() + { + string set1Name = PrefixedKey("testunionset1"); + string set2Name = PrefixedKey("testunionset2"); + string storeSetName = PrefixedKey("testunionsetstore"); + var set1Members = new List { "one", "two", "three", "four", "five" }; + var set2Members = new List { "four", "five", "six", "seven" }; + + await set1Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set1Name, x)); + await set2Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set2Name, x)); + + await RedisAsync.StoreUnionFromSetsAsync(storeSetName, new[] { set1Name, set2Name }); + + var unionMembers = await RedisAsync.GetAllItemsFromSetAsync(storeSetName); + + Assert.That(unionMembers, Is.EquivalentTo( + new List { "one", "two", "three", "four", "five", "six", "seven" })); + } + + [Test] + public async Task Can_DiffBetweenSets() + { + string set1Name = PrefixedKey("testdiffset1"); + string set2Name = PrefixedKey("testdiffset2"); + string set3Name = PrefixedKey("testdiffset3"); + var set1Members = new List { "one", "two", "three", "four", "five" }; + var set2Members = new List { "four", "five", "six", "seven" }; + var set3Members = new List { "one", "five", "seven", "eleven" }; + + await set1Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set1Name, x)); + await set2Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set2Name, x)); + await set3Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set3Name, x)); + + var diffMembers = await RedisAsync.GetDifferencesFromSetAsync(set1Name, new[] { set2Name, set3Name }); + + Assert.That(diffMembers, Is.EquivalentTo( + new List { "two", "three" })); + } + + [Test] + public async Task Can_Store_DiffBetweenSets() + { + string set1Name = PrefixedKey("testdiffset1"); + string set2Name = PrefixedKey("testdiffset2"); + string set3Name = PrefixedKey("testdiffset3"); + string storeSetName = PrefixedKey("testdiffsetstore"); + var set1Members = new List { "one", "two", "three", "four", "five" }; + var set2Members = new List { "four", "five", "six", "seven" }; + var set3Members = new List { "one", "five", "seven", "eleven" }; + + await set1Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set1Name, x)); + await set2Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set2Name, x)); + await set3Members.ForEachAsync(x => RedisAsync.AddItemToSetAsync(set3Name, x)); + + await RedisAsync.StoreDifferencesFromSetAsync(storeSetName, set1Name, new[] { set2Name, set3Name }); + + var diffMembers = await RedisAsync.GetAllItemsFromSetAsync(storeSetName); + + Assert.That(diffMembers, Is.EquivalentTo( + new List { "two", "three" })); + } + + [Test] + public async Task Can_GetRandomEntryFromSet() + { + await storeMembers.ForEachAsync(x => RedisAsync.AddItemToSetAsync(SetId, x)); + + var randomEntry = await RedisAsync.GetRandomItemFromSetAsync(SetId); + + Assert.That(storeMembers.Contains(randomEntry), Is.True); + } + + + [Test] + public async Task Can_enumerate_small_ICollection_Set() + { + await storeMembers.ForEachAsync(x => RedisAsync.AddItemToSetAsync(SetId, x)); + + var members = new List(); + await foreach (var item in RedisAsync.Sets[SetId]) + { + members.Add(item); + } + members.Sort(); + Assert.That(members.Count, Is.EqualTo(storeMembers.Count)); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_enumerate_large_ICollection_Set() + { + if (TestConfig.IgnoreLongTests) return; + + const int setSize = 2500; + + storeMembers = new List(); + await setSize.TimesAsync(async x => + { + await RedisAsync.AddItemToSetAsync(SetId, x.ToString()); + storeMembers.Add(x.ToString()); + }); + + var members = new List(); + await foreach (var item in RedisAsync.Sets[SetId]) + { + members.Add(item); + } + members.Sort((x, y) => int.Parse(x).CompareTo(int.Parse(y))); + Assert.That(members.Count, Is.EqualTo(storeMembers.Count)); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_Add_to_ICollection_Set() + { + var list = RedisAsync.Sets[SetId]; + await storeMembers.ForEachAsync(x => list.AddAsync(x)); + + var members = await list.ToListAsync(); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_Clear_ICollection_Set() + { + var list = RedisAsync.Sets[SetId]; + await storeMembers.ForEachAsync(x => list.AddAsync(x)); + + Assert.That(await list.CountAsync(), Is.EqualTo(storeMembers.Count)); + + await list.ClearAsync(); + + Assert.That(await list.CountAsync(), Is.EqualTo(0)); + } + + [Test] + public async Task Can_Test_Contains_in_ICollection_Set() + { + var list = RedisAsync.Sets[SetId]; + await storeMembers.ForEachAsync(x => list.AddAsync(x)); + + Assert.That(await list.ContainsAsync("two"), Is.True); + Assert.That(await list.ContainsAsync("five"), Is.False); + } + + [Test] + public async Task Can_Remove_value_from_ICollection_Set() + { + var list = RedisAsync.Sets[SetId]; + await storeMembers.ForEachAsync(x => list.AddAsync(x)); + + storeMembers.Remove("two"); + await list.RemoveAsync("two"); + + var members = await list.ToListAsync(); + + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + } + +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisClientSortedSetTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientSortedSetTests.Async.cs new file mode 100644 index 00000000..ae31d2d3 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisClientSortedSetTests.Async.cs @@ -0,0 +1,454 @@ +using NUnit.Framework; +using ServiceStack.Text; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Integration")] + public class RedisClientSortedSetTestsAsync + : RedisClientTestsBaseAsync + { + private const string SetIdSuffix = "testzset"; + private List storeMembers; + + private string SetId + { + get + { + return PrefixedKey(SetIdSuffix); + } + } + + Dictionary stringDoubleMap; + + public override void OnBeforeEachTest() + { + base.OnBeforeEachTest(); + RedisRaw.NamespacePrefix = "RedisClientSortedSetTests"; + storeMembers = new List { "one", "two", "three", "four" }; + + stringDoubleMap = new Dictionary { + {"one",1}, {"two",2}, {"three",3}, {"four",4} + }; + } + + [Test] + public async Task Can_AddItemToSortedSet_and_GetAllFromSet() + { + var i = 0; + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x, i++)); + + var members = await RedisAsync.GetAllItemsFromSortedSetAsync(SetId); + Assert.That(members.EquivalentTo(storeMembers), Is.True); + } + + [Test] + public async Task Can_AddRangeToSortedSet_and_GetAllFromSet() + { + var success = await RedisAsync.AddRangeToSortedSetAsync(SetId, storeMembers, 1); + Assert.That(success, Is.True); + + var members = await RedisAsync.GetAllItemsFromSortedSetAsync(SetId); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task AddToSet_without_score_adds_an_implicit_lexical_order_score() + { + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x)); + + var members = await RedisAsync.GetAllItemsFromSortedSetAsync(SetId); + + storeMembers.Sort((x, y) => x.CompareTo(y)); + Assert.That(members.EquivalentTo(storeMembers), Is.True); + } + + [Test] + public async Task AddToSet_with_same_score_is_still_returned_in_lexical_order_score() + { + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x, 1)); + + var members = await RedisAsync.GetAllItemsFromSortedSetAsync(SetId); + + storeMembers.Sort((x, y) => x.CompareTo(y)); + Assert.That(members.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_RemoveFromSet() + { + const string removeMember = "two"; + + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x)); + + await RedisAsync.RemoveItemFromSortedSetAsync(SetId, removeMember); + + storeMembers.Remove(removeMember); + + var members = await RedisAsync.GetAllItemsFromSortedSetAsync(SetId); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_RemoveItemsFromSortedSet() + { + var removeMembers = new[] { "two" , "four", "six" }; + + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x)); + + var removeCount = await RedisAsync.RemoveItemsFromSortedSetAsync(SetId, removeMembers.ToList()); + Assert.That(removeCount, Is.EqualTo(2)); + + removeMembers.Each(x => storeMembers.Remove(x)); + + var members = await RedisAsync.GetAllItemsFromSortedSetAsync(SetId); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_PopFromSet() + { + var i = 0; + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x, i++)); + + var member = await RedisAsync.PopItemWithHighestScoreFromSortedSetAsync(SetId); + + Assert.That(member, Is.EqualTo("four")); + } + + [Test] + public async Task Can_GetSetCount() + { + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x)); + + var setCount = await RedisAsync.GetSortedSetCountAsync(SetId); + + Assert.That(setCount, Is.EqualTo(storeMembers.Count)); + } + + [Test] + public async Task Can_GetSetCountByScores() + { + var scores = new List(); + + await storeMembers.ForEachAsync(async x => + { + await RedisAsync.AddItemToSortedSetAsync(SetId, x); + scores.Add(RedisClient.GetLexicalScore(x)); + }); + + Assert.That(await RedisAsync.GetSortedSetCountAsync(SetId, scores.Min(), scores.Max()), Is.EqualTo(storeMembers.Count())); + Assert.That(await RedisAsync.GetSortedSetCountAsync(SetId, scores.Min(), scores.Min()), Is.EqualTo(1)); + } + + [Test] + public async Task Does_SortedSetContainsValue() + { + const string existingMember = "two"; + const string nonExistingMember = "five"; + + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x)); + + Assert.That(await RedisAsync.SortedSetContainsItemAsync(SetId, existingMember), Is.True); + Assert.That(await RedisAsync.SortedSetContainsItemAsync(SetId, nonExistingMember), Is.False); + } + + [Test] + public async Task Can_GetItemIndexInSortedSet_in_Asc_and_Desc() + { + var i = 10; + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x, i++)); + + Assert.That(await RedisAsync.GetItemIndexInSortedSetAsync(SetId, "one"), Is.EqualTo(0)); + Assert.That(await RedisAsync.GetItemIndexInSortedSetAsync(SetId, "two"), Is.EqualTo(1)); + Assert.That(await RedisAsync.GetItemIndexInSortedSetAsync(SetId, "three"), Is.EqualTo(2)); + Assert.That(await RedisAsync.GetItemIndexInSortedSetAsync(SetId, "four"), Is.EqualTo(3)); + + Assert.That(await RedisAsync.GetItemIndexInSortedSetDescAsync(SetId, "one"), Is.EqualTo(3)); + Assert.That(await RedisAsync.GetItemIndexInSortedSetDescAsync(SetId, "two"), Is.EqualTo(2)); + Assert.That(await RedisAsync.GetItemIndexInSortedSetDescAsync(SetId, "three"), Is.EqualTo(1)); + Assert.That(await RedisAsync.GetItemIndexInSortedSetDescAsync(SetId, "four"), Is.EqualTo(0)); + } + + [Test] + public async Task Can_Store_IntersectBetweenSets() + { + string set1Name = PrefixedKey("testintersectset1"); + string set2Name = PrefixedKey("testintersectset2"); + string storeSetName = PrefixedKey("testintersectsetstore"); + var set1Members = new List { "one", "two", "three", "four", "five" }; + var set2Members = new List { "four", "five", "six", "seven" }; + + await set1Members.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(set1Name, x)); + await set2Members.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(set2Name, x)); + + await RedisAsync.StoreIntersectFromSortedSetsAsync(storeSetName, new[] { set1Name, set2Name }); + + var intersectingMembers = await RedisAsync.GetAllItemsFromSortedSetAsync(storeSetName); + + Assert.That(intersectingMembers, Is.EquivalentTo(new List { "four", "five" })); + } + + [Test] + public async Task Can_Store_UnionBetweenSets() + { + string set1Name = PrefixedKey("testunionset1"); + string set2Name = PrefixedKey("testunionset2"); + string storeSetName = PrefixedKey("testunionsetstore"); + var set1Members = new List { "one", "two", "three", "four", "five" }; + var set2Members = new List { "four", "five", "six", "seven" }; + + await set1Members.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(set1Name, x)); + await set2Members.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(set2Name, x)); + + await RedisAsync.StoreUnionFromSortedSetsAsync(storeSetName, new[] { set1Name, set2Name }); + + var unionMembers = await RedisAsync.GetAllItemsFromSortedSetAsync(storeSetName); + + Assert.That(unionMembers, Is.EquivalentTo( + new List { "one", "two", "three", "four", "five", "six", "seven" })); + } + + [Test] + public async Task Can_pop_items_with_lowest_and_highest_scores_from_sorted_set() + { + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x)); + + storeMembers.Sort((x, y) => x.CompareTo(y)); + + var lowestScore = await RedisAsync.PopItemWithLowestScoreFromSortedSetAsync(SetId); + Assert.That(lowestScore, Is.EqualTo(storeMembers.First())); + + var highestScore = await RedisAsync.PopItemWithHighestScoreFromSortedSetAsync(SetId); + Assert.That(highestScore, Is.EqualTo(storeMembers[storeMembers.Count - 1])); + } + + [Test, Ignore("seems unstable?")] + public async Task Can_GetRangeFromSortedSetByLowestScore_from_sorted_set() + { + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x)); + + storeMembers.Sort((x, y) => x.CompareTo(y)); + var memberRage = storeMembers.Where(x => + x.CompareTo("four") >= 0 && x.CompareTo("three") <= 0).ToList(); + + var range = await RedisAsync.GetRangeFromSortedSetByLowestScoreAsync(SetId, "four", "three"); + Assert.That(range.EquivalentTo(memberRage)); + } + + [Test] + public async Task Can_IncrementItemInSortedSet() + { + await stringDoubleMap.ForEachAsync(async (k,v) => await RedisAsync.AddItemToSortedSetAsync(SetId, k, v)); + + var currentScore = await RedisAsync.IncrementItemInSortedSetAsync(SetId, "one", 3); + stringDoubleMap["one"] = stringDoubleMap["one"] + 3; + Assert.That(currentScore, Is.EqualTo(stringDoubleMap["one"])); + + currentScore = await RedisAsync.IncrementItemInSortedSetAsync(SetId, "four", -3); + stringDoubleMap["four"] = stringDoubleMap["four"] - 3; + Assert.That(currentScore, Is.EqualTo(stringDoubleMap["four"])); + + var map = await RedisAsync.GetAllWithScoresFromSortedSetAsync(SetId); + + Assert.That(stringDoubleMap.UnorderedEquivalentTo(map)); + } + + [Test] + public async Task Can_WorkInSortedSetUnderDifferentCulture() + { +#if NETCORE + var prevCulture = CultureInfo.CurrentCulture; + CultureInfo.CurrentCulture = new CultureInfo("ru-RU"); +#else + var prevCulture = Thread.CurrentThread.CurrentCulture; + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("ru-RU"); +#endif + await RedisAsync.AddItemToSortedSetAsync(SetId, "key", 123.22); + + var map = await RedisAsync.GetAllWithScoresFromSortedSetAsync(SetId); + + Assert.AreEqual(123.22, map["key"]); + +#if NETCORE + CultureInfo.CurrentCulture = prevCulture; +#else + Thread.CurrentThread.CurrentCulture = prevCulture; +#endif + } + + + [Ignore("Not implemented yet")] + [Test] + public async Task Can_GetRangeFromSortedSetByHighestScore_from_sorted_set() + { + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x)); + + storeMembers.Sort((x, y) => y.CompareTo(x)); + var memberRage = storeMembers.Where(x => + x.CompareTo("four") >= 0 && x.CompareTo("three") <= 0).ToList(); + + var range = await RedisAsync.GetRangeFromSortedSetByHighestScoreAsync(SetId, "four", "three"); + Assert.That(range.EquivalentTo(memberRage)); + } + + [Test] + public async Task Can_get_index_and_score_from_SortedSet() + { + storeMembers = new List { "a", "b", "c", "d" }; + const double initialScore = 10d; + var i = initialScore; + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x, i++)); + + Assert.That(await RedisAsync.GetItemIndexInSortedSetAsync(SetId, "a"), Is.EqualTo(0)); + Assert.That(await RedisAsync.GetItemIndexInSortedSetDescAsync(SetId, "a"), Is.EqualTo(storeMembers.Count - 1)); + + Assert.That(await RedisAsync.GetItemScoreInSortedSetAsync(SetId, "a"), Is.EqualTo(initialScore)); + Assert.That(await RedisAsync.GetItemScoreInSortedSetAsync(SetId, "d"), Is.EqualTo(initialScore + storeMembers.Count - 1)); + } + + [Test] + public async Task Can_enumerate_small_ICollection_Set() + { + await storeMembers.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(SetId, x)); + + var members = new List(); + await foreach (var item in RedisAsync.SortedSets[SetId]) + { + members.Add(item); + } + members.Sort(); + Assert.That(members.Count, Is.EqualTo(storeMembers.Count)); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_enumerate_large_ICollection_Set() + { + if (TestConfig.IgnoreLongTests) return; + + const int setSize = 2500; + + storeMembers = new List(); + await setSize.TimesAsync(async x => + { + await RedisAsync.AddItemToSortedSetAsync(SetId, x.ToString()); + storeMembers.Add(x.ToString()); + }); + + var members = new List(); + await foreach (var item in RedisAsync.SortedSets[SetId]) + { + members.Add(item); + } + members.Sort((x, y) => int.Parse(x).CompareTo(int.Parse(y))); + Assert.That(members.Count, Is.EqualTo(storeMembers.Count)); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_Add_to_ICollection_Set() + { + var list = RedisAsync.SortedSets[SetId]; + await storeMembers.ForEachAsync(async x => await list.AddAsync(x)); + + var members = await list.ToListAsync(); + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Can_Clear_ICollection_Set() + { + var list = RedisAsync.SortedSets[SetId]; + await storeMembers.ForEachAsync(async x => await list.AddAsync(x)); + + Assert.That(await list.CountAsync(), Is.EqualTo(storeMembers.Count)); + + await list.ClearAsync(); + + Assert.That(await list.CountAsync(), Is.EqualTo(0)); + } + + [Test] + public async Task Can_Test_Contains_in_ICollection_Set() + { + var list = RedisAsync.SortedSets[SetId]; + await storeMembers.ForEachAsync(async x => await list.AddAsync(x)); + + Assert.That(await list.ContainsAsync("two"), Is.True); + Assert.That(await list.ContainsAsync("five"), Is.False); + } + + [Test] + public async Task Can_Remove_value_from_ICollection_Set() + { + var list = RedisAsync.SortedSets[SetId]; + await storeMembers.ForEachAsync(async x => await list.AddAsync(x)); + + storeMembers.Remove("two"); + await list.RemoveAsync("two"); + + var members = await list.ToListAsync(); + + Assert.That(members, Is.EquivalentTo(storeMembers)); + } + + [Test] + public async Task Score_from_non_existent_item_returns_NaN() + { + var score = await RedisAsync.GetItemScoreInSortedSetAsync("nonexistentset", "value"); + + Assert.That(score, Is.EqualTo(Double.NaN)); + } + + [Test] + public async Task Can_add_large_score_to_sortedset() + { + await RedisAsync.AddItemToSortedSetAsync(SetId, "value", 12345678901234567890d); + var score = await RedisAsync.GetItemScoreInSortedSetAsync(SetId, "value"); + + Assert.That(score, Is.EqualTo(12345678901234567890d)); + } + + public class Article + { + public int Id { get; set; } + public string Title { get; set; } + public DateTime ModifiedDate { get; set; } + } + + [Test] + public async Task Can_use_SortedIndex_to_store_articles_by_Date() + { + var redisArticles = RedisAsync.As
(); + + var articles = new[] + { + new Article { Id = 1, Title = "Article 1", ModifiedDate = new DateTime(2015, 01, 02) }, + new Article { Id = 2, Title = "Article 2", ModifiedDate = new DateTime(2015, 01, 01) }, + new Article { Id = 3, Title = "Article 3", ModifiedDate = new DateTime(2015, 01, 03) }, + }; + + await redisArticles.StoreAllAsync(articles); + + const string LatestArticlesSet = "urn:Article:modified"; + + foreach (var article in articles) + { + await RedisAsync.AddItemToSortedSetAsync(LatestArticlesSet, article.Id.ToString(), article.ModifiedDate.Ticks); + } + + var articleIds = await RedisAsync.GetAllItemsFromSortedSetDescAsync(LatestArticlesSet); + articleIds.PrintDump(); + + var latestArticles = await redisArticles.GetByIdsAsync(articleIds); + latestArticles.PrintDump(); + } + } + +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisClientSortedSetTests.cs b/tests/ServiceStack.Redis.Tests/RedisClientSortedSetTests.cs index 5f48d5bf..bfcb695d 100644 --- a/tests/ServiceStack.Redis.Tests/RedisClientSortedSetTests.cs +++ b/tests/ServiceStack.Redis.Tests/RedisClientSortedSetTests.cs @@ -229,7 +229,7 @@ public void Can_pop_items_with_lowest_and_highest_scores_from_sorted_set() Assert.That(highestScore, Is.EqualTo(storeMembers[storeMembers.Count - 1])); } - [Test] + [Test, Ignore("seems unstable?")] public void Can_GetRangeFromSortedSetByLowestScore_from_sorted_set() { storeMembers.ForEach(x => Redis.AddItemToSortedSet(SetId, x)); diff --git a/tests/ServiceStack.Redis.Tests/RedisClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientTests.Async.cs new file mode 100644 index 00000000..5b3cba1e --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisClientTests.Async.cs @@ -0,0 +1,669 @@ +using NUnit.Framework; +using ServiceStack.Redis.Support.Locking; +using ServiceStack.Text; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Integration")] + public class RedisClientTestsAsync + : RedisClientTestsBaseAsync + { + const string Value = "Value"; + + public override void OnBeforeEachTest() + { + base.OnBeforeEachTest(); + RedisRaw.NamespacePrefix = nameof(RedisClientTestsAsync); + } + + [Test] + public async Task Can_Set_and_Get_string() + { + await RedisAsync.SetValueAsync("key", Value); + var valueBytes = await NativeAsync.GetAsync("key"); + var valueString = GetString(valueBytes); + await RedisAsync.RemoveAsync("key"); + + Assert.That(valueString, Is.EqualTo(Value)); + } + + [Test] + public async Task Can_Set_and_Get_key_with_space() + { + await RedisAsync.SetValueAsync("key with space", Value); + var valueBytes = await NativeAsync.GetAsync("key with space"); + var valueString = GetString(valueBytes); + await RedisAsync.RemoveAsync("key with space"); + + Assert.That(valueString, Is.EqualTo(Value)); + } + + [Test] + public async Task Can_Set_and_Get_key_with_spaces() + { + const string key = "key with spaces"; + + await RedisAsync.SetValueAsync(key, Value); + var valueBytes = await NativeAsync.GetAsync(key); + var valueString = GetString(valueBytes); + + Assert.That(valueString, Is.EqualTo(Value)); + } + + [Test] + public async Task Can_Set_and_Get_key_with_all_byte_values() + { + const string key = "bytesKey"; + + var value = new byte[256]; + for (var i = 0; i < value.Length; i++) + { + value[i] = (byte)i; + } + + await RedisAsync.SetAsync(key, value); + var resultValue = await NativeAsync.GetAsync(key); + + Assert.That(resultValue, Is.EquivalentTo(value)); + } + + [Test] + public async Task GetKeys_returns_matching_collection() + { + await RedisAsync.SetAsync("ss-tests:a1", "One"); + await RedisAsync.SetAsync("ss-tests:a2", "One"); + await RedisAsync.SetAsync("ss-tests:b3", "One"); + + var matchingKeys = await RedisAsync.SearchKeysAsync("ss-tests:a*"); + + Assert.That(matchingKeys.Count, Is.EqualTo(2)); + } + + [Test] + public async Task GetKeys_on_non_existent_keys_returns_empty_collection() + { + var matchingKeys = await RedisAsync.SearchKeysAsync("ss-tests:NOTEXISTS"); + + Assert.That(matchingKeys.Count, Is.EqualTo(0)); + } + + [Test] + public async Task Can_get_Types() + { + await RedisAsync.SetValueAsync("string", "string"); + await RedisAsync.AddItemToListAsync("list", "list"); + await RedisAsync.AddItemToSetAsync("set", "set"); + await RedisAsync.AddItemToSortedSetAsync("sortedset", "sortedset"); + await RedisAsync.SetEntryInHashAsync("hash", "key", "val"); + + Assert.That(await RedisAsync.GetEntryTypeAsync("nokey"), Is.EqualTo(RedisKeyType.None)); + Assert.That(await RedisAsync.GetEntryTypeAsync("string"), Is.EqualTo(RedisKeyType.String)); + Assert.That(await RedisAsync.GetEntryTypeAsync("list"), Is.EqualTo(RedisKeyType.List)); + Assert.That(await RedisAsync.GetEntryTypeAsync("set"), Is.EqualTo(RedisKeyType.Set)); + Assert.That(await RedisAsync.GetEntryTypeAsync("sortedset"), Is.EqualTo(RedisKeyType.SortedSet)); + Assert.That(await RedisAsync.GetEntryTypeAsync("hash"), Is.EqualTo(RedisKeyType.Hash)); + } + + [Test] + public async Task Can_delete_keys() + { + await RedisAsync.SetValueAsync("key", "val"); + + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.True); + + await RedisAsync.RemoveAsync("key"); + + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.False); + + var keysMap = new Dictionary(); + + 10.Times(i => keysMap.Add("key" + i, "val" + i)); + + await RedisAsync.SetAllAsync(keysMap); + + for (int i = 0; i < 10; i++) + Assert.That(await RedisAsync.ContainsKeyAsync("key" + i), Is.True); + + await RedisAsync.RemoveEntryAsync(keysMap.Keys.ToArray()); + + for (int i = 0; i < 10; i++) + Assert.That(await RedisAsync.ContainsKeyAsync("key" + i), Is.False); + } + + [Test] + public async Task Can_get_RandomKey() + { + await RedisAsync.SelectAsync(15); + var keysMap = new Dictionary(); + + 10.Times(i => keysMap.Add(RedisRaw.NamespacePrefix + "key" + i, "val" + i)); + + await RedisAsync.SetAllAsync(keysMap); + + var randKey = await RedisAsync.GetRandomKeyAsync(); + + Assert.That(keysMap.ContainsKey(randKey), Is.True); + } + + [Test] + public async Task Can_RenameKey() + { + await RedisAsync.SetValueAsync("oldkey", "val"); + await NativeAsync.RenameAsync("oldkey", "newkey"); + + Assert.That(await RedisAsync.ContainsKeyAsync("oldkey"), Is.False); + Assert.That(await RedisAsync.ContainsKeyAsync("newkey"), Is.True); + } + + [Test] + public async Task Can_Expire() + { + await RedisAsync.SetValueAsync("key", "val"); + await RedisAsync.ExpireEntryInAsync("key", TimeSpan.FromSeconds(1)); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.True); + await Task.Delay(2000); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.False); + } + + [Test] + public async Task Can_Expire_Ms() + { + await RedisAsync.SetValueAsync("key", "val"); + await RedisAsync.ExpireEntryInAsync("key", TimeSpan.FromMilliseconds(100)); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.True); + await Task.Delay(500); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.False); + } + + [Ignore("Changes in system clock can break test")] + [Test] + public async Task Can_ExpireAt() + { + await RedisAsync.SetValueAsync("key", "val"); + + var unixNow = DateTime.Now.ToUnixTime(); + var in2Secs = unixNow + 2; + + await NativeAsync.ExpireAtAsync("key", in2Secs); + + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.True); + await Task.Delay(3000); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.False); + } + + [Test] + public async Task Can_GetTimeToLive() + { + await RedisAsync.SetValueAsync("key", "val"); + await RedisAsync.ExpireEntryInAsync("key", TimeSpan.FromSeconds(10)); + + var ttl = await RedisAsync.GetTimeToLiveAsync("key"); + Assert.That(ttl.Value.TotalSeconds, Is.GreaterThanOrEqualTo(9)); + await Task.Delay(1700); + + ttl = await RedisAsync.GetTimeToLiveAsync("key"); + Assert.That(ttl.Value.TotalSeconds, Is.LessThanOrEqualTo(9)); + } + + [Test] + public async Task Can_GetServerTime() + { + var now = await RedisAsync.GetServerTimeAsync(); + + now.Kind.PrintDump(); + now.ToString("D").Print(); + now.ToString("T").Print(); + + "UtcNow".Print(); + DateTime.UtcNow.ToString("D").Print(); + DateTime.UtcNow.ToString("T").Print(); + + Assert.That(now.Date, Is.EqualTo(DateTime.UtcNow.Date)); + } + + [Test] + public async Task Can_Ping() + { + Assert.That(await RedisAsync.PingAsync(), Is.True); + } + + [Test] + public async Task Can_Echo() + { + Assert.That(await RedisAsync.EchoAsync("Hello"), Is.EqualTo("Hello")); + } + + [Test] + public async Task Can_SlaveOfNoOne() + { + await NativeAsync.SlaveOfNoOneAsync(); + } + + [Test] + public async Task Can_Save() + { + try + { + await NativeAsync.SaveAsync(); + } + catch (RedisResponseException e) + { + // if exception has that message then it still proves that BgSave works as expected. + if (e.Message.StartsWith("Can't BGSAVE while AOF log rewriting is in progress") + || e.Message.StartsWith("An AOF log rewriting in progress: can't BGSAVE right now") + || e.Message.StartsWith("Background save already in progress") + || e.Message.StartsWith("Another child process is active (AOF?): can't BGSAVE right now")) + return; + + throw; + } + } + + [Test] + public async Task Can_BgSave() + { + try + { + await NativeAsync.BgSaveAsync(); + } + catch (RedisResponseException e) + { + // if exception has that message then it still proves that BgSave works as expected. + if (e.Message.StartsWith("Can't BGSAVE while AOF log rewriting is in progress") + || e.Message.StartsWith("An AOF log rewriting in progress: can't BGSAVE right now") + || e.Message.StartsWith("Background save already in progress") + || e.Message.StartsWith("Another child process is active (AOF?): can't BGSAVE right now")) + return; + + throw; + } + } + + [Test] + public async Task Can_Quit() + { + await NativeAsync.QuitAsync(); + RedisRaw.NamespacePrefix = null; + CleanMask = null; + } + + [Test] + public async Task Can_BgRewriteAof() + { + await NativeAsync.BgRewriteAofAsync(); + } + + [Test] + [Ignore("Works too well and shutdown the server")] + public async Task Can_Shutdown() + { + await RedisAsync.ShutdownAsync(); + } + + [Test] + public async Task Can_get_Keys_with_pattern() + { + for (int i = 0; i < 5; i++) + await RedisAsync.SetValueAsync("k1:" + i, "val"); + for (int i = 0; i < 5; i++) + await RedisAsync.SetValueAsync("k2:" + i, "val"); + + var keys = await NativeAsync.KeysAsync("k1:*"); + Assert.That(keys.Length, Is.EqualTo(5)); + + var scanKeys = await RedisAsync.SearchKeysAsync("k1:*"); + Assert.That(scanKeys.Count, Is.EqualTo(5)); + } + + [Test] + public async Task Can_GetAll() + { + var keysMap = new Dictionary(); + + 10.Times(i => keysMap.Add("key" + i, "val" + i)); + + await RedisAsync.SetAllAsync(keysMap); + + var map = await RedisAsync.GetAllAsync(keysMap.Keys); + var mapKeys = await RedisAsync.GetValuesAsync(keysMap.Keys.ToList()); + + foreach (var entry in keysMap) + { + Assert.That(map.ContainsKey(entry.Key), Is.True); + Assert.That(mapKeys.Contains(entry.Value), Is.True); + } + } + + [Test] + public async Task Can_GetValues_JSON_strings() + { + var val = "{\"AuthorId\":0,\"Created\":\"\\/Date(1345961754013)\\/\",\"Name\":\"test\",\"Base64\":\"BQELAAEBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAViA/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP8BWAFYgP8BWAFYAViA/wFYAVgBWID/AVgBWAFYgP8BWAFYAViA/4D/gP+A/4D/AVgBWID/gP8BWID/gP8BWID/gP+A/wFYgP+A/4D/gP8BWID/gP+A/4D/gP+A/wFYAViA/4D/AViA/4D/AVgBWAFYgP8BWAFYAViA/4D/AViA/4D/gP+A/4D/gP8BWAFYgP+A/wFYgP+A/wFYgP+A/4D/gP+A/wFYgP+A/wFYgP+A/4D/gP+A/4D/AVgBWID/gP8BWID/gP8BWAFYAViA/wFYAVgBWID/gP8BWID/gP+A/4D/gP+A/wFYAViA/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP8BWAFYgP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/AVgBWID/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/wFYAViA/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP8BWAFYgP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/AVgBWID/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/wFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"}"; + + await RedisAsync.SetValueAsync("UserLevel/1", val); + + var vals = await RedisAsync.GetValuesAsync(new List(new[] { "UserLevel/1" })); + + Assert.That(vals.Count, Is.EqualTo(1)); + Assert.That(vals[0], Is.EqualTo(val)); + } + + [Test] + public async Task Can_AcquireLock() + { + // guid here is to prevent competition between concurrent runtime tests + var key = PrefixedKey("AcquireLockKeyTimeOut:" + Guid.NewGuid()); + var lockKey = PrefixedKey("Can_AcquireLock_TimeOut:" + Guid.NewGuid()); + await RedisAsync.IncrementValueAsync(key); //1 + + Task[] tasks = new Task[5]; + for (int i = 0; i < tasks.Length; i++) + { + var snapsot = i; + tasks[snapsot] = Task.Run( + () => IncrementKeyInsideLock(key, lockKey, snapsot, new RedisClient(TestConfig.SingleHost) { NamespacePrefix = RedisRaw.NamespacePrefix }.ForAsyncOnly()) + ); + } + await Task.WhenAll(tasks); + + var val = await RedisAsync.GetAsync(key); + + Assert.That(val, Is.EqualTo(1 + 5)); + } + + private async Task IncrementKeyInsideLock(String key, String lockKey, int clientNo, IRedisClientAsync client) + { + await using (await client.AcquireLockAsync(lockKey)) + { + Debug.WriteLine(String.Format("client {0} acquired lock", clientNo)); + var val = await client.GetAsync(key); + + await Task.Delay(200); + + await client.SetAsync(key, val + 1); + Debug.WriteLine(String.Format("client {0} released lock", clientNo)); + } + } + + [Test] + public async Task Can_AcquireLock_TimeOut() + { + // guid here is to prevent competition between concurrent runtime tests + var key = PrefixedKey("AcquireLockKeyTimeOut:" + Guid.NewGuid()); + var lockKey = PrefixedKey("Can_AcquireLock_TimeOut:" + Guid.NewGuid()); + await RedisAsync.IncrementValueAsync(key); //1 + _ = await RedisAsync.AcquireLockAsync(lockKey); + var waitFor = TimeSpan.FromMilliseconds(1000); + var now = DateTime.Now; + + try + { + await using var client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await using (await client.AcquireLockAsync(lockKey, waitFor)) + { + await client.IncrementValueAsync(key); //2 + } + } + catch (TimeoutException) + { + var val = await RedisAsync.GetAsync(key); + Assert.That(val, Is.EqualTo(1)); + + var timeTaken = DateTime.Now - now; + Assert.That(timeTaken.TotalMilliseconds > waitFor.TotalMilliseconds, Is.True); + Assert.That(timeTaken.TotalMilliseconds < waitFor.TotalMilliseconds + 1000, Is.True); + return; + } + finally + { + await RedisAsync.RemoveAsync(key); + await RedisAsync.RemoveAsync(lockKey); + } + Assert.Fail("should have Timed out"); + } + + [Test] + public async Task Can_Append() + { + const string expectedString = "Hello, " + "World!"; + await RedisAsync.SetValueAsync("key", "Hello, "); + var currentLength = await RedisAsync.AppendToValueAsync("key", "World!"); + + Assert.That(currentLength, Is.EqualTo(expectedString.Length)); + + var val = await RedisAsync.GetValueAsync("key"); + Assert.That(val, Is.EqualTo(expectedString)); + } + + [Test] + public async Task Can_GetRange() + { + const string helloWorld = "Hello, World!"; + await RedisAsync.SetValueAsync("key", helloWorld); + + var fromIndex = "Hello, ".Length; + var toIndex = "Hello, World".Length - 1; + + var expectedString = helloWorld.Substring(fromIndex, toIndex - fromIndex + 1); + var world = await NativeAsync.GetRangeAsync("key", fromIndex, toIndex); + + Assert.That(world.Length, Is.EqualTo(expectedString.Length)); + } + + [Test] + public async Task Can_create_distributed_lock() + { + var key = "lockkey"; + int lockTimeout = 2; + + var distributedLock = new DistributedLock().AsAsync(); + + var state = await distributedLock.LockAsync(key, lockTimeout, lockTimeout, RedisAsync); + Assert.AreEqual(state.Result, DistributedLock.LOCK_ACQUIRED); + + //can't re-lock + distributedLock = new DistributedLock(); + state = await distributedLock.LockAsync(key, lockTimeout, lockTimeout, RedisAsync); + Assert.AreEqual(state.Result, DistributedLock.LOCK_NOT_ACQUIRED); + + // re-acquire lock after timeout + await Task.Delay(lockTimeout * 1000 + 1000); + distributedLock = new DistributedLock(); + state = await distributedLock.LockAsync(key, lockTimeout, lockTimeout, RedisAsync); + + (var result, var expire) = state; // test decomposition since we are here + Assert.AreEqual(result, DistributedLock.LOCK_RECOVERED); + + Assert.IsTrue(await distributedLock.UnlockAsync(key, expire, RedisAsync)); + + //can now lock + distributedLock = new DistributedLock(); + state = await distributedLock.LockAsync(key, lockTimeout, lockTimeout, RedisAsync); + Assert.AreEqual(state.Result, DistributedLock.LOCK_ACQUIRED); + + //cleanup + Assert.IsTrue(await distributedLock.UnlockAsync(key, state.Expiration, RedisAsync)); + } + + public class MyPoco + { + public int Id { get; set; } + public string Name { get; set; } + } + + [Test] + public async Task Can_StoreObject() + { + object poco = new MyPoco { Id = 1, Name = "Test" }; + + await RedisAsync.StoreObjectAsync(poco); + + Assert.That(await RedisAsync.GetValueAsync(RedisRaw.NamespacePrefix + "urn:mypoco:1"), Is.EqualTo("{\"Id\":1,\"Name\":\"Test\"}")); + + Assert.That(await RedisAsync.PopItemFromSetAsync(RedisRaw.NamespacePrefix + "ids:MyPoco"), Is.EqualTo("1")); + } + + [Test] + public async Task Can_store_multiple_keys() + { + var keys = 5.Times(x => "key" + x); + var vals = 5.Times(x => "val" + x); + + using var redis = RedisClient.New(); + await RedisAsync.SetAllAsync(keys, vals); + + var all = await RedisAsync.GetValuesAsync(keys); + Assert.AreEqual(vals, all); + } + + [Test] + public async Task Can_store_Dictionary() + { + var keys = 5.Times(x => "key" + x); + var vals = 5.Times(x => "val" + x); + var map = new Dictionary(); + keys.ForEach(x => map[x] = "val" + x); + + await using var client = RedisClient.New().ForAsyncOnly(); + await client.SetAllAsync(map); + + var all = await client.GetValuesMapAsync(keys); + Assert.AreEqual(map, all); + } + + [Test] + public async Task Can_store_Dictionary_as_objects() + { + var map = new Dictionary + { + ["key_a"] = "123", + ["key_b"] = null + }; + + await using var client = RedisClient.New().ForAsyncOnly(); + await client.SetAllAsync(map); + + Assert.That(await client.GetAsync("key_a"), Is.EqualTo("123")); + Assert.That(await client.GetValueAsync("key_b"), Is.EqualTo("")); + } + + + [Test] + public async Task Can_store_Dictionary_as_bytes() + { + var map = new Dictionary + { + ["key_a"] = "123".ToUtf8Bytes(), + ["key_b"] = null + }; + + await using var client = RedisClient.New().ForAsyncOnly(); + await client.SetAllAsync(map); + + Assert.That(await client.GetAsync("key_a"), Is.EqualTo("123")); + Assert.That(await client.GetValueAsync("key_b"), Is.EqualTo("")); + } + + [Test] + public async Task Should_reset_slowlog() + { + await using var client = RedisClient.New().ForAsyncOnly(); + await client.SlowlogResetAsync(); + } + + [Test] + public async Task Can_get_slowlog() + { + await using var client = RedisClient.New().ForAsyncOnly(); + var log = await client.GetSlowlogAsync(10); + + foreach (var t in log) + { + Console.WriteLine(t.Id); + Console.WriteLine(t.Duration); + Console.WriteLine(t.Timestamp); + Console.WriteLine(string.Join(":", t.Arguments)); + } + } + + + [Test] + public async Task Can_change_db_at_runtime() + { + await using var redis = new RedisClient(TestConfig.SingleHost, TestConfig.RedisPort, db: 1).ForAsyncOnly(); + var val = Environment.TickCount; + var key = "test" + val; + try + { + await redis.SetAsync(key, val); + await redis.SelectAsync(2); + Assert.That(await redis.GetAsync(key), Is.EqualTo(0)); + await redis.SelectAsync(1); + Assert.That(await redis.GetAsync(key), Is.EqualTo(val)); + await redis.DisposeAsync(); + } + finally + { + await redis.SelectAsync(1); + await redis.RemoveAsync(key); + } + } + + [Test] + public async Task Can_Set_Expire_Seconds() + { + await RedisAsync.SetValueAsync("key", "val", expireIn: TimeSpan.FromSeconds(1)); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.True); + await Task.Delay(2000); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.False); + } + + [Test] + public async Task Can_Set_Expire_MilliSeconds() + { + await RedisAsync.SetValueAsync("key", "val", expireIn: TimeSpan.FromMilliseconds(1000)); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.True); + await Task.Delay(2000); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.False); + } + + [Test] + public async Task Can_Set_Expire_Seconds_if_exists() + { + Assert.That(await RedisAsync.SetValueIfExistsAsync("key", "val", expireIn: TimeSpan.FromMilliseconds(1500)), + Is.False); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.False); + + await RedisAsync.SetValueAsync("key", "val"); + Assert.That(await RedisAsync.SetValueIfExistsAsync("key", "val", expireIn: TimeSpan.FromMilliseconds(1000)), + Is.True); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.True); + + await Task.Delay(2000); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.False); + } + + [Test] + public async Task Can_Set_Expire_Seconds_if_not_exists() + { + Assert.That(await RedisAsync.SetValueIfNotExistsAsync("key", "val", expireIn: TimeSpan.FromMilliseconds(1000)), + Is.True); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.True); + + Assert.That(await RedisAsync.SetValueIfNotExistsAsync("key", "val", expireIn: TimeSpan.FromMilliseconds(1000)), + Is.False); + + await Task.Delay(2000); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.False); + + await RedisAsync.RemoveAsync("key"); + await RedisAsync.SetValueIfNotExistsAsync("key", "val", expireIn: TimeSpan.FromMilliseconds(1000)); + Assert.That(await RedisAsync.ContainsKeyAsync("key"), Is.True); + } + } + +} diff --git a/tests/ServiceStack.Redis.Tests/RedisClientTests.cs b/tests/ServiceStack.Redis.Tests/RedisClientTests.cs index d8f04fc2..19bfbfe5 100644 --- a/tests/ServiceStack.Redis.Tests/RedisClientTests.cs +++ b/tests/ServiceStack.Redis.Tests/RedisClientTests.cs @@ -256,7 +256,8 @@ public void Can_Save() // if exception has that message then it still proves that BgSave works as expected. if (e.Message.StartsWith("Can't BGSAVE while AOF log rewriting is in progress") || e.Message.StartsWith("An AOF log rewriting in progress: can't BGSAVE right now") - || e.Message.StartsWith("Background save already in progress")) + || e.Message.StartsWith("Background save already in progress") + || e.Message.StartsWith("Another child process is active (AOF?): can't BGSAVE right now")) return; throw; @@ -275,7 +276,8 @@ public void Can_BgSave() // if exception has that message then it still proves that BgSave works as expected. if (e.Message.StartsWith("Can't BGSAVE while AOF log rewriting is in progress") || e.Message.StartsWith("An AOF log rewriting in progress: can't BGSAVE right now") - || e.Message.StartsWith("Background save already in progress")) + || e.Message.StartsWith("Background save already in progress") + || e.Message.StartsWith("Another child process is active (AOF?): can't BGSAVE right now")) return; throw; @@ -351,8 +353,9 @@ public void Can_GetValues_JSON_strings() [Test] public void Can_AcquireLock() { - var key = PrefixedKey("AcquireLockKey"); - var lockKey = PrefixedKey("Can_AcquireLock"); + // guid here is to prevent competition between concurrent runtime tests + var key = PrefixedKey("AcquireLockKeyTimeOut:" + Guid.NewGuid()); + var lockKey = PrefixedKey("Can_AcquireLock_TimeOut:" + Guid.NewGuid()); Redis.IncrementValue(key); //1 var asyncResults = 5.TimesAsync(i => @@ -382,8 +385,9 @@ private void IncrementKeyInsideLock(String key, String lockKey, int clientNo, IR [Test] public void Can_AcquireLock_TimeOut() { - var key = PrefixedKey("AcquireLockKeyTimeOut"); - var lockKey = PrefixedKey("Can_AcquireLock_TimeOut"); + // guid here is to prevent competition between concurrent runtime tests + var key = PrefixedKey("AcquireLockKeyTimeOut:" + Guid.NewGuid()); + var lockKey = PrefixedKey("Can_AcquireLock_TimeOut:" + Guid.NewGuid()); Redis.IncrementValue(key); //1 var acquiredLock = Redis.AcquireLock(lockKey); var waitFor = TimeSpan.FromMilliseconds(1000); @@ -409,6 +413,11 @@ public void Can_AcquireLock_TimeOut() Assert.That(timeTaken.TotalMilliseconds < waitFor.TotalMilliseconds + 1000, Is.True); return; } + finally + { + Redis.Remove(key); + Redis.Remove(lockKey); + } Assert.Fail("should have Timed out"); } @@ -564,7 +573,7 @@ public void Should_reset_slowlog() } [Test] - public void Can_get_showlog() + public void Can_get_slowlog() { using (var redis = RedisClient.New()) { diff --git a/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs new file mode 100644 index 00000000..d7c6244e --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs @@ -0,0 +1,126 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + public static class AsyncExtensions + { + public static async ValueTask ForEachAsync(this List source, Func action) + { + foreach (var item in source) + await action(item).ConfigureAwait(false); + } + public static async ValueTask ForEachAsync(this Dictionary source, Func action) + { + foreach (var item in source) + await action(item.Key, item.Value).ConfigureAwait(false); + } + public static async ValueTask TimesAsync(this int times, Func action) + { + for (int i = 0; i < times; i++) + { + await action(i).ConfigureAwait(false); + } + } + public static async ValueTask> ToListAsync(this IAsyncEnumerable source) + { + var list = new List(); + await foreach (var item in source.ConfigureAwait(false)) + list.Add(item); + return list; + } + + public static async ValueTask CountAsync(this IAsyncEnumerable source) + { + int count = 0; + await foreach (var item in source.ConfigureAwait(false)) + count++; + return count; + } + + public static IRedisClientAsync ForAsyncOnly(this RedisClient client) + { +#if DEBUG + if (client is object) client.DebugAllowSync = false; +#endif + return client; + } + + public static async IAsyncEnumerable TakeAsync(this IAsyncEnumerable source, int count) + { + await foreach (var item in source.ConfigureAwait(false)) + { + if (count > 0) + { + count--; + yield return item; + } + } + } + + public static async ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func valueSelector) + { + var result = new Dictionary(); + await foreach (var item in source.ConfigureAwait(false)) + { + result.Add(keySelector(item), valueSelector(item)); + } + return result; + } + } + public class RedisClientTestsBaseAsyncTests // testing the base class features + : RedisClientTestsBaseAsync + { + [Test] + public void DetectUnexpectedSync() + { + #if DEBUG + Assert.False(RedisRaw.DebugAllowSync, nameof(RedisRaw.DebugAllowSync)); + var ex = Assert.Throws(() => RedisRaw.Ping()); + Assert.AreEqual("Unexpected synchronous operation detected from 'SendReceive'", ex.Message); + #endif + } + } + + [Category("Async")] + public abstract class RedisClientTestsBaseAsync : RedisClientTestsBase + { + protected IRedisClientAsync RedisAsync => base.Redis; + protected IRedisNativeClientAsync NativeAsync => base.Redis; + + [Obsolete("This should use RedisAsync or RedisRaw", true)] + protected new RedisClient Redis => base.Redis; + + protected RedisClient RedisRaw + { + get => base.Redis; + set => base.Redis = value; + } + + public override void OnBeforeEachTest() + { + base.OnBeforeEachTest(); + _ = RedisRaw.ForAsyncOnly(); + } + public override void OnAfterEachTest() + { +#if DEBUG + if(RedisRaw is object) RedisRaw.DebugAllowSync = true; +#endif + base.OnAfterEachTest(); + } + + protected static async ValueTask> ToListAsync(IAsyncEnumerable source, CancellationToken cancellationToken = default) + { + var list = new List(); + await foreach (var value in source.ConfigureAwait(false).WithCancellation(cancellationToken)) + { + list.Add(value); + } + return list; + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.cs b/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.cs index 40386c28..087c1c4f 100644 --- a/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.cs +++ b/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.cs @@ -1,8 +1,6 @@ -using System; +using NUnit.Framework; using System.Diagnostics; using System.Text; -using NUnit.Framework; -using ServiceStack.Text; namespace ServiceStack.Redis.Tests { diff --git a/tests/ServiceStack.Redis.Tests/RedisGeoNativeClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisGeoNativeClientTests.Async.cs new file mode 100644 index 00000000..c6f267c3 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisGeoNativeClientTests.Async.cs @@ -0,0 +1,286 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Async")] + [Ignore("CI requires redis-server v3.2.0")] + public class RedisGeoNativeClientTestsAsync + { + private readonly IRedisNativeClientAsync redis; + + public RedisGeoNativeClientTestsAsync() + { + redis = new RedisNativeClient(TestConfig.GeoHost); + } + + [OneTimeTearDown] + public async Task OneTimeTearDown() + { + await redis.DisposeAsync(); + } + + [Test] + public async Task Can_GeoAdd_and_GeoPos() + { + await redis.FlushDbAsync(); + var count = await redis.GeoAddAsync("Sicily", 13.361389, 38.115556, "Palermo"); + Assert.That(count, Is.EqualTo(1)); + var results = await redis.GeoPosAsync("Sicily", new[] { "Palermo" }); + + Assert.That(results.Count, Is.EqualTo(1)); + Assert.That(results[0].Longitude, Is.EqualTo(13.361389).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(38.115556).Within(.1)); + Assert.That(results[0].Member, Is.EqualTo("Palermo")); + } + + [Test] + public async Task GeoPos_on_NonExistingMember_returns_no_results() + { + await redis.FlushDbAsync(); + var count = await redis.GeoAddAsync("Sicily", 13.361389, 38.115556, "Palermo"); + var results = await redis.GeoPosAsync("Sicily", new[] { "NonExistingMember" }); + Assert.That(results.Count, Is.EqualTo(0)); + + results = await redis.GeoPosAsync("Sicily", new[] { "Palermo", "NonExistingMember" }); + Assert.That(results.Count, Is.EqualTo(1)); + } + + [Test] + public async Task Can_GeoAdd_and_GeoPos_multiple() + { + await redis.FlushDbAsync(); + var count = await redis.GeoAddAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + Assert.That(count, Is.EqualTo(2)); + + var results = await redis.GeoPosAsync("Sicily", new[] { "Palermo", "Catania" }); + + Assert.That(results.Count, Is.EqualTo(2)); + Assert.That(results[0].Longitude, Is.EqualTo(13.361389).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(38.115556).Within(.1)); + Assert.That(results[0].Member, Is.EqualTo("Palermo")); + + Assert.That(results[1].Longitude, Is.EqualTo(15.087269).Within(.1)); + Assert.That(results[1].Latitude, Is.EqualTo(37.502669).Within(.1)); + Assert.That(results[1].Member, Is.EqualTo("Catania")); + } + + [Test] + public async Task Can_GeoDist() + { + await redis.FlushDbAsync(); + await redis.GeoAddAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var distance = await redis.GeoDistAsync("Sicily", "Palermo", "Catania"); + Assert.That(distance, Is.EqualTo(166274.15156960039).Within(.1)); + } + + [Test] + public async Task GeoDist_on_NonExistingMember_returns_NaN() + { + await redis.FlushDbAsync(); + await redis.GeoAddAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var distance = await redis.GeoDistAsync("Sicily", "Palermo", "NonExistingMember"); + Assert.That(distance, Is.EqualTo(double.NaN)); + } + + [Test] + public async Task Can_GeoHash() + { + await redis.FlushDbAsync(); + await redis.GeoAddAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var hashes = await redis.GeoHashAsync("Sicily", new[] { "Palermo", "Catania" }); + Assert.That(hashes[0], Is.EqualTo("sqc8b49rny0")); + Assert.That(hashes[1], Is.EqualTo("sqdtr74hyu0")); + + hashes = await redis.GeoHashAsync("Sicily", new[] { "Palermo", "NonExistingMember", "Catania" }); + Assert.That(hashes[0], Is.EqualTo("sqc8b49rny0")); + Assert.That(hashes[1], Is.Null); + Assert.That(hashes[2], Is.EqualTo("sqdtr74hyu0")); + } + + [Test] + public async Task Can_GeoRadius_default() + { + await redis.FlushDbAsync(); + await redis.GeoAddAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var results = await redis.GeoRadiusAsync("Sicily", 15, 37, 200, RedisGeoUnit.Kilometers); + + Assert.That(results.Count, Is.EqualTo(2)); + Assert.That(results[0].Member, Is.EqualTo("Palermo")); + Assert.That(results[0].Unit, Is.Null); + Assert.That(results[1].Member, Is.EqualTo("Catania")); + Assert.That(results[1].Unit, Is.Null); + } + + [Test] + public async Task Can_GeoRadiusByMember_default() + { + await redis.FlushDbAsync(); + await redis.GeoAddAsync("Sicily", new[] { + new RedisGeo(13.583333, 37.316667, "Agrigento"), + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var results = await redis.GeoRadiusByMemberAsync("Sicily", "Agrigento", 100, RedisGeoUnit.Kilometers); + + Assert.That(results.Count, Is.EqualTo(2)); + Assert.That(results[0].Member, Is.EqualTo("Agrigento")); + Assert.That(results[0].Unit, Is.Null); + Assert.That(results[1].Member, Is.EqualTo("Palermo")); + Assert.That(results[1].Unit, Is.Null); + } + + [Test] + public async Task Can_GeoRadius_WithCoord() + { + await redis.FlushDbAsync(); + await redis.GeoAddAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var results = await redis.GeoRadiusAsync("Sicily", 15, 37, 200, RedisGeoUnit.Kilometers, withCoords: true); + + Assert.That(results.Count, Is.EqualTo(2)); + Assert.That(results[0].Member, Is.EqualTo("Palermo")); + Assert.That(results[0].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[0].Longitude, Is.EqualTo(13.361389).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(38.115556).Within(.1)); + + Assert.That(results[1].Member, Is.EqualTo("Catania")); + Assert.That(results[1].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[1].Longitude, Is.EqualTo(15.087269).Within(.1)); + Assert.That(results[1].Latitude, Is.EqualTo(37.502669).Within(.1)); + } + + [Test] + public async Task Can_GeoRadius_WithDist() + { + await redis.FlushDbAsync(); + await redis.GeoAddAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var results = await redis.GeoRadiusAsync("Sicily", 15, 37, 200, RedisGeoUnit.Kilometers, withDist: true); + + Assert.That(results.Count, Is.EqualTo(2)); + Assert.That(results[0].Member, Is.EqualTo("Palermo")); + Assert.That(results[0].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[0].Distance, Is.EqualTo(190.4424).Within(.1)); + + Assert.That(results[1].Member, Is.EqualTo("Catania")); + Assert.That(results[1].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[1].Distance, Is.EqualTo(56.4413).Within(.1)); + } + + [Test] + public async Task Can_GeoRadius_WithCoord_WithDist_WithHash() + { + await redis.FlushDbAsync(); + await redis.GeoAddAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var results = await redis.GeoRadiusAsync("Sicily", 15, 37, 200, RedisGeoUnit.Kilometers, + withCoords: true, withDist: true, withHash: true); + + Assert.That(results.Count, Is.EqualTo(2)); + Assert.That(results[0].Member, Is.EqualTo("Palermo")); + Assert.That(results[0].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[0].Longitude, Is.EqualTo(13.361389).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(38.115556).Within(.1)); + Assert.That(results[0].Distance, Is.EqualTo(190.4424).Within(.1)); + Assert.That(results[0].Hash, Is.EqualTo(3479099956230698)); + + Assert.That(results[1].Member, Is.EqualTo("Catania")); + Assert.That(results[1].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[1].Longitude, Is.EqualTo(15.087269).Within(.1)); + Assert.That(results[1].Latitude, Is.EqualTo(37.502669).Within(.1)); + Assert.That(results[1].Distance, Is.EqualTo(56.4413).Within(.1)); + Assert.That(results[1].Hash, Is.EqualTo(3479447370796909)); + } + + [Test] + public async Task Can_GeoRadiusByMember_WithCoord_WithDist_WithHash() + { + await redis.FlushDbAsync(); + await redis.GeoAddAsync("Sicily", new[] { + new RedisGeo(13.583333, 37.316667, "Agrigento"), + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var results = await redis.GeoRadiusByMemberAsync("Sicily", "Agrigento", 100, RedisGeoUnit.Kilometers, + withCoords: true, withDist: true, withHash: true); + + Assert.That(results.Count, Is.EqualTo(2)); + Assert.That(results[0].Member, Is.EqualTo("Agrigento")); + Assert.That(results[0].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[0].Longitude, Is.EqualTo(13.583333).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(37.316667).Within(.1)); + Assert.That(results[0].Distance, Is.EqualTo(0)); + Assert.That(results[0].Hash, Is.EqualTo(3479030013248308)); + + Assert.That(results[1].Member, Is.EqualTo("Palermo")); + Assert.That(results[1].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[1].Longitude, Is.EqualTo(13.361389).Within(.1)); + Assert.That(results[1].Latitude, Is.EqualTo(38.115556).Within(.1)); + Assert.That(results[1].Distance, Is.EqualTo(90.9778).Within(.1)); + Assert.That(results[1].Hash, Is.EqualTo(3479099956230698)); + } + + [Test] + public async Task Can_GeoRadius_WithCoord_WithDist_WithHash_Count_and_Asc() + { + await redis.FlushDbAsync(); + await redis.GeoAddAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var results = await redis.GeoRadiusAsync("Sicily", 15, 37, 200, RedisGeoUnit.Kilometers, + withCoords: true, withDist: true, withHash: true, count:1, asc:false); + + Assert.That(results.Count, Is.EqualTo(1)); + Assert.That(results[0].Member, Is.EqualTo("Palermo")); + Assert.That(results[0].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[0].Longitude, Is.EqualTo(13.361389).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(38.115556).Within(.1)); + Assert.That(results[0].Distance, Is.EqualTo(190.4424).Within(.1)); + Assert.That(results[0].Hash, Is.EqualTo(3479099956230698)); + + results = await redis.GeoRadiusAsync("Sicily", 15, 37, 200, RedisGeoUnit.Kilometers, + withCoords: true, withDist: true, withHash: true, count: 1, asc: true); + + Assert.That(results.Count, Is.EqualTo(1)); + Assert.That(results[0].Member, Is.EqualTo("Catania")); + Assert.That(results[0].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[0].Longitude, Is.EqualTo(15.087269).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(37.502669).Within(.1)); + Assert.That(results[0].Distance, Is.EqualTo(56.4413).Within(.1)); + Assert.That(results[0].Hash, Is.EqualTo(3479447370796909)); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisGeoTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisGeoTests.Async.cs new file mode 100644 index 00000000..1d26818c --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisGeoTests.Async.cs @@ -0,0 +1,239 @@ +using NUnit.Framework; +using ServiceStack.Text; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Async")] + [Ignore("CI requires redis-server v3.2.0")] + public class RedisGeoTestsAsync + { + private readonly IRedisClientAsync redis; + + public RedisGeoTestsAsync() + { + redis = new RedisClient(TestConfig.GeoHost); + } + + [OneTimeTearDown] + public async Task OneTimeTearDown() + { + await redis.DisposeAsync(); + } + + [Test] + public async Task Can_AddGeoMember_and_GetGeoCoordinates() + { + await redis.FlushDbAsync(); + var count = await redis.AddGeoMemberAsync("Sicily", 13.361389, 38.115556, "Palermo"); + Assert.That(count, Is.EqualTo(1)); + var results = await redis.GetGeoCoordinatesAsync("Sicily", new[] { "Palermo" }); + + Assert.That(results.Count, Is.EqualTo(1)); + Assert.That(results[0].Longitude, Is.EqualTo(13.361389).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(38.115556).Within(.1)); + Assert.That(results[0].Member, Is.EqualTo("Palermo")); + } + + [Test] + public async Task GetGeoCoordinates_on_NonExistingMember_returns_no_results() + { + await redis.FlushDbAsync(); + var count = await redis.AddGeoMemberAsync("Sicily", 13.361389, 38.115556, "Palermo"); + var results = await redis.GetGeoCoordinatesAsync("Sicily", new[] { "NonExistingMember" }); + Assert.That(results.Count, Is.EqualTo(0)); + + results = await redis.GetGeoCoordinatesAsync("Sicily", new[] { "Palermo", "NonExistingMember" }); + Assert.That(results.Count, Is.EqualTo(1)); + } + + [Test] + public async Task Can_AddGeoMembers_and_GetGeoCoordinates_multiple() + { + await redis.FlushDbAsync(); + var count = await redis.AddGeoMembersAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + Assert.That(count, Is.EqualTo(2)); + + var results = await redis.GetGeoCoordinatesAsync("Sicily", new[] { "Palermo", "Catania" }); + + Assert.That(results.Count, Is.EqualTo(2)); + Assert.That(results[0].Longitude, Is.EqualTo(13.361389).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(38.115556).Within(.1)); + Assert.That(results[0].Member, Is.EqualTo("Palermo")); + + Assert.That(results[1].Longitude, Is.EqualTo(15.087269).Within(.1)); + Assert.That(results[1].Latitude, Is.EqualTo(37.502669).Within(.1)); + Assert.That(results[1].Member, Is.EqualTo("Catania")); + } + + [Test] + public async Task Can_CalculateDistanceBetweenGeoMembers() + { + await redis.FlushDbAsync(); + await redis.AddGeoMembersAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var distance = await redis.CalculateDistanceBetweenGeoMembersAsync("Sicily", "Palermo", "Catania"); + Assert.That(distance, Is.EqualTo(166274.15156960039).Within(.1)); + } + + [Test] + public async Task CalculateDistanceBetweenGeoMembers_on_NonExistingMember_returns_NaN() + { + await redis.FlushDbAsync(); + await redis.AddGeoMembersAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var distance = await redis.CalculateDistanceBetweenGeoMembersAsync("Sicily", "Palermo", "NonExistingMember"); + Assert.That(distance, Is.EqualTo(double.NaN)); + } + + [Test] + public async Task Can_GetGeohashes() + { + await redis.FlushDbAsync(); + await redis.AddGeoMembersAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var hashes = await redis.GetGeohashesAsync("Sicily", new[] { "Palermo", "Catania" }); + Assert.That(hashes[0], Is.EqualTo("sqc8b49rny0")); + Assert.That(hashes[1], Is.EqualTo("sqdtr74hyu0")); + + hashes = await redis.GetGeohashesAsync("Sicily", new[] { "Palermo", "NonExistingMember", "Catania" }); + Assert.That(hashes[0], Is.EqualTo("sqc8b49rny0")); + Assert.That(hashes[1], Is.Null); + Assert.That(hashes[2], Is.EqualTo("sqdtr74hyu0")); + } + + [Test] + public async Task Can_FindGeoMembersInRadius() + { + await redis.FlushDbAsync(); + await redis.AddGeoMembersAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var results = await redis.FindGeoMembersInRadiusAsync("Sicily", 15, 37, 200, RedisGeoUnit.Kilometers); + + Assert.That(results.Length, Is.EqualTo(2)); + Assert.That(results[0], Is.EqualTo("Palermo")); + Assert.That(results[1], Is.EqualTo("Catania")); + } + + //[Test] // method does not exist on IRedisClient/IRedisClientAsync + //public async Task Can_GeoRadiusByMember() + //{ + // await redis.FlushDbAsync(); + // await redis.AddGeoMembersAsync("Sicily", new[] { + // new RedisGeo(13.583333, 37.316667, "Agrigento"), + // new RedisGeo(13.361389, 38.115556, "Palermo"), + // new RedisGeo(15.087269, 37.502669, "Catania") + // }); + + // var results = await redis.GeoRadiusByMemberAsync("Sicily", "Agrigento", 100, RedisGeoUnit.Kilometers); + + // Assert.That(results.Count, Is.EqualTo(2)); + // Assert.That(results[0].Member, Is.EqualTo("Agrigento")); + // Assert.That(results[0].Unit, Is.Null); + // Assert.That(results[1].Member, Is.EqualTo("Palermo")); + // Assert.That(results[1].Unit, Is.Null); + //} + + [Test] + public async Task Can_FindGeoResultsInRadius() + { + await redis.FlushDbAsync(); + await redis.AddGeoMembersAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var results = await redis.FindGeoResultsInRadiusAsync("Sicily", 15, 37, 200, RedisGeoUnit.Kilometers); + + Assert.That(results.Count, Is.EqualTo(2)); + Assert.That(results[0].Member, Is.EqualTo("Palermo")); + Assert.That(results[0].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[0].Longitude, Is.EqualTo(13.361389).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(38.115556).Within(.1)); + Assert.That(results[0].Distance, Is.EqualTo(190.4424).Within(.1)); + Assert.That(results[0].Hash, Is.EqualTo(3479099956230698)); + + Assert.That(results[1].Member, Is.EqualTo("Catania")); + Assert.That(results[1].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[1].Longitude, Is.EqualTo(15.087269).Within(.1)); + Assert.That(results[1].Latitude, Is.EqualTo(37.502669).Within(.1)); + Assert.That(results[1].Distance, Is.EqualTo(56.4413).Within(.1)); + Assert.That(results[1].Hash, Is.EqualTo(3479447370796909)); + } + + [Test] + public async Task Can_FindGeoResultsInRadius_by_Member() + { + await redis.FlushDbAsync(); + await redis.AddGeoMembersAsync("Sicily", new[] { + new RedisGeo(13.583333, 37.316667, "Agrigento"), + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var results = await redis.FindGeoResultsInRadiusAsync("Sicily", "Agrigento", 100, RedisGeoUnit.Kilometers); + + Assert.That(results.Count, Is.EqualTo(2)); + Assert.That(results[0].Member, Is.EqualTo("Agrigento")); + Assert.That(results[0].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[0].Longitude, Is.EqualTo(13.583333).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(37.316667).Within(.1)); + Assert.That(results[0].Distance, Is.EqualTo(0)); + Assert.That(results[0].Hash, Is.EqualTo(3479030013248308)); + + Assert.That(results[1].Member, Is.EqualTo("Palermo")); + Assert.That(results[1].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[1].Longitude, Is.EqualTo(13.361389).Within(.1)); + Assert.That(results[1].Latitude, Is.EqualTo(38.115556).Within(.1)); + Assert.That(results[1].Distance, Is.EqualTo(90.9778).Within(.1)); + Assert.That(results[1].Hash, Is.EqualTo(3479099956230698)); + } + + [Test] + public async Task Can_GeoRadius_WithCoord_WithDist_WithHash_Count_and_Asc() + { + await redis.FlushDbAsync(); + await redis.AddGeoMembersAsync("Sicily", new[] { + new RedisGeo(13.361389, 38.115556, "Palermo"), + new RedisGeo(15.087269, 37.502669, "Catania") + }); + + var results = await redis.FindGeoResultsInRadiusAsync("Sicily", 15, 37, 200, RedisGeoUnit.Kilometers, + count: 1, sortByNearest: false); + + Assert.That(results.Count, Is.EqualTo(1)); + Assert.That(results[0].Member, Is.EqualTo("Palermo")); + Assert.That(results[0].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[0].Longitude, Is.EqualTo(13.361389).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(38.115556).Within(.1)); + Assert.That(results[0].Distance, Is.EqualTo(190.4424).Within(.1)); + Assert.That(results[0].Hash, Is.EqualTo(3479099956230698)); + + results = await redis.FindGeoResultsInRadiusAsync("Sicily", 15, 37, 200, RedisGeoUnit.Kilometers, + count: 1, sortByNearest: true); + + Assert.That(results.Count, Is.EqualTo(1)); + Assert.That(results[0].Member, Is.EqualTo("Catania")); + Assert.That(results[0].Unit, Is.EqualTo(RedisGeoUnit.Kilometers)); + Assert.That(results[0].Longitude, Is.EqualTo(15.087269).Within(.1)); + Assert.That(results[0].Latitude, Is.EqualTo(37.502669).Within(.1)); + Assert.That(results[0].Distance, Is.EqualTo(56.4413).Within(.1)); + Assert.That(results[0].Hash, Is.EqualTo(3479447370796909)); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisHyperLogTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisHyperLogTests.Async.cs new file mode 100644 index 00000000..c29bb227 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisHyperLogTests.Async.cs @@ -0,0 +1,35 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Ignore("Integration"), Category("Async")] + public class RedisHyperLogTestsAsync + { + const string Host = "localhost"; // "10.0.0.14" + private IRedisClientAsync Connect() => new RedisClient(Host); + + [Test] + public async Task Can_Add_to_Hyperlog() + { + await using var redis = Connect(); + + await redis.FlushAllAsync(); + + await redis.AddToHyperLogAsync("hyperlog", new[] { "a", "b", "c" }); + await redis.AddToHyperLogAsync("hyperlog", new[] { "c", "d" }); + + var count = await redis.CountHyperLogAsync("hyperlog"); + + Assert.That(count, Is.EqualTo(4)); + + await redis.AddToHyperLogAsync("hyperlog2", new[] { "c", "d", "e", "f" }); + + await redis.MergeHyperLogsAsync("hypermerge", new[] { "hyperlog", "hyperlog2" }); + + var mergeCount = await redis.CountHyperLogAsync("hypermerge"); + + Assert.That(mergeCount, Is.EqualTo(6)); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisPersistenceProviderTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisPersistenceProviderTests.Async.cs new file mode 100644 index 00000000..0a20c8ec --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisPersistenceProviderTests.Async.cs @@ -0,0 +1,69 @@ +using NUnit.Framework; +using ServiceStack.Common.Tests.Models; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Integration"), Category("Async")] + public class RedisPersistenceProviderTestsAsync + { + [Test] + public async Task Can_Store_and_GetById_ModelWithIdAndName() + { + await using (IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost)) + { + const int modelId = 1; + var to = ModelWithIdAndName.Create(modelId); + await redis.StoreAsync(to); + + var from = await redis.GetByIdAsync(modelId); + + ModelWithIdAndName.AssertIsEqual(to, from); + } + } + + [Test] + public async Task Can_StoreAll_and_GetByIds_ModelWithIdAndName() + { + await using (IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost)) + { + var ids = new[] { 1, 2, 3, 4, 5 }; + var tos = ids.Map(ModelWithIdAndName.Create); + + await redis.StoreAllAsync(tos); + + var froms = await redis.GetByIdsAsync(ids); + var fromIds = froms.Map(x => x.Id); + + Assert.That(fromIds, Is.EquivalentTo(ids)); + } + } + + [Test] + public async Task Can_Delete_ModelWithIdAndName() + { + await using (IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost)) + { + var ids = new List { 1, 2, 3, 4, 5 }; + var tos = ids.ConvertAll(ModelWithIdAndName.Create); + + await redis.StoreAllAsync(tos); + + var deleteIds = new List { 2, 4 }; + + await redis.DeleteByIdsAsync(deleteIds); + + var froms = await redis.GetByIdsAsync(ids); + var fromIds = froms.Map(x => x.Id); + + var expectedIds = ids.Where(x => !deleteIds.Contains(x)).ToList(); + + Assert.That(fromIds, Is.EquivalentTo(expectedIds)); + } + } + + } + +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisPipelineCommonTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisPipelineCommonTests.Async.cs new file mode 100644 index 00000000..4c0a707a --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisPipelineCommonTests.Async.cs @@ -0,0 +1,73 @@ +using NUnit.Framework; +using ServiceStack.Text; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class RedisPipelineCommonTestsAsync + : RedisClientTestsBaseAsync + { + [Test] + public async Task Can_Set_and_Expire_key_in_atomic_transaction() + { + var oneSec = TimeSpan.FromSeconds(1); + + Assert.That(await RedisAsync.GetValueAsync("key"), Is.Null); + await using (var trans = RedisAsync.CreatePipeline()) //Calls 'MULTI' + { + trans.QueueCommand(r => r.SetValueAsync("key", "a")); //Queues 'SET key a' + trans.QueueCommand(r => r.ExpireEntryInAsync("key", oneSec)); //Queues 'EXPIRE key 1' + + await trans.FlushAsync(); //Calls 'EXEC' + + } //Calls 'DISCARD' if 'EXEC' wasn't called + + Assert.That(await RedisAsync.GetValueAsync("key"), Is.EqualTo("a")); + await Task.Delay(TimeSpan.FromSeconds(2)); + Assert.That(await RedisAsync.GetValueAsync("key"), Is.Null); + } + + [Test] + public async Task Can_SetAll_and_Publish_in_atomic_transaction() + { + var messages = new Dictionary { { "a", "a" }, { "b", "b" } }; + await using var pipeline = RedisAsync.CreatePipeline(); + pipeline.QueueCommand(c => c.SetAllAsync(messages.ToDictionary(t => t.Key, t => t.Value))); + pipeline.QueueCommand(c => c.PublishMessageAsync("uc", "b")); + + await pipeline.FlushAsync(); + } + + [Test] + public async Task Can_Pop_priority_message_from_SortedSet_and_Add_to_workq_in_atomic_transaction() + { + var messages = new List { "message4", "message3", "message2" }; + + await RedisAsync.AddItemToListAsync("workq", "message1"); + + var priority = 1; + await messages.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync("prioritymsgs", x, priority++)); + + var highestPriorityMessage = await RedisAsync.PopItemWithHighestScoreFromSortedSetAsync("prioritymsgs"); + + await using (var trans = RedisAsync.CreatePipeline()) + { + trans.QueueCommand(r => r.RemoveItemFromSortedSetAsync("prioritymsgs", highestPriorityMessage)); + trans.QueueCommand(r => r.AddItemToListAsync("workq", highestPriorityMessage)); + + await trans.FlushAsync(); + } + + Assert.That(await RedisAsync.GetAllItemsFromListAsync("workq"), + Is.EquivalentTo(new List { "message1", "message2" })); + Assert.That(await RedisAsync.GetAllItemsFromSortedSetAsync("prioritymsgs"), + Is.EquivalentTo(new List { "message3", "message4" })); + } + + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisPipelineTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisPipelineTests.Async.cs new file mode 100644 index 00000000..0b91fd04 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisPipelineTests.Async.cs @@ -0,0 +1,281 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class RedisPipelineTestsAsync + : RedisClientTestsBaseAsync + { + private const string Key = "pipemultitest"; + private const string ListKey = "pipemultitest-list"; + private const string SetKey = "pipemultitest-set"; + private const string SortedSetKey = "pipemultitest-sortedset"; + + public override void OnAfterEachTest() + { + CleanMask = Key + "*"; + base.OnAfterEachTest(); + } + + [Test] + public async Task Can_call_single_operation_in_pipeline() + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + await using (var pipeline = RedisAsync.CreatePipeline()) + { + pipeline.QueueCommand(r => r.IncrementValueAsync(Key)); + var map = new Dictionary(); + pipeline.QueueCommand(r => r.GetAsync(Key), y => map[Key] = y); + + await pipeline.FlushAsync(); + } + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("1")); + } + + [Test] + public async Task No_commit_of_atomic_pipelines_discards_all_commands() + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + await using (var pipeline = RedisAsync.CreatePipeline()) + { + pipeline.QueueCommand(r => r.IncrementValueAsync(Key)); + } + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + } + + [Test] + public async Task Exception_in_atomic_pipelines_discards_all_commands() + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + try + { + await using var pipeline = RedisAsync.CreatePipeline(); + pipeline.QueueCommand(r => r.IncrementValueAsync(Key)); + throw new NotSupportedException(); + } + catch (NotSupportedException) + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + } + } + + [Test] + public async Task Can_call_single_operation_3_Times_in_pipeline() + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + await using (var pipeline = RedisAsync.CreatePipeline()) + { + pipeline.QueueCommand(r => r.IncrementValueAsync(Key)); + pipeline.QueueCommand(r => r.IncrementValueAsync(Key)); + pipeline.QueueCommand(r => r.IncrementValueAsync(Key)); + + await pipeline.FlushAsync(); + } + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("3")); + } + [Test] + public async Task Can_call_hash_operations_in_pipeline() + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + var fields = new[] { "field1", "field2", "field3" }; + var values = new[] { "1", "2", "3" }; + var fieldBytes = new byte[fields.Length][]; + for (int i = 0; i < fields.Length; ++i) + { + fieldBytes[i] = GetBytes(fields[i]); + + } + var valueBytes = new byte[values.Length][]; + for (int i = 0; i < values.Length; ++i) + { + valueBytes[i] = GetBytes(values[i]); + + } + byte[][] members = null; + await using var pipeline = RedisAsync.CreatePipeline(); + + + pipeline.QueueCommand(r => ((IRedisNativeClientAsync)r).HMSetAsync(Key, fieldBytes, valueBytes)); + pipeline.QueueCommand(r => ((IRedisNativeClientAsync)r).HGetAllAsync(Key), x => members = x); + + + await pipeline.FlushAsync(); + + + for (var i = 0; i < members.Length; i += 2) + { + Assert.AreEqual(members[i], fieldBytes[i / 2]); + Assert.AreEqual(members[i + 1], valueBytes[i / 2]); + + } + } + + [Test] + public async Task Can_call_multiple_setexs_in_pipeline() + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + var keys = new[] { Key + "key1", Key + "key2", Key + "key3" }; + var values = new[] { "1", "2", "3" }; + await using var pipeline = RedisAsync.CreatePipeline(); + + for (int i = 0; i < 3; ++i) + { + int index0 = i; + pipeline.QueueCommand(r => ((IRedisNativeClientAsync)r).SetExAsync(keys[index0], 100, GetBytes(values[index0]))); + } + + await pipeline.FlushAsync(); + await pipeline.ReplayAsync(); + + + for (int i = 0; i < 3; ++i) + Assert.AreEqual(await RedisAsync.GetValueAsync(keys[i]), values[i]); + } + + [Test] + public async Task Can_call_single_operation_with_callback_3_Times_in_pipeline() + { + var results = new List(); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + await using (var pipeline = RedisAsync.CreatePipeline()) + { + pipeline.QueueCommand(r => r.IncrementValueAsync(Key), results.Add); + pipeline.QueueCommand(r => r.IncrementValueAsync(Key), results.Add); + pipeline.QueueCommand(r => r.IncrementValueAsync(Key), results.Add); + + await pipeline.FlushAsync(); + } + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("3")); + Assert.That(results, Is.EquivalentTo(new List { 1, 2, 3 })); + } + + [Test] + public async Task Supports_different_operation_types_in_same_pipeline() + { + var incrementResults = new List(); + var collectionCounts = new List(); + var containsItem = false; + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + await using (var pipeline = RedisAsync.CreatePipeline()) + { + pipeline.QueueCommand(r => r.IncrementValueAsync(Key), intResult => incrementResults.Add(intResult)); + pipeline.QueueCommand(r => r.AddItemToListAsync(ListKey, "listitem1")); + pipeline.QueueCommand(r => r.AddItemToListAsync(ListKey, "listitem2")); + pipeline.QueueCommand(r => r.AddItemToSetAsync(SetKey, "setitem")); + pipeline.QueueCommand(r => r.SetContainsItemAsync(SetKey, "setitem"), b => containsItem = b); + pipeline.QueueCommand(r => r.AddItemToSortedSetAsync(SortedSetKey, "sortedsetitem1")); + pipeline.QueueCommand(r => r.AddItemToSortedSetAsync(SortedSetKey, "sortedsetitem2")); + pipeline.QueueCommand(r => r.AddItemToSortedSetAsync(SortedSetKey, "sortedsetitem3")); + pipeline.QueueCommand(r => r.GetListCountAsync(ListKey), intResult => collectionCounts.Add(intResult)); + pipeline.QueueCommand(r => r.GetSetCountAsync(SetKey), intResult => collectionCounts.Add(intResult)); + pipeline.QueueCommand(r => r.GetSortedSetCountAsync(SortedSetKey), intResult => collectionCounts.Add(intResult)); + pipeline.QueueCommand(r => r.IncrementValueAsync(Key), intResult => incrementResults.Add(intResult)); + + await pipeline.FlushAsync(); + } + + Assert.That(containsItem, Is.True); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("2")); + Assert.That(incrementResults, Is.EquivalentTo(new List { 1, 2 })); + Assert.That(collectionCounts, Is.EquivalentTo(new List { 2, 1, 3 })); + Assert.That(await RedisAsync.GetAllItemsFromListAsync(ListKey), Is.EquivalentTo(new List { "listitem1", "listitem2" })); + Assert.That(await RedisAsync.GetAllItemsFromSetAsync(SetKey), Is.EquivalentTo(new List { "setitem" })); + Assert.That(await RedisAsync.GetAllItemsFromSortedSetAsync(SortedSetKey), Is.EquivalentTo(new List { "sortedsetitem1", "sortedsetitem2", "sortedsetitem3" })); + } + + [Test] + public async Task Can_call_multi_string_operations_in_pipeline() + { + string item1 = null; + string item4 = null; + + var results = new List(); + Assert.That(await RedisAsync.GetListCountAsync(ListKey), Is.EqualTo(0)); + await using (var pipeline = RedisAsync.CreatePipeline()) + { + pipeline.QueueCommand(r => r.AddItemToListAsync(ListKey, "listitem1")); + pipeline.QueueCommand(r => r.AddItemToListAsync(ListKey, "listitem2")); + pipeline.QueueCommand(r => r.AddItemToListAsync(ListKey, "listitem3")); + pipeline.QueueCommand(r => r.GetAllItemsFromListAsync(ListKey), x => results = x); + pipeline.QueueCommand(r => r.GetItemFromListAsync(ListKey, 0), x => item1 = x); + pipeline.QueueCommand(r => r.GetItemFromListAsync(ListKey, 4), x => item4 = x); + + await pipeline.FlushAsync(); + } + + Assert.That(await RedisAsync.GetListCountAsync(ListKey), Is.EqualTo(3)); + Assert.That(results, Is.EquivalentTo(new List { "listitem1", "listitem2", "listitem3" })); + Assert.That(item1, Is.EqualTo("listitem1")); + Assert.That(item4, Is.Null); + } + [Test] + // Operations that are not supported in older versions will look at server info to determine what to do. + // If server info is fetched each time, then it will interfer with pipeline + public async Task Can_call_operation_not_supported_on_older_servers_in_pipeline() + { + var temp = new byte[1]; + await using var pipeline = RedisAsync.CreatePipeline(); + pipeline.QueueCommand(r => ((IRedisNativeClientAsync)r).SetExAsync(Key + "key", 5, temp)); + await pipeline.FlushAsync(); + } + [Test] + public async Task Pipeline_can_be_replayed() + { + string KeySquared = Key + Key; + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.Null); + await using var pipeline = RedisAsync.CreatePipeline(); + pipeline.QueueCommand(r => r.IncrementValueAsync(Key)); + pipeline.QueueCommand(r => r.IncrementValueAsync(KeySquared)); + await pipeline.FlushAsync(); + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("1")); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.EqualTo("1")); + await NativeAsync.DelAsync(Key); + await NativeAsync.DelAsync(KeySquared); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.Null); + + await pipeline.ReplayAsync(); + await pipeline.DisposeAsync(); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("1")); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.EqualTo("1")); + } + + [Test] + public async Task Pipeline_can_be_contain_watch() + { + string KeySquared = Key + Key; + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.Null); + await using var pipeline = RedisAsync.CreatePipeline(); + pipeline.QueueCommand(r => r.IncrementValueAsync(Key)); + pipeline.QueueCommand(r => r.IncrementValueAsync(KeySquared)); + pipeline.QueueCommand(r => ((IRedisNativeClientAsync)r).WatchAsync(new[] { Key + "FOO" })); + await pipeline.FlushAsync(); + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("1")); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.EqualTo("1")); + } + + [Test] + public async Task Can_call_AddRangeToSet_in_pipeline() + { + await using var pipeline = RedisAsync.CreatePipeline(); + var key = "pipeline-test"; + + pipeline.QueueCommand(r => r.RemoveAsync(key)); + pipeline.QueueCommand(r => r.AddRangeToSetAsync(key, new[] { "A", "B", "C" }.ToList())); + + await pipeline.FlushAsync(); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisPubSubTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisPubSubTests.Async.cs new file mode 100644 index 00000000..20e2df9e --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisPubSubTests.Async.cs @@ -0,0 +1,293 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Integration")] + public class RedisPubSubTestsAsync + : RedisClientTestsBaseAsync + { + public override void OnBeforeEachTest() + { + base.OnBeforeEachTest(); + RedisRaw.NamespacePrefix = "RedisPubSubTests"; + } + + [Test] + public async Task Can_Subscribe_and_Publish_single_message() + { + var channelName = PrefixedKey("CHANNEL1"); + const string message = "Hello, World!"; + var key = PrefixedKey("Can_Subscribe_and_Publish_single_message"); + + await RedisAsync.IncrementValueAsync(key); + + await using (var subscription = await RedisAsync.CreateSubscriptionAsync()) + { + subscription.OnSubscribeAsync += channel => + { + Log("Subscribed to '{0}'", channel); + Assert.That(channel, Is.EqualTo(channelName)); + return default; + }; + subscription.OnUnSubscribeAsync += channel => + { + Log("UnSubscribed from '{0}'", channel); + Assert.That(channel, Is.EqualTo(channelName)); + return default; + }; + subscription.OnMessageAsync += async (channel, msg) => + { + Log("Received '{0}' from channel '{1}'", msg, channel); + Assert.That(channel, Is.EqualTo(channelName)); + Assert.That(msg, Is.EqualTo(message)); + await subscription.UnSubscribeFromAllChannelsAsync(); + }; + + ThreadPool.QueueUserWorkItem(async x => + { + await Task.Delay(100); // to be sure that we have subscribers + await using var redisClient = CreateRedisClient().ForAsyncOnly(); + Log("Publishing '{0}' to '{1}'", message, channelName); + await redisClient.PublishMessageAsync(channelName, message); + }); + + Log("Start Listening On " + channelName); + await subscription.SubscribeToChannelsAsync(new[] { channelName }); //blocking + } + + Log("Using as normal client again..."); + await RedisAsync.IncrementValueAsync(key); + Assert.That(await RedisAsync.GetAsync(key), Is.EqualTo(2)); + } + + [Test] + public async Task Can_Subscribe_and_Publish_single_message_using_wildcard() + { + var channelWildcard = PrefixedKey("CHANNEL.*"); + var channelName = PrefixedKey("CHANNEL.1"); + const string message = "Hello, World!"; + var key = PrefixedKey("Can_Subscribe_and_Publish_single_message"); + + await RedisAsync.IncrementValueAsync(key); + + await using (var subscription = await RedisAsync.CreateSubscriptionAsync()) + { + subscription.OnSubscribeAsync += channel => + { + Log("Subscribed to '{0}'", channelWildcard); + Assert.That(channel, Is.EqualTo(channelWildcard)); + return default; + }; + subscription.OnUnSubscribeAsync += channel => + { + Log("UnSubscribed from '{0}'", channelWildcard); + Assert.That(channel, Is.EqualTo(channelWildcard)); + return default; + }; + subscription.OnMessageAsync += async (channel, msg) => + { + Log("Received '{0}' from channel '{1}'", msg, channel); + Assert.That(channel, Is.EqualTo(channelName)); + Assert.That(msg, Is.EqualTo(message), "we should get the message, not the channel"); + await subscription.UnSubscribeFromChannelsMatchingAsync(new string[0]); + }; + + ThreadPool.QueueUserWorkItem(async x => + { + await Task.Delay(100); // to be sure that we have subscribers + await using var redisClient = CreateRedisClient().ForAsyncOnly(); + Log("Publishing '{0}' to '{1}'", message, channelName); + await redisClient.PublishMessageAsync(channelName, message); + }); + + Log("Start Listening On " + channelName); + await subscription.SubscribeToChannelsMatchingAsync(new[] { channelWildcard }); //blocking + } + + Log("Using as normal client again..."); + await RedisAsync.IncrementValueAsync(key); + Assert.That(await RedisAsync.GetAsync(key), Is.EqualTo(2)); + } + + [Test] + public async Task Can_Subscribe_and_Publish_multiple_message() + { + var channelName = PrefixedKey("CHANNEL2"); + const string messagePrefix = "MESSAGE "; + string key = PrefixedKey("Can_Subscribe_and_Publish_multiple_message"); + const int publishMessageCount = 5; + var messagesReceived = 0; + + await RedisAsync.IncrementValueAsync(key); + + await using (var subscription = await RedisAsync.CreateSubscriptionAsync()) + { + subscription.OnSubscribeAsync += channel => + { + Log("Subscribed to '{0}'", channel); + Assert.That(channel, Is.EqualTo(channelName)); + return default; + }; + subscription.OnUnSubscribeAsync += channel => + { + Log("UnSubscribed from '{0}'", channel); + Assert.That(channel, Is.EqualTo(channelName)); + return default; + }; + subscription.OnMessageAsync += async (channel, msg) => + { + Log("Received '{0}' from channel '{1}'", msg, channel); + Assert.That(channel, Is.EqualTo(channelName)); + Assert.That(msg, Is.EqualTo(messagePrefix + messagesReceived++)); + + if (messagesReceived == publishMessageCount) + { + await subscription.UnSubscribeFromAllChannelsAsync(); + } + }; + + ThreadPool.QueueUserWorkItem(async x => + { + await Task.Delay(100); // to be sure that we have subscribers + + await using var redisClient = CreateRedisClient().ForAsyncOnly(); + for (var i = 0; i < publishMessageCount; i++) + { + var message = messagePrefix + i; + Log("Publishing '{0}' to '{1}'", message, channelName); + await redisClient.PublishMessageAsync(channelName, message); + } + }); + + Log("Start Listening On"); + await subscription.SubscribeToChannelsAsync(new[] { channelName }); //blocking + } + + Log("Using as normal client again..."); + await RedisAsync.IncrementValueAsync(key); + Assert.That(await RedisAsync.GetAsync(key), Is.EqualTo(2)); + + Assert.That(messagesReceived, Is.EqualTo(publishMessageCount)); + } + + [Test] + public async Task Can_Subscribe_and_Publish_message_to_multiple_channels() + { + var channelPrefix = PrefixedKey("CHANNEL3 "); + const string message = "MESSAGE"; + const int publishChannelCount = 5; + var key = PrefixedKey("Can_Subscribe_and_Publish_message_to_multiple_channels"); + + var channels = new List(); + publishChannelCount.Times(i => channels.Add(channelPrefix + i)); + + var messagesReceived = 0; + var channelsSubscribed = 0; + var channelsUnSubscribed = 0; + + await RedisAsync.IncrementValueAsync(key); + + await using (var subscription = await RedisAsync.CreateSubscriptionAsync()) + { + subscription.OnSubscribeAsync += channel => + { + Log("Subscribed to '{0}'", channel); + Assert.That(channel, Is.EqualTo(channelPrefix + channelsSubscribed++)); + return default; + }; + subscription.OnUnSubscribeAsync += channel => + { + Log("UnSubscribed from '{0}'", channel); + Assert.That(channel, Is.EqualTo(channelPrefix + channelsUnSubscribed++)); + return default; + }; + subscription.OnMessageAsync += async (channel, msg) => + { + Log("Received '{0}' from channel '{1}'", msg, channel); + Assert.That(channel, Is.EqualTo(channelPrefix + messagesReceived++)); + Assert.That(msg, Is.EqualTo(message)); + + await subscription.UnSubscribeFromChannelsAsync(new[] { channel }); + }; + + ThreadPool.QueueUserWorkItem(async x => + { + await Task.Delay(100); // to be sure that we have subscribers + + await using var redisClient = CreateRedisClient().ForAsyncOnly(); + foreach (var channel in channels) + { + Log("Publishing '{0}' to '{1}'", message, channel); + await redisClient.PublishMessageAsync(channel, message); + } + }); + + Log("Start Listening On"); + await subscription.SubscribeToChannelsAsync(channels.ToArray()); //blocking + } + + Log("Using as normal client again..."); + await RedisAsync.IncrementValueAsync(key); + Assert.That(await RedisAsync.GetAsync(key), Is.EqualTo(2)); + + Assert.That(messagesReceived, Is.EqualTo(publishChannelCount)); + Assert.That(channelsSubscribed, Is.EqualTo(publishChannelCount)); + Assert.That(channelsUnSubscribed, Is.EqualTo(publishChannelCount)); + } + + [Test] + public async Task Can_Subscribe_to_channel_pattern() + { + int msgs = 0; + await using var subscription = await RedisAsync.CreateSubscriptionAsync(); + subscription.OnMessageAsync += async (channel, msg) => + { + Debug.WriteLine(String.Format("{0}: {1}", channel, msg + msgs++)); + await subscription.UnSubscribeFromChannelsMatchingAsync(new[] { PrefixedKey("CHANNEL4:TITLE*") }); + }; + + ThreadPool.QueueUserWorkItem(async x => + { + await Task.Delay(100); // to be sure that we have subscribers + + await using var redisClient = CreateRedisClient().ForAsyncOnly(); + Log("Publishing msg..."); + await redisClient.PublishMessageAsync(PrefixedKey("CHANNEL4:TITLE1"), "hello"); // .ToUtf8Bytes() + }); + + Log("Start Listening On"); + await subscription.SubscribeToChannelsMatchingAsync(new[] { PrefixedKey("CHANNEL4:TITLE*") }); + } + + [Test] + public async Task Can_Subscribe_to_multiplechannel_pattern() + { + var channels = new[] { PrefixedKey("CHANNEL5:TITLE*"), PrefixedKey("CHANNEL5:BODY*") }; + int msgs = 0; + await using var subscription = await RedisAsync.CreateSubscriptionAsync(); + subscription.OnMessageAsync += async (channel, msg) => + { + Debug.WriteLine(String.Format("{0}: {1}", channel, msg + msgs++)); + await subscription.UnSubscribeFromChannelsMatchingAsync(channels); + }; + + ThreadPool.QueueUserWorkItem(async x => + { + await Task.Delay(100); // to be sure that we have subscribers + + await using var redisClient = CreateRedisClient().ForAsyncOnly(); + Log("Publishing msg..."); + await redisClient.PublishMessageAsync(PrefixedKey("CHANNEL5:BODY"), "hello"); // .ToUtf8Bytes() + }); + + Log("Start Listening On"); + await subscription.SubscribeToChannelsMatchingAsync(channels); + } + + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisScanTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisScanTests.Async.cs new file mode 100644 index 00000000..b5a79762 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisScanTests.Async.cs @@ -0,0 +1,173 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Text; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class RedisScanTestsAsync + : RedisClientTestsBaseAsync + { + [Test] + public async Task Can_scan_10_collection() + { + await RedisAsync.FlushAllAsync(); + var keys = 10.Times(x => "KEY" + x); + await RedisAsync.SetAllAsync(keys.ToSafeDictionary(x => x)); + + var ret = await NativeAsync.ScanAsync(0); + + Assert.That(ret.Cursor, Is.GreaterThanOrEqualTo(0)); + Assert.That(ret.AsStrings(), Is.EquivalentTo(keys)); + } + + [Test] + public async Task Can_scan_100_collection_over_cursor() + { + var allKeys = new HashSet(); + await RedisAsync.FlushAllAsync(); + var keys = 100.Times(x => "KEY" + x); + await RedisAsync.SetAllAsync(keys.ToSafeDictionary(x => x)); + + var i = 0; + var ret = new ScanResult(); + while (true) + { + ret = await NativeAsync.ScanAsync(ret.Cursor, 10); + i++; + ret.AsStrings().ForEach(x => allKeys.Add(x)); + if (ret.Cursor == 0) break; + } + + Assert.That(i, Is.GreaterThanOrEqualTo(2)); + Assert.That(allKeys.Count, Is.EqualTo(keys.Count)); + Assert.That(allKeys, Is.EquivalentTo(keys)); + } + + [Test] + public async Task Can_scan_and_search_10_collection() + { + await RedisAsync.FlushAllAsync(); + var keys = 11.Times(x => "KEY" + x); + await RedisAsync.SetAllAsync(keys.ToSafeDictionary(x => x)); + + var ret = await NativeAsync.ScanAsync(0, 11, match: "KEY1*"); + + Assert.That(ret.Cursor, Is.GreaterThanOrEqualTo(0)); + Assert.That(ret.AsStrings(), Is.EquivalentTo(new[] { "KEY1", "KEY10" })); + } + + [Test] + public async Task Can_SScan_10_sets() + { + await RedisAsync.FlushAllAsync(); + var items = 10.Times(x => "item" + x); + await items.ForEachAsync(async x => await RedisAsync.AddItemToSetAsync("scanset", x)); + + var ret = await NativeAsync.SScanAsync("scanset", 0); + + Assert.That(ret.Cursor, Is.GreaterThanOrEqualTo(0)); + Assert.That(ret.AsStrings(), Is.EquivalentTo(items)); + } + + [Test] + public async Task Can_ZScan_10_sortedsets() + { + await RedisAsync.FlushAllAsync(); + var items = 10.Times(x => "item" + x); + var i = 0; + await items.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync("scanzset", x, i++)); + + var ret = await NativeAsync.ZScanAsync("scanzset", 0); + var itemsWithScore = ret.AsItemsWithScores(); + + Assert.That(itemsWithScore.Keys, Is.EqualTo(items)); + Assert.That(itemsWithScore.Values, Is.EqualTo(10.Times(x => (double)x))); + } + + [Test] + public async Task Can_HScan_10_hashes() + { + await RedisAsync.FlushAllAsync(); + var values = 10.Times(x => "VALUE" + x); + await RedisAsync.SetRangeInHashAsync("scanhash", values.ToSafeDictionary(x => x.Replace("VALUE", "KEY"))); + + var ret = await NativeAsync.HScanAsync("scanhash", 0); + + var keyValues = ret.AsKeyValues(); + + Assert.That(ret.Cursor, Is.GreaterThanOrEqualTo(0)); + Assert.That(keyValues.Keys, Is.EquivalentTo(values.ConvertAll(x => x.Replace("VALUE", "KEY")))); + Assert.That(keyValues.Values, Is.EquivalentTo(values)); + } + + [Test] + public async Task Does_lazy_scan_all_keys() + { + await RedisAsync.FlushAllAsync(); + var keys = 100.Times(x => "KEY" + x); + await RedisAsync.SetAllAsync(keys.ToSafeDictionary(x => x)); + + var scanAllKeys = RedisAsync.ScanAllKeysAsync(pageSize: 10); + var tenKeys = await scanAllKeys.TakeAsync(10).ToListAsync(); + + Assert.That(tenKeys.Count, Is.EqualTo(10)); + + Assert.That(await scanAllKeys.CountAsync(), Is.EqualTo(100)); + } + + [Test] + public async Task Does_lazy_scan_all_set_items() + { + await RedisAsync.FlushAllAsync(); + var items = 100.Times(x => "item" + x); + await items.ForEachAsync(async x => await RedisAsync.AddItemToSetAsync("scanset", x)); + + var scanAllItems = RedisAsync.ScanAllSetItemsAsync("scanset", pageSize: 10); + var tenKeys = await scanAllItems.TakeAsync(10).ToListAsync(); + + Assert.That(tenKeys.Count, Is.EqualTo(10)); + + Assert.That(await scanAllItems.CountAsync(), Is.EqualTo(100)); + } + + [Test] + public async Task Does_lazy_scan_all_sortedset_items() + { + await RedisAsync.FlushAllAsync(); + var items = 100.Times(x => "item" + x); + var i = 0; + await items.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync("scanzset", x, i++)); + + var scanAllItems = RedisAsync.ScanAllSortedSetItemsAsync("scanzset", pageSize: 10); + var tenKeys = await scanAllItems.TakeAsync(10).ToListAsync(); + + Assert.That(tenKeys.Count, Is.EqualTo(10)); + + Assert.That(await scanAllItems.CountAsync(), Is.EqualTo(100)); + + var map = await scanAllItems.ToDictionaryAsync(x => x.Key, x => x.Value); + Assert.That(map.Keys, Is.EquivalentTo(items)); + } + + [Test] + public async Task Does_lazy_scan_all_hash_items() + { + await RedisAsync.FlushAllAsync(); + var values = 100.Times(x => "VALUE" + x); + await RedisAsync.SetRangeInHashAsync("scanhash", values.ToSafeDictionary(x => x.Replace("VALUE", "KEY"))); + + var scanAllItems = RedisAsync.ScanAllHashEntriesAsync("scanhash", pageSize: 10); + var tenKeys = await scanAllItems.TakeAsync(10).ToListAsync(); + + Assert.That(tenKeys.Count, Is.EqualTo(10)); + + Assert.That(await scanAllItems.CountAsync(), Is.EqualTo(100)); + + var map = await scanAllItems.ToDictionaryAsync(x => x.Key, x => x.Value); + Assert.That(map.Values, Is.EquivalentTo(values)); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisStatsTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisStatsTests.Async.cs new file mode 100644 index 00000000..2fd145ef --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisStatsTests.Async.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class RedisStatsTestsAsync + : RedisClientTestsBaseAsync + { + [OneTimeSetUp] + public void OneTimeSetUp() + { + RedisConfig.AssumeServerVersion = 2821; + } + + [Test] + [Ignore("too long")] + public async Task Batch_and_Pipeline_requests_only_counts_as_1_request() + { + var reqCount = RedisNativeClient.RequestsPerHour; + + var map = new Dictionary(); + 10.Times(i => map["key" + i] = "value" + i); + + await RedisAsync.SetValuesAsync(map); + + Assert.That(RedisNativeClient.RequestsPerHour, Is.EqualTo(reqCount + 1)); + + var keyTypes = new Dictionary(); + await using (var pipeline = RedisAsync.CreatePipeline()) + { + map.Keys.Each(key => + pipeline.QueueCommand(r => r.TypeAsync(key), x => keyTypes[key] = x)); + + await pipeline.FlushAsync(); + } + + Assert.That(RedisNativeClient.RequestsPerHour, Is.EqualTo(reqCount + 2)); + Assert.That(keyTypes.Count, Is.EqualTo(map.Count)); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisTransactionCommonTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisTransactionCommonTests.Async.cs new file mode 100644 index 00000000..6ecd123b --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisTransactionCommonTests.Async.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class RedisTransactionCommonTestsAsync + : RedisClientTestsBaseAsync + { + private const string Prefix = "tran"; + + public override void OnAfterEachTest() + { + CleanMask = Prefix + "*"; + base.OnAfterEachTest(); + } + + [Test] + public async Task Can_Set_and_Expire_key_in_atomic_transaction() + { + var oneSec = TimeSpan.FromSeconds(1); + + Assert.That(await RedisAsync.GetValueAsync(Prefix + "key"), Is.Null); + await using (var trans = await RedisAsync.CreateTransactionAsync()) //Calls 'MULTI' + { + trans.QueueCommand(r => r.SetValueAsync(Prefix + "key", "a")); //Queues 'SET key a' + trans.QueueCommand(r => r.ExpireEntryInAsync(Prefix + "key", oneSec)); //Queues 'EXPIRE key 1' + + await trans.CommitAsync(); //Calls 'EXEC' + + } //Calls 'DISCARD' if 'EXEC' wasn't called + + Assert.That(await RedisAsync.GetValueAsync(Prefix + "key"), Is.EqualTo("a")); + await Task.Delay(TimeSpan.FromSeconds(2)); + Assert.That(await RedisAsync.GetValueAsync(Prefix + "key"), Is.Null); + } + + [Test] + public async Task Can_Pop_priority_message_from_SortedSet_and_Add_to_workq_in_atomic_transaction() + { + var messages = new List { "message4", "message3", "message2" }; + + await RedisAsync.AddItemToListAsync(Prefix + "workq", "message1"); + + var priority = 1; + await messages.ForEachAsync(async x => await RedisAsync.AddItemToSortedSetAsync(Prefix + "prioritymsgs", x, priority++)); + + var highestPriorityMessage = await RedisAsync.PopItemWithHighestScoreFromSortedSetAsync(Prefix + "prioritymsgs"); + + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.RemoveItemFromSortedSetAsync(Prefix + "prioritymsgs", highestPriorityMessage)); + trans.QueueCommand(r => r.AddItemToListAsync(Prefix + "workq", highestPriorityMessage)); + + await trans.CommitAsync(); + } + + Assert.That(await RedisAsync.GetAllItemsFromListAsync(Prefix + "workq"), + Is.EquivalentTo(new List { "message1", "message2" })); + Assert.That(await RedisAsync.GetAllItemsFromSortedSetAsync(Prefix + "prioritymsgs"), + Is.EquivalentTo(new List { "message3", "message4" })); + } + + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisTransactionTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisTransactionTests.Async.cs new file mode 100644 index 00000000..565dedcb --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RedisTransactionTests.Async.cs @@ -0,0 +1,417 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Text; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture] + public class RedisTransactionTestsAsync + : RedisClientTestsBaseAsync + { + private const string Key = "rdtmultitest"; + private const string ListKey = "rdtmultitest-list"; + private const string SetKey = "rdtmultitest-set"; + private const string SortedSetKey = "rdtmultitest-sortedset"; + private const string HashKey = "rdthashtest"; + + public override void OnAfterEachTest() + { + CleanMask = Key + "*"; + base.OnAfterEachTest(); + } + + [Test] + public async Task Can_call_single_operation_in_transaction() + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.IncrementValueAsync(Key)); + var map = new Dictionary(); + trans.QueueCommand(r => r.GetAsync(Key), y => map[Key] = y); + + await trans.CommitAsync(); + } + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("1")); + } + + [Test] + public async Task No_commit_of_atomic_transactions_discards_all_commands() + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.IncrementValueAsync(Key)); + } + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + } + + [Test] + public async Task Watch_aborts_transaction() + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + const string value1 = "value1"; + try + { + await RedisAsync.WatchAsync(new[] { Key }); + await RedisAsync.SetAsync(Key, value1); + await using var trans = await RedisAsync.CreateTransactionAsync(); + trans.QueueCommand(r => r.SetAsync(Key, value1)); + var success = await trans.CommitAsync(); + Assert.False(success); + Assert.AreEqual(value1, await RedisAsync.GetAsync(Key)); + } + catch (NotSupportedException) + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + } + } + + [Test] + public async Task Exception_in_atomic_transactions_discards_all_commands() + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + try + { + await using var trans = await RedisAsync.CreateTransactionAsync(); + trans.QueueCommand(r => r.IncrementValueAsync(Key)); + throw new NotSupportedException(); + } + catch (NotSupportedException) + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + } + } + + [Test] + public async Task Can_call_single_operation_3_Times_in_transaction() + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.IncrementValueAsync(Key)); + trans.QueueCommand(r => r.IncrementValueAsync(Key)); + trans.QueueCommand(r => r.IncrementValueAsync(Key)); + + await trans.CommitAsync(); + } + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("3")); + } + + [Test] + public async Task Can_call_single_operation_with_callback_3_Times_in_transaction() + { + var results = new List(); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.IncrementValueAsync(Key), results.Add); + trans.QueueCommand(r => r.IncrementValueAsync(Key), results.Add); + trans.QueueCommand(r => r.IncrementValueAsync(Key), results.Add); + + await trans.CommitAsync(); + } + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("3")); + Assert.That(results, Is.EquivalentTo(new List { 1, 2, 3 })); + } + + [Test] + public async Task Supports_different_operation_types_in_same_transaction() + { + var incrementResults = new List(); + var collectionCounts = new List(); + var containsItem = false; + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.IncrementValueAsync(Key), intResult => incrementResults.Add(intResult)); + trans.QueueCommand(r => r.AddItemToListAsync(ListKey, "listitem1")); + trans.QueueCommand(r => r.AddItemToListAsync(ListKey, "listitem2")); + trans.QueueCommand(r => r.AddItemToSetAsync(SetKey, "setitem")); + trans.QueueCommand(r => r.SetContainsItemAsync(SetKey, "setitem"), b => containsItem = b); + trans.QueueCommand(r => r.AddItemToSortedSetAsync(SortedSetKey, "sortedsetitem1")); + trans.QueueCommand(r => r.AddItemToSortedSetAsync(SortedSetKey, "sortedsetitem2")); + trans.QueueCommand(r => r.AddItemToSortedSetAsync(SortedSetKey, "sortedsetitem3")); + trans.QueueCommand(r => r.GetListCountAsync(ListKey), intResult => collectionCounts.Add(intResult)); + trans.QueueCommand(r => r.GetSetCountAsync(SetKey), intResult => collectionCounts.Add(intResult)); + trans.QueueCommand(r => r.GetSortedSetCountAsync(SortedSetKey), intResult => collectionCounts.Add(intResult)); + trans.QueueCommand(r => r.IncrementValueAsync(Key), intResult => incrementResults.Add(intResult)); + + await trans.CommitAsync(); + } + + Assert.That(containsItem, Is.True); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("2")); + Assert.That(incrementResults, Is.EquivalentTo(new List { 1, 2 })); + Assert.That(collectionCounts, Is.EquivalentTo(new List { 2, 1, 3 })); + Assert.That(await RedisAsync.GetAllItemsFromListAsync(ListKey), Is.EquivalentTo(new List { "listitem1", "listitem2" })); + Assert.That(await RedisAsync.GetAllItemsFromSetAsync(SetKey), Is.EquivalentTo(new List { "setitem" })); + Assert.That(await RedisAsync.GetAllItemsFromSortedSetAsync(SortedSetKey), Is.EquivalentTo(new List { "sortedsetitem1", "sortedsetitem2", "sortedsetitem3" })); + } + + [Test] + public async Task Can_call_multi_string_operations_in_transaction() + { + string item1 = null; + string item4 = null; + + var results = new List(); + Assert.That(await RedisAsync.GetListCountAsync(ListKey), Is.EqualTo(0)); + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.AddItemToListAsync(ListKey, "listitem1")); + trans.QueueCommand(r => r.AddItemToListAsync(ListKey, "listitem2")); + trans.QueueCommand(r => r.AddItemToListAsync(ListKey, "listitem3")); + trans.QueueCommand(r => r.GetAllItemsFromListAsync(ListKey), x => results = x); + trans.QueueCommand(r => r.GetItemFromListAsync(ListKey, 0), x => item1 = x); + trans.QueueCommand(r => r.GetItemFromListAsync(ListKey, 4), x => item4 = x); + + await trans.CommitAsync(); + } + + Assert.That(await RedisAsync.GetListCountAsync(ListKey), Is.EqualTo(3)); + Assert.That(results, Is.EquivalentTo(new List { "listitem1", "listitem2", "listitem3" })); + Assert.That(item1, Is.EqualTo("listitem1")); + Assert.That(item4, Is.Null); + } + [Test] + public async Task Can_call_multiple_setexs_in_transaction() + { + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + var keys = new[] { "key1", "key2", "key3" }; + var values = new[] { "1", "2", "3" }; + await using var trans = await RedisAsync.CreateTransactionAsync(); + + for (int i = 0; i < 3; ++i) + { + int index0 = i; + trans.QueueCommand(r => ((IRedisNativeClientAsync)r).SetExAsync(keys[index0], 100, GetBytes(values[index0]))); + } + + await trans.CommitAsync(); + await trans.ReplayAsync(); + + + for (int i = 0; i < 3; ++i) + Assert.AreEqual(await RedisAsync.GetValueAsync(keys[i]), values[i]); + } + [Test] + // Operations that are not supported in older versions will look at server info to determine what to do. + // If server info is fetched each time, then it will interfer with transaction + public async Task Can_call_operation_not_supported_on_older_servers_in_transaction() + { + var temp = new byte[1]; + await using var trans = await RedisAsync.CreateTransactionAsync(); + trans.QueueCommand(r => ((IRedisNativeClientAsync)r).SetExAsync(Key, 5, temp)); + await trans.CommitAsync(); + } + + + [Test] + public async Task Transaction_can_be_replayed() + { + string KeySquared = Key + Key; + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.Null); + await using var trans = await RedisAsync.CreateTransactionAsync(); + trans.QueueCommand(r => r.IncrementValueAsync(Key)); + trans.QueueCommand(r => r.IncrementValueAsync(KeySquared)); + await trans.CommitAsync(); + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("1")); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.EqualTo("1")); + await NativeAsync.DelAsync(Key); + await NativeAsync.DelAsync(KeySquared); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.Null); + + await trans.ReplayAsync(); + await trans.DisposeAsync(); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("1")); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.EqualTo("1")); + } + + [Test] + public async Task Transaction_can_issue_watch() + { + await NativeAsync.DelAsync(Key); + Assert.That(await RedisAsync.GetValueAsync(Key), Is.Null); + + string KeySquared = Key + Key; + await NativeAsync.DelAsync(KeySquared); + + await RedisAsync.WatchAsync(new[] { Key, KeySquared }); + await RedisAsync.SetAsync(Key, 7); + + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.SetAsync(Key, 1)); + trans.QueueCommand(r => r.SetAsync(KeySquared, 2)); + await trans.CommitAsync(); + } + + Assert.That(await RedisAsync.GetValueAsync(Key), Is.EqualTo("7")); + Assert.That(await RedisAsync.GetValueAsync(KeySquared), Is.Null); + } + + [Test] + public async Task Can_set_Expiry_on_key_in_transaction() + { + var expiresIn = TimeSpan.FromMinutes(15); + + const string key = "No TTL-Transaction"; + var keyWithTtl = "{0}s TTL-Transaction".Fmt(expiresIn.TotalSeconds); + + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.AddAsync(key, "Foo")); + trans.QueueCommand(r => r.AddAsync(keyWithTtl, "Bar", expiresIn)); + + if (!await trans.CommitAsync()) + throw new Exception("Transaction Failed"); + } + + Assert.That(await RedisAsync.GetAsync(key), Is.EqualTo("Foo")); + Assert.That(await RedisAsync.GetAsync(keyWithTtl), Is.EqualTo("Bar")); + + Assert.That(await RedisAsync.GetTimeToLiveAsync(key), Is.EqualTo(TimeSpan.MaxValue)); + Assert.That((await RedisAsync.GetTimeToLiveAsync(keyWithTtl)).Value.TotalSeconds, Is.GreaterThan(1)); + } + + [Test] + public async Task Does_not_set_Expiry_on_existing_key_in_transaction() + { + var expiresIn = TimeSpan.FromMinutes(15); + + var key = "Exting TTL-Transaction"; + await RedisAsync.AddAsync(key, "Foo"); + + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.AddAsync(key, "Bar", expiresIn)); + + if (!await trans.CommitAsync()) + throw new Exception("Transaction Failed"); + } + + Assert.That(await RedisAsync.GetAsync(key), Is.EqualTo("Foo")); + Assert.That(await RedisAsync.GetTimeToLiveAsync(key), Is.EqualTo(TimeSpan.MaxValue)); + } + + [Test] + public async Task Can_call_GetAllEntriesFromHash_in_transaction() + { + var stringMap = new Dictionary { + {"one","a"}, {"two","b"}, {"three","c"}, {"four","d"} + }; + foreach (var x in stringMap) + { + await RedisAsync.SetEntryInHashAsync(HashKey, x.Key, x.Value); + } + + Dictionary results = null; + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.GetAllEntriesFromHashAsync(HashKey), x => results = x); + + await trans.CommitAsync(); + } + + Assert.That(results, Is.EquivalentTo(stringMap)); + } + + [Test] + public async Task Can_call_Type_in_transaction() + { + await RedisAsync.SetValueAsync("string", "STRING"); + await RedisAsync.AddItemToListAsync("list", "LIST"); + await RedisAsync.AddItemToSetAsync("set", "SET"); + await RedisAsync.AddItemToSortedSetAsync("zset", "ZSET", 1); + + var keys = new[] { "string", "list", "set", "zset" }; + + var results = new Dictionary(); + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + foreach (var key in keys) + { + trans.QueueCommand(r => r.TypeAsync(key), x => results[key] = x); + } + + await trans.CommitAsync(); + } + + results.PrintDump(); + + Assert.That(results, Is.EquivalentTo(new Dictionary + { + {"string", "string" }, + {"list", "list" }, + {"set", "set" }, + {"zset", "zset" }, + })); + } + + [Test] + public async Task Can_call_HashSet_commands_in_transaction() + { + await RedisAsync.AddItemToSetAsync("set", "ITEM 1"); + await RedisAsync.AddItemToSetAsync("set", "ITEM 2"); + HashSet result = null; + + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.GetAllItemsFromSetAsync("set"), values => result = values); + + await trans.CommitAsync(); + } + + Assert.That(result, Is.EquivalentTo(new[] { "ITEM 1", "ITEM 2" })); + } + + [Test] + public async Task Can_call_LUA_Script_in_transaction() + { + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.ExecLuaAsync("return {'myval', 'myotherval'}", new string[0])); + + await trans.CommitAsync(); + } + + RedisText result = null; + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(r => r.ExecLuaAsync("return {'myval', 'myotherval'}", new string[0]), s => result = s); + + await trans.CommitAsync(); + } + + Assert.That(result.Children[0].Text, Is.EqualTo("myval")); + Assert.That(result.Children[1].Text, Is.EqualTo("myotherval")); + } + + [Test] + public async Task Can_call_SetValueIfNotExists_in_transaction() + { + bool f = false; + bool s = false; + + await using (var trans = await RedisAsync.CreateTransactionAsync()) + { + trans.QueueCommand(c => c.SetValueIfNotExistsAsync("foo", "blah"), r => f = r); + trans.QueueCommand(c => c.SetValueIfNotExistsAsync("bar", "blah"), r => s = r); + await trans.CommitAsync(); + } + + Assert.That(f); + Assert.That(s); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RetryCommandTests.Async.cs b/tests/ServiceStack.Redis.Tests/RetryCommandTests.Async.cs new file mode 100644 index 00000000..6e2ed462 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/RetryCommandTests.Async.cs @@ -0,0 +1,143 @@ +using NUnit.Framework; +using System; +using System.Linq; +using System.Net.Sockets; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Async")] + public class RetryCommandTestsAsync + { + [Test, Ignore("3 vs 2 needs investigation; does same in non-async")] + public async Task Does_retry_failed_commands() + { + // warning: this test looks brittle; is often failing "Expected: 3 But was: 2" (on main branch); + + // LogManager.LogFactory = new ConsoleLogFactory(debugEnabled: true); + // RedisConfig.EnableVerboseLogging = true; + RedisStats.Reset(); + + var redisCtrl = new RedisClient(RedisConfig.DefaultHost).ForAsyncOnly(); + await redisCtrl.FlushAllAsync(); + await redisCtrl.SetClientAsync("redisCtrl"); + + var redis = new RedisClient(RedisConfig.DefaultHost).ForAsyncOnly(); + await redis.SetClientAsync("redisRetry"); + + var clientInfo = await redisCtrl.GetClientsInfoAsync(); + var redisId = clientInfo.First(m => m["name"] == "redisRetry")["id"]; + Assert.That(redisId.Length, Is.GreaterThan(0)); + + Assert.That(await redis.IncrementValueAsync("retryCounter"), Is.EqualTo(1)); + + ((RedisClient)redis).OnBeforeFlush = () => + { + ((IRedisClient)redisCtrl).KillClients(withId: redisId); + }; + + Assert.That(await redis.IncrementValueAsync("retryCounter"), Is.EqualTo(2)); + Assert.That(await redis.GetAsync("retryCounter"), Is.EqualTo(3)); + + Assert.That(RedisStats.TotalRetryCount, Is.EqualTo(1)); + Assert.That(RedisStats.TotalRetrySuccess, Is.EqualTo(1)); + Assert.That(RedisStats.TotalRetryTimedout, Is.EqualTo(0)); + } + + [Test] + public async Task Does_retry_failed_commands_with_SocketException() + { + RedisStats.Reset(); + + var redis = new RedisClient(RedisConfig.DefaultHost).ForAsyncOnly(); + await redis.FlushAllAsync(); + + Assert.That(await redis.IncrementValueAsync("retryCounter"), Is.EqualTo(1)); + + ((RedisClient)redis).OnBeforeFlush = () => + { + ((RedisClient)redis).OnBeforeFlush = null; + throw new SocketException(); + }; + + Assert.That(await redis.IncrementValueAsync("retryCounter"), Is.EqualTo(2)); + Assert.That(await redis.GetAsync("retryCounter"), Is.EqualTo(3)); + + Assert.That(RedisStats.TotalRetryCount, Is.EqualTo(1)); + Assert.That(RedisStats.TotalRetrySuccess, Is.EqualTo(1)); + Assert.That(RedisStats.TotalRetryTimedout, Is.EqualTo(0)); + } + + [Test] + public async Task Does_Timeout_with_repeated_SocketException() + { + RedisConfig.Reset(); + RedisConfig.DefaultRetryTimeout = 100; + + var redis = new RedisClient(RedisConfig.DefaultHost).ForAsyncOnly(); + await redis.FlushAllAsync(); + + Assert.That(await redis.IncrementValueAsync("retryCounter"), Is.EqualTo(1)); + + ((RedisClient)redis).OnBeforeFlush = () => + { + throw new SocketException(); + }; + + try + { + await redis.IncrementValueAsync("retryCounter"); + Assert.Fail("Should throw"); + } + catch (RedisException ex) + { + Assert.That(ex.Message, Does.StartWith("Exceeded timeout")); + + ((RedisClient)redis).OnBeforeFlush = null; + Assert.That(await redis.GetAsync("retryCounter"), Is.EqualTo(1)); + + Assert.That(RedisStats.TotalRetryCount, Is.GreaterThan(1)); + Assert.That(RedisStats.TotalRetrySuccess, Is.EqualTo(0)); + Assert.That(RedisStats.TotalRetryTimedout, Is.EqualTo(1)); + } + + RedisConfig.Reset(); + } + + [Test] + public async Task Does_not_retry_when_RetryTimeout_is_Zero() + { + RedisConfig.Reset(); + RedisConfig.DefaultRetryTimeout = 0; + + var redis = new RedisClient(RedisConfig.DefaultHost).ForAsyncOnly(); + await redis.FlushAllAsync(); + + Assert.That(await redis.IncrementValueAsync("retryCounter"), Is.EqualTo(1)); + + ((RedisClient)redis).OnBeforeFlush = () => + { + throw new SocketException(); + }; + + try + { + await redis.IncrementValueAsync("retryCounter"); + Assert.Fail("Should throw"); + } + catch (Exception ex) + { + Assert.That(ex.Message, Does.StartWith("Exceeded timeout")); + + ((RedisClient)redis).OnBeforeFlush = null; + Assert.That(await redis.GetAsync("retryCounter"), Is.EqualTo(1)); + + Assert.That(RedisStats.TotalRetryCount, Is.EqualTo(0)); + Assert.That(RedisStats.TotalRetrySuccess, Is.EqualTo(0)); + Assert.That(RedisStats.TotalRetryTimedout, Is.EqualTo(1)); + } + + RedisConfig.Reset(); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RetryCommandTests.cs b/tests/ServiceStack.Redis.Tests/RetryCommandTests.cs index 3589827e..e81575d4 100644 --- a/tests/ServiceStack.Redis.Tests/RetryCommandTests.cs +++ b/tests/ServiceStack.Redis.Tests/RetryCommandTests.cs @@ -14,9 +14,10 @@ public class RetryCommandTests [Test] public void Does_retry_failed_commands() { -// LogManager.LogFactory = new ConsoleLogFactory(debugEnabled: true); -// RedisConfig.EnableVerboseLogging = true; + // warning: this test looks brittle; is often failing "Expected: 3 But was: 2" (on main branch); + // LogManager.LogFactory = new ConsoleLogFactory(debugEnabled: true); + // RedisConfig.EnableVerboseLogging = true; RedisStats.Reset(); var redisCtrl = new RedisClient(RedisConfig.DefaultHost); diff --git a/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj b/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj index a08bf928..1bde00c3 100644 --- a/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj +++ b/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj @@ -1,6 +1,13 @@  - net46;netcoreapp2.1 + + net46;net472;netcoreapp2.1;netcoreapp3.1 portable ServiceStack.Redis.Tests Library @@ -25,6 +32,13 @@ $(DefineConstants);NET45 + + + + + + $(DefineConstants);NETCORE + diff --git a/tests/ServiceStack.Redis.Tests/ShippersExample.Async.cs b/tests/ServiceStack.Redis.Tests/ShippersExample.Async.cs new file mode 100644 index 00000000..e93eb09a --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/ShippersExample.Async.cs @@ -0,0 +1,128 @@ +// +// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Demis Bellot (demis.bellot@gmail.com) +// +// Copyright 2013 Service Stack LLC. All Rights Reserved. +// +// Licensed under the same terms of reddis and ServiceStack: new BSD license. +// + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using ServiceStack.Common.Tests.Models; +using ServiceStack.Redis.Generic; +using ServiceStack.Text; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Async")] + public class ShippersExampleAsync + { + + public class Shipper + { + public long Id { get; set; } + public string CompanyName { get; set; } + public DateTime DateCreated { get; set; } + public ShipperType ShipperType { get; set; } + public Guid UniqueRef { get; set; } + } + + static void Dump(string message, T entity) + { + var text = TypeSerializer.SerializeToString(entity); + + //make it a little easier on the eyes + var prettyLines = text.Split(new[] { "[", "},{", "]" }, + StringSplitOptions.RemoveEmptyEntries) + .ToList().ConvertAll(x => x.Replace("{", "").Replace("}", "")); + + Debug.WriteLine("\n" + message); + foreach (var l in prettyLines) Debug.WriteLine(l); + } + + [Test] + public async Task Shippers_UseCase() + { + await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + { + //Create a 'strongly-typed' API that makes all Redis Value operations to apply against Shippers + IRedisTypedClientAsync redis = redisClient.As(); + + //Redis lists implement IList while Redis sets implement ICollection + var currentShippers = redis.Lists["urn:shippers:current"]; + var prospectiveShippers = redis.Lists["urn:shippers:prospective"]; + + await currentShippers.AddAsync( + new Shipper + { + Id = await redis.GetNextSequenceAsync(), + CompanyName = "Trains R Us", + DateCreated = DateTime.UtcNow, + ShipperType = ShipperType.Trains, + UniqueRef = Guid.NewGuid() + }); + + await currentShippers.AddAsync( + new Shipper + { + Id = await redis.GetNextSequenceAsync(), + CompanyName = "Planes R Us", + DateCreated = DateTime.UtcNow, + ShipperType = ShipperType.Planes, + UniqueRef = Guid.NewGuid() + }); + + var lameShipper = new Shipper + { + Id = await redis.GetNextSequenceAsync(), + CompanyName = "We do everything!", + DateCreated = DateTime.UtcNow, + ShipperType = ShipperType.All, + UniqueRef = Guid.NewGuid() + }; + + await currentShippers.AddAsync(lameShipper); + + Dump("ADDED 3 SHIPPERS:", await currentShippers.ToListAsync()); + + await currentShippers.RemoveAsync(lameShipper); + + Dump("REMOVED 1:", await currentShippers.ToListAsync()); + + await prospectiveShippers.AddAsync( + new Shipper + { + Id = await redis.GetNextSequenceAsync(), + CompanyName = "Trucks R Us", + DateCreated = DateTime.UtcNow, + ShipperType = ShipperType.Automobiles, + UniqueRef = Guid.NewGuid() + }); + + Dump("ADDED A PROSPECTIVE SHIPPER:", await prospectiveShippers.ToListAsync()); + + await redis.PopAndPushItemBetweenListsAsync(prospectiveShippers, currentShippers); + + Dump("CURRENT SHIPPERS AFTER POP n' PUSH:", await currentShippers.ToListAsync()); + Dump("PROSPECTIVE SHIPPERS AFTER POP n' PUSH:", await prospectiveShippers.ToListAsync()); + + var poppedShipper = await redis.PopItemFromListAsync(currentShippers); + Dump("POPPED a SHIPPER:", poppedShipper); + Dump("CURRENT SHIPPERS AFTER POP:", await currentShippers.ToListAsync()); + + //reset sequence and delete all lists + await redis.SetSequenceAsync(0); + await redis.RemoveEntryAsync(new[] { currentShippers, prospectiveShippers }); + Dump("DELETING CURRENT AND PROSPECTIVE SHIPPERS:", await currentShippers.ToListAsync()); + } + + } + + } +} diff --git a/tests/ServiceStack.Redis.Tests/SslTests.cs b/tests/ServiceStack.Redis.Tests/SslTests.cs index 41532b62..ae082a06 100644 --- a/tests/ServiceStack.Redis.Tests/SslTests.cs +++ b/tests/ServiceStack.Redis.Tests/SslTests.cs @@ -175,7 +175,7 @@ public void Can_connect_to_Buffered_SslStream() if (!sslStream.IsEncrypted) throw new Exception("Could not establish an encrypted connection to " + Host); - var bstream = new BufferedStream(sslStream, 16 * 1024); + var bstream = new System.IO.BufferedStream(sslStream, 16 * 1024); SendAuth(bstream); } diff --git a/tests/ServiceStack.Redis.Tests/TrackThreadTests.cs b/tests/ServiceStack.Redis.Tests/TrackThreadTests.cs index 2e08f59a..88ed49aa 100644 --- a/tests/ServiceStack.Redis.Tests/TrackThreadTests.cs +++ b/tests/ServiceStack.Redis.Tests/TrackThreadTests.cs @@ -18,7 +18,7 @@ public void Does_throw_when_using_same_client_on_different_threads() { var threadId = Thread.CurrentThread.ManagedThreadId.ToString(); var key = $"Thread#{threadId}"; - redis.SetValue(key, threadId); + redis.SetValue(key, threadId); ThreadPool.QueueUserWorkItem(_ => { @@ -43,7 +43,7 @@ public void Does_throw_when_using_same_client_on_different_threads() Thread.Sleep(100); - Console.WriteLine("From Test: " + redis.GetValue(key)); + Console.WriteLine("From Test: " + redis.GetValue(key)); if (poolEx == null) throw new Exception("Should throw InvalidAccessException"); diff --git a/tests/ServiceStack.Redis.Tests/UserSessionRedisClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/UserSessionRedisClientTests.Async.cs new file mode 100644 index 00000000..f6736364 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/UserSessionRedisClientTests.Async.cs @@ -0,0 +1,436 @@ +using NUnit.Framework; +using ServiceStack.Caching; +using ServiceStack.Logging; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Integration"), Category("Async")] + public class UserSessionTestsAsync + { + static UserSessionTestsAsync() + { + LogManager.LogFactory = new ConsoleLogFactory(); + } + + //MasterUser master; + + static readonly Guid UserClientGlobalId1 = new Guid("71A30DE3-D7AF-4B8E-BCA2-AB646EE1F3E9"); + static readonly Guid UserClientGlobalId2 = new Guid("A8D300CF-0414-4C99-A495-A7F34C93CDE1"); + static readonly string UserClientKey = new Guid("10B7D0F7-4D4E-4676-AAC7-CF0234E9133E").ToString("N"); + static readonly Guid UserId = new Guid("5697B030-A369-43A2-A842-27303A0A62BC"); + private const string UserName = "User1"; + private const string ShardId = "0"; + + readonly UserClientSession session = new UserClientSession( + Guid.NewGuid(), UserId, "192.168.0.1", UserClientKey, UserClientGlobalId1); + + private RedisClient redisCache; + + [SetUp] + public void OnBeforeEachTest() + { + redisCache = new RedisClient(TestConfig.SingleHost); + redisCache.FlushAll(); + //master = UserMasterDataAccessModel.Instance.MasterUsers.NewDataAccessObject(true); + } + + public CachedUserSessionManagerAsync GetCacheManager(ICacheClientAsync cacheClient) + { + return new CachedUserSessionManagerAsync(cacheClient); + } + + private static void AssertClientSessionsAreEqual( + UserClientSession clientSession, UserClientSession resolvedClientSession) + { + Assert.That(resolvedClientSession.Id, Is.EqualTo(clientSession.Id)); + Assert.That(resolvedClientSession.Base64ClientModulus, Is.EqualTo(clientSession.Base64ClientModulus)); + Assert.That(resolvedClientSession.IPAddress, Is.EqualTo(clientSession.IPAddress)); + Assert.That(resolvedClientSession.UserClientGlobalId, Is.EqualTo(clientSession.UserClientGlobalId)); + Assert.That(resolvedClientSession.UserId, Is.EqualTo(clientSession.UserId)); + } + + [Test] + public async Task Can_add_single_UserSession() + { + var cacheManager = GetCacheManager(redisCache); + + var clientSession = await cacheManager.StoreClientSessionAsync( + UserId, + UserName, + ShardId, + session.IPAddress, + UserClientKey, + UserClientGlobalId1); + + var resolvedClientSession = await cacheManager.GetUserClientSessionAsync( + clientSession.UserId, clientSession.Id); + + AssertClientSessionsAreEqual(clientSession, resolvedClientSession); + } + + [Test] + public async Task Can_add_multiple_UserClientSessions() + { + var cacheManager = GetCacheManager(redisCache); + + var clientSession1 = await cacheManager.StoreClientSessionAsync( + UserId, + UserName, + ShardId, + session.IPAddress, + UserClientKey, + UserClientGlobalId1); + + var clientSession2 = await cacheManager.StoreClientSessionAsync( + UserId, + UserName, + ShardId, + session.IPAddress, + UserClientKey, + UserClientGlobalId2); + + var resolvedClientSession1 = await cacheManager.GetUserClientSessionAsync( + clientSession1.UserId, clientSession1.Id); + + var resolvedClientSession2 = await cacheManager.GetUserClientSessionAsync( + clientSession2.UserId, clientSession2.Id); + + AssertClientSessionsAreEqual(clientSession1, resolvedClientSession1); + AssertClientSessionsAreEqual(clientSession2, resolvedClientSession2); + } + + [Test] + public async Task Does_remove_UserClientSession() + { + var cacheManager = GetCacheManager(redisCache); + + var clientSession1 = await cacheManager.StoreClientSessionAsync( + UserId, + UserName, + ShardId, + session.IPAddress, + UserClientKey, + UserClientGlobalId1); + + var userSession = await cacheManager.GetUserSessionAsync(UserId); + var resolvedClientSession1 = userSession.GetClientSession(clientSession1.Id); + AssertClientSessionsAreEqual(resolvedClientSession1, clientSession1); + + resolvedClientSession1.ExpiryDate = DateTime.UtcNow.AddSeconds(-1); + await cacheManager.UpdateUserSessionAsync(userSession); + + userSession = await cacheManager.GetUserSessionAsync(UserId); + Assert.That(userSession, Is.Null); + } + + } + + public class CachedUserSessionManagerAsync + { + private static readonly ILog Log = LogManager.GetLogger(typeof(CachedUserSessionManager)); + + /// + /// Google/Yahoo seems to make you to login every 2 weeks?? + /// + private readonly ICacheClientAsync cacheClient; + + /// + /// Big perf hit if we Log on every session change + /// + /// The FMT. + /// The args. + [Conditional("DEBUG")] + protected void LogIfDebug(string fmt, params object[] args) + { + if (args.Length > 0) + Log.DebugFormat(fmt, args); + else + Log.Debug(fmt); + } + + public CachedUserSessionManagerAsync(ICacheClientAsync cacheClient) + { + this.cacheClient = cacheClient; + } + + /// + /// Removes the client session. + /// + /// The user global id. + /// The client session ids. + public async ValueTask RemoveClientSession(Guid userId, ICollection clientSessionIds) + { + var userSession = await this.GetUserSessionAsync(userId); + if (userSession == null) return; + + foreach (var clientSessionId in clientSessionIds) + { + userSession.RemoveClientSession(clientSessionId); + } + await this.UpdateUserSessionAsync(userSession); + } + + /// + /// Adds a new client session. + /// Should this be changed to GetOrCreateClientSession? + /// + /// The user global id. + /// Title of the user. + /// + /// The ip address. + /// The base64 client modulus. + /// The user client global id. + /// + public async ValueTask StoreClientSessionAsync(Guid userId, string userName, string shardId, string ipAddress, string base64ClientModulus, Guid userClientGlobalId) + { + var userSession = await this.GetOrCreateSessionAsync(userId, userName, shardId); + + var existingClientSession = userSession.GetClientSessionWithClientId(userClientGlobalId); + if (existingClientSession != null) + { + userSession.RemoveClientSession(existingClientSession.Id); + } + + var newClientSession = userSession.CreateNewClientSession( + ipAddress, base64ClientModulus, userClientGlobalId); + + await this.UpdateUserSessionAsync(userSession); + + return newClientSession; + } + + /// + /// Updates the UserSession in the cache, or removes expired ones. + /// + /// The user session. + public async ValueTask UpdateUserSessionAsync(UserSessionAsync userSession) + { + var hasSessionExpired = userSession.HasExpired(); + if (hasSessionExpired) + { + LogIfDebug("Session has expired, removing: " + userSession.ToCacheKey()); + await this.cacheClient.RemoveAsync(userSession.ToCacheKey()); + } + else + { + LogIfDebug("Updating session: " + userSession.ToCacheKey()); + await this.cacheClient.ReplaceAsync(userSession.ToCacheKey(), userSession, userSession.ExpiryDate.Value); + } + } + + /// + /// Gets the user session if it exists or null. + /// + /// The user global id. + /// + public async ValueTask GetUserSessionAsync(Guid userId) + { + var cacheKey = UserSession.ToCacheKey(userId); + var bytes = await this.cacheClient.GetAsync(cacheKey); + if (bytes != null) + { + var modelStr = Encoding.UTF8.GetString(bytes); + LogIfDebug("UserSession => " + modelStr); + } + return await this.cacheClient.GetAsync(cacheKey); + } + + /// + /// Gets or create a user session if one doesn't exist. + /// + /// The user global id. + /// Title of the user. + /// + /// + public async ValueTask GetOrCreateSessionAsync(Guid userId, string userName, string shardId) + { + var userSession = await this.GetUserSessionAsync(userId); + if (userSession == null) + { + userSession = new UserSessionAsync(userId, userName, shardId); + + await this.cacheClient.AddAsync(userSession.ToCacheKey(), userSession, + userSession.ExpiryDate.GetValueOrDefault(DateTime.UtcNow) + TimeSpan.FromHours(1)); + } + return userSession; + } + + /// + /// Gets the user client session identified by the id if exists otherwise null. + /// + /// The user global id. + /// The client session id. + /// + public async ValueTask GetUserClientSessionAsync(Guid userId, Guid clientSessionId) + { + var userSession = await this.GetUserSessionAsync(userId); + return userSession != null ? userSession.GetClientSession(clientSessionId) : null; + } + } + +#if !NETCORE + [Serializable /* was required when storing in memcached, not required in Redis */] +#endif + public class UserSessionAsync + { + //Empty constructor required for TypeSerializer + public UserSessionAsync() + { + this.PublicClientSessions = new Dictionary(); + } + + public Guid UserId { get; private set; } + + public string UserName { get; private set; } + + public string ShardId { get; private set; } + + public Dictionary PublicClientSessions { get; private set; } + + public UserSessionAsync(Guid userId, string userName, string shardId) + : this() + { + this.UserId = userId; + this.UserName = userName; + this.ShardId = shardId; + } + + /// + /// Gets the max expiry date of all the users client sessions. + /// If the user has no more active client sessions we can remove them from the cache. + /// + /// The expiry date. + public DateTime? ExpiryDate + { + get + { + DateTime? maxExpiryDate = null; + + foreach (var session in this.PublicClientSessions.Values) + { + if (maxExpiryDate == null || session.ExpiryDate > maxExpiryDate) + { + maxExpiryDate = session.ExpiryDate; + } + } + return maxExpiryDate; + } + } + + /// + /// Creates a new client session for the user. + /// + /// The ip address. + /// The base64 client modulus. + /// The user client global id. + /// + public UserClientSession CreateNewClientSession(string ipAddress, string base64ClientModulus, Guid userClientGlobalId) + { + return this.CreateClientSession(Guid.NewGuid(), ipAddress, base64ClientModulus, userClientGlobalId); + } + + public UserClientSession CreateClientSession(Guid sessionId, string ipAddress, string base64ClientModulus, Guid userClientGlobalId) + { + var clientSession = new UserClientSession( + sessionId, this.UserId, ipAddress, base64ClientModulus, userClientGlobalId); + + this.PublicClientSessions[clientSession.Id] = clientSession; + + return clientSession; + } + + /// + /// Removes the client session. + /// + /// The client session id. + public void RemoveClientSession(Guid clientSessionId) + { + if (this.PublicClientSessions.ContainsKey(clientSessionId)) + { + this.PublicClientSessions.Remove(clientSessionId); + } + } + + public UserClientSession GetClientSessionWithClientId(Guid userClientId) + { + foreach (var entry in this.PublicClientSessions) + { + if (entry.Value.UserClientGlobalId == userClientId) + { + return entry.Value; + } + } + + return null; + } + + /// + /// Verifies this UserSession, removing any expired sessions. + /// Returns true to keep the UserSession in the cache. + /// + /// + /// true if this session has any active client sessions; otherwise, false. + /// + public bool HasExpired() + { + RemoveExpiredSessions(this.PublicClientSessions); + + //If there are no more active client sessions we can remove the entire UserSessions + var sessionHasExpired = + this.ExpiryDate == null //There are no UserClientSessions + || this.ExpiryDate.Value <= DateTime.UtcNow; //The max UserClientSession ExpiryDate has expired + + return sessionHasExpired; + } + + private static void RemoveExpiredSessions(IDictionary clientSessions) + { + var expiredSessionKeys = new List(); + + foreach (var clientSession in clientSessions) + { + if (clientSession.Value.ExpiryDate < DateTime.UtcNow) + { + expiredSessionKeys.Add(clientSession.Key); + } + } + + foreach (var sessionKey in expiredSessionKeys) + { + clientSessions.Remove(sessionKey); + } + } + + public void RemoveAllSessions() + { + this.PublicClientSessions.Clear(); + } + + public UserClientSession GetClientSession(Guid clientSessionId) + { + UserClientSession session; + + if (this.PublicClientSessions.TryGetValue(clientSessionId, out session)) + { + return session; + } + + return null; + } + + public string ToCacheKey() + { + return ToCacheKey(this.UserId); + } + + public static string ToCacheKey(Guid userId) + { + return UrnId.Create(userId.ToString()); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/ValueTypeExamples.Async.cs b/tests/ServiceStack.Redis.Tests/ValueTypeExamples.Async.cs new file mode 100644 index 00000000..c4c00063 --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/ValueTypeExamples.Async.cs @@ -0,0 +1,138 @@ +using NUnit.Framework; +using ServiceStack.Redis.Generic; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ServiceStack.Redis.Tests +{ + [TestFixture, Category("Integration"), Category("Async")] + public class ValueTypeExamplesAsync + { + [SetUp] + public async Task SetUp() + { + await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + { + await redisClient.FlushAllAsync(); + } + } + + [Test] + public async Task Working_with_int_values() + { + const string intKey = "intkey"; + const int intValue = 1; + + //STORING AN INT USING THE BASIC CLIENT + await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + { + await redisClient.SetValueAsync(intKey, intValue.ToString()); + string strGetIntValue = await redisClient.GetValueAsync(intKey); + int toIntValue = int.Parse(strGetIntValue); + + Assert.That(toIntValue, Is.EqualTo(intValue)); + } + + //STORING AN INT USING THE GENERIC CLIENT + await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + { + //Create a generic client that treats all values as ints: + IRedisTypedClientAsync intRedis = redisClient.As(); + + await intRedis.SetValueAsync(intKey, intValue); + var toIntValue = await intRedis.GetValueAsync(intKey); + + Assert.That(toIntValue, Is.EqualTo(intValue)); + } + } + + [Test] + public async Task Working_with_int_list_values() + { + const string intListKey = "intListKey"; + var intValues = new List { 2, 4, 6, 8 }; + + //STORING INTS INTO A LIST USING THE BASIC CLIENT + await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + { + IRedisListAsync strList = redisClient.Lists[intListKey]; + + //storing all int values in the redis list 'intListKey' as strings + await intValues.ForEachAsync(async x => await strList.AddAsync(x.ToString())); + + //retrieve all values again as strings + List strListValues = await strList.ToListAsync(); + + //convert back to list of ints + List toIntValues = strListValues.ConvertAll(x => int.Parse(x)); + + Assert.That(toIntValues, Is.EqualTo(intValues)); + + //delete all items in the list + await strList.ClearAsync(); + } + + //STORING INTS INTO A LIST USING THE GENERIC CLIENT + await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + { + //Create a generic client that treats all values as ints: + IRedisTypedClientAsync intRedis = redisClient.As(); + + IRedisListAsync intList = intRedis.Lists[intListKey]; + + //storing all int values in the redis list 'intListKey' as ints + await intValues.ForEachAsync(async x => await intList.AddAsync(x)); + + List toIntListValues = await intList.ToListAsync(); + + Assert.That(toIntListValues, Is.EqualTo(intValues)); + } + } + + public class IntAndString + { + public int Id { get; set; } + public string Letter { get; set; } + } + + [Test] + public async Task Working_with_Generic_types() + { + await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + { + //Create a typed Redis client that treats all values as IntAndString: + var typedRedis = redisClient.As(); + + var pocoValue = new IntAndString { Id = 1, Letter = "A" }; + await typedRedis.SetValueAsync("pocoKey", pocoValue); + IntAndString toPocoValue = await typedRedis.GetValueAsync("pocoKey"); + + Assert.That(toPocoValue.Id, Is.EqualTo(pocoValue.Id)); + Assert.That(toPocoValue.Letter, Is.EqualTo(pocoValue.Letter)); + + var pocoListValues = new List { + new IntAndString {Id = 2, Letter = "B"}, + new IntAndString {Id = 3, Letter = "C"}, + new IntAndString {Id = 4, Letter = "D"}, + new IntAndString {Id = 5, Letter = "E"}, + }; + + IRedisListAsync pocoList = typedRedis.Lists["pocoListKey"]; + + //Adding all IntAndString objects into the redis list 'pocoListKey' + await pocoListValues.ForEachAsync(async x => await pocoList.AddAsync(x)); + + List toPocoListValues = await pocoList.ToListAsync(); + + for (var i = 0; i < pocoListValues.Count; i++) + { + pocoValue = pocoListValues[i]; + toPocoValue = toPocoListValues[i]; + Assert.That(toPocoValue.Id, Is.EqualTo(pocoValue.Id)); + Assert.That(toPocoValue.Letter, Is.EqualTo(pocoValue.Letter)); + } + } + } + + } +} \ No newline at end of file From 602cfe5cb17b4202b2103b18938377c501dc5509 Mon Sep 17 00:00:00 2001 From: mgravell Date: Wed, 2 Sep 2020 10:51:42 +0100 Subject: [PATCH 008/107] fixup API signatures using interfaces from ServiceStack.Interfaces; build only (not tested) --- .../AsyncInterfaces/IRedisClientAsync.cs | 395 ------------------ .../IRedisClientsManagerAsync.cs | 46 -- .../IRedisHashAsync.Generic.cs | 32 -- .../AsyncInterfaces/IRedisHashAsync.cs | 23 - .../IRedisListAsync.Generic.cs | 64 --- .../AsyncInterfaces/IRedisListAsync.cs | 58 --- .../IRedisNativeClientAsync.cs | 297 ------------- .../AsyncInterfaces/IRedisPipelineAsync.cs | 9 - .../IRedisPipelineSharedAsync.cs | 15 - .../IRedisQueueCompletableOperationAsync.cs | 23 - .../IRedisQueueableOperationAsync.cs | 26 -- .../AsyncInterfaces/IRedisSetAsync.Generic.cs | 42 -- .../AsyncInterfaces/IRedisSetAsync.cs | 47 --- .../IRedisSortedSetAsync.Generic.cs | 51 --- .../AsyncInterfaces/IRedisSortedSetAsync.cs | 46 -- .../IRedisSubscriptionAsync.cs | 61 --- .../AsyncInterfaces/IRedisTransactionAsync.cs | 29 -- .../IRedisTransactionBaseAsync.cs | 11 - .../AsyncInterfaces/IRedisTypedClientAsync.cs | 211 ---------- .../IRedisTypedPipelineAsync.cs | 11 - .../IRedisTypedQueueableOperationAsync.cs | 26 -- .../IRedisTypedTransactionAsync.cs | 28 -- .../Caching/ICacheClientAsync.cs | 116 ----- .../Caching/ICacheClientExtendedAsync.cs | 19 - .../Caching/IRemoveByPatternAsync.cs | 19 - .../Data/IEntityStoreAsync.Generic.cs | 36 -- .../Data/IEntityStoreAsync.cs | 30 -- .../BasicRedisClientManager.Async.cs | 188 ++++++--- .../BufferedReader.Async.cs | 10 +- .../Generic/RedisTypedClient.Async.cs | 36 +- .../PooledRedisClientManager.Async.cs | 8 +- src/ServiceStack.Redis/RedisClient.Async.cs | 136 +++--- .../RedisClientHash.Async.cs | 2 +- .../RedisClientManagerCacheClient.Async.cs | 202 +++++---- .../RedisClientSet.Async.cs | 2 +- .../RedisClientSortedSet.Async.cs | 2 +- src/ServiceStack.Redis/RedisClient_Slowlog.cs | 16 - .../RedisClientsManagerExtensions.Async.cs | 50 ++- src/ServiceStack.Redis/RedisLock.Async.cs | 2 +- .../RedisManagerPool.Async.cs | 8 +- .../RedisNativeClient.Async.cs | 6 +- .../ServiceStack.Redis.csproj | 3 + .../ValueTask_Utils.Async.cs | 50 ++- .../AdhocClientTests.Async.cs | 4 +- .../AsyncImplementationsTests.Async.cs | 13 +- .../BasicRediscClientManagerTests.Async.cs | 13 +- .../Generic/RedisClientHashTestsBase.Async.cs | 5 +- .../Generic/RedisClientListTestExtra.Async.cs | 5 +- .../Generic/RedisClientListTestsBase.Async.cs | 13 +- .../Generic/RedisClientSetTestsBase.Async.cs | 5 +- ...RedisPersistenceProviderTestsBase.Async.cs | 5 +- .../LuaCachedScripts.Async.cs | 280 +++++++------ .../PooledRedisClientManagerTests.Async.cs | 58 +-- .../RedisCacheClientTests.Async.cs | 9 +- .../RedisClientTests.Async.cs | 101 +++-- .../RedisClientTestsBase.Async.cs | 17 + .../RedisGeoTests.Async.cs | 7 +- .../RedisHyperLogTests.Async.cs | 24 +- .../RedisPersistenceProviderTests.Async.cs | 10 +- .../RedisPipelineTests.Async.cs | 4 +- .../RedisPubSubTests.Async.cs | 64 ++- .../RedisTransactionTests.Async.cs | 14 +- .../ShippersExample.Async.cs | 3 +- .../ValueTypeExamples.Async.cs | 19 +- 64 files changed, 842 insertions(+), 2323 deletions(-) delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisClientAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisClientsManagerAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.Generic.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.Generic.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisNativeClientAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineSharedAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueCompletableOperationAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueableOperationAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.Generic.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.Generic.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisSubscriptionAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionBaseAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedClientAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedPipelineAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedQueueableOperationAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedTransactionAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientExtendedAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/IRemoveByPatternAsync.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.Generic.cs delete mode 100644 src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.cs diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisClientAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisClientAsync.cs deleted file mode 100644 index 50aac66f..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisClientAsync.cs +++ /dev/null @@ -1,395 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis/ -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot (demis.bellot@gmail.com) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// - -using ServiceStack.Caching; -using ServiceStack.Data; -using ServiceStack.Model; -using ServiceStack.Redis.Generic; -using ServiceStack.Redis.Pipeline; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace ServiceStack.Redis -{ - public interface IRedisClientAsync - : IEntityStoreAsync, ICacheClientExtendedAsync, IRemoveByPatternAsync - { - /* non-obvious changes from IRedisClient: - - sync API is Save (foreground) and SaveAsync (background); renamed here to ForegroundSaveAsync and BackgroundSaveAsync - to avoid overload problems and accidental swaps from bg to fg when migrating to async API - - RewriteAppendOnlyFileAsync becomes BackgroundRewriteAppendOnlyFileAsync for consistency with the above - - AcquireLockAsync - timeout made an optional arg rather than an overload - - SetValueIf[Not]ExistsAsync - flatten overloads via optional expiry - - move all Dictionary<,> args to IDictionary<,> - - add SlowlogGet / Reset - */ - //Basic Redis Connection operations - - ////Basic Redis Connection Info - //ValueTask this[string key] { get; set; } - - IHasNamed Lists { get; } - IHasNamed Sets { get; } - IHasNamed SortedSets { get; } - IHasNamed Hashes { get; } - - long Db { get; } - ValueTask SelectAsync(long db, CancellationToken cancellationToken = default); - ValueTask DbSizeAsync(CancellationToken cancellationToken = default); - - ValueTask> InfoAsync(CancellationToken cancellationToken = default); - ValueTask GetServerTimeAsync(CancellationToken cancellationToken = default); - ValueTask LastSaveAsync(CancellationToken cancellationToken = default); - string Host { get; } - int Port { get; } - int ConnectTimeout { get; set; } - int RetryTimeout { get; set; } - int RetryCount { get; set; } - int SendTimeout { get; set; } - string Password { get; set; } - bool HadExceptions { get; } - - ValueTask PingAsync(CancellationToken cancellationToken = default); - ValueTask EchoAsync(string text, CancellationToken cancellationToken = default); - - ValueTask CustomAsync(object[] cmdWithArgs, CancellationToken cancellationToken = default); - ValueTask CustomAsync(params object[] cmdWithArgs); // convenience API - - ValueTask ForegroundSaveAsync(CancellationToken cancellationToken = default); - ValueTask BackgroundSaveAsync(CancellationToken cancellationToken = default); - ValueTask ShutdownAsync(CancellationToken cancellationToken = default); - ValueTask ShutdownNoSaveAsync(CancellationToken cancellationToken = default); - ValueTask BackgroundRewriteAppendOnlyFileAsync(CancellationToken cancellationToken = default); - ValueTask FlushDbAsync(CancellationToken cancellationToken = default); - - - ValueTask GetServerRoleAsync(CancellationToken cancellationToken = default); - ValueTask GetServerRoleInfoAsync(CancellationToken cancellationToken = default); - ValueTask GetConfigAsync(string item, CancellationToken cancellationToken = default); - ValueTask SetConfigAsync(string item, string value, CancellationToken cancellationToken = default); - ValueTask SaveConfigAsync(CancellationToken cancellationToken = default); - ValueTask ResetInfoStatsAsync(CancellationToken cancellationToken = default); - - ValueTask GetClientAsync(CancellationToken cancellationToken = default); - ValueTask SetClientAsync(string name, CancellationToken cancellationToken = default); - ValueTask KillClientAsync(string address, CancellationToken cancellationToken = default); - ValueTask KillClientsAsync(string fromAddress = null, string withId = null, RedisClientType? ofType = null, bool? skipMe = null, CancellationToken cancellationToken = default); - ValueTask>> GetClientsInfoAsync(CancellationToken cancellationToken = default); - ValueTask PauseAllClientsAsync(TimeSpan duration, CancellationToken cancellationToken = default); - - ValueTask> GetAllKeysAsync(CancellationToken cancellationToken = default); - - //Fetch fully qualified key for specific Type and Id - string UrnKey(T value); - string UrnKey(object id); - string UrnKey(Type type, object id); - - ValueTask SetAllAsync(IEnumerable keys, IEnumerable values, CancellationToken cancellationToken = default); - ValueTask SetAllAsync(IDictionary map, CancellationToken cancellationToken = default); - ValueTask SetValuesAsync(IDictionary map, CancellationToken cancellationToken = default); - - ValueTask SetValueAsync(string key, string value, CancellationToken cancellationToken = default); - ValueTask SetValueAsync(string key, string value, TimeSpan expireIn, CancellationToken cancellationToken = default); - ValueTask SetValueIfNotExistsAsync(string key, string value, TimeSpan? expireIn = default, CancellationToken cancellationToken = default); - ValueTask SetValueIfExistsAsync(string key, string value, TimeSpan? expireIn = default, CancellationToken cancellationToken = default); - - ValueTask GetValueAsync(string key, CancellationToken cancellationToken = default); - ValueTask GetAndSetValueAsync(string key, string value, CancellationToken cancellationToken = default); - - ValueTask> GetValuesAsync(List keys, CancellationToken cancellationToken = default); - ValueTask> GetValuesAsync(List keys, CancellationToken cancellationToken = default); - ValueTask> GetValuesMapAsync(List keys, CancellationToken cancellationToken = default); - ValueTask> GetValuesMapAsync(List keys, CancellationToken cancellationToken = default); - ValueTask AppendToValueAsync(string key, string value, CancellationToken cancellationToken = default); - ValueTask RenameKeyAsync(string fromName, string toName, CancellationToken cancellationToken = default); - - //store POCOs as hash - ValueTask GetFromHashAsync(object id, CancellationToken cancellationToken = default); - ValueTask StoreAsHashAsync(T entity, CancellationToken cancellationToken = default); - - ValueTask StoreObjectAsync(object entity, CancellationToken cancellationToken = default); - - ValueTask ContainsKeyAsync(string key, CancellationToken cancellationToken = default); - ValueTask RemoveEntryAsync(string[] args, CancellationToken cancellationToken = default); - ValueTask RemoveEntryAsync(params string[] args); // convenience API - ValueTask IncrementValueAsync(string key, CancellationToken cancellationToken = default); - ValueTask IncrementValueByAsync(string key, int count, CancellationToken cancellationToken = default); - ValueTask IncrementValueByAsync(string key, long count, CancellationToken cancellationToken = default); - ValueTask IncrementValueByAsync(string key, double count, CancellationToken cancellationToken = default); - ValueTask DecrementValueAsync(string key, CancellationToken cancellationToken = default); - ValueTask DecrementValueByAsync(string key, int count, CancellationToken cancellationToken = default); - ValueTask> SearchKeysAsync(string pattern, CancellationToken cancellationToken = default); - - ValueTask TypeAsync(string key, CancellationToken cancellationToken = default); - ValueTask GetEntryTypeAsync(string key, CancellationToken cancellationToken = default); - ValueTask GetStringCountAsync(string key, CancellationToken cancellationToken = default); - ValueTask GetRandomKeyAsync(CancellationToken cancellationToken = default); - ValueTask ExpireEntryInAsync(string key, TimeSpan expireIn, CancellationToken cancellationToken = default); - ValueTask ExpireEntryAtAsync(string key, DateTime expireAt, CancellationToken cancellationToken = default); - ValueTask> GetSortedEntryValuesAsync(string key, int startingFrom, int endingAt, CancellationToken cancellationToken = default); - - //Store entities without registering entity ids - ValueTask WriteAllAsync(IEnumerable entities, CancellationToken cancellationToken = default); - - //Scan APIs - IAsyncEnumerable ScanAllKeysAsync(string pattern = null, int pageSize = 1000, CancellationToken cancellationToken = default); - IAsyncEnumerable ScanAllSetItemsAsync(string setId, string pattern = null, int pageSize = 1000, CancellationToken cancellationToken = default); - IAsyncEnumerable> ScanAllSortedSetItemsAsync(string setId, string pattern = null, int pageSize = 1000, CancellationToken cancellationToken = default); - IAsyncEnumerable> ScanAllHashEntriesAsync(string hashId, string pattern = null, int pageSize = 1000, CancellationToken cancellationToken = default); - - //Hyperlog APIs - ValueTask AddToHyperLogAsync(string key, string[] elements, CancellationToken cancellationToken = default); - ValueTask AddToHyperLogAsync(string key, params string[] elements); // convenience API - ValueTask CountHyperLogAsync(string key, CancellationToken cancellationToken = default); - ValueTask MergeHyperLogsAsync(string toKey, string[] fromKeys, CancellationToken cancellationToken = default); - ValueTask MergeHyperLogsAsync(string toKey, params string[] fromKeys); // convenience API - - //GEO APIs - ValueTask AddGeoMemberAsync(string key, double longitude, double latitude, string member, CancellationToken cancellationToken = default); - ValueTask AddGeoMembersAsync(string key, RedisGeo[] geoPoints, CancellationToken cancellationToken = default); - ValueTask AddGeoMembersAsync(string key, params RedisGeo[] geoPoints); // convenience API - ValueTask CalculateDistanceBetweenGeoMembersAsync(string key, string fromMember, string toMember, string unit = null, CancellationToken cancellationToken = default); - ValueTask GetGeohashesAsync(string key, string[] members, CancellationToken cancellationToken = default); - ValueTask GetGeohashesAsync(string key, params string[] members); // convenience API - ValueTask> GetGeoCoordinatesAsync(string key, string[] members, CancellationToken cancellationToken = default); - ValueTask> GetGeoCoordinatesAsync(string key, params string[] members); // convenience API - ValueTask FindGeoMembersInRadiusAsync(string key, double longitude, double latitude, double radius, string unit, CancellationToken cancellationToken = default); - ValueTask> FindGeoResultsInRadiusAsync(string key, double longitude, double latitude, double radius, string unit, int? count = null, bool? sortByNearest = null, CancellationToken cancellationToken = default); - ValueTask FindGeoMembersInRadiusAsync(string key, string member, double radius, string unit, CancellationToken cancellationToken = default); - ValueTask> FindGeoResultsInRadiusAsync(string key, string member, double radius, string unit, int? count = null, bool? sortByNearest = null, CancellationToken cancellationToken = default); - - /// - /// Returns a high-level typed client API - /// - /// - IRedisTypedClientAsync As(); - - ValueTask CreateTransactionAsync(CancellationToken cancellationToken = default); - IRedisPipelineAsync CreatePipeline(); - - ValueTask AcquireLockAsync(string key, TimeSpan? timeOut = default, CancellationToken cancellationToken = default); - - #region Redis pubsub - - ValueTask WatchAsync(string[] keys, CancellationToken cancellationToken = default); - ValueTask WatchAsync(params string[] keys); // convenience API - ValueTask UnWatchAsync(CancellationToken cancellationToken = default); - ValueTask CreateSubscriptionAsync(CancellationToken cancellationToken = default); - ValueTask PublishMessageAsync(string toChannel, string message, CancellationToken cancellationToken = default); - - #endregion - - - #region Set operations - - ValueTask> GetAllItemsFromSetAsync(string setId, CancellationToken cancellationToken = default); - ValueTask AddItemToSetAsync(string setId, string item, CancellationToken cancellationToken = default); - ValueTask AddRangeToSetAsync(string setId, List items, CancellationToken cancellationToken = default); - ValueTask RemoveItemFromSetAsync(string setId, string item, CancellationToken cancellationToken = default); - ValueTask PopItemFromSetAsync(string setId, CancellationToken cancellationToken = default); - ValueTask> PopItemsFromSetAsync(string setId, int count, CancellationToken cancellationToken = default); - ValueTask MoveBetweenSetsAsync(string fromSetId, string toSetId, string item, CancellationToken cancellationToken = default); - ValueTask GetSetCountAsync(string setId, CancellationToken cancellationToken = default); - ValueTask SetContainsItemAsync(string setId, string item, CancellationToken cancellationToken = default); - ValueTask> GetIntersectFromSetsAsync(string[] setIds, CancellationToken cancellationToken = default); - ValueTask> GetIntersectFromSetsAsync(params string[] setIds); // convenience API - ValueTask StoreIntersectFromSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); - ValueTask StoreIntersectFromSetsAsync(string intoSetId, params string[] setIds); // convenience API - ValueTask> GetUnionFromSetsAsync(string[] setIds, CancellationToken cancellationToken = default); - ValueTask> GetUnionFromSetsAsync(params string[] setIds); // convenience API - ValueTask StoreUnionFromSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); - ValueTask StoreUnionFromSetsAsync(string intoSetId, params string[] setIds); // convenience API - ValueTask> GetDifferencesFromSetAsync(string fromSetId, string[] withSetIds, CancellationToken cancellationToken = default); - ValueTask> GetDifferencesFromSetAsync(string fromSetId, params string[] withSetIds); // convenience API - ValueTask StoreDifferencesFromSetAsync(string intoSetId, string fromSetId, string[] withSetIds, CancellationToken cancellationToken = default); - ValueTask StoreDifferencesFromSetAsync(string intoSetId, string fromSetId, params string[] withSetIds); // convenience API - ValueTask GetRandomItemFromSetAsync(string setId, CancellationToken cancellationToken = default); - - #endregion - - - #region List operations - - ValueTask> GetAllItemsFromListAsync(string listId, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromListAsync(string listId, int startingFrom, int endingAt, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedListAsync(string listId, int startingFrom, int endingAt, CancellationToken cancellationToken = default); - ValueTask> GetSortedItemsFromListAsync(string listId, SortOptions sortOptions, CancellationToken cancellationToken = default); - ValueTask AddItemToListAsync(string listId, string value, CancellationToken cancellationToken = default); - ValueTask AddRangeToListAsync(string listId, List values, CancellationToken cancellationToken = default); - ValueTask PrependItemToListAsync(string listId, string value, CancellationToken cancellationToken = default); - ValueTask PrependRangeToListAsync(string listId, List values, CancellationToken cancellationToken = default); - - ValueTask RemoveAllFromListAsync(string listId, CancellationToken cancellationToken = default); - ValueTask RemoveStartFromListAsync(string listId, CancellationToken cancellationToken = default); - ValueTask BlockingRemoveStartFromListAsync(string listId, TimeSpan? timeOut, CancellationToken cancellationToken = default); - ValueTask BlockingRemoveStartFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken cancellationToken = default); - ValueTask RemoveEndFromListAsync(string listId, CancellationToken cancellationToken = default); - ValueTask TrimListAsync(string listId, int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken = default); - ValueTask RemoveItemFromListAsync(string listId, string value, CancellationToken cancellationToken = default); - ValueTask RemoveItemFromListAsync(string listId, string value, int noOfMatches, CancellationToken cancellationToken = default); - ValueTask GetListCountAsync(string listId, CancellationToken cancellationToken = default); - ValueTask GetItemFromListAsync(string listId, int listIndex, CancellationToken cancellationToken = default); - ValueTask SetItemInListAsync(string listId, int listIndex, string value, CancellationToken cancellationToken = default); - - //Queue operations - ValueTask EnqueueItemOnListAsync(string listId, string value, CancellationToken cancellationToken = default); - ValueTask DequeueItemFromListAsync(string listId, CancellationToken cancellationToken = default); - ValueTask BlockingDequeueItemFromListAsync(string listId, TimeSpan? timeOut, CancellationToken cancellationToken = default); - ValueTask BlockingDequeueItemFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken cancellationToken = default); - - //Stack operations - ValueTask PushItemToListAsync(string listId, string value, CancellationToken cancellationToken = default); - ValueTask PopItemFromListAsync(string listId, CancellationToken cancellationToken = default); - ValueTask BlockingPopItemFromListAsync(string listId, TimeSpan? timeOut, CancellationToken cancellationToken = default); - ValueTask BlockingPopItemFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken cancellationToken = default); - ValueTask PopAndPushItemBetweenListsAsync(string fromListId, string toListId, CancellationToken cancellationToken = default); - ValueTask BlockingPopAndPushItemBetweenListsAsync(string fromListId, string toListId, TimeSpan? timeOut, CancellationToken cancellationToken = default); - - #endregion - - - #region Sorted Set operations - - ValueTask AddItemToSortedSetAsync(string setId, string value, CancellationToken cancellationToken = default); - ValueTask AddItemToSortedSetAsync(string setId, string value, double score, CancellationToken cancellationToken = default); - ValueTask AddRangeToSortedSetAsync(string setId, List values, double score, CancellationToken cancellationToken = default); - ValueTask AddRangeToSortedSetAsync(string setId, List values, long score, CancellationToken cancellationToken = default); - ValueTask RemoveItemFromSortedSetAsync(string setId, string value, CancellationToken cancellationToken = default); - ValueTask RemoveItemsFromSortedSetAsync(string setId, List values, CancellationToken cancellationToken = default); - ValueTask PopItemWithLowestScoreFromSortedSetAsync(string setId, CancellationToken cancellationToken = default); - ValueTask PopItemWithHighestScoreFromSortedSetAsync(string setId, CancellationToken cancellationToken = default); - ValueTask SortedSetContainsItemAsync(string setId, string value, CancellationToken cancellationToken = default); - ValueTask IncrementItemInSortedSetAsync(string setId, string value, double incrementBy, CancellationToken cancellationToken = default); - ValueTask IncrementItemInSortedSetAsync(string setId, string value, long incrementBy, CancellationToken cancellationToken = default); - ValueTask GetItemIndexInSortedSetAsync(string setId, string value, CancellationToken cancellationToken = default); - ValueTask GetItemIndexInSortedSetDescAsync(string setId, string value, CancellationToken cancellationToken = default); - ValueTask> GetAllItemsFromSortedSetAsync(string setId, CancellationToken cancellationToken = default); - ValueTask> GetAllItemsFromSortedSetDescAsync(string setId, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetDescAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken = default); - ValueTask> GetAllWithScoresFromSortedSetAsync(string setId, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetDescAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask RemoveRangeFromSortedSetAsync(string setId, int minRank, int maxRank, CancellationToken cancellationToken = default); - ValueTask RemoveRangeFromSortedSetByScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask RemoveRangeFromSortedSetByScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); - ValueTask GetSortedSetCountAsync(string setId, CancellationToken cancellationToken = default); - ValueTask GetSortedSetCountAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); - ValueTask GetSortedSetCountAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); - ValueTask GetSortedSetCountAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask GetItemScoreInSortedSetAsync(string setId, string value, CancellationToken cancellationToken = default); - ValueTask StoreIntersectFromSortedSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); - ValueTask StoreIntersectFromSortedSetsAsync(string intoSetId, params string[] setIds); // convenience API - ValueTask StoreIntersectFromSortedSetsAsync(string intoSetId, string[] setIds, string[] args, CancellationToken cancellationToken = default); - ValueTask StoreUnionFromSortedSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); - ValueTask StoreUnionFromSortedSetsAsync(string intoSetId, params string[] setIds); // convenience API - ValueTask StoreUnionFromSortedSetsAsync(string intoSetId, string[] setIds, string[] args, CancellationToken cancellationToken = default); - ValueTask> SearchSortedSetAsync(string setId, string start = null, string end = null, int? skip = null, int? take = null, CancellationToken cancellationToken = default); - ValueTask SearchSortedSetCountAsync(string setId, string start = null, string end = null, CancellationToken cancellationToken = default); - ValueTask RemoveRangeFromSortedSetBySearchAsync(string setId, string start = null, string end = null, CancellationToken cancellationToken = default); - - #endregion - - - #region Hash operations - - ValueTask HashContainsEntryAsync(string hashId, string key, CancellationToken cancellationToken = default); - ValueTask SetEntryInHashAsync(string hashId, string key, string value, CancellationToken cancellationToken = default); - ValueTask SetEntryInHashIfNotExistsAsync(string hashId, string key, string value, CancellationToken cancellationToken = default); - ValueTask SetRangeInHashAsync(string hashId, IEnumerable> keyValuePairs, CancellationToken cancellationToken = default); - ValueTask IncrementValueInHashAsync(string hashId, string key, int incrementBy, CancellationToken cancellationToken = default); - ValueTask IncrementValueInHashAsync(string hashId, string key, double incrementBy, CancellationToken cancellationToken = default); - ValueTask GetValueFromHashAsync(string hashId, string key, CancellationToken cancellationToken = default); - ValueTask> GetValuesFromHashAsync(string hashId, string[] keys, CancellationToken cancellationToken = default); - ValueTask> GetValuesFromHashAsync(string hashId, params string[] keys); // convenience API - ValueTask RemoveEntryFromHashAsync(string hashId, string key, CancellationToken cancellationToken = default); - ValueTask GetHashCountAsync(string hashId, CancellationToken cancellationToken = default); - ValueTask> GetHashKeysAsync(string hashId, CancellationToken cancellationToken = default); - ValueTask> GetHashValuesAsync(string hashId, CancellationToken cancellationToken = default); - ValueTask> GetAllEntriesFromHashAsync(string hashId, CancellationToken cancellationToken = default); - - #endregion - - - #region Eval/Lua operations - - ValueTask ExecCachedLuaAsync(string scriptBody, Func> scriptSha1, CancellationToken cancellationToken = default); - - ValueTask ExecLuaAsync(string body, string[] args, CancellationToken cancellationToken = default); - ValueTask ExecLuaAsync(string body, params string[] args); // conveinence API - ValueTask ExecLuaAsync(string luaBody, string[] keys, string[] args, CancellationToken cancellationToken = default); - ValueTask ExecLuaShaAsync(string sha1, string[] args, CancellationToken cancellationToken = default); - ValueTask ExecLuaShaAsync(string sha1, params string[] args); // convenience API - ValueTask ExecLuaShaAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken = default); - - ValueTask ExecLuaAsStringAsync(string luaBody, string[] args, CancellationToken cancellationToken = default); - ValueTask ExecLuaAsStringAsync(string luaBody, params string[] args); // convenience API - ValueTask ExecLuaAsStringAsync(string luaBody, string[] keys, string[] args, CancellationToken cancellationToken = default); - ValueTask ExecLuaShaAsStringAsync(string sha1, string[] args, CancellationToken cancellationToken = default); - ValueTask ExecLuaShaAsStringAsync(string sha1, params string[] args); // convenience API - ValueTask ExecLuaShaAsStringAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken = default); - - ValueTask ExecLuaAsIntAsync(string luaBody, string[] args, CancellationToken cancellationToken = default); - ValueTask ExecLuaAsIntAsync(string luaBody, params string[] args); // convenience API - ValueTask ExecLuaAsIntAsync(string luaBody, string[] keys, string[] args, CancellationToken cancellationToken = default); - ValueTask ExecLuaShaAsIntAsync(string sha1, string[] args, CancellationToken cancellationToken = default); - ValueTask ExecLuaShaAsIntAsync(string sha1, params string[] args); // convenience API - ValueTask ExecLuaShaAsIntAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken = default); - - ValueTask> ExecLuaAsListAsync(string luaBody, string[] args, CancellationToken cancellationToken = default); - ValueTask> ExecLuaAsListAsync(string luaBody, params string[] args); // convenience API - ValueTask> ExecLuaAsListAsync(string luaBody, string[] keys, string[] args, CancellationToken cancellationToken = default); - ValueTask> ExecLuaShaAsListAsync(string sha1, string[] args, CancellationToken cancellationToken = default); - ValueTask> ExecLuaShaAsListAsync(string sha1, params string[] args); // convenience API - ValueTask> ExecLuaShaAsListAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken = default); - - ValueTask CalculateSha1Async(string luaBody, CancellationToken cancellationToken = default); - - ValueTask HasLuaScriptAsync(string sha1Ref, CancellationToken cancellationToken = default); - ValueTask> WhichLuaScriptsExistsAsync(string[] sha1Refs, CancellationToken cancellationToken = default); - ValueTask> WhichLuaScriptsExistsAsync(params string[] sha1Refs); // convenience API - ValueTask RemoveAllLuaScriptsAsync(CancellationToken cancellationToken = default); - ValueTask KillRunningLuaScriptAsync(CancellationToken cancellationToken = default); - ValueTask LoadLuaScriptAsync(string body, CancellationToken cancellationToken = default); - - #endregion - - ValueTask SlowlogResetAsync(CancellationToken cancellationToken = default); - ValueTask GetSlowlogAsync(int? numberOfRecords = null, CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisClientsManagerAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisClientsManagerAsync.cs deleted file mode 100644 index f731af18..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisClientsManagerAsync.cs +++ /dev/null @@ -1,46 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot (demis.bellot@gmail.com) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// - -using System; -using System.Threading; -using System.Threading.Tasks; -using ServiceStack.Caching; - -namespace ServiceStack.Redis -{ - public interface IRedisClientsManagerAsync : IAsyncDisposable - { - /// - /// Returns a Read/Write client (The default) using the hosts defined in ReadWriteHosts - /// - /// - ValueTask GetClientAsync(CancellationToken cancellationToken = default); - - /// - /// Returns a ReadOnly client using the hosts defined in ReadOnlyHosts. - /// - /// - ValueTask GetReadOnlyClientAsync(CancellationToken cancellationToken = default); - - /// - /// Returns a Read/Write ICacheClient (The default) using the hosts defined in ReadWriteHosts - /// - /// - ValueTask GetCacheClientAsync(CancellationToken cancellationToken = default); - - /// - /// Returns a ReadOnly ICacheClient using the hosts defined in ReadOnlyHosts. - /// - /// - ValueTask GetReadOnlyCacheClientAsync(CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.Generic.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.Generic.cs deleted file mode 100644 index 3e192f94..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.Generic.cs +++ /dev/null @@ -1,32 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot (demis.bellot@gmail.com) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using ServiceStack.Model; - -namespace ServiceStack.Redis.Generic -{ - public interface IRedisHashAsync : IAsyncEnumerable>, IHasStringId - { - ValueTask> GetAllAsync(CancellationToken cancellationToken = default); - - ValueTask CountAsync(CancellationToken cancellationToken = default); - ValueTask AddAsync(KeyValuePair item, CancellationToken cancellationToken = default); - ValueTask AddAsync(TKey key, TValue value, CancellationToken cancellationToken = default); - ValueTask ClearAsync(CancellationToken cancellationToken = default); - ValueTask ContainsKeyAsync(TKey key, CancellationToken cancellationToken = default); - ValueTask RemoveAsync(TKey key, CancellationToken cancellationToken = default); - } - -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.cs deleted file mode 100644 index 3ff0e9e4..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisHashAsync.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using ServiceStack.Model; - -namespace ServiceStack.Redis -{ - public interface IRedisHashAsync - : IAsyncEnumerable>, IHasStringId - { - ValueTask AddIfNotExistsAsync(KeyValuePair item, CancellationToken cancellationToken = default); - ValueTask AddRangeAsync(IEnumerable> items, CancellationToken cancellationToken = default); - ValueTask IncrementValueAsync(string key, int incrementBy, CancellationToken cancellationToken = default); - - // shim the basic ICollection etc APIs - ValueTask CountAsync(CancellationToken cancellationToken = default); - ValueTask AddAsync(KeyValuePair item, CancellationToken cancellationToken = default); - ValueTask AddAsync(string key, string value, CancellationToken cancellationToken = default); - ValueTask ClearAsync(CancellationToken cancellationToken = default); - ValueTask ContainsKeyAsync(string key, CancellationToken cancellationToken = default); - ValueTask RemoveAsync(string key, CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.Generic.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.Generic.cs deleted file mode 100644 index 1234b924..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.Generic.cs +++ /dev/null @@ -1,64 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot Async(demis.bellot@gmail.com, CancellationToken cancellationToken = default) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using ServiceStack.Model; - -namespace ServiceStack.Redis.Generic -{ - /// - /// Wrap the common redis list operations under a IList[string] interface. - /// - - public interface IRedisListAsync - : IAsyncEnumerable, IHasStringId - { - ValueTask CountAsync(CancellationToken cancellationToken = default); - ValueTask> GetAllAsync(CancellationToken cancellationToken = default); - ValueTask> GetRangeAsync(int startingFrom, int endingAt, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedListAsync(int startingFrom, int endingAt, CancellationToken cancellationToken = default); - ValueTask RemoveAllAsync(CancellationToken cancellationToken = default); - ValueTask TrimAsync(int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken = default); - ValueTask RemoveValueAsync(T value, CancellationToken cancellationToken = default); - ValueTask RemoveValueAsync(T value, int noOfMatches, CancellationToken cancellationToken = default); - - ValueTask AddRangeAsync(IEnumerable values, CancellationToken cancellationToken = default); - ValueTask AppendAsync(T value, CancellationToken cancellationToken = default); - ValueTask PrependAsync(T value, CancellationToken cancellationToken = default); - ValueTask RemoveStartAsync(CancellationToken cancellationToken = default); - ValueTask BlockingRemoveStartAsync(TimeSpan? timeOut, CancellationToken cancellationToken = default); - ValueTask RemoveEndAsync(CancellationToken cancellationToken = default); - - ValueTask EnqueueAsync(T value, CancellationToken cancellationToken = default); - ValueTask DequeueAsync(CancellationToken cancellationToken = default); - ValueTask BlockingDequeueAsync(TimeSpan? timeOut, CancellationToken cancellationToken = default); - - ValueTask PushAsync(T value, CancellationToken cancellationToken = default); - ValueTask PopAsync(CancellationToken cancellationToken = default); - ValueTask BlockingPopAsync(TimeSpan? timeOut, CancellationToken cancellationToken = default); - ValueTask PopAndPushAsync(IRedisListAsync toList, CancellationToken cancellationToken = default); - - - ValueTask RemoveAsync(T item, CancellationToken cancellationToken = default); - ValueTask AddAsync(T item, CancellationToken cancellationToken = default); - ValueTask RemoveAtAsync(int index, CancellationToken cancellationToken = default); - ValueTask ContainsAsync(T item, CancellationToken cancellationToken = default); - ValueTask ClearAsync(CancellationToken cancellationToken = default); - ValueTask IndexOfAsync(T item, CancellationToken cancellationToken = default); - - ValueTask ElementAtAsync(int index, CancellationToken cancellationToken = default); - ValueTask SetValueAsync(int index, T value, CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.cs deleted file mode 100644 index 9a9f98fc..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisListAsync.cs +++ /dev/null @@ -1,58 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot Async(demis.bellot@gmail.com) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using ServiceStack.Model; - -namespace ServiceStack.Redis -{ - public interface IRedisListAsync - : IAsyncEnumerable, IHasStringId - { - ValueTask CountAsync(CancellationToken cancellationToken = default); - ValueTask> GetAllAsync(CancellationToken cancellationToken = default); - ValueTask> GetRangeAsync(int startingFrom, int endingAt, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedListAsync(int startingFrom, int endingAt, CancellationToken cancellationToken = default); - ValueTask RemoveAllAsync(CancellationToken cancellationToken = default); - ValueTask TrimAsync(int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken = default); - ValueTask RemoveValueAsync(string value, CancellationToken cancellationToken = default); - ValueTask RemoveValueAsync(string value, int noOfMatches, CancellationToken cancellationToken = default); - - ValueTask PrependAsync(string value, CancellationToken cancellationToken = default); - ValueTask AppendAsync(string value, CancellationToken cancellationToken = default); - ValueTask RemoveStartAsync(CancellationToken cancellationToken = default); - ValueTask BlockingRemoveStartAsync(TimeSpan? timeOut, CancellationToken cancellationToken = default); - ValueTask RemoveEndAsync(CancellationToken cancellationToken = default); - - ValueTask EnqueueAsync(string value, CancellationToken cancellationToken = default); - ValueTask DequeueAsync(CancellationToken cancellationToken = default); - ValueTask BlockingDequeueAsync(TimeSpan? timeOut, CancellationToken cancellationToken = default); - - ValueTask PushAsync(string value, CancellationToken cancellationToken = default); - ValueTask PopAsync(CancellationToken cancellationToken = default); - ValueTask BlockingPopAsync(TimeSpan? timeOut, CancellationToken cancellationToken = default); - ValueTask PopAndPushAsync(IRedisListAsync toList, CancellationToken cancellationToken = default); - - ValueTask RemoveAsync(string item, CancellationToken cancellationToken = default); - ValueTask AddAsync(string item, CancellationToken cancellationToken = default); - ValueTask RemoveAtAsync(int index, CancellationToken cancellationToken = default); - ValueTask ContainsAsync(string item, CancellationToken cancellationToken = default); - ValueTask ClearAsync(CancellationToken cancellationToken = default); - ValueTask IndexOfAsync(string item, CancellationToken cancellationToken = default); - - ValueTask ElementAtAsync(int index, CancellationToken cancellationToken = default); - ValueTask SetValueAsync(int index, string value, CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisNativeClientAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisNativeClientAsync.cs deleted file mode 100644 index 5c26b511..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisNativeClientAsync.cs +++ /dev/null @@ -1,297 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis/ -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot (demis.bellot@gmail.com) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace ServiceStack.Redis -{ - public interface IRedisNativeClientAsync : IAsyncDisposable - { - /* - non-obvious changes: - - Db is get only; addition of SelectAsync - - LastSave is now a method - - shutdown now takes nosave arg - - expose the optional args on Set - - add SlowlogGet and SlowlogReset - - add ZCount - */ - - //Redis utility operations - ValueTask> InfoAsync(CancellationToken cancellationToken = default); - long Db { get; } - ValueTask SelectAsync(long db, CancellationToken cancellationToken = default); - - ValueTask DbSizeAsync(CancellationToken cancellationToken = default); - ValueTask LastSaveAsync(CancellationToken cancellationToken = default); - ValueTask SaveAsync(CancellationToken cancellationToken = default); - ValueTask BgSaveAsync(CancellationToken cancellationToken = default); - ValueTask ShutdownAsync(bool noSave = false, CancellationToken cancellationToken = default); - ValueTask BgRewriteAofAsync(CancellationToken cancellationToken = default); - ValueTask QuitAsync(CancellationToken cancellationToken = default); - ValueTask FlushDbAsync(CancellationToken cancellationToken = default); - ValueTask FlushAllAsync(CancellationToken cancellationToken = default); - ValueTask PingAsync(CancellationToken cancellationToken = default); - ValueTask EchoAsync(string text, CancellationToken cancellationToken = default); - ValueTask SlaveOfAsync(string hostname, int port, CancellationToken cancellationToken = default); - ValueTask SlaveOfNoOneAsync(CancellationToken cancellationToken = default); - ValueTask ConfigGetAsync(string pattern, CancellationToken cancellationToken = default); - ValueTask ConfigSetAsync(string item, byte[] value, CancellationToken cancellationToken = default); - ValueTask ConfigResetStatAsync(CancellationToken cancellationToken = default); - ValueTask ConfigRewriteAsync(CancellationToken cancellationToken = default); - ValueTask TimeAsync(CancellationToken cancellationToken = default); - ValueTask DebugSegfaultAsync(CancellationToken cancellationToken = default); - ValueTask DumpAsync(string key, CancellationToken cancellationToken = default); - ValueTask RestoreAsync(string key, long expireMs, byte[] dumpValue, CancellationToken cancellationToken = default); - ValueTask MigrateAsync(string host, int port, string key, int destinationDb, long timeoutMs, CancellationToken cancellationToken = default); - ValueTask MoveAsync(string key, int db, CancellationToken cancellationToken = default); - ValueTask ObjectIdleTimeAsync(string key, CancellationToken cancellationToken = default); - ValueTask RoleAsync(CancellationToken cancellationToken = default); - - ValueTask RawCommandAsync(object[] cmdWithArgs, CancellationToken cancellationToken = default); - ValueTask RawCommandAsync(params object[] cmdWithArgs); // convenience API - ValueTask RawCommandAsync(byte[][] cmdWithBinaryArgs, CancellationToken cancellationToken = default); - ValueTask RawCommandAsync(params byte[][] cmdWithBinaryArgs); // convenience API - - ValueTask ClientGetNameAsync(CancellationToken cancellationToken = default); - ValueTask ClientSetNameAsync(string client, CancellationToken cancellationToken = default); - ValueTask ClientKillAsync(string host, CancellationToken cancellationToken = default); - ValueTask ClientKillAsync(string addr = null, string id = null, string type = null, string skipMe = null, CancellationToken cancellationToken = default); - ValueTask ClientListAsync(CancellationToken cancellationToken = default); - ValueTask ClientPauseAsync(int timeOutMs, CancellationToken cancellationToken = default); - - //Common key-value Redis operations - ValueTask KeysAsync(string pattern, CancellationToken cancellationToken = default); - ValueTask TypeAsync(string key, CancellationToken cancellationToken = default); - ValueTask ExistsAsync(string key, CancellationToken cancellationToken = default); - ValueTask StrLenAsync(string key, CancellationToken cancellationToken = default); - ValueTask SetAsync(string key, byte[] value, bool exists, long expirySeconds = 0, long expiryMilliseconds = 0, CancellationToken cancellationToken = default); - ValueTask SetAsync(string key, byte[] value, long expirySeconds = 0, long expiryMilliseconds = 0, CancellationToken cancellationToken = default); - ValueTask SetExAsync(string key, int expireInSeconds, byte[] value, CancellationToken cancellationToken = default); - ValueTask PersistAsync(string key, CancellationToken cancellationToken = default); - ValueTask PSetExAsync(string key, long expireInMs, byte[] value, CancellationToken cancellationToken = default); - ValueTask SetNXAsync(string key, byte[] value, CancellationToken cancellationToken = default); - ValueTask MSetAsync(byte[][] keys, byte[][] values, CancellationToken cancellationToken = default); - ValueTask MSetAsync(string[] keys, byte[][] values, CancellationToken cancellationToken = default); - ValueTask MSetNxAsync(byte[][] keys, byte[][] values, CancellationToken cancellationToken = default); - ValueTask MSetNxAsync(string[] keys, byte[][] values, CancellationToken cancellationToken = default); - ValueTask GetAsync(string key, CancellationToken cancellationToken = default); - ValueTask GetSetAsync(string key, byte[] value, CancellationToken cancellationToken = default); - ValueTask MGetAsync(byte[][] keysAndArgs, CancellationToken cancellationToken = default); - ValueTask MGetAsync(params byte[][] keysAndArgs); // convenience API - ValueTask MGetAsync(string[] keys, CancellationToken cancellationToken = default); - ValueTask MGetAsync(params string[] keys); // convenience API - ValueTask DelAsync(string key, CancellationToken cancellationToken = default); - ValueTask DelAsync(string[] keys, CancellationToken cancellationToken = default); - ValueTask DelAsync(params string[] keys); // convenience API - ValueTask IncrAsync(string key, CancellationToken cancellationToken = default); - ValueTask IncrByAsync(string key, long incrBy, CancellationToken cancellationToken = default); - ValueTask IncrByFloatAsync(string key, double incrBy, CancellationToken cancellationToken = default); - ValueTask DecrAsync(string key, CancellationToken cancellationToken = default); - ValueTask DecrByAsync(string key, long decrBy, CancellationToken cancellationToken = default); - ValueTask AppendAsync(string key, byte[] value, CancellationToken cancellationToken = default); - ValueTask GetRangeAsync(string key, int fromIndex, int toIndex, CancellationToken cancellationToken = default); - ValueTask SetRangeAsync(string key, int offset, byte[] value, CancellationToken cancellationToken = default); - ValueTask GetBitAsync(string key, int offset, CancellationToken cancellationToken = default); - ValueTask SetBitAsync(string key, int offset, int value, CancellationToken cancellationToken = default); - ValueTask BitCountAsync(string key, CancellationToken cancellationToken = default); - ValueTask RandomKeyAsync(CancellationToken cancellationToken = default); - ValueTask RenameAsync(string oldKeyname, string newKeyname, CancellationToken cancellationToken = default); - ValueTask RenameNxAsync(string oldKeyname, string newKeyname, CancellationToken cancellationToken = default); - ValueTask ExpireAsync(string key, int seconds, CancellationToken cancellationToken = default); - ValueTask PExpireAsync(string key, long ttlMs, CancellationToken cancellationToken = default); - ValueTask ExpireAtAsync(string key, long unixTime, CancellationToken cancellationToken = default); - ValueTask PExpireAtAsync(string key, long unixTimeMs, CancellationToken cancellationToken = default); - ValueTask TtlAsync(string key, CancellationToken cancellationToken = default); - ValueTask PTtlAsync(string key, CancellationToken cancellationToken = default); - - //Scan APIs - ValueTask ScanAsync(ulong cursor, int count = 10, string match = null, CancellationToken cancellationToken = default); - ValueTask SScanAsync(string setId, ulong cursor, int count = 10, string match = null, CancellationToken cancellationToken = default); - ValueTask ZScanAsync(string setId, ulong cursor, int count = 10, string match = null, CancellationToken cancellationToken = default); - ValueTask HScanAsync(string hashId, ulong cursor, int count = 10, string match = null, CancellationToken cancellationToken = default); - - //Hyperlog - ValueTask PfAddAsync(string key, byte[][] elements, CancellationToken cancellationToken = default); - ValueTask PfAddAsync(string key, params byte[][] elements); // convenience API - ValueTask PfCountAsync(string key, CancellationToken cancellationToken = default); - ValueTask PfMergeAsync(string toKeyId, string[] fromKeys, CancellationToken cancellationToken = default); - ValueTask PfMergeAsync(string toKeyId, params string[] fromKeys); - - //Redis Sort operation Async(works on lists, sets or hashes) - ValueTask SortAsync(string listOrSetId, SortOptions sortOptions, CancellationToken cancellationToken = default); - - //Redis List operations - ValueTask LRangeAsync(string listId, int startingFrom, int endingAt, CancellationToken cancellationToken = default); - ValueTask RPushAsync(string listId, byte[] value, CancellationToken cancellationToken = default); - ValueTask RPushXAsync(string listId, byte[] value, CancellationToken cancellationToken = default); - ValueTask LPushAsync(string listId, byte[] value, CancellationToken cancellationToken = default); - ValueTask LPushXAsync(string listId, byte[] value, CancellationToken cancellationToken = default); - ValueTask LTrimAsync(string listId, int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken = default); - ValueTask LRemAsync(string listId, int removeNoOfMatches, byte[] value, CancellationToken cancellationToken = default); - ValueTask LLenAsync(string listId, CancellationToken cancellationToken = default); - ValueTask LIndexAsync(string listId, int listIndex, CancellationToken cancellationToken = default); - ValueTask LInsertAsync(string listId, bool insertBefore, byte[] pivot, byte[] value, CancellationToken cancellationToken = default); - ValueTask LSetAsync(string listId, int listIndex, byte[] value, CancellationToken cancellationToken = default); - ValueTask LPopAsync(string listId, CancellationToken cancellationToken = default); - ValueTask RPopAsync(string listId, CancellationToken cancellationToken = default); - ValueTask BLPopAsync(string listId, int timeOutSecs, CancellationToken cancellationToken = default); - ValueTask BLPopAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken = default); - ValueTask BLPopValueAsync(string listId, int timeOutSecs, CancellationToken cancellationToken = default); - ValueTask BLPopValueAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken = default); - ValueTask BRPopAsync(string listId, int timeOutSecs, CancellationToken cancellationToken = default); - ValueTask BRPopAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken = default); - ValueTask RPopLPushAsync(string fromListId, string toListId, CancellationToken cancellationToken = default); - ValueTask BRPopValueAsync(string listId, int timeOutSecs, CancellationToken cancellationToken = default); - ValueTask BRPopValueAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken = default); - ValueTask BRPopLPushAsync(string fromListId, string toListId, int timeOutSecs, CancellationToken cancellationToken = default); - - //Redis Set operations - ValueTask SMembersAsync(string setId, CancellationToken cancellationToken = default); - ValueTask SAddAsync(string setId, byte[] value, CancellationToken cancellationToken = default); - ValueTask SAddAsync(string setId, byte[][] value, CancellationToken cancellationToken = default); - ValueTask SRemAsync(string setId, byte[] value, CancellationToken cancellationToken = default); - ValueTask SPopAsync(string setId, CancellationToken cancellationToken = default); - ValueTask SPopAsync(string setId, int count, CancellationToken cancellationToken = default); - ValueTask SMoveAsync(string fromSetId, string toSetId, byte[] value, CancellationToken cancellationToken = default); - ValueTask SCardAsync(string setId, CancellationToken cancellationToken = default); - ValueTask SIsMemberAsync(string setId, byte[] value, CancellationToken cancellationToken = default); - ValueTask SInterAsync(string[] setIds, CancellationToken cancellationToken = default); - ValueTask SInterAsync(params string[] setIds); // convenience API - ValueTask SInterStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); - ValueTask SInterStoreAsync(string intoSetId, params string[] setIds); // convenience API - ValueTask SUnionAsync(string[] setIds, CancellationToken cancellationToken = default); - ValueTask SUnionAsync(params string[] setIds); // convenience API - ValueTask SUnionStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); - ValueTask SUnionStoreAsync(string intoSetId, params string[] setIds); // convenience API - ValueTask SDiffAsync(string fromSetId, string[] withSetIds, CancellationToken cancellationToken = default); - ValueTask SDiffAsync(string fromSetId, params string[] withSetIds); // convenience API - ValueTask SDiffStoreAsync(string intoSetId, string fromSetId, string[] withSetIds, CancellationToken cancellationToken = default); - ValueTask SDiffStoreAsync(string intoSetId, string fromSetId, params string[] withSetIds); // convenience API - ValueTask SRandMemberAsync(string setId, CancellationToken cancellationToken = default); - - - ////Redis Sorted Set operations - ValueTask ZAddAsync(string setId, double score, byte[] value, CancellationToken cancellationToken = default); - ValueTask ZAddAsync(string setId, long score, byte[] value, CancellationToken cancellationToken = default); - ValueTask ZRemAsync(string setId, byte[] value, CancellationToken cancellationToken = default); - ValueTask ZRemAsync(string setId, byte[][] values, CancellationToken cancellationToken = default); - ValueTask ZIncrByAsync(string setId, double incrBy, byte[] value, CancellationToken cancellationToken = default); - ValueTask ZIncrByAsync(string setId, long incrBy, byte[] value, CancellationToken cancellationToken = default); - ValueTask ZRankAsync(string setId, byte[] value, CancellationToken cancellationToken = default); - ValueTask ZRevRankAsync(string setId, byte[] value, CancellationToken cancellationToken = default); - ValueTask ZRangeAsync(string setId, int min, int max, CancellationToken cancellationToken = default); - ValueTask ZRangeWithScoresAsync(string setId, int min, int max, CancellationToken cancellationToken = default); - ValueTask ZRevRangeAsync(string setId, int min, int max, CancellationToken cancellationToken = default); - ValueTask ZRevRangeWithScoresAsync(string setId, int min, int max, CancellationToken cancellationToken = default); - ValueTask ZRangeByScoreAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask ZRangeByScoreAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask ZRangeByScoreWithScoresAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask ZRangeByScoreWithScoresAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask ZRevRangeByScoreAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask ZRevRangeByScoreAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask ZRevRangeByScoreWithScoresAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask ZRevRangeByScoreWithScoresAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask ZRemRangeByRankAsync(string setId, int min, int max, CancellationToken cancellationToken = default); - ValueTask ZRemRangeByScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask ZRemRangeByScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken = default); - ValueTask ZCardAsync(string setId, CancellationToken cancellationToken = default); - ValueTask ZCountAsync(string setId, double min, double max, CancellationToken cancellationToken = default); - ValueTask ZScoreAsync(string setId, byte[] value, CancellationToken cancellationToken = default); - ValueTask ZUnionStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); - ValueTask ZUnionStoreAsync(string intoSetId, params string[] setIds); // convenience API - ValueTask ZInterStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken = default); - ValueTask ZInterStoreAsync(string intoSetId, params string[] setIds); // convenience API - ValueTask ZRangeByLexAsync(string setId, string min, string max, int? skip = null, int? take = null, CancellationToken cancellationToken = default); - ValueTask ZLexCountAsync(string setId, string min, string max, CancellationToken cancellationToken = default); - ValueTask ZRemRangeByLexAsync(string setId, string min, string max, CancellationToken cancellationToken = default); - - ////Redis Hash operations - ValueTask HSetAsync(string hashId, byte[] key, byte[] value, CancellationToken cancellationToken = default); - ValueTask HMSetAsync(string hashId, byte[][] keys, byte[][] values, CancellationToken cancellationToken = default); - ValueTask HSetNXAsync(string hashId, byte[] key, byte[] value, CancellationToken cancellationToken = default); - ValueTask HIncrbyAsync(string hashId, byte[] key, int incrementBy, CancellationToken cancellationToken = default); - ValueTask HIncrbyFloatAsync(string hashId, byte[] key, double incrementBy, CancellationToken cancellationToken = default); - ValueTask HGetAsync(string hashId, byte[] key, CancellationToken cancellationToken = default); - ValueTask HMGetAsync(string hashId, byte[][] keysAndArgs, CancellationToken cancellationToken = default); - ValueTask HMGetAsync(string hashId, params byte[][] keysAndArgs); // convenience API - ValueTask HDelAsync(string hashId, byte[] key, CancellationToken cancellationToken = default); - ValueTask HExistsAsync(string hashId, byte[] key, CancellationToken cancellationToken = default); - ValueTask HLenAsync(string hashId, CancellationToken cancellationToken = default); - ValueTask HKeysAsync(string hashId, CancellationToken cancellationToken = default); - ValueTask HValsAsync(string hashId, CancellationToken cancellationToken = default); - ValueTask HGetAllAsync(string hashId, CancellationToken cancellationToken = default); - - //Redis GEO operations - ValueTask GeoAddAsync(string key, double longitude, double latitude, string member, CancellationToken cancellationToken = default); - ValueTask GeoAddAsync(string key, RedisGeo[] geoPoints, CancellationToken cancellationToken = default); - ValueTask GeoAddAsync(string key, params RedisGeo[] geoPoints); // convenience API - ValueTask GeoDistAsync(string key, string fromMember, string toMember, string unit = null, CancellationToken cancellationToken = default); - ValueTask GeoHashAsync(string key, string[] members, CancellationToken cancellationToken = default); - ValueTask GeoHashAsync(string key, params string[] members); // convenience API - ValueTask> GeoPosAsync(string key, string[] members, CancellationToken cancellationToken = default); - ValueTask> GeoPosAsync(string key, params string[] members); // convenience API - ValueTask> GeoRadiusAsync(string key, double longitude, double latitude, double radius, string unit, - bool withCoords = false, bool withDist = false, bool withHash = false, int? count = null, bool? asc = null, CancellationToken cancellationToken = default); - ValueTask> GeoRadiusByMemberAsync(string key, string member, double radius, string unit, - bool withCoords = false, bool withDist = false, bool withHash = false, int? count = null, bool? asc = null, CancellationToken cancellationToken = default); - - //Redis Pub/Sub operations - ValueTask WatchAsync(string[] keys, CancellationToken cancellationToken = default); - ValueTask WatchAsync(params string[] keys); // convenience API - ValueTask UnWatchAsync(CancellationToken cancellationToken = default); - ValueTask PublishAsync(string toChannel, byte[] message, CancellationToken cancellationToken = default); - ValueTask SubscribeAsync(string[] toChannels, CancellationToken cancellationToken = default); - ValueTask SubscribeAsync(params string[] toChannels); // convenience API - ValueTask UnSubscribeAsync(string[] toChannels, CancellationToken cancellationToken = default); - ValueTask UnSubscribeAsync(params string[] toChannels); // convenience API - ValueTask PSubscribeAsync(string[] toChannelsMatchingPatterns, CancellationToken cancellationToken = default); - ValueTask PSubscribeAsync(params string[] toChannelsMatchingPatterns); // convenience API - ValueTask PUnSubscribeAsync(string[] toChannelsMatchingPatterns, CancellationToken cancellationToken = default); - ValueTask PUnSubscribeAsync(params string[] toChannelsMatchingPatterns); // convenience API - ValueTask ReceiveMessagesAsync(CancellationToken cancellationToken = default); - ValueTask CreateSubscriptionAsync(CancellationToken cancellationToken = default); - - //Redis LUA support - ValueTask EvalCommandAsync(string luaBody, int numberKeysInArgs, byte[][] keys, CancellationToken cancellationToken = default); - ValueTask EvalCommandAsync(string luaBody, int numberKeysInArgs, params byte[][] keys); // convenience API - ValueTask EvalShaCommandAsync(string sha1, int numberKeysInArgs, byte[][] keys, CancellationToken cancellationToken = default); - ValueTask EvalShaCommandAsync(string sha1, int numberKeysInArgs, params byte[][] keys); // convenience API - - ValueTask EvalAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken = default); - ValueTask EvalAsync(string luaBody, int numberOfKeys, params byte[][] keysAndArgs); // convenience API - ValueTask EvalShaAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken = default); - ValueTask EvalShaAsync(string sha1, int numberOfKeys, params byte[][] keysAndArgs); // convenience API - - ValueTask EvalIntAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken = default); - ValueTask EvalIntAsync(string luaBody, int numberOfKeys, params byte[][] keysAndArgs); // convenience API - ValueTask EvalShaIntAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken = default); - ValueTask EvalShaIntAsync(string sha1, int numberOfKeys, params byte[][] keysAndArgs); // convenience API - ValueTask EvalStrAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken = default); - ValueTask EvalStrAsync(string luaBody, int numberOfKeys, params byte[][] keysAndArgs); // convenience API - ValueTask EvalShaStrAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken = default); - ValueTask EvalShaStrAsync(string sha1, int numberOfKeys, params byte[][] keysAndArgs); // convenience API - - ValueTask CalculateSha1Async(string luaBody, CancellationToken cancellationToken = default); - ValueTask ScriptExistsAsync(byte[][] sha1Refs, CancellationToken cancellationToken = default); - ValueTask ScriptExistsAsync(params byte[][] sha1Refs); // convenience API - ValueTask ScriptFlushAsync(CancellationToken cancellationToken = default); - ValueTask ScriptKillAsync(CancellationToken cancellationToken = default); - ValueTask ScriptLoadAsync(string body, CancellationToken cancellationToken = default); - - ValueTask SlowlogResetAsync(CancellationToken cancellationToken = default); - ValueTask SlowlogGetAsync(int? top = null, CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineAsync.cs deleted file mode 100644 index b703c4f0..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineAsync.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace ServiceStack.Redis.Pipeline -{ - /// - /// Interface to redis pipeline - /// - public interface IRedisPipelineAsync : IRedisPipelineSharedAsync, IRedisQueueableOperationAsync - { - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineSharedAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineSharedAsync.cs deleted file mode 100644 index 9e3e2292..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisPipelineSharedAsync.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace ServiceStack.Redis.Pipeline -{ - /// - /// Pipeline interface shared by typed and non-typed pipelines - /// - public interface IRedisPipelineSharedAsync : IAsyncDisposable, IRedisQueueCompletableOperationAsync - { - ValueTask FlushAsync(CancellationToken cancellationToken = default); - ValueTask ReplayAsync(CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueCompletableOperationAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueCompletableOperationAsync.cs deleted file mode 100644 index f99bfb7d..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueCompletableOperationAsync.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace ServiceStack.Redis.Pipeline -{ - /// - /// Interface to operations that allow queued commands to be completed - /// - public interface IRedisQueueCompletableOperationAsync - { - void CompleteVoidQueuedCommandAsync(Func voidReadCommand); - void CompleteIntQueuedCommandAsync(Func> intReadCommand); - void CompleteLongQueuedCommandAsync(Func> longReadCommand); - void CompleteBytesQueuedCommandAsync(Func> bytesReadCommand); - void CompleteMultiBytesQueuedCommandAsync(Func> multiBytesReadCommand); - void CompleteStringQueuedCommandAsync(Func> stringReadCommand); - void CompleteMultiStringQueuedCommandAsync(Func>> multiStringReadCommand); - void CompleteDoubleQueuedCommandAsync(Func> doubleReadCommand); - void CompleteRedisDataQueuedCommandAsync(Func> redisDataReadCommand); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueableOperationAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueableOperationAsync.cs deleted file mode 100644 index ec862703..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisQueueableOperationAsync.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace ServiceStack.Redis.Pipeline -{ - /// - /// interface to operation that can queue commands - /// - public interface IRedisQueueableOperationAsync - { - void QueueCommand(Func command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func>> command, Action> onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func>> command, Action> onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func>> command, Action> onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func> command, Action onSuccessCallback = null, Action onErrorCallback = null); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.Generic.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.Generic.cs deleted file mode 100644 index b3363eb8..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.Generic.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot Async(demis.bellot@gmail.com, CancellationToken cancellationToken = default) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using ServiceStack.Model; - -namespace ServiceStack.Redis.Generic -{ - public interface IRedisSetAsync : IAsyncEnumerable, IHasStringId - { - ValueTask CountAsync(CancellationToken cancellationToken = default); - ValueTask> SortAsync(int startingFrom, int endingAt, CancellationToken cancellationToken = default); - ValueTask> GetAllAsync(CancellationToken cancellationToken = default); - ValueTask PopRandomItemAsync(CancellationToken cancellationToken = default); - ValueTask GetRandomItemAsync(CancellationToken cancellationToken = default); - ValueTask MoveToAsync(T item, IRedisSetAsync toSet, CancellationToken cancellationToken = default); - ValueTask PopulateWithIntersectOfAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken = default); - ValueTask PopulateWithIntersectOfAsync(params IRedisSetAsync[] sets); // convenience API - ValueTask PopulateWithUnionOfAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken = default); - ValueTask PopulateWithUnionOfAsync(params IRedisSetAsync[] sets); // convenience API - ValueTask GetDifferencesAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); - ValueTask GetDifferencesAsync(params IRedisSetAsync[] withSets); // convenience API - ValueTask PopulateWithDifferencesOfAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); - ValueTask PopulateWithDifferencesOfAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets); // convenience API - ValueTask ClearAsync(CancellationToken cancellationToken = default); - ValueTask ContainsAsync(T item, CancellationToken cancellationToken = default); - ValueTask RemoveAsync(T item, CancellationToken cancellationToken = default); - ValueTask AddAsync(T item, CancellationToken cancellationToken = default); - } - -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.cs deleted file mode 100644 index bd8ac6c3..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSetAsync.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot Async(demis.bellot@gmail.com) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using ServiceStack.Model; - -namespace ServiceStack.Redis -{ - public interface IRedisSetAsync - : IAsyncEnumerable, IHasStringId - { - ValueTask CountAsync(CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetAsync(int startingFrom, int endingAt, CancellationToken cancellationToken = default); - ValueTask> GetAllAsync(CancellationToken cancellationToken = default); - ValueTask PopAsync(CancellationToken cancellationToken = default); - ValueTask MoveAsync(string value, IRedisSetAsync toSet, CancellationToken cancellationToken = default); - ValueTask> IntersectAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); - ValueTask> IntersectAsync(params IRedisSetAsync[] withSets); // conveinence API - ValueTask StoreIntersectAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); - ValueTask StoreIntersectAsync(params IRedisSetAsync[] withSets); // conveinence API - ValueTask> UnionAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); - ValueTask> UnionAsync(params IRedisSetAsync[] withSets); // conveinence API - ValueTask StoreUnionAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); - ValueTask StoreUnionAsync(params IRedisSetAsync[] withSets); // conveinence API - ValueTask> DiffAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); - ValueTask StoreDiffAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); - ValueTask StoreDiffAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets); // conveinence API - ValueTask GetRandomEntryAsync(CancellationToken cancellationToken = default); - - - ValueTask RemoveAsync(string item, CancellationToken cancellationToken = default); - ValueTask AddAsync(string item, CancellationToken cancellationToken = default); - ValueTask ContainsAsync(string item, CancellationToken cancellationToken = default); - ValueTask ClearAsync(CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.Generic.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.Generic.cs deleted file mode 100644 index ec7927cb..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.Generic.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot Async(demis.bellot@gmail.com, CancellationToken cancellationToken = default) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using ServiceStack.Model; - -namespace ServiceStack.Redis.Generic -{ - public interface IRedisSortedSetAsync : IAsyncEnumerable, IHasStringId - { - ValueTask CountAsync(CancellationToken cancellationToken = default); - ValueTask AddAsync(T item, double score, CancellationToken cancellationToken = default); - ValueTask PopItemWithHighestScoreAsync(CancellationToken cancellationToken = default); - ValueTask PopItemWithLowestScoreAsync(CancellationToken cancellationToken = default); - ValueTask IncrementItemAsync(T item, double incrementBy, CancellationToken cancellationToken = default); - ValueTask IndexOfAsync(T item, CancellationToken cancellationToken = default); - ValueTask IndexOfDescendingAsync(T item, CancellationToken cancellationToken = default); - ValueTask> GetAllAsync(CancellationToken cancellationToken = default); - ValueTask> GetAllDescendingAsync(CancellationToken cancellationToken = default); - ValueTask> GetRangeAsync(int fromRank, int toRank, CancellationToken cancellationToken = default); - ValueTask> GetRangeByLowestScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeByLowestScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeByHighestScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeByHighestScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask RemoveRangeAsync(int minRank, int maxRank, CancellationToken cancellationToken = default); - ValueTask RemoveRangeByScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask GetItemScoreAsync(T item, CancellationToken cancellationToken = default); - ValueTask PopulateWithIntersectOfAsync(IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken = default); - ValueTask PopulateWithIntersectOfAsync(params IRedisSortedSetAsync[] setIds); // convenience API - ValueTask PopulateWithIntersectOfAsync(IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken = default); - ValueTask PopulateWithUnionOfAsync(IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken = default); - ValueTask PopulateWithUnionOfAsync(params IRedisSortedSetAsync[] setIds); // convenience API - ValueTask PopulateWithUnionOfAsync(IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken = default); - ValueTask ClearAsync(CancellationToken cancellationToken = default); - - ValueTask ContainsAsync(T item, CancellationToken cancellationToken = default); - ValueTask AddAsync(T item, CancellationToken cancellationToken = default); - ValueTask RemoveAsync(T item, CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.cs deleted file mode 100644 index 9e85d906..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSortedSetAsync.cs +++ /dev/null @@ -1,46 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot Async(demis.bellot@gmail.com) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using ServiceStack.Model; - -namespace ServiceStack.Redis -{ - public interface IRedisSortedSetAsync - : IAsyncEnumerable, IHasStringId - { - ValueTask CountAsync(CancellationToken cancellationToken = default); - ValueTask> GetAllAsync(CancellationToken cancellationToken = default); - ValueTask> GetRangeAsync(int startingRank, int endingRank, CancellationToken cancellationToken = default); - ValueTask> GetRangeByScoreAsync(string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeByScoreAsync(string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeByScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeByScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask RemoveRangeAsync(int fromRank, int toRank, CancellationToken cancellationToken = default); - ValueTask RemoveRangeByScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask StoreFromIntersectAsync(IRedisSortedSetAsync[] ofSets, CancellationToken cancellationToken = default); - ValueTask StoreFromIntersectAsync(params IRedisSortedSetAsync[] ofSets); // convenience API - ValueTask StoreFromUnionAsync(IRedisSortedSetAsync[] ofSets, CancellationToken cancellationToken = default); - ValueTask StoreFromUnionAsync(params IRedisSortedSetAsync[] ofSets); // convenience API - ValueTask GetItemIndexAsync(string value, CancellationToken cancellationToken = default); - ValueTask GetItemScoreAsync(string value, CancellationToken cancellationToken = default); - ValueTask IncrementItemScoreAsync(string value, double incrementByScore, CancellationToken cancellationToken = default); - ValueTask PopItemWithHighestScoreAsync(CancellationToken cancellationToken = default); - ValueTask PopItemWithLowestScoreAsync(CancellationToken cancellationToken = default); - ValueTask ClearAsync(CancellationToken cancellationToken = default); - ValueTask ContainsAsync(string item, CancellationToken cancellationToken = default); - ValueTask AddAsync(string item, CancellationToken cancellationToken = default); - ValueTask RemoveAsync(string item, CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSubscriptionAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisSubscriptionAsync.cs deleted file mode 100644 index 6b06a032..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisSubscriptionAsync.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace ServiceStack.Redis -{ - public interface IRedisSubscriptionAsync - : IAsyncDisposable - { - /// - /// The number of active subscriptions this client has - /// - long SubscriptionCount { get; } - - /// - /// Registered handler called after client *Subscribes* to each new channel - /// - event Func OnSubscribeAsync; - - /// - /// Registered handler called when each message is received - /// - event Func OnMessageAsync; - - /// - /// Registered handler called when each message is received - /// - event Func OnMessageBytesAsync; - - /// - /// Registered handler called when each channel is unsubscribed - /// - event Func OnUnSubscribeAsync; - - /// - /// Subscribe to channels by name - /// - ValueTask SubscribeToChannelsAsync(string[] channels, CancellationToken cancellationToken = default); - - /// - /// Subscribe to channels by name - /// - ValueTask SubscribeToChannelsAsync(params string[] channels); // convenience API - - /// - /// Subscribe to channels matching the supplied patterns - /// - ValueTask SubscribeToChannelsMatchingAsync(string[] patterns, CancellationToken cancellationToken = default); - - /// - /// Subscribe to channels matching the supplied patterns - /// - ValueTask SubscribeToChannelsMatchingAsync(params string[] patterns); // convenience API - - ValueTask UnSubscribeFromAllChannelsAsync(CancellationToken cancellationToken = default); - ValueTask UnSubscribeFromChannelsAsync(string[] channels, CancellationToken cancellationToken = default); - ValueTask UnSubscribeFromChannelsAsync(params string[] channels); // convenience API - ValueTask UnSubscribeFromChannelsMatchingAsync(string[] patterns, CancellationToken cancellationToken = default); - ValueTask UnSubscribeFromChannelsMatchingAsync(params string[] patterns); // convenience API - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionAsync.cs deleted file mode 100644 index 5ee56047..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionAsync.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot (demis.bellot@gmail.com) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// - -using System; -using System.Threading; -using System.Threading.Tasks; -using ServiceStack.Redis.Pipeline; - -namespace ServiceStack.Redis -{ - /// - /// Interface to redis transaction - /// - public interface IRedisTransactionAsync - : IRedisTransactionBaseAsync, IRedisQueueableOperationAsync, IAsyncDisposable - { - ValueTask CommitAsync(CancellationToken cancellationToken = default); - ValueTask RollbackAsync(CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionBaseAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionBaseAsync.cs deleted file mode 100644 index fb2089e5..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTransactionBaseAsync.cs +++ /dev/null @@ -1,11 +0,0 @@ -using ServiceStack.Redis.Pipeline; - -namespace ServiceStack.Redis -{ - /// - /// Base transaction interface, shared by typed and non-typed transactions - /// - public interface IRedisTransactionBaseAsync : IRedisPipelineSharedAsync - { - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedClientAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedClientAsync.cs deleted file mode 100644 index 3af3b6b0..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedClientAsync.cs +++ /dev/null @@ -1,211 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot (demis.bellot@gmail.com) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using ServiceStack.Data; -using ServiceStack.Model; - -namespace ServiceStack.Redis.Generic -{ - public interface IRedisTypedClientAsync : IEntityStoreAsync - { - IHasNamed> Lists { get; } - IHasNamed> Sets { get; } - IHasNamed> SortedSets { get; } - IRedisHashAsync GetHash(string hashId); - IRedisSetAsync TypeIdsSet { get; } - - // not provided: use GetValueAsync/SetValueAsync instead - // T this[string key] { get; set; } - - ValueTask> CreateTransactionAsync(CancellationToken cancellationToken = default); - IRedisTypedPipelineAsync CreatePipeline(); - - IRedisClientAsync RedisClient { get; } - - ValueTask AcquireLockAsync(TimeSpan? timeOut = default, CancellationToken cancellationToken = default); - - long Db { get; } - ValueTask SelectAsync(long db, CancellationToken cancellationToken = default); - - ValueTask> GetAllKeysAsync(CancellationToken cancellationToken = default); - - string UrnKey(T value); - - string SequenceKey { get; set; } - ValueTask SetSequenceAsync(int value, CancellationToken cancellationToken = default); - ValueTask GetNextSequenceAsync(CancellationToken cancellationToken = default); - ValueTask GetNextSequenceAsync(int incrBy, CancellationToken cancellationToken = default); - ValueTask GetEntryTypeAsync(string key, CancellationToken cancellationToken = default); - ValueTask GetRandomKeyAsync(CancellationToken cancellationToken = default); - - ValueTask SetValueAsync(string key, T entity, CancellationToken cancellationToken = default); - ValueTask SetValueAsync(string key, T entity, TimeSpan expireIn, CancellationToken cancellationToken = default); - ValueTask SetValueIfNotExistsAsync(string key, T entity, CancellationToken cancellationToken = default); - ValueTask SetValueIfExistsAsync(string key, T entity, CancellationToken cancellationToken = default); - - ValueTask StoreAsync(T entity, TimeSpan expireIn, CancellationToken cancellationToken = default); - - ValueTask GetValueAsync(string key, CancellationToken cancellationToken = default); - ValueTask GetAndSetValueAsync(string key, T value, CancellationToken cancellationToken = default); - ValueTask ContainsKeyAsync(string key, CancellationToken cancellationToken = default); - ValueTask RemoveEntryAsync(string key, CancellationToken cancellationToken = default); - ValueTask RemoveEntryAsync(string[] args, CancellationToken cancellationToken = default); - ValueTask RemoveEntryAsync(params string[] args); // convenience API - ValueTask RemoveEntryAsync(IHasStringId[] entities, CancellationToken cancellationToken = default); - ValueTask RemoveEntryAsync(params IHasStringId[] entities); // convenience API - ValueTask IncrementValueAsync(string key, CancellationToken cancellationToken = default); - ValueTask IncrementValueByAsync(string key, int count, CancellationToken cancellationToken = default); - ValueTask DecrementValueAsync(string key, CancellationToken cancellationToken = default); - ValueTask DecrementValueByAsync(string key, int count, CancellationToken cancellationToken = default); - - ValueTask ExpireInAsync(object id, TimeSpan expiresAt, CancellationToken cancellationToken = default); - ValueTask ExpireAtAsync(object id, DateTime dateTime, CancellationToken cancellationToken = default); - ValueTask ExpireEntryInAsync(string key, TimeSpan expiresAt, CancellationToken cancellationToken = default); - ValueTask ExpireEntryAtAsync(string key, DateTime dateTime, CancellationToken cancellationToken = default); - - ValueTask GetTimeToLiveAsync(string key, CancellationToken cancellationToken = default); - ValueTask ForegroundSaveAsync(CancellationToken cancellationToken = default); - ValueTask BackgroundSaveAsync(CancellationToken cancellationToken = default); - ValueTask FlushDbAsync(CancellationToken cancellationToken = default); - ValueTask FlushAllAsync(CancellationToken cancellationToken = default); - ValueTask SearchKeysAsync(string pattern, CancellationToken cancellationToken = default); - ValueTask> GetValuesAsync(List keys, CancellationToken cancellationToken = default); - ValueTask> GetSortedEntryValuesAsync(IRedisSetAsync fromSet, int startingFrom, int endingAt, CancellationToken cancellationToken = default); - ValueTask StoreAsHashAsync(T entity, CancellationToken cancellationToken = default); - ValueTask GetFromHashAsync(object id, CancellationToken cancellationToken = default); - - //Set operations - ValueTask> GetAllItemsFromSetAsync(IRedisSetAsync fromSet, CancellationToken cancellationToken = default); - ValueTask AddItemToSetAsync(IRedisSetAsync toSet, T item, CancellationToken cancellationToken = default); - ValueTask RemoveItemFromSetAsync(IRedisSetAsync fromSet, T item, CancellationToken cancellationToken = default); - ValueTask PopItemFromSetAsync(IRedisSetAsync fromSet, CancellationToken cancellationToken = default); - ValueTask MoveBetweenSetsAsync(IRedisSetAsync fromSet, IRedisSetAsync toSet, T item, CancellationToken cancellationToken = default); - ValueTask GetSetCountAsync(IRedisSetAsync set, CancellationToken cancellationToken = default); - ValueTask SetContainsItemAsync(IRedisSetAsync set, T item, CancellationToken cancellationToken = default); - ValueTask> GetIntersectFromSetsAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken = default); - ValueTask> GetIntersectFromSetsAsync(params IRedisSetAsync[] sets); - ValueTask StoreIntersectFromSetsAsync(IRedisSetAsync intoSet, IRedisSetAsync[] sets, CancellationToken cancellationToken = default); - ValueTask StoreIntersectFromSetsAsync(IRedisSetAsync intoSet, params IRedisSetAsync[] sets); // convenience API - ValueTask> GetUnionFromSetsAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken = default); - ValueTask> GetUnionFromSetsAsync(params IRedisSetAsync[] sets); // convenience API - ValueTask StoreUnionFromSetsAsync(IRedisSetAsync intoSet, IRedisSetAsync[] sets, CancellationToken cancellationToken = default); - ValueTask StoreUnionFromSetsAsync(IRedisSetAsync intoSet, params IRedisSetAsync[] sets); // convenience API - ValueTask> GetDifferencesFromSetAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); - ValueTask> GetDifferencesFromSetAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets); // convenience API - ValueTask StoreDifferencesFromSetAsync(IRedisSetAsync intoSet, IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken = default); - ValueTask StoreDifferencesFromSetAsync(IRedisSetAsync intoSet, IRedisSetAsync fromSet, params IRedisSetAsync[] withSets); // convenience API - ValueTask GetRandomItemFromSetAsync(IRedisSetAsync fromSet, CancellationToken cancellationToken = default); - - //List operations - ValueTask> GetAllItemsFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromListAsync(IRedisListAsync fromList, int startingFrom, int endingAt, CancellationToken cancellationToken = default); - ValueTask> SortListAsync(IRedisListAsync fromList, int startingFrom, int endingAt, CancellationToken cancellationToken = default); - ValueTask AddItemToListAsync(IRedisListAsync fromList, T value, CancellationToken cancellationToken = default); - ValueTask PrependItemToListAsync(IRedisListAsync fromList, T value, CancellationToken cancellationToken = default); - ValueTask RemoveStartFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); - ValueTask BlockingRemoveStartFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken cancellationToken = default); - ValueTask RemoveEndFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); - ValueTask RemoveAllFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); - ValueTask TrimListAsync(IRedisListAsync fromList, int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken = default); - ValueTask RemoveItemFromListAsync(IRedisListAsync fromList, T value, CancellationToken cancellationToken = default); - ValueTask RemoveItemFromListAsync(IRedisListAsync fromList, T value, int noOfMatches, CancellationToken cancellationToken = default); - ValueTask GetListCountAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); - ValueTask GetItemFromListAsync(IRedisListAsync fromList, int listIndex, CancellationToken cancellationToken = default); - ValueTask SetItemInListAsync(IRedisListAsync toList, int listIndex, T value, CancellationToken cancellationToken = default); - ValueTask InsertBeforeItemInListAsync(IRedisListAsync toList, T pivot, T value, CancellationToken cancellationToken = default); - ValueTask InsertAfterItemInListAsync(IRedisListAsync toList, T pivot, T value, CancellationToken cancellationToken = default); - - //Queue operations - ValueTask EnqueueItemOnListAsync(IRedisListAsync fromList, T item, CancellationToken cancellationToken = default); - ValueTask DequeueItemFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); - ValueTask BlockingDequeueItemFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken cancellationToken = default); - - //Stack operations - ValueTask PushItemToListAsync(IRedisListAsync fromList, T item, CancellationToken cancellationToken = default); - ValueTask PopItemFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken = default); - ValueTask BlockingPopItemFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken cancellationToken = default); - ValueTask PopAndPushItemBetweenListsAsync(IRedisListAsync fromList, IRedisListAsync toList, CancellationToken cancellationToken = default); - ValueTask BlockingPopAndPushItemBetweenListsAsync(IRedisListAsync fromList, IRedisListAsync toList, TimeSpan? timeOut, CancellationToken cancellationToken = default); - - //Sorted Set operations - ValueTask AddItemToSortedSetAsync(IRedisSortedSetAsync toSet, T value, CancellationToken cancellationToken = default); - ValueTask AddItemToSortedSetAsync(IRedisSortedSetAsync toSet, T value, double score, CancellationToken cancellationToken = default); - ValueTask RemoveItemFromSortedSetAsync(IRedisSortedSetAsync fromSet, T value, CancellationToken cancellationToken = default); - ValueTask PopItemWithLowestScoreFromSortedSetAsync(IRedisSortedSetAsync fromSet, CancellationToken cancellationToken = default); - ValueTask PopItemWithHighestScoreFromSortedSetAsync(IRedisSortedSetAsync fromSet, CancellationToken cancellationToken = default); - ValueTask SortedSetContainsItemAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken = default); - ValueTask IncrementItemInSortedSetAsync(IRedisSortedSetAsync set, T value, double incrementBy, CancellationToken cancellationToken = default); - ValueTask GetItemIndexInSortedSetAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken = default); - ValueTask GetItemIndexInSortedSetDescAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken = default); - ValueTask> GetAllItemsFromSortedSetAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken = default); - ValueTask> GetAllItemsFromSortedSetDescAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetDescAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken = default); - ValueTask> GetAllWithScoresFromSortedSetAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetDescAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask> GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken = default); - ValueTask RemoveRangeFromSortedSetAsync(IRedisSortedSetAsync set, int minRank, int maxRank, CancellationToken cancellationToken = default); - ValueTask RemoveRangeFromSortedSetByScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken = default); - ValueTask GetSortedSetCountAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken = default); - ValueTask GetItemScoreInSortedSetAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken = default); - ValueTask StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken = default); - ValueTask StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, params IRedisSortedSetAsync[] setIds); // convenience API - ValueTask StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken = default); - ValueTask StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken = default); - ValueTask StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, params IRedisSortedSetAsync[] setIds); // convenience API - ValueTask StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken = default); - - //Hash operations - ValueTask HashContainsEntryAsync(IRedisHashAsync hash, TKey key, CancellationToken cancellationToken = default); - ValueTask SetEntryInHashAsync(IRedisHashAsync hash, TKey key, T value, CancellationToken cancellationToken = default); - ValueTask SetEntryInHashIfNotExistsAsync(IRedisHashAsync hash, TKey key, T value, CancellationToken cancellationToken = default); - ValueTask SetRangeInHashAsync(IRedisHashAsync hash, IEnumerable> keyValuePairs, CancellationToken cancellationToken = default); - ValueTask GetValueFromHashAsync(IRedisHashAsync hash, TKey key, CancellationToken cancellationToken = default); - ValueTask RemoveEntryFromHashAsync(IRedisHashAsync hash, TKey key, CancellationToken cancellationToken = default); - ValueTask GetHashCountAsync(IRedisHashAsync hash, CancellationToken cancellationToken = default); - ValueTask> GetHashKeysAsync(IRedisHashAsync hash, CancellationToken cancellationToken = default); - ValueTask> GetHashValuesAsync(IRedisHashAsync hash, CancellationToken cancellationToken = default); - ValueTask> GetAllEntriesFromHashAsync(IRedisHashAsync hash, CancellationToken cancellationToken = default); - - //Useful common app-logic - ValueTask StoreRelatedEntitiesAsync(object parentId, List children, CancellationToken cancellationToken = default); - ValueTask StoreRelatedEntitiesAsync(object parentId, TChild[] children, CancellationToken cancellationToken = default); - ValueTask StoreRelatedEntitiesAsync(object parentId, params TChild[] children); // convenience API - ValueTask DeleteRelatedEntitiesAsync(object parentId, CancellationToken cancellationToken = default); - ValueTask DeleteRelatedEntityAsync(object parentId, object childId, CancellationToken cancellationToken = default); - ValueTask> GetRelatedEntitiesAsync(object parentId, CancellationToken cancellationToken = default); - ValueTask GetRelatedEntitiesCountAsync(object parentId, CancellationToken cancellationToken = default); - ValueTask AddToRecentsListAsync(T value, CancellationToken cancellationToken = default); - ValueTask> GetLatestFromRecentsListAsync(int skip, int take, CancellationToken cancellationToken = default); - ValueTask> GetEarliestFromRecentsListAsync(int skip, int take, CancellationToken cancellationToken = default); - } - -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedPipelineAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedPipelineAsync.cs deleted file mode 100644 index 1bc9db8a..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedPipelineAsync.cs +++ /dev/null @@ -1,11 +0,0 @@ -using ServiceStack.Redis.Pipeline; - -namespace ServiceStack.Redis.Generic -{ - /// - /// Interface to redis typed pipeline - /// - public interface IRedisTypedPipelineAsync : IRedisPipelineSharedAsync, IRedisTypedQueueableOperationAsync - { - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedQueueableOperationAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedQueueableOperationAsync.cs deleted file mode 100644 index 0fac7b59..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedQueueableOperationAsync.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace ServiceStack.Redis.Generic -{ - /// - /// interface to queueable operation using typed redis client - /// - /// - public interface IRedisTypedQueueableOperationAsync - { - void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func, ValueTask> command, Action onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func, ValueTask>> command, Action> onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func, ValueTask>> command, Action> onSuccessCallback = null, Action onErrorCallback = null); - void QueueCommand(Func, ValueTask>> command, Action> onSuccessCallback = null, Action onErrorCallback = null); - - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedTransactionAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedTransactionAsync.cs deleted file mode 100644 index b89ef5a8..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/IRedisTypedTransactionAsync.cs +++ /dev/null @@ -1,28 +0,0 @@ -// -// https://github.com/ServiceStack/ServiceStack.Redis -// ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Demis Bellot (demis.bellot@gmail.com) -// -// Copyright 2017 ServiceStack, Inc. All Rights Reserved. -// -// Licensed under the same terms of ServiceStack. -// - -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace ServiceStack.Redis.Generic -{ - /// - /// Redis transaction for typed client - /// - /// - public interface IRedisTypedTransactionAsync : IRedisTypedQueueableOperationAsync, IAsyncDisposable - { - ValueTask CommitAsync(CancellationToken cancellationToken = default); - ValueTask RollbackAsync(CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientAsync.cs deleted file mode 100644 index 19d8ccf7..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientAsync.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace ServiceStack.Caching -{ - /// - /// A common interface implementation that is implemented by most cache providers - /// - public interface ICacheClientAsync - : IAsyncDisposable - { - /// - /// Removes the specified item from the cache. - /// - /// The identifier for the item to delete. - /// - /// true if the item was successfully removed from the cache; false otherwise. - /// - ValueTask RemoveAsync(string key, CancellationToken cancellationToken = default); - - /// - /// Removes the cache for all the keys provided. - /// - /// The keys. - ValueTask RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken = default); - - /// - /// Retrieves the specified item from the cache. - /// - /// - /// The identifier for the item to retrieve. - /// - /// The retrieved item, or null if the key was not found. - /// - ValueTask GetAsync(string key, CancellationToken cancellationToken = default); - - /// - /// Increments the value of the specified key by the given amount. - /// The operation is atomic and happens on the server. - /// A non existent value at key starts at 0 - /// - /// The identifier for the item to increment. - /// The amount by which the client wants to increase the item. - /// - /// The new value of the item or -1 if not found. - /// - /// The item must be inserted into the cache before it can be changed. The item must be inserted as a . The operation only works with values, so -1 always indicates that the item was not found. - ValueTask IncrementAsync(string key, uint amount, CancellationToken cancellationToken = default); - - /// - /// Increments the value of the specified key by the given amount. - /// The operation is atomic and happens on the server. - /// A non existent value at key starts at 0 - /// - /// The identifier for the item to increment. - /// The amount by which the client wants to decrease the item. - /// - /// The new value of the item or -1 if not found. - /// - /// The item must be inserted into the cache before it can be changed. The item must be inserted as a . The operation only works with values, so -1 always indicates that the item was not found. - ValueTask DecrementAsync(string key, uint amount, CancellationToken cancellationToken = default); - - /// - /// Adds a new item into the cache at the specified cache key only if the cache is empty. - /// - /// The key used to reference the item. - /// The object to be inserted into the cache. - /// - /// true if the item was successfully stored in the cache; false otherwise. - /// - /// The item does not expire unless it is removed due memory pressure. - ValueTask AddAsync(string key, T value, CancellationToken cancellationToken = default); - - /// - /// Sets an item into the cache at the cache key specified regardless if it already exists or not. - /// - ValueTask SetAsync(string key, T value, CancellationToken cancellationToken = default); - - /// - /// Replaces the item at the cachekey specified only if an items exists at the location already. - /// - ValueTask ReplaceAsync(string key, T value, CancellationToken cancellationToken = default); - - ValueTask AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken = default); - ValueTask SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken = default); - ValueTask ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken = default); - - ValueTask AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken = default); - ValueTask SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken = default); - ValueTask ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken = default); - - /// - /// Invalidates all data on the cache. - /// - ValueTask FlushAllAsync(CancellationToken cancellationToken = default); - - /// - /// Retrieves multiple items from the cache. - /// The default value of T is set for all keys that do not exist. - /// - /// The list of identifiers for the items to retrieve. - /// - /// a Dictionary holding all items indexed by their key. - /// - ValueTask> GetAllAsync(IEnumerable keys, CancellationToken cancellationToken = default); - - /// - /// Sets multiple items to the cache. - /// - /// - /// The values. - ValueTask SetAllAsync(IDictionary values, CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientExtendedAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientExtendedAsync.cs deleted file mode 100644 index b8270e2d..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/ICacheClientExtendedAsync.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace ServiceStack.Caching -{ - /// - /// Extend ICacheClient API with shared, non-core features - /// - public interface ICacheClientExtendedAsync : ICacheClientAsync - { - ValueTask GetTimeToLiveAsync(string key, CancellationToken cancellationToken = default); - - IAsyncEnumerable GetKeysByPatternAsync(string pattern, CancellationToken cancellationToken = default); - - ValueTask RemoveExpiredEntriesAsync(CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/IRemoveByPatternAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/IRemoveByPatternAsync.cs deleted file mode 100644 index 09e65f32..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Caching/IRemoveByPatternAsync.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; - -namespace ServiceStack.Caching -{ - public interface IRemoveByPatternAsync - { - /// - /// Removes items from cache that have keys matching the specified wildcard pattern - /// - /// The wildcard, where "*" means any sequence of characters and "?" means any single character. - ValueTask RemoveByPatternAsync(string pattern, CancellationToken cancellationToken = default); - /// - /// Removes items from the cache based on the specified regular expression pattern - /// - /// Regular expression pattern to search cache keys - ValueTask RemoveByRegexAsync(string regex, CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.Generic.cs b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.Generic.cs deleted file mode 100644 index 717cc0c6..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.Generic.cs +++ /dev/null @@ -1,36 +0,0 @@ -//Copyright (c) ServiceStack, Inc. All Rights Reserved. -//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace ServiceStack.Data -{ - /// - /// For providers that want a cleaner API with a little more perf - /// - /// - public interface IEntityStoreAsync - { - ValueTask GetByIdAsync(object id, CancellationToken cancellationToken = default); - - ValueTask> GetByIdsAsync(IEnumerable ids, CancellationToken cancellationToken = default); - - ValueTask> GetAllAsync(CancellationToken cancellationToken = default); - - ValueTask StoreAsync(T entity, CancellationToken cancellationToken = default); - - ValueTask StoreAllAsync(IEnumerable entities, CancellationToken cancellationToken = default); - - ValueTask DeleteAsync(T entity, CancellationToken cancellationToken = default); - - ValueTask DeleteByIdAsync(object id, CancellationToken cancellationToken = default); - - ValueTask DeleteByIdsAsync(IEnumerable ids, CancellationToken cancellationToken = default); - - ValueTask DeleteAllAsync(CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.cs b/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.cs deleted file mode 100644 index 41b04939..00000000 --- a/src/ServiceStack.Redis/AsyncInterfaces/ServiceStack.Interfaces/Data/IEntityStoreAsync.cs +++ /dev/null @@ -1,30 +0,0 @@ -//Copyright (c) ServiceStack, Inc. All Rights Reserved. -//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace ServiceStack.Data -{ - public interface IEntityStoreAsync : IAsyncDisposable - { - ValueTask GetByIdAsync(object id, CancellationToken cancellationToken = default); - - ValueTask> GetByIdsAsync(ICollection ids, CancellationToken cancellationToken = default); - - ValueTask StoreAsync(T entity, CancellationToken cancellationToken = default); - - ValueTask StoreAllAsync(IEnumerable entities, CancellationToken cancellationToken = default); - - ValueTask DeleteAsync(T entity, CancellationToken cancellationToken = default); - - ValueTask DeleteByIdAsync(object id, CancellationToken cancellationToken = default); - - ValueTask DeleteByIdsAsync(ICollection ids, CancellationToken cancellationToken = default); - - ValueTask DeleteAllAsync(CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs b/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs index 5e0d2069..d8fa8bc8 100644 --- a/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs +++ b/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs @@ -27,10 +27,10 @@ public partial class BasicRedisClientManager : IRedisClientsManagerAsync, ICacheClientAsync { private ValueTask GetCacheClientAsync(in CancellationToken _) - => new RedisClientManagerCacheClient(this).AsValueTask(); + => new RedisClientManagerCacheClient(this).AsValueTaskResult(); private ValueTask GetReadOnlyCacheClientAsync(in CancellationToken _) - => ConfigureRedisClientAsync(this.GetReadOnlyClientImpl()).AsValueTask(); + => ConfigureRedisClientAsync(this.GetReadOnlyClientImpl()).AsValueTaskResult(); private IRedisClientAsync ConfigureRedisClientAsync(IRedisClientAsync client) => client; @@ -39,13 +39,13 @@ ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(Cance => GetCacheClientAsync(cancellationToken); ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken cancellationToken) - => GetClientImpl().AsValueTask(); + => GetClientImpl().AsValueTaskResult(); ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken cancellationToken) => GetReadOnlyCacheClientAsync(cancellationToken); ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken cancellationToken) - => GetReadOnlyClientImpl().AsValueTask(); + => GetReadOnlyClientImpl().AsValueTaskResult(); ValueTask IAsyncDisposable.DisposeAsync() { @@ -53,106 +53,184 @@ ValueTask IAsyncDisposable.DisposeAsync() return default; } - async ValueTask ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) + async Task ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) { - await using var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.GetAsync(key).ConfigureAwait(false); + var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.GetAsync(key).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.SetAsync(key, value, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.SetAsync(key, value, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.SetAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.SetAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.SetAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.SetAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) + async Task ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await client.FlushAllAsync(cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + await client.FlushAllAsync(cancellationToken).ConfigureAwait(false); + } } - async ValueTask> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) + async Task> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) { - await using var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.GetAllAsync(keys, cancellationToken).ConfigureAwait(false); + var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.GetAllAsync(keys, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await client.SetAllAsync(values, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + await client.SetAllAsync(values, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) + async Task ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.RemoveAsync(key, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.RemoveAsync(key, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) + async Task ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await client.RemoveAllAsync(keys, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + await client.RemoveAllAsync(keys, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) + async Task ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.IncrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.IncrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) + async Task ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.DecrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.DecrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) + async Task ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.AddAsync(key, value, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.AddAsync(key, value, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) + async Task ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + async Task ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.AddAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.AddAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + async Task ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.ReplaceAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + async Task ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.AddAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.AddAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + async Task ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.ReplaceAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + } + } + + async Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) + { + var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.GetTimeToLiveAsync(key, cancellationToken).ConfigureAwait(false); + } + } + + async Task> ICacheClientAsync.GetKeysByPatternAsync(string pattern, CancellationToken cancellationToken) + { + var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.GetKeysByPatternAsync(pattern, cancellationToken).ConfigureAwait(false); + } + } + + async Task ICacheClientAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationToken) + { + var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + await client.RemoveExpiredEntriesAsync(cancellationToken).ConfigureAwait(false); + } } } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/BufferedReader.Async.cs b/src/ServiceStack.Redis/BufferedReader.Async.cs index b722b82b..c6c9985e 100644 --- a/src/ServiceStack.Redis/BufferedReader.Async.cs +++ b/src/ServiceStack.Redis/BufferedReader.Async.cs @@ -8,7 +8,7 @@ namespace ServiceStack.Redis internal sealed partial class BufferedReader { internal ValueTask ReadByteAsync(in CancellationToken cancellationToken = default) - => _available > 0 ? ReadByteFromBuffer().AsValueTask() : ReadByteSlowAsync(cancellationToken); + => _available > 0 ? ReadByteFromBuffer().AsValueTaskResult() : ReadByteSlowAsync(cancellationToken); private ValueTask ReadByteSlowAsync(in CancellationToken cancellationToken) { @@ -25,7 +25,7 @@ private ValueTask ReadByteSlowAsync(in CancellationToken cancellationToken) #endif _available = pending.Result; - return (_available > 0 ? ReadByteFromBuffer() : -1).AsValueTask(); + return (_available > 0 ? ReadByteFromBuffer() : -1).AsValueTaskResult(); #if ASYNC_MEMORY static async ValueTask Awaited(BufferedReader @this, ValueTask pending) @@ -44,7 +44,7 @@ static async ValueTask Awaited(BufferedReader @this, Task pending) internal ValueTask ReadAsync(byte[] buffer, int offset, int count, in CancellationToken cancellationToken = default) => _available > 0 - ? ReadFromBuffer(buffer, offset, count).AsValueTask() + ? ReadFromBuffer(buffer, offset, count).AsValueTaskResult() : ReadSlowAsync(buffer, offset, count, cancellationToken); private ValueTask ReadSlowAsync(byte[] buffer, int offset, int count, in CancellationToken cancellationToken) @@ -67,7 +67,7 @@ private ValueTask ReadSlowAsync(byte[] buffer, int offset, int count, in Ca return Awaited(this, pending, buffer, offset, count); _available = pending.Result; // already checked status, this is fine - return (_available > 0 ? ReadFromBuffer(buffer, offset, count) : 0).AsValueTask(); + return (_available > 0 ? ReadFromBuffer(buffer, offset, count) : 0).AsValueTaskResult(); static async ValueTask Awaited(BufferedReader @this, ValueTask pending, byte[] buffer, int offset, int count) { @@ -80,7 +80,7 @@ static async ValueTask Awaited(BufferedReader @this, ValueTask pending return Awaited(this, pending, buffer, offset, count); _available = pending.Result; // already checked status, this is fine - return (_available > 0 ? ReadFromBuffer(buffer, offset, count) : 0).AsValueTask(); + return (_available > 0 ? ReadFromBuffer(buffer, offset, count) : 0).AsValueTaskResult(); static async ValueTask Awaited(BufferedReader @this, Task pending, byte[] buffer, int offset, int count) { diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs index a6388731..18af3ec1 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs @@ -56,10 +56,10 @@ async ValueTask IRedisTypedClientAsync.SetValueAsync(string key, T entity, Ca await client.RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); } - ValueTask IEntityStoreAsync.GetByIdAsync(object id, CancellationToken cancellationToken) + Task IEntityStoreAsync.GetByIdAsync(object id, CancellationToken cancellationToken) { var key = client.UrnKey(id); - return AsAsync().GetValueAsync(key, cancellationToken); + return AsAsync().GetValueAsync(key, cancellationToken).AsTask(); } internal ValueTask FlushSendBufferAsync(CancellationToken cancellationToken) @@ -68,7 +68,7 @@ internal ValueTask FlushSendBufferAsync(CancellationToken cancellationToken) internal ValueTask AddTypeIdsRegisteredDuringPipelineAsync(CancellationToken cancellationToken) => client.AddTypeIdsRegisteredDuringPipelineAsync(cancellationToken); - async ValueTask> IEntityStoreAsync.GetByIdsAsync(IEnumerable ids, CancellationToken cancellationToken) + async Task> IEntityStoreAsync.GetByIdsAsync(IEnumerable ids, CancellationToken cancellationToken) { if (ids != null) { @@ -80,20 +80,20 @@ async ValueTask> IEntityStoreAsync.GetByIdsAsync(IEnumerable ids, Ca return new List(); } - async ValueTask> IEntityStoreAsync.GetAllAsync(CancellationToken cancellationToken) + async Task> IEntityStoreAsync.GetAllAsync(CancellationToken cancellationToken) { var allKeys = await AsyncClient.GetAllItemsFromSetAsync(this.TypeIdsSetKey, cancellationToken).ConfigureAwait(false); return await AsAsync().GetByIdsAsync(allKeys.ToArray(), cancellationToken).ConfigureAwait(false); } - async ValueTask IEntityStoreAsync.StoreAsync(T entity, CancellationToken cancellationToken) + async Task IEntityStoreAsync.StoreAsync(T entity, CancellationToken cancellationToken) { var urnKey = client.UrnKey(entity); await AsAsync().SetValueAsync(urnKey, entity, cancellationToken).ConfigureAwait(false); return entity; } - async ValueTask IEntityStoreAsync.StoreAllAsync(IEnumerable entities, CancellationToken cancellationToken) + async Task IEntityStoreAsync.StoreAllAsync(IEnumerable entities, CancellationToken cancellationToken) { if (PrepareStoreAll(entities, out var keys, out var values, out var entitiesList)) { @@ -102,14 +102,14 @@ async ValueTask IEntityStoreAsync.StoreAllAsync(IEnumerable entities, Canc } } - async ValueTask IEntityStoreAsync.DeleteAsync(T entity, CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteAsync(T entity, CancellationToken cancellationToken) { var urnKey = client.UrnKey(entity); await AsyncClient.RemoveEntryAsync(new[] { urnKey }, cancellationToken).ConfigureAwait(false); await client.RemoveTypeIdsAsync(new[] { entity }, cancellationToken).ConfigureAwait(false); } - async ValueTask IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken cancellationToken) { var urnKey = client.UrnKey(id); @@ -117,7 +117,7 @@ async ValueTask IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToke await client.RemoveTypeIdsAsync(new[] { id.ToString() }, cancellationToken).ConfigureAwait(false); } - async ValueTask IEntityStoreAsync.DeleteByIdsAsync(IEnumerable ids, CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteByIdsAsync(IEnumerable ids, CancellationToken cancellationToken) { if (ids == null) return; @@ -129,7 +129,7 @@ async ValueTask IEntityStoreAsync.DeleteByIdsAsync(IEnumerable ids, Cancellat } } - async ValueTask IEntityStoreAsync.DeleteAllAsync(CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteAllAsync(CancellationToken cancellationToken) { var ids = await AsyncClient.GetAllItemsFromSetAsync(this.TypeIdsSetKey, cancellationToken).ConfigureAwait(false); var urnKeys = ids.Map(t => client.UrnKey(t)); @@ -151,7 +151,7 @@ async ValueTask> IRedisTypedClientAsync.GetValuesAsync(List k ValueTask> IRedisTypedClientAsync.CreateTransactionAsync(CancellationToken cancellationToken) { IRedisTypedTransactionAsync obj = new RedisTypedTransaction(this, true); - return obj.AsValueTask(); + return obj.AsValueTaskResult(); } IRedisTypedPipelineAsync IRedisTypedClientAsync.CreatePipeline() @@ -288,7 +288,7 @@ ValueTask IRedisTypedClientAsync.FlushDbAsync(CancellationToken cancellationT => AsyncClient.FlushDbAsync(cancellationToken); ValueTask IRedisTypedClientAsync.FlushAllAsync(CancellationToken cancellationToken) - => AsyncClient.FlushAllAsync(cancellationToken); + => new ValueTask(AsyncClient.FlushAllAsync(cancellationToken)); async ValueTask IRedisTypedClientAsync.SearchKeysAsync(string pattern, CancellationToken cancellationToken) { @@ -298,34 +298,34 @@ async ValueTask IRedisTypedClientAsync.SearchKeysAsync(string pattern, C private ValueTask> CreateList(ValueTask pending) { - return pending.IsCompletedSuccessfully ? CreateList(pending.Result).AsValueTask() : Awaited(this, pending); + return pending.IsCompletedSuccessfully ? CreateList(pending.Result).AsValueTaskResult() : Awaited(this, pending); static async ValueTask> Awaited(RedisTypedClient obj, ValueTask pending) => obj.CreateList(await pending.ConfigureAwait(false)); } private ValueTask DeserializeValueAsync(ValueTask pending) { - return pending.IsCompletedSuccessfully ? DeserializeValue(pending.Result).AsValueTask() : Awaited(this, pending); + return pending.IsCompletedSuccessfully ? DeserializeValue(pending.Result).AsValueTaskResult() : Awaited(this, pending); static async ValueTask Awaited(RedisTypedClient obj, ValueTask pending) => obj.DeserializeValue(await pending.ConfigureAwait(false)); } private static ValueTask DeserializeFromStringAsync(ValueTask pending) { - return pending.IsCompletedSuccessfully ? DeserializeFromString(pending.Result).AsValueTask() : Awaited(pending); + return pending.IsCompletedSuccessfully ? DeserializeFromString(pending.Result).AsValueTaskResult() : Awaited(pending); static async ValueTask Awaited(ValueTask pending) => DeserializeFromString(await pending.ConfigureAwait(false)); } private static ValueTask> CreateGenericMapAsync(ValueTask> pending) { - return pending.IsCompletedSuccessfully ? CreateGenericMap(pending.Result).AsValueTask() : Awaited(pending); + return pending.IsCompletedSuccessfully ? CreateGenericMap(pending.Result).AsValueTaskResult() : Awaited(pending); static async ValueTask> Awaited(ValueTask> pending) => CreateGenericMap(await pending.ConfigureAwait(false)); } private static ValueTask> ConvertEachToAsync(ValueTask> pending) { - return pending.IsCompletedSuccessfully ? ConvertEachTo(pending.Result).AsValueTask() : Awaited(pending); + return pending.IsCompletedSuccessfully ? ConvertEachTo(pending.Result).AsValueTaskResult() : Awaited(pending); static async ValueTask> Awaited(ValueTask> pending) => ConvertEachTo(await pending.ConfigureAwait(false)); } @@ -673,7 +673,7 @@ ValueTask IRedisTypedClientAsync.StoreRelatedEntitiesAsync(object par ValueTask IRedisTypedClientAsync.DeleteRelatedEntitiesAsync(object parentId, CancellationToken cancellationToken) { var childRefKey = GetChildReferenceSetKey(parentId); - return AsyncClient.RemoveAsync(childRefKey, cancellationToken).Await(); + return new ValueTask(AsyncClient.RemoveAsync(childRefKey, cancellationToken)); } ValueTask IRedisTypedClientAsync.DeleteRelatedEntityAsync(object parentId, object childId, CancellationToken cancellationToken) diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs b/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs index d8d6dc62..8fc0b08d 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs @@ -22,16 +22,16 @@ public partial class PooledRedisClientManager : IRedisClientsManagerAsync { ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(CancellationToken cancellationToken) - => new RedisClientManagerCacheClient(this).AsValueTask(); + => new RedisClientManagerCacheClient(this).AsValueTaskResult(); ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken cancellationToken) - => GetClient(true).AsValueTask(); + => GetClient(true).AsValueTaskResult(); ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken cancellationToken) - => new RedisClientManagerCacheClient(this) { ReadOnly = true }.AsValueTask(); + => new RedisClientManagerCacheClient(this) { ReadOnly = true }.AsValueTaskResult(); ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken cancellationToken) - => GetReadOnlyClient(true).AsValueTask(); + => GetReadOnlyClient(true).AsValueTaskResult(); ValueTask IAsyncDisposable.DisposeAsync() { diff --git a/src/ServiceStack.Redis/RedisClient.Async.cs b/src/ServiceStack.Redis/RedisClient.Async.cs index 0fb8414d..81c95286 100644 --- a/src/ServiceStack.Redis/RedisClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClient.Async.cs @@ -27,7 +27,7 @@ namespace ServiceStack.Redis { - partial class RedisClient : IRedisClientAsync, IRemoveByPatternAsync, ICacheClientAsync + partial class RedisClient : IRedisClientAsync, IRemoveByPatternAsync, ICacheClientAsync, IAsyncDisposable { /// /// Access this instance for async usage @@ -87,7 +87,7 @@ IRedisPipelineAsync IRedisClientAsync.CreatePipeline() ValueTask IRedisClientAsync.CreateTransactionAsync(CancellationToken cancellationToken) { AssertServerVersionNumber(); // pre-fetch call to INFO before transaction if needed - return new RedisTransaction(this, true).AsValueTask(); // note that the MULTI here will be held and flushed async + return new RedisTransaction(this, true).AsValueTaskResult(); // note that the MULTI here will be held and flushed async } ValueTask IRedisClientAsync.RemoveEntryAsync(string[] keys, CancellationToken cancellationToken) @@ -118,13 +118,13 @@ ValueTask IRedisClientAsync.SetValueAsync(string key, string value, Cancellation ValueTask IRedisClientAsync.GetValueAsync(string key, CancellationToken cancellationToken) => NativeAsync.GetAsync(key, cancellationToken).FromUtf8BytesAsync(); - ValueTask ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) + Task ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) { return ExecAsync(r => typeof(T) == typeof(byte[]) ? ((IRedisNativeClientAsync)r).GetAsync(key, cancellationToken).Await(val => (T)(object)val) : r.GetValueAsync(key, cancellationToken).Await(val => JsonSerializer.DeserializeFromString(val)) - ); + ).AsTask(); } async ValueTask> IRedisClientAsync.SearchKeysAsync(string pattern, CancellationToken cancellationToken) @@ -180,7 +180,7 @@ ValueTask IRedisClientAsync.SetAllAsync(IDictionary map, Cancell ValueTask IRedisClientAsync.SetAllAsync(IEnumerable keys, IEnumerable values, CancellationToken cancellationToken) => GetSetAllBytes(keys, values, out var keyBytes, out var valBytes) ? NativeAsync.MSetAsync(keyBytes, valBytes, cancellationToken) : default; - ValueTask ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) + Task ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) { if (values.Count != 0) { @@ -189,11 +189,11 @@ ValueTask ICacheClientAsync.SetAllAsync(IDictionary values, Cancel // need to do this inside Exec for the JSON config bits GetSetAllBytesTyped(values, out var keys, out var valBytes); return ((IRedisNativeClientAsync)r).MSetAsync(keys, valBytes, cancellationToken); - }); + }).AsTask(); } else { - return default; + return Task.CompletedTask; } } @@ -220,8 +220,8 @@ ValueTask IRedisClientAsync.ExpireEntryAtAsync(string key, DateTime expire ? NativeAsync.PExpireAtAsync(key, ConvertToServerDate(expireAt).ToUnixTimeMs(), cancellationToken) : NativeAsync.ExpireAtAsync(key, ConvertToServerDate(expireAt).ToUnixTime(), cancellationToken); - ValueTask ICacheClientExtendedAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) - => NativeAsync.TtlAsync(key, cancellationToken).Await(ttlSecs => ParseTimeToLiveResult(ttlSecs)); + Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) + => NativeAsync.TtlAsync(key, cancellationToken).Await(ttlSecs => ParseTimeToLiveResult(ttlSecs)).AsTask(); ValueTask IRedisClientAsync.PingAsync(CancellationToken cancellationToken) => NativeAsync.PingAsync(cancellationToken); @@ -250,7 +250,7 @@ ValueTask IRedisClientAsync.FlushDbAsync(CancellationToken cancellationToken) ValueTask> IRedisClientAsync.GetValuesAsync(List keys, CancellationToken cancellationToken) { if (keys == null) throw new ArgumentNullException(nameof(keys)); - if (keys.Count == 0) return new List().AsValueTask(); + if (keys.Count == 0) return new List().AsValueTaskResult(); return NativeAsync.MGetAsync(keys.ToArray(), cancellationToken).Await(val => ParseGetValuesResult(val)); } @@ -258,7 +258,7 @@ ValueTask> IRedisClientAsync.GetValuesAsync(List keys, Canc ValueTask> IRedisClientAsync.GetValuesAsync(List keys, CancellationToken cancellationToken) { if (keys == null) throw new ArgumentNullException(nameof(keys)); - if (keys.Count == 0) return new List().AsValueTask(); + if (keys.Count == 0) return new List().AsValueTaskResult(); return NativeAsync.MGetAsync(keys.ToArray(), cancellationToken).Await(value => ParseGetValuesResult(value)); } @@ -266,7 +266,7 @@ ValueTask> IRedisClientAsync.GetValuesAsync(List keys, Cancel ValueTask> IRedisClientAsync.GetValuesMapAsync(List keys, CancellationToken cancellationToken) { if (keys == null) throw new ArgumentNullException(nameof(keys)); - if (keys.Count == 0) return new Dictionary().AsValueTask(); + if (keys.Count == 0) return new Dictionary().AsValueTaskResult(); var keysArray = keys.ToArray(); return NativeAsync.MGetAsync(keysArray, cancellationToken).Await((resultBytesArray, state) => ParseGetValuesMapResult(state, resultBytesArray), keysArray); @@ -275,7 +275,7 @@ ValueTask> IRedisClientAsync.GetValuesMapAsync(List> IRedisClientAsync.GetValuesMapAsync(List keys, CancellationToken cancellationToken) { if (keys == null) throw new ArgumentNullException(nameof(keys)); - if (keys.Count == 0) return new Dictionary().AsValueTask(); + if (keys.Count == 0) return new Dictionary().AsValueTaskResult(); var keysArray = keys.ToArray(); return NativeAsync.MGetAsync(keysArray, cancellationToken).Await((resultBytesArray, state) => ParseGetValuesMapResult(state, resultBytesArray), keysArray); @@ -368,8 +368,8 @@ ValueTask IRedisClientAsync.GetSlowlogAsync(int? numberOfRecords, => NativeAsync.SlowlogGetAsync(numberOfRecords, cancellationToken).Await(data => ParseSlowlog(data)); - ValueTask ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) - => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), cancellationToken: cancellationToken)).AwaitAsTrue(); + Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), cancellationToken: cancellationToken)).AwaitAsTrueTask(); ValueTask IAsyncDisposable.DisposeAsync() { @@ -402,53 +402,61 @@ ValueTask IRedisClientAsync.CustomAsync(object[] cmdWithArgs, Cancell ValueTask IRedisClientAsync.SetValuesAsync(IDictionary map, CancellationToken cancellationToken) => ((IRedisClientAsync)this).SetAllAsync(map, cancellationToken); - ValueTask ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { AssertNotInTransaction(); return ExecAsync(async r => { await r.SetAsync(key, value).ConfigureAwait(false); await r.ExpireEntryAtAsync(key, ConvertToServerDate(expiresAt)).ConfigureAwait(false); - }).AwaitAsTrue(); + }).AwaitAsTrueTask(); } - ValueTask ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { if (AssertServerVersionNumber() >= 2600) { - return ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), 0, expiryMilliseconds: (long)expiresIn.TotalMilliseconds)).AwaitAsTrue(); + return ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), 0, expiryMilliseconds: (long)expiresIn.TotalMilliseconds)).AwaitAsTrueTask(); } else { - return ExecAsync(r => ((IRedisNativeClientAsync)r).SetExAsync(key, (int)expiresIn.TotalSeconds, ToBytes(value))).AwaitAsTrue(); + return ExecAsync(r => ((IRedisNativeClientAsync)r).SetExAsync(key, (int)expiresIn.TotalSeconds, ToBytes(value))).AwaitAsTrueTask(); } } - ValueTask ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) - => NativeAsync.FlushAllAsync(cancellationToken); + Task ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) + => NativeAsync.FlushAllAsync(cancellationToken).AsTask(); - ValueTask> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) + Task> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) { return ExecAsync(r => { var keysArray = keys.ToArray(); return ((IRedisNativeClientAsync)r).MGetAsync(keysArray, cancellationToken).Await((keyValues, state) => ProcessGetAllResult(state, keyValues), keysArray); - }); + }).AsTask(); } - ValueTask ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) - => NativeAsync.DelAsync(key, cancellationToken).IsSuccessAsync(); + Task ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) + => NativeAsync.DelAsync(key, cancellationToken).IsSuccessTaskAsync(); - IAsyncEnumerable ICacheClientExtendedAsync.GetKeysByPatternAsync(string pattern, CancellationToken cancellationToken) - => AsAsync().ScanAllKeysAsync(pattern, cancellationToken: cancellationToken); + async Task> ICacheClientAsync.GetKeysByPatternAsync(string pattern, CancellationToken cancellationToken) + { + // buffer to match shape + var list = new List(); + await foreach (var key in AsAsync().ScanAllKeysAsync(pattern, cancellationToken: cancellationToken).ConfigureAwait(false).WithCancellation(cancellationToken)) + { + list.Add(key); + } + return list; + } - ValueTask ICacheClientExtendedAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationToken) + Task ICacheClientAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationToken) { //Redis automatically removed expired Cache Entries return default; } - async ValueTask IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, CancellationToken cancellationToken) + async Task IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, CancellationToken cancellationToken) { List buffer = null; const int BATCH_SIZE = 1024; @@ -467,26 +475,26 @@ async ValueTask IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, Cance } } - ValueTask IRemoveByPatternAsync.RemoveByRegexAsync(string regex, CancellationToken cancellationToken) + Task IRemoveByPatternAsync.RemoveByRegexAsync(string regex, CancellationToken cancellationToken) => AsAsync().RemoveByPatternAsync(RegexToGlob(regex), cancellationToken); - ValueTask ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) - => ExecAsync(r => r.RemoveEntryAsync(keys.ToArray(), cancellationToken)).Await(); + Task ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) + => ExecAsync(r => r.RemoveEntryAsync(keys.ToArray(), cancellationToken)).AsTask(); - ValueTask ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) - => ExecAsync(r => r.IncrementValueByAsync(key, (int)amount, cancellationToken)); + Task ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) + => ExecAsync(r => r.IncrementValueByAsync(key, (int)amount, cancellationToken)).AsTask(); - ValueTask ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) - => ExecAsync(r => r.DecrementValueByAsync(key, (int)amount, cancellationToken)); + Task ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) + => ExecAsync(r => r.DecrementValueByAsync(key, (int)amount, cancellationToken)).AsTask(); - ValueTask ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) - => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: false, cancellationToken: cancellationToken)); + Task ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: false, cancellationToken: cancellationToken)).AsTask(); - ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) - => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: true, cancellationToken: cancellationToken)); + Task ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: true, cancellationToken: cancellationToken)).AsTask(); - ValueTask ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + Task ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { AssertNotInTransaction(); @@ -498,10 +506,10 @@ ValueTask ICacheClientAsync.AddAsync(string key, T value, DateTime expi return true; } return false; - }); + }).AsTask(); } - ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + Task ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { AssertNotInTransaction(); @@ -513,14 +521,14 @@ ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, DateTime return true; } return false; - }); + }).AsTask(); } - ValueTask ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) - => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: false, cancellationToken: cancellationToken)); + Task ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: false, cancellationToken: cancellationToken)).AsTask(); - ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) - => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: true, cancellationToken: cancellationToken)); + Task ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: true, cancellationToken: cancellationToken)).AsTask(); ValueTask IRedisClientAsync.DbSizeAsync(CancellationToken cancellationToken) => NativeAsync.DbSizeAsync(cancellationToken); @@ -531,7 +539,7 @@ ValueTask> IRedisClientAsync.InfoAsync(CancellationTo ValueTask IRedisClientAsync.LastSaveAsync(CancellationToken cancellationToken) => NativeAsync.LastSaveAsync(cancellationToken); - async ValueTask IEntityStoreAsync.GetByIdAsync(object id, CancellationToken cancellationToken) + async Task IEntityStoreAsync.GetByIdAsync(object id, CancellationToken cancellationToken) { var key = UrnKey(id); var valueString = await AsAsync().GetValueAsync(key, cancellationToken).ConfigureAwait(false); @@ -539,7 +547,7 @@ async ValueTask IEntityStoreAsync.GetByIdAsync(object id, CancellationToke return value; } - async ValueTask> IEntityStoreAsync.GetByIdsAsync(ICollection ids, CancellationToken cancellationToken) + async Task> IEntityStoreAsync.GetByIdsAsync(ICollection ids, CancellationToken cancellationToken) { if (ids == null || ids.Count == 0) return new List(); @@ -548,7 +556,7 @@ async ValueTask> IEntityStoreAsync.GetByIdsAsync(ICollection ids, Ca return await AsAsync().GetValuesAsync(urnKeys, cancellationToken).ConfigureAwait(false); } - async ValueTask IEntityStoreAsync.StoreAsync(T entity, CancellationToken cancellationToken) + async Task IEntityStoreAsync.StoreAsync(T entity, CancellationToken cancellationToken) { var urnKey = UrnKey(entity); var valueString = JsonSerializer.SerializeToString(entity); @@ -559,8 +567,8 @@ async ValueTask IEntityStoreAsync.StoreAsync(T entity, CancellationToken c return entity; } - ValueTask IEntityStoreAsync.StoreAllAsync(IEnumerable entities, CancellationToken cancellationToken) - => StoreAllAsyncImpl(entities, cancellationToken); + Task IEntityStoreAsync.StoreAllAsync(IEnumerable entities, CancellationToken cancellationToken) + => StoreAllAsyncImpl(entities, cancellationToken).AsTask(); internal async ValueTask StoreAllAsyncImpl(IEnumerable entities, CancellationToken cancellationToken) { @@ -622,21 +630,21 @@ internal async ValueTask RemoveTypeIdsAsync(string[] ids, CancellationToken c } } - async ValueTask IEntityStoreAsync.DeleteAsync(T entity, CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteAsync(T entity, CancellationToken cancellationToken) { var urnKey = UrnKey(entity); await AsAsync().RemoveAsync(urnKey, cancellationToken).ConfigureAwait(false); await this.RemoveTypeIdsAsync(new[] { entity }, cancellationToken).ConfigureAwait(false); } - async ValueTask IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken cancellationToken) { var urnKey = UrnKey(id); await AsAsync().RemoveAsync(urnKey, cancellationToken).ConfigureAwait(false); await this.RemoveTypeIdsAsync(new[] { id.ToString() }, cancellationToken).ConfigureAwait(false); } - async ValueTask IEntityStoreAsync.DeleteByIdsAsync(ICollection ids, CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteByIdsAsync(ICollection ids, CancellationToken cancellationToken) { if (ids == null || ids.Count == 0) return; @@ -646,15 +654,15 @@ async ValueTask IEntityStoreAsync.DeleteByIdsAsync(ICollection ids, Cancellat await this.RemoveTypeIdsAsync(idsList.Map(x => x.ToString()).ToArray(), cancellationToken).ConfigureAwait(false); } - async ValueTask IEntityStoreAsync.DeleteAllAsync(CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteAllAsync(CancellationToken cancellationToken) { var typeIdsSetKey = this.GetTypeIdsSetKey(); var ids = await AsAsync().GetAllItemsFromSetAsync(typeIdsSetKey, cancellationToken).ConfigureAwait(false); if (ids.Count > 0) { var urnKeys = ids.ToList().ConvertAll(UrnKey); - await AsAsync().RemoveEntryAsync(urnKeys.ToArray()).ConfigureAwait(false); - await AsAsync().RemoveAsync(typeIdsSetKey).ConfigureAwait(false); + await AsAsync().RemoveEntryAsync(urnKeys.ToArray(), cancellationToken).ConfigureAwait(false); + await AsAsync().RemoveAsync(typeIdsSetKey, cancellationToken).ConfigureAwait(false); } } @@ -981,7 +989,7 @@ ValueTask> IRedisClientAsync.FindGeoResultsInRadiusAsync(st => NativeAsync.GeoRadiusByMemberAsync(key, member, radius, unit, withCoords: true, withDist: true, withHash: true, count: count, asc: sortByNearest, cancellationToken: cancellationToken); ValueTask IRedisClientAsync.CreateSubscriptionAsync(CancellationToken cancellationToken) - => new RedisSubscription(this).AsValueTask(); + => new RedisSubscription(this).AsValueTaskResult(); ValueTask IRedisClientAsync.PublishMessageAsync(string toChannel, string message, CancellationToken cancellationToken) => NativeAsync.PublishAsync(toChannel, message.ToUtf8Bytes(), cancellationToken); @@ -1226,7 +1234,7 @@ ValueTask> IRedisClientAsync.GetRangeFromSortedSetDescAsync(string [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ValueTask> CreateSortedScoreMapAsync(ValueTask pending) { - return pending.IsCompletedSuccessfully ? CreateSortedScoreMap(pending.Result).AsValueTask() : Awaited(pending); + return pending.IsCompletedSuccessfully ? CreateSortedScoreMap(pending.Result).AsValueTaskResult() : Awaited(pending); static async ValueTask> Awaited(ValueTask pending) => CreateSortedScoreMap(await pending.ConfigureAwait(false)); } @@ -1369,7 +1377,7 @@ ValueTask IRedisClientAsync.GetValueFromHashAsync(string hashId, string ValueTask> IRedisClientAsync.GetValuesFromHashAsync(string hashId, string[] keys, CancellationToken cancellationToken) { - if (keys.Length == 0) return new List().AsValueTask(); + if (keys.Length == 0) return new List().AsValueTaskResult(); var keyBytes = ConvertToBytes(keys); return NativeAsync.HMGetAsync(hashId, keyBytes, cancellationToken).ToStringListAsync(); } @@ -1423,7 +1431,7 @@ ValueTask> IRedisClientAsync.ExecLuaShaAsListAsync(string sha1, str => NativeAsync.EvalShaAsync(sha1, keys.Length, MergeAndConvertToBytes(keys, args), cancellationToken).ToStringListAsync(); ValueTask IRedisClientAsync.CalculateSha1Async(string luaBody, CancellationToken cancellationToken) - => CalculateSha1(luaBody).AsValueTask(); + => CalculateSha1(luaBody).AsValueTaskResult(); async ValueTask IRedisClientAsync.HasLuaScriptAsync(string sha1Ref, CancellationToken cancellationToken) { diff --git a/src/ServiceStack.Redis/RedisClientHash.Async.cs b/src/ServiceStack.Redis/RedisClientHash.Async.cs index 1285572f..049a6e19 100644 --- a/src/ServiceStack.Redis/RedisClientHash.Async.cs +++ b/src/ServiceStack.Redis/RedisClientHash.Async.cs @@ -35,7 +35,7 @@ ValueTask IRedisHashAsync.AddRangeAsync(IEnumerable => AsyncClient.SetRangeInHashAsync(hashId, items, cancellationToken); ValueTask IRedisHashAsync.ClearAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveAsync(hashId, cancellationToken).Await(); + => new ValueTask(AsyncClient.RemoveAsync(hashId, cancellationToken)); ValueTask IRedisHashAsync.ContainsKeyAsync(string key, CancellationToken cancellationToken) => AsyncClient.HashContainsEntryAsync(hashId, key, cancellationToken); diff --git a/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs b/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs index aa4dfd44..7e259345 100644 --- a/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs @@ -7,7 +7,7 @@ namespace ServiceStack.Redis { - partial class RedisClientManagerCacheClient : ICacheClientAsync, IRemoveByPatternAsync, ICacheClientExtendedAsync + partial class RedisClientManagerCacheClient : ICacheClientAsync, IRemoveByPatternAsync, IAsyncDisposable { ValueTask IAsyncDisposable.DisposeAsync() { @@ -21,153 +21,205 @@ private ValueTask GetClientAsync(in CancellationToken cancell return redisManager.GetClientAsync(cancellationToken); } - async ValueTask ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) + async Task ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) { - await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.GetAsync(key).ConfigureAwait(false); + var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.GetAsync(key, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.SetAsync(key, value, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.SetAsync(key, value, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.SetAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.SetAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.SetAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.SetAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) + async Task ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await client.FlushAllAsync(cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + await client.FlushAllAsync(cancellationToken).ConfigureAwait(false); + } } - async ValueTask> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) + async Task> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) { - await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.GetAllAsync(keys, cancellationToken).ConfigureAwait(false); + var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.GetAllAsync(keys, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await client.SetAllAsync(values, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + await client.SetAllAsync(values, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) + async Task ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.RemoveAsync(key, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.RemoveAsync(key, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientExtendedAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) + async Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) { - await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - if (client is ICacheClientExtendedAsync extended) + var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) { - return await extended.GetTimeToLiveAsync(key, cancellationToken).ConfigureAwait(false); + return await client.GetTimeToLiveAsync(key, cancellationToken).ConfigureAwait(false); } - return null; - } - async IAsyncEnumerable ICacheClientExtendedAsync.GetKeysByPatternAsync(string pattern, [EnumeratorCancellation] CancellationToken cancellationToken) + async Task> ICacheClientAsync.GetKeysByPatternAsync(string pattern, CancellationToken cancellationToken) { - await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - if (client is ICacheClientExtendedAsync extended) + var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) { - await foreach (var key in extended.GetKeysByPatternAsync(pattern).WithCancellation(cancellationToken).ConfigureAwait(false)) - { - yield return key; - } + return await client.GetKeysByPatternAsync(pattern, cancellationToken); } } - ValueTask ICacheClientExtendedAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationToken) + Task ICacheClientAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationToken) { //Redis automatically removed expired Cache Entries - return default; + return Task.CompletedTask; } - async ValueTask IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, CancellationToken cancellationToken) + async Task IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - if (client is IRemoveByPatternAsync redisClient) + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) { - await redisClient.RemoveByPatternAsync(pattern).ConfigureAwait(false); + if (client is IRemoveByPatternAsync redisClient) + { + await redisClient.RemoveByPatternAsync(pattern, cancellationToken).ConfigureAwait(false); + } } } - async ValueTask IRemoveByPatternAsync.RemoveByRegexAsync(string regex, CancellationToken cancellationToken) + async Task IRemoveByPatternAsync.RemoveByRegexAsync(string regex, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - if (client is IRemoveByPatternAsync redisClient) + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) { - await redisClient.RemoveByRegexAsync(regex).ConfigureAwait(false); + if (client is IRemoveByPatternAsync redisClient) + { + await redisClient.RemoveByRegexAsync(regex, cancellationToken).ConfigureAwait(false); + } } } - async ValueTask ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) + async Task ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await client.RemoveAllAsync(keys, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + await client.RemoveAllAsync(keys, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) + async Task ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.IncrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.IncrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) + async Task ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.DecrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.DecrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) + async Task ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.AddAsync(key, value, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.AddAsync(key, value, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) + async Task ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + async Task ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.AddAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.AddAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + async Task ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.ReplaceAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + async Task ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.AddAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.AddAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + } } - async ValueTask ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + async Task ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using (client as IAsyncDisposable) + { + return await client.ReplaceAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + } } } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientSet.Async.cs b/src/ServiceStack.Redis/RedisClientSet.Async.cs index 38476eed..7d7aafcb 100644 --- a/src/ServiceStack.Redis/RedisClientSet.Async.cs +++ b/src/ServiceStack.Redis/RedisClientSet.Async.cs @@ -28,7 +28,7 @@ ValueTask IRedisSetAsync.AddAsync(string item, CancellationToken cancellationTok => AsyncClient.AddItemToSetAsync(setId, item, cancellationToken); ValueTask IRedisSetAsync.ClearAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveAsync(setId, cancellationToken).Await(); + => new ValueTask(AsyncClient.RemoveAsync(setId, cancellationToken)); ValueTask IRedisSetAsync.ContainsAsync(string item, CancellationToken cancellationToken) => AsyncClient.SetContainsItemAsync(setId, item, cancellationToken); diff --git a/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs b/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs index 9c00c1ec..692242dd 100644 --- a/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs +++ b/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs @@ -28,7 +28,7 @@ ValueTask IRedisSortedSetAsync.AddAsync(string value, CancellationToken cancella private IRedisSortedSetAsync AsAsync() => this; ValueTask IRedisSortedSetAsync.ClearAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveAsync(setId, cancellationToken).Await(); + => new ValueTask(AsyncClient.RemoveAsync(setId, cancellationToken)); ValueTask IRedisSortedSetAsync.ContainsAsync(string value, CancellationToken cancellationToken) => AsyncClient.SortedSetContainsItemAsync(setId, value, cancellationToken); diff --git a/src/ServiceStack.Redis/RedisClient_Slowlog.cs b/src/ServiceStack.Redis/RedisClient_Slowlog.cs index c84a3ea2..25df795d 100644 --- a/src/ServiceStack.Redis/RedisClient_Slowlog.cs +++ b/src/ServiceStack.Redis/RedisClient_Slowlog.cs @@ -52,20 +52,4 @@ private static SlowlogItem[] ParseSlowlog(object[] data) } - - public class SlowlogItem - { - public SlowlogItem(int id, DateTime timeStamp, int duration, string[] arguments) - { - Id = id; - Timestamp = timeStamp; - Duration = duration; - Arguments = arguments; - } - - public int Id { get; private set; } - public int Duration { get; private set; } - public DateTime Timestamp { get; private set; } - public string[] Arguments { get; private set; } - } } diff --git a/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs b/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs index d57ae1a0..0a65b206 100644 --- a/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs +++ b/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs @@ -44,41 +44,47 @@ public static ValueTask GetClientAsync(this IRedisClientsMana { return redisManager is IRedisClientsManagerAsync asyncManager ? asyncManager.GetClientAsync(cancellationToken) - : (redisManager.GetClient() as IRedisClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetClient))).AsValueTask(); + : (redisManager.GetClient() as IRedisClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetClient))).AsValueTaskResult(); } public static ValueTask GetReadOnlyClientAsync(this IRedisClientsManager redisManager, CancellationToken cancellationToken = default) { return redisManager is IRedisClientsManagerAsync asyncManager ? asyncManager.GetReadOnlyClientAsync(cancellationToken) - : (redisManager.GetReadOnlyClient() as IRedisClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetReadOnlyClient))).AsValueTask(); + : (redisManager.GetReadOnlyClient() as IRedisClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetReadOnlyClient))).AsValueTaskResult(); } public static ValueTask GetCacheClientAsync(this IRedisClientsManager redisManager, CancellationToken cancellationToken = default) { return redisManager is IRedisClientsManagerAsync asyncManager ? asyncManager.GetCacheClientAsync(cancellationToken) - : (redisManager.GetCacheClient() as ICacheClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetCacheClient))).AsValueTask(); + : (redisManager.GetCacheClient() as ICacheClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetCacheClient))).AsValueTaskResult(); } public static ValueTask GetReadOnlyCacheClientAsync(this IRedisClientsManager redisManager, CancellationToken cancellationToken = default) { return redisManager is IRedisClientsManagerAsync asyncManager ? asyncManager.GetReadOnlyCacheClientAsync(cancellationToken) - : (redisManager.GetReadOnlyCacheClient() as ICacheClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetCacheClient))).AsValueTask(); + : (redisManager.GetReadOnlyCacheClient() as ICacheClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetCacheClient))).AsValueTaskResult(); } public static async ValueTask ExecAsync(this IRedisClientsManager redisManager, Func lambda) { - await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); - await lambda(redis).ConfigureAwait(false); + var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + await using (redis as IAsyncDisposable) + { + await lambda(redis).ConfigureAwait(false); + } } public static async ValueTask ExecAsync(this IRedisClientsManager redisManager, Func> lambda) { - await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); - return await lambda(redis).ConfigureAwait(false); + var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + await using (redis as IAsyncDisposable) + { + return await lambda(redis).ConfigureAwait(false); + } } //public static void ExecTrans(this IRedisClientsManager redisManager, Action lambda) @@ -94,26 +100,38 @@ public static async ValueTask ExecAsync(this IRedisClientsManager redisMan public static async ValueTask ExecAsAsync(this IRedisClientsManager redisManager, Func, ValueTask> lambda) { - await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); - await lambda(redis.As()).ConfigureAwait(false); + var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + await using (redis as IAsyncDisposable) + { + await lambda(redis.As()).ConfigureAwait(false); + } } public static async ValueTask ExecAsAsync(this IRedisClientsManager redisManager, Func, ValueTask> lambda) { - await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); - return await lambda(redis.As()).ConfigureAwait(false); + var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + await using (redis as IAsyncDisposable) + { + return await lambda(redis.As()).ConfigureAwait(false); + } } public static async ValueTask> ExecAsAsync(this IRedisClientsManager redisManager, Func, ValueTask>> lambda) { - await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); - return await lambda(redis.As()).ConfigureAwait(false); + var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + await using (redis as IAsyncDisposable) + { + return await lambda(redis.As()).ConfigureAwait(false); + } } public static async ValueTask> ExecAsAsync(this IRedisClientsManager redisManager, Func, ValueTask>> lambda) { - await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); - return await lambda(redis.As()).ConfigureAwait(false); + var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + await using (redis as IAsyncDisposable) + { + return await lambda(redis.As()).ConfigureAwait(false); + } } } diff --git a/src/ServiceStack.Redis/RedisLock.Async.cs b/src/ServiceStack.Redis/RedisLock.Async.cs index 20013da9..2b026b6a 100644 --- a/src/ServiceStack.Redis/RedisLock.Async.cs +++ b/src/ServiceStack.Redis/RedisLock.Async.cs @@ -88,6 +88,6 @@ await RetryUntilTrue( // .ConfigureAwait(false) is below } ValueTask IAsyncDisposable.DisposeAsync() - => ((IRedisClientAsync)untypedClient).RemoveAsync(key).Await(); + => new ValueTask(((IRedisClientAsync)untypedClient).RemoveAsync(key)); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisManagerPool.Async.cs b/src/ServiceStack.Redis/RedisManagerPool.Async.cs index f677dc10..74a48d51 100644 --- a/src/ServiceStack.Redis/RedisManagerPool.Async.cs +++ b/src/ServiceStack.Redis/RedisManagerPool.Async.cs @@ -13,16 +13,16 @@ public partial class RedisManagerPool : IRedisClientsManagerAsync { ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(CancellationToken cancellationToken) - => new RedisClientManagerCacheClient(this).AsValueTask(); + => new RedisClientManagerCacheClient(this).AsValueTaskResult(); ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken cancellationToken) - => GetClient(true).AsValueTask(); + => GetClient(true).AsValueTaskResult(); ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken cancellationToken) - => new RedisClientManagerCacheClient(this) { ReadOnly = true }.AsValueTask(); + => new RedisClientManagerCacheClient(this) { ReadOnly = true }.AsValueTaskResult(); ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken cancellationToken) - => GetClient(true).AsValueTask(); + => GetClient(true).AsValueTaskResult(); ValueTask IAsyncDisposable.DisposeAsync() { diff --git a/src/ServiceStack.Redis/RedisNativeClient.Async.cs b/src/ServiceStack.Redis/RedisNativeClient.Async.cs index 3a28d701..6ff971fc 100644 --- a/src/ServiceStack.Redis/RedisNativeClient.Async.cs +++ b/src/ServiceStack.Redis/RedisNativeClient.Async.cs @@ -240,7 +240,7 @@ ValueTask IRedisNativeClientAsync.PingAsync(CancellationToken cancellation private static ValueTask IsString(ValueTask pending, string expected) { - return pending.IsCompletedSuccessfully ? (pending.Result == expected).AsValueTask() + return pending.IsCompletedSuccessfully ? (pending.Result == expected).AsValueTaskResult() : Awaited(pending, expected); static async ValueTask Awaited(ValueTask pending, string expected) @@ -481,7 +481,7 @@ ValueTask IRedisNativeClientAsync.CalculateSha1Async(string luaBody, Can AssertNotNull(luaBody, nameof(luaBody)); byte[] buffer = Encoding.UTF8.GetBytes(luaBody); - return BitConverter.ToString(buffer.ToSha1Hash()).Replace("-", "").AsValueTask(); + return BitConverter.ToString(buffer.ToSha1Hash()).Replace("-", "").AsValueTaskResult(); } ValueTask IRedisNativeClientAsync.ScriptExistsAsync(byte[][] sha1Refs, CancellationToken cancellationToken) @@ -1377,7 +1377,7 @@ ValueTask IRedisNativeClientAsync.ReceiveMessagesAsync(CancellationTok => ReadMultiDataAsync(cancellationToken); ValueTask IRedisNativeClientAsync.CreateSubscriptionAsync(CancellationToken cancellationToken) - => new RedisSubscription(this).AsValueTask(); + => new RedisSubscription(this).AsValueTaskResult(); ValueTask IRedisNativeClientAsync.BitCountAsync(string key, CancellationToken cancellationToken) { diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.csproj index 4808134c..fa08d197 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.csproj @@ -42,5 +42,8 @@ + + + \ No newline at end of file diff --git a/src/ServiceStack.Redis/ValueTask_Utils.Async.cs b/src/ServiceStack.Redis/ValueTask_Utils.Async.cs index cc48d28e..78306d73 100644 --- a/src/ServiceStack.Redis/ValueTask_Utils.Async.cs +++ b/src/ServiceStack.Redis/ValueTask_Utils.Async.cs @@ -26,7 +26,7 @@ async static ValueTask Awaited(ValueTask pending) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ValueTask Await(this ValueTask pending, Func projection) { - return pending.IsCompletedSuccessfully ? projection(pending.Result).AsValueTask() : Awaited(pending, projection); + return pending.IsCompletedSuccessfully ? projection(pending.Result).AsValueTaskResult() : Awaited(pending, projection); async static ValueTask Awaited(ValueTask pending, Func projection) => projection(await pending.ConfigureAwait(false)); } @@ -34,7 +34,7 @@ async static ValueTask Awaited(ValueTask pending, Func p [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ValueTask AsInt32(this ValueTask pending) { - return pending.IsCompletedSuccessfully ? (checked((int)pending.Result)).AsValueTask() : Awaited(pending); + return pending.IsCompletedSuccessfully ? (checked((int)pending.Result)).AsValueTaskResult() : Awaited(pending); async static ValueTask Awaited(ValueTask pending) => checked((int)await pending.ConfigureAwait(false)); } @@ -42,7 +42,7 @@ async static ValueTask Awaited(ValueTask pending) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ValueTask Await(this ValueTask pending, Func projection, TState state) { - return pending.IsCompletedSuccessfully ? projection(pending.Result, state).AsValueTask() : Awaited(pending, projection, state); + return pending.IsCompletedSuccessfully ? projection(pending.Result, state).AsValueTaskResult() : Awaited(pending, projection, state); async static ValueTask Awaited(ValueTask pending, Func projection, TState state) => projection(await pending.ConfigureAwait(false), state); } @@ -66,6 +66,25 @@ async static ValueTask Awaited(ValueTask pending) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Task AwaitAsTrueTask(this ValueTask pending) + { + if (pending.IsCompletedSuccessfully) + { + pending.GetAwaiter().GetResult(); // for IValueTaskSource reasons + return s_TaskTrue; + } + else + { + return Awaited(pending); + } + async static Task Awaited(ValueTask pending) + { + await pending.ConfigureAwait(false); + return true; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ValueTask AwaitAsTrue(this ValueTask pending) { @@ -88,15 +107,26 @@ async static ValueTask Awaited(ValueTask pending) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ValueTask IsSuccessAsync(this ValueTask pending) { - return pending.IsCompletedSuccessfully ? (pending.Result == RedisNativeClient.Success).AsValueTask() : Awaited(pending); + return pending.IsCompletedSuccessfully ? (pending.Result == RedisNativeClient.Success).AsValueTaskResult() : Awaited(pending); async static ValueTask Awaited(ValueTask pending) => (await pending.ConfigureAwait(false)) == RedisNativeClient.Success; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Task IsSuccessTaskAsync(this ValueTask pending) + { + return pending.IsCompletedSuccessfully ? (pending.Result == RedisNativeClient.Success ? s_TaskTrue : s_TaskFalse) : Awaited(pending); + async static Task Awaited(ValueTask pending) + => (await pending.ConfigureAwait(false)) == RedisNativeClient.Success; + } + + static readonly Task s_TaskTrue = Task.FromResult(true), s_TaskFalse = Task.FromResult(false); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ValueTask> ConvertEachToAsync(this ValueTask> pending) { - return pending.IsCompletedSuccessfully ? pending.Result.ConvertEachTo().AsValueTask() : Awaited(pending); + return pending.IsCompletedSuccessfully ? pending.Result.ConvertEachTo().AsValueTaskResult() : Awaited(pending); static async ValueTask> Awaited(ValueTask> pending) => (await pending.ConfigureAwait(false)).ConvertEachTo(); } @@ -104,17 +134,17 @@ static async ValueTask> Awaited(ValueTask> pending) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ValueTask> ToStringListAsync(this ValueTask pending) { - return pending.IsCompletedSuccessfully ? pending.Result.ToStringList().AsValueTask() : Awaited(pending); + return pending.IsCompletedSuccessfully ? pending.Result.ToStringList().AsValueTaskResult() : Awaited(pending); static async ValueTask> Awaited(ValueTask pending) => (await pending.ConfigureAwait(false)).ToStringList(); } - private static readonly ValueTask s_ValueTaskTrue = true.AsValueTask(); + private static readonly ValueTask s_ValueTaskTrue = true.AsValueTaskResult(); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ValueTask Await(this ValueTask pending, T result) { - return pending.IsCompletedSuccessfully ? result.AsValueTask() : Awaited(pending, result); + return pending.IsCompletedSuccessfully ? result.AsValueTaskResult() : Awaited(pending, result); async static ValueTask Awaited(ValueTask pending, T result) { await pending.ConfigureAwait(false); @@ -123,12 +153,12 @@ async static ValueTask Awaited(ValueTask pending, T result) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ValueTask AsValueTask(this T value) => new ValueTask(value); + internal static ValueTask AsValueTaskResult(this T value) => new ValueTask(value); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ValueTask FromUtf8BytesAsync(this ValueTask pending) { - return pending.IsCompletedSuccessfully ? pending.Result.FromUtf8Bytes().AsValueTask() : Awaited(pending); + return pending.IsCompletedSuccessfully ? pending.Result.FromUtf8Bytes().AsValueTaskResult() : Awaited(pending); static async ValueTask Awaited(ValueTask pending) => (await pending.ConfigureAwait(false)).FromUtf8Bytes(); } diff --git a/tests/ServiceStack.Redis.Tests/AdhocClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/AdhocClientTests.Async.cs index ebd418d9..9c4366e5 100644 --- a/tests/ServiceStack.Redis.Tests/AdhocClientTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/AdhocClientTests.Async.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using System; using System.Threading.Tasks; namespace ServiceStack.Redis.Tests @@ -9,7 +10,8 @@ public class AdhocClientTestsAsync [Test] public async Task Search_Test() { - await using (var client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + var client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await using (client as IAsyncDisposable) { const string cacheKey = "urn+metadata:All:SearchProProfiles?SwanShinichi Osawa /0/8,0,0,0"; const long value = 1L; diff --git a/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs b/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs index 52cd5c27..5c758695 100644 --- a/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs @@ -39,7 +39,6 @@ private string Log(string message) } [TestCase(typeof(ICacheClient), typeof(ICacheClientAsync))] - [TestCase(typeof(ICacheClientExtended), typeof(ICacheClientExtendedAsync))] [TestCase(typeof(IEntityStore), typeof(IEntityStoreAsync))] [TestCase(typeof(IEntityStore<>), typeof(IEntityStoreAsync<>))] [TestCase(typeof(IRedisClient), typeof(IRedisClientAsync))] @@ -127,7 +126,7 @@ public void TestSameAPI(Type syncInterface, Type asyncInterface) } else if (asyncInterface == typeof(IRedisHashAsync<,>)) { - AddFrom(typeof(ICollection<>).MakeGenericType(typeof(KeyValuePair<,>).MakeGenericType(asyncInterface.GetGenericArguments())), nameof(IDictionary.Add)); + AddFrom(typeof(ICollection<>).MakeGenericType(typeof(KeyValuePair<,>).MakeGenericType(asyncInterface.GetGenericArguments())), nameof(IDictionary.Add)); AddFrom(typeof(IDictionary<,>), nameof(IDictionary.Add)); AddFrom(typeof(ICollection<>), nameof(IDictionary.Clear)); AddFrom(typeof(IDictionary<,>), nameof(IDictionary.ContainsKey)); @@ -137,7 +136,7 @@ public void TestSameAPI(Type syncInterface, Type asyncInterface) else if (asyncInterface == typeof(IRedisHashAsync)) { AddFrom(typeof(ICollection>), nameof(IDictionary.Add)); - AddFrom(typeof(IDictionary), nameof(IDictionary.Add)); + AddFrom(typeof(IDictionary), nameof(IDictionary.Add)); AddFrom(typeof(ICollection), nameof(IDictionary.Clear)); AddFrom(typeof(IDictionary), nameof(IDictionary.ContainsKey)); AddFrom(typeof(IDictionary), nameof(IDictionary.Remove)); @@ -158,6 +157,12 @@ public void TestSameAPI(Type syncInterface, Type asyncInterface) expected.Add("ValueTask GetSlowlogAsync(int? numberOfRecords = default, CancellationToken cancellationToken = default)"); expected.Add("ValueTask SlowlogResetAsync(CancellationToken cancellationToken = default)"); } + else if (asyncInterface == typeof(ICacheClientAsync)) + { + AddFrom(typeof(ICacheClientExtended), nameof(ICacheClientExtended.GetKeysByPattern)); + AddFrom(typeof(ICacheClientExtended), nameof(ICacheClientExtended.GetTimeToLive)); + AddFrom(typeof(ICacheClientExtended), nameof(ICacheClientExtended.RemoveExpiredEntries)); + } void AddFrom(Type syncInterface, string name, bool fromPropertyToMethod = false) => AddExpected(syncInterface.GetMethod(name), fromPropertyToMethod); @@ -788,7 +793,7 @@ public MethodToken(string name, Type returnType, ParameterToken[] parameters, } [TestCase(typeof(ICacheClient), typeof(ICacheClientAsync))] - [TestCase(typeof(ICacheClientExtended), typeof(ICacheClientExtendedAsync))] + [TestCase(typeof(ICacheClientExtended), typeof(ICacheClientAsync))] // duplicate not an error; APIs are coalesced [TestCase(typeof(IEntityStore), typeof(IEntityStoreAsync))] [TestCase(typeof(IEntityStore<>), typeof(IEntityStoreAsync<>))] [TestCase(typeof(IRedisClient), typeof(IRedisClientAsync))] diff --git a/tests/ServiceStack.Redis.Tests/BasicRediscClientManagerTests.Async.cs b/tests/ServiceStack.Redis.Tests/BasicRediscClientManagerTests.Async.cs index 57e01a70..9bc15328 100644 --- a/tests/ServiceStack.Redis.Tests/BasicRediscClientManagerTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/BasicRediscClientManagerTests.Async.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using System; using System.Threading.Tasks; namespace ServiceStack.Redis.Tests @@ -11,19 +12,22 @@ public async Task Can_select_db() { var redisManager = new BasicRedisClientManager("127.0.0.1"); - await using (var client = await redisManager.GetClientAsync()) + var client = await redisManager.GetClientAsync(); + await using (client as IAsyncDisposable) { await client.SelectAsync(2); await client.SetAsync("db", 2); } - await using (var client = await redisManager.GetClientAsync()) + client = await redisManager.GetClientAsync(); + await using (client as IAsyncDisposable) { await client.SelectAsync(3); await client.SetAsync("db", 3); } - await using (var client = await redisManager.GetClientAsync()) + client = await redisManager.GetClientAsync(); + await using (client as IAsyncDisposable) { await client.SelectAsync(2); //((RedisClient)client).ChangeDb(2); @@ -32,7 +36,8 @@ public async Task Can_select_db() } redisManager = new BasicRedisClientManager("127.0.0.1?db=3"); - await using (var client = await redisManager.GetClientAsync()) + client = await redisManager.GetClientAsync(); + await using (client as IAsyncDisposable) { var db = await client.GetAsync("db"); Assert.That(db, Is.EqualTo(3)); diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsBase.Async.cs index 91171dad..56f9a306 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsBase.Async.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsBase.Async.cs @@ -5,6 +5,7 @@ using ServiceStack.Redis.Generic; using System.Linq; using System.Threading.Tasks; +using System; namespace ServiceStack.Redis.Tests.Generic { @@ -22,9 +23,9 @@ public abstract class RedisClientHashTestsBaseAsync [SetUp] public async Task SetUp() { - if (client != null) + if (client is IAsyncDisposable d) { - await client.DisposeAsync(); + await d.DisposeAsync(); client = null; } client = new RedisClient(TestConfig.SingleHost); diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestExtra.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestExtra.Async.cs index 6e03d56c..aef2f342 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestExtra.Async.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestExtra.Async.cs @@ -2,6 +2,7 @@ using ServiceStack.Common.Tests.Models; using ServiceStack.Redis.Generic; using ServiceStack.Redis.Tests.Support; +using System; using System.Threading.Tasks; namespace ServiceStack.Redis.Tests.Generic @@ -25,9 +26,9 @@ public class RedisClientListTestExtraAsync [SetUp] public async Task SetUp() { - if (client != null) + if (client is IAsyncDisposable d) { - await client.DisposeAsync(); + await d.DisposeAsync(); client = null; } client = new RedisClient(TestConfig.SingleHost); diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsBase.Async.cs index 662cc87c..fade47b5 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsBase.Async.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsBase.Async.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; using NUnit.Framework; -using ServiceStack.Common; using ServiceStack.Common.Tests.Models; using ServiceStack.Redis.Generic; -using ServiceStack.Text; -using ServiceStack.Redis.Tests.Support; +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; namespace ServiceStack.Redis.Tests.Generic @@ -27,9 +24,9 @@ public abstract class RedisClientListTestsBaseAsync [SetUp] public async Task SetUp() { - if (client != null) + if (client is IAsyncDisposable d) { - await client.DisposeAsync(); + await d.DisposeAsync(); client = null; } client = new RedisClient(TestConfig.SingleHost); diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsBase.Async.cs index 3e1729fe..01eef3b0 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsBase.Async.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsBase.Async.cs @@ -4,6 +4,7 @@ using ServiceStack.Redis.Generic; using System.Linq; using System.Threading.Tasks; +using System; namespace ServiceStack.Redis.Tests.Generic { @@ -24,9 +25,9 @@ public abstract class RedisClientSetTestsBaseAsync [SetUp] public async Task SetUp() { - if (client != null) + if (client is IAsyncDisposable d) { - await client.DisposeAsync(); + await d.DisposeAsync(); client = null; } client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBase.Async.cs index 7bf630a9..c69247db 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBase.Async.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBase.Async.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Threading.Tasks; using NUnit.Framework; @@ -17,9 +18,9 @@ public abstract class RedisPersistenceProviderTestsBaseAsync [SetUp] public async Task SetUp() { - if (client != null) + if (client is IAsyncDisposable d) { - await client.DisposeAsync(); + await d.DisposeAsync(); client = null; } client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); diff --git a/tests/ServiceStack.Redis.Tests/LuaCachedScripts.Async.cs b/tests/ServiceStack.Redis.Tests/LuaCachedScripts.Async.cs index aca93239..15b40e5c 100644 --- a/tests/ServiceStack.Redis.Tests/LuaCachedScripts.Async.cs +++ b/tests/ServiceStack.Redis.Tests/LuaCachedScripts.Async.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NUnit.Framework; @@ -39,91 +40,109 @@ private static async Task AddTestKeysAsync(IRedisClientAsync redis, int count) [Test] public async Task Can_call_repeated_scans_in_LUA() { - await using var redis = new RedisClient().ForAsyncOnly(); - await AddTestKeysAsync(redis, 20); + var redis = new RedisClient().ForAsyncOnly(); + await using (redis as IAsyncDisposable) + { + await AddTestKeysAsync(redis, 20); - var r = await redis.ExecLuaAsync(LuaScript, "key:*", "10"); - Assert.That(r.Children.Count, Is.EqualTo(10)); + var r = await redis.ExecLuaAsync(LuaScript, "key:*", "10"); + Assert.That(r.Children.Count, Is.EqualTo(10)); - r = await redis.ExecLuaAsync(LuaScript, "key:*", "40"); - Assert.That(r.Children.Count, Is.EqualTo(20)); + r = await redis.ExecLuaAsync(LuaScript, "key:*", "40"); + Assert.That(r.Children.Count, Is.EqualTo(20)); + } } [Test] public async Task Can_call_Cached_Lua() { - await using var redis = new RedisClient().ForAsyncOnly(); - await AddTestKeysAsync(redis, 20); + var redis = new RedisClient().ForAsyncOnly(); + await using (redis as IAsyncDisposable) + { + await AddTestKeysAsync(redis, 20); - var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, "key:*", "10")); - Assert.That(r.Children.Count, Is.EqualTo(10)); + var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, "key:*", "10")); + Assert.That(r.Children.Count, Is.EqualTo(10)); - r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, "key:*", "10")); - Assert.That(r.Children.Count, Is.EqualTo(10)); + r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, "key:*", "10")); + Assert.That(r.Children.Count, Is.EqualTo(10)); + } } [Test] public async Task Can_call_Cached_Lua_even_after_script_is_flushed() { - await using var redis = new RedisClient().ForAsyncOnly(); - await AddTestKeysAsync(redis, 20); + var redis = new RedisClient().ForAsyncOnly(); + await using (redis as IAsyncDisposable) + { + await AddTestKeysAsync(redis, 20); - var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, "key:*", "10")); - Assert.That(r.Children.Count, Is.EqualTo(10)); + var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, "key:*", "10")); + Assert.That(r.Children.Count, Is.EqualTo(10)); - await ((IRedisNativeClientAsync)redis).ScriptFlushAsync(); + await ((IRedisNativeClientAsync)redis).ScriptFlushAsync(); - r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, "key:*", "10")); - Assert.That(r.Children.Count, Is.EqualTo(10)); + r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, "key:*", "10")); + Assert.That(r.Children.Count, Is.EqualTo(10)); + } } [Test] public async Task Can_call_repeated_scans_in_LUA_longhand() { - await using var redis = new RedisClient().ForAsyncOnly(); - await AddTestKeysAsync(redis, 20); + var redis = new RedisClient().ForAsyncOnly(); + await using (redis as IAsyncDisposable) + { + await AddTestKeysAsync(redis, 20); - var r = await redis.ExecLuaAsync(LuaScript, null, new[] { "key:*", "10" }); - Assert.That(r.Children.Count, Is.EqualTo(10)); + var r = await redis.ExecLuaAsync(LuaScript, null, new[] { "key:*", "10" }); + Assert.That(r.Children.Count, Is.EqualTo(10)); - r = await redis.ExecLuaAsync(LuaScript, null, new[] { "key:*", "40" }); - Assert.That(r.Children.Count, Is.EqualTo(20)); + r = await redis.ExecLuaAsync(LuaScript, null, new[] { "key:*", "40" }); + Assert.That(r.Children.Count, Is.EqualTo(20)); + } } [Test] public async Task Can_call_Cached_Lua_longhand() { - await using var redis = new RedisClient().ForAsyncOnly(); - await AddTestKeysAsync(redis, 20); + var redis = new RedisClient().ForAsyncOnly(); + await using (redis as IAsyncDisposable) + { + await AddTestKeysAsync(redis, 20); - var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); - Assert.That(r.Children.Count, Is.EqualTo(10)); + var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); + Assert.That(r.Children.Count, Is.EqualTo(10)); - r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); - Assert.That(r.Children.Count, Is.EqualTo(10)); + r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); + Assert.That(r.Children.Count, Is.EqualTo(10)); + } } [Test] public async Task Can_call_Cached_Lua_even_after_script_is_flushed_longhand() { - await using var redis = new RedisClient().ForAsyncOnly(); - await AddTestKeysAsync(redis, 20); + var redis = new RedisClient().ForAsyncOnly(); + await using (redis as IAsyncDisposable) + { + await AddTestKeysAsync(redis, 20); - var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); - Assert.That(r.Children.Count, Is.EqualTo(10)); + var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); + Assert.That(r.Children.Count, Is.EqualTo(10)); - await ((IRedisNativeClientAsync)redis).ScriptFlushAsync(); + await ((IRedisNativeClientAsync)redis).ScriptFlushAsync(); - r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); - Assert.That(r.Children.Count, Is.EqualTo(10)); + r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); + Assert.That(r.Children.Count, Is.EqualTo(10)); + } } private const string KeyAttributesScript = @" @@ -170,41 +189,47 @@ public async Task Can_call_Cached_Lua_even_after_script_is_flushed_longhand() [Test] public async Task Can_call_script_with_complex_response() { - await using var redis = new RedisClient().ForAsyncOnly(); - var r = await redis.ExecCachedLuaAsync(KeyAttributesScript, sha1 => + var redis = new RedisClient().ForAsyncOnly(); + await using (redis as IAsyncDisposable) + { + var r = await redis.ExecCachedLuaAsync(KeyAttributesScript, sha1 => redis.ExecLuaShaAsStringAsync(sha1, "key:*", "10")); - r.Print(); + r.Print(); - var results = r.FromJson>(); + var results = r.FromJson>(); - Assert.That(results.Count, Is.EqualTo(10)); + Assert.That(results.Count, Is.EqualTo(10)); - var result = results[0]; - Assert.That(result.Id.StartsWith("key:")); - Assert.That(result.Type, Is.EqualTo("string")); - Assert.That(result.Size, Is.GreaterThan("value:".Length)); - Assert.That(result.Ttl, Is.EqualTo(-1)); + var result = results[0]; + Assert.That(result.Id.StartsWith("key:")); + Assert.That(result.Type, Is.EqualTo("string")); + Assert.That(result.Size, Is.GreaterThan("value:".Length)); + Assert.That(result.Ttl, Is.EqualTo(-1)); + } } [Test] public async Task Can_call_script_with_complex_response_longhand() { - await using var redis = new RedisClient().ForAsyncOnly(); - var r = await redis.ExecCachedLuaAsync(KeyAttributesScript, sha1 => + var redis = new RedisClient().ForAsyncOnly(); + await using (redis as IAsyncDisposable) + { + var r = await redis.ExecCachedLuaAsync(KeyAttributesScript, sha1 => redis.ExecLuaShaAsStringAsync(sha1, null, new[] { "key:*", "10" })); - r.Print(); + r.Print(); - var results = r.FromJson>(); + var results = r.FromJson>(); - Assert.That(results.Count, Is.EqualTo(10)); + Assert.That(results.Count, Is.EqualTo(10)); - var result = results[0]; - Assert.That(result.Id.StartsWith("key:")); - Assert.That(result.Type, Is.EqualTo("string")); - Assert.That(result.Size, Is.GreaterThan("value:".Length)); - Assert.That(result.Ttl, Is.EqualTo(-1)); + var result = results[0]; + Assert.That(result.Id.StartsWith("key:")); + Assert.That(result.Type, Is.EqualTo("string")); + Assert.That(result.Size, Is.GreaterThan("value:".Length)); + Assert.That(result.Ttl, Is.EqualTo(-1)); + } } public class SearchResult @@ -218,78 +243,81 @@ public class SearchResult [Test] public async Task Can_merge_multiple_SearchResults() { - await using var Redis = new RedisClient().ForAsyncOnly(); - var limit = 10; - var query = "key:*"; - - List keys = new List(limit); - await foreach (var key in Redis.ScanAllKeysAsync(pattern: query, pageSize: limit)) + var Redis = new RedisClient().ForAsyncOnly(); + await using (Redis as IAsyncDisposable) { - keys.Add(key); - if (keys.Count == limit) break; - } + var limit = 10; + var query = "key:*"; + + List keys = new List(limit); + await foreach (var key in Redis.ScanAllKeysAsync(pattern: query, pageSize: limit)) + { + keys.Add(key); + if (keys.Count == limit) break; + } - var keyTypes = new Dictionary(); - var keyTtls = new Dictionary(); - var keySizes = new Dictionary(); + var keyTypes = new Dictionary(); + var keyTtls = new Dictionary(); + var keySizes = new Dictionary(); - if (keys.Count > 0) - { - await using (var pipeline = Redis.CreatePipeline()) + if (keys.Count > 0) { - foreach (var key in keys) - pipeline.QueueCommand(r => r.TypeAsync(key), x => keyTypes[key] = x); + await using (var pipeline = Redis.CreatePipeline()) + { + foreach (var key in keys) + pipeline.QueueCommand(r => r.TypeAsync(key), x => keyTypes[key] = x); - foreach (var key in keys) - pipeline.QueueCommand(r => ((IRedisNativeClientAsync)r).PTtlAsync(key), x => keyTtls[key] = x); + foreach (var key in keys) + pipeline.QueueCommand(r => ((IRedisNativeClientAsync)r).PTtlAsync(key), x => keyTtls[key] = x); - await pipeline.FlushAsync(); - } + await pipeline.FlushAsync(); + } - await using (var pipeline = Redis.CreatePipeline()) - { - foreach (var entry in keyTypes) + await using (var pipeline = Redis.CreatePipeline()) { - var key = entry.Key; - switch (entry.Value) + foreach (var entry in keyTypes) { - case "string": - pipeline.QueueCommand(r => r.GetStringCountAsync(key), x => keySizes[key] = x); - break; - case "list": - pipeline.QueueCommand(r => r.GetListCountAsync(key), x => keySizes[key] = x); - break; - case "set": - pipeline.QueueCommand(r => r.GetSetCountAsync(key), x => keySizes[key] = x); - break; - case "zset": - pipeline.QueueCommand(r => r.GetSortedSetCountAsync(key), x => keySizes[key] = x); - break; - case "hash": - pipeline.QueueCommand(r => r.GetHashCountAsync(key), x => keySizes[key] = x); - break; + var key = entry.Key; + switch (entry.Value) + { + case "string": + pipeline.QueueCommand(r => r.GetStringCountAsync(key), x => keySizes[key] = x); + break; + case "list": + pipeline.QueueCommand(r => r.GetListCountAsync(key), x => keySizes[key] = x); + break; + case "set": + pipeline.QueueCommand(r => r.GetSetCountAsync(key), x => keySizes[key] = x); + break; + case "zset": + pipeline.QueueCommand(r => r.GetSortedSetCountAsync(key), x => keySizes[key] = x); + break; + case "hash": + pipeline.QueueCommand(r => r.GetHashCountAsync(key), x => keySizes[key] = x); + break; + } } - } - await pipeline.FlushAsync(); + await pipeline.FlushAsync(); + } } - } - var results = keys.Map(x => new SearchResult - { - Id = x, - Type = keyTypes.GetValueOrDefault(x), - Ttl = keyTtls.GetValueOrDefault(x), - Size = keySizes.GetValueOrDefault(x), - }); - - Assert.That(results.Count, Is.EqualTo(limit)); - - var result = results[0]; - Assert.That(result.Id.StartsWith("key:")); - Assert.That(result.Type, Is.EqualTo("string")); - Assert.That(result.Size, Is.GreaterThan("value:".Length)); - Assert.That(result.Ttl, Is.EqualTo(-1)); + var results = keys.Map(x => new SearchResult + { + Id = x, + Type = keyTypes.GetValueOrDefault(x), + Ttl = keyTtls.GetValueOrDefault(x), + Size = keySizes.GetValueOrDefault(x), + }); + + Assert.That(results.Count, Is.EqualTo(limit)); + + var result = results[0]; + Assert.That(result.Id.StartsWith("key:")); + Assert.That(result.Type, Is.EqualTo("string")); + Assert.That(result.Size, Is.GreaterThan("value:".Length)); + Assert.That(result.Ttl, Is.EqualTo(-1)); + } } } } \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.Async.cs b/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.Async.cs index fdf442f6..a900582b 100644 --- a/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.Async.cs @@ -188,7 +188,7 @@ public async Task Does_loop_through_ReadWrite_hosts() { await using var manager = CreateAndStartManager(); var client1 = await manager.GetClientAsync(); - await client1.DisposeAsync(); + await client1.TryDisposeAsync(); var client2 = await manager.GetClientAsync(); var client3 = await manager.GetClientAsync(); var client4 = await manager.GetClientAsync(); @@ -206,9 +206,9 @@ public async Task Does_loop_through_ReadOnly_hosts() { await using var manager = CreateAndStartManager(); var client1 = await manager.GetReadOnlyClientAsync(); - await client1.DisposeAsync(); + await client1.TryDisposeAsync(); var client2 = await manager.GetReadOnlyClientAsync(); - await client2.DisposeAsync(); + await client2.TryDisposeAsync(); var client3 = await manager.GetReadOnlyClientAsync(); var client4 = await manager.GetReadOnlyClientAsync(); var client5 = await manager.GetReadOnlyClientAsync(); @@ -237,35 +237,35 @@ public async Task Can_have_different_pool_size_and_host_configurations() } ); //A poolsize of 4 will not block getting 4 clients - await using (var client1 = await manager.GetClientAsync()) - await using (var client2 = await manager.GetClientAsync()) - await using (var client3 = await manager.GetClientAsync()) - await using (var client4 = await manager.GetClientAsync()) + await using (var client1 = (await manager.GetClientAsync()).WrapForDispose()) + await using (var client2 = (await manager.GetClientAsync()).WrapForDispose()) + await using (var client3 = (await manager.GetClientAsync()).WrapForDispose()) + await using (var client4 = (await manager.GetClientAsync()).WrapForDispose()) { - AssertClientHasHost(client1, writeHosts[0]); - AssertClientHasHost(client2, writeHosts[0]); - AssertClientHasHost(client3, writeHosts[0]); - AssertClientHasHost(client4, writeHosts[0]); + AssertClientHasHost(client1.Value, writeHosts[0]); + AssertClientHasHost(client2.Value, writeHosts[0]); + AssertClientHasHost(client3.Value, writeHosts[0]); + AssertClientHasHost(client4.Value, writeHosts[0]); } //A poolsize of 8 will not block getting 8 clients - await using (var client1 = await manager.GetReadOnlyClientAsync()) - await using (var client2 = await manager.GetReadOnlyClientAsync()) - await using (var client3 = await manager.GetReadOnlyClientAsync()) - await using (var client4 = await manager.GetReadOnlyClientAsync()) - await using (var client5 = await manager.GetReadOnlyClientAsync()) - await using (var client6 = await manager.GetReadOnlyClientAsync()) - await using (var client7 = await manager.GetReadOnlyClientAsync()) - await using (var client8 = await manager.GetReadOnlyClientAsync()) + await using (var client1 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) + await using (var client2 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) + await using (var client3 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) + await using (var client4 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) + await using (var client5 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) + await using (var client6 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) + await using (var client7 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) + await using (var client8 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) { - AssertClientHasHost(client1, readHosts[0]); - AssertClientHasHost(client2, readHosts[1]); - AssertClientHasHost(client3, readHosts[0]); - AssertClientHasHost(client4, readHosts[1]); - AssertClientHasHost(client5, readHosts[0]); - AssertClientHasHost(client6, readHosts[1]); - AssertClientHasHost(client7, readHosts[0]); - AssertClientHasHost(client8, readHosts[1]); + AssertClientHasHost(client1.Value, readHosts[0]); + AssertClientHasHost(client2.Value, readHosts[1]); + AssertClientHasHost(client3.Value, readHosts[0]); + AssertClientHasHost(client4.Value, readHosts[1]); + AssertClientHasHost(client5.Value, readHosts[0]); + AssertClientHasHost(client6.Value, readHosts[1]); + AssertClientHasHost(client7.Value, readHosts[0]); + AssertClientHasHost(client8.Value, readHosts[1]); } } @@ -284,7 +284,7 @@ public async Task Does_block_ReadWrite_clients_pool() #pragma warning restore IDE0039 // Use local function { await Task.Delay(delay + TimeSpan.FromSeconds(0.5)); - await client4.DisposeAsync(); + await client4.TryDisposeAsync(); }; #if NETCORE @@ -321,7 +321,7 @@ public async Task Does_block_ReadOnly_clients_pool() #pragma warning restore IDE0039 // Use local function { await Task.Delay(delay + TimeSpan.FromSeconds(0.5)); - await client3.DisposeAsync(); + await client3.TryDisposeAsync(); }; #if NETCORE _ =Task.Run(func); diff --git a/tests/ServiceStack.Redis.Tests/RedisCacheClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisCacheClientTests.Async.cs index fcbde83d..c98b8a29 100644 --- a/tests/ServiceStack.Redis.Tests/RedisCacheClientTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisCacheClientTests.Async.cs @@ -10,13 +10,13 @@ namespace ServiceStack.Redis.Tests [Category("Async")] public class RedisCacheClientTestsAsync { - private ICacheClientExtendedAsync cacheClient; + private ICacheClientAsync cacheClient; [SetUp] public async Task OnBeforeEachTest() { - if (cacheClient != null) - await cacheClient.DisposeAsync(); + if (cacheClient is IAsyncDisposable d) + await d.DisposeAsync(); cacheClient = new RedisClient(TestConfig.SingleHost); await cacheClient.FlushAllAsync(); @@ -129,7 +129,8 @@ public async Task Can_GetTimeToLive() [Test] public async Task Can_increment_and_reset_values() { - await using (var client = await new RedisManagerPool(TestConfig.SingleHost).GetCacheClientAsync()) + var client = await new RedisManagerPool(TestConfig.SingleHost).GetCacheClientAsync(); + await using (client as IAsyncDisposable) { Assert.That(await client.IncrementAsync("incr:counter", 10), Is.EqualTo(10)); await client.SetAsync("incr:counter", 0); diff --git a/tests/ServiceStack.Redis.Tests/RedisClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientTests.Async.cs index 5b3cba1e..4f2535b1 100644 --- a/tests/ServiceStack.Redis.Tests/RedisClientTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisClientTests.Async.cs @@ -402,10 +402,13 @@ public async Task Can_AcquireLock_TimeOut() try { - await using var client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); - await using (await client.AcquireLockAsync(lockKey, waitFor)) + var client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await using (client as IAsyncDisposable) { - await client.IncrementValueAsync(key); //2 + await using (await client.AcquireLockAsync(lockKey, waitFor)) + { + await client.IncrementValueAsync(key); //2 + } } } catch (TimeoutException) @@ -528,11 +531,14 @@ public async Task Can_store_Dictionary() var map = new Dictionary(); keys.ForEach(x => map[x] = "val" + x); - await using var client = RedisClient.New().ForAsyncOnly(); - await client.SetAllAsync(map); + var client = RedisClient.New().ForAsyncOnly(); + await using (client as IAsyncDisposable) + { + await client.SetAllAsync(map); - var all = await client.GetValuesMapAsync(keys); - Assert.AreEqual(map, all); + var all = await client.GetValuesMapAsync(keys); + Assert.AreEqual(map, all); + } } [Test] @@ -544,11 +550,14 @@ public async Task Can_store_Dictionary_as_objects() ["key_b"] = null }; - await using var client = RedisClient.New().ForAsyncOnly(); - await client.SetAllAsync(map); + var client = RedisClient.New().ForAsyncOnly(); + await using (client as IAsyncDisposable) + { + await client.SetAllAsync(map); - Assert.That(await client.GetAsync("key_a"), Is.EqualTo("123")); - Assert.That(await client.GetValueAsync("key_b"), Is.EqualTo("")); + Assert.That(await client.GetAsync("key_a"), Is.EqualTo("123")); + Assert.That(await client.GetValueAsync("key_b"), Is.EqualTo("")); + } } @@ -561,32 +570,41 @@ public async Task Can_store_Dictionary_as_bytes() ["key_b"] = null }; - await using var client = RedisClient.New().ForAsyncOnly(); - await client.SetAllAsync(map); + var client = RedisClient.New().ForAsyncOnly(); + await using (client as IAsyncDisposable) + { + await client.SetAllAsync(map); - Assert.That(await client.GetAsync("key_a"), Is.EqualTo("123")); - Assert.That(await client.GetValueAsync("key_b"), Is.EqualTo("")); + Assert.That(await client.GetAsync("key_a"), Is.EqualTo("123")); + Assert.That(await client.GetValueAsync("key_b"), Is.EqualTo("")); + } } [Test] public async Task Should_reset_slowlog() { - await using var client = RedisClient.New().ForAsyncOnly(); - await client.SlowlogResetAsync(); + var client = RedisClient.New().ForAsyncOnly(); + await using (client as IAsyncDisposable) + { + await client.SlowlogResetAsync(); + } } [Test] public async Task Can_get_slowlog() { - await using var client = RedisClient.New().ForAsyncOnly(); - var log = await client.GetSlowlogAsync(10); - - foreach (var t in log) + var client = RedisClient.New().ForAsyncOnly(); + await using (client as IAsyncDisposable) { - Console.WriteLine(t.Id); - Console.WriteLine(t.Duration); - Console.WriteLine(t.Timestamp); - Console.WriteLine(string.Join(":", t.Arguments)); + var log = await client.GetSlowlogAsync(10); + + foreach (var t in log) + { + Console.WriteLine(t.Id); + Console.WriteLine(t.Duration); + Console.WriteLine(t.Timestamp); + Console.WriteLine(string.Join(":", t.Arguments)); + } } } @@ -594,22 +612,25 @@ public async Task Can_get_slowlog() [Test] public async Task Can_change_db_at_runtime() { - await using var redis = new RedisClient(TestConfig.SingleHost, TestConfig.RedisPort, db: 1).ForAsyncOnly(); - var val = Environment.TickCount; - var key = "test" + val; - try - { - await redis.SetAsync(key, val); - await redis.SelectAsync(2); - Assert.That(await redis.GetAsync(key), Is.EqualTo(0)); - await redis.SelectAsync(1); - Assert.That(await redis.GetAsync(key), Is.EqualTo(val)); - await redis.DisposeAsync(); - } - finally + var redis = new RedisClient(TestConfig.SingleHost, TestConfig.RedisPort, db: 1).ForAsyncOnly(); + await using (redis as IAsyncDisposable) { - await redis.SelectAsync(1); - await redis.RemoveAsync(key); + var val = Environment.TickCount; + var key = "test" + val; + try + { + await redis.SetAsync(key, val); + await redis.SelectAsync(2); + Assert.That(await redis.GetAsync(key), Is.EqualTo(0)); + await redis.SelectAsync(1); + Assert.That(await redis.GetAsync(key), Is.EqualTo(val)); + await redis.TryDisposeAsync(); + } + finally + { + await redis.SelectAsync(1); + await redis.RemoveAsync(key); + } } } diff --git a/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs index d7c6244e..efd0d345 100644 --- a/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs @@ -41,6 +41,23 @@ public static async ValueTask CountAsync(this IAsyncEnumerable source return count; } + public static ValueTask TryDisposeAsync(this object obj) + { + if (obj is IAsyncDisposable d) return d.DisposeAsync(); + return default; + } + + public static WrappedDisposable WrapForDispose(this T value) where T : class + => new WrappedDisposable(value); + + public readonly struct WrappedDisposable : IAsyncDisposable where T : class + { + public readonly T Value; + public ValueTask DisposeAsync() => TryDisposeAsync(Value); + public WrappedDisposable(T value) => Value = value; + public static implicit operator T (in WrappedDisposable value) => value.Value; + } + public static IRedisClientAsync ForAsyncOnly(this RedisClient client) { #if DEBUG diff --git a/tests/ServiceStack.Redis.Tests/RedisGeoTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisGeoTests.Async.cs index 1d26818c..683b29a2 100644 --- a/tests/ServiceStack.Redis.Tests/RedisGeoTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisGeoTests.Async.cs @@ -1,5 +1,5 @@ using NUnit.Framework; -using ServiceStack.Text; +using System; using System.Threading.Tasks; namespace ServiceStack.Redis.Tests @@ -18,7 +18,10 @@ public RedisGeoTestsAsync() [OneTimeTearDown] public async Task OneTimeTearDown() { - await redis.DisposeAsync(); + if (redis is IAsyncDisposable d) + { + await d.DisposeAsync(); + } } [Test] diff --git a/tests/ServiceStack.Redis.Tests/RedisHyperLogTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisHyperLogTests.Async.cs index c29bb227..f0d64e1d 100644 --- a/tests/ServiceStack.Redis.Tests/RedisHyperLogTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisHyperLogTests.Async.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using System; using System.Threading.Tasks; namespace ServiceStack.Redis.Tests @@ -12,24 +13,27 @@ public class RedisHyperLogTestsAsync [Test] public async Task Can_Add_to_Hyperlog() { - await using var redis = Connect(); + var redis = Connect(); + await using (redis as IAsyncDisposable) + { - await redis.FlushAllAsync(); + await redis.FlushAllAsync(); - await redis.AddToHyperLogAsync("hyperlog", new[] { "a", "b", "c" }); - await redis.AddToHyperLogAsync("hyperlog", new[] { "c", "d" }); + await redis.AddToHyperLogAsync("hyperlog", new[] { "a", "b", "c" }); + await redis.AddToHyperLogAsync("hyperlog", new[] { "c", "d" }); - var count = await redis.CountHyperLogAsync("hyperlog"); + var count = await redis.CountHyperLogAsync("hyperlog"); - Assert.That(count, Is.EqualTo(4)); + Assert.That(count, Is.EqualTo(4)); - await redis.AddToHyperLogAsync("hyperlog2", new[] { "c", "d", "e", "f" }); + await redis.AddToHyperLogAsync("hyperlog2", new[] { "c", "d", "e", "f" }); - await redis.MergeHyperLogsAsync("hypermerge", new[] { "hyperlog", "hyperlog2" }); + await redis.MergeHyperLogsAsync("hypermerge", new[] { "hyperlog", "hyperlog2" }); - var mergeCount = await redis.CountHyperLogAsync("hypermerge"); + var mergeCount = await redis.CountHyperLogAsync("hypermerge"); - Assert.That(mergeCount, Is.EqualTo(6)); + Assert.That(mergeCount, Is.EqualTo(6)); + } } } } \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisPersistenceProviderTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisPersistenceProviderTests.Async.cs index 0a20c8ec..830a75ca 100644 --- a/tests/ServiceStack.Redis.Tests/RedisPersistenceProviderTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisPersistenceProviderTests.Async.cs @@ -1,5 +1,6 @@ using NUnit.Framework; using ServiceStack.Common.Tests.Models; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -12,7 +13,8 @@ public class RedisPersistenceProviderTestsAsync [Test] public async Task Can_Store_and_GetById_ModelWithIdAndName() { - await using (IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost)) + IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost); + await using (redis as IAsyncDisposable) { const int modelId = 1; var to = ModelWithIdAndName.Create(modelId); @@ -27,7 +29,8 @@ public async Task Can_Store_and_GetById_ModelWithIdAndName() [Test] public async Task Can_StoreAll_and_GetByIds_ModelWithIdAndName() { - await using (IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost)) + IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost); + await using (redis as IAsyncDisposable) { var ids = new[] { 1, 2, 3, 4, 5 }; var tos = ids.Map(ModelWithIdAndName.Create); @@ -44,7 +47,8 @@ public async Task Can_StoreAll_and_GetByIds_ModelWithIdAndName() [Test] public async Task Can_Delete_ModelWithIdAndName() { - await using (IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost)) + IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost); + await using (redis as IAsyncDisposable) { var ids = new List { 1, 2, 3, 4, 5 }; var tos = ids.ConvertAll(ModelWithIdAndName.Create); diff --git a/tests/ServiceStack.Redis.Tests/RedisPipelineTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisPipelineTests.Async.cs index 0b91fd04..32c528de 100644 --- a/tests/ServiceStack.Redis.Tests/RedisPipelineTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisPipelineTests.Async.cs @@ -29,7 +29,7 @@ public async Task Can_call_single_operation_in_pipeline() { pipeline.QueueCommand(r => r.IncrementValueAsync(Key)); var map = new Dictionary(); - pipeline.QueueCommand(r => r.GetAsync(Key), y => map[Key] = y); + pipeline.QueueCommand(r => new ValueTask(r.GetAsync(Key)), y => map[Key] = y); await pipeline.FlushAsync(); } @@ -272,7 +272,7 @@ public async Task Can_call_AddRangeToSet_in_pipeline() await using var pipeline = RedisAsync.CreatePipeline(); var key = "pipeline-test"; - pipeline.QueueCommand(r => r.RemoveAsync(key)); + pipeline.QueueCommand(r => new ValueTask(r.RemoveAsync(key))); pipeline.QueueCommand(r => r.AddRangeToSetAsync(key, new[] { "A", "B", "C" }.ToList())); await pipeline.FlushAsync(); diff --git a/tests/ServiceStack.Redis.Tests/RedisPubSubTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisPubSubTests.Async.cs index 20e2df9e..55f1bd82 100644 --- a/tests/ServiceStack.Redis.Tests/RedisPubSubTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisPubSubTests.Async.cs @@ -51,9 +51,12 @@ public async Task Can_Subscribe_and_Publish_single_message() ThreadPool.QueueUserWorkItem(async x => { await Task.Delay(100); // to be sure that we have subscribers - await using var redisClient = CreateRedisClient().ForAsyncOnly(); - Log("Publishing '{0}' to '{1}'", message, channelName); - await redisClient.PublishMessageAsync(channelName, message); + var redisClient = CreateRedisClient().ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) + { + Log("Publishing '{0}' to '{1}'", message, channelName); + await redisClient.PublishMessageAsync(channelName, message); + } }); Log("Start Listening On " + channelName); @@ -100,9 +103,12 @@ public async Task Can_Subscribe_and_Publish_single_message_using_wildcard() ThreadPool.QueueUserWorkItem(async x => { await Task.Delay(100); // to be sure that we have subscribers - await using var redisClient = CreateRedisClient().ForAsyncOnly(); - Log("Publishing '{0}' to '{1}'", message, channelName); - await redisClient.PublishMessageAsync(channelName, message); + var redisClient = CreateRedisClient().ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) + { + Log("Publishing '{0}' to '{1}'", message, channelName); + await redisClient.PublishMessageAsync(channelName, message); + } }); Log("Start Listening On " + channelName); @@ -155,12 +161,15 @@ public async Task Can_Subscribe_and_Publish_multiple_message() { await Task.Delay(100); // to be sure that we have subscribers - await using var redisClient = CreateRedisClient().ForAsyncOnly(); - for (var i = 0; i < publishMessageCount; i++) + var redisClient = CreateRedisClient().ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) { - var message = messagePrefix + i; - Log("Publishing '{0}' to '{1}'", message, channelName); - await redisClient.PublishMessageAsync(channelName, message); + for (var i = 0; i < publishMessageCount; i++) + { + var message = messagePrefix + i; + Log("Publishing '{0}' to '{1}'", message, channelName); + await redisClient.PublishMessageAsync(channelName, message); + } } }); @@ -219,11 +228,14 @@ public async Task Can_Subscribe_and_Publish_message_to_multiple_channels() { await Task.Delay(100); // to be sure that we have subscribers - await using var redisClient = CreateRedisClient().ForAsyncOnly(); - foreach (var channel in channels) + var redisClient = CreateRedisClient().ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) { - Log("Publishing '{0}' to '{1}'", message, channel); - await redisClient.PublishMessageAsync(channel, message); + foreach (var channel in channels) + { + Log("Publishing '{0}' to '{1}'", message, channel); + await redisClient.PublishMessageAsync(channel, message); + } } }); @@ -255,10 +267,13 @@ public async Task Can_Subscribe_to_channel_pattern() { await Task.Delay(100); // to be sure that we have subscribers - await using var redisClient = CreateRedisClient().ForAsyncOnly(); - Log("Publishing msg..."); - await redisClient.PublishMessageAsync(PrefixedKey("CHANNEL4:TITLE1"), "hello"); // .ToUtf8Bytes() - }); + var redisClient = CreateRedisClient().ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) + { + Log("Publishing msg..."); + await redisClient.PublishMessageAsync(PrefixedKey("CHANNEL4:TITLE1"), "hello"); // .ToUtf8Bytes() + } + }); Log("Start Listening On"); await subscription.SubscribeToChannelsMatchingAsync(new[] { PrefixedKey("CHANNEL4:TITLE*") }); @@ -280,10 +295,13 @@ public async Task Can_Subscribe_to_multiplechannel_pattern() { await Task.Delay(100); // to be sure that we have subscribers - await using var redisClient = CreateRedisClient().ForAsyncOnly(); - Log("Publishing msg..."); - await redisClient.PublishMessageAsync(PrefixedKey("CHANNEL5:BODY"), "hello"); // .ToUtf8Bytes() - }); + var redisClient = CreateRedisClient().ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) + { + Log("Publishing msg..."); + await redisClient.PublishMessageAsync(PrefixedKey("CHANNEL5:BODY"), "hello"); // .ToUtf8Bytes() + } + }); Log("Start Listening On"); await subscription.SubscribeToChannelsMatchingAsync(channels); diff --git a/tests/ServiceStack.Redis.Tests/RedisTransactionTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisTransactionTests.Async.cs index 565dedcb..300f34a2 100644 --- a/tests/ServiceStack.Redis.Tests/RedisTransactionTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisTransactionTests.Async.cs @@ -30,7 +30,7 @@ public async Task Can_call_single_operation_in_transaction() { trans.QueueCommand(r => r.IncrementValueAsync(Key)); var map = new Dictionary(); - trans.QueueCommand(r => r.GetAsync(Key), y => map[Key] = y); + trans.QueueCommand(r => new ValueTask(r.GetAsync(Key)), y => map[Key] = y); await trans.CommitAsync(); } @@ -59,7 +59,7 @@ public async Task Watch_aborts_transaction() await RedisAsync.WatchAsync(new[] { Key }); await RedisAsync.SetAsync(Key, value1); await using var trans = await RedisAsync.CreateTransactionAsync(); - trans.QueueCommand(r => r.SetAsync(Key, value1)); + trans.QueueCommand(r => new ValueTask(r.SetAsync(Key, value1))); var success = await trans.CommitAsync(); Assert.False(success); Assert.AreEqual(value1, await RedisAsync.GetAsync(Key)); @@ -251,8 +251,8 @@ public async Task Transaction_can_issue_watch() await using (var trans = await RedisAsync.CreateTransactionAsync()) { - trans.QueueCommand(r => r.SetAsync(Key, 1)); - trans.QueueCommand(r => r.SetAsync(KeySquared, 2)); + trans.QueueCommand(r => new ValueTask(r.SetAsync(Key, 1))); + trans.QueueCommand(r => new ValueTask(r.SetAsync(KeySquared, 2))); await trans.CommitAsync(); } @@ -270,8 +270,8 @@ public async Task Can_set_Expiry_on_key_in_transaction() await using (var trans = await RedisAsync.CreateTransactionAsync()) { - trans.QueueCommand(r => r.AddAsync(key, "Foo")); - trans.QueueCommand(r => r.AddAsync(keyWithTtl, "Bar", expiresIn)); + trans.QueueCommand(r => new ValueTask(r.AddAsync(key, "Foo"))); + trans.QueueCommand(r => new ValueTask(r.AddAsync(keyWithTtl, "Bar", expiresIn))); if (!await trans.CommitAsync()) throw new Exception("Transaction Failed"); @@ -294,7 +294,7 @@ public async Task Does_not_set_Expiry_on_existing_key_in_transaction() await using (var trans = await RedisAsync.CreateTransactionAsync()) { - trans.QueueCommand(r => r.AddAsync(key, "Bar", expiresIn)); + trans.QueueCommand(r => new ValueTask(r.AddAsync(key, "Bar", expiresIn))); if (!await trans.CommitAsync()) throw new Exception("Transaction Failed"); diff --git a/tests/ServiceStack.Redis.Tests/ShippersExample.Async.cs b/tests/ServiceStack.Redis.Tests/ShippersExample.Async.cs index e93eb09a..26cfd6cb 100644 --- a/tests/ServiceStack.Redis.Tests/ShippersExample.Async.cs +++ b/tests/ServiceStack.Redis.Tests/ShippersExample.Async.cs @@ -49,7 +49,8 @@ static void Dump(string message, T entity) [Test] public async Task Shippers_UseCase() { - await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) { //Create a 'strongly-typed' API that makes all Redis Value operations to apply against Shippers IRedisTypedClientAsync redis = redisClient.As(); diff --git a/tests/ServiceStack.Redis.Tests/ValueTypeExamples.Async.cs b/tests/ServiceStack.Redis.Tests/ValueTypeExamples.Async.cs index c4c00063..996c1673 100644 --- a/tests/ServiceStack.Redis.Tests/ValueTypeExamples.Async.cs +++ b/tests/ServiceStack.Redis.Tests/ValueTypeExamples.Async.cs @@ -1,5 +1,6 @@ using NUnit.Framework; using ServiceStack.Redis.Generic; +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -11,7 +12,8 @@ public class ValueTypeExamplesAsync [SetUp] public async Task SetUp() { - await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) { await redisClient.FlushAllAsync(); } @@ -24,7 +26,8 @@ public async Task Working_with_int_values() const int intValue = 1; //STORING AN INT USING THE BASIC CLIENT - await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) { await redisClient.SetValueAsync(intKey, intValue.ToString()); string strGetIntValue = await redisClient.GetValueAsync(intKey); @@ -34,7 +37,8 @@ public async Task Working_with_int_values() } //STORING AN INT USING THE GENERIC CLIENT - await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) { //Create a generic client that treats all values as ints: IRedisTypedClientAsync intRedis = redisClient.As(); @@ -53,7 +57,8 @@ public async Task Working_with_int_list_values() var intValues = new List { 2, 4, 6, 8 }; //STORING INTS INTO A LIST USING THE BASIC CLIENT - await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) { IRedisListAsync strList = redisClient.Lists[intListKey]; @@ -73,7 +78,8 @@ public async Task Working_with_int_list_values() } //STORING INTS INTO A LIST USING THE GENERIC CLIENT - await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) { //Create a generic client that treats all values as ints: IRedisTypedClientAsync intRedis = redisClient.As(); @@ -98,7 +104,8 @@ public class IntAndString [Test] public async Task Working_with_Generic_types() { - await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) + var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await using (redisClient as IAsyncDisposable) { //Create a typed Redis client that treats all values as IntAndString: var typedRedis = redisClient.As(); From 1daefb63ab4267436fef5eae8ffd0b22284f3768 Mon Sep 17 00:00:00 2001 From: mgravell Date: Wed, 2 Sep 2020 11:43:58 +0100 Subject: [PATCH 009/107] reapply using IAsyncDisposable --- .../BasicRedisClientManager.Async.cs | 142 +++------ src/ServiceStack.Redis/RedisClient.Async.cs | 14 +- .../RedisClientManagerCacheClient.Async.cs | 152 +++------- .../RedisClientsManagerExtensions.Async.cs | 42 +-- .../AdhocClientTests.Async.cs | 16 +- .../BasicRediscClientManagerTests.Async.cs | 12 +- .../Generic/RedisClientHashTestsBase.Async.cs | 4 +- .../Generic/RedisClientListTestExtra.Async.cs | 4 +- .../Generic/RedisClientListTestsBase.Async.cs | 4 +- .../Generic/RedisClientSetTestsBase.Async.cs | 4 +- ...RedisPersistenceProviderTestsBase.Async.cs | 4 +- .../LuaCachedScripts.Async.cs | 282 ++++++++---------- .../PooledRedisClientManagerTests.Async.cs | 58 ++-- .../RedisCacheClientTests.Async.cs | 16 +- .../RedisClientTests.Async.cs | 105 +++---- .../RedisClientTestsBase.Async.cs | 17 -- .../RedisGeoTests.Async.cs | 4 +- .../RedisHyperLogTests.Async.cs | 23 +- .../RedisPersistenceProviderTests.Async.cs | 56 ++-- .../RedisPubSubTests.Async.cs | 60 ++-- .../ShippersExample.Async.cs | 115 ++++--- .../ValueTypeExamples.Async.cs | 70 ++--- 22 files changed, 476 insertions(+), 728 deletions(-) diff --git a/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs b/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs index d8fa8bc8..39054fc9 100644 --- a/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs +++ b/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs @@ -14,6 +14,7 @@ using ServiceStack.Redis.Internal; using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -55,182 +56,125 @@ ValueTask IAsyncDisposable.DisposeAsync() async Task ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) { - var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.GetAsync(key).ConfigureAwait(false); - } + await using var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.GetAsync(key).ConfigureAwait(false); } async Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.SetAsync(key, value, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.SetAsync(key, value, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.SetAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.SetAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - await client.FlushAllAsync(cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await client.FlushAllAsync(cancellationToken).ConfigureAwait(false); } async Task> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) { - var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.GetAllAsync(keys, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.GetAllAsync(keys, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - await client.SetAllAsync(values, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await client.SetAllAsync(values, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.RemoveAsync(key, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.RemoveAsync(key, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - await client.RemoveAllAsync(keys, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await client.RemoveAllAsync(keys, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.IncrementAsync(key, amount, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.IncrementAsync(key, amount, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.DecrementAsync(key, amount, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.DecrementAsync(key, amount, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.AddAsync(key, value, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.AddAsync(key, value, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.AddAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.ReplaceAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.AddAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.ReplaceAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) { - var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.GetTimeToLiveAsync(key, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.GetTimeToLiveAsync(key, cancellationToken).ConfigureAwait(false); } - async Task> ICacheClientAsync.GetKeysByPatternAsync(string pattern, CancellationToken cancellationToken) + async IAsyncEnumerable ICacheClientAsync.GetKeysByPatternAsync(string pattern, [EnumeratorCancellation] CancellationToken cancellationToken) { - var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) + await using var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + await foreach (var key in client.GetKeysByPatternAsync(pattern, cancellationToken).ConfigureAwait(false).WithCancellation(cancellationToken)) { - return await client.GetKeysByPatternAsync(pattern, cancellationToken).ConfigureAwait(false); + yield return key; } } async Task ICacheClientAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationToken) { - var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - await client.RemoveExpiredEntriesAsync(cancellationToken).ConfigureAwait(false); - } + await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); + await client.RemoveExpiredEntriesAsync(cancellationToken).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClient.Async.cs b/src/ServiceStack.Redis/RedisClient.Async.cs index 81c95286..b058bbb7 100644 --- a/src/ServiceStack.Redis/RedisClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClient.Async.cs @@ -439,21 +439,13 @@ Task> ICacheClientAsync.GetAllAsync(IEnumerable ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) => NativeAsync.DelAsync(key, cancellationToken).IsSuccessTaskAsync(); - async Task> ICacheClientAsync.GetKeysByPatternAsync(string pattern, CancellationToken cancellationToken) - { - // buffer to match shape - var list = new List(); - await foreach (var key in AsAsync().ScanAllKeysAsync(pattern, cancellationToken: cancellationToken).ConfigureAwait(false).WithCancellation(cancellationToken)) - { - list.Add(key); - } - return list; - } + IAsyncEnumerable ICacheClientAsync.GetKeysByPatternAsync(string pattern, CancellationToken cancellationToken) + => AsAsync().ScanAllKeysAsync(pattern, cancellationToken: cancellationToken); Task ICacheClientAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationToken) { //Redis automatically removed expired Cache Entries - return default; + return Task.CompletedTask; } async Task IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, CancellationToken cancellationToken) diff --git a/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs b/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs index 7e259345..399f6d8e 100644 --- a/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs @@ -23,91 +23,64 @@ private ValueTask GetClientAsync(in CancellationToken cancell async Task ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) { - var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.GetAsync(key, cancellationToken).ConfigureAwait(false); - } + await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.GetAsync(key, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.SetAsync(key, value, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.SetAsync(key, value, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.SetAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.SetAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - await client.FlushAllAsync(cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await client.FlushAllAsync(cancellationToken).ConfigureAwait(false); } async Task> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) { - var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.GetAllAsync(keys, cancellationToken).ConfigureAwait(false); - } + await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.GetAllAsync(keys, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - await client.SetAllAsync(values, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await client.SetAllAsync(values, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.RemoveAsync(key, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.RemoveAsync(key, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) { - var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.GetTimeToLiveAsync(key, cancellationToken).ConfigureAwait(false); - } + await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + return await client.GetTimeToLiveAsync(key, cancellationToken).ConfigureAwait(false); } - async Task> ICacheClientAsync.GetKeysByPatternAsync(string pattern, CancellationToken cancellationToken) + async IAsyncEnumerable ICacheClientAsync.GetKeysByPatternAsync(string pattern, [EnumeratorCancellation] CancellationToken cancellationToken) { - var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) + await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + await foreach (var key in client.GetKeysByPatternAsync(pattern, cancellationToken).ConfigureAwait(false).WithCancellation(cancellationToken)) { - return await client.GetKeysByPatternAsync(pattern, cancellationToken); + yield return key; } } @@ -119,107 +92,74 @@ Task ICacheClientAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationT async Task IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + if (client is IRemoveByPatternAsync redisClient) { - if (client is IRemoveByPatternAsync redisClient) - { - await redisClient.RemoveByPatternAsync(pattern, cancellationToken).ConfigureAwait(false); - } + await redisClient.RemoveByPatternAsync(pattern, cancellationToken).ConfigureAwait(false); } } async Task IRemoveByPatternAsync.RemoveByRegexAsync(string regex, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + if (client is IRemoveByPatternAsync redisClient) { - if (client is IRemoveByPatternAsync redisClient) - { - await redisClient.RemoveByRegexAsync(regex, cancellationToken).ConfigureAwait(false); - } + await redisClient.RemoveByRegexAsync(regex, cancellationToken).ConfigureAwait(false); } } async Task ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - await client.RemoveAllAsync(keys, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await client.RemoveAllAsync(keys, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.IncrementAsync(key, amount, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.IncrementAsync(key, amount, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.DecrementAsync(key, amount, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.DecrementAsync(key, amount, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.AddAsync(key, value, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.AddAsync(key, value, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.AddAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.ReplaceAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.AddAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); } async Task ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) { - var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await using (client as IAsyncDisposable) - { - return await client.ReplaceAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); - } + await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs b/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs index 0a65b206..a8b917c3 100644 --- a/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs +++ b/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs @@ -71,20 +71,14 @@ public static ValueTask GetReadOnlyCacheClientAsync(this IRed public static async ValueTask ExecAsync(this IRedisClientsManager redisManager, Func lambda) { - var redis = await redisManager.GetClientAsync().ConfigureAwait(false); - await using (redis as IAsyncDisposable) - { - await lambda(redis).ConfigureAwait(false); - } + await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + await lambda(redis).ConfigureAwait(false); } public static async ValueTask ExecAsync(this IRedisClientsManager redisManager, Func> lambda) { - var redis = await redisManager.GetClientAsync().ConfigureAwait(false); - await using (redis as IAsyncDisposable) - { - return await lambda(redis).ConfigureAwait(false); - } + await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + return await lambda(redis).ConfigureAwait(false); } //public static void ExecTrans(this IRedisClientsManager redisManager, Action lambda) @@ -100,38 +94,26 @@ public static async ValueTask ExecAsync(this IRedisClientsManager redisMan public static async ValueTask ExecAsAsync(this IRedisClientsManager redisManager, Func, ValueTask> lambda) { - var redis = await redisManager.GetClientAsync().ConfigureAwait(false); - await using (redis as IAsyncDisposable) - { - await lambda(redis.As()).ConfigureAwait(false); - } + await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + await lambda(redis.As()).ConfigureAwait(false); } public static async ValueTask ExecAsAsync(this IRedisClientsManager redisManager, Func, ValueTask> lambda) { - var redis = await redisManager.GetClientAsync().ConfigureAwait(false); - await using (redis as IAsyncDisposable) - { - return await lambda(redis.As()).ConfigureAwait(false); - } + await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + return await lambda(redis.As()).ConfigureAwait(false); } public static async ValueTask> ExecAsAsync(this IRedisClientsManager redisManager, Func, ValueTask>> lambda) { - var redis = await redisManager.GetClientAsync().ConfigureAwait(false); - await using (redis as IAsyncDisposable) - { - return await lambda(redis.As()).ConfigureAwait(false); - } + await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + return await lambda(redis.As()).ConfigureAwait(false); } public static async ValueTask> ExecAsAsync(this IRedisClientsManager redisManager, Func, ValueTask>> lambda) { - var redis = await redisManager.GetClientAsync().ConfigureAwait(false); - await using (redis as IAsyncDisposable) - { - return await lambda(redis.As()).ConfigureAwait(false); - } + await using var redis = await redisManager.GetClientAsync().ConfigureAwait(false); + return await lambda(redis.As()).ConfigureAwait(false); } } diff --git a/tests/ServiceStack.Redis.Tests/AdhocClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/AdhocClientTests.Async.cs index 9c4366e5..0bbf678f 100644 --- a/tests/ServiceStack.Redis.Tests/AdhocClientTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/AdhocClientTests.Async.cs @@ -10,16 +10,14 @@ public class AdhocClientTestsAsync [Test] public async Task Search_Test() { - var client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); - await using (client as IAsyncDisposable) - { - const string cacheKey = "urn+metadata:All:SearchProProfiles?SwanShinichi Osawa /0/8,0,0,0"; - const long value = 1L; - await client.SetAsync(cacheKey, value); - var result = await client.GetAsync(cacheKey); + await using var client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + + const string cacheKey = "urn+metadata:All:SearchProProfiles?SwanShinichi Osawa /0/8,0,0,0"; + const long value = 1L; + await client.SetAsync(cacheKey, value); + var result = await client.GetAsync(cacheKey); - Assert.That(result, Is.EqualTo(value)); - } + Assert.That(result, Is.EqualTo(value)); } // remaining tests from parent do not touch redis diff --git a/tests/ServiceStack.Redis.Tests/BasicRediscClientManagerTests.Async.cs b/tests/ServiceStack.Redis.Tests/BasicRediscClientManagerTests.Async.cs index 9bc15328..70685fd4 100644 --- a/tests/ServiceStack.Redis.Tests/BasicRediscClientManagerTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/BasicRediscClientManagerTests.Async.cs @@ -12,22 +12,19 @@ public async Task Can_select_db() { var redisManager = new BasicRedisClientManager("127.0.0.1"); - var client = await redisManager.GetClientAsync(); - await using (client as IAsyncDisposable) + await using (var client = await redisManager.GetClientAsync()) { await client.SelectAsync(2); await client.SetAsync("db", 2); } - client = await redisManager.GetClientAsync(); - await using (client as IAsyncDisposable) + await using (var client = await redisManager.GetClientAsync()) { await client.SelectAsync(3); await client.SetAsync("db", 3); } - client = await redisManager.GetClientAsync(); - await using (client as IAsyncDisposable) + await using (var client = await redisManager.GetClientAsync()) { await client.SelectAsync(2); //((RedisClient)client).ChangeDb(2); @@ -36,8 +33,7 @@ public async Task Can_select_db() } redisManager = new BasicRedisClientManager("127.0.0.1?db=3"); - client = await redisManager.GetClientAsync(); - await using (client as IAsyncDisposable) + await using (var client = await redisManager.GetClientAsync()) { var db = await client.GetAsync("db"); Assert.That(db, Is.EqualTo(3)); diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsBase.Async.cs index 56f9a306..1889d193 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsBase.Async.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientHashTestsBase.Async.cs @@ -23,9 +23,9 @@ public abstract class RedisClientHashTestsBaseAsync [SetUp] public async Task SetUp() { - if (client is IAsyncDisposable d) + if (client is object) { - await d.DisposeAsync(); + await client.DisposeAsync(); client = null; } client = new RedisClient(TestConfig.SingleHost); diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestExtra.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestExtra.Async.cs index aef2f342..42f92e1e 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestExtra.Async.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestExtra.Async.cs @@ -26,9 +26,9 @@ public class RedisClientListTestExtraAsync [SetUp] public async Task SetUp() { - if (client is IAsyncDisposable d) + if (client is object) { - await d.DisposeAsync(); + await client.DisposeAsync(); client = null; } client = new RedisClient(TestConfig.SingleHost); diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsBase.Async.cs index fade47b5..58e42b5d 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsBase.Async.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientListTestsBase.Async.cs @@ -24,9 +24,9 @@ public abstract class RedisClientListTestsBaseAsync [SetUp] public async Task SetUp() { - if (client is IAsyncDisposable d) + if (client is object) { - await d.DisposeAsync(); + await client.DisposeAsync(); client = null; } client = new RedisClient(TestConfig.SingleHost); diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsBase.Async.cs index 01eef3b0..7f3028fd 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsBase.Async.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisClientSetTestsBase.Async.cs @@ -25,9 +25,9 @@ public abstract class RedisClientSetTestsBaseAsync [SetUp] public async Task SetUp() { - if (client is IAsyncDisposable d) + if (client is object) { - await d.DisposeAsync(); + await client.DisposeAsync(); client = null; } client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBase.Async.cs index c69247db..92880411 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBase.Async.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisPersistenceProviderTestsBase.Async.cs @@ -18,9 +18,9 @@ public abstract class RedisPersistenceProviderTestsBaseAsync [SetUp] public async Task SetUp() { - if (client is IAsyncDisposable d) + if (client is object) { - await d.DisposeAsync(); + await client.DisposeAsync(); client = null; } client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); diff --git a/tests/ServiceStack.Redis.Tests/LuaCachedScripts.Async.cs b/tests/ServiceStack.Redis.Tests/LuaCachedScripts.Async.cs index 15b40e5c..4cec7af8 100644 --- a/tests/ServiceStack.Redis.Tests/LuaCachedScripts.Async.cs +++ b/tests/ServiceStack.Redis.Tests/LuaCachedScripts.Async.cs @@ -40,109 +40,92 @@ private static async Task AddTestKeysAsync(IRedisClientAsync redis, int count) [Test] public async Task Can_call_repeated_scans_in_LUA() { - var redis = new RedisClient().ForAsyncOnly(); - await using (redis as IAsyncDisposable) - { - await AddTestKeysAsync(redis, 20); + await using var redis = new RedisClient().ForAsyncOnly(); + await AddTestKeysAsync(redis, 20); - var r = await redis.ExecLuaAsync(LuaScript, "key:*", "10"); - Assert.That(r.Children.Count, Is.EqualTo(10)); + var r = await redis.ExecLuaAsync(LuaScript, "key:*", "10"); + Assert.That(r.Children.Count, Is.EqualTo(10)); - r = await redis.ExecLuaAsync(LuaScript, "key:*", "40"); - Assert.That(r.Children.Count, Is.EqualTo(20)); - } + r = await redis.ExecLuaAsync(LuaScript, "key:*", "40"); + Assert.That(r.Children.Count, Is.EqualTo(20)); } [Test] public async Task Can_call_Cached_Lua() { - var redis = new RedisClient().ForAsyncOnly(); - await using (redis as IAsyncDisposable) - { - await AddTestKeysAsync(redis, 20); + await using var redis = new RedisClient().ForAsyncOnly(); + await AddTestKeysAsync(redis, 20); - var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, "key:*", "10")); - Assert.That(r.Children.Count, Is.EqualTo(10)); + var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, "key:*", "10")); + Assert.That(r.Children.Count, Is.EqualTo(10)); - r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, "key:*", "10")); - Assert.That(r.Children.Count, Is.EqualTo(10)); - } + r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, "key:*", "10")); + Assert.That(r.Children.Count, Is.EqualTo(10)); } [Test] public async Task Can_call_Cached_Lua_even_after_script_is_flushed() { - var redis = new RedisClient().ForAsyncOnly(); - await using (redis as IAsyncDisposable) - { - await AddTestKeysAsync(redis, 20); + await using var redis = new RedisClient().ForAsyncOnly(); + await AddTestKeysAsync(redis, 20); - var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, "key:*", "10")); - Assert.That(r.Children.Count, Is.EqualTo(10)); + var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, "key:*", "10")); + Assert.That(r.Children.Count, Is.EqualTo(10)); - await ((IRedisNativeClientAsync)redis).ScriptFlushAsync(); + await ((IRedisNativeClientAsync)redis).ScriptFlushAsync(); - r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, "key:*", "10")); - Assert.That(r.Children.Count, Is.EqualTo(10)); - } + r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, "key:*", "10")); + Assert.That(r.Children.Count, Is.EqualTo(10)); } [Test] public async Task Can_call_repeated_scans_in_LUA_longhand() { - var redis = new RedisClient().ForAsyncOnly(); - await using (redis as IAsyncDisposable) - { - await AddTestKeysAsync(redis, 20); + await using var redis = new RedisClient().ForAsyncOnly(); + + await AddTestKeysAsync(redis, 20); - var r = await redis.ExecLuaAsync(LuaScript, null, new[] { "key:*", "10" }); - Assert.That(r.Children.Count, Is.EqualTo(10)); + var r = await redis.ExecLuaAsync(LuaScript, null, new[] { "key:*", "10" }); + Assert.That(r.Children.Count, Is.EqualTo(10)); - r = await redis.ExecLuaAsync(LuaScript, null, new[] { "key:*", "40" }); - Assert.That(r.Children.Count, Is.EqualTo(20)); - } + r = await redis.ExecLuaAsync(LuaScript, null, new[] { "key:*", "40" }); + Assert.That(r.Children.Count, Is.EqualTo(20)); } [Test] public async Task Can_call_Cached_Lua_longhand() { - var redis = new RedisClient().ForAsyncOnly(); - await using (redis as IAsyncDisposable) - { - await AddTestKeysAsync(redis, 20); + await using var redis = new RedisClient().ForAsyncOnly(); + await AddTestKeysAsync(redis, 20); - var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); - Assert.That(r.Children.Count, Is.EqualTo(10)); + var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); + Assert.That(r.Children.Count, Is.EqualTo(10)); - r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); - Assert.That(r.Children.Count, Is.EqualTo(10)); - } + r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); + Assert.That(r.Children.Count, Is.EqualTo(10)); } [Test] public async Task Can_call_Cached_Lua_even_after_script_is_flushed_longhand() { - var redis = new RedisClient().ForAsyncOnly(); - await using (redis as IAsyncDisposable) - { - await AddTestKeysAsync(redis, 20); + await using var redis = new RedisClient().ForAsyncOnly(); + await AddTestKeysAsync(redis, 20); - var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); - Assert.That(r.Children.Count, Is.EqualTo(10)); + var r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); + Assert.That(r.Children.Count, Is.EqualTo(10)); - await ((IRedisNativeClientAsync)redis).ScriptFlushAsync(); + await ((IRedisNativeClientAsync)redis).ScriptFlushAsync(); - r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => - redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); - Assert.That(r.Children.Count, Is.EqualTo(10)); - } + r = await redis.ExecCachedLuaAsync(LuaScript, sha1 => + redis.ExecLuaShaAsync(sha1, null, new[] { "key:*", "10" })); + Assert.That(r.Children.Count, Is.EqualTo(10)); } private const string KeyAttributesScript = @" @@ -189,47 +172,41 @@ public async Task Can_call_Cached_Lua_even_after_script_is_flushed_longhand() [Test] public async Task Can_call_script_with_complex_response() { - var redis = new RedisClient().ForAsyncOnly(); - await using (redis as IAsyncDisposable) - { - var r = await redis.ExecCachedLuaAsync(KeyAttributesScript, sha1 => - redis.ExecLuaShaAsStringAsync(sha1, "key:*", "10")); + await using var redis = new RedisClient().ForAsyncOnly(); + var r = await redis.ExecCachedLuaAsync(KeyAttributesScript, sha1 => + redis.ExecLuaShaAsStringAsync(sha1, "key:*", "10")); - r.Print(); + r.Print(); - var results = r.FromJson>(); + var results = r.FromJson>(); - Assert.That(results.Count, Is.EqualTo(10)); + Assert.That(results.Count, Is.EqualTo(10)); - var result = results[0]; - Assert.That(result.Id.StartsWith("key:")); - Assert.That(result.Type, Is.EqualTo("string")); - Assert.That(result.Size, Is.GreaterThan("value:".Length)); - Assert.That(result.Ttl, Is.EqualTo(-1)); - } + var result = results[0]; + Assert.That(result.Id.StartsWith("key:")); + Assert.That(result.Type, Is.EqualTo("string")); + Assert.That(result.Size, Is.GreaterThan("value:".Length)); + Assert.That(result.Ttl, Is.EqualTo(-1)); } [Test] public async Task Can_call_script_with_complex_response_longhand() { - var redis = new RedisClient().ForAsyncOnly(); - await using (redis as IAsyncDisposable) - { - var r = await redis.ExecCachedLuaAsync(KeyAttributesScript, sha1 => - redis.ExecLuaShaAsStringAsync(sha1, null, new[] { "key:*", "10" })); + await using var redis = new RedisClient().ForAsyncOnly(); + var r = await redis.ExecCachedLuaAsync(KeyAttributesScript, sha1 => + redis.ExecLuaShaAsStringAsync(sha1, null, new[] { "key:*", "10" })); - r.Print(); + r.Print(); - var results = r.FromJson>(); + var results = r.FromJson>(); - Assert.That(results.Count, Is.EqualTo(10)); + Assert.That(results.Count, Is.EqualTo(10)); - var result = results[0]; - Assert.That(result.Id.StartsWith("key:")); - Assert.That(result.Type, Is.EqualTo("string")); - Assert.That(result.Size, Is.GreaterThan("value:".Length)); - Assert.That(result.Ttl, Is.EqualTo(-1)); - } + var result = results[0]; + Assert.That(result.Id.StartsWith("key:")); + Assert.That(result.Type, Is.EqualTo("string")); + Assert.That(result.Size, Is.GreaterThan("value:".Length)); + Assert.That(result.Ttl, Is.EqualTo(-1)); } public class SearchResult @@ -243,81 +220,78 @@ public class SearchResult [Test] public async Task Can_merge_multiple_SearchResults() { - var Redis = new RedisClient().ForAsyncOnly(); - await using (Redis as IAsyncDisposable) - { - var limit = 10; - var query = "key:*"; + await using var Redis = new RedisClient().ForAsyncOnly(); + var limit = 10; + var query = "key:*"; - List keys = new List(limit); - await foreach (var key in Redis.ScanAllKeysAsync(pattern: query, pageSize: limit)) - { - keys.Add(key); - if (keys.Count == limit) break; - } + List keys = new List(limit); + await foreach (var key in Redis.ScanAllKeysAsync(pattern: query, pageSize: limit)) + { + keys.Add(key); + if (keys.Count == limit) break; + } - var keyTypes = new Dictionary(); - var keyTtls = new Dictionary(); - var keySizes = new Dictionary(); + var keyTypes = new Dictionary(); + var keyTtls = new Dictionary(); + var keySizes = new Dictionary(); - if (keys.Count > 0) + if (keys.Count > 0) + { + await using (var pipeline = Redis.CreatePipeline()) { - await using (var pipeline = Redis.CreatePipeline()) - { - foreach (var key in keys) - pipeline.QueueCommand(r => r.TypeAsync(key), x => keyTypes[key] = x); + foreach (var key in keys) + pipeline.QueueCommand(r => r.TypeAsync(key), x => keyTypes[key] = x); - foreach (var key in keys) - pipeline.QueueCommand(r => ((IRedisNativeClientAsync)r).PTtlAsync(key), x => keyTtls[key] = x); + foreach (var key in keys) + pipeline.QueueCommand(r => ((IRedisNativeClientAsync)r).PTtlAsync(key), x => keyTtls[key] = x); - await pipeline.FlushAsync(); - } + await pipeline.FlushAsync(); + } - await using (var pipeline = Redis.CreatePipeline()) + await using (var pipeline = Redis.CreatePipeline()) + { + foreach (var entry in keyTypes) { - foreach (var entry in keyTypes) + var key = entry.Key; + switch (entry.Value) { - var key = entry.Key; - switch (entry.Value) - { - case "string": - pipeline.QueueCommand(r => r.GetStringCountAsync(key), x => keySizes[key] = x); - break; - case "list": - pipeline.QueueCommand(r => r.GetListCountAsync(key), x => keySizes[key] = x); - break; - case "set": - pipeline.QueueCommand(r => r.GetSetCountAsync(key), x => keySizes[key] = x); - break; - case "zset": - pipeline.QueueCommand(r => r.GetSortedSetCountAsync(key), x => keySizes[key] = x); - break; - case "hash": - pipeline.QueueCommand(r => r.GetHashCountAsync(key), x => keySizes[key] = x); - break; - } + case "string": + pipeline.QueueCommand(r => r.GetStringCountAsync(key), x => keySizes[key] = x); + break; + case "list": + pipeline.QueueCommand(r => r.GetListCountAsync(key), x => keySizes[key] = x); + break; + case "set": + pipeline.QueueCommand(r => r.GetSetCountAsync(key), x => keySizes[key] = x); + break; + case "zset": + pipeline.QueueCommand(r => r.GetSortedSetCountAsync(key), x => keySizes[key] = x); + break; + case "hash": + pipeline.QueueCommand(r => r.GetHashCountAsync(key), x => keySizes[key] = x); + break; } - - await pipeline.FlushAsync(); } - } - var results = keys.Map(x => new SearchResult - { - Id = x, - Type = keyTypes.GetValueOrDefault(x), - Ttl = keyTtls.GetValueOrDefault(x), - Size = keySizes.GetValueOrDefault(x), - }); - - Assert.That(results.Count, Is.EqualTo(limit)); - - var result = results[0]; - Assert.That(result.Id.StartsWith("key:")); - Assert.That(result.Type, Is.EqualTo("string")); - Assert.That(result.Size, Is.GreaterThan("value:".Length)); - Assert.That(result.Ttl, Is.EqualTo(-1)); + await pipeline.FlushAsync(); + } } + + var results = keys.Map(x => new SearchResult + { + Id = x, + Type = keyTypes.GetValueOrDefault(x), + Ttl = keyTtls.GetValueOrDefault(x), + Size = keySizes.GetValueOrDefault(x), + }); + + Assert.That(results.Count, Is.EqualTo(limit)); + + var result = results[0]; + Assert.That(result.Id.StartsWith("key:")); + Assert.That(result.Type, Is.EqualTo("string")); + Assert.That(result.Size, Is.GreaterThan("value:".Length)); + Assert.That(result.Ttl, Is.EqualTo(-1)); } } } \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.Async.cs b/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.Async.cs index a900582b..fdf442f6 100644 --- a/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.Async.cs @@ -188,7 +188,7 @@ public async Task Does_loop_through_ReadWrite_hosts() { await using var manager = CreateAndStartManager(); var client1 = await manager.GetClientAsync(); - await client1.TryDisposeAsync(); + await client1.DisposeAsync(); var client2 = await manager.GetClientAsync(); var client3 = await manager.GetClientAsync(); var client4 = await manager.GetClientAsync(); @@ -206,9 +206,9 @@ public async Task Does_loop_through_ReadOnly_hosts() { await using var manager = CreateAndStartManager(); var client1 = await manager.GetReadOnlyClientAsync(); - await client1.TryDisposeAsync(); + await client1.DisposeAsync(); var client2 = await manager.GetReadOnlyClientAsync(); - await client2.TryDisposeAsync(); + await client2.DisposeAsync(); var client3 = await manager.GetReadOnlyClientAsync(); var client4 = await manager.GetReadOnlyClientAsync(); var client5 = await manager.GetReadOnlyClientAsync(); @@ -237,35 +237,35 @@ public async Task Can_have_different_pool_size_and_host_configurations() } ); //A poolsize of 4 will not block getting 4 clients - await using (var client1 = (await manager.GetClientAsync()).WrapForDispose()) - await using (var client2 = (await manager.GetClientAsync()).WrapForDispose()) - await using (var client3 = (await manager.GetClientAsync()).WrapForDispose()) - await using (var client4 = (await manager.GetClientAsync()).WrapForDispose()) + await using (var client1 = await manager.GetClientAsync()) + await using (var client2 = await manager.GetClientAsync()) + await using (var client3 = await manager.GetClientAsync()) + await using (var client4 = await manager.GetClientAsync()) { - AssertClientHasHost(client1.Value, writeHosts[0]); - AssertClientHasHost(client2.Value, writeHosts[0]); - AssertClientHasHost(client3.Value, writeHosts[0]); - AssertClientHasHost(client4.Value, writeHosts[0]); + AssertClientHasHost(client1, writeHosts[0]); + AssertClientHasHost(client2, writeHosts[0]); + AssertClientHasHost(client3, writeHosts[0]); + AssertClientHasHost(client4, writeHosts[0]); } //A poolsize of 8 will not block getting 8 clients - await using (var client1 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) - await using (var client2 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) - await using (var client3 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) - await using (var client4 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) - await using (var client5 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) - await using (var client6 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) - await using (var client7 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) - await using (var client8 = (await manager.GetReadOnlyClientAsync()).WrapForDispose()) + await using (var client1 = await manager.GetReadOnlyClientAsync()) + await using (var client2 = await manager.GetReadOnlyClientAsync()) + await using (var client3 = await manager.GetReadOnlyClientAsync()) + await using (var client4 = await manager.GetReadOnlyClientAsync()) + await using (var client5 = await manager.GetReadOnlyClientAsync()) + await using (var client6 = await manager.GetReadOnlyClientAsync()) + await using (var client7 = await manager.GetReadOnlyClientAsync()) + await using (var client8 = await manager.GetReadOnlyClientAsync()) { - AssertClientHasHost(client1.Value, readHosts[0]); - AssertClientHasHost(client2.Value, readHosts[1]); - AssertClientHasHost(client3.Value, readHosts[0]); - AssertClientHasHost(client4.Value, readHosts[1]); - AssertClientHasHost(client5.Value, readHosts[0]); - AssertClientHasHost(client6.Value, readHosts[1]); - AssertClientHasHost(client7.Value, readHosts[0]); - AssertClientHasHost(client8.Value, readHosts[1]); + AssertClientHasHost(client1, readHosts[0]); + AssertClientHasHost(client2, readHosts[1]); + AssertClientHasHost(client3, readHosts[0]); + AssertClientHasHost(client4, readHosts[1]); + AssertClientHasHost(client5, readHosts[0]); + AssertClientHasHost(client6, readHosts[1]); + AssertClientHasHost(client7, readHosts[0]); + AssertClientHasHost(client8, readHosts[1]); } } @@ -284,7 +284,7 @@ public async Task Does_block_ReadWrite_clients_pool() #pragma warning restore IDE0039 // Use local function { await Task.Delay(delay + TimeSpan.FromSeconds(0.5)); - await client4.TryDisposeAsync(); + await client4.DisposeAsync(); }; #if NETCORE @@ -321,7 +321,7 @@ public async Task Does_block_ReadOnly_clients_pool() #pragma warning restore IDE0039 // Use local function { await Task.Delay(delay + TimeSpan.FromSeconds(0.5)); - await client3.TryDisposeAsync(); + await client3.DisposeAsync(); }; #if NETCORE _ =Task.Run(func); diff --git a/tests/ServiceStack.Redis.Tests/RedisCacheClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisCacheClientTests.Async.cs index c98b8a29..4ee85060 100644 --- a/tests/ServiceStack.Redis.Tests/RedisCacheClientTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisCacheClientTests.Async.cs @@ -15,8 +15,8 @@ public class RedisCacheClientTestsAsync [SetUp] public async Task OnBeforeEachTest() { - if (cacheClient is IAsyncDisposable d) - await d.DisposeAsync(); + if (cacheClient is object) + await cacheClient.DisposeAsync(); cacheClient = new RedisClient(TestConfig.SingleHost); await cacheClient.FlushAllAsync(); @@ -129,13 +129,11 @@ public async Task Can_GetTimeToLive() [Test] public async Task Can_increment_and_reset_values() { - var client = await new RedisManagerPool(TestConfig.SingleHost).GetCacheClientAsync(); - await using (client as IAsyncDisposable) - { - Assert.That(await client.IncrementAsync("incr:counter", 10), Is.EqualTo(10)); - await client.SetAsync("incr:counter", 0); - Assert.That(await client.IncrementAsync("incr:counter", 10), Is.EqualTo(10)); - } + await using var client = await new RedisManagerPool(TestConfig.SingleHost).GetCacheClientAsync(); + + Assert.That(await client.IncrementAsync("incr:counter", 10), Is.EqualTo(10)); + await client.SetAsync("incr:counter", 0); + Assert.That(await client.IncrementAsync("incr:counter", 10), Is.EqualTo(10)); } } } \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientTests.Async.cs index 4f2535b1..b5b7aad3 100644 --- a/tests/ServiceStack.Redis.Tests/RedisClientTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisClientTests.Async.cs @@ -402,13 +402,10 @@ public async Task Can_AcquireLock_TimeOut() try { - var client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); - await using (client as IAsyncDisposable) + await using var client = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await using (await client.AcquireLockAsync(lockKey, waitFor)) { - await using (await client.AcquireLockAsync(lockKey, waitFor)) - { - await client.IncrementValueAsync(key); //2 - } + await client.IncrementValueAsync(key); //2 } } catch (TimeoutException) @@ -531,14 +528,11 @@ public async Task Can_store_Dictionary() var map = new Dictionary(); keys.ForEach(x => map[x] = "val" + x); - var client = RedisClient.New().ForAsyncOnly(); - await using (client as IAsyncDisposable) - { - await client.SetAllAsync(map); + await using var client = RedisClient.New().ForAsyncOnly(); + await client.SetAllAsync(map); - var all = await client.GetValuesMapAsync(keys); - Assert.AreEqual(map, all); - } + var all = await client.GetValuesMapAsync(keys); + Assert.AreEqual(map, all); } [Test] @@ -550,14 +544,12 @@ public async Task Can_store_Dictionary_as_objects() ["key_b"] = null }; - var client = RedisClient.New().ForAsyncOnly(); - await using (client as IAsyncDisposable) - { - await client.SetAllAsync(map); + await using var client = RedisClient.New().ForAsyncOnly(); + + await client.SetAllAsync(map); - Assert.That(await client.GetAsync("key_a"), Is.EqualTo("123")); - Assert.That(await client.GetValueAsync("key_b"), Is.EqualTo("")); - } + Assert.That(await client.GetAsync("key_a"), Is.EqualTo("123")); + Assert.That(await client.GetValueAsync("key_b"), Is.EqualTo("")); } @@ -570,41 +562,34 @@ public async Task Can_store_Dictionary_as_bytes() ["key_b"] = null }; - var client = RedisClient.New().ForAsyncOnly(); - await using (client as IAsyncDisposable) - { - await client.SetAllAsync(map); + await using var client = RedisClient.New().ForAsyncOnly(); - Assert.That(await client.GetAsync("key_a"), Is.EqualTo("123")); - Assert.That(await client.GetValueAsync("key_b"), Is.EqualTo("")); - } + await client.SetAllAsync(map); + + Assert.That(await client.GetAsync("key_a"), Is.EqualTo("123")); + Assert.That(await client.GetValueAsync("key_b"), Is.EqualTo("")); } [Test] public async Task Should_reset_slowlog() { - var client = RedisClient.New().ForAsyncOnly(); - await using (client as IAsyncDisposable) - { - await client.SlowlogResetAsync(); - } + await using var client = RedisClient.New().ForAsyncOnly(); + await client.SlowlogResetAsync(); } [Test] public async Task Can_get_slowlog() { - var client = RedisClient.New().ForAsyncOnly(); - await using (client as IAsyncDisposable) - { - var log = await client.GetSlowlogAsync(10); + await using var client = RedisClient.New().ForAsyncOnly(); + + var log = await client.GetSlowlogAsync(10); - foreach (var t in log) - { - Console.WriteLine(t.Id); - Console.WriteLine(t.Duration); - Console.WriteLine(t.Timestamp); - Console.WriteLine(string.Join(":", t.Arguments)); - } + foreach (var t in log) + { + Console.WriteLine(t.Id); + Console.WriteLine(t.Duration); + Console.WriteLine(t.Timestamp); + Console.WriteLine(string.Join(":", t.Arguments)); } } @@ -612,25 +597,23 @@ public async Task Can_get_slowlog() [Test] public async Task Can_change_db_at_runtime() { - var redis = new RedisClient(TestConfig.SingleHost, TestConfig.RedisPort, db: 1).ForAsyncOnly(); - await using (redis as IAsyncDisposable) + await using var redis = new RedisClient(TestConfig.SingleHost, TestConfig.RedisPort, db: 1).ForAsyncOnly(); + + var val = Environment.TickCount; + var key = "test" + val; + try { - var val = Environment.TickCount; - var key = "test" + val; - try - { - await redis.SetAsync(key, val); - await redis.SelectAsync(2); - Assert.That(await redis.GetAsync(key), Is.EqualTo(0)); - await redis.SelectAsync(1); - Assert.That(await redis.GetAsync(key), Is.EqualTo(val)); - await redis.TryDisposeAsync(); - } - finally - { - await redis.SelectAsync(1); - await redis.RemoveAsync(key); - } + await redis.SetAsync(key, val); + await redis.SelectAsync(2); + Assert.That(await redis.GetAsync(key), Is.EqualTo(0)); + await redis.SelectAsync(1); + Assert.That(await redis.GetAsync(key), Is.EqualTo(val)); + await redis.DisposeAsync(); + } + finally + { + await redis.SelectAsync(1); + await redis.RemoveAsync(key); } } diff --git a/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs index efd0d345..d7c6244e 100644 --- a/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs @@ -41,23 +41,6 @@ public static async ValueTask CountAsync(this IAsyncEnumerable source return count; } - public static ValueTask TryDisposeAsync(this object obj) - { - if (obj is IAsyncDisposable d) return d.DisposeAsync(); - return default; - } - - public static WrappedDisposable WrapForDispose(this T value) where T : class - => new WrappedDisposable(value); - - public readonly struct WrappedDisposable : IAsyncDisposable where T : class - { - public readonly T Value; - public ValueTask DisposeAsync() => TryDisposeAsync(Value); - public WrappedDisposable(T value) => Value = value; - public static implicit operator T (in WrappedDisposable value) => value.Value; - } - public static IRedisClientAsync ForAsyncOnly(this RedisClient client) { #if DEBUG diff --git a/tests/ServiceStack.Redis.Tests/RedisGeoTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisGeoTests.Async.cs index 683b29a2..f06280e2 100644 --- a/tests/ServiceStack.Redis.Tests/RedisGeoTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisGeoTests.Async.cs @@ -18,9 +18,9 @@ public RedisGeoTestsAsync() [OneTimeTearDown] public async Task OneTimeTearDown() { - if (redis is IAsyncDisposable d) + if (redis is object) { - await d.DisposeAsync(); + await redis.DisposeAsync(); } } diff --git a/tests/ServiceStack.Redis.Tests/RedisHyperLogTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisHyperLogTests.Async.cs index f0d64e1d..31748b6e 100644 --- a/tests/ServiceStack.Redis.Tests/RedisHyperLogTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisHyperLogTests.Async.cs @@ -13,27 +13,24 @@ public class RedisHyperLogTestsAsync [Test] public async Task Can_Add_to_Hyperlog() { - var redis = Connect(); - await using (redis as IAsyncDisposable) - { + await using var redis = Connect(); - await redis.FlushAllAsync(); + await redis.FlushAllAsync(); - await redis.AddToHyperLogAsync("hyperlog", new[] { "a", "b", "c" }); - await redis.AddToHyperLogAsync("hyperlog", new[] { "c", "d" }); + await redis.AddToHyperLogAsync("hyperlog", new[] { "a", "b", "c" }); + await redis.AddToHyperLogAsync("hyperlog", new[] { "c", "d" }); - var count = await redis.CountHyperLogAsync("hyperlog"); + var count = await redis.CountHyperLogAsync("hyperlog"); - Assert.That(count, Is.EqualTo(4)); + Assert.That(count, Is.EqualTo(4)); - await redis.AddToHyperLogAsync("hyperlog2", new[] { "c", "d", "e", "f" }); + await redis.AddToHyperLogAsync("hyperlog2", new[] { "c", "d", "e", "f" }); - await redis.MergeHyperLogsAsync("hypermerge", new[] { "hyperlog", "hyperlog2" }); + await redis.MergeHyperLogsAsync("hypermerge", new[] { "hyperlog", "hyperlog2" }); - var mergeCount = await redis.CountHyperLogAsync("hypermerge"); + var mergeCount = await redis.CountHyperLogAsync("hypermerge"); - Assert.That(mergeCount, Is.EqualTo(6)); - } + Assert.That(mergeCount, Is.EqualTo(6)); } } } \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/RedisPersistenceProviderTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisPersistenceProviderTests.Async.cs index 830a75ca..1f0e5e42 100644 --- a/tests/ServiceStack.Redis.Tests/RedisPersistenceProviderTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisPersistenceProviderTests.Async.cs @@ -13,59 +13,51 @@ public class RedisPersistenceProviderTestsAsync [Test] public async Task Can_Store_and_GetById_ModelWithIdAndName() { - IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost); - await using (redis as IAsyncDisposable) - { - const int modelId = 1; - var to = ModelWithIdAndName.Create(modelId); - await redis.StoreAsync(to); + await using IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost); + const int modelId = 1; + var to = ModelWithIdAndName.Create(modelId); + await redis.StoreAsync(to); - var from = await redis.GetByIdAsync(modelId); + var from = await redis.GetByIdAsync(modelId); - ModelWithIdAndName.AssertIsEqual(to, from); - } + ModelWithIdAndName.AssertIsEqual(to, from); } [Test] public async Task Can_StoreAll_and_GetByIds_ModelWithIdAndName() { - IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost); - await using (redis as IAsyncDisposable) - { - var ids = new[] { 1, 2, 3, 4, 5 }; - var tos = ids.Map(ModelWithIdAndName.Create); + await using IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost); + + var ids = new[] { 1, 2, 3, 4, 5 }; + var tos = ids.Map(ModelWithIdAndName.Create); - await redis.StoreAllAsync(tos); + await redis.StoreAllAsync(tos); - var froms = await redis.GetByIdsAsync(ids); - var fromIds = froms.Map(x => x.Id); + var froms = await redis.GetByIdsAsync(ids); + var fromIds = froms.Map(x => x.Id); - Assert.That(fromIds, Is.EquivalentTo(ids)); - } + Assert.That(fromIds, Is.EquivalentTo(ids)); } [Test] public async Task Can_Delete_ModelWithIdAndName() { - IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost); - await using (redis as IAsyncDisposable) - { - var ids = new List { 1, 2, 3, 4, 5 }; - var tos = ids.ConvertAll(ModelWithIdAndName.Create); + await using IRedisClientAsync redis = new RedisClient(TestConfig.SingleHost); + var ids = new List { 1, 2, 3, 4, 5 }; + var tos = ids.ConvertAll(ModelWithIdAndName.Create); - await redis.StoreAllAsync(tos); + await redis.StoreAllAsync(tos); - var deleteIds = new List { 2, 4 }; + var deleteIds = new List { 2, 4 }; - await redis.DeleteByIdsAsync(deleteIds); + await redis.DeleteByIdsAsync(deleteIds); - var froms = await redis.GetByIdsAsync(ids); - var fromIds = froms.Map(x => x.Id); + var froms = await redis.GetByIdsAsync(ids); + var fromIds = froms.Map(x => x.Id); - var expectedIds = ids.Where(x => !deleteIds.Contains(x)).ToList(); + var expectedIds = ids.Where(x => !deleteIds.Contains(x)).ToList(); - Assert.That(fromIds, Is.EquivalentTo(expectedIds)); - } + Assert.That(fromIds, Is.EquivalentTo(expectedIds)); } } diff --git a/tests/ServiceStack.Redis.Tests/RedisPubSubTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisPubSubTests.Async.cs index 55f1bd82..b8865dc6 100644 --- a/tests/ServiceStack.Redis.Tests/RedisPubSubTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisPubSubTests.Async.cs @@ -51,12 +51,9 @@ public async Task Can_Subscribe_and_Publish_single_message() ThreadPool.QueueUserWorkItem(async x => { await Task.Delay(100); // to be sure that we have subscribers - var redisClient = CreateRedisClient().ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) - { - Log("Publishing '{0}' to '{1}'", message, channelName); - await redisClient.PublishMessageAsync(channelName, message); - } + await using var redisClient = CreateRedisClient().ForAsyncOnly(); + Log("Publishing '{0}' to '{1}'", message, channelName); + await redisClient.PublishMessageAsync(channelName, message); }); Log("Start Listening On " + channelName); @@ -103,12 +100,9 @@ public async Task Can_Subscribe_and_Publish_single_message_using_wildcard() ThreadPool.QueueUserWorkItem(async x => { await Task.Delay(100); // to be sure that we have subscribers - var redisClient = CreateRedisClient().ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) - { - Log("Publishing '{0}' to '{1}'", message, channelName); - await redisClient.PublishMessageAsync(channelName, message); - } + await using var redisClient = CreateRedisClient().ForAsyncOnly(); + Log("Publishing '{0}' to '{1}'", message, channelName); + await redisClient.PublishMessageAsync(channelName, message); }); Log("Start Listening On " + channelName); @@ -161,15 +155,12 @@ public async Task Can_Subscribe_and_Publish_multiple_message() { await Task.Delay(100); // to be sure that we have subscribers - var redisClient = CreateRedisClient().ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) + await using var redisClient = CreateRedisClient().ForAsyncOnly(); + for (var i = 0; i < publishMessageCount; i++) { - for (var i = 0; i < publishMessageCount; i++) - { - var message = messagePrefix + i; - Log("Publishing '{0}' to '{1}'", message, channelName); - await redisClient.PublishMessageAsync(channelName, message); - } + var message = messagePrefix + i; + Log("Publishing '{0}' to '{1}'", message, channelName); + await redisClient.PublishMessageAsync(channelName, message); } }); @@ -228,14 +219,11 @@ public async Task Can_Subscribe_and_Publish_message_to_multiple_channels() { await Task.Delay(100); // to be sure that we have subscribers - var redisClient = CreateRedisClient().ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) + await using var redisClient = CreateRedisClient().ForAsyncOnly(); + foreach (var channel in channels) { - foreach (var channel in channels) - { - Log("Publishing '{0}' to '{1}'", message, channel); - await redisClient.PublishMessageAsync(channel, message); - } + Log("Publishing '{0}' to '{1}'", message, channel); + await redisClient.PublishMessageAsync(channel, message); } }); @@ -267,12 +255,9 @@ public async Task Can_Subscribe_to_channel_pattern() { await Task.Delay(100); // to be sure that we have subscribers - var redisClient = CreateRedisClient().ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) - { - Log("Publishing msg..."); - await redisClient.PublishMessageAsync(PrefixedKey("CHANNEL4:TITLE1"), "hello"); // .ToUtf8Bytes() - } + await using var redisClient = CreateRedisClient().ForAsyncOnly(); + Log("Publishing msg..."); + await redisClient.PublishMessageAsync(PrefixedKey("CHANNEL4:TITLE1"), "hello"); // .ToUtf8Bytes() }); Log("Start Listening On"); @@ -295,12 +280,9 @@ public async Task Can_Subscribe_to_multiplechannel_pattern() { await Task.Delay(100); // to be sure that we have subscribers - var redisClient = CreateRedisClient().ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) - { - Log("Publishing msg..."); - await redisClient.PublishMessageAsync(PrefixedKey("CHANNEL5:BODY"), "hello"); // .ToUtf8Bytes() - } + await using var redisClient = CreateRedisClient().ForAsyncOnly(); + Log("Publishing msg..."); + await redisClient.PublishMessageAsync(PrefixedKey("CHANNEL5:BODY"), "hello"); // .ToUtf8Bytes() }); Log("Start Listening On"); diff --git a/tests/ServiceStack.Redis.Tests/ShippersExample.Async.cs b/tests/ServiceStack.Redis.Tests/ShippersExample.Async.cs index 26cfd6cb..c0154116 100644 --- a/tests/ServiceStack.Redis.Tests/ShippersExample.Async.cs +++ b/tests/ServiceStack.Redis.Tests/ShippersExample.Async.cs @@ -49,79 +49,76 @@ static void Dump(string message, T entity) [Test] public async Task Shippers_UseCase() { - var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) - { - //Create a 'strongly-typed' API that makes all Redis Value operations to apply against Shippers - IRedisTypedClientAsync redis = redisClient.As(); - - //Redis lists implement IList while Redis sets implement ICollection - var currentShippers = redis.Lists["urn:shippers:current"]; - var prospectiveShippers = redis.Lists["urn:shippers:prospective"]; - - await currentShippers.AddAsync( - new Shipper - { - Id = await redis.GetNextSequenceAsync(), - CompanyName = "Trains R Us", - DateCreated = DateTime.UtcNow, - ShipperType = ShipperType.Trains, - UniqueRef = Guid.NewGuid() - }); - - await currentShippers.AddAsync( - new Shipper - { - Id = await redis.GetNextSequenceAsync(), - CompanyName = "Planes R Us", - DateCreated = DateTime.UtcNow, - ShipperType = ShipperType.Planes, - UniqueRef = Guid.NewGuid() - }); - - var lameShipper = new Shipper + await using var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + //Create a 'strongly-typed' API that makes all Redis Value operations to apply against Shippers + IRedisTypedClientAsync redis = redisClient.As(); + + //Redis lists implement IList while Redis sets implement ICollection + var currentShippers = redis.Lists["urn:shippers:current"]; + var prospectiveShippers = redis.Lists["urn:shippers:prospective"]; + + await currentShippers.AddAsync( + new Shipper + { + Id = await redis.GetNextSequenceAsync(), + CompanyName = "Trains R Us", + DateCreated = DateTime.UtcNow, + ShipperType = ShipperType.Trains, + UniqueRef = Guid.NewGuid() + }); + + await currentShippers.AddAsync( + new Shipper { Id = await redis.GetNextSequenceAsync(), - CompanyName = "We do everything!", + CompanyName = "Planes R Us", DateCreated = DateTime.UtcNow, - ShipperType = ShipperType.All, + ShipperType = ShipperType.Planes, UniqueRef = Guid.NewGuid() - }; + }); + + var lameShipper = new Shipper + { + Id = await redis.GetNextSequenceAsync(), + CompanyName = "We do everything!", + DateCreated = DateTime.UtcNow, + ShipperType = ShipperType.All, + UniqueRef = Guid.NewGuid() + }; - await currentShippers.AddAsync(lameShipper); + await currentShippers.AddAsync(lameShipper); - Dump("ADDED 3 SHIPPERS:", await currentShippers.ToListAsync()); + Dump("ADDED 3 SHIPPERS:", await currentShippers.ToListAsync()); - await currentShippers.RemoveAsync(lameShipper); + await currentShippers.RemoveAsync(lameShipper); - Dump("REMOVED 1:", await currentShippers.ToListAsync()); + Dump("REMOVED 1:", await currentShippers.ToListAsync()); - await prospectiveShippers.AddAsync( - new Shipper - { - Id = await redis.GetNextSequenceAsync(), - CompanyName = "Trucks R Us", - DateCreated = DateTime.UtcNow, - ShipperType = ShipperType.Automobiles, - UniqueRef = Guid.NewGuid() - }); + await prospectiveShippers.AddAsync( + new Shipper + { + Id = await redis.GetNextSequenceAsync(), + CompanyName = "Trucks R Us", + DateCreated = DateTime.UtcNow, + ShipperType = ShipperType.Automobiles, + UniqueRef = Guid.NewGuid() + }); - Dump("ADDED A PROSPECTIVE SHIPPER:", await prospectiveShippers.ToListAsync()); + Dump("ADDED A PROSPECTIVE SHIPPER:", await prospectiveShippers.ToListAsync()); - await redis.PopAndPushItemBetweenListsAsync(prospectiveShippers, currentShippers); + await redis.PopAndPushItemBetweenListsAsync(prospectiveShippers, currentShippers); - Dump("CURRENT SHIPPERS AFTER POP n' PUSH:", await currentShippers.ToListAsync()); - Dump("PROSPECTIVE SHIPPERS AFTER POP n' PUSH:", await prospectiveShippers.ToListAsync()); + Dump("CURRENT SHIPPERS AFTER POP n' PUSH:", await currentShippers.ToListAsync()); + Dump("PROSPECTIVE SHIPPERS AFTER POP n' PUSH:", await prospectiveShippers.ToListAsync()); - var poppedShipper = await redis.PopItemFromListAsync(currentShippers); - Dump("POPPED a SHIPPER:", poppedShipper); - Dump("CURRENT SHIPPERS AFTER POP:", await currentShippers.ToListAsync()); + var poppedShipper = await redis.PopItemFromListAsync(currentShippers); + Dump("POPPED a SHIPPER:", poppedShipper); + Dump("CURRENT SHIPPERS AFTER POP:", await currentShippers.ToListAsync()); - //reset sequence and delete all lists - await redis.SetSequenceAsync(0); - await redis.RemoveEntryAsync(new[] { currentShippers, prospectiveShippers }); - Dump("DELETING CURRENT AND PROSPECTIVE SHIPPERS:", await currentShippers.ToListAsync()); - } + //reset sequence and delete all lists + await redis.SetSequenceAsync(0); + await redis.RemoveEntryAsync(new[] { currentShippers, prospectiveShippers }); + Dump("DELETING CURRENT AND PROSPECTIVE SHIPPERS:", await currentShippers.ToListAsync()); } diff --git a/tests/ServiceStack.Redis.Tests/ValueTypeExamples.Async.cs b/tests/ServiceStack.Redis.Tests/ValueTypeExamples.Async.cs index 996c1673..c4baa07f 100644 --- a/tests/ServiceStack.Redis.Tests/ValueTypeExamples.Async.cs +++ b/tests/ServiceStack.Redis.Tests/ValueTypeExamples.Async.cs @@ -12,11 +12,8 @@ public class ValueTypeExamplesAsync [SetUp] public async Task SetUp() { - var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) - { - await redisClient.FlushAllAsync(); - } + await using var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + await redisClient.FlushAllAsync(); } [Test] @@ -26,8 +23,7 @@ public async Task Working_with_int_values() const int intValue = 1; //STORING AN INT USING THE BASIC CLIENT - var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) + await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) { await redisClient.SetValueAsync(intKey, intValue.ToString()); string strGetIntValue = await redisClient.GetValueAsync(intKey); @@ -37,8 +33,7 @@ public async Task Working_with_int_values() } //STORING AN INT USING THE GENERIC CLIENT - redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) + await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) { //Create a generic client that treats all values as ints: IRedisTypedClientAsync intRedis = redisClient.As(); @@ -57,8 +52,7 @@ public async Task Working_with_int_list_values() var intValues = new List { 2, 4, 6, 8 }; //STORING INTS INTO A LIST USING THE BASIC CLIENT - var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) + await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) { IRedisListAsync strList = redisClient.Lists[intListKey]; @@ -78,8 +72,7 @@ public async Task Working_with_int_list_values() } //STORING INTS INTO A LIST USING THE GENERIC CLIENT - redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) + await using (var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly()) { //Create a generic client that treats all values as ints: IRedisTypedClientAsync intRedis = redisClient.As(); @@ -104,40 +97,37 @@ public class IntAndString [Test] public async Task Working_with_Generic_types() { - var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); - await using (redisClient as IAsyncDisposable) - { - //Create a typed Redis client that treats all values as IntAndString: - var typedRedis = redisClient.As(); + await using var redisClient = new RedisClient(TestConfig.SingleHost).ForAsyncOnly(); + //Create a typed Redis client that treats all values as IntAndString: + var typedRedis = redisClient.As(); - var pocoValue = new IntAndString { Id = 1, Letter = "A" }; - await typedRedis.SetValueAsync("pocoKey", pocoValue); - IntAndString toPocoValue = await typedRedis.GetValueAsync("pocoKey"); + var pocoValue = new IntAndString { Id = 1, Letter = "A" }; + await typedRedis.SetValueAsync("pocoKey", pocoValue); + IntAndString toPocoValue = await typedRedis.GetValueAsync("pocoKey"); - Assert.That(toPocoValue.Id, Is.EqualTo(pocoValue.Id)); - Assert.That(toPocoValue.Letter, Is.EqualTo(pocoValue.Letter)); + Assert.That(toPocoValue.Id, Is.EqualTo(pocoValue.Id)); + Assert.That(toPocoValue.Letter, Is.EqualTo(pocoValue.Letter)); - var pocoListValues = new List { - new IntAndString {Id = 2, Letter = "B"}, - new IntAndString {Id = 3, Letter = "C"}, - new IntAndString {Id = 4, Letter = "D"}, - new IntAndString {Id = 5, Letter = "E"}, - }; + var pocoListValues = new List { + new IntAndString {Id = 2, Letter = "B"}, + new IntAndString {Id = 3, Letter = "C"}, + new IntAndString {Id = 4, Letter = "D"}, + new IntAndString {Id = 5, Letter = "E"}, + }; - IRedisListAsync pocoList = typedRedis.Lists["pocoListKey"]; + IRedisListAsync pocoList = typedRedis.Lists["pocoListKey"]; - //Adding all IntAndString objects into the redis list 'pocoListKey' - await pocoListValues.ForEachAsync(async x => await pocoList.AddAsync(x)); + //Adding all IntAndString objects into the redis list 'pocoListKey' + await pocoListValues.ForEachAsync(async x => await pocoList.AddAsync(x)); - List toPocoListValues = await pocoList.ToListAsync(); + List toPocoListValues = await pocoList.ToListAsync(); - for (var i = 0; i < pocoListValues.Count; i++) - { - pocoValue = pocoListValues[i]; - toPocoValue = toPocoListValues[i]; - Assert.That(toPocoValue.Id, Is.EqualTo(pocoValue.Id)); - Assert.That(toPocoValue.Letter, Is.EqualTo(pocoValue.Letter)); - } + for (var i = 0; i < pocoListValues.Count; i++) + { + pocoValue = pocoListValues[i]; + toPocoValue = toPocoListValues[i]; + Assert.That(toPocoValue.Id, Is.EqualTo(pocoValue.Id)); + Assert.That(toPocoValue.Letter, Is.EqualTo(pocoValue.Letter)); } } From 594a15bcdadc481a9fa352f2a9e5be3c920d3602 Mon Sep 17 00:00:00 2001 From: mgravell Date: Thu, 3 Sep 2020 08:10:26 +0100 Subject: [PATCH 010/107] fixup test re signature changes --- .../AsyncImplementationsTests.Async.cs | 26 ++++++++++++++----- ...edisBasicPersistenceProviderTests.Async.cs | 4 +-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs b/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs index 5c758695..005a2e01 100644 --- a/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs @@ -83,7 +83,9 @@ public void TestSameAPI(Type syncInterface, Type asyncInterface) } var expected = new List(); - ParameterToken cancellationToken = new ParameterToken("cancellationToken", typeof(CancellationToken), ParameterAttributes.Optional); + ParameterToken cancellationToken = new ParameterToken( + (asyncInterface == typeof(IRemoveByPatternAsync) || asyncInterface == typeof(ICacheClientAsync)) + ? "token" : "cancellationToken", typeof(CancellationToken), ParameterAttributes.Optional); foreach (var method in syncInterface.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { AddExpected(method); @@ -169,6 +171,18 @@ void AddFrom(Type syncInterface, string name, bool fromPropertyToMethod = false) void AddFromTyped(Type syncInterface, string name, params Type[] types) => AddExpected(syncInterface.GetMethod(name, types), false); + Type AsyncType(Type result) + { + bool useTask = asyncInterface == typeof(ICacheClientAsync) + || asyncInterface == typeof(IRemoveByPatternAsync) + || asyncInterface == typeof(IEntityStoreAsync) + || asyncInterface == typeof(IEntityStoreAsync<>); + + if (result is null || result == typeof(void)) + return useTask ? typeof(Task) : typeof(ValueTask); + + return (useTask ? typeof(Task<>) : typeof(ValueTask<>)).MakeGenericType(result); + } void AddExpected(MethodInfo method, bool fromPropertyToMethod = false) { if (method is null) return; @@ -180,7 +194,7 @@ void AddExpected(MethodInfo method, bool fromPropertyToMethod = false) Type returnType; if (tok.ReturnType == typeof(void)) { - returnType = typeof(ValueTask); + returnType = AsyncType(tok.ReturnType); } else if (tok.ReturnType == typeof(IDisposable)) { @@ -192,7 +206,7 @@ void AddExpected(MethodInfo method, bool fromPropertyToMethod = false) } else { - returnType = typeof(ValueTask<>).MakeGenericType(SwapForAsyncIfNeedeed(tok.ReturnType)); + returnType = AsyncType(SwapForAsyncIfNeedeed(tok.ReturnType)); } string name = tok.Name + "Async"; bool addCancellation = true; @@ -793,7 +807,7 @@ public MethodToken(string name, Type returnType, ParameterToken[] parameters, } [TestCase(typeof(ICacheClient), typeof(ICacheClientAsync))] - [TestCase(typeof(ICacheClientExtended), typeof(ICacheClientAsync))] // duplicate not an error; APIs are coalesced + [TestCase(typeof(ICacheClientExtended), typeof(ICacheClientAsync), typeof(BasicRedisClientManager))] // duplicate not an error; APIs are coalesced [TestCase(typeof(IEntityStore), typeof(IEntityStoreAsync))] [TestCase(typeof(IEntityStore<>), typeof(IEntityStoreAsync<>))] [TestCase(typeof(IRedisClient), typeof(IRedisClientAsync))] @@ -825,9 +839,9 @@ public MethodToken(string name, Type returnType, ParameterToken[] parameters, [TestCase(typeof(IRedisTypedPipeline<>), typeof(IRedisTypedPipelineAsync<>))] [TestCase(typeof(IRedisTypedQueueableOperation<>), typeof(IRedisTypedQueueableOperationAsync<>))] [TestCase(typeof(IRedisTypedTransaction<>), typeof(IRedisTypedTransactionAsync<>))] - public void TestFullyImplemented(Type syncInterface, Type asyncInterface) + public void TestFullyImplemented(Type syncInterface, Type asyncInterface, params Type[] ignore) { - HashSet except = new HashSet(); + HashSet except = new HashSet(ignore ?? Type.EmptyTypes); #if NET472 // only exists there! if (syncInterface == typeof(IRedisClientsManager)) { diff --git a/tests/ServiceStack.Redis.Tests/RedisBasicPersistenceProviderTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisBasicPersistenceProviderTests.Async.cs index 0f4e0b7b..e2e447b1 100644 --- a/tests/ServiceStack.Redis.Tests/RedisBasicPersistenceProviderTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisBasicPersistenceProviderTests.Async.cs @@ -175,7 +175,7 @@ public async Task Can_DeleteAll_with_runtime_type() var mi = typeof(IEntityStoreAsync).GetMethod(nameof(IEntityStoreAsync.DeleteAllAsync)); var genericMi = mi.MakeGenericMethod(typeof(TestModel)); - await (ValueTask)genericMi.Invoke(RedisAsync, new object[] { CancellationToken.None }); + await (Task)genericMi.Invoke(RedisAsync, new object[] { CancellationToken.None }); var allModels = await RedisAsync.As().GetAllAsync(); Assert.That(allModels, Is.Empty); @@ -192,7 +192,7 @@ public async Task Can_As_DeleteAll_with_runtime_type() var genericMi = mi.MakeGenericMethod(typeof(TestModel)); var typedClient = genericMi.Invoke(RedisAsync, TypeConstants.EmptyObjectArray); var deleteMi = typeof(IEntityStoreAsync).GetMethod(nameof(IEntityStoreAsync.DeleteAllAsync)); - await (ValueTask)deleteMi.Invoke(typedClient, new object[] { CancellationToken.None }); + await (Task)deleteMi.Invoke(typedClient, new object[] { CancellationToken.None }); var allModels = await RedisAsync.As().GetAllAsync(); Assert.That(allModels, Is.Empty); From 4a774b8774b9f0396fc3a8d0c8090fdbe210adc1 Mon Sep 17 00:00:00 2001 From: mgravell Date: Thu, 3 Sep 2020 10:49:10 +0100 Subject: [PATCH 011/107] cancellationToken => token; use .AsValueTask() --- .../BasicRedisClientManager.Async.cs | 130 +- .../BufferedReader.Async.cs | 26 +- .../Generic/RedisClientHash.Generic.Async.cs | 32 +- .../Generic/RedisClientList.Generic.Async.cs | 128 +- .../Generic/RedisClientSet.Generic.Async.cs | 70 +- .../RedisClientSortedSet.Generic.Async.cs | 106 +- .../Generic/RedisTypedClient.Async.cs | 648 +++++----- .../Generic/RedisTypedPipeline.Async.cs | 12 +- .../Generic/RedisTypedTransaction.Async.cs | 16 +- .../Pipeline/QueuedRedisOperation.Async.cs | 24 +- .../Pipeline/RedisAllPurposePipeline.Async.cs | 14 +- .../Pipeline/RedisPipelineCommand.Async.cs | 10 +- .../PooledRedisClientManager.Async.cs | 8 +- src/ServiceStack.Redis/RedisClient.Async.cs | 1146 ++++++++--------- .../RedisClientHash.Async.cs | 40 +- .../RedisClientList.Async.cs | 114 +- .../RedisClientManagerCacheClient.Async.cs | 132 +- .../RedisClientSet.Async.cs | 68 +- .../RedisClientSortedSet.Async.cs | 88 +- .../RedisClientsManagerExtensions.Async.cs | 16 +- src/ServiceStack.Redis/RedisLock.Async.cs | 16 +- .../RedisManagerPool.Async.cs | 8 +- .../RedisNativeClient.Async.cs | 834 ++++++------ .../RedisNativeClient_Utils.Async.cs | 170 +-- .../RedisSubscription.Async.cs | 36 +- .../Support/Locking/DistributedLock.Async.cs | 36 +- .../Support/Locking/IDistributedLock.Async.cs | 4 +- .../Transaction/RedisTransaction.Async.cs | 22 +- .../AsyncImplementationsTests.Async.cs | 12 +- .../RedisPipelineTests.Async.cs | 4 +- .../RedisTransactionTests.Async.cs | 14 +- 31 files changed, 1991 insertions(+), 1993 deletions(-) diff --git a/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs b/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs index 39054fc9..0883ae3b 100644 --- a/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs +++ b/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs @@ -36,16 +36,16 @@ private ValueTask GetReadOnlyCacheClientAsync(in Cancellation private IRedisClientAsync ConfigureRedisClientAsync(IRedisClientAsync client) => client; - ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(CancellationToken cancellationToken) - => GetCacheClientAsync(cancellationToken); + ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(CancellationToken token) + => GetCacheClientAsync(token); - ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken cancellationToken) + ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken token) => GetClientImpl().AsValueTaskResult(); - ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken cancellationToken) - => GetReadOnlyCacheClientAsync(cancellationToken); + ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken token) + => GetReadOnlyCacheClientAsync(token); - ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken cancellationToken) + ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken token) => GetReadOnlyClientImpl().AsValueTaskResult(); ValueTask IAsyncDisposable.DisposeAsync() @@ -54,127 +54,127 @@ ValueTask IAsyncDisposable.DisposeAsync() return default; } - async Task ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) + async Task ICacheClientAsync.GetAsync(string key, CancellationToken token) { - await using var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); + await using var client = await GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); return await client.GetAsync(key).ConfigureAwait(false); } - async Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.SetAsync(key, value, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + return await client.SetAsync(key, value, token).ConfigureAwait(false); } - async Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.SetAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresAt, token).ConfigureAwait(false); } - async Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.SetAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresIn, token).ConfigureAwait(false); } - async Task ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) + async Task ICacheClientAsync.FlushAllAsync(CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await client.FlushAllAsync(cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + await client.FlushAllAsync(token).ConfigureAwait(false); } - async Task> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) + async Task> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken token) { - await using var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.GetAllAsync(keys, cancellationToken).ConfigureAwait(false); + await using var client = await GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); + return await client.GetAllAsync(keys, token).ConfigureAwait(false); } - async Task ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await client.SetAllAsync(values, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + await client.SetAllAsync(values, token).ConfigureAwait(false); } - async Task ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) + async Task ICacheClientAsync.RemoveAsync(string key, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.RemoveAsync(key, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + return await client.RemoveAsync(key, token).ConfigureAwait(false); } - async Task ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) + async Task ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await client.RemoveAllAsync(keys, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + await client.RemoveAllAsync(keys, token).ConfigureAwait(false); } - async Task ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) + async Task ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.IncrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + return await client.IncrementAsync(key, amount, token).ConfigureAwait(false); } - async Task ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) + async Task ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.DecrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + return await client.DecrementAsync(key, amount, token).ConfigureAwait(false); } - async Task ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) + async Task ICacheClientAsync.AddAsync(string key, T value, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.AddAsync(key, value, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + return await client.AddAsync(key, value, token).ConfigureAwait(false); } - async Task ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) + async Task ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, token).ConfigureAwait(false); } - async Task ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + async Task ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.AddAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresAt, token).ConfigureAwait(false); } - async Task ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + async Task ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresAt, token).ConfigureAwait(false); } - async Task ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + async Task ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.AddAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresIn, token).ConfigureAwait(false); } - async Task ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + async Task ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresIn, token).ConfigureAwait(false); } - async Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) + async Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken token) { - await using var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.GetTimeToLiveAsync(key, cancellationToken).ConfigureAwait(false); + await using var client = await GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); + return await client.GetTimeToLiveAsync(key, token).ConfigureAwait(false); } - async IAsyncEnumerable ICacheClientAsync.GetKeysByPatternAsync(string pattern, [EnumeratorCancellation] CancellationToken cancellationToken) + async IAsyncEnumerable ICacheClientAsync.GetKeysByPatternAsync(string pattern, [EnumeratorCancellation] CancellationToken token) { - await using var client = await GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - await foreach (var key in client.GetKeysByPatternAsync(pattern, cancellationToken).ConfigureAwait(false).WithCancellation(cancellationToken)) + await using var client = await GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); + await foreach (var key in client.GetKeysByPatternAsync(pattern, token).ConfigureAwait(false).WithCancellation(token)) { yield return key; } } - async Task ICacheClientAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationToken) + async Task ICacheClientAsync.RemoveExpiredEntriesAsync(CancellationToken token) { - await using var client = await GetCacheClientAsync(cancellationToken).ConfigureAwait(false); - await client.RemoveExpiredEntriesAsync(cancellationToken).ConfigureAwait(false); + await using var client = await GetCacheClientAsync(token).ConfigureAwait(false); + await client.RemoveExpiredEntriesAsync(token).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/BufferedReader.Async.cs b/src/ServiceStack.Redis/BufferedReader.Async.cs index c6c9985e..39ad9d7e 100644 --- a/src/ServiceStack.Redis/BufferedReader.Async.cs +++ b/src/ServiceStack.Redis/BufferedReader.Async.cs @@ -7,19 +7,19 @@ namespace ServiceStack.Redis { internal sealed partial class BufferedReader { - internal ValueTask ReadByteAsync(in CancellationToken cancellationToken = default) - => _available > 0 ? ReadByteFromBuffer().AsValueTaskResult() : ReadByteSlowAsync(cancellationToken); + internal ValueTask ReadByteAsync(in CancellationToken token = default) + => _available > 0 ? ReadByteFromBuffer().AsValueTaskResult() : ReadByteSlowAsync(token); - private ValueTask ReadByteSlowAsync(in CancellationToken cancellationToken) + private ValueTask ReadByteSlowAsync(in CancellationToken token) { - cancellationToken.ThrowIfCancellationRequested(); + token.ThrowIfCancellationRequested(); _offset = 0; #if ASYNC_MEMORY - var pending = _source.ReadAsync(new Memory(_buffer), cancellationToken); + var pending = _source.ReadAsync(new Memory(_buffer), token); if (!pending.IsCompletedSuccessfully) return Awaited(this, pending); #else - var pending = _source.ReadAsync(_buffer, 0, _buffer.Length, cancellationToken); + var pending = _source.ReadAsync(_buffer, 0, _buffer.Length, token); if (pending.Status != TaskStatus.RanToCompletion) return Awaited(this, pending); #endif @@ -42,27 +42,27 @@ static async ValueTask Awaited(BufferedReader @this, Task pending) #endif } - internal ValueTask ReadAsync(byte[] buffer, int offset, int count, in CancellationToken cancellationToken = default) + internal ValueTask ReadAsync(byte[] buffer, int offset, int count, in CancellationToken token = default) => _available > 0 ? ReadFromBuffer(buffer, offset, count).AsValueTaskResult() - : ReadSlowAsync(buffer, offset, count, cancellationToken); + : ReadSlowAsync(buffer, offset, count, token); - private ValueTask ReadSlowAsync(byte[] buffer, int offset, int count, in CancellationToken cancellationToken) + private ValueTask ReadSlowAsync(byte[] buffer, int offset, int count, in CancellationToken token) { // if they're asking for more than we deal in, just step out of the way if (count >= buffer.Length) { #if ASYNC_MEMORY - return _source.ReadAsync(new Memory(buffer, offset, count), cancellationToken); + return _source.ReadAsync(new Memory(buffer, offset, count), token); #else - return new ValueTask(_source.ReadAsync(buffer, offset, count, cancellationToken)); + return new ValueTask(_source.ReadAsync(buffer, offset, count, token)); #endif } // they're asking for less, so we could still have some left _offset = 0; #if ASYNC_MEMORY - var pending = _source.ReadAsync(new Memory(_buffer), cancellationToken); + var pending = _source.ReadAsync(new Memory(_buffer), token); if (!pending.IsCompletedSuccessfully) return Awaited(this, pending, buffer, offset, count); @@ -75,7 +75,7 @@ static async ValueTask Awaited(BufferedReader @this, ValueTask pending return @this._available > 0 ? @this.ReadFromBuffer(buffer, offset, count) : 0; } #else - var pending = _source.ReadAsync(_buffer, 0, _buffer.Length, cancellationToken); + var pending = _source.ReadAsync(_buffer, 0, _buffer.Length, token); if (pending.Status != TaskStatus.RanToCompletion) return Awaited(this, pending, buffer, offset, count); diff --git a/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs index 1a214151..dfd59db1 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs @@ -22,34 +22,34 @@ internal partial class RedisClientHash { IRedisTypedClientAsync AsyncClient => client; - ValueTask IRedisHashAsync.AddAsync(KeyValuePair item, CancellationToken cancellationToken) - => AsyncClient.SetEntryInHashAsync(this, item.Key, item.Value, cancellationToken).Await(); + ValueTask IRedisHashAsync.AddAsync(KeyValuePair item, CancellationToken token) + => AsyncClient.SetEntryInHashAsync(this, item.Key, item.Value, token).Await(); - ValueTask IRedisHashAsync.AddAsync(TKey key, T value, CancellationToken cancellationToken) - => AsyncClient.SetEntryInHashAsync(this, key, value, cancellationToken).Await(); + ValueTask IRedisHashAsync.AddAsync(TKey key, T value, CancellationToken token) + => AsyncClient.SetEntryInHashAsync(this, key, value, token).Await(); - ValueTask IRedisHashAsync.ClearAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveEntryAsync(new[] { this }, cancellationToken).Await(); + ValueTask IRedisHashAsync.ClearAsync(CancellationToken token) + => AsyncClient.RemoveEntryAsync(new[] { this }, token).Await(); - ValueTask IRedisHashAsync.ContainsKeyAsync(TKey key, CancellationToken cancellationToken) - => AsyncClient.HashContainsEntryAsync(this, key, cancellationToken); + ValueTask IRedisHashAsync.ContainsKeyAsync(TKey key, CancellationToken token) + => AsyncClient.HashContainsEntryAsync(this, key, token); - ValueTask IRedisHashAsync.CountAsync(CancellationToken cancellationToken) - => AsyncClient.GetHashCountAsync(this, cancellationToken).AsInt32(); + ValueTask IRedisHashAsync.CountAsync(CancellationToken token) + => AsyncClient.GetHashCountAsync(this, token).AsInt32(); - ValueTask> IRedisHashAsync.GetAllAsync(CancellationToken cancellationToken) - => AsyncClient.GetAllEntriesFromHashAsync(this, cancellationToken); + ValueTask> IRedisHashAsync.GetAllAsync(CancellationToken token) + => AsyncClient.GetAllEntriesFromHashAsync(this, token); - async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) + async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken token) { - var all = await AsyncClient.GetAllEntriesFromHashAsync(this, cancellationToken).ConfigureAwait(false); + var all = await AsyncClient.GetAllEntriesFromHashAsync(this, token).ConfigureAwait(false); foreach (var pair in all) { yield return pair; } } - ValueTask IRedisHashAsync.RemoveAsync(TKey key, CancellationToken cancellationToken) - => AsyncClient.RemoveEntryFromHashAsync(this, key, cancellationToken); + ValueTask IRedisHashAsync.RemoveAsync(TKey key, CancellationToken token) + => AsyncClient.RemoveEntryFromHashAsync(this, key, token); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs index bf779689..b961d2e1 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs @@ -25,45 +25,45 @@ internal partial class RedisClientList IRedisTypedClientAsync AsyncClient => client; IRedisListAsync AsAsync() => this; - async ValueTask IRedisListAsync.AddRangeAsync(IEnumerable values, CancellationToken cancellationToken) + async ValueTask IRedisListAsync.AddRangeAsync(IEnumerable values, CancellationToken token) { //TODO: replace it with a pipeline implementation ala AddRangeToSet foreach (var value in values) { - await AsyncClient.AddItemToListAsync(this, value, cancellationToken).ConfigureAwait(false); + await AsyncClient.AddItemToListAsync(this, value, token).ConfigureAwait(false); } } - ValueTask IRedisListAsync.AppendAsync(T value, CancellationToken cancellationToken) - => AsyncClient.AddItemToListAsync(this, value, cancellationToken); + ValueTask IRedisListAsync.AppendAsync(T value, CancellationToken token) + => AsyncClient.AddItemToListAsync(this, value, token); - ValueTask IRedisListAsync.BlockingDequeueAsync(TimeSpan? timeOut, CancellationToken cancellationToken) - => AsyncClient.BlockingDequeueItemFromListAsync(this, timeOut, cancellationToken); + ValueTask IRedisListAsync.BlockingDequeueAsync(TimeSpan? timeOut, CancellationToken token) + => AsyncClient.BlockingDequeueItemFromListAsync(this, timeOut, token); - ValueTask IRedisListAsync.BlockingPopAsync(TimeSpan? timeOut, CancellationToken cancellationToken) - => AsyncClient.BlockingPopItemFromListAsync(this, timeOut, cancellationToken); + ValueTask IRedisListAsync.BlockingPopAsync(TimeSpan? timeOut, CancellationToken token) + => AsyncClient.BlockingPopItemFromListAsync(this, timeOut, token); - ValueTask IRedisListAsync.BlockingRemoveStartAsync(TimeSpan? timeOut, CancellationToken cancellationToken) - => AsyncClient.BlockingRemoveStartFromListAsync(this, timeOut, cancellationToken); + ValueTask IRedisListAsync.BlockingRemoveStartAsync(TimeSpan? timeOut, CancellationToken token) + => AsyncClient.BlockingRemoveStartFromListAsync(this, timeOut, token); - ValueTask IRedisListAsync.CountAsync(CancellationToken cancellationToken) - => AsyncClient.GetListCountAsync(this, cancellationToken).AsInt32(); + ValueTask IRedisListAsync.CountAsync(CancellationToken token) + => AsyncClient.GetListCountAsync(this, token).AsInt32(); - ValueTask IRedisListAsync.DequeueAsync(CancellationToken cancellationToken) - => AsyncClient.DequeueItemFromListAsync(this, cancellationToken); + ValueTask IRedisListAsync.DequeueAsync(CancellationToken token) + => AsyncClient.DequeueItemFromListAsync(this, token); - ValueTask IRedisListAsync.EnqueueAsync(T value, CancellationToken cancellationToken) - => AsyncClient.EnqueueItemOnListAsync(this, value, cancellationToken); + ValueTask IRedisListAsync.EnqueueAsync(T value, CancellationToken token) + => AsyncClient.EnqueueItemOnListAsync(this, value, token); - ValueTask> IRedisListAsync.GetAllAsync(CancellationToken cancellationToken) - => AsyncClient.GetAllItemsFromListAsync(this, cancellationToken); + ValueTask> IRedisListAsync.GetAllAsync(CancellationToken token) + => AsyncClient.GetAllItemsFromListAsync(this, token); - async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) + async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken token) { - var count = await AsAsync().CountAsync(cancellationToken).ConfigureAwait(false); + var count = await AsAsync().CountAsync(token).ConfigureAwait(false); if (count <= PageLimit) { - var all = await AsyncClient.GetAllItemsFromListAsync(this, cancellationToken).ConfigureAwait(false); + var all = await AsyncClient.GetAllItemsFromListAsync(this, token).ConfigureAwait(false); foreach (var item in all) { yield return item; @@ -76,7 +76,7 @@ async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationTok List pageResults; do { - pageResults = await AsyncClient.GetRangeFromListAsync(this, skip, PageLimit, cancellationToken).ConfigureAwait(false); + pageResults = await AsyncClient.GetRangeFromListAsync(this, skip, PageLimit, token).ConfigureAwait(false); foreach (var result in pageResults) { yield return result; @@ -86,57 +86,57 @@ async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationTok } } - ValueTask> IRedisListAsync.GetRangeAsync(int startingFrom, int endingAt, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromListAsync(this, startingFrom, endingAt, cancellationToken); + ValueTask> IRedisListAsync.GetRangeAsync(int startingFrom, int endingAt, CancellationToken token) + => AsyncClient.GetRangeFromListAsync(this, startingFrom, endingAt, token); - ValueTask> IRedisListAsync.GetRangeFromSortedListAsync(int startingFrom, int endingAt, CancellationToken cancellationToken) - => AsyncClient.SortListAsync(this, startingFrom, endingAt, cancellationToken); + ValueTask> IRedisListAsync.GetRangeFromSortedListAsync(int startingFrom, int endingAt, CancellationToken token) + => AsyncClient.SortListAsync(this, startingFrom, endingAt, token); - ValueTask IRedisListAsync.PopAndPushAsync(IRedisListAsync toList, CancellationToken cancellationToken) - => AsyncClient.PopAndPushItemBetweenListsAsync(this, toList, cancellationToken); + ValueTask IRedisListAsync.PopAndPushAsync(IRedisListAsync toList, CancellationToken token) + => AsyncClient.PopAndPushItemBetweenListsAsync(this, toList, token); - ValueTask IRedisListAsync.PopAsync(CancellationToken cancellationToken) - => AsyncClient.PopItemFromListAsync(this, cancellationToken); + ValueTask IRedisListAsync.PopAsync(CancellationToken token) + => AsyncClient.PopItemFromListAsync(this, token); - ValueTask IRedisListAsync.PrependAsync(T value, CancellationToken cancellationToken) - => AsyncClient.PrependItemToListAsync(this, value, cancellationToken); + ValueTask IRedisListAsync.PrependAsync(T value, CancellationToken token) + => AsyncClient.PrependItemToListAsync(this, value, token); - ValueTask IRedisListAsync.PushAsync(T value, CancellationToken cancellationToken) - => AsyncClient.PushItemToListAsync(this, value, cancellationToken); + ValueTask IRedisListAsync.PushAsync(T value, CancellationToken token) + => AsyncClient.PushItemToListAsync(this, value, token); - ValueTask IRedisListAsync.RemoveAllAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveAllFromListAsync(this, cancellationToken); + ValueTask IRedisListAsync.RemoveAllAsync(CancellationToken token) + => AsyncClient.RemoveAllFromListAsync(this, token); - ValueTask IRedisListAsync.RemoveEndAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveEndFromListAsync(this, cancellationToken); + ValueTask IRedisListAsync.RemoveEndAsync(CancellationToken token) + => AsyncClient.RemoveEndFromListAsync(this, token); - ValueTask IRedisListAsync.RemoveStartAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveStartFromListAsync(this, cancellationToken); + ValueTask IRedisListAsync.RemoveStartAsync(CancellationToken token) + => AsyncClient.RemoveStartFromListAsync(this, token); - ValueTask IRedisListAsync.RemoveValueAsync(T value, CancellationToken cancellationToken) - => AsyncClient.RemoveItemFromListAsync(this, value, cancellationToken); + ValueTask IRedisListAsync.RemoveValueAsync(T value, CancellationToken token) + => AsyncClient.RemoveItemFromListAsync(this, value, token); - ValueTask IRedisListAsync.RemoveValueAsync(T value, int noOfMatches, CancellationToken cancellationToken) - => AsyncClient.RemoveItemFromListAsync(this, value, noOfMatches, cancellationToken); + ValueTask IRedisListAsync.RemoveValueAsync(T value, int noOfMatches, CancellationToken token) + => AsyncClient.RemoveItemFromListAsync(this, value, noOfMatches, token); - ValueTask IRedisListAsync.TrimAsync(int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken) - => AsyncClient.TrimListAsync(this, keepStartingFrom, keepEndingAt, cancellationToken); + ValueTask IRedisListAsync.TrimAsync(int keepStartingFrom, int keepEndingAt, CancellationToken token) + => AsyncClient.TrimListAsync(this, keepStartingFrom, keepEndingAt, token); - async ValueTask IRedisListAsync.RemoveAsync(T value, CancellationToken cancellationToken) + async ValueTask IRedisListAsync.RemoveAsync(T value, CancellationToken token) { - var index = await AsAsync().IndexOfAsync(value, cancellationToken).ConfigureAwait(false); + var index = await AsAsync().IndexOfAsync(value, token).ConfigureAwait(false); if (index != -1) { - await AsAsync().RemoveAtAsync(index, cancellationToken).ConfigureAwait(false); + await AsAsync().RemoveAtAsync(index, token).ConfigureAwait(false); return true; } return false; } - ValueTask IRedisListAsync.AddAsync(T value, CancellationToken cancellationToken) - => AsyncClient.AddItemToListAsync(this, value, cancellationToken); + ValueTask IRedisListAsync.AddAsync(T value, CancellationToken token) + => AsyncClient.AddItemToListAsync(this, value, token); - async ValueTask IRedisListAsync.RemoveAtAsync(int index, CancellationToken cancellationToken) + async ValueTask IRedisListAsync.RemoveAtAsync(int index, CancellationToken token) { //TODO: replace with native implementation when one exists @@ -144,30 +144,30 @@ async ValueTask IRedisListAsync.RemoveAtAsync(int index, CancellationToken ca $"The native client ('{client.NativeClient.GetType().Name}') does not implement {nameof(IRedisNativeClientAsync)}"); var markForDelete = Guid.NewGuid().ToString(); - await nativeClient.LSetAsync(listId, index, Encoding.UTF8.GetBytes(markForDelete), cancellationToken).ConfigureAwait(false); + await nativeClient.LSetAsync(listId, index, Encoding.UTF8.GetBytes(markForDelete), token).ConfigureAwait(false); const int removeAll = 0; - await nativeClient.LRemAsync(listId, removeAll, Encoding.UTF8.GetBytes(markForDelete), cancellationToken).ConfigureAwait(false); + await nativeClient.LRemAsync(listId, removeAll, Encoding.UTF8.GetBytes(markForDelete), token).ConfigureAwait(false); } - async ValueTask IRedisListAsync.ContainsAsync(T value, CancellationToken cancellationToken) + async ValueTask IRedisListAsync.ContainsAsync(T value, CancellationToken token) { //TODO: replace with native implementation when exists - await foreach (var existingItem in this.ConfigureAwait(false).WithCancellation(cancellationToken)) + await foreach (var existingItem in this.ConfigureAwait(false).WithCancellation(token)) { if (Equals(existingItem, value)) return true; } return false; } - ValueTask IRedisListAsync.ClearAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveAllFromListAsync(this, cancellationToken); + ValueTask IRedisListAsync.ClearAsync(CancellationToken token) + => AsyncClient.RemoveAllFromListAsync(this, token); - async ValueTask IRedisListAsync.IndexOfAsync(T value, CancellationToken cancellationToken) + async ValueTask IRedisListAsync.IndexOfAsync(T value, CancellationToken token) { //TODO: replace with native implementation when exists var i = 0; - await foreach (var existingItem in this.ConfigureAwait(false).WithCancellation(cancellationToken)) + await foreach (var existingItem in this.ConfigureAwait(false).WithCancellation(token)) { if (Equals(existingItem, value)) return i; i++; @@ -175,10 +175,10 @@ async ValueTask IRedisListAsync.IndexOfAsync(T value, CancellationToken return -1; } - ValueTask IRedisListAsync.ElementAtAsync(int index, CancellationToken cancellationToken) - => AsyncClient.GetItemFromListAsync(this, index, cancellationToken); + ValueTask IRedisListAsync.ElementAtAsync(int index, CancellationToken token) + => AsyncClient.GetItemFromListAsync(this, index, token); - ValueTask IRedisListAsync.SetValueAsync(int index, T value, CancellationToken cancellationToken) - => AsyncClient.SetItemInListAsync(this, index, value, cancellationToken); + ValueTask IRedisListAsync.SetValueAsync(int index, T value, CancellationToken token) + => AsyncClient.SetItemInListAsync(this, index, value, token); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs index e8080193..9d3c7818 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs @@ -22,29 +22,29 @@ internal partial class RedisClientSet { IRedisTypedClientAsync AsyncClient => client; - ValueTask IRedisSetAsync.AddAsync(T value, CancellationToken cancellationToken) - => AsyncClient.AddItemToSetAsync(this, value, cancellationToken); + ValueTask IRedisSetAsync.AddAsync(T value, CancellationToken token) + => AsyncClient.AddItemToSetAsync(this, value, token); IRedisSetAsync AsAsync() => this; - ValueTask IRedisSetAsync.ClearAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveEntryAsync(setId, cancellationToken).Await(); + ValueTask IRedisSetAsync.ClearAsync(CancellationToken token) + => AsyncClient.RemoveEntryAsync(setId, token).Await(); - ValueTask IRedisSetAsync.ContainsAsync(T item, CancellationToken cancellationToken) - => AsyncClient.SetContainsItemAsync(this, item, cancellationToken); + ValueTask IRedisSetAsync.ContainsAsync(T item, CancellationToken token) + => AsyncClient.SetContainsItemAsync(this, item, token); - ValueTask IRedisSetAsync.CountAsync(CancellationToken cancellationToken) - => AsyncClient.GetSetCountAsync(this, cancellationToken).AsInt32(); + ValueTask IRedisSetAsync.CountAsync(CancellationToken token) + => AsyncClient.GetSetCountAsync(this, token).AsInt32(); - ValueTask> IRedisSetAsync.GetAllAsync(CancellationToken cancellationToken) - => AsyncClient.GetAllItemsFromSetAsync(this, cancellationToken); + ValueTask> IRedisSetAsync.GetAllAsync(CancellationToken token) + => AsyncClient.GetAllItemsFromSetAsync(this, token); - async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) + async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken token) { - var count = await AsAsync().CountAsync(cancellationToken).ConfigureAwait(false); + var count = await AsAsync().CountAsync(token).ConfigureAwait(false); if (count <= PageLimit) { - var all = await AsyncClient.GetAllItemsFromSetAsync(this, cancellationToken).ConfigureAwait(false); + var all = await AsyncClient.GetAllItemsFromSetAsync(this, token).ConfigureAwait(false); foreach (var item in all) { yield return item; @@ -67,43 +67,43 @@ async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationTok } } - ValueTask IRedisSetAsync.GetDifferencesAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken) - => AsyncClient.StoreUnionFromSetsAsync(this, withSets, cancellationToken); + ValueTask IRedisSetAsync.GetDifferencesAsync(IRedisSetAsync[] withSets, CancellationToken token) + => AsyncClient.StoreUnionFromSetsAsync(this, withSets, token); ValueTask IRedisSetAsync.GetDifferencesAsync(params IRedisSetAsync[] withSets) - => AsAsync().GetDifferencesAsync(withSets, cancellationToken: default); + => AsAsync().GetDifferencesAsync(withSets, token: default); - ValueTask IRedisSetAsync.GetRandomItemAsync(CancellationToken cancellationToken) - => AsyncClient.GetRandomItemFromSetAsync(this, cancellationToken); + ValueTask IRedisSetAsync.GetRandomItemAsync(CancellationToken token) + => AsyncClient.GetRandomItemFromSetAsync(this, token); - ValueTask IRedisSetAsync.MoveToAsync(T item, IRedisSetAsync toSet, CancellationToken cancellationToken) - => AsyncClient.MoveBetweenSetsAsync(this, toSet, item, cancellationToken); + ValueTask IRedisSetAsync.MoveToAsync(T item, IRedisSetAsync toSet, CancellationToken token) + => AsyncClient.MoveBetweenSetsAsync(this, toSet, item, token); - ValueTask IRedisSetAsync.PopRandomItemAsync(CancellationToken cancellationToken) - => AsyncClient.PopItemFromSetAsync(this, cancellationToken); + ValueTask IRedisSetAsync.PopRandomItemAsync(CancellationToken token) + => AsyncClient.PopItemFromSetAsync(this, token); - ValueTask IRedisSetAsync.PopulateWithDifferencesOfAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken) - => AsyncClient.StoreDifferencesFromSetAsync(this, fromSet, withSets, cancellationToken); + ValueTask IRedisSetAsync.PopulateWithDifferencesOfAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken token) + => AsyncClient.StoreDifferencesFromSetAsync(this, fromSet, withSets, token); ValueTask IRedisSetAsync.PopulateWithDifferencesOfAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets) - => AsAsync().PopulateWithDifferencesOfAsync(fromSet, withSets, cancellationToken: default); + => AsAsync().PopulateWithDifferencesOfAsync(fromSet, withSets, token: default); - ValueTask IRedisSetAsync.PopulateWithIntersectOfAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken) - => AsyncClient.StoreIntersectFromSetsAsync(this, sets, cancellationToken); + ValueTask IRedisSetAsync.PopulateWithIntersectOfAsync(IRedisSetAsync[] sets, CancellationToken token) + => AsyncClient.StoreIntersectFromSetsAsync(this, sets, token); ValueTask IRedisSetAsync.PopulateWithIntersectOfAsync(params IRedisSetAsync[] sets) - => AsAsync().PopulateWithIntersectOfAsync(sets, cancellationToken: default); + => AsAsync().PopulateWithIntersectOfAsync(sets, token: default); - ValueTask IRedisSetAsync.PopulateWithUnionOfAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken) - => AsyncClient.StoreUnionFromSetsAsync(this, sets, cancellationToken); + ValueTask IRedisSetAsync.PopulateWithUnionOfAsync(IRedisSetAsync[] sets, CancellationToken token) + => AsyncClient.StoreUnionFromSetsAsync(this, sets, token); ValueTask IRedisSetAsync.PopulateWithUnionOfAsync(params IRedisSetAsync[] sets) - => AsAsync().PopulateWithUnionOfAsync(sets, cancellationToken: default); + => AsAsync().PopulateWithUnionOfAsync(sets, token: default); - ValueTask IRedisSetAsync.RemoveAsync(T value, CancellationToken cancellationToken) - => AsyncClient.RemoveItemFromSetAsync(this, value, cancellationToken).AwaitAsTrue(); // see Remove for why "true" + ValueTask IRedisSetAsync.RemoveAsync(T value, CancellationToken token) + => AsyncClient.RemoveItemFromSetAsync(this, value, token).AwaitAsTrue(); // see Remove for why "true" - ValueTask> IRedisSetAsync.SortAsync(int startingFrom, int endingAt, CancellationToken cancellationToken) - => AsyncClient.GetSortedEntryValuesAsync(this, startingFrom, endingAt, cancellationToken); + ValueTask> IRedisSetAsync.SortAsync(int startingFrom, int endingAt, CancellationToken token) + => AsyncClient.GetSortedEntryValuesAsync(this, startingFrom, endingAt, token); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs index 4e5d2cfe..d0973d66 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs @@ -24,24 +24,24 @@ internal partial class RedisClientSortedSet IRedisSortedSetAsync AsAsync() => this; - ValueTask IRedisSortedSetAsync.AddAsync(T item, double score, CancellationToken cancellationToken) + ValueTask IRedisSortedSetAsync.AddAsync(T item, double score, CancellationToken token) => AsyncClient.AddItemToSortedSetAsync(this, item, score); - ValueTask IRedisSortedSetAsync.CountAsync(CancellationToken cancellationToken) - => AsyncClient.GetSortedSetCountAsync(this, cancellationToken).AsInt32(); + ValueTask IRedisSortedSetAsync.CountAsync(CancellationToken token) + => AsyncClient.GetSortedSetCountAsync(this, token).AsInt32(); - ValueTask> IRedisSortedSetAsync.GetAllAsync(CancellationToken cancellationToken) + ValueTask> IRedisSortedSetAsync.GetAllAsync(CancellationToken token) => AsyncClient.GetAllItemsFromSortedSetAsync(this); - ValueTask> IRedisSortedSetAsync.GetAllDescendingAsync(CancellationToken cancellationToken) + ValueTask> IRedisSortedSetAsync.GetAllDescendingAsync(CancellationToken token) => AsyncClient.GetAllItemsFromSortedSetDescAsync(this); - async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) + async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken token) { - var count = await AsAsync().CountAsync(cancellationToken).ConfigureAwait(false); + var count = await AsAsync().CountAsync(token).ConfigureAwait(false); if (count <= PageLimit) { - var all = await AsyncClient.GetAllItemsFromSortedSetAsync(this, cancellationToken).ConfigureAwait(false); + var all = await AsyncClient.GetAllItemsFromSortedSetAsync(this, token).ConfigureAwait(false); foreach (var item in all) { yield return item; @@ -54,7 +54,7 @@ async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationTok List pageResults; do { - pageResults = await AsyncClient.GetRangeFromSortedSetAsync(this, skip, skip + PageLimit - 1, cancellationToken).ConfigureAwait(false); + pageResults = await AsyncClient.GetRangeFromSortedSetAsync(this, skip, skip + PageLimit - 1, token).ConfigureAwait(false); foreach (var result in pageResults) { yield return result; @@ -64,73 +64,73 @@ async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationTok } } - ValueTask IRedisSortedSetAsync.GetItemScoreAsync(T item, CancellationToken cancellationToken) - => AsyncClient.GetItemScoreInSortedSetAsync(this, item, cancellationToken); + ValueTask IRedisSortedSetAsync.GetItemScoreAsync(T item, CancellationToken token) + => AsyncClient.GetItemScoreInSortedSetAsync(this, item, token); - ValueTask> IRedisSortedSetAsync.GetRangeAsync(int fromRank, int toRank, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetAsync(this, fromRank, toRank, cancellationToken); + ValueTask> IRedisSortedSetAsync.GetRangeAsync(int fromRank, int toRank, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetAsync(this, fromRank, toRank, token); - ValueTask> IRedisSortedSetAsync.GetRangeByHighestScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(this, fromScore, toScore, cancellationToken); + ValueTask> IRedisSortedSetAsync.GetRangeByHighestScoreAsync(double fromScore, double toScore, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(this, fromScore, toScore, token); - ValueTask> IRedisSortedSetAsync.GetRangeByHighestScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(this, fromScore, toScore, skip, take, cancellationToken); + ValueTask> IRedisSortedSetAsync.GetRangeByHighestScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(this, fromScore, toScore, skip, take, token); - ValueTask> IRedisSortedSetAsync.GetRangeByLowestScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(this, fromScore, toScore, cancellationToken); + ValueTask> IRedisSortedSetAsync.GetRangeByLowestScoreAsync(double fromScore, double toScore, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(this, fromScore, toScore, token); - ValueTask> IRedisSortedSetAsync.GetRangeByLowestScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(this, fromScore, toScore, skip, take, cancellationToken); + ValueTask> IRedisSortedSetAsync.GetRangeByLowestScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(this, fromScore, toScore, skip, take, token); - ValueTask IRedisSortedSetAsync.IncrementItemAsync(T item, double incrementBy, CancellationToken cancellationToken) - => AsyncClient.IncrementItemInSortedSetAsync(this, item, incrementBy, cancellationToken); + ValueTask IRedisSortedSetAsync.IncrementItemAsync(T item, double incrementBy, CancellationToken token) + => AsyncClient.IncrementItemInSortedSetAsync(this, item, incrementBy, token); - ValueTask IRedisSortedSetAsync.IndexOfAsync(T item, CancellationToken cancellationToken) - => AsyncClient.GetItemIndexInSortedSetAsync(this, item, cancellationToken).AsInt32(); + ValueTask IRedisSortedSetAsync.IndexOfAsync(T item, CancellationToken token) + => AsyncClient.GetItemIndexInSortedSetAsync(this, item, token).AsInt32(); - ValueTask IRedisSortedSetAsync.IndexOfDescendingAsync(T item, CancellationToken cancellationToken) - => AsyncClient.GetItemIndexInSortedSetDescAsync(this, item, cancellationToken); + ValueTask IRedisSortedSetAsync.IndexOfDescendingAsync(T item, CancellationToken token) + => AsyncClient.GetItemIndexInSortedSetDescAsync(this, item, token); - ValueTask IRedisSortedSetAsync.PopItemWithHighestScoreAsync(CancellationToken cancellationToken) - => AsyncClient.PopItemWithHighestScoreFromSortedSetAsync(this, cancellationToken); + ValueTask IRedisSortedSetAsync.PopItemWithHighestScoreAsync(CancellationToken token) + => AsyncClient.PopItemWithHighestScoreFromSortedSetAsync(this, token); - ValueTask IRedisSortedSetAsync.PopItemWithLowestScoreAsync(CancellationToken cancellationToken) - => AsyncClient.PopItemWithLowestScoreFromSortedSetAsync(this, cancellationToken); + ValueTask IRedisSortedSetAsync.PopItemWithLowestScoreAsync(CancellationToken token) + => AsyncClient.PopItemWithLowestScoreFromSortedSetAsync(this, token); - ValueTask IRedisSortedSetAsync.PopulateWithIntersectOfAsync(IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken) - => AsyncClient.StoreIntersectFromSortedSetsAsync(this, setIds, cancellationToken); + ValueTask IRedisSortedSetAsync.PopulateWithIntersectOfAsync(IRedisSortedSetAsync[] setIds, CancellationToken token) + => AsyncClient.StoreIntersectFromSortedSetsAsync(this, setIds, token); - ValueTask IRedisSortedSetAsync.PopulateWithIntersectOfAsync(IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken) - => AsyncClient.StoreIntersectFromSortedSetsAsync(this, setIds, args, cancellationToken); + ValueTask IRedisSortedSetAsync.PopulateWithIntersectOfAsync(IRedisSortedSetAsync[] setIds, string[] args, CancellationToken token) + => AsyncClient.StoreIntersectFromSortedSetsAsync(this, setIds, args, token); - ValueTask IRedisSortedSetAsync.PopulateWithUnionOfAsync(IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken) - => AsyncClient.StoreUnionFromSortedSetsAsync(this, setIds, cancellationToken); + ValueTask IRedisSortedSetAsync.PopulateWithUnionOfAsync(IRedisSortedSetAsync[] setIds, CancellationToken token) + => AsyncClient.StoreUnionFromSortedSetsAsync(this, setIds, token); - ValueTask IRedisSortedSetAsync.PopulateWithUnionOfAsync(IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken) - => AsyncClient.StoreUnionFromSortedSetsAsync(this, setIds, args, cancellationToken); + ValueTask IRedisSortedSetAsync.PopulateWithUnionOfAsync(IRedisSortedSetAsync[] setIds, string[] args, CancellationToken token) + => AsyncClient.StoreUnionFromSortedSetsAsync(this, setIds, args, token); - ValueTask IRedisSortedSetAsync.RemoveRangeAsync(int minRank, int maxRank, CancellationToken cancellationToken) - => AsyncClient.RemoveRangeFromSortedSetAsync(this, minRank, maxRank, cancellationToken); + ValueTask IRedisSortedSetAsync.RemoveRangeAsync(int minRank, int maxRank, CancellationToken token) + => AsyncClient.RemoveRangeFromSortedSetAsync(this, minRank, maxRank, token); - ValueTask IRedisSortedSetAsync.RemoveRangeByScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken) - => AsyncClient.RemoveRangeFromSortedSetByScoreAsync(this, fromScore, toScore, cancellationToken); + ValueTask IRedisSortedSetAsync.RemoveRangeByScoreAsync(double fromScore, double toScore, CancellationToken token) + => AsyncClient.RemoveRangeFromSortedSetByScoreAsync(this, fromScore, toScore, token); - ValueTask IRedisSortedSetAsync.ClearAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveEntryAsync(setId, cancellationToken).Await(); + ValueTask IRedisSortedSetAsync.ClearAsync(CancellationToken token) + => AsyncClient.RemoveEntryAsync(setId, token).Await(); - ValueTask IRedisSortedSetAsync.ContainsAsync(T value, CancellationToken cancellationToken) - => AsyncClient.SortedSetContainsItemAsync(this, value, cancellationToken); + ValueTask IRedisSortedSetAsync.ContainsAsync(T value, CancellationToken token) + => AsyncClient.SortedSetContainsItemAsync(this, value, token); - ValueTask IRedisSortedSetAsync.AddAsync(T value, CancellationToken cancellationToken) - => AsyncClient.AddItemToSortedSetAsync(this, value, cancellationToken); + ValueTask IRedisSortedSetAsync.AddAsync(T value, CancellationToken token) + => AsyncClient.AddItemToSortedSetAsync(this, value, token); - ValueTask IRedisSortedSetAsync.RemoveAsync(T value, CancellationToken cancellationToken) - => AsyncClient.RemoveItemFromSortedSetAsync(this, value, cancellationToken).AwaitAsTrue(); // see Remove for why "true" + ValueTask IRedisSortedSetAsync.RemoveAsync(T value, CancellationToken token) + => AsyncClient.RemoveItemFromSortedSetAsync(this, value, token).AwaitAsTrue(); // see Remove for why "true" ValueTask IRedisSortedSetAsync.PopulateWithIntersectOfAsync(params IRedisSortedSetAsync[] setIds) - => AsAsync().PopulateWithIntersectOfAsync(setIds, cancellationToken: default); + => AsAsync().PopulateWithIntersectOfAsync(setIds, token: default); ValueTask IRedisSortedSetAsync.PopulateWithUnionOfAsync(params IRedisSortedSetAsync[] setIds) - => AsAsync().PopulateWithUnionOfAsync(setIds, cancellationToken: default); + => AsAsync().PopulateWithUnionOfAsync(setIds, token: default); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs index 18af3ec1..f4d7b67b 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs @@ -37,118 +37,118 @@ partial class RedisTypedClient IRedisClientAsync IRedisTypedClientAsync.RedisClient => client; - internal ValueTask ExpectQueuedAsync(CancellationToken cancellationToken) - => client.ExpectQueuedAsync(cancellationToken); + internal ValueTask ExpectQueuedAsync(CancellationToken token) + => client.ExpectQueuedAsync(token); - internal ValueTask ExpectOkAsync(CancellationToken cancellationToken) - => client.ExpectOkAsync(cancellationToken); + internal ValueTask ExpectOkAsync(CancellationToken token) + => client.ExpectOkAsync(token); - internal ValueTask ReadMultiDataResultCountAsync(CancellationToken cancellationToken) - => client.ReadMultiDataResultCountAsync(cancellationToken); + internal ValueTask ReadMultiDataResultCountAsync(CancellationToken token) + => client.ReadMultiDataResultCountAsync(token); - ValueTask IRedisTypedClientAsync.GetValueAsync(string key, CancellationToken cancellationToken) - => DeserializeValueAsync(AsyncNative.GetAsync(key, cancellationToken)); + ValueTask IRedisTypedClientAsync.GetValueAsync(string key, CancellationToken token) + => DeserializeValueAsync(AsyncNative.GetAsync(key, token)); - async ValueTask IRedisTypedClientAsync.SetValueAsync(string key, T entity, CancellationToken cancellationToken) + async ValueTask IRedisTypedClientAsync.SetValueAsync(string key, T entity, CancellationToken token) { AssertNotNull(key); - await AsyncClient.SetAsync(key, SerializeValue(entity), cancellationToken).ConfigureAwait(false); - await client.RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); + await AsyncClient.SetAsync(key, SerializeValue(entity), token).ConfigureAwait(false); + await client.RegisterTypeIdAsync(entity, token).ConfigureAwait(false); } - Task IEntityStoreAsync.GetByIdAsync(object id, CancellationToken cancellationToken) + Task IEntityStoreAsync.GetByIdAsync(object id, CancellationToken token) { var key = client.UrnKey(id); - return AsAsync().GetValueAsync(key, cancellationToken).AsTask(); + return AsAsync().GetValueAsync(key, token).AsTask(); } - internal ValueTask FlushSendBufferAsync(CancellationToken cancellationToken) - => client.FlushSendBufferAsync(cancellationToken); + internal ValueTask FlushSendBufferAsync(CancellationToken token) + => client.FlushSendBufferAsync(token); - internal ValueTask AddTypeIdsRegisteredDuringPipelineAsync(CancellationToken cancellationToken) - => client.AddTypeIdsRegisteredDuringPipelineAsync(cancellationToken); + internal ValueTask AddTypeIdsRegisteredDuringPipelineAsync(CancellationToken token) + => client.AddTypeIdsRegisteredDuringPipelineAsync(token); - async Task> IEntityStoreAsync.GetByIdsAsync(IEnumerable ids, CancellationToken cancellationToken) + async Task> IEntityStoreAsync.GetByIdsAsync(IEnumerable ids, CancellationToken token) { if (ids != null) { var urnKeys = ids.Map(x => client.UrnKey(x)); if (urnKeys.Count != 0) - return await AsAsync().GetValuesAsync(urnKeys, cancellationToken).ConfigureAwait(false); + return await AsAsync().GetValuesAsync(urnKeys, token).ConfigureAwait(false); } return new List(); } - async Task> IEntityStoreAsync.GetAllAsync(CancellationToken cancellationToken) + async Task> IEntityStoreAsync.GetAllAsync(CancellationToken token) { - var allKeys = await AsyncClient.GetAllItemsFromSetAsync(this.TypeIdsSetKey, cancellationToken).ConfigureAwait(false); - return await AsAsync().GetByIdsAsync(allKeys.ToArray(), cancellationToken).ConfigureAwait(false); + var allKeys = await AsyncClient.GetAllItemsFromSetAsync(this.TypeIdsSetKey, token).ConfigureAwait(false); + return await AsAsync().GetByIdsAsync(allKeys.ToArray(), token).ConfigureAwait(false); } - async Task IEntityStoreAsync.StoreAsync(T entity, CancellationToken cancellationToken) + async Task IEntityStoreAsync.StoreAsync(T entity, CancellationToken token) { var urnKey = client.UrnKey(entity); - await AsAsync().SetValueAsync(urnKey, entity, cancellationToken).ConfigureAwait(false); + await AsAsync().SetValueAsync(urnKey, entity, token).ConfigureAwait(false); return entity; } - async Task IEntityStoreAsync.StoreAllAsync(IEnumerable entities, CancellationToken cancellationToken) + async Task IEntityStoreAsync.StoreAllAsync(IEnumerable entities, CancellationToken token) { if (PrepareStoreAll(entities, out var keys, out var values, out var entitiesList)) { - await AsyncNative.MSetAsync(keys, values, cancellationToken).ConfigureAwait(false); - await client.RegisterTypeIdsAsync(entitiesList, cancellationToken).ConfigureAwait(false); + await AsyncNative.MSetAsync(keys, values, token).ConfigureAwait(false); + await client.RegisterTypeIdsAsync(entitiesList, token).ConfigureAwait(false); } } - async Task IEntityStoreAsync.DeleteAsync(T entity, CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteAsync(T entity, CancellationToken token) { var urnKey = client.UrnKey(entity); - await AsyncClient.RemoveEntryAsync(new[] { urnKey }, cancellationToken).ConfigureAwait(false); - await client.RemoveTypeIdsAsync(new[] { entity }, cancellationToken).ConfigureAwait(false); + await AsyncClient.RemoveEntryAsync(new[] { urnKey }, token).ConfigureAwait(false); + await client.RemoveTypeIdsAsync(new[] { entity }, token).ConfigureAwait(false); } - async Task IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken token) { var urnKey = client.UrnKey(id); - await AsyncClient.RemoveEntryAsync(new[] { urnKey }, cancellationToken).ConfigureAwait(false); - await client.RemoveTypeIdsAsync(new[] { id.ToString() }, cancellationToken).ConfigureAwait(false); + await AsyncClient.RemoveEntryAsync(new[] { urnKey }, token).ConfigureAwait(false); + await client.RemoveTypeIdsAsync(new[] { id.ToString() }, token).ConfigureAwait(false); } - async Task IEntityStoreAsync.DeleteByIdsAsync(IEnumerable ids, CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteByIdsAsync(IEnumerable ids, CancellationToken token) { if (ids == null) return; var urnKeys = ids.Map(t => client.UrnKey(t)); if (urnKeys.Count > 0) { - await AsyncClient.RemoveEntryAsync(urnKeys.ToArray(), cancellationToken).ConfigureAwait(false); - await client.RemoveTypeIdsAsync(ids.Map(x => x.ToString()).ToArray(), cancellationToken).ConfigureAwait(false); + await AsyncClient.RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); + await client.RemoveTypeIdsAsync(ids.Map(x => x.ToString()).ToArray(), token).ConfigureAwait(false); } } - async Task IEntityStoreAsync.DeleteAllAsync(CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteAllAsync(CancellationToken token) { - var ids = await AsyncClient.GetAllItemsFromSetAsync(this.TypeIdsSetKey, cancellationToken).ConfigureAwait(false); + var ids = await AsyncClient.GetAllItemsFromSetAsync(this.TypeIdsSetKey, token).ConfigureAwait(false); var urnKeys = ids.Map(t => client.UrnKey(t)); if (urnKeys.Count > 0) { - await AsyncClient.RemoveEntryAsync(urnKeys.ToArray(), cancellationToken).ConfigureAwait(false); - await AsyncClient.RemoveEntryAsync(new[] { this.TypeIdsSetKey }, cancellationToken).ConfigureAwait(false); + await AsyncClient.RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); + await AsyncClient.RemoveEntryAsync(new[] { this.TypeIdsSetKey }, token).ConfigureAwait(false); } } - async ValueTask> IRedisTypedClientAsync.GetValuesAsync(List keys, CancellationToken cancellationToken) + async ValueTask> IRedisTypedClientAsync.GetValuesAsync(List keys, CancellationToken token) { if (keys.IsNullOrEmpty()) return new List(); - var resultBytesArray = await AsyncNative.MGetAsync(keys.ToArray(), cancellationToken).ConfigureAwait(false); + var resultBytesArray = await AsyncNative.MGetAsync(keys.ToArray(), token).ConfigureAwait(false); return ProcessGetValues(resultBytesArray); } - ValueTask> IRedisTypedClientAsync.CreateTransactionAsync(CancellationToken cancellationToken) + ValueTask> IRedisTypedClientAsync.CreateTransactionAsync(CancellationToken token) { IRedisTypedTransactionAsync obj = new RedisTypedTransaction(this, true); return obj.AsValueTaskResult(); @@ -158,8 +158,8 @@ IRedisTypedPipelineAsync IRedisTypedClientAsync.CreatePipeline() => new RedisTypedPipeline(this); - ValueTask IRedisTypedClientAsync.AcquireLockAsync(TimeSpan? timeOut, CancellationToken cancellationToken) - => AsyncClient.AcquireLockAsync(this.TypeLockKey, timeOut, cancellationToken); + ValueTask IRedisTypedClientAsync.AcquireLockAsync(TimeSpan? timeOut, CancellationToken token) + => AsyncClient.AcquireLockAsync(this.TypeLockKey, timeOut, token); long IRedisTypedClientAsync.Db => AsyncClient.Db; @@ -169,26 +169,26 @@ ValueTask IRedisTypedClientAsync.AcquireLockAsync(TimeSpan? IRedisHashAsync IRedisTypedClientAsync.GetHash(string hashId) => GetHash(hashId) as IRedisHashAsync ?? throw new NotSupportedException("The provided Hash does not support IRedisHashAsync"); - ValueTask IRedisTypedClientAsync.SelectAsync(long db, CancellationToken cancellationToken) - => AsyncClient.SelectAsync(db, cancellationToken); + ValueTask IRedisTypedClientAsync.SelectAsync(long db, CancellationToken token) + => AsyncClient.SelectAsync(db, token); - ValueTask> IRedisTypedClientAsync.GetAllKeysAsync(CancellationToken cancellationToken) - => AsyncClient.GetAllKeysAsync(cancellationToken); + ValueTask> IRedisTypedClientAsync.GetAllKeysAsync(CancellationToken token) + => AsyncClient.GetAllKeysAsync(token); - ValueTask IRedisTypedClientAsync.SetSequenceAsync(int value, CancellationToken cancellationToken) - => AsyncNative.GetSetAsync(SequenceKey, Encoding.UTF8.GetBytes(value.ToString()), cancellationToken).Await(); + ValueTask IRedisTypedClientAsync.SetSequenceAsync(int value, CancellationToken token) + => AsyncNative.GetSetAsync(SequenceKey, Encoding.UTF8.GetBytes(value.ToString()), token).Await(); - ValueTask IRedisTypedClientAsync.GetNextSequenceAsync(CancellationToken cancellationToken) - => AsAsync().IncrementValueAsync(SequenceKey, cancellationToken); + ValueTask IRedisTypedClientAsync.GetNextSequenceAsync(CancellationToken token) + => AsAsync().IncrementValueAsync(SequenceKey, token); - ValueTask IRedisTypedClientAsync.GetNextSequenceAsync(int incrBy, CancellationToken cancellationToken) - => AsAsync().IncrementValueByAsync(SequenceKey, incrBy, cancellationToken); + ValueTask IRedisTypedClientAsync.GetNextSequenceAsync(int incrBy, CancellationToken token) + => AsAsync().IncrementValueByAsync(SequenceKey, incrBy, token); - ValueTask IRedisTypedClientAsync.GetEntryTypeAsync(string key, CancellationToken cancellationToken) - => AsyncClient.GetEntryTypeAsync(key, cancellationToken); + ValueTask IRedisTypedClientAsync.GetEntryTypeAsync(string key, CancellationToken token) + => AsyncClient.GetEntryTypeAsync(key, token); - ValueTask IRedisTypedClientAsync.GetRandomKeyAsync(CancellationToken cancellationToken) - => AsyncClient.GetRandomKeyAsync(cancellationToken); + ValueTask IRedisTypedClientAsync.GetRandomKeyAsync(CancellationToken token) + => AsyncClient.GetRandomKeyAsync(token); [MethodImpl(MethodImplOptions.AggressiveInlining)] static void AssertNotNull(object obj, string name = "key") @@ -197,102 +197,102 @@ static void AssertNotNull(object obj, string name = "key") static void Throw(string name) => throw new ArgumentNullException(name); } - async ValueTask IRedisTypedClientAsync.SetValueAsync(string key, T entity, TimeSpan expireIn, CancellationToken cancellationToken) + async ValueTask IRedisTypedClientAsync.SetValueAsync(string key, T entity, TimeSpan expireIn, CancellationToken token) { AssertNotNull(key); await AsyncClient.SetAsync(key, SerializeValue(entity)).ConfigureAwait(false); - await client.RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); + await client.RegisterTypeIdAsync(entity, token).ConfigureAwait(false); } - async ValueTask IRedisTypedClientAsync.SetValueIfNotExistsAsync(string key, T entity, CancellationToken cancellationToken) + async ValueTask IRedisTypedClientAsync.SetValueIfNotExistsAsync(string key, T entity, CancellationToken token) { var success = await AsyncNative.SetNXAsync(key, SerializeValue(entity)).IsSuccessAsync().ConfigureAwait(false); - if (success) await client.RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); + if (success) await client.RegisterTypeIdAsync(entity, token).ConfigureAwait(false); return success; } - async ValueTask IRedisTypedClientAsync.SetValueIfExistsAsync(string key, T entity, CancellationToken cancellationToken) + async ValueTask IRedisTypedClientAsync.SetValueIfExistsAsync(string key, T entity, CancellationToken token) { var success = await AsyncNative.SetAsync(key, SerializeValue(entity), exists: true).ConfigureAwait(false); - if (success) await client.RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); + if (success) await client.RegisterTypeIdAsync(entity, token).ConfigureAwait(false); return success; } - async ValueTask IRedisTypedClientAsync.StoreAsync(T entity, TimeSpan expireIn, CancellationToken cancellationToken) + async ValueTask IRedisTypedClientAsync.StoreAsync(T entity, TimeSpan expireIn, CancellationToken token) { var urnKey = client.UrnKey(entity); - await AsAsync().SetValueAsync(urnKey, entity, cancellationToken).ConfigureAwait(false); + await AsAsync().SetValueAsync(urnKey, entity, token).ConfigureAwait(false); return entity; } - ValueTask IRedisTypedClientAsync.GetAndSetValueAsync(string key, T value, CancellationToken cancellationToken) - => DeserializeValueAsync(AsyncNative.GetSetAsync(key, SerializeValue(value), cancellationToken)); + ValueTask IRedisTypedClientAsync.GetAndSetValueAsync(string key, T value, CancellationToken token) + => DeserializeValueAsync(AsyncNative.GetSetAsync(key, SerializeValue(value), token)); - ValueTask IRedisTypedClientAsync.ContainsKeyAsync(string key, CancellationToken cancellationToken) - => AsyncNative.ExistsAsync(key, cancellationToken).IsSuccessAsync(); + ValueTask IRedisTypedClientAsync.ContainsKeyAsync(string key, CancellationToken token) + => AsyncNative.ExistsAsync(key, token).IsSuccessAsync(); - ValueTask IRedisTypedClientAsync.RemoveEntryAsync(string key, CancellationToken cancellationToken) - => AsyncNative.DelAsync(key, cancellationToken).IsSuccessAsync(); + ValueTask IRedisTypedClientAsync.RemoveEntryAsync(string key, CancellationToken token) + => AsyncNative.DelAsync(key, token).IsSuccessAsync(); - ValueTask IRedisTypedClientAsync.RemoveEntryAsync(string[] keys, CancellationToken cancellationToken) - => AsyncNative.DelAsync(keys, cancellationToken).IsSuccessAsync(); + ValueTask IRedisTypedClientAsync.RemoveEntryAsync(string[] keys, CancellationToken token) + => AsyncNative.DelAsync(keys, token).IsSuccessAsync(); - async ValueTask IRedisTypedClientAsync.RemoveEntryAsync(IHasStringId[] entities, CancellationToken cancellationToken) + async ValueTask IRedisTypedClientAsync.RemoveEntryAsync(IHasStringId[] entities, CancellationToken token) { var ids = entities.Map(x => x.Id); - var success = await AsyncNative.DelAsync(ids.ToArray(), cancellationToken).IsSuccessAsync().ConfigureAwait(false); - if (success) await client.RemoveTypeIdsAsync(ids.ToArray(), cancellationToken).ConfigureAwait(false); + var success = await AsyncNative.DelAsync(ids.ToArray(), token).IsSuccessAsync().ConfigureAwait(false); + if (success) await client.RemoveTypeIdsAsync(ids.ToArray(), token).ConfigureAwait(false); return success; } - ValueTask IRedisTypedClientAsync.IncrementValueAsync(string key, CancellationToken cancellationToken) - => AsyncNative.IncrAsync(key, cancellationToken); + ValueTask IRedisTypedClientAsync.IncrementValueAsync(string key, CancellationToken token) + => AsyncNative.IncrAsync(key, token); - ValueTask IRedisTypedClientAsync.IncrementValueByAsync(string key, int count, CancellationToken cancellationToken) - => AsyncNative.IncrByAsync(key, count, cancellationToken); + ValueTask IRedisTypedClientAsync.IncrementValueByAsync(string key, int count, CancellationToken token) + => AsyncNative.IncrByAsync(key, count, token); - ValueTask IRedisTypedClientAsync.DecrementValueAsync(string key, CancellationToken cancellationToken) - => AsyncNative.DecrAsync(key, cancellationToken); + ValueTask IRedisTypedClientAsync.DecrementValueAsync(string key, CancellationToken token) + => AsyncNative.DecrAsync(key, token); - ValueTask IRedisTypedClientAsync.DecrementValueByAsync(string key, int count, CancellationToken cancellationToken) - => AsyncNative.DecrByAsync(key, count, cancellationToken); + ValueTask IRedisTypedClientAsync.DecrementValueByAsync(string key, int count, CancellationToken token) + => AsyncNative.DecrByAsync(key, count, token); - ValueTask IRedisTypedClientAsync.ExpireInAsync(object id, TimeSpan expiresIn, CancellationToken cancellationToken) + ValueTask IRedisTypedClientAsync.ExpireInAsync(object id, TimeSpan expiresIn, CancellationToken token) { var key = client.UrnKey(id); - return AsyncClient.ExpireEntryInAsync(key, expiresIn, cancellationToken); + return AsyncClient.ExpireEntryInAsync(key, expiresIn, token); } - ValueTask IRedisTypedClientAsync.ExpireAtAsync(object id, DateTime expireAt, CancellationToken cancellationToken) + ValueTask IRedisTypedClientAsync.ExpireAtAsync(object id, DateTime expireAt, CancellationToken token) { var key = client.UrnKey(id); - return AsyncClient.ExpireEntryAtAsync(key, expireAt, cancellationToken); + return AsyncClient.ExpireEntryAtAsync(key, expireAt, token); } - ValueTask IRedisTypedClientAsync.ExpireEntryInAsync(string key, TimeSpan expireIn, CancellationToken cancellationToken) - => AsyncClient.ExpireEntryInAsync(key, expireIn, cancellationToken); + ValueTask IRedisTypedClientAsync.ExpireEntryInAsync(string key, TimeSpan expireIn, CancellationToken token) + => AsyncClient.ExpireEntryInAsync(key, expireIn, token); - ValueTask IRedisTypedClientAsync.ExpireEntryAtAsync(string key, DateTime expireAt, CancellationToken cancellationToken) - => AsyncClient.ExpireEntryAtAsync(key, expireAt, cancellationToken); + ValueTask IRedisTypedClientAsync.ExpireEntryAtAsync(string key, DateTime expireAt, CancellationToken token) + => AsyncClient.ExpireEntryAtAsync(key, expireAt, token); - async ValueTask IRedisTypedClientAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) - => TimeSpan.FromSeconds(await AsyncNative.TtlAsync(key, cancellationToken).ConfigureAwait(false)); + async ValueTask IRedisTypedClientAsync.GetTimeToLiveAsync(string key, CancellationToken token) + => TimeSpan.FromSeconds(await AsyncNative.TtlAsync(key, token).ConfigureAwait(false)); - ValueTask IRedisTypedClientAsync.ForegroundSaveAsync(CancellationToken cancellationToken) - => AsyncClient.ForegroundSaveAsync(cancellationToken); + ValueTask IRedisTypedClientAsync.ForegroundSaveAsync(CancellationToken token) + => AsyncClient.ForegroundSaveAsync(token); - ValueTask IRedisTypedClientAsync.BackgroundSaveAsync(CancellationToken cancellationToken) - => AsyncClient.BackgroundSaveAsync(cancellationToken); + ValueTask IRedisTypedClientAsync.BackgroundSaveAsync(CancellationToken token) + => AsyncClient.BackgroundSaveAsync(token); - ValueTask IRedisTypedClientAsync.FlushDbAsync(CancellationToken cancellationToken) - => AsyncClient.FlushDbAsync(cancellationToken); + ValueTask IRedisTypedClientAsync.FlushDbAsync(CancellationToken token) + => AsyncClient.FlushDbAsync(token); - ValueTask IRedisTypedClientAsync.FlushAllAsync(CancellationToken cancellationToken) - => new ValueTask(AsyncClient.FlushAllAsync(cancellationToken)); + ValueTask IRedisTypedClientAsync.FlushAllAsync(CancellationToken token) + => new ValueTask(AsyncClient.FlushAllAsync(token)); - async ValueTask IRedisTypedClientAsync.SearchKeysAsync(string pattern, CancellationToken cancellationToken) + async ValueTask IRedisTypedClientAsync.SearchKeysAsync(string pattern, CancellationToken token) { - var strKeys = await AsyncClient.SearchKeysAsync(pattern, cancellationToken).ConfigureAwait(false); + var strKeys = await AsyncClient.SearchKeysAsync(pattern, token).ConfigureAwait(false); return SearchKeysParse(strKeys); } @@ -330,426 +330,426 @@ static async ValueTask> Awaited(ValueTask ConvertEachTo(await pending.ConfigureAwait(false)); } - ValueTask> IRedisTypedClientAsync.GetSortedEntryValuesAsync(IRedisSetAsync fromSet, int startingFrom, int endingAt, CancellationToken cancellationToken) + ValueTask> IRedisTypedClientAsync.GetSortedEntryValuesAsync(IRedisSetAsync fromSet, int startingFrom, int endingAt, CancellationToken token) { var sortOptions = new SortOptions { Skip = startingFrom, Take = endingAt, }; - var multiDataList = AsyncNative.SortAsync(fromSet.Id, sortOptions, cancellationToken); + var multiDataList = AsyncNative.SortAsync(fromSet.Id, sortOptions, token); return CreateList(multiDataList); } - ValueTask IRedisTypedClientAsync.StoreAsHashAsync(T entity, CancellationToken cancellationToken) - => AsyncClient.StoreAsHashAsync(entity, cancellationToken); + ValueTask IRedisTypedClientAsync.StoreAsHashAsync(T entity, CancellationToken token) + => AsyncClient.StoreAsHashAsync(entity, token); - ValueTask IRedisTypedClientAsync.GetFromHashAsync(object id, CancellationToken cancellationToken) - => AsyncClient.GetFromHashAsync(id, cancellationToken); + ValueTask IRedisTypedClientAsync.GetFromHashAsync(object id, CancellationToken token) + => AsyncClient.GetFromHashAsync(id, token); - async ValueTask> IRedisTypedClientAsync.GetAllItemsFromSetAsync(IRedisSetAsync fromSet, CancellationToken cancellationToken) + async ValueTask> IRedisTypedClientAsync.GetAllItemsFromSetAsync(IRedisSetAsync fromSet, CancellationToken token) { - var multiDataList = await AsyncNative.SMembersAsync(fromSet.Id, cancellationToken).ConfigureAwait(false); + var multiDataList = await AsyncNative.SMembersAsync(fromSet.Id, token).ConfigureAwait(false); return CreateHashSet(multiDataList); } - ValueTask IRedisTypedClientAsync.AddItemToSetAsync(IRedisSetAsync toSet, T item, CancellationToken cancellationToken) - => AsyncNative.SAddAsync(toSet.Id, SerializeValue(item), cancellationToken).Await(); + ValueTask IRedisTypedClientAsync.AddItemToSetAsync(IRedisSetAsync toSet, T item, CancellationToken token) + => AsyncNative.SAddAsync(toSet.Id, SerializeValue(item), token).Await(); - ValueTask IRedisTypedClientAsync.RemoveItemFromSetAsync(IRedisSetAsync fromSet, T item, CancellationToken cancellationToken) - => AsyncNative.SRemAsync(fromSet.Id, SerializeValue(item), cancellationToken).Await(); + ValueTask IRedisTypedClientAsync.RemoveItemFromSetAsync(IRedisSetAsync fromSet, T item, CancellationToken token) + => AsyncNative.SRemAsync(fromSet.Id, SerializeValue(item), token).Await(); - ValueTask IRedisTypedClientAsync.PopItemFromSetAsync(IRedisSetAsync fromSet, CancellationToken cancellationToken) - => DeserializeValueAsync(AsyncNative.SPopAsync(fromSet.Id, cancellationToken)); + ValueTask IRedisTypedClientAsync.PopItemFromSetAsync(IRedisSetAsync fromSet, CancellationToken token) + => DeserializeValueAsync(AsyncNative.SPopAsync(fromSet.Id, token)); - ValueTask IRedisTypedClientAsync.MoveBetweenSetsAsync(IRedisSetAsync fromSet, IRedisSetAsync toSet, T item, CancellationToken cancellationToken) - => AsyncNative.SMoveAsync(fromSet.Id, toSet.Id, SerializeValue(item), cancellationToken); + ValueTask IRedisTypedClientAsync.MoveBetweenSetsAsync(IRedisSetAsync fromSet, IRedisSetAsync toSet, T item, CancellationToken token) + => AsyncNative.SMoveAsync(fromSet.Id, toSet.Id, SerializeValue(item), token); - ValueTask IRedisTypedClientAsync.GetSetCountAsync(IRedisSetAsync set, CancellationToken cancellationToken) - => AsyncNative.SCardAsync(set.Id, cancellationToken); + ValueTask IRedisTypedClientAsync.GetSetCountAsync(IRedisSetAsync set, CancellationToken token) + => AsyncNative.SCardAsync(set.Id, token); - ValueTask IRedisTypedClientAsync.SetContainsItemAsync(IRedisSetAsync set, T item, CancellationToken cancellationToken) + ValueTask IRedisTypedClientAsync.SetContainsItemAsync(IRedisSetAsync set, T item, CancellationToken token) => AsyncNative.SIsMemberAsync(set.Id, SerializeValue(item)).IsSuccessAsync(); - async ValueTask> IRedisTypedClientAsync.GetIntersectFromSetsAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken) + async ValueTask> IRedisTypedClientAsync.GetIntersectFromSetsAsync(IRedisSetAsync[] sets, CancellationToken token) { - var multiDataList = await AsyncNative.SInterAsync(sets.Map(x => x.Id).ToArray(), cancellationToken).ConfigureAwait(false); + var multiDataList = await AsyncNative.SInterAsync(sets.Map(x => x.Id).ToArray(), token).ConfigureAwait(false); return CreateHashSet(multiDataList); } - ValueTask IRedisTypedClientAsync.StoreIntersectFromSetsAsync(IRedisSetAsync intoSet, IRedisSetAsync[] sets, CancellationToken cancellationToken) - => AsyncNative.SInterStoreAsync(intoSet.Id, sets.Map(x => x.Id).ToArray(), cancellationToken); + ValueTask IRedisTypedClientAsync.StoreIntersectFromSetsAsync(IRedisSetAsync intoSet, IRedisSetAsync[] sets, CancellationToken token) + => AsyncNative.SInterStoreAsync(intoSet.Id, sets.Map(x => x.Id).ToArray(), token); - async ValueTask> IRedisTypedClientAsync.GetUnionFromSetsAsync(IRedisSetAsync[] sets, CancellationToken cancellationToken) + async ValueTask> IRedisTypedClientAsync.GetUnionFromSetsAsync(IRedisSetAsync[] sets, CancellationToken token) { - var multiDataList = await AsyncNative.SUnionAsync(sets.Map(x => x.Id).ToArray(), cancellationToken).ConfigureAwait(false); + var multiDataList = await AsyncNative.SUnionAsync(sets.Map(x => x.Id).ToArray(), token).ConfigureAwait(false); return CreateHashSet(multiDataList); } - ValueTask IRedisTypedClientAsync.StoreUnionFromSetsAsync(IRedisSetAsync intoSet, IRedisSetAsync[] sets, CancellationToken cancellationToken) - => AsyncNative.SUnionStoreAsync(intoSet.Id, sets.Map(x => x.Id).ToArray(), cancellationToken); + ValueTask IRedisTypedClientAsync.StoreUnionFromSetsAsync(IRedisSetAsync intoSet, IRedisSetAsync[] sets, CancellationToken token) + => AsyncNative.SUnionStoreAsync(intoSet.Id, sets.Map(x => x.Id).ToArray(), token); - async ValueTask> IRedisTypedClientAsync.GetDifferencesFromSetAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken) + async ValueTask> IRedisTypedClientAsync.GetDifferencesFromSetAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken token) { - var multiDataList = await AsyncNative.SDiffAsync(fromSet.Id, withSets.Map(x => x.Id).ToArray(), cancellationToken).ConfigureAwait(false); + var multiDataList = await AsyncNative.SDiffAsync(fromSet.Id, withSets.Map(x => x.Id).ToArray(), token).ConfigureAwait(false); return CreateHashSet(multiDataList); } - ValueTask IRedisTypedClientAsync.StoreDifferencesFromSetAsync(IRedisSetAsync intoSet, IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken) - => AsyncNative.SDiffStoreAsync(intoSet.Id, fromSet.Id, withSets.Map(x => x.Id).ToArray(), cancellationToken); + ValueTask IRedisTypedClientAsync.StoreDifferencesFromSetAsync(IRedisSetAsync intoSet, IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken token) + => AsyncNative.SDiffStoreAsync(intoSet.Id, fromSet.Id, withSets.Map(x => x.Id).ToArray(), token); - ValueTask IRedisTypedClientAsync.GetRandomItemFromSetAsync(IRedisSetAsync fromSet, CancellationToken cancellationToken) - => DeserializeValueAsync(AsyncNative.SRandMemberAsync(fromSet.Id, cancellationToken)); + ValueTask IRedisTypedClientAsync.GetRandomItemFromSetAsync(IRedisSetAsync fromSet, CancellationToken token) + => DeserializeValueAsync(AsyncNative.SRandMemberAsync(fromSet.Id, token)); - ValueTask> IRedisTypedClientAsync.GetAllItemsFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken) + ValueTask> IRedisTypedClientAsync.GetAllItemsFromListAsync(IRedisListAsync fromList, CancellationToken token) { - var multiDataList = AsyncNative.LRangeAsync(fromList.Id, FirstElement, LastElement, cancellationToken); + var multiDataList = AsyncNative.LRangeAsync(fromList.Id, FirstElement, LastElement, token); return CreateList(multiDataList); } - ValueTask> IRedisTypedClientAsync.GetRangeFromListAsync(IRedisListAsync fromList, int startingFrom, int endingAt, CancellationToken cancellationToken) + ValueTask> IRedisTypedClientAsync.GetRangeFromListAsync(IRedisListAsync fromList, int startingFrom, int endingAt, CancellationToken token) { - var multiDataList = AsyncNative.LRangeAsync(fromList.Id, startingFrom, endingAt, cancellationToken); + var multiDataList = AsyncNative.LRangeAsync(fromList.Id, startingFrom, endingAt, token); return CreateList(multiDataList); } - ValueTask> IRedisTypedClientAsync.SortListAsync(IRedisListAsync fromList, int startingFrom, int endingAt, CancellationToken cancellationToken) + ValueTask> IRedisTypedClientAsync.SortListAsync(IRedisListAsync fromList, int startingFrom, int endingAt, CancellationToken token) { var sortOptions = new SortOptions { Skip = startingFrom, Take = endingAt, }; - var multiDataList = AsyncNative.SortAsync(fromList.Id, sortOptions, cancellationToken); + var multiDataList = AsyncNative.SortAsync(fromList.Id, sortOptions, token); return CreateList(multiDataList); } - ValueTask IRedisTypedClientAsync.AddItemToListAsync(IRedisListAsync fromList, T value, CancellationToken cancellationToken) - => AsyncNative.RPushAsync(fromList.Id, SerializeValue(value), cancellationToken).Await(); + ValueTask IRedisTypedClientAsync.AddItemToListAsync(IRedisListAsync fromList, T value, CancellationToken token) + => AsyncNative.RPushAsync(fromList.Id, SerializeValue(value), token).Await(); - ValueTask IRedisTypedClientAsync.PrependItemToListAsync(IRedisListAsync fromList, T value, CancellationToken cancellationToken) - => AsyncNative.LPushAsync(fromList.Id, SerializeValue(value), cancellationToken).Await(); + ValueTask IRedisTypedClientAsync.PrependItemToListAsync(IRedisListAsync fromList, T value, CancellationToken token) + => AsyncNative.LPushAsync(fromList.Id, SerializeValue(value), token).Await(); - ValueTask IRedisTypedClientAsync.RemoveStartFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken) - => DeserializeValueAsync(AsyncNative.LPopAsync(fromList.Id, cancellationToken)); + ValueTask IRedisTypedClientAsync.RemoveStartFromListAsync(IRedisListAsync fromList, CancellationToken token) + => DeserializeValueAsync(AsyncNative.LPopAsync(fromList.Id, token)); - async ValueTask IRedisTypedClientAsync.BlockingRemoveStartFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken cancellationToken) + async ValueTask IRedisTypedClientAsync.BlockingRemoveStartFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken token) { - var unblockingKeyAndValue = await AsyncNative.BLPopAsync(fromList.Id, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).ConfigureAwait(false); + var unblockingKeyAndValue = await AsyncNative.BLPopAsync(fromList.Id, (int)timeOut.GetValueOrDefault().TotalSeconds, token).ConfigureAwait(false); return unblockingKeyAndValue.Length == 0 ? default : DeserializeValue(unblockingKeyAndValue[1]); } - ValueTask IRedisTypedClientAsync.RemoveEndFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken) - => DeserializeValueAsync(AsyncNative.RPopAsync(fromList.Id, cancellationToken)); + ValueTask IRedisTypedClientAsync.RemoveEndFromListAsync(IRedisListAsync fromList, CancellationToken token) + => DeserializeValueAsync(AsyncNative.RPopAsync(fromList.Id, token)); - ValueTask IRedisTypedClientAsync.RemoveAllFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken) - => AsyncNative.LTrimAsync(fromList.Id, int.MaxValue, FirstElement, cancellationToken); + ValueTask IRedisTypedClientAsync.RemoveAllFromListAsync(IRedisListAsync fromList, CancellationToken token) + => AsyncNative.LTrimAsync(fromList.Id, int.MaxValue, FirstElement, token); - ValueTask IRedisTypedClientAsync.TrimListAsync(IRedisListAsync fromList, int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken) - => AsyncNative.LTrimAsync(fromList.Id, keepStartingFrom, keepEndingAt, cancellationToken); + ValueTask IRedisTypedClientAsync.TrimListAsync(IRedisListAsync fromList, int keepStartingFrom, int keepEndingAt, CancellationToken token) + => AsyncNative.LTrimAsync(fromList.Id, keepStartingFrom, keepEndingAt, token); - ValueTask IRedisTypedClientAsync.RemoveItemFromListAsync(IRedisListAsync fromList, T value, CancellationToken cancellationToken) + ValueTask IRedisTypedClientAsync.RemoveItemFromListAsync(IRedisListAsync fromList, T value, CancellationToken token) { const int removeAll = 0; - return AsyncNative.LRemAsync(fromList.Id, removeAll, SerializeValue(value), cancellationToken); + return AsyncNative.LRemAsync(fromList.Id, removeAll, SerializeValue(value), token); } - ValueTask IRedisTypedClientAsync.RemoveItemFromListAsync(IRedisListAsync fromList, T value, int noOfMatches, CancellationToken cancellationToken) - => AsyncNative.LRemAsync(fromList.Id, noOfMatches, SerializeValue(value), cancellationToken); + ValueTask IRedisTypedClientAsync.RemoveItemFromListAsync(IRedisListAsync fromList, T value, int noOfMatches, CancellationToken token) + => AsyncNative.LRemAsync(fromList.Id, noOfMatches, SerializeValue(value), token); - ValueTask IRedisTypedClientAsync.GetListCountAsync(IRedisListAsync fromList, CancellationToken cancellationToken) - => AsyncNative.LLenAsync(fromList.Id, cancellationToken); + ValueTask IRedisTypedClientAsync.GetListCountAsync(IRedisListAsync fromList, CancellationToken token) + => AsyncNative.LLenAsync(fromList.Id, token); - ValueTask IRedisTypedClientAsync.GetItemFromListAsync(IRedisListAsync fromList, int listIndex, CancellationToken cancellationToken) - => DeserializeValueAsync(AsyncNative.LIndexAsync(fromList.Id, listIndex, cancellationToken)); + ValueTask IRedisTypedClientAsync.GetItemFromListAsync(IRedisListAsync fromList, int listIndex, CancellationToken token) + => DeserializeValueAsync(AsyncNative.LIndexAsync(fromList.Id, listIndex, token)); - ValueTask IRedisTypedClientAsync.SetItemInListAsync(IRedisListAsync toList, int listIndex, T value, CancellationToken cancellationToken) - => AsyncNative.LSetAsync(toList.Id, listIndex, SerializeValue(value), cancellationToken); + ValueTask IRedisTypedClientAsync.SetItemInListAsync(IRedisListAsync toList, int listIndex, T value, CancellationToken token) + => AsyncNative.LSetAsync(toList.Id, listIndex, SerializeValue(value), token); - ValueTask IRedisTypedClientAsync.InsertBeforeItemInListAsync(IRedisListAsync toList, T pivot, T value, CancellationToken cancellationToken) - => AsyncNative.LInsertAsync(toList.Id, insertBefore: true, pivot: SerializeValue(pivot), value: SerializeValue(value), cancellationToken: cancellationToken); + ValueTask IRedisTypedClientAsync.InsertBeforeItemInListAsync(IRedisListAsync toList, T pivot, T value, CancellationToken token) + => AsyncNative.LInsertAsync(toList.Id, insertBefore: true, pivot: SerializeValue(pivot), value: SerializeValue(value), token: token); - ValueTask IRedisTypedClientAsync.InsertAfterItemInListAsync(IRedisListAsync toList, T pivot, T value, CancellationToken cancellationToken) - => AsyncNative.LInsertAsync(toList.Id, insertBefore: false, pivot: SerializeValue(pivot), value: SerializeValue(value), cancellationToken: cancellationToken); + ValueTask IRedisTypedClientAsync.InsertAfterItemInListAsync(IRedisListAsync toList, T pivot, T value, CancellationToken token) + => AsyncNative.LInsertAsync(toList.Id, insertBefore: false, pivot: SerializeValue(pivot), value: SerializeValue(value), token: token); - ValueTask IRedisTypedClientAsync.EnqueueItemOnListAsync(IRedisListAsync fromList, T item, CancellationToken cancellationToken) - => AsyncNative.LPushAsync(fromList.Id, SerializeValue(item), cancellationToken).Await(); + ValueTask IRedisTypedClientAsync.EnqueueItemOnListAsync(IRedisListAsync fromList, T item, CancellationToken token) + => AsyncNative.LPushAsync(fromList.Id, SerializeValue(item), token).Await(); - ValueTask IRedisTypedClientAsync.DequeueItemFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken) - => DeserializeValueAsync(AsyncNative.RPopAsync(fromList.Id, cancellationToken)); + ValueTask IRedisTypedClientAsync.DequeueItemFromListAsync(IRedisListAsync fromList, CancellationToken token) + => DeserializeValueAsync(AsyncNative.RPopAsync(fromList.Id, token)); - async ValueTask IRedisTypedClientAsync.BlockingDequeueItemFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken cancellationToken) + async ValueTask IRedisTypedClientAsync.BlockingDequeueItemFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken token) { - var unblockingKeyAndValue = await AsyncNative.BRPopAsync(fromList.Id, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).ConfigureAwait(false); + var unblockingKeyAndValue = await AsyncNative.BRPopAsync(fromList.Id, (int)timeOut.GetValueOrDefault().TotalSeconds, token).ConfigureAwait(false); return unblockingKeyAndValue.Length == 0 ? default : DeserializeValue(unblockingKeyAndValue[1]); } - ValueTask IRedisTypedClientAsync.PushItemToListAsync(IRedisListAsync fromList, T item, CancellationToken cancellationToken) - => AsyncNative.RPushAsync(fromList.Id, SerializeValue(item), cancellationToken).Await(); + ValueTask IRedisTypedClientAsync.PushItemToListAsync(IRedisListAsync fromList, T item, CancellationToken token) + => AsyncNative.RPushAsync(fromList.Id, SerializeValue(item), token).Await(); - ValueTask IRedisTypedClientAsync.PopItemFromListAsync(IRedisListAsync fromList, CancellationToken cancellationToken) - => DeserializeValueAsync(AsyncNative.RPopAsync(fromList.Id, cancellationToken)); + ValueTask IRedisTypedClientAsync.PopItemFromListAsync(IRedisListAsync fromList, CancellationToken token) + => DeserializeValueAsync(AsyncNative.RPopAsync(fromList.Id, token)); - async ValueTask IRedisTypedClientAsync.BlockingPopItemFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken cancellationToken) + async ValueTask IRedisTypedClientAsync.BlockingPopItemFromListAsync(IRedisListAsync fromList, TimeSpan? timeOut, CancellationToken token) { - var unblockingKeyAndValue = await AsyncNative.BRPopAsync(fromList.Id, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).ConfigureAwait(false); + var unblockingKeyAndValue = await AsyncNative.BRPopAsync(fromList.Id, (int)timeOut.GetValueOrDefault().TotalSeconds, token).ConfigureAwait(false); return unblockingKeyAndValue.Length == 0 ? default : DeserializeValue(unblockingKeyAndValue[1]); } - ValueTask IRedisTypedClientAsync.PopAndPushItemBetweenListsAsync(IRedisListAsync fromList, IRedisListAsync toList, CancellationToken cancellationToken) - => DeserializeValueAsync(AsyncNative.RPopLPushAsync(fromList.Id, toList.Id, cancellationToken)); + ValueTask IRedisTypedClientAsync.PopAndPushItemBetweenListsAsync(IRedisListAsync fromList, IRedisListAsync toList, CancellationToken token) + => DeserializeValueAsync(AsyncNative.RPopLPushAsync(fromList.Id, toList.Id, token)); - ValueTask IRedisTypedClientAsync.BlockingPopAndPushItemBetweenListsAsync(IRedisListAsync fromList, IRedisListAsync toList, TimeSpan? timeOut, CancellationToken cancellationToken) - => DeserializeValueAsync(AsyncNative.BRPopLPushAsync(fromList.Id, toList.Id, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken)); + ValueTask IRedisTypedClientAsync.BlockingPopAndPushItemBetweenListsAsync(IRedisListAsync fromList, IRedisListAsync toList, TimeSpan? timeOut, CancellationToken token) + => DeserializeValueAsync(AsyncNative.BRPopLPushAsync(fromList.Id, toList.Id, (int)timeOut.GetValueOrDefault().TotalSeconds, token)); - ValueTask IRedisTypedClientAsync.AddItemToSortedSetAsync(IRedisSortedSetAsync toSet, T value, CancellationToken cancellationToken) - => AsyncClient.AddItemToSortedSetAsync(toSet.Id, value.SerializeToString(), cancellationToken).Await(); + ValueTask IRedisTypedClientAsync.AddItemToSortedSetAsync(IRedisSortedSetAsync toSet, T value, CancellationToken token) + => AsyncClient.AddItemToSortedSetAsync(toSet.Id, value.SerializeToString(), token).Await(); - ValueTask IRedisTypedClientAsync.AddItemToSortedSetAsync(IRedisSortedSetAsync toSet, T value, double score, CancellationToken cancellationToken) - => AsyncClient.AddItemToSortedSetAsync(toSet.Id, value.SerializeToString(), score, cancellationToken).Await(); + ValueTask IRedisTypedClientAsync.AddItemToSortedSetAsync(IRedisSortedSetAsync toSet, T value, double score, CancellationToken token) + => AsyncClient.AddItemToSortedSetAsync(toSet.Id, value.SerializeToString(), score, token).Await(); - ValueTask IRedisTypedClientAsync.RemoveItemFromSortedSetAsync(IRedisSortedSetAsync fromSet, T value, CancellationToken cancellationToken) - => AsyncClient.RemoveItemFromSortedSetAsync(fromSet.Id, value.SerializeToString(), cancellationToken); + ValueTask IRedisTypedClientAsync.RemoveItemFromSortedSetAsync(IRedisSortedSetAsync fromSet, T value, CancellationToken token) + => AsyncClient.RemoveItemFromSortedSetAsync(fromSet.Id, value.SerializeToString(), token); - ValueTask IRedisTypedClientAsync.PopItemWithLowestScoreFromSortedSetAsync(IRedisSortedSetAsync fromSet, CancellationToken cancellationToken) - => DeserializeFromStringAsync(AsyncClient.PopItemWithLowestScoreFromSortedSetAsync(fromSet.Id, cancellationToken)); + ValueTask IRedisTypedClientAsync.PopItemWithLowestScoreFromSortedSetAsync(IRedisSortedSetAsync fromSet, CancellationToken token) + => DeserializeFromStringAsync(AsyncClient.PopItemWithLowestScoreFromSortedSetAsync(fromSet.Id, token)); - ValueTask IRedisTypedClientAsync.PopItemWithHighestScoreFromSortedSetAsync(IRedisSortedSetAsync fromSet, CancellationToken cancellationToken) - => DeserializeFromStringAsync(AsyncClient.PopItemWithHighestScoreFromSortedSetAsync(fromSet.Id, cancellationToken)); + ValueTask IRedisTypedClientAsync.PopItemWithHighestScoreFromSortedSetAsync(IRedisSortedSetAsync fromSet, CancellationToken token) + => DeserializeFromStringAsync(AsyncClient.PopItemWithHighestScoreFromSortedSetAsync(fromSet.Id, token)); - ValueTask IRedisTypedClientAsync.SortedSetContainsItemAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken) - => AsyncClient.SortedSetContainsItemAsync(set.Id, value.SerializeToString(), cancellationToken); + ValueTask IRedisTypedClientAsync.SortedSetContainsItemAsync(IRedisSortedSetAsync set, T value, CancellationToken token) + => AsyncClient.SortedSetContainsItemAsync(set.Id, value.SerializeToString(), token); - ValueTask IRedisTypedClientAsync.IncrementItemInSortedSetAsync(IRedisSortedSetAsync set, T value, double incrementBy, CancellationToken cancellationToken) - => AsyncClient.IncrementItemInSortedSetAsync(set.Id, value.SerializeToString(), incrementBy, cancellationToken); + ValueTask IRedisTypedClientAsync.IncrementItemInSortedSetAsync(IRedisSortedSetAsync set, T value, double incrementBy, CancellationToken token) + => AsyncClient.IncrementItemInSortedSetAsync(set.Id, value.SerializeToString(), incrementBy, token); - ValueTask IRedisTypedClientAsync.GetItemIndexInSortedSetAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken) - => AsyncClient.GetItemIndexInSortedSetAsync(set.Id, value.SerializeToString(), cancellationToken); + ValueTask IRedisTypedClientAsync.GetItemIndexInSortedSetAsync(IRedisSortedSetAsync set, T value, CancellationToken token) + => AsyncClient.GetItemIndexInSortedSetAsync(set.Id, value.SerializeToString(), token); - ValueTask IRedisTypedClientAsync.GetItemIndexInSortedSetDescAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken) - => AsyncClient.GetItemIndexInSortedSetDescAsync(set.Id, value.SerializeToString(), cancellationToken); + ValueTask IRedisTypedClientAsync.GetItemIndexInSortedSetDescAsync(IRedisSortedSetAsync set, T value, CancellationToken token) + => AsyncClient.GetItemIndexInSortedSetDescAsync(set.Id, value.SerializeToString(), token); - ValueTask> IRedisTypedClientAsync.GetAllItemsFromSortedSetAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken) - => AsyncClient.GetAllItemsFromSortedSetAsync(set.Id, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetAllItemsFromSortedSetAsync(IRedisSortedSetAsync set, CancellationToken token) + => AsyncClient.GetAllItemsFromSortedSetAsync(set.Id, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetAllItemsFromSortedSetDescAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken) - => AsyncClient.GetAllItemsFromSortedSetDescAsync(set.Id, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetAllItemsFromSortedSetDescAsync(IRedisSortedSetAsync set, CancellationToken token) + => AsyncClient.GetAllItemsFromSortedSetDescAsync(set.Id, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetAsync(set.Id, fromRank, toRank, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetAsync(set.Id, fromRank, toRank, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetDescAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetDescAsync(set.Id, fromRank, toRank, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetDescAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetDescAsync(set.Id, fromRank, toRank, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetAllWithScoresFromSortedSetAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken) - => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetAsync(set.Id, FirstElement, LastElement, cancellationToken)); + ValueTask> IRedisTypedClientAsync.GetAllWithScoresFromSortedSetAsync(IRedisSortedSetAsync set, CancellationToken token) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetAsync(set.Id, FirstElement, LastElement, token)); - ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken) - => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetAsync(set.Id, fromRank, toRank, cancellationToken)); + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken token) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetAsync(set.Id, fromRank, toRank, token)); - ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetDescAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken cancellationToken) - => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetDescAsync(set.Id, fromRank, toRank, cancellationToken)); + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetDescAsync(IRedisSortedSetAsync set, int fromRank, int toRank, CancellationToken token) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetDescAsync(set.Id, fromRank, toRank, token)); - ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(set.Id, fromStringScore, toStringScore, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(set.Id, fromStringScore, toStringScore, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(set.Id, fromStringScore, toStringScore, skip, take, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(set.Id, fromStringScore, toStringScore, skip, take, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(set.Id, fromScore, toScore, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(set.Id, fromScore, toScore, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(set.Id, fromScore, toScore, skip, take, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(set.Id, fromScore, toScore, skip, take, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken) - => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByLowestScoreAsync(set.Id, fromStringScore, toStringScore, cancellationToken)); + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken token) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByLowestScoreAsync(set.Id, fromStringScore, toStringScore, token)); - ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) - => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByLowestScoreAsync(set.Id, fromStringScore, toStringScore, skip, take, cancellationToken)); + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken token) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByLowestScoreAsync(set.Id, fromStringScore, toStringScore, skip, take, token)); - ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken) - => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByLowestScoreAsync(set.Id, fromScore, toScore, cancellationToken)); + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken token) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByLowestScoreAsync(set.Id, fromScore, toScore, token)); - ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) - => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByLowestScoreAsync(set.Id, fromScore, toScore, skip, take, cancellationToken)); - ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(set.Id, fromStringScore, toStringScore, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken token) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByLowestScoreAsync(set.Id, fromScore, toScore, skip, take, token)); + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(set.Id, fromStringScore, toStringScore, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(set.Id, fromStringScore, toStringScore, skip, take, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(set.Id, fromStringScore, toStringScore, skip, take, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(set.Id, fromScore, toScore, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(set.Id, fromScore, toScore, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(set.Id, fromScore, toScore, skip, take, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetRangeFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByHighestScoreAsync(set.Id, fromScore, toScore, skip, take, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken cancellationToken) - => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByHighestScoreAsync(set.Id, fromStringScore, toStringScore, cancellationToken)); + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, CancellationToken token) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByHighestScoreAsync(set.Id, fromStringScore, toStringScore, token)); - ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) - => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByHighestScoreAsync(set.Id, fromStringScore, toStringScore, skip, take, cancellationToken)); + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken token) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByHighestScoreAsync(set.Id, fromStringScore, toStringScore, skip, take, token)); - ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken) - => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByHighestScoreAsync(set.Id, fromScore, toScore, cancellationToken)); + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken token) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByHighestScoreAsync(set.Id, fromScore, toScore, token)); - ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) - => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByHighestScoreAsync(set.Id, fromScore, toScore, skip, take, cancellationToken)); + ValueTask> IRedisTypedClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, int? skip, int? take, CancellationToken token) + => CreateGenericMapAsync(AsyncClient.GetRangeWithScoresFromSortedSetByHighestScoreAsync(set.Id, fromScore, toScore, skip, take, token)); - ValueTask IRedisTypedClientAsync.RemoveRangeFromSortedSetAsync(IRedisSortedSetAsync set, int minRank, int maxRank, CancellationToken cancellationToken) - => AsyncClient.RemoveRangeFromSortedSetAsync(set.Id, minRank, maxRank, cancellationToken); + ValueTask IRedisTypedClientAsync.RemoveRangeFromSortedSetAsync(IRedisSortedSetAsync set, int minRank, int maxRank, CancellationToken token) + => AsyncClient.RemoveRangeFromSortedSetAsync(set.Id, minRank, maxRank, token); - ValueTask IRedisTypedClientAsync.RemoveRangeFromSortedSetByScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken cancellationToken) - => AsyncClient.RemoveRangeFromSortedSetByScoreAsync(set.Id, fromScore, toScore, cancellationToken); + ValueTask IRedisTypedClientAsync.RemoveRangeFromSortedSetByScoreAsync(IRedisSortedSetAsync set, double fromScore, double toScore, CancellationToken token) + => AsyncClient.RemoveRangeFromSortedSetByScoreAsync(set.Id, fromScore, toScore, token); - ValueTask IRedisTypedClientAsync.GetSortedSetCountAsync(IRedisSortedSetAsync set, CancellationToken cancellationToken) - => AsyncClient.GetSortedSetCountAsync(set.Id, cancellationToken); + ValueTask IRedisTypedClientAsync.GetSortedSetCountAsync(IRedisSortedSetAsync set, CancellationToken token) + => AsyncClient.GetSortedSetCountAsync(set.Id, token); - ValueTask IRedisTypedClientAsync.GetItemScoreInSortedSetAsync(IRedisSortedSetAsync set, T value, CancellationToken cancellationToken) - => AsyncClient.GetItemScoreInSortedSetAsync(set.Id, value.SerializeToString(), cancellationToken); + ValueTask IRedisTypedClientAsync.GetItemScoreInSortedSetAsync(IRedisSortedSetAsync set, T value, CancellationToken token) + => AsyncClient.GetItemScoreInSortedSetAsync(set.Id, value.SerializeToString(), token); - ValueTask IRedisTypedClientAsync.StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken) - => AsyncClient.StoreIntersectFromSortedSetsAsync(intoSetId.Id, setIds.Map(x => x.Id).ToArray(), cancellationToken); + ValueTask IRedisTypedClientAsync.StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, CancellationToken token) + => AsyncClient.StoreIntersectFromSortedSetsAsync(intoSetId.Id, setIds.Map(x => x.Id).ToArray(), token); - ValueTask IRedisTypedClientAsync.StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken) - => AsyncClient.StoreIntersectFromSortedSetsAsync(intoSetId.Id, setIds.Map(x => x.Id).ToArray(), args, cancellationToken); + ValueTask IRedisTypedClientAsync.StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, string[] args, CancellationToken token) + => AsyncClient.StoreIntersectFromSortedSetsAsync(intoSetId.Id, setIds.Map(x => x.Id).ToArray(), args, token); - ValueTask IRedisTypedClientAsync.StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, CancellationToken cancellationToken) - => AsyncClient.StoreUnionFromSortedSetsAsync(intoSetId.Id, setIds.Map(x => x.Id).ToArray(), cancellationToken); + ValueTask IRedisTypedClientAsync.StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, CancellationToken token) + => AsyncClient.StoreUnionFromSortedSetsAsync(intoSetId.Id, setIds.Map(x => x.Id).ToArray(), token); - ValueTask IRedisTypedClientAsync.StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, string[] args, CancellationToken cancellationToken) - => AsyncClient.StoreUnionFromSortedSetsAsync(intoSetId.Id, setIds.Map(x => x.Id).ToArray(), args, cancellationToken); + ValueTask IRedisTypedClientAsync.StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, IRedisSortedSetAsync[] setIds, string[] args, CancellationToken token) + => AsyncClient.StoreUnionFromSortedSetsAsync(intoSetId.Id, setIds.Map(x => x.Id).ToArray(), args, token); - ValueTask IRedisTypedClientAsync.HashContainsEntryAsync(IRedisHashAsync hash, TKey key, CancellationToken cancellationToken) - => AsyncClient.HashContainsEntryAsync(hash.Id, key.SerializeToString(), cancellationToken); + ValueTask IRedisTypedClientAsync.HashContainsEntryAsync(IRedisHashAsync hash, TKey key, CancellationToken token) + => AsyncClient.HashContainsEntryAsync(hash.Id, key.SerializeToString(), token); - ValueTask IRedisTypedClientAsync.SetEntryInHashAsync(IRedisHashAsync hash, TKey key, T value, CancellationToken cancellationToken) - => AsyncClient.SetEntryInHashAsync(hash.Id, key.SerializeToString(), value.SerializeToString(), cancellationToken); + ValueTask IRedisTypedClientAsync.SetEntryInHashAsync(IRedisHashAsync hash, TKey key, T value, CancellationToken token) + => AsyncClient.SetEntryInHashAsync(hash.Id, key.SerializeToString(), value.SerializeToString(), token); - ValueTask IRedisTypedClientAsync.SetEntryInHashIfNotExistsAsync(IRedisHashAsync hash, TKey key, T value, CancellationToken cancellationToken) - => AsyncClient.SetEntryInHashIfNotExistsAsync(hash.Id, key.SerializeToString(), value.SerializeToString(), cancellationToken); + ValueTask IRedisTypedClientAsync.SetEntryInHashIfNotExistsAsync(IRedisHashAsync hash, TKey key, T value, CancellationToken token) + => AsyncClient.SetEntryInHashIfNotExistsAsync(hash.Id, key.SerializeToString(), value.SerializeToString(), token); - ValueTask IRedisTypedClientAsync.SetRangeInHashAsync(IRedisHashAsync hash, IEnumerable> keyValuePairs, CancellationToken cancellationToken) + ValueTask IRedisTypedClientAsync.SetRangeInHashAsync(IRedisHashAsync hash, IEnumerable> keyValuePairs, CancellationToken token) { var stringKeyValuePairs = keyValuePairs.ToList().ConvertAll( x => new KeyValuePair(x.Key.SerializeToString(), x.Value.SerializeToString())); - return AsyncClient.SetRangeInHashAsync(hash.Id, stringKeyValuePairs, cancellationToken); + return AsyncClient.SetRangeInHashAsync(hash.Id, stringKeyValuePairs, token); } - ValueTask IRedisTypedClientAsync.GetValueFromHashAsync(IRedisHashAsync hash, TKey key, CancellationToken cancellationToken) - => DeserializeFromStringAsync(AsyncClient.GetValueFromHashAsync(hash.Id, key.SerializeToString(), cancellationToken)); + ValueTask IRedisTypedClientAsync.GetValueFromHashAsync(IRedisHashAsync hash, TKey key, CancellationToken token) + => DeserializeFromStringAsync(AsyncClient.GetValueFromHashAsync(hash.Id, key.SerializeToString(), token)); - ValueTask IRedisTypedClientAsync.RemoveEntryFromHashAsync(IRedisHashAsync hash, TKey key, CancellationToken cancellationToken) - => AsyncClient.RemoveEntryFromHashAsync(hash.Id, key.SerializeToString(), cancellationToken); + ValueTask IRedisTypedClientAsync.RemoveEntryFromHashAsync(IRedisHashAsync hash, TKey key, CancellationToken token) + => AsyncClient.RemoveEntryFromHashAsync(hash.Id, key.SerializeToString(), token); - ValueTask IRedisTypedClientAsync.GetHashCountAsync(IRedisHashAsync hash, CancellationToken cancellationToken) - => AsyncClient.GetHashCountAsync(hash.Id, cancellationToken); + ValueTask IRedisTypedClientAsync.GetHashCountAsync(IRedisHashAsync hash, CancellationToken token) + => AsyncClient.GetHashCountAsync(hash.Id, token); - ValueTask> IRedisTypedClientAsync.GetHashKeysAsync(IRedisHashAsync hash, CancellationToken cancellationToken) - => AsyncClient.GetHashKeysAsync(hash.Id, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetHashKeysAsync(IRedisHashAsync hash, CancellationToken token) + => AsyncClient.GetHashKeysAsync(hash.Id, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetHashValuesAsync(IRedisHashAsync hash, CancellationToken cancellationToken) - => AsyncClient.GetHashValuesAsync(hash.Id, cancellationToken).ConvertEachToAsync(); + ValueTask> IRedisTypedClientAsync.GetHashValuesAsync(IRedisHashAsync hash, CancellationToken token) + => AsyncClient.GetHashValuesAsync(hash.Id, token).ConvertEachToAsync(); - ValueTask> IRedisTypedClientAsync.GetAllEntriesFromHashAsync(IRedisHashAsync hash, CancellationToken cancellationToken) - => ConvertEachToAsync(AsyncClient.GetAllEntriesFromHashAsync(hash.Id, cancellationToken)); + ValueTask> IRedisTypedClientAsync.GetAllEntriesFromHashAsync(IRedisHashAsync hash, CancellationToken token) + => ConvertEachToAsync(AsyncClient.GetAllEntriesFromHashAsync(hash.Id, token)); - async ValueTask IRedisTypedClientAsync.StoreRelatedEntitiesAsync(object parentId, List children, CancellationToken cancellationToken) + async ValueTask IRedisTypedClientAsync.StoreRelatedEntitiesAsync(object parentId, List children, CancellationToken token) { var childRefKey = GetChildReferenceSetKey(parentId); var childKeys = children.ConvertAll(x => client.UrnKey(x)); - await using var trans = await AsyncClient.CreateTransactionAsync(cancellationToken).ConfigureAwait(false); + await using var trans = await AsyncClient.CreateTransactionAsync(token).ConfigureAwait(false); //Ugly but need access to a generic constraint-free StoreAll method - trans.QueueCommand(c => ((RedisClient)c).StoreAllAsyncImpl(children, cancellationToken)); - trans.QueueCommand(c => c.AddRangeToSetAsync(childRefKey, childKeys, cancellationToken)); + trans.QueueCommand(c => ((RedisClient)c).StoreAllAsyncImpl(children, token)); + trans.QueueCommand(c => c.AddRangeToSetAsync(childRefKey, childKeys, token)); - await trans.CommitAsync(cancellationToken).ConfigureAwait(false); + await trans.CommitAsync(token).ConfigureAwait(false); } - ValueTask IRedisTypedClientAsync.StoreRelatedEntitiesAsync(object parentId, TChild[] children, CancellationToken cancellationToken) - => AsAsync().StoreRelatedEntitiesAsync(parentId, new List(children), cancellationToken); + ValueTask IRedisTypedClientAsync.StoreRelatedEntitiesAsync(object parentId, TChild[] children, CancellationToken token) + => AsAsync().StoreRelatedEntitiesAsync(parentId, new List(children), token); - ValueTask IRedisTypedClientAsync.DeleteRelatedEntitiesAsync(object parentId, CancellationToken cancellationToken) + ValueTask IRedisTypedClientAsync.DeleteRelatedEntitiesAsync(object parentId, CancellationToken token) { var childRefKey = GetChildReferenceSetKey(parentId); - return new ValueTask(AsyncClient.RemoveAsync(childRefKey, cancellationToken)); + return new ValueTask(AsyncClient.RemoveAsync(childRefKey, token)); } - ValueTask IRedisTypedClientAsync.DeleteRelatedEntityAsync(object parentId, object childId, CancellationToken cancellationToken) + ValueTask IRedisTypedClientAsync.DeleteRelatedEntityAsync(object parentId, object childId, CancellationToken token) { var childRefKey = GetChildReferenceSetKey(parentId); - return AsyncClient.RemoveItemFromSetAsync(childRefKey, TypeSerializer.SerializeToString(childId), cancellationToken); + return AsyncClient.RemoveItemFromSetAsync(childRefKey, TypeSerializer.SerializeToString(childId), token); } - async ValueTask> IRedisTypedClientAsync.GetRelatedEntitiesAsync(object parentId, CancellationToken cancellationToken) + async ValueTask> IRedisTypedClientAsync.GetRelatedEntitiesAsync(object parentId, CancellationToken token) { var childRefKey = GetChildReferenceSetKey(parentId); - var childKeys = (await AsyncClient.GetAllItemsFromSetAsync(childRefKey, cancellationToken).ConfigureAwait(false)).ToList(); + var childKeys = (await AsyncClient.GetAllItemsFromSetAsync(childRefKey, token).ConfigureAwait(false)).ToList(); - return await AsyncClient.As().GetValuesAsync(childKeys, cancellationToken).ConfigureAwait(false); + return await AsyncClient.As().GetValuesAsync(childKeys, token).ConfigureAwait(false); } - ValueTask IRedisTypedClientAsync.GetRelatedEntitiesCountAsync(object parentId, CancellationToken cancellationToken) + ValueTask IRedisTypedClientAsync.GetRelatedEntitiesCountAsync(object parentId, CancellationToken token) { var childRefKey = GetChildReferenceSetKey(parentId); - return AsyncClient.GetSetCountAsync(childRefKey, cancellationToken); + return AsyncClient.GetSetCountAsync(childRefKey, token); } - ValueTask IRedisTypedClientAsync.AddToRecentsListAsync(T value, CancellationToken cancellationToken) + ValueTask IRedisTypedClientAsync.AddToRecentsListAsync(T value, CancellationToken token) { var key = client.UrnKey(value); var nowScore = DateTime.UtcNow.ToUnixTime(); - return AsyncClient.AddItemToSortedSetAsync(RecentSortedSetKey, key, nowScore, cancellationToken).Await(); + return AsyncClient.AddItemToSortedSetAsync(RecentSortedSetKey, key, nowScore, token).Await(); } - async ValueTask> IRedisTypedClientAsync.GetLatestFromRecentsListAsync(int skip, int take, CancellationToken cancellationToken) + async ValueTask> IRedisTypedClientAsync.GetLatestFromRecentsListAsync(int skip, int take, CancellationToken token) { var toRank = take - 1; - var keys = await AsyncClient.GetRangeFromSortedSetDescAsync(RecentSortedSetKey, skip, toRank, cancellationToken).ConfigureAwait(false); - var values = await AsAsync().GetValuesAsync(keys, cancellationToken).ConfigureAwait(false); + var keys = await AsyncClient.GetRangeFromSortedSetDescAsync(RecentSortedSetKey, skip, toRank, token).ConfigureAwait(false); + var values = await AsAsync().GetValuesAsync(keys, token).ConfigureAwait(false); return values; } - async ValueTask> IRedisTypedClientAsync.GetEarliestFromRecentsListAsync(int skip, int take, CancellationToken cancellationToken) + async ValueTask> IRedisTypedClientAsync.GetEarliestFromRecentsListAsync(int skip, int take, CancellationToken token) { var toRank = take - 1; - var keys = await AsyncClient.GetRangeFromSortedSetAsync(RecentSortedSetKey, skip, toRank, cancellationToken).ConfigureAwait(false); - var values = await AsAsync().GetValuesAsync(keys, cancellationToken).ConfigureAwait(false); + var keys = await AsyncClient.GetRangeFromSortedSetAsync(RecentSortedSetKey, skip, toRank, token).ConfigureAwait(false); + var values = await AsAsync().GetValuesAsync(keys, token).ConfigureAwait(false); return values; } ValueTask IRedisTypedClientAsync.RemoveEntryAsync(params string[] args) - => AsAsync().RemoveEntryAsync(args, cancellationToken: default); + => AsAsync().RemoveEntryAsync(args, token: default); ValueTask IRedisTypedClientAsync.RemoveEntryAsync(params IHasStringId[] entities) - => AsAsync().RemoveEntryAsync(entities, cancellationToken: default); + => AsAsync().RemoveEntryAsync(entities, token: default); ValueTask> IRedisTypedClientAsync.GetIntersectFromSetsAsync(params IRedisSetAsync[] sets) - => AsAsync().GetIntersectFromSetsAsync(sets, cancellationToken: default); + => AsAsync().GetIntersectFromSetsAsync(sets, token: default); ValueTask IRedisTypedClientAsync.StoreIntersectFromSetsAsync(IRedisSetAsync intoSet, params IRedisSetAsync[] sets) - => AsAsync().StoreIntersectFromSetsAsync(intoSet, sets, cancellationToken: default); + => AsAsync().StoreIntersectFromSetsAsync(intoSet, sets, token: default); ValueTask> IRedisTypedClientAsync.GetUnionFromSetsAsync(params IRedisSetAsync[] sets) - => AsAsync().GetUnionFromSetsAsync(sets, cancellationToken: default); + => AsAsync().GetUnionFromSetsAsync(sets, token: default); ValueTask IRedisTypedClientAsync.StoreUnionFromSetsAsync(IRedisSetAsync intoSet, params IRedisSetAsync[] sets) - => AsAsync().StoreUnionFromSetsAsync(intoSet, sets, cancellationToken: default); + => AsAsync().StoreUnionFromSetsAsync(intoSet, sets, token: default); ValueTask> IRedisTypedClientAsync.GetDifferencesFromSetAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets) - => AsAsync().GetDifferencesFromSetAsync(fromSet, withSets, cancellationToken: default); + => AsAsync().GetDifferencesFromSetAsync(fromSet, withSets, token: default); ValueTask IRedisTypedClientAsync.StoreDifferencesFromSetAsync(IRedisSetAsync intoSet, IRedisSetAsync fromSet, params IRedisSetAsync[] withSets) - => AsAsync().StoreDifferencesFromSetAsync(intoSet, fromSet, withSets, cancellationToken: default); + => AsAsync().StoreDifferencesFromSetAsync(intoSet, fromSet, withSets, token: default); ValueTask IRedisTypedClientAsync.StoreIntersectFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, params IRedisSortedSetAsync[] setIds) - => AsAsync().StoreIntersectFromSortedSetsAsync(intoSetId, setIds, cancellationToken: default); + => AsAsync().StoreIntersectFromSortedSetsAsync(intoSetId, setIds, token: default); ValueTask IRedisTypedClientAsync.StoreUnionFromSortedSetsAsync(IRedisSortedSetAsync intoSetId, params IRedisSortedSetAsync[] setIds) - => AsAsync().StoreUnionFromSortedSetsAsync(intoSetId, setIds, cancellationToken: default); + => AsAsync().StoreUnionFromSortedSetsAsync(intoSetId, setIds, token: default); ValueTask IRedisTypedClientAsync.StoreRelatedEntitiesAsync(object parentId, params TChild[] children) - => AsAsync().StoreRelatedEntitiesAsync(parentId, children, cancellationToken: default); + => AsAsync().StoreRelatedEntitiesAsync(parentId, children, token: default); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs index f20ce174..5f7908b8 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs @@ -113,25 +113,25 @@ ValueTask IAsyncDisposable.DisposeAsync() return default; } - async ValueTask IRedisPipelineSharedAsync.FlushAsync(CancellationToken cancellationToken) + async ValueTask IRedisPipelineSharedAsync.FlushAsync(CancellationToken token) { try { // flush send buffers - await RedisClient.FlushSendBufferAsync(cancellationToken).ConfigureAwait(false); + await RedisClient.FlushSendBufferAsync(token).ConfigureAwait(false); RedisClient.ResetSendBuffer(); //receive expected results foreach (var queuedCommand in QueuedCommands) { - await queuedCommand.ProcessResultAsync(cancellationToken).ConfigureAwait(false); + await queuedCommand.ProcessResultAsync(token).ConfigureAwait(false); } } finally { ClosePipeline(); - await RedisClient.AddTypeIdsRegisteredDuringPipelineAsync(cancellationToken).ConfigureAwait(false); + await RedisClient.AddTypeIdsRegisteredDuringPipelineAsync(token).ConfigureAwait(false); } } @@ -249,7 +249,7 @@ void IRedisTypedQueueableOperationAsync.QueueCommand(Func IRedisPipelineSharedAsync.ReplayAsync(CancellationToken cancellationToken) + async ValueTask IRedisPipelineSharedAsync.ReplayAsync(CancellationToken token) { RedisClient.Pipeline = this; // execute @@ -258,7 +258,7 @@ async ValueTask IRedisPipelineSharedAsync.ReplayAsync(CancellationToken ca if (queuedCommand is QueuedRedisTypedCommand cmd) await cmd.ExecuteAsync(RedisClient).ConfigureAwait(false); } - await AsAsync().FlushAsync(cancellationToken).ConfigureAwait(false); + await AsAsync().FlushAsync(token).ConfigureAwait(false); return true; } } diff --git a/src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs index 76709608..fdefb400 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs @@ -24,7 +24,7 @@ namespace ServiceStack.Redis.Generic internal partial class RedisTypedTransaction : IRedisTypedTransactionAsync, IRedisTransactionBaseAsync { - async ValueTask IRedisTypedTransactionAsync.CommitAsync(CancellationToken cancellationToken) + async ValueTask IRedisTypedTransactionAsync.CommitAsync(CancellationToken token) { bool rc = true; try @@ -47,16 +47,16 @@ async ValueTask IRedisTypedTransactionAsync.CommitAsync(CancellationTok // add Exec command at end (not queued) QueuedCommands.Add(new RedisCommand() { - }.WithAsyncReturnCommand(r => ExecAsync(cancellationToken))); + }.WithAsyncReturnCommand(r => ExecAsync(token))); //execute transaction - await ExecAsync(cancellationToken).ConfigureAwait(false); + await ExecAsync(token).ConfigureAwait(false); ///////////////////////////// //receive expected results foreach (var queuedCommand in QueuedCommands) { - await queuedCommand.ProcessResultAsync(cancellationToken).ConfigureAwait(false); + await queuedCommand.ProcessResultAsync(token).ConfigureAwait(false); } } catch (RedisTransactionFailedException) @@ -67,18 +67,18 @@ async ValueTask IRedisTypedTransactionAsync.CommitAsync(CancellationTok { RedisClient.Transaction = null; ClosePipeline(); - await RedisClient.AddTypeIdsRegisteredDuringPipelineAsync(cancellationToken).ConfigureAwait(false); + await RedisClient.AddTypeIdsRegisteredDuringPipelineAsync(token).ConfigureAwait(false); } return rc; } - private ValueTask ExecAsync(CancellationToken cancellationToken) + private ValueTask ExecAsync(CancellationToken token) { RedisClient.Exec(); - return RedisClient.FlushSendBufferAsync(cancellationToken); + return RedisClient.FlushSendBufferAsync(token); } - ValueTask IRedisTypedTransactionAsync.RollbackAsync(CancellationToken cancellationToken) + ValueTask IRedisTypedTransactionAsync.RollbackAsync(CancellationToken token) { Rollback(); // no async bits needed return default; diff --git a/src/ServiceStack.Redis/Pipeline/QueuedRedisOperation.Async.cs b/src/ServiceStack.Redis/Pipeline/QueuedRedisOperation.Async.cs index 8041e0b0..e1779605 100644 --- a/src/ServiceStack.Redis/Pipeline/QueuedRedisOperation.Async.cs +++ b/src/ServiceStack.Redis/Pipeline/QueuedRedisOperation.Async.cs @@ -42,7 +42,7 @@ internal QueuedRedisOperation WithAsyncReadCommand(Func> RedisDataReadCommandAsync) => SetAsyncReadCommand(RedisDataReadCommandAsync); - public async ValueTask ProcessResultAsync(CancellationToken cancellationToken) + public async ValueTask ProcessResultAsync(CancellationToken token) { try { @@ -52,29 +52,29 @@ public async ValueTask ProcessResultAsync(CancellationToken cancellationToken) ProcessResultThrowIfSync(); break; case Func VoidReadCommandAsync: - await VoidReadCommandAsync(cancellationToken).ConfigureAwait(false); + await VoidReadCommandAsync(token).ConfigureAwait(false); OnSuccessVoidCallback?.Invoke(); break; case Func> IntReadCommandAsync: - var i32 = await IntReadCommandAsync(cancellationToken).ConfigureAwait(false); + var i32 = await IntReadCommandAsync(token).ConfigureAwait(false); OnSuccessIntCallback?.Invoke(i32); OnSuccessLongCallback?.Invoke(i32); OnSuccessBoolCallback?.Invoke(i32 == RedisNativeClient.Success); OnSuccessVoidCallback?.Invoke(); break; case Func> LongReadCommandAsync: - var i64 = await LongReadCommandAsync(cancellationToken).ConfigureAwait(false); + var i64 = await LongReadCommandAsync(token).ConfigureAwait(false); OnSuccessIntCallback?.Invoke((int)i64); OnSuccessLongCallback?.Invoke(i64); OnSuccessBoolCallback?.Invoke(i64 == RedisNativeClient.Success); OnSuccessVoidCallback?.Invoke(); break; case Func> DoubleReadCommandAsync: - var f64 = await DoubleReadCommandAsync(cancellationToken).ConfigureAwait(false); + var f64 = await DoubleReadCommandAsync(token).ConfigureAwait(false); OnSuccessDoubleCallback?.Invoke(f64); break; case Func> BytesReadCommandAsync: - var bytes = await BytesReadCommandAsync(cancellationToken).ConfigureAwait(false); + var bytes = await BytesReadCommandAsync(token).ConfigureAwait(false); if (bytes != null && bytes.Length == 0) bytes = null; OnSuccessBytesCallback?.Invoke(bytes); OnSuccessStringCallback?.Invoke(bytes != null ? Encoding.UTF8.GetString(bytes) : null); @@ -83,32 +83,32 @@ public async ValueTask ProcessResultAsync(CancellationToken cancellationToken) OnSuccessBoolCallback?.Invoke(bytes != null && Encoding.UTF8.GetString(bytes) == "OK"); break; case Func> StringReadCommandAsync: - var s = await StringReadCommandAsync(cancellationToken).ConfigureAwait(false); + var s = await StringReadCommandAsync(token).ConfigureAwait(false); OnSuccessStringCallback?.Invoke(s); OnSuccessTypeCallback?.Invoke(s); break; case Func> MultiBytesReadCommandAsync: - var multiBytes = await MultiBytesReadCommandAsync(cancellationToken).ConfigureAwait(false); + var multiBytes = await MultiBytesReadCommandAsync(token).ConfigureAwait(false); OnSuccessMultiBytesCallback?.Invoke(multiBytes); OnSuccessMultiStringCallback?.Invoke(multiBytes?.ToStringList()); OnSuccessMultiTypeCallback?.Invoke(multiBytes.ToStringList()); OnSuccessDictionaryStringCallback?.Invoke(multiBytes.ToStringDictionary()); break; case Func>> MultiStringReadCommandAsync: - var multiString = await MultiStringReadCommandAsync(cancellationToken).ConfigureAwait(false); + var multiString = await MultiStringReadCommandAsync(token).ConfigureAwait(false); OnSuccessMultiStringCallback?.Invoke(multiString); break; case Func> RedisDataReadCommandAsync: - var data = await RedisDataReadCommandAsync(cancellationToken).ConfigureAwait(false); + var data = await RedisDataReadCommandAsync(token).ConfigureAwait(false); OnSuccessRedisTextCallback?.Invoke(data.ToRedisText()); OnSuccessRedisDataCallback?.Invoke(data); break; case Func> BoolReadCommandAsync: - var b = await BoolReadCommandAsync(cancellationToken).ConfigureAwait(false); + var b = await BoolReadCommandAsync(token).ConfigureAwait(false); OnSuccessBoolCallback?.Invoke(b); break; case Func>> DictionaryStringReadCommandAsync: - var dict = await DictionaryStringReadCommandAsync(cancellationToken).ConfigureAwait(false); + var dict = await DictionaryStringReadCommandAsync(token).ConfigureAwait(false); OnSuccessDictionaryStringCallback?.Invoke(dict); break; default: diff --git a/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.Async.cs b/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.Async.cs index 17a30497..2b346e74 100644 --- a/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.Async.cs +++ b/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.Async.cs @@ -12,11 +12,11 @@ public partial class RedisAllPurposePipeline : IRedisPipelineAsync { private IRedisPipelineAsync AsAsync() => this; - private protected virtual async ValueTask ReplayAsync(CancellationToken cancellationToken) + private protected virtual async ValueTask ReplayAsync(CancellationToken token) { Init(); await ExecuteAsync().ConfigureAwait(false); - await AsAsync().FlushAsync(cancellationToken).ConfigureAwait(false); + await AsAsync().FlushAsync(token).ConfigureAwait(false); return true; } @@ -32,13 +32,13 @@ protected async ValueTask ExecuteAsync() } } - ValueTask IRedisPipelineSharedAsync.ReplayAsync(CancellationToken cancellationToken) - => ReplayAsync(cancellationToken); + ValueTask IRedisPipelineSharedAsync.ReplayAsync(CancellationToken token) + => ReplayAsync(token); - async ValueTask IRedisPipelineSharedAsync.FlushAsync(CancellationToken cancellationToken) + async ValueTask IRedisPipelineSharedAsync.FlushAsync(CancellationToken token) { // flush send buffers - await RedisClient.FlushSendBufferAsync(cancellationToken).ConfigureAwait(false); + await RedisClient.FlushSendBufferAsync(token).ConfigureAwait(false); RedisClient.ResetSendBuffer(); try @@ -46,7 +46,7 @@ async ValueTask IRedisPipelineSharedAsync.FlushAsync(CancellationToken cancellat //receive expected results foreach (var queuedCommand in QueuedCommands) { - await queuedCommand.ProcessResultAsync(cancellationToken).ConfigureAwait(false); + await queuedCommand.ProcessResultAsync(token).ConfigureAwait(false); } } catch (Exception) diff --git a/src/ServiceStack.Redis/Pipeline/RedisPipelineCommand.Async.cs b/src/ServiceStack.Redis/Pipeline/RedisPipelineCommand.Async.cs index 19f71fdc..bea7be2f 100644 --- a/src/ServiceStack.Redis/Pipeline/RedisPipelineCommand.Async.cs +++ b/src/ServiceStack.Redis/Pipeline/RedisPipelineCommand.Async.cs @@ -7,23 +7,23 @@ namespace ServiceStack.Redis.Pipeline { partial class RedisPipelineCommand { - internal async ValueTask> ReadAllAsIntsAsync(CancellationToken cancellationToken) + internal async ValueTask> ReadAllAsIntsAsync(CancellationToken token) { var results = new List(); while (cmdCount-- > 0) { - results.Add(await client.ReadLongAsync(cancellationToken).ConfigureAwait(false)); + results.Add(await client.ReadLongAsync(token).ConfigureAwait(false)); } return results; } - internal async ValueTask ReadAllAsIntsHaveSuccessAsync(CancellationToken cancellationToken) + internal async ValueTask ReadAllAsIntsHaveSuccessAsync(CancellationToken token) { - var allResults = await ReadAllAsIntsAsync(cancellationToken).ConfigureAwait(false); + var allResults = await ReadAllAsIntsAsync(token).ConfigureAwait(false); return allResults.All(x => x == RedisNativeClient.Success); } - internal ValueTask FlushAsync(CancellationToken cancellationToken) + internal ValueTask FlushAsync(CancellationToken token) { Flush(); return default; diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs b/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs index 8fc0b08d..a7bb0ab2 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs @@ -21,16 +21,16 @@ namespace ServiceStack.Redis public partial class PooledRedisClientManager : IRedisClientsManagerAsync { - ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(CancellationToken cancellationToken) + ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(CancellationToken token) => new RedisClientManagerCacheClient(this).AsValueTaskResult(); - ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken cancellationToken) + ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken token) => GetClient(true).AsValueTaskResult(); - ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken cancellationToken) + ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken token) => new RedisClientManagerCacheClient(this) { ReadOnly = true }.AsValueTaskResult(); - ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken cancellationToken) + ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken token) => GetReadOnlyClient(true).AsValueTaskResult(); ValueTask IAsyncDisposable.DisposeAsync() diff --git a/src/ServiceStack.Redis/RedisClient.Async.cs b/src/ServiceStack.Redis/RedisClient.Async.cs index b058bbb7..d0cfc466 100644 --- a/src/ServiceStack.Redis/RedisClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClient.Async.cs @@ -46,14 +46,14 @@ partial class RedisClient : IRedisClientAsync, IRemoveByPatternAsync, ICacheClie IHasNamed IRedisClientAsync.SortedSets => SortedSets as IHasNamed ?? throw new NotSupportedException($"The provided SortedSets ({SortedSets?.GetType().FullName})does not support IRedisSortedSetAsync"); IHasNamed IRedisClientAsync.Hashes => Hashes as IHasNamed ?? throw new NotSupportedException($"The provided Hashes ({Hashes?.GetType().FullName})does not support IRedisHashAsync"); - internal ValueTask RegisterTypeIdAsync(T value, CancellationToken cancellationToken) + internal ValueTask RegisterTypeIdAsync(T value, CancellationToken token) { var typeIdsSetKey = GetTypeIdsSetKey(); var id = value.GetId().ToString(); - return RegisterTypeIdAsync(typeIdsSetKey, id, cancellationToken); + return RegisterTypeIdAsync(typeIdsSetKey, id, token); } - internal ValueTask RegisterTypeIdAsync(string typeIdsSetKey, string id, CancellationToken cancellationToken) + internal ValueTask RegisterTypeIdAsync(string typeIdsSetKey, string id, CancellationToken token) { if (this.Pipeline != null) { @@ -63,35 +63,35 @@ internal ValueTask RegisterTypeIdAsync(string typeIdsSetKey, string id, Cancella } else { - return AsAsync().AddItemToSetAsync(typeIdsSetKey, id, cancellationToken); + return AsAsync().AddItemToSetAsync(typeIdsSetKey, id, token); } } // Called just after original Pipeline is closed. - internal async ValueTask AddTypeIdsRegisteredDuringPipelineAsync(CancellationToken cancellationToken) + internal async ValueTask AddTypeIdsRegisteredDuringPipelineAsync(CancellationToken token) { foreach (var entry in registeredTypeIdsWithinPipelineMap) { - await AsAsync().AddRangeToSetAsync(entry.Key, entry.Value.ToList(), cancellationToken).ConfigureAwait(false); + await AsAsync().AddRangeToSetAsync(entry.Key, entry.Value.ToList(), token).ConfigureAwait(false); } registeredTypeIdsWithinPipelineMap = new Dictionary>(); } - ValueTask IRedisClientAsync.GetServerTimeAsync(CancellationToken cancellationToken) - => NativeAsync.TimeAsync(cancellationToken).Await(parts => ParseTimeResult(parts)); + ValueTask IRedisClientAsync.GetServerTimeAsync(CancellationToken token) + => NativeAsync.TimeAsync(token).Await(parts => ParseTimeResult(parts)); IRedisPipelineAsync IRedisClientAsync.CreatePipeline() => new RedisAllPurposePipeline(this); - ValueTask IRedisClientAsync.CreateTransactionAsync(CancellationToken cancellationToken) + ValueTask IRedisClientAsync.CreateTransactionAsync(CancellationToken token) { AssertServerVersionNumber(); // pre-fetch call to INFO before transaction if needed return new RedisTransaction(this, true).AsValueTaskResult(); // note that the MULTI here will be held and flushed async } - ValueTask IRedisClientAsync.RemoveEntryAsync(string[] keys, CancellationToken cancellationToken) - => keys.Length == 0 ? default : NativeAsync.DelAsync(keys, cancellationToken).IsSuccessAsync(); + ValueTask IRedisClientAsync.RemoveEntryAsync(string[] keys, CancellationToken token) + => keys.Length == 0 ? default : NativeAsync.DelAsync(keys, token).IsSuccessAsync(); private async ValueTask ExecAsync(Func action) { @@ -109,42 +109,42 @@ private async ValueTask ExecAsync(Func> ac } } - ValueTask IRedisClientAsync.SetValueAsync(string key, string value, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.SetValueAsync(string key, string value, CancellationToken token) { var bytesValue = value?.ToUtf8Bytes(); - return NativeAsync.SetAsync(key, bytesValue, cancellationToken: cancellationToken); + return NativeAsync.SetAsync(key, bytesValue, token: token); } - ValueTask IRedisClientAsync.GetValueAsync(string key, CancellationToken cancellationToken) - => NativeAsync.GetAsync(key, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.GetValueAsync(string key, CancellationToken token) + => NativeAsync.GetAsync(key, token).FromUtf8BytesAsync(); - Task ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) + Task ICacheClientAsync.GetAsync(string key, CancellationToken token) { return ExecAsync(r => typeof(T) == typeof(byte[]) - ? ((IRedisNativeClientAsync)r).GetAsync(key, cancellationToken).Await(val => (T)(object)val) - : r.GetValueAsync(key, cancellationToken).Await(val => JsonSerializer.DeserializeFromString(val)) + ? ((IRedisNativeClientAsync)r).GetAsync(key, token).Await(val => (T)(object)val) + : r.GetValueAsync(key, token).Await(val => JsonSerializer.DeserializeFromString(val)) ).AsTask(); } - async ValueTask> IRedisClientAsync.SearchKeysAsync(string pattern, CancellationToken cancellationToken) + async ValueTask> IRedisClientAsync.SearchKeysAsync(string pattern, CancellationToken token) { var list = new List(); - await foreach (var value in ((IRedisClientAsync)this).ScanAllKeysAsync(pattern, cancellationToken: cancellationToken).WithCancellation(cancellationToken).ConfigureAwait(false)) + await foreach (var value in ((IRedisClientAsync)this).ScanAllKeysAsync(pattern, token: token).WithCancellation(token).ConfigureAwait(false)) { list.Add(value); } return list; } - async IAsyncEnumerable IRedisClientAsync.ScanAllKeysAsync(string pattern, int pageSize, [EnumeratorCancellation] CancellationToken cancellationToken) + async IAsyncEnumerable IRedisClientAsync.ScanAllKeysAsync(string pattern, int pageSize, [EnumeratorCancellation] CancellationToken token) { ScanResult ret = default; while (true) { ret = await (pattern != null // note ConfigureAwait is handled below - ? NativeAsync.ScanAsync(ret?.Cursor ?? 0, pageSize, match: pattern, cancellationToken: cancellationToken) - : NativeAsync.ScanAsync(ret?.Cursor ?? 0, pageSize, cancellationToken: cancellationToken) + ? NativeAsync.ScanAsync(ret?.Cursor ?? 0, pageSize, match: pattern, token: token) + : NativeAsync.ScanAsync(ret?.Cursor ?? 0, pageSize, token: token) ).ConfigureAwait(false); foreach (var key in ret.Results) @@ -156,31 +156,31 @@ async IAsyncEnumerable IRedisClientAsync.ScanAllKeysAsync(string pattern } } - ValueTask IRedisClientAsync.GetEntryTypeAsync(string key, CancellationToken cancellationToken) - => NativeAsync.TypeAsync(key, cancellationToken).Await((val, state) => state.ParseEntryType(val), this); + ValueTask IRedisClientAsync.GetEntryTypeAsync(string key, CancellationToken token) + => NativeAsync.TypeAsync(key, token).Await((val, state) => state.ParseEntryType(val), this); - ValueTask IRedisClientAsync.AddItemToSetAsync(string setId, string item, CancellationToken cancellationToken) - => NativeAsync.SAddAsync(setId, item.ToUtf8Bytes(), cancellationToken).Await(); + ValueTask IRedisClientAsync.AddItemToSetAsync(string setId, string item, CancellationToken token) + => NativeAsync.SAddAsync(setId, item.ToUtf8Bytes(), token).Await(); - ValueTask IRedisClientAsync.AddItemToListAsync(string listId, string value, CancellationToken cancellationToken) - => NativeAsync.RPushAsync(listId, value.ToUtf8Bytes(), cancellationToken).Await(); + ValueTask IRedisClientAsync.AddItemToListAsync(string listId, string value, CancellationToken token) + => NativeAsync.RPushAsync(listId, value.ToUtf8Bytes(), token).Await(); - ValueTask IRedisClientAsync.AddItemToSortedSetAsync(string setId, string value, CancellationToken cancellationToken) - => ((IRedisClientAsync)this).AddItemToSortedSetAsync(setId, value, GetLexicalScore(value), cancellationToken); + ValueTask IRedisClientAsync.AddItemToSortedSetAsync(string setId, string value, CancellationToken token) + => ((IRedisClientAsync)this).AddItemToSortedSetAsync(setId, value, GetLexicalScore(value), token); - ValueTask IRedisClientAsync.AddItemToSortedSetAsync(string setId, string value, double score, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.AddItemToSortedSetAsync(string setId, string value, double score, CancellationToken token) => NativeAsync.ZAddAsync(setId, score, value.ToUtf8Bytes()).IsSuccessAsync(); - ValueTask IRedisClientAsync.SetEntryInHashAsync(string hashId, string key, string value, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.SetEntryInHashAsync(string hashId, string key, string value, CancellationToken token) => NativeAsync.HSetAsync(hashId, key.ToUtf8Bytes(), value.ToUtf8Bytes()).IsSuccessAsync(); - ValueTask IRedisClientAsync.SetAllAsync(IDictionary map, CancellationToken cancellationToken) - => GetSetAllBytes(map, out var keyBytes, out var valBytes) ? NativeAsync.MSetAsync(keyBytes, valBytes, cancellationToken) : default; + ValueTask IRedisClientAsync.SetAllAsync(IDictionary map, CancellationToken token) + => GetSetAllBytes(map, out var keyBytes, out var valBytes) ? NativeAsync.MSetAsync(keyBytes, valBytes, token) : default; - ValueTask IRedisClientAsync.SetAllAsync(IEnumerable keys, IEnumerable values, CancellationToken cancellationToken) - => GetSetAllBytes(keys, values, out var keyBytes, out var valBytes) ? NativeAsync.MSetAsync(keyBytes, valBytes, cancellationToken) : default; + ValueTask IRedisClientAsync.SetAllAsync(IEnumerable keys, IEnumerable values, CancellationToken token) + => GetSetAllBytes(keys, values, out var keyBytes, out var valBytes) ? NativeAsync.MSetAsync(keyBytes, valBytes, token) : default; - Task ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) + Task ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken token) { if (values.Count != 0) { @@ -188,7 +188,7 @@ Task ICacheClientAsync.SetAllAsync(IDictionary values, Cancellatio { // need to do this inside Exec for the JSON config bits GetSetAllBytesTyped(values, out var keys, out var valBytes); - return ((IRedisNativeClientAsync)r).MSetAsync(keys, valBytes, cancellationToken); + return ((IRedisNativeClientAsync)r).MSetAsync(keys, valBytes, token); }).AsTask(); } else @@ -197,94 +197,94 @@ Task ICacheClientAsync.SetAllAsync(IDictionary values, Cancellatio } } - ValueTask IRedisClientAsync.RenameKeyAsync(string fromName, string toName, CancellationToken cancellationToken) - => NativeAsync.RenameAsync(fromName, toName, cancellationToken); + ValueTask IRedisClientAsync.RenameKeyAsync(string fromName, string toName, CancellationToken token) + => NativeAsync.RenameAsync(fromName, toName, token); - ValueTask IRedisClientAsync.ContainsKeyAsync(string key, CancellationToken cancellationToken) - => NativeAsync.ExistsAsync(key, cancellationToken).IsSuccessAsync(); + ValueTask IRedisClientAsync.ContainsKeyAsync(string key, CancellationToken token) + => NativeAsync.ExistsAsync(key, token).IsSuccessAsync(); - ValueTask IRedisClientAsync.GetRandomKeyAsync(CancellationToken cancellationToken) - => NativeAsync.RandomKeyAsync(cancellationToken); + ValueTask IRedisClientAsync.GetRandomKeyAsync(CancellationToken token) + => NativeAsync.RandomKeyAsync(token); - ValueTask IRedisClientAsync.SelectAsync(long db, CancellationToken cancellationToken) - => NativeAsync.SelectAsync(db, cancellationToken); + ValueTask IRedisClientAsync.SelectAsync(long db, CancellationToken token) + => NativeAsync.SelectAsync(db, token); - ValueTask IRedisClientAsync.ExpireEntryInAsync(string key, TimeSpan expireIn, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.ExpireEntryInAsync(string key, TimeSpan expireIn, CancellationToken token) => UseMillisecondExpiration(expireIn) - ? NativeAsync.PExpireAsync(key, (long)expireIn.TotalMilliseconds, cancellationToken) - : NativeAsync.ExpireAsync(key, (int)expireIn.TotalSeconds, cancellationToken); + ? NativeAsync.PExpireAsync(key, (long)expireIn.TotalMilliseconds, token) + : NativeAsync.ExpireAsync(key, (int)expireIn.TotalSeconds, token); - ValueTask IRedisClientAsync.ExpireEntryAtAsync(string key, DateTime expireAt, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.ExpireEntryAtAsync(string key, DateTime expireAt, CancellationToken token) => AssertServerVersionNumber() >= 2600 - ? NativeAsync.PExpireAtAsync(key, ConvertToServerDate(expireAt).ToUnixTimeMs(), cancellationToken) - : NativeAsync.ExpireAtAsync(key, ConvertToServerDate(expireAt).ToUnixTime(), cancellationToken); + ? NativeAsync.PExpireAtAsync(key, ConvertToServerDate(expireAt).ToUnixTimeMs(), token) + : NativeAsync.ExpireAtAsync(key, ConvertToServerDate(expireAt).ToUnixTime(), token); - Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) - => NativeAsync.TtlAsync(key, cancellationToken).Await(ttlSecs => ParseTimeToLiveResult(ttlSecs)).AsTask(); + Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken token) + => NativeAsync.TtlAsync(key, token).Await(ttlSecs => ParseTimeToLiveResult(ttlSecs)).AsTask(); - ValueTask IRedisClientAsync.PingAsync(CancellationToken cancellationToken) - => NativeAsync.PingAsync(cancellationToken); + ValueTask IRedisClientAsync.PingAsync(CancellationToken token) + => NativeAsync.PingAsync(token); - ValueTask IRedisClientAsync.EchoAsync(string text, CancellationToken cancellationToken) - => NativeAsync.EchoAsync(text, cancellationToken); + ValueTask IRedisClientAsync.EchoAsync(string text, CancellationToken token) + => NativeAsync.EchoAsync(text, token); - ValueTask IRedisClientAsync.ForegroundSaveAsync(CancellationToken cancellationToken) - => NativeAsync.SaveAsync(cancellationToken); + ValueTask IRedisClientAsync.ForegroundSaveAsync(CancellationToken token) + => NativeAsync.SaveAsync(token); - ValueTask IRedisClientAsync.BackgroundSaveAsync(CancellationToken cancellationToken) - => NativeAsync.BgSaveAsync(cancellationToken); + ValueTask IRedisClientAsync.BackgroundSaveAsync(CancellationToken token) + => NativeAsync.BgSaveAsync(token); - ValueTask IRedisClientAsync.ShutdownAsync(CancellationToken cancellationToken) - => NativeAsync.ShutdownAsync(false, cancellationToken); + ValueTask IRedisClientAsync.ShutdownAsync(CancellationToken token) + => NativeAsync.ShutdownAsync(false, token); - ValueTask IRedisClientAsync.ShutdownNoSaveAsync(CancellationToken cancellationToken) - => NativeAsync.ShutdownAsync(true, cancellationToken); + ValueTask IRedisClientAsync.ShutdownNoSaveAsync(CancellationToken token) + => NativeAsync.ShutdownAsync(true, token); - ValueTask IRedisClientAsync.BackgroundRewriteAppendOnlyFileAsync(CancellationToken cancellationToken) - => NativeAsync.BgRewriteAofAsync(cancellationToken); + ValueTask IRedisClientAsync.BackgroundRewriteAppendOnlyFileAsync(CancellationToken token) + => NativeAsync.BgRewriteAofAsync(token); - ValueTask IRedisClientAsync.FlushDbAsync(CancellationToken cancellationToken) - => NativeAsync.FlushDbAsync(cancellationToken); + ValueTask IRedisClientAsync.FlushDbAsync(CancellationToken token) + => NativeAsync.FlushDbAsync(token); - ValueTask> IRedisClientAsync.GetValuesAsync(List keys, CancellationToken cancellationToken) + ValueTask> IRedisClientAsync.GetValuesAsync(List keys, CancellationToken token) { if (keys == null) throw new ArgumentNullException(nameof(keys)); if (keys.Count == 0) return new List().AsValueTaskResult(); - return NativeAsync.MGetAsync(keys.ToArray(), cancellationToken).Await(val => ParseGetValuesResult(val)); + return NativeAsync.MGetAsync(keys.ToArray(), token).Await(val => ParseGetValuesResult(val)); } - ValueTask> IRedisClientAsync.GetValuesAsync(List keys, CancellationToken cancellationToken) + ValueTask> IRedisClientAsync.GetValuesAsync(List keys, CancellationToken token) { if (keys == null) throw new ArgumentNullException(nameof(keys)); if (keys.Count == 0) return new List().AsValueTaskResult(); - return NativeAsync.MGetAsync(keys.ToArray(), cancellationToken).Await(value => ParseGetValuesResult(value)); + return NativeAsync.MGetAsync(keys.ToArray(), token).Await(value => ParseGetValuesResult(value)); } - ValueTask> IRedisClientAsync.GetValuesMapAsync(List keys, CancellationToken cancellationToken) + ValueTask> IRedisClientAsync.GetValuesMapAsync(List keys, CancellationToken token) { if (keys == null) throw new ArgumentNullException(nameof(keys)); if (keys.Count == 0) return new Dictionary().AsValueTaskResult(); var keysArray = keys.ToArray(); - return NativeAsync.MGetAsync(keysArray, cancellationToken).Await((resultBytesArray, state) => ParseGetValuesMapResult(state, resultBytesArray), keysArray); + return NativeAsync.MGetAsync(keysArray, token).Await((resultBytesArray, state) => ParseGetValuesMapResult(state, resultBytesArray), keysArray); } - ValueTask> IRedisClientAsync.GetValuesMapAsync(List keys, CancellationToken cancellationToken) + ValueTask> IRedisClientAsync.GetValuesMapAsync(List keys, CancellationToken token) { if (keys == null) throw new ArgumentNullException(nameof(keys)); if (keys.Count == 0) return new Dictionary().AsValueTaskResult(); var keysArray = keys.ToArray(); - return NativeAsync.MGetAsync(keysArray, cancellationToken).Await((resultBytesArray, state) => ParseGetValuesMapResult(state, resultBytesArray), keysArray); + return NativeAsync.MGetAsync(keysArray, token).Await((resultBytesArray, state) => ParseGetValuesMapResult(state, resultBytesArray), keysArray); } - ValueTask IRedisClientAsync.AcquireLockAsync(string key, TimeSpan? timeOut, CancellationToken cancellationToken) - => RedisLock.CreateAsync(this, key, timeOut, cancellationToken).Await(value => value); + ValueTask IRedisClientAsync.AcquireLockAsync(string key, TimeSpan? timeOut, CancellationToken token) + => RedisLock.CreateAsync(this, key, timeOut, token).Await(value => value); - ValueTask IRedisClientAsync.SetValueAsync(string key, string value, TimeSpan expireIn, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.SetValueAsync(string key, string value, TimeSpan expireIn, CancellationToken token) { var bytesValue = value?.ToUtf8Bytes(); @@ -292,11 +292,11 @@ ValueTask IRedisClientAsync.SetValueAsync(string key, string value, TimeSpan exp { PickTime(expireIn, out var seconds, out var milliseconds); return NativeAsync.SetAsync(key, bytesValue, expirySeconds: seconds, - expiryMilliseconds: milliseconds, cancellationToken: cancellationToken); + expiryMilliseconds: milliseconds, token: token); } else { - return NativeAsync.SetExAsync(key, (int)expireIn.TotalSeconds, bytesValue, cancellationToken); + return NativeAsync.SetExAsync(key, (int)expireIn.TotalSeconds, bytesValue, token); } } @@ -316,30 +316,30 @@ static void PickTime(TimeSpan? value, out long expirySeconds, out long expiryMil } } } - ValueTask IRedisClientAsync.SetValueIfNotExistsAsync(string key, string value, TimeSpan? expireIn, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.SetValueIfNotExistsAsync(string key, string value, TimeSpan? expireIn, CancellationToken token) { var bytesValue = value?.ToUtf8Bytes(); PickTime(expireIn, out var seconds, out var milliseconds); - return NativeAsync.SetAsync(key, bytesValue, false, seconds, milliseconds, cancellationToken); + return NativeAsync.SetAsync(key, bytesValue, false, seconds, milliseconds, token); } - ValueTask IRedisClientAsync.SetValueIfExistsAsync(string key, string value, TimeSpan? expireIn, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.SetValueIfExistsAsync(string key, string value, TimeSpan? expireIn, CancellationToken token) { var bytesValue = value?.ToUtf8Bytes(); PickTime(expireIn, out var seconds, out var milliseconds); - return NativeAsync.SetAsync(key, bytesValue, true, seconds, milliseconds, cancellationToken); + return NativeAsync.SetAsync(key, bytesValue, true, seconds, milliseconds, token); } - ValueTask IRedisClientAsync.WatchAsync(string[] keys, CancellationToken cancellationToken) - => NativeAsync.WatchAsync(keys, cancellationToken); + ValueTask IRedisClientAsync.WatchAsync(string[] keys, CancellationToken token) + => NativeAsync.WatchAsync(keys, token); - ValueTask IRedisClientAsync.UnWatchAsync(CancellationToken cancellationToken) - => NativeAsync.UnWatchAsync(cancellationToken); + ValueTask IRedisClientAsync.UnWatchAsync(CancellationToken token) + => NativeAsync.UnWatchAsync(token); - ValueTask IRedisClientAsync.AppendToValueAsync(string key, string value, CancellationToken cancellationToken) - => NativeAsync.AppendAsync(key, value.ToUtf8Bytes(), cancellationToken); + ValueTask IRedisClientAsync.AppendToValueAsync(string key, string value, CancellationToken token) + => NativeAsync.AppendAsync(key, value.ToUtf8Bytes(), token); - async ValueTask IRedisClientAsync.StoreObjectAsync(object entity, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.StoreObjectAsync(object entity, CancellationToken token) { if (entity == null) throw new ArgumentNullException(nameof(entity)); @@ -348,28 +348,28 @@ async ValueTask IRedisClientAsync.StoreObjectAsync(object entity, Cancel var urnKey = UrnKey(entityType, id); var valueString = JsonSerializer.SerializeToString(entity); - await ((IRedisClientAsync)this).SetValueAsync(urnKey, valueString, cancellationToken).ConfigureAwait(false); + await ((IRedisClientAsync)this).SetValueAsync(urnKey, valueString, token).ConfigureAwait(false); - await RegisterTypeIdAsync(GetTypeIdsSetKey(entityType), id.ToString(), cancellationToken).ConfigureAwait(false); + await RegisterTypeIdAsync(GetTypeIdsSetKey(entityType), id.ToString(), token).ConfigureAwait(false); return entity; } - ValueTask IRedisClientAsync.PopItemFromSetAsync(string setId, CancellationToken cancellationToken) - => NativeAsync.SPopAsync(setId, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.PopItemFromSetAsync(string setId, CancellationToken token) + => NativeAsync.SPopAsync(setId, token).FromUtf8BytesAsync(); - ValueTask> IRedisClientAsync.PopItemsFromSetAsync(string setId, int count, CancellationToken cancellationToken) - => NativeAsync.SPopAsync(setId, count, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.PopItemsFromSetAsync(string setId, int count, CancellationToken token) + => NativeAsync.SPopAsync(setId, count, token).ToStringListAsync(); - ValueTask IRedisClientAsync.SlowlogResetAsync(CancellationToken cancellationToken) - => NativeAsync.SlowlogResetAsync(cancellationToken); + ValueTask IRedisClientAsync.SlowlogResetAsync(CancellationToken token) + => NativeAsync.SlowlogResetAsync(token); - ValueTask IRedisClientAsync.GetSlowlogAsync(int? numberOfRecords, CancellationToken cancellationToken) - => NativeAsync.SlowlogGetAsync(numberOfRecords, cancellationToken).Await(data => ParseSlowlog(data)); + ValueTask IRedisClientAsync.GetSlowlogAsync(int? numberOfRecords, CancellationToken token) + => NativeAsync.SlowlogGetAsync(numberOfRecords, token).Await(data => ParseSlowlog(data)); - Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) - => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), cancellationToken: cancellationToken)).AwaitAsTrueTask(); + Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken token) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), token: token)).AwaitAsTrueTask(); ValueTask IAsyncDisposable.DisposeAsync() { @@ -377,32 +377,32 @@ ValueTask IAsyncDisposable.DisposeAsync() return default; } - ValueTask IRedisClientAsync.GetSortedSetCountAsync(string setId, CancellationToken cancellationToken) - => NativeAsync.ZCardAsync(setId, cancellationToken); + ValueTask IRedisClientAsync.GetSortedSetCountAsync(string setId, CancellationToken token) + => NativeAsync.ZCardAsync(setId, token); - ValueTask IRedisClientAsync.GetSortedSetCountAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.GetSortedSetCountAsync(string setId, string fromStringScore, string toStringScore, CancellationToken token) { var fromScore = GetLexicalScore(fromStringScore); var toScore = GetLexicalScore(toStringScore); - return AsAsync().GetSortedSetCountAsync(setId, fromScore, toScore, cancellationToken); + return AsAsync().GetSortedSetCountAsync(setId, fromScore, toScore, token); } - ValueTask IRedisClientAsync.GetSortedSetCountAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) - => NativeAsync.ZCountAsync(setId, fromScore, toScore, cancellationToken); + ValueTask IRedisClientAsync.GetSortedSetCountAsync(string setId, double fromScore, double toScore, CancellationToken token) + => NativeAsync.ZCountAsync(setId, fromScore, toScore, token); - ValueTask IRedisClientAsync.GetSortedSetCountAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) - => NativeAsync.ZCountAsync(setId, fromScore, toScore, cancellationToken); + ValueTask IRedisClientAsync.GetSortedSetCountAsync(string setId, long fromScore, long toScore, CancellationToken token) + => NativeAsync.ZCountAsync(setId, fromScore, toScore, token); - ValueTask IRedisClientAsync.GetItemScoreInSortedSetAsync(string setId, string value, CancellationToken cancellationToken) - => NativeAsync.ZScoreAsync(setId, value.ToUtf8Bytes(), cancellationToken); + ValueTask IRedisClientAsync.GetItemScoreInSortedSetAsync(string setId, string value, CancellationToken token) + => NativeAsync.ZScoreAsync(setId, value.ToUtf8Bytes(), token); - ValueTask IRedisClientAsync.CustomAsync(object[] cmdWithArgs, CancellationToken cancellationToken) - => RawCommandAsync(cancellationToken, cmdWithArgs).Await(result => result.ToRedisText()); + ValueTask IRedisClientAsync.CustomAsync(object[] cmdWithArgs, CancellationToken token) + => RawCommandAsync(token, cmdWithArgs).Await(result => result.ToRedisText()); - ValueTask IRedisClientAsync.SetValuesAsync(IDictionary map, CancellationToken cancellationToken) - => ((IRedisClientAsync)this).SetAllAsync(map, cancellationToken); + ValueTask IRedisClientAsync.SetValuesAsync(IDictionary map, CancellationToken token) + => ((IRedisClientAsync)this).SetAllAsync(map, token); - Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken token) { AssertNotInTransaction(); return ExecAsync(async r => @@ -411,7 +411,7 @@ Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt await r.ExpireEntryAtAsync(key, ConvertToServerDate(expiresAt)).ConfigureAwait(false); }).AwaitAsTrueTask(); } - Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) { if (AssertServerVersionNumber() >= 2600) { @@ -423,155 +423,155 @@ Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn } } - Task ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) - => NativeAsync.FlushAllAsync(cancellationToken).AsTask(); + Task ICacheClientAsync.FlushAllAsync(CancellationToken token) + => NativeAsync.FlushAllAsync(token).AsTask(); - Task> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) + Task> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken token) { return ExecAsync(r => { var keysArray = keys.ToArray(); - return ((IRedisNativeClientAsync)r).MGetAsync(keysArray, cancellationToken).Await((keyValues, state) => ProcessGetAllResult(state, keyValues), keysArray); + return ((IRedisNativeClientAsync)r).MGetAsync(keysArray, token).Await((keyValues, state) => ProcessGetAllResult(state, keyValues), keysArray); }).AsTask(); } - Task ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) - => NativeAsync.DelAsync(key, cancellationToken).IsSuccessTaskAsync(); + Task ICacheClientAsync.RemoveAsync(string key, CancellationToken token) + => NativeAsync.DelAsync(key, token).IsSuccessTaskAsync(); - IAsyncEnumerable ICacheClientAsync.GetKeysByPatternAsync(string pattern, CancellationToken cancellationToken) - => AsAsync().ScanAllKeysAsync(pattern, cancellationToken: cancellationToken); + IAsyncEnumerable ICacheClientAsync.GetKeysByPatternAsync(string pattern, CancellationToken token) + => AsAsync().ScanAllKeysAsync(pattern, token: token); - Task ICacheClientAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationToken) + Task ICacheClientAsync.RemoveExpiredEntriesAsync(CancellationToken token) { //Redis automatically removed expired Cache Entries return Task.CompletedTask; } - async Task IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, CancellationToken cancellationToken) + async Task IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, CancellationToken token) { List buffer = null; const int BATCH_SIZE = 1024; - await foreach (var key in AsAsync().ScanAllKeysAsync(pattern, cancellationToken: cancellationToken).WithCancellation(cancellationToken).ConfigureAwait(false)) + await foreach (var key in AsAsync().ScanAllKeysAsync(pattern, token: token).WithCancellation(token).ConfigureAwait(false)) { (buffer ??= new List()).Add(key); if (buffer.Count == BATCH_SIZE) { - await NativeAsync.DelAsync(buffer.ToArray(), cancellationToken).ConfigureAwait(false); + await NativeAsync.DelAsync(buffer.ToArray(), token).ConfigureAwait(false); buffer.Clear(); } } if (buffer is object && buffer.Count != 0) { - await NativeAsync.DelAsync(buffer.ToArray(), cancellationToken).ConfigureAwait(false); + await NativeAsync.DelAsync(buffer.ToArray(), token).ConfigureAwait(false); } } - Task IRemoveByPatternAsync.RemoveByRegexAsync(string regex, CancellationToken cancellationToken) - => AsAsync().RemoveByPatternAsync(RegexToGlob(regex), cancellationToken); + Task IRemoveByPatternAsync.RemoveByRegexAsync(string regex, CancellationToken token) + => AsAsync().RemoveByPatternAsync(RegexToGlob(regex), token); - Task ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) - => ExecAsync(r => r.RemoveEntryAsync(keys.ToArray(), cancellationToken)).AsTask(); + Task ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken token) + => ExecAsync(r => r.RemoveEntryAsync(keys.ToArray(), token)).AsTask(); - Task ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) - => ExecAsync(r => r.IncrementValueByAsync(key, (int)amount, cancellationToken)).AsTask(); + Task ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken token) + => ExecAsync(r => r.IncrementValueByAsync(key, (int)amount, token)).AsTask(); - Task ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) - => ExecAsync(r => r.DecrementValueByAsync(key, (int)amount, cancellationToken)).AsTask(); + Task ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken token) + => ExecAsync(r => r.DecrementValueByAsync(key, (int)amount, token)).AsTask(); - Task ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) - => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: false, cancellationToken: cancellationToken)).AsTask(); + Task ICacheClientAsync.AddAsync(string key, T value, CancellationToken token) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: false, token: token)).AsTask(); - Task ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) - => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: true, cancellationToken: cancellationToken)).AsTask(); + Task ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken token) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: true, token: token)).AsTask(); - Task ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + Task ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken token) { AssertNotInTransaction(); return ExecAsync(async r => { - if (await r.AddAsync(key, value, cancellationToken).ConfigureAwait(false)) + if (await r.AddAsync(key, value, token).ConfigureAwait(false)) { - await r.ExpireEntryAtAsync(key, ConvertToServerDate(expiresAt), cancellationToken).ConfigureAwait(false); + await r.ExpireEntryAtAsync(key, ConvertToServerDate(expiresAt), token).ConfigureAwait(false); return true; } return false; }).AsTask(); } - Task ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + Task ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken token) { AssertNotInTransaction(); return ExecAsync(async r => { - if (await r.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false)) + if (await r.ReplaceAsync(key, value, token).ConfigureAwait(false)) { - await r.ExpireEntryAtAsync(key, ConvertToServerDate(expiresAt), cancellationToken).ConfigureAwait(false); + await r.ExpireEntryAtAsync(key, ConvertToServerDate(expiresAt), token).ConfigureAwait(false); return true; } return false; }).AsTask(); } - Task ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) - => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: false, cancellationToken: cancellationToken)).AsTask(); + Task ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: false, token: token)).AsTask(); - Task ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) - => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: true, cancellationToken: cancellationToken)).AsTask(); + Task ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) + => ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), exists: true, token: token)).AsTask(); - ValueTask IRedisClientAsync.DbSizeAsync(CancellationToken cancellationToken) - => NativeAsync.DbSizeAsync(cancellationToken); + ValueTask IRedisClientAsync.DbSizeAsync(CancellationToken token) + => NativeAsync.DbSizeAsync(token); - ValueTask> IRedisClientAsync.InfoAsync(CancellationToken cancellationToken) - => NativeAsync.InfoAsync(cancellationToken); + ValueTask> IRedisClientAsync.InfoAsync(CancellationToken token) + => NativeAsync.InfoAsync(token); - ValueTask IRedisClientAsync.LastSaveAsync(CancellationToken cancellationToken) - => NativeAsync.LastSaveAsync(cancellationToken); + ValueTask IRedisClientAsync.LastSaveAsync(CancellationToken token) + => NativeAsync.LastSaveAsync(token); - async Task IEntityStoreAsync.GetByIdAsync(object id, CancellationToken cancellationToken) + async Task IEntityStoreAsync.GetByIdAsync(object id, CancellationToken token) { var key = UrnKey(id); - var valueString = await AsAsync().GetValueAsync(key, cancellationToken).ConfigureAwait(false); + var valueString = await AsAsync().GetValueAsync(key, token).ConfigureAwait(false); var value = JsonSerializer.DeserializeFromString(valueString); return value; } - async Task> IEntityStoreAsync.GetByIdsAsync(ICollection ids, CancellationToken cancellationToken) + async Task> IEntityStoreAsync.GetByIdsAsync(ICollection ids, CancellationToken token) { if (ids == null || ids.Count == 0) return new List(); var urnKeys = ids.Cast().Map(UrnKey); - return await AsAsync().GetValuesAsync(urnKeys, cancellationToken).ConfigureAwait(false); + return await AsAsync().GetValuesAsync(urnKeys, token).ConfigureAwait(false); } - async Task IEntityStoreAsync.StoreAsync(T entity, CancellationToken cancellationToken) + async Task IEntityStoreAsync.StoreAsync(T entity, CancellationToken token) { var urnKey = UrnKey(entity); var valueString = JsonSerializer.SerializeToString(entity); - await AsAsync().SetValueAsync(urnKey, valueString, cancellationToken).ConfigureAwait(false); - await RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); + await AsAsync().SetValueAsync(urnKey, valueString, token).ConfigureAwait(false); + await RegisterTypeIdAsync(entity, token).ConfigureAwait(false); return entity; } - Task IEntityStoreAsync.StoreAllAsync(IEnumerable entities, CancellationToken cancellationToken) - => StoreAllAsyncImpl(entities, cancellationToken).AsTask(); + Task IEntityStoreAsync.StoreAllAsync(IEnumerable entities, CancellationToken token) + => StoreAllAsyncImpl(entities, token).AsTask(); - internal async ValueTask StoreAllAsyncImpl(IEnumerable entities, CancellationToken cancellationToken) + internal async ValueTask StoreAllAsyncImpl(IEnumerable entities, CancellationToken token) { if (PrepareStoreAll(entities, out var keys, out var values, out var entitiesList)) { - await NativeAsync.MSetAsync(keys, values, cancellationToken).ConfigureAwait(false); - await RegisterTypeIdsAsync(entitiesList, cancellationToken).ConfigureAwait(false); + await NativeAsync.MSetAsync(keys, values, token).ConfigureAwait(false); + await RegisterTypeIdsAsync(entitiesList, token).ConfigureAwait(false); } } - internal ValueTask RegisterTypeIdsAsync(IEnumerable values, CancellationToken cancellationToken) + internal ValueTask RegisterTypeIdsAsync(IEnumerable values, CancellationToken token) { var typeIdsSetKey = GetTypeIdsSetKey(); var ids = values.Map(x => x.GetId().ToString()); @@ -584,11 +584,11 @@ internal ValueTask RegisterTypeIdsAsync(IEnumerable values, CancellationTo } else { - return AsAsync().AddRangeToSetAsync(typeIdsSetKey, ids, cancellationToken); + return AsAsync().AddRangeToSetAsync(typeIdsSetKey, ids, token); } } - internal async ValueTask RemoveTypeIdsAsync(T[] values, CancellationToken cancellationToken) + internal async ValueTask RemoveTypeIdsAsync(T[] values, CancellationToken token) { var typeIdsSetKey = GetTypeIdsSetKey(); if (this.Pipeline != null) @@ -600,12 +600,12 @@ internal async ValueTask RemoveTypeIdsAsync(T[] values, CancellationToken can { foreach (var x in values) { - await AsAsync().RemoveItemFromSetAsync(typeIdsSetKey, x.GetId().ToString(), cancellationToken).ConfigureAwait(false); + await AsAsync().RemoveItemFromSetAsync(typeIdsSetKey, x.GetId().ToString(), token).ConfigureAwait(false); } } } - internal async ValueTask RemoveTypeIdsAsync(string[] ids, CancellationToken cancellationToken) + internal async ValueTask RemoveTypeIdsAsync(string[] ids, CancellationToken token) { var typeIdsSetKey = GetTypeIdsSetKey(); if (this.Pipeline != null) @@ -617,80 +617,80 @@ internal async ValueTask RemoveTypeIdsAsync(string[] ids, CancellationToken c { foreach (var x in ids) { - await AsAsync().RemoveItemFromSetAsync(typeIdsSetKey, x, cancellationToken).ConfigureAwait(false); + await AsAsync().RemoveItemFromSetAsync(typeIdsSetKey, x, token).ConfigureAwait(false); } } } - async Task IEntityStoreAsync.DeleteAsync(T entity, CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteAsync(T entity, CancellationToken token) { var urnKey = UrnKey(entity); - await AsAsync().RemoveAsync(urnKey, cancellationToken).ConfigureAwait(false); - await this.RemoveTypeIdsAsync(new[] { entity }, cancellationToken).ConfigureAwait(false); + await AsAsync().RemoveAsync(urnKey, token).ConfigureAwait(false); + await this.RemoveTypeIdsAsync(new[] { entity }, token).ConfigureAwait(false); } - async Task IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken token) { var urnKey = UrnKey(id); - await AsAsync().RemoveAsync(urnKey, cancellationToken).ConfigureAwait(false); - await this.RemoveTypeIdsAsync(new[] { id.ToString() }, cancellationToken).ConfigureAwait(false); + await AsAsync().RemoveAsync(urnKey, token).ConfigureAwait(false); + await this.RemoveTypeIdsAsync(new[] { id.ToString() }, token).ConfigureAwait(false); } - async Task IEntityStoreAsync.DeleteByIdsAsync(ICollection ids, CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteByIdsAsync(ICollection ids, CancellationToken token) { if (ids == null || ids.Count == 0) return; var idsList = ids.Cast(); var urnKeys = idsList.Map(UrnKey); - await AsAsync().RemoveEntryAsync(urnKeys.ToArray(), cancellationToken).ConfigureAwait(false); - await this.RemoveTypeIdsAsync(idsList.Map(x => x.ToString()).ToArray(), cancellationToken).ConfigureAwait(false); + await AsAsync().RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); + await this.RemoveTypeIdsAsync(idsList.Map(x => x.ToString()).ToArray(), token).ConfigureAwait(false); } - async Task IEntityStoreAsync.DeleteAllAsync(CancellationToken cancellationToken) + async Task IEntityStoreAsync.DeleteAllAsync(CancellationToken token) { var typeIdsSetKey = this.GetTypeIdsSetKey(); - var ids = await AsAsync().GetAllItemsFromSetAsync(typeIdsSetKey, cancellationToken).ConfigureAwait(false); + var ids = await AsAsync().GetAllItemsFromSetAsync(typeIdsSetKey, token).ConfigureAwait(false); if (ids.Count > 0) { var urnKeys = ids.ToList().ConvertAll(UrnKey); - await AsAsync().RemoveEntryAsync(urnKeys.ToArray(), cancellationToken).ConfigureAwait(false); - await AsAsync().RemoveAsync(typeIdsSetKey, cancellationToken).ConfigureAwait(false); + await AsAsync().RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); + await AsAsync().RemoveAsync(typeIdsSetKey, token).ConfigureAwait(false); } } - ValueTask> IRedisClientAsync.SearchSortedSetAsync(string setId, string start, string end, int? skip, int? take, CancellationToken cancellationToken) + ValueTask> IRedisClientAsync.SearchSortedSetAsync(string setId, string start, string end, int? skip, int? take, CancellationToken token) { start = GetSearchStart(start); end = GetSearchEnd(end); - return NativeAsync.ZRangeByLexAsync(setId, start, end, skip, take, cancellationToken).ToStringListAsync(); + return NativeAsync.ZRangeByLexAsync(setId, start, end, skip, take, token).ToStringListAsync(); } - ValueTask IRedisClientAsync.SearchSortedSetCountAsync(string setId, string start, string end, CancellationToken cancellationToken) - => NativeAsync.ZLexCountAsync(setId, GetSearchStart(start), GetSearchEnd(end), cancellationToken); + ValueTask IRedisClientAsync.SearchSortedSetCountAsync(string setId, string start, string end, CancellationToken token) + => NativeAsync.ZLexCountAsync(setId, GetSearchStart(start), GetSearchEnd(end), token); - ValueTask IRedisClientAsync.RemoveRangeFromSortedSetBySearchAsync(string setId, string start, string end, CancellationToken cancellationToken) - => NativeAsync.ZRemRangeByLexAsync(setId, GetSearchStart(start), GetSearchEnd(end), cancellationToken); + ValueTask IRedisClientAsync.RemoveRangeFromSortedSetBySearchAsync(string setId, string start, string end, CancellationToken token) + => NativeAsync.ZRemRangeByLexAsync(setId, GetSearchStart(start), GetSearchEnd(end), token); - ValueTask IRedisClientAsync.TypeAsync(string key, CancellationToken cancellationToken) - => NativeAsync.TypeAsync(key, cancellationToken); + ValueTask IRedisClientAsync.TypeAsync(string key, CancellationToken token) + => NativeAsync.TypeAsync(key, token); - ValueTask IRedisClientAsync.GetStringCountAsync(string key, CancellationToken cancellationToken) - => NativeAsync.StrLenAsync(key, cancellationToken); + ValueTask IRedisClientAsync.GetStringCountAsync(string key, CancellationToken token) + => NativeAsync.StrLenAsync(key, token); - ValueTask IRedisClientAsync.GetSetCountAsync(string setId, CancellationToken cancellationToken) - => NativeAsync.SCardAsync(setId, cancellationToken); + ValueTask IRedisClientAsync.GetSetCountAsync(string setId, CancellationToken token) + => NativeAsync.SCardAsync(setId, token); - ValueTask IRedisClientAsync.GetListCountAsync(string listId, CancellationToken cancellationToken) - => NativeAsync.LLenAsync(listId, cancellationToken); + ValueTask IRedisClientAsync.GetListCountAsync(string listId, CancellationToken token) + => NativeAsync.LLenAsync(listId, token); - ValueTask IRedisClientAsync.GetHashCountAsync(string hashId, CancellationToken cancellationToken) - => NativeAsync.HLenAsync(hashId, cancellationToken); + ValueTask IRedisClientAsync.GetHashCountAsync(string hashId, CancellationToken token) + => NativeAsync.HLenAsync(hashId, token); - async ValueTask IRedisClientAsync.ExecCachedLuaAsync(string scriptBody, Func> scriptSha1, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.ExecCachedLuaAsync(string scriptBody, Func> scriptSha1, CancellationToken token) { if (!CachedLuaSha1Map.TryGetValue(scriptBody, out var sha1)) - CachedLuaSha1Map[scriptBody] = sha1 = await AsAsync().LoadLuaScriptAsync(scriptBody, cancellationToken).ConfigureAwait(false); + CachedLuaSha1Map[scriptBody] = sha1 = await AsAsync().LoadLuaScriptAsync(scriptBody, token).ConfigureAwait(false); try { @@ -701,36 +701,36 @@ async ValueTask IRedisClientAsync.ExecCachedLuaAsync(string scriptBody, Fu if (!ex.Message.StartsWith("NOSCRIPT")) throw; - CachedLuaSha1Map[scriptBody] = sha1 = await AsAsync().LoadLuaScriptAsync(scriptBody, cancellationToken).ConfigureAwait(false); + CachedLuaSha1Map[scriptBody] = sha1 = await AsAsync().LoadLuaScriptAsync(scriptBody, token).ConfigureAwait(false); return await scriptSha1(sha1).ConfigureAwait(false); } } - ValueTask IRedisClientAsync.ExecLuaAsync(string luaBody, string[] keys, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalCommandAsync(luaBody, keys?.Length ?? 0, MergeAndConvertToBytes(keys, args), cancellationToken).Await(data => data.ToRedisText()); + ValueTask IRedisClientAsync.ExecLuaAsync(string luaBody, string[] keys, string[] args, CancellationToken token) + => NativeAsync.EvalCommandAsync(luaBody, keys?.Length ?? 0, MergeAndConvertToBytes(keys, args), token).Await(data => data.ToRedisText()); - ValueTask IRedisClientAsync.ExecLuaShaAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalShaCommandAsync(sha1, keys?.Length ?? 0, MergeAndConvertToBytes(keys, args), cancellationToken).Await(data => data.ToRedisText()); + ValueTask IRedisClientAsync.ExecLuaShaAsync(string sha1, string[] keys, string[] args, CancellationToken token) + => NativeAsync.EvalShaCommandAsync(sha1, keys?.Length ?? 0, MergeAndConvertToBytes(keys, args), token).Await(data => data.ToRedisText()); - ValueTask IRedisClientAsync.ExecLuaAsStringAsync(string luaBody, string[] keys, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalStrAsync(luaBody, keys?.Length ?? 0, MergeAndConvertToBytes(keys, args), cancellationToken); + ValueTask IRedisClientAsync.ExecLuaAsStringAsync(string luaBody, string[] keys, string[] args, CancellationToken token) + => NativeAsync.EvalStrAsync(luaBody, keys?.Length ?? 0, MergeAndConvertToBytes(keys, args), token); - ValueTask IRedisClientAsync.ExecLuaShaAsStringAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalShaStrAsync(sha1, keys?.Length ?? 0, MergeAndConvertToBytes(keys, args), cancellationToken); + ValueTask IRedisClientAsync.ExecLuaShaAsStringAsync(string sha1, string[] keys, string[] args, CancellationToken token) + => NativeAsync.EvalShaStrAsync(sha1, keys?.Length ?? 0, MergeAndConvertToBytes(keys, args), token); - ValueTask IRedisClientAsync.LoadLuaScriptAsync(string body, CancellationToken cancellationToken) - => NativeAsync.ScriptLoadAsync(body, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.LoadLuaScriptAsync(string body, CancellationToken token) + => NativeAsync.ScriptLoadAsync(body, token).FromUtf8BytesAsync(); - ValueTask IRedisClientAsync.WriteAllAsync(IEnumerable entities, CancellationToken cancellationToken) - => PrepareWriteAll(entities, out var keys, out var values) ? NativeAsync.MSetAsync(keys, values, cancellationToken) : default; + ValueTask IRedisClientAsync.WriteAllAsync(IEnumerable entities, CancellationToken token) + => PrepareWriteAll(entities, out var keys, out var values) ? NativeAsync.MSetAsync(keys, values, token) : default; - async ValueTask> IRedisClientAsync.GetAllItemsFromSetAsync(string setId, CancellationToken cancellationToken) + async ValueTask> IRedisClientAsync.GetAllItemsFromSetAsync(string setId, CancellationToken token) { - var multiDataList = await NativeAsync.SMembersAsync(setId, cancellationToken).ConfigureAwait(false); + var multiDataList = await NativeAsync.SMembersAsync(setId, token).ConfigureAwait(false); return CreateHashSet(multiDataList); } - async ValueTask IRedisClientAsync.AddRangeToSetAsync(string setId, List items, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.AddRangeToSetAsync(string setId, List items, CancellationToken token) { if (await AddRangeToSetNeedsSendAsync(setId, items).ConfigureAwait(false)) { @@ -740,10 +740,10 @@ async ValueTask IRedisClientAsync.AddRangeToSetAsync(string setId, List { pipeline.WriteCommand(Commands.SAdd, uSetId, item.ToUtf8Bytes()); } - await pipeline.FlushAsync(cancellationToken).ConfigureAwait(false); + await pipeline.FlushAsync(token).ConfigureAwait(false); //the number of items after - _ = await pipeline.ReadAllAsIntsAsync(cancellationToken).ConfigureAwait(false); + _ = await pipeline.ReadAllAsIntsAsync(token).ConfigureAwait(false); } } @@ -781,114 +781,114 @@ async ValueTask AddRangeToSetNeedsSendAsync(string setId, List ite } } - ValueTask IRedisClientAsync.RemoveItemFromSetAsync(string setId, string item, CancellationToken cancellationToken) - => NativeAsync.SRemAsync(setId, item.ToUtf8Bytes(), cancellationToken).Await(); + ValueTask IRedisClientAsync.RemoveItemFromSetAsync(string setId, string item, CancellationToken token) + => NativeAsync.SRemAsync(setId, item.ToUtf8Bytes(), token).Await(); - ValueTask IRedisClientAsync.IncrementValueByAsync(string key, int count, CancellationToken cancellationToken) - => NativeAsync.IncrByAsync(key, count, cancellationToken); + ValueTask IRedisClientAsync.IncrementValueByAsync(string key, int count, CancellationToken token) + => NativeAsync.IncrByAsync(key, count, token); - ValueTask IRedisClientAsync.IncrementValueByAsync(string key, long count, CancellationToken cancellationToken) - => NativeAsync.IncrByAsync(key, count, cancellationToken); + ValueTask IRedisClientAsync.IncrementValueByAsync(string key, long count, CancellationToken token) + => NativeAsync.IncrByAsync(key, count, token); - ValueTask IRedisClientAsync.IncrementValueByAsync(string key, double count, CancellationToken cancellationToken) - => NativeAsync.IncrByFloatAsync(key, count, cancellationToken); - ValueTask IRedisClientAsync.IncrementValueAsync(string key, CancellationToken cancellationToken) - => NativeAsync.IncrAsync(key, cancellationToken); + ValueTask IRedisClientAsync.IncrementValueByAsync(string key, double count, CancellationToken token) + => NativeAsync.IncrByFloatAsync(key, count, token); + ValueTask IRedisClientAsync.IncrementValueAsync(string key, CancellationToken token) + => NativeAsync.IncrAsync(key, token); - ValueTask IRedisClientAsync.DecrementValueAsync(string key, CancellationToken cancellationToken) - => NativeAsync.DecrAsync(key, cancellationToken); + ValueTask IRedisClientAsync.DecrementValueAsync(string key, CancellationToken token) + => NativeAsync.DecrAsync(key, token); - ValueTask IRedisClientAsync.DecrementValueByAsync(string key, int count, CancellationToken cancellationToken) - => NativeAsync.DecrByAsync(key, count, cancellationToken); + ValueTask IRedisClientAsync.DecrementValueByAsync(string key, int count, CancellationToken token) + => NativeAsync.DecrByAsync(key, count, token); - async ValueTask IRedisClientAsync.GetServerRoleAsync(CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.GetServerRoleAsync(CancellationToken token) { if (AssertServerVersionNumber() >= 2812) { - var text = await NativeAsync.RoleAsync(cancellationToken).ConfigureAwait(false); + var text = await NativeAsync.RoleAsync(token).ConfigureAwait(false); var roleName = text.Children[0].Text; return ToServerRole(roleName); } - var info = await AsAsync().InfoAsync(cancellationToken).ConfigureAwait(false); + var info = await AsAsync().InfoAsync(token).ConfigureAwait(false); info.TryGetValue("role", out var role); return ToServerRole(role); } - ValueTask IRedisClientAsync.GetServerRoleInfoAsync(CancellationToken cancellationToken) - => NativeAsync.RoleAsync(cancellationToken); + ValueTask IRedisClientAsync.GetServerRoleInfoAsync(CancellationToken token) + => NativeAsync.RoleAsync(token); - async ValueTask IRedisClientAsync.GetConfigAsync(string configItem, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.GetConfigAsync(string configItem, CancellationToken token) { - var byteArray = await NativeAsync.ConfigGetAsync(configItem, cancellationToken).ConfigureAwait(false); + var byteArray = await NativeAsync.ConfigGetAsync(configItem, token).ConfigureAwait(false); return GetConfigParse(byteArray); } - ValueTask IRedisClientAsync.SetConfigAsync(string configItem, string value, CancellationToken cancellationToken) - => NativeAsync.ConfigSetAsync(configItem, value.ToUtf8Bytes(), cancellationToken); + ValueTask IRedisClientAsync.SetConfigAsync(string configItem, string value, CancellationToken token) + => NativeAsync.ConfigSetAsync(configItem, value.ToUtf8Bytes(), token); - ValueTask IRedisClientAsync.SaveConfigAsync(CancellationToken cancellationToken) - => NativeAsync.ConfigRewriteAsync(cancellationToken); + ValueTask IRedisClientAsync.SaveConfigAsync(CancellationToken token) + => NativeAsync.ConfigRewriteAsync(token); - ValueTask IRedisClientAsync.ResetInfoStatsAsync(CancellationToken cancellationToken) - => NativeAsync.ConfigResetStatAsync(cancellationToken); + ValueTask IRedisClientAsync.ResetInfoStatsAsync(CancellationToken token) + => NativeAsync.ConfigResetStatAsync(token); - ValueTask IRedisClientAsync.GetClientAsync(CancellationToken cancellationToken) - => NativeAsync.ClientGetNameAsync(cancellationToken); + ValueTask IRedisClientAsync.GetClientAsync(CancellationToken token) + => NativeAsync.ClientGetNameAsync(token); - ValueTask IRedisClientAsync.SetClientAsync(string name, CancellationToken cancellationToken) - => NativeAsync.ClientSetNameAsync(name, cancellationToken); + ValueTask IRedisClientAsync.SetClientAsync(string name, CancellationToken token) + => NativeAsync.ClientSetNameAsync(name, token); - ValueTask IRedisClientAsync.KillClientAsync(string address, CancellationToken cancellationToken) - => NativeAsync.ClientKillAsync(address, cancellationToken); + ValueTask IRedisClientAsync.KillClientAsync(string address, CancellationToken token) + => NativeAsync.ClientKillAsync(address, token); - ValueTask IRedisClientAsync.KillClientsAsync(string fromAddress, string withId, RedisClientType? ofType, bool? skipMe, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.KillClientsAsync(string fromAddress, string withId, RedisClientType? ofType, bool? skipMe, CancellationToken token) { var typeString = ofType?.ToString().ToLower(); var skipMeString = skipMe.HasValue ? (skipMe.Value ? "yes" : "no") : null; - return NativeAsync.ClientKillAsync(addr: fromAddress, id: withId, type: typeString, skipMe: skipMeString, cancellationToken); + return NativeAsync.ClientKillAsync(addr: fromAddress, id: withId, type: typeString, skipMe: skipMeString, token); } - async ValueTask>> IRedisClientAsync.GetClientsInfoAsync(CancellationToken cancellationToken) - => GetClientsInfoParse(await NativeAsync.ClientListAsync(cancellationToken).ConfigureAwait(false)); + async ValueTask>> IRedisClientAsync.GetClientsInfoAsync(CancellationToken token) + => GetClientsInfoParse(await NativeAsync.ClientListAsync(token).ConfigureAwait(false)); - ValueTask IRedisClientAsync.PauseAllClientsAsync(TimeSpan duration, CancellationToken cancellationToken) - => NativeAsync.ClientPauseAsync((int)duration.TotalMilliseconds, cancellationToken); + ValueTask IRedisClientAsync.PauseAllClientsAsync(TimeSpan duration, CancellationToken token) + => NativeAsync.ClientPauseAsync((int)duration.TotalMilliseconds, token); - ValueTask> IRedisClientAsync.GetAllKeysAsync(CancellationToken cancellationToken) - => AsAsync().SearchKeysAsync("*", cancellationToken); + ValueTask> IRedisClientAsync.GetAllKeysAsync(CancellationToken token) + => AsAsync().SearchKeysAsync("*", token); - ValueTask IRedisClientAsync.GetAndSetValueAsync(string key, string value, CancellationToken cancellationToken) - => NativeAsync.GetSetAsync(key, value.ToUtf8Bytes(), cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.GetAndSetValueAsync(string key, string value, CancellationToken token) + => NativeAsync.GetSetAsync(key, value.ToUtf8Bytes(), token).FromUtf8BytesAsync(); - async ValueTask IRedisClientAsync.GetFromHashAsync(object id, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.GetFromHashAsync(object id, CancellationToken token) { var key = UrnKey(id); - return (await AsAsync().GetAllEntriesFromHashAsync(key, cancellationToken).ConfigureAwait(false)).ToJson().FromJson(); + return (await AsAsync().GetAllEntriesFromHashAsync(key, token).ConfigureAwait(false)).ToJson().FromJson(); } - async ValueTask IRedisClientAsync.StoreAsHashAsync(T entity, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.StoreAsHashAsync(T entity, CancellationToken token) { var key = UrnKey(entity); var hash = ConvertToHashFn(entity); - await AsAsync().SetRangeInHashAsync(key, hash, cancellationToken).ConfigureAwait(false); - await RegisterTypeIdAsync(entity, cancellationToken).ConfigureAwait(false); + await AsAsync().SetRangeInHashAsync(key, hash, token).ConfigureAwait(false); + await RegisterTypeIdAsync(entity, token).ConfigureAwait(false); } - ValueTask> IRedisClientAsync.GetSortedEntryValuesAsync(string setId, int startingFrom, int endingAt, CancellationToken cancellationToken) + ValueTask> IRedisClientAsync.GetSortedEntryValuesAsync(string setId, int startingFrom, int endingAt, CancellationToken token) { var sortOptions = new SortOptions { Skip = startingFrom, Take = endingAt, }; - return NativeAsync.SortAsync(setId, sortOptions, cancellationToken).ToStringListAsync(); + return NativeAsync.SortAsync(setId, sortOptions, token).ToStringListAsync(); } - async IAsyncEnumerable IRedisClientAsync.ScanAllSetItemsAsync(string setId, string pattern, int pageSize, [EnumeratorCancellation] CancellationToken cancellationToken) + async IAsyncEnumerable IRedisClientAsync.ScanAllSetItemsAsync(string setId, string pattern, int pageSize, [EnumeratorCancellation] CancellationToken token) { var ret = new ScanResult(); while (true) { ret = await (pattern != null // note ConfigureAwait is handled below - ? NativeAsync.SScanAsync(setId, ret.Cursor, pageSize, match: pattern, cancellationToken: cancellationToken) - : NativeAsync.SScanAsync(setId, ret.Cursor, pageSize, cancellationToken: cancellationToken) + ? NativeAsync.SScanAsync(setId, ret.Cursor, pageSize, match: pattern, token: token) + : NativeAsync.SScanAsync(setId, ret.Cursor, pageSize, token: token) ).ConfigureAwait(false); foreach (var key in ret.Results) @@ -900,14 +900,14 @@ async IAsyncEnumerable IRedisClientAsync.ScanAllSetItemsAsync(string set } } - async IAsyncEnumerable> IRedisClientAsync.ScanAllSortedSetItemsAsync(string setId, string pattern, int pageSize, [EnumeratorCancellation] CancellationToken cancellationToken) + async IAsyncEnumerable> IRedisClientAsync.ScanAllSortedSetItemsAsync(string setId, string pattern, int pageSize, [EnumeratorCancellation] CancellationToken token) { var ret = new ScanResult(); while (true) { ret = await (pattern != null // note ConfigureAwait is handled below - ? NativeAsync.ZScanAsync(setId, ret.Cursor, pageSize, match: pattern, cancellationToken: cancellationToken) - : NativeAsync.ZScanAsync(setId, ret.Cursor, pageSize, cancellationToken: cancellationToken) + ? NativeAsync.ZScanAsync(setId, ret.Cursor, pageSize, match: pattern, token: token) + : NativeAsync.ZScanAsync(setId, ret.Cursor, pageSize, token: token) ).ConfigureAwait(false); foreach (var entry in ret.AsItemsWithScores()) @@ -919,14 +919,14 @@ async IAsyncEnumerable> IRedisClientAsync.ScanAllSo } } - async IAsyncEnumerable> IRedisClientAsync.ScanAllHashEntriesAsync(string hashId, string pattern, int pageSize, [EnumeratorCancellation] CancellationToken cancellationToken) + async IAsyncEnumerable> IRedisClientAsync.ScanAllHashEntriesAsync(string hashId, string pattern, int pageSize, [EnumeratorCancellation] CancellationToken token) { var ret = new ScanResult(); while (true) { ret = await (pattern != null // note ConfigureAwait is handled below - ? NativeAsync.HScanAsync(hashId, ret.Cursor, pageSize, match: pattern, cancellationToken: cancellationToken) - : NativeAsync.HScanAsync(hashId, ret.Cursor, pageSize, cancellationToken: cancellationToken) + ? NativeAsync.HScanAsync(hashId, ret.Cursor, pageSize, match: pattern, token: token) + : NativeAsync.HScanAsync(hashId, ret.Cursor, pageSize, token: token) ).ConfigureAwait(false); foreach (var entry in ret.AsKeyValues()) @@ -938,290 +938,290 @@ async IAsyncEnumerable> IRedisClientAsync.ScanAllHa } } - ValueTask IRedisClientAsync.AddToHyperLogAsync(string key, string[] elements, CancellationToken cancellationToken) - => NativeAsync.PfAddAsync(key, elements.Map(x => x.ToUtf8Bytes()).ToArray(), cancellationToken); + ValueTask IRedisClientAsync.AddToHyperLogAsync(string key, string[] elements, CancellationToken token) + => NativeAsync.PfAddAsync(key, elements.Map(x => x.ToUtf8Bytes()).ToArray(), token); - ValueTask IRedisClientAsync.CountHyperLogAsync(string key, CancellationToken cancellationToken) - => NativeAsync.PfCountAsync(key, cancellationToken); + ValueTask IRedisClientAsync.CountHyperLogAsync(string key, CancellationToken token) + => NativeAsync.PfCountAsync(key, token); - ValueTask IRedisClientAsync.MergeHyperLogsAsync(string toKey, string[] fromKeys, CancellationToken cancellationToken) - => NativeAsync.PfMergeAsync(toKey, fromKeys, cancellationToken); + ValueTask IRedisClientAsync.MergeHyperLogsAsync(string toKey, string[] fromKeys, CancellationToken token) + => NativeAsync.PfMergeAsync(toKey, fromKeys, token); - ValueTask IRedisClientAsync.AddGeoMemberAsync(string key, double longitude, double latitude, string member, CancellationToken cancellationToken) - => NativeAsync.GeoAddAsync(key, longitude, latitude, member, cancellationToken); + ValueTask IRedisClientAsync.AddGeoMemberAsync(string key, double longitude, double latitude, string member, CancellationToken token) + => NativeAsync.GeoAddAsync(key, longitude, latitude, member, token); - ValueTask IRedisClientAsync.AddGeoMembersAsync(string key, RedisGeo[] geoPoints, CancellationToken cancellationToken) - => NativeAsync.GeoAddAsync(key, geoPoints, cancellationToken); + ValueTask IRedisClientAsync.AddGeoMembersAsync(string key, RedisGeo[] geoPoints, CancellationToken token) + => NativeAsync.GeoAddAsync(key, geoPoints, token); - ValueTask IRedisClientAsync.CalculateDistanceBetweenGeoMembersAsync(string key, string fromMember, string toMember, string unit, CancellationToken cancellationToken) - => NativeAsync.GeoDistAsync(key, fromMember, toMember, unit, cancellationToken); + ValueTask IRedisClientAsync.CalculateDistanceBetweenGeoMembersAsync(string key, string fromMember, string toMember, string unit, CancellationToken token) + => NativeAsync.GeoDistAsync(key, fromMember, toMember, unit, token); - ValueTask IRedisClientAsync.GetGeohashesAsync(string key, string[] members, CancellationToken cancellationToken) - => NativeAsync.GeoHashAsync(key, members, cancellationToken); + ValueTask IRedisClientAsync.GetGeohashesAsync(string key, string[] members, CancellationToken token) + => NativeAsync.GeoHashAsync(key, members, token); - ValueTask> IRedisClientAsync.GetGeoCoordinatesAsync(string key, string[] members, CancellationToken cancellationToken) - => NativeAsync.GeoPosAsync(key, members, cancellationToken); + ValueTask> IRedisClientAsync.GetGeoCoordinatesAsync(string key, string[] members, CancellationToken token) + => NativeAsync.GeoPosAsync(key, members, token); - async ValueTask IRedisClientAsync.FindGeoMembersInRadiusAsync(string key, double longitude, double latitude, double radius, string unit, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.FindGeoMembersInRadiusAsync(string key, double longitude, double latitude, double radius, string unit, CancellationToken token) { - var results = await NativeAsync.GeoRadiusAsync(key, longitude, latitude, radius, unit, cancellationToken: cancellationToken).ConfigureAwait(false); + var results = await NativeAsync.GeoRadiusAsync(key, longitude, latitude, radius, unit, token: token).ConfigureAwait(false); return ParseFindGeoMembersResult(results); } - ValueTask> IRedisClientAsync.FindGeoResultsInRadiusAsync(string key, double longitude, double latitude, double radius, string unit, int? count, bool? sortByNearest, CancellationToken cancellationToken) - => NativeAsync.GeoRadiusAsync(key, longitude, latitude, radius, unit, withCoords: true, withDist: true, withHash: true, count: count, asc: sortByNearest, cancellationToken: cancellationToken); + ValueTask> IRedisClientAsync.FindGeoResultsInRadiusAsync(string key, double longitude, double latitude, double radius, string unit, int? count, bool? sortByNearest, CancellationToken token) + => NativeAsync.GeoRadiusAsync(key, longitude, latitude, radius, unit, withCoords: true, withDist: true, withHash: true, count: count, asc: sortByNearest, token: token); - async ValueTask IRedisClientAsync.FindGeoMembersInRadiusAsync(string key, string member, double radius, string unit, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.FindGeoMembersInRadiusAsync(string key, string member, double radius, string unit, CancellationToken token) { - var results = await NativeAsync.GeoRadiusByMemberAsync(key, member, radius, unit, cancellationToken: cancellationToken).ConfigureAwait(false); + var results = await NativeAsync.GeoRadiusByMemberAsync(key, member, radius, unit, token: token).ConfigureAwait(false); return ParseFindGeoMembersResult(results); } - ValueTask> IRedisClientAsync.FindGeoResultsInRadiusAsync(string key, string member, double radius, string unit, int? count, bool? sortByNearest, CancellationToken cancellationToken) - => NativeAsync.GeoRadiusByMemberAsync(key, member, radius, unit, withCoords: true, withDist: true, withHash: true, count: count, asc: sortByNearest, cancellationToken: cancellationToken); + ValueTask> IRedisClientAsync.FindGeoResultsInRadiusAsync(string key, string member, double radius, string unit, int? count, bool? sortByNearest, CancellationToken token) + => NativeAsync.GeoRadiusByMemberAsync(key, member, radius, unit, withCoords: true, withDist: true, withHash: true, count: count, asc: sortByNearest, token: token); - ValueTask IRedisClientAsync.CreateSubscriptionAsync(CancellationToken cancellationToken) + ValueTask IRedisClientAsync.CreateSubscriptionAsync(CancellationToken token) => new RedisSubscription(this).AsValueTaskResult(); - ValueTask IRedisClientAsync.PublishMessageAsync(string toChannel, string message, CancellationToken cancellationToken) - => NativeAsync.PublishAsync(toChannel, message.ToUtf8Bytes(), cancellationToken); + ValueTask IRedisClientAsync.PublishMessageAsync(string toChannel, string message, CancellationToken token) + => NativeAsync.PublishAsync(toChannel, message.ToUtf8Bytes(), token); - ValueTask IRedisClientAsync.MoveBetweenSetsAsync(string fromSetId, string toSetId, string item, CancellationToken cancellationToken) - => NativeAsync.SMoveAsync(fromSetId, toSetId, item.ToUtf8Bytes(), cancellationToken); + ValueTask IRedisClientAsync.MoveBetweenSetsAsync(string fromSetId, string toSetId, string item, CancellationToken token) + => NativeAsync.SMoveAsync(fromSetId, toSetId, item.ToUtf8Bytes(), token); - ValueTask IRedisClientAsync.SetContainsItemAsync(string setId, string item, CancellationToken cancellationToken) - => NativeAsync.SIsMemberAsync(setId, item.ToUtf8Bytes(), cancellationToken).IsSuccessAsync(); + ValueTask IRedisClientAsync.SetContainsItemAsync(string setId, string item, CancellationToken token) + => NativeAsync.SIsMemberAsync(setId, item.ToUtf8Bytes(), token).IsSuccessAsync(); - async ValueTask> IRedisClientAsync.GetIntersectFromSetsAsync(string[] setIds, CancellationToken cancellationToken) + async ValueTask> IRedisClientAsync.GetIntersectFromSetsAsync(string[] setIds, CancellationToken token) { if (setIds.Length == 0) return new HashSet(); - var multiDataList = await NativeAsync.SInterAsync(setIds, cancellationToken).ConfigureAwait(false); + var multiDataList = await NativeAsync.SInterAsync(setIds, token).ConfigureAwait(false); return CreateHashSet(multiDataList); } - ValueTask IRedisClientAsync.StoreIntersectFromSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.StoreIntersectFromSetsAsync(string intoSetId, string[] setIds, CancellationToken token) { if (setIds.Length == 0) return default; - return NativeAsync.SInterStoreAsync(intoSetId, setIds, cancellationToken); + return NativeAsync.SInterStoreAsync(intoSetId, setIds, token); } - async ValueTask> IRedisClientAsync.GetUnionFromSetsAsync(string[] setIds, CancellationToken cancellationToken) + async ValueTask> IRedisClientAsync.GetUnionFromSetsAsync(string[] setIds, CancellationToken token) { if (setIds.Length == 0) return new HashSet(); - var multiDataList = await NativeAsync.SUnionAsync(setIds, cancellationToken).ConfigureAwait(false); + var multiDataList = await NativeAsync.SUnionAsync(setIds, token).ConfigureAwait(false); return CreateHashSet(multiDataList); } - ValueTask IRedisClientAsync.StoreUnionFromSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.StoreUnionFromSetsAsync(string intoSetId, string[] setIds, CancellationToken token) { if (setIds.Length == 0) return default; - return NativeAsync.SUnionStoreAsync(intoSetId, setIds, cancellationToken); + return NativeAsync.SUnionStoreAsync(intoSetId, setIds, token); } - async ValueTask> IRedisClientAsync.GetDifferencesFromSetAsync(string fromSetId, string[] withSetIds, CancellationToken cancellationToken) + async ValueTask> IRedisClientAsync.GetDifferencesFromSetAsync(string fromSetId, string[] withSetIds, CancellationToken token) { if (withSetIds.Length == 0) return new HashSet(); - var multiDataList = await NativeAsync.SDiffAsync(fromSetId, withSetIds, cancellationToken).ConfigureAwait(false); + var multiDataList = await NativeAsync.SDiffAsync(fromSetId, withSetIds, token).ConfigureAwait(false); return CreateHashSet(multiDataList); } - ValueTask IRedisClientAsync.StoreDifferencesFromSetAsync(string intoSetId, string fromSetId, string[] withSetIds, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.StoreDifferencesFromSetAsync(string intoSetId, string fromSetId, string[] withSetIds, CancellationToken token) { if (withSetIds.Length == 0) return default; - return NativeAsync.SDiffStoreAsync(intoSetId, fromSetId, withSetIds, cancellationToken); + return NativeAsync.SDiffStoreAsync(intoSetId, fromSetId, withSetIds, token); } - ValueTask IRedisClientAsync.GetRandomItemFromSetAsync(string setId, CancellationToken cancellationToken) - => NativeAsync.SRandMemberAsync(setId, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.GetRandomItemFromSetAsync(string setId, CancellationToken token) + => NativeAsync.SRandMemberAsync(setId, token).FromUtf8BytesAsync(); - ValueTask> IRedisClientAsync.GetAllItemsFromListAsync(string listId, CancellationToken cancellationToken) - => NativeAsync.LRangeAsync(listId, FirstElement, LastElement, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetAllItemsFromListAsync(string listId, CancellationToken token) + => NativeAsync.LRangeAsync(listId, FirstElement, LastElement, token).ToStringListAsync(); - ValueTask> IRedisClientAsync.GetRangeFromListAsync(string listId, int startingFrom, int endingAt, CancellationToken cancellationToken) - => NativeAsync.LRangeAsync(listId, startingFrom, endingAt, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetRangeFromListAsync(string listId, int startingFrom, int endingAt, CancellationToken token) + => NativeAsync.LRangeAsync(listId, startingFrom, endingAt, token).ToStringListAsync(); - ValueTask> IRedisClientAsync.GetRangeFromSortedListAsync(string listId, int startingFrom, int endingAt, CancellationToken cancellationToken) + ValueTask> IRedisClientAsync.GetRangeFromSortedListAsync(string listId, int startingFrom, int endingAt, CancellationToken token) { var sortOptions = new SortOptions { Skip = startingFrom, Take = endingAt, SortAlpha = true }; - return AsAsync().GetSortedItemsFromListAsync(listId, sortOptions, cancellationToken); + return AsAsync().GetSortedItemsFromListAsync(listId, sortOptions, token); } - ValueTask> IRedisClientAsync.GetSortedItemsFromListAsync(string listId, SortOptions sortOptions, CancellationToken cancellationToken) - => NativeAsync.SortAsync(listId, sortOptions, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetSortedItemsFromListAsync(string listId, SortOptions sortOptions, CancellationToken token) + => NativeAsync.SortAsync(listId, sortOptions, token).ToStringListAsync(); - async ValueTask IRedisClientAsync.AddRangeToListAsync(string listId, List values, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.AddRangeToListAsync(string listId, List values, CancellationToken token) { var pipeline = AddRangeToListPrepareNonFlushed(listId, values); - await pipeline.FlushAsync(cancellationToken).ConfigureAwait(false); + await pipeline.FlushAsync(token).ConfigureAwait(false); //the number of items after - _ = await pipeline.ReadAllAsIntsAsync(cancellationToken).ConfigureAwait(false); + _ = await pipeline.ReadAllAsIntsAsync(token).ConfigureAwait(false); } - ValueTask IRedisClientAsync.PrependItemToListAsync(string listId, string value, CancellationToken cancellationToken) - => NativeAsync.LPushAsync(listId, value.ToUtf8Bytes(), cancellationToken).Await(); + ValueTask IRedisClientAsync.PrependItemToListAsync(string listId, string value, CancellationToken token) + => NativeAsync.LPushAsync(listId, value.ToUtf8Bytes(), token).Await(); - async ValueTask IRedisClientAsync.PrependRangeToListAsync(string listId, List values, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.PrependRangeToListAsync(string listId, List values, CancellationToken token) { var pipeline = PrependRangeToListPrepareNonFlushed(listId, values); - await pipeline.FlushAsync(cancellationToken).ConfigureAwait(false); + await pipeline.FlushAsync(token).ConfigureAwait(false); //the number of items after - _ = await pipeline.ReadAllAsIntsAsync(cancellationToken).ConfigureAwait(false); + _ = await pipeline.ReadAllAsIntsAsync(token).ConfigureAwait(false); } - ValueTask IRedisClientAsync.RemoveAllFromListAsync(string listId, CancellationToken cancellationToken) - => NativeAsync.LTrimAsync(listId, LastElement, FirstElement, cancellationToken); + ValueTask IRedisClientAsync.RemoveAllFromListAsync(string listId, CancellationToken token) + => NativeAsync.LTrimAsync(listId, LastElement, FirstElement, token); - ValueTask IRedisClientAsync.RemoveStartFromListAsync(string listId, CancellationToken cancellationToken) - => NativeAsync.LPopAsync(listId, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.RemoveStartFromListAsync(string listId, CancellationToken token) + => NativeAsync.LPopAsync(listId, token).FromUtf8BytesAsync(); - ValueTask IRedisClientAsync.BlockingRemoveStartFromListAsync(string listId, TimeSpan? timeOut, CancellationToken cancellationToken) - => NativeAsync.BLPopValueAsync(listId, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.BlockingRemoveStartFromListAsync(string listId, TimeSpan? timeOut, CancellationToken token) + => NativeAsync.BLPopValueAsync(listId, (int)timeOut.GetValueOrDefault().TotalSeconds, token).FromUtf8BytesAsync(); - async ValueTask IRedisClientAsync.BlockingRemoveStartFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.BlockingRemoveStartFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken token) { - var value = await NativeAsync.BLPopValueAsync(listIds, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).ConfigureAwait(false); + var value = await NativeAsync.BLPopValueAsync(listIds, (int)timeOut.GetValueOrDefault().TotalSeconds, token).ConfigureAwait(false); if (value == null) return null; return new ItemRef { Id = value[0].FromUtf8Bytes(), Item = value[1].FromUtf8Bytes() }; } - ValueTask IRedisClientAsync.RemoveEndFromListAsync(string listId, CancellationToken cancellationToken) - => NativeAsync.RPopAsync(listId, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.RemoveEndFromListAsync(string listId, CancellationToken token) + => NativeAsync.RPopAsync(listId, token).FromUtf8BytesAsync(); - ValueTask IRedisClientAsync.TrimListAsync(string listId, int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken) - => NativeAsync.LTrimAsync(listId, keepStartingFrom, keepEndingAt, cancellationToken); + ValueTask IRedisClientAsync.TrimListAsync(string listId, int keepStartingFrom, int keepEndingAt, CancellationToken token) + => NativeAsync.LTrimAsync(listId, keepStartingFrom, keepEndingAt, token); - ValueTask IRedisClientAsync.RemoveItemFromListAsync(string listId, string value, CancellationToken cancellationToken) - => NativeAsync.LRemAsync(listId, 0, value.ToUtf8Bytes(), cancellationToken); + ValueTask IRedisClientAsync.RemoveItemFromListAsync(string listId, string value, CancellationToken token) + => NativeAsync.LRemAsync(listId, 0, value.ToUtf8Bytes(), token); - ValueTask IRedisClientAsync.RemoveItemFromListAsync(string listId, string value, int noOfMatches, CancellationToken cancellationToken) - => NativeAsync.LRemAsync(listId, 0, value.ToUtf8Bytes(), cancellationToken); + ValueTask IRedisClientAsync.RemoveItemFromListAsync(string listId, string value, int noOfMatches, CancellationToken token) + => NativeAsync.LRemAsync(listId, 0, value.ToUtf8Bytes(), token); - ValueTask IRedisClientAsync.GetItemFromListAsync(string listId, int listIndex, CancellationToken cancellationToken) - => NativeAsync.LIndexAsync(listId, listIndex, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.GetItemFromListAsync(string listId, int listIndex, CancellationToken token) + => NativeAsync.LIndexAsync(listId, listIndex, token).FromUtf8BytesAsync(); - ValueTask IRedisClientAsync.SetItemInListAsync(string listId, int listIndex, string value, CancellationToken cancellationToken) - => NativeAsync.LSetAsync(listId, listIndex, value.ToUtf8Bytes(), cancellationToken); + ValueTask IRedisClientAsync.SetItemInListAsync(string listId, int listIndex, string value, CancellationToken token) + => NativeAsync.LSetAsync(listId, listIndex, value.ToUtf8Bytes(), token); - ValueTask IRedisClientAsync.EnqueueItemOnListAsync(string listId, string value, CancellationToken cancellationToken) - => NativeAsync.LPushAsync(listId, value.ToUtf8Bytes(), cancellationToken).Await(); + ValueTask IRedisClientAsync.EnqueueItemOnListAsync(string listId, string value, CancellationToken token) + => NativeAsync.LPushAsync(listId, value.ToUtf8Bytes(), token).Await(); - ValueTask IRedisClientAsync.DequeueItemFromListAsync(string listId, CancellationToken cancellationToken) - => NativeAsync.RPopAsync(listId, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.DequeueItemFromListAsync(string listId, CancellationToken token) + => NativeAsync.RPopAsync(listId, token).FromUtf8BytesAsync(); - ValueTask IRedisClientAsync.BlockingDequeueItemFromListAsync(string listId, TimeSpan? timeOut, CancellationToken cancellationToken) - => NativeAsync.BRPopValueAsync(listId, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.BlockingDequeueItemFromListAsync(string listId, TimeSpan? timeOut, CancellationToken token) + => NativeAsync.BRPopValueAsync(listId, (int)timeOut.GetValueOrDefault().TotalSeconds, token).FromUtf8BytesAsync(); - async ValueTask IRedisClientAsync.BlockingDequeueItemFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.BlockingDequeueItemFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken token) { - var value = await NativeAsync.BRPopValueAsync(listIds, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).ConfigureAwait(false); + var value = await NativeAsync.BRPopValueAsync(listIds, (int)timeOut.GetValueOrDefault().TotalSeconds, token).ConfigureAwait(false); if (value == null) return null; return new ItemRef { Id = value[0].FromUtf8Bytes(), Item = value[1].FromUtf8Bytes() }; } - ValueTask IRedisClientAsync.PushItemToListAsync(string listId, string value, CancellationToken cancellationToken) - => NativeAsync.RPushAsync(listId, value.ToUtf8Bytes(), cancellationToken).Await(); + ValueTask IRedisClientAsync.PushItemToListAsync(string listId, string value, CancellationToken token) + => NativeAsync.RPushAsync(listId, value.ToUtf8Bytes(), token).Await(); - ValueTask IRedisClientAsync.PopItemFromListAsync(string listId, CancellationToken cancellationToken) - => NativeAsync.RPopAsync(listId, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.PopItemFromListAsync(string listId, CancellationToken token) + => NativeAsync.RPopAsync(listId, token).FromUtf8BytesAsync(); - ValueTask IRedisClientAsync.BlockingPopItemFromListAsync(string listId, TimeSpan? timeOut, CancellationToken cancellationToken) - => NativeAsync.BRPopValueAsync(listId, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.BlockingPopItemFromListAsync(string listId, TimeSpan? timeOut, CancellationToken token) + => NativeAsync.BRPopValueAsync(listId, (int)timeOut.GetValueOrDefault().TotalSeconds, token).FromUtf8BytesAsync(); - async ValueTask IRedisClientAsync.BlockingPopItemFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.BlockingPopItemFromListsAsync(string[] listIds, TimeSpan? timeOut, CancellationToken token) { - var value = await NativeAsync.BRPopValueAsync(listIds, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).ConfigureAwait(false); + var value = await NativeAsync.BRPopValueAsync(listIds, (int)timeOut.GetValueOrDefault().TotalSeconds, token).ConfigureAwait(false); if (value == null) return null; return new ItemRef { Id = value[0].FromUtf8Bytes(), Item = value[1].FromUtf8Bytes() }; } - ValueTask IRedisClientAsync.PopAndPushItemBetweenListsAsync(string fromListId, string toListId, CancellationToken cancellationToken) - => NativeAsync.RPopLPushAsync(fromListId, toListId, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.PopAndPushItemBetweenListsAsync(string fromListId, string toListId, CancellationToken token) + => NativeAsync.RPopLPushAsync(fromListId, toListId, token).FromUtf8BytesAsync(); - ValueTask IRedisClientAsync.BlockingPopAndPushItemBetweenListsAsync(string fromListId, string toListId, TimeSpan? timeOut, CancellationToken cancellationToken) - => NativeAsync.BRPopLPushAsync(fromListId, toListId, (int)timeOut.GetValueOrDefault().TotalSeconds, cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.BlockingPopAndPushItemBetweenListsAsync(string fromListId, string toListId, TimeSpan? timeOut, CancellationToken token) + => NativeAsync.BRPopLPushAsync(fromListId, toListId, (int)timeOut.GetValueOrDefault().TotalSeconds, token).FromUtf8BytesAsync(); - async ValueTask IRedisClientAsync.AddRangeToSortedSetAsync(string setId, List values, double score, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.AddRangeToSortedSetAsync(string setId, List values, double score, CancellationToken token) { var pipeline = AddRangeToSortedSetPrepareNonFlushed(setId, values, score.ToFastUtf8Bytes()); - await pipeline.FlushAsync(cancellationToken).ConfigureAwait(false); + await pipeline.FlushAsync(token).ConfigureAwait(false); - return await pipeline.ReadAllAsIntsHaveSuccessAsync(cancellationToken).ConfigureAwait(false); + return await pipeline.ReadAllAsIntsHaveSuccessAsync(token).ConfigureAwait(false); } - async ValueTask IRedisClientAsync.AddRangeToSortedSetAsync(string setId, List values, long score, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.AddRangeToSortedSetAsync(string setId, List values, long score, CancellationToken token) { var pipeline = AddRangeToSortedSetPrepareNonFlushed(setId, values, score.ToUtf8Bytes()); - await pipeline.FlushAsync(cancellationToken).ConfigureAwait(false); + await pipeline.FlushAsync(token).ConfigureAwait(false); - return await pipeline.ReadAllAsIntsHaveSuccessAsync(cancellationToken).ConfigureAwait(false); + return await pipeline.ReadAllAsIntsHaveSuccessAsync(token).ConfigureAwait(false); } - ValueTask IRedisClientAsync.RemoveItemFromSortedSetAsync(string setId, string value, CancellationToken cancellationToken) - => NativeAsync.ZRemAsync(setId, value.ToUtf8Bytes(), cancellationToken).IsSuccessAsync(); + ValueTask IRedisClientAsync.RemoveItemFromSortedSetAsync(string setId, string value, CancellationToken token) + => NativeAsync.ZRemAsync(setId, value.ToUtf8Bytes(), token).IsSuccessAsync(); - ValueTask IRedisClientAsync.RemoveItemsFromSortedSetAsync(string setId, List values, CancellationToken cancellationToken) - => NativeAsync.ZRemAsync(setId, values.Map(x => x.ToUtf8Bytes()).ToArray(), cancellationToken); + ValueTask IRedisClientAsync.RemoveItemsFromSortedSetAsync(string setId, List values, CancellationToken token) + => NativeAsync.ZRemAsync(setId, values.Map(x => x.ToUtf8Bytes()).ToArray(), token); - async ValueTask IRedisClientAsync.PopItemWithLowestScoreFromSortedSetAsync(string setId, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.PopItemWithLowestScoreFromSortedSetAsync(string setId, CancellationToken token) { //TODO: this should be atomic - var topScoreItemBytes = await NativeAsync.ZRangeAsync(setId, FirstElement, 1, cancellationToken).ConfigureAwait(false); + var topScoreItemBytes = await NativeAsync.ZRangeAsync(setId, FirstElement, 1, token).ConfigureAwait(false); if (topScoreItemBytes.Length == 0) return null; - await NativeAsync.ZRemAsync(setId, topScoreItemBytes[0], cancellationToken).ConfigureAwait(false); + await NativeAsync.ZRemAsync(setId, topScoreItemBytes[0], token).ConfigureAwait(false); return topScoreItemBytes[0].FromUtf8Bytes(); } - async ValueTask IRedisClientAsync.PopItemWithHighestScoreFromSortedSetAsync(string setId, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.PopItemWithHighestScoreFromSortedSetAsync(string setId, CancellationToken token) { //TODO: this should be atomic - var topScoreItemBytes = await NativeAsync.ZRevRangeAsync(setId, FirstElement, 1, cancellationToken).ConfigureAwait(false); + var topScoreItemBytes = await NativeAsync.ZRevRangeAsync(setId, FirstElement, 1, token).ConfigureAwait(false); if (topScoreItemBytes.Length == 0) return null; - await NativeAsync.ZRemAsync(setId, topScoreItemBytes[0], cancellationToken).ConfigureAwait(false); + await NativeAsync.ZRemAsync(setId, topScoreItemBytes[0], token).ConfigureAwait(false); return topScoreItemBytes[0].FromUtf8Bytes(); } - ValueTask IRedisClientAsync.SortedSetContainsItemAsync(string setId, string value, CancellationToken cancellationToken) - => NativeAsync.ZRankAsync(setId, value.ToUtf8Bytes(),cancellationToken).Await(val => val != -1); + ValueTask IRedisClientAsync.SortedSetContainsItemAsync(string setId, string value, CancellationToken token) + => NativeAsync.ZRankAsync(setId, value.ToUtf8Bytes(), token).Await(val => val != -1); - ValueTask IRedisClientAsync.IncrementItemInSortedSetAsync(string setId, string value, double incrementBy, CancellationToken cancellationToken) - => NativeAsync.ZIncrByAsync(setId, incrementBy, value.ToUtf8Bytes(), cancellationToken); + ValueTask IRedisClientAsync.IncrementItemInSortedSetAsync(string setId, string value, double incrementBy, CancellationToken token) + => NativeAsync.ZIncrByAsync(setId, incrementBy, value.ToUtf8Bytes(), token); - ValueTask IRedisClientAsync.IncrementItemInSortedSetAsync(string setId, string value, long incrementBy, CancellationToken cancellationToken) - => NativeAsync.ZIncrByAsync(setId, incrementBy, value.ToUtf8Bytes(), cancellationToken); + ValueTask IRedisClientAsync.IncrementItemInSortedSetAsync(string setId, string value, long incrementBy, CancellationToken token) + => NativeAsync.ZIncrByAsync(setId, incrementBy, value.ToUtf8Bytes(), token); - ValueTask IRedisClientAsync.GetItemIndexInSortedSetAsync(string setId, string value, CancellationToken cancellationToken) - => NativeAsync.ZRankAsync(setId, value.ToUtf8Bytes(), cancellationToken); + ValueTask IRedisClientAsync.GetItemIndexInSortedSetAsync(string setId, string value, CancellationToken token) + => NativeAsync.ZRankAsync(setId, value.ToUtf8Bytes(), token); - ValueTask IRedisClientAsync.GetItemIndexInSortedSetDescAsync(string setId, string value, CancellationToken cancellationToken) - => NativeAsync.ZRevRankAsync(setId, value.ToUtf8Bytes(), cancellationToken); + ValueTask IRedisClientAsync.GetItemIndexInSortedSetDescAsync(string setId, string value, CancellationToken token) + => NativeAsync.ZRevRankAsync(setId, value.ToUtf8Bytes(), token); - ValueTask> IRedisClientAsync.GetAllItemsFromSortedSetAsync(string setId, CancellationToken cancellationToken) - => NativeAsync.ZRangeAsync(setId, FirstElement, LastElement, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetAllItemsFromSortedSetAsync(string setId, CancellationToken token) + => NativeAsync.ZRangeAsync(setId, FirstElement, LastElement, token).ToStringListAsync(); - ValueTask> IRedisClientAsync.GetAllItemsFromSortedSetDescAsync(string setId, CancellationToken cancellationToken) - => NativeAsync.ZRevRangeAsync(setId, FirstElement, LastElement, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetAllItemsFromSortedSetDescAsync(string setId, CancellationToken token) + => NativeAsync.ZRevRangeAsync(setId, FirstElement, LastElement, token).ToStringListAsync(); - ValueTask> IRedisClientAsync.GetRangeFromSortedSetAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken) - => NativeAsync.ZRangeAsync(setId, fromRank, toRank, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetRangeFromSortedSetAsync(string setId, int fromRank, int toRank, CancellationToken token) + => NativeAsync.ZRangeAsync(setId, fromRank, toRank, token).ToStringListAsync(); - ValueTask> IRedisClientAsync.GetRangeFromSortedSetDescAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken) - => NativeAsync.ZRevRangeAsync(setId, fromRank, toRank, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetRangeFromSortedSetDescAsync(string setId, int fromRank, int toRank, CancellationToken token) + => NativeAsync.ZRevRangeAsync(setId, fromRank, toRank, token).ToStringListAsync(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ValueTask> CreateSortedScoreMapAsync(ValueTask pending) @@ -1231,294 +1231,294 @@ static async ValueTask> Awaited(ValueTask => CreateSortedScoreMap(await pending.ConfigureAwait(false)); } - ValueTask> IRedisClientAsync.GetAllWithScoresFromSortedSetAsync(string setId, CancellationToken cancellationToken) - => CreateSortedScoreMapAsync(NativeAsync.ZRangeWithScoresAsync(setId, FirstElement, LastElement, cancellationToken)); + ValueTask> IRedisClientAsync.GetAllWithScoresFromSortedSetAsync(string setId, CancellationToken token) + => CreateSortedScoreMapAsync(NativeAsync.ZRangeWithScoresAsync(setId, FirstElement, LastElement, token)); - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken) - => CreateSortedScoreMapAsync(NativeAsync.ZRangeWithScoresAsync(setId, fromRank, toRank, cancellationToken)); + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetAsync(string setId, int fromRank, int toRank, CancellationToken token) + => CreateSortedScoreMapAsync(NativeAsync.ZRangeWithScoresAsync(setId, fromRank, toRank, token)); - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetDescAsync(string setId, int fromRank, int toRank, CancellationToken cancellationToken) - => CreateSortedScoreMapAsync(NativeAsync.ZRevRangeWithScoresAsync(setId, fromRank, toRank, cancellationToken)); + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetDescAsync(string setId, int fromRank, int toRank, CancellationToken token) + => CreateSortedScoreMapAsync(NativeAsync.ZRevRangeWithScoresAsync(setId, fromRank, toRank, token)); - ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken) - => AsAsync().GetRangeFromSortedSetByLowestScoreAsync(setId, fromStringScore, toStringScore, null, null, cancellationToken); + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken token) + => AsAsync().GetRangeFromSortedSetByLowestScoreAsync(setId, fromStringScore, toStringScore, null, null, token); - ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken token) { var fromScore = GetLexicalScore(fromStringScore); var toScore = GetLexicalScore(toStringScore); - return AsAsync().GetRangeFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken); + return AsAsync().GetRangeFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, skip, take, token); } - ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) - => AsAsync().GetRangeFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, CancellationToken token) + => AsAsync().GetRangeFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, null, null, token); - ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) - => AsAsync().GetRangeFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, CancellationToken token) + => AsAsync().GetRangeFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, null, null, token); - ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) - => NativeAsync.ZRangeByScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken token) + => NativeAsync.ZRangeByScoreAsync(setId, fromScore, toScore, skip, take, token).ToStringListAsync(); - ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken) - => NativeAsync.ZRangeByScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken token) + => NativeAsync.ZRangeByScoreAsync(setId, fromScore, toScore, skip, take, token).ToStringListAsync(); - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken) - => AsAsync().GetRangeWithScoresFromSortedSetByLowestScoreAsync(setId, fromStringScore, toStringScore, null, null, cancellationToken); + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken token) + => AsAsync().GetRangeWithScoresFromSortedSetByLowestScoreAsync(setId, fromStringScore, toStringScore, null, null, token); - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken token) { var fromScore = GetLexicalScore(fromStringScore); var toScore = GetLexicalScore(toStringScore); - return AsAsync().GetRangeWithScoresFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken); + return AsAsync().GetRangeWithScoresFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, skip, take, token); } - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) - => AsAsync().GetRangeWithScoresFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, CancellationToken token) + => AsAsync().GetRangeWithScoresFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, null, null, token); - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) - => AsAsync().GetRangeWithScoresFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, CancellationToken token) + => AsAsync().GetRangeWithScoresFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, null, null, token); - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) - => CreateSortedScoreMapAsync(NativeAsync.ZRangeByScoreWithScoresAsync(setId, fromScore, toScore, skip, take, cancellationToken)); + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken token) + => CreateSortedScoreMapAsync(NativeAsync.ZRangeByScoreWithScoresAsync(setId, fromScore, toScore, skip, take, token)); - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken) - => CreateSortedScoreMapAsync(NativeAsync.ZRangeByScoreWithScoresAsync(setId, fromScore, toScore, skip, take, cancellationToken)); + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByLowestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken token) + => CreateSortedScoreMapAsync(NativeAsync.ZRangeByScoreWithScoresAsync(setId, fromScore, toScore, skip, take, token)); - ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken) - => AsAsync().GetRangeFromSortedSetByHighestScoreAsync(setId, fromStringScore, toStringScore, null, null, cancellationToken); + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken token) + => AsAsync().GetRangeFromSortedSetByHighestScoreAsync(setId, fromStringScore, toStringScore, null, null, token); - ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken token) { var fromScore = GetLexicalScore(fromStringScore); var toScore = GetLexicalScore(toStringScore); - return AsAsync().GetRangeFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken); + return AsAsync().GetRangeFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, skip, take, token); } - ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) - => AsAsync().GetRangeFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, CancellationToken token) + => AsAsync().GetRangeFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, null, null, token); - ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) - => AsAsync().GetRangeFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, CancellationToken token) + => AsAsync().GetRangeFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, null, null, token); - ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) - => NativeAsync.ZRevRangeByScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken token) + => NativeAsync.ZRevRangeByScoreAsync(setId, fromScore, toScore, skip, take, token).ToStringListAsync(); - ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken) - => NativeAsync.ZRevRangeByScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetRangeFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken token) + => NativeAsync.ZRevRangeByScoreAsync(setId, fromScore, toScore, skip, take, token).ToStringListAsync(); - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken cancellationToken) - => AsAsync().GetRangeWithScoresFromSortedSetByHighestScoreAsync(setId, fromStringScore, toStringScore, null, null, cancellationToken); + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, CancellationToken token) + => AsAsync().GetRangeWithScoresFromSortedSetByHighestScoreAsync(setId, fromStringScore, toStringScore, null, null, token); - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken token) { var fromScore = GetLexicalScore(fromStringScore); var toScore = GetLexicalScore(toStringScore); - return AsAsync().GetRangeWithScoresFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken); + return AsAsync().GetRangeWithScoresFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, skip, take, token); } - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) - => AsAsync().GetRangeWithScoresFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, CancellationToken token) + => AsAsync().GetRangeWithScoresFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, null, null, token); - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) - => AsAsync().GetRangeWithScoresFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, null, null, cancellationToken); + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, CancellationToken token) + => AsAsync().GetRangeWithScoresFromSortedSetByHighestScoreAsync(setId, fromScore, toScore, null, null, token); - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) - => CreateSortedScoreMapAsync(NativeAsync.ZRevRangeByScoreWithScoresAsync(setId, fromScore, toScore, skip, take, cancellationToken)); + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, double fromScore, double toScore, int? skip, int? take, CancellationToken token) + => CreateSortedScoreMapAsync(NativeAsync.ZRevRangeByScoreWithScoresAsync(setId, fromScore, toScore, skip, take, token)); - ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken cancellationToken) - => CreateSortedScoreMapAsync(NativeAsync.ZRevRangeByScoreWithScoresAsync(setId, fromScore, toScore, skip, take, cancellationToken)); + ValueTask> IRedisClientAsync.GetRangeWithScoresFromSortedSetByHighestScoreAsync(string setId, long fromScore, long toScore, int? skip, int? take, CancellationToken token) + => CreateSortedScoreMapAsync(NativeAsync.ZRevRangeByScoreWithScoresAsync(setId, fromScore, toScore, skip, take, token)); - ValueTask IRedisClientAsync.RemoveRangeFromSortedSetAsync(string setId, int minRank, int maxRank, CancellationToken cancellationToken) - => NativeAsync.ZRemRangeByRankAsync(setId, minRank, maxRank, cancellationToken); + ValueTask IRedisClientAsync.RemoveRangeFromSortedSetAsync(string setId, int minRank, int maxRank, CancellationToken token) + => NativeAsync.ZRemRangeByRankAsync(setId, minRank, maxRank, token); - ValueTask IRedisClientAsync.RemoveRangeFromSortedSetByScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) - => NativeAsync.ZRemRangeByScoreAsync(setId, fromScore, toScore, cancellationToken); + ValueTask IRedisClientAsync.RemoveRangeFromSortedSetByScoreAsync(string setId, double fromScore, double toScore, CancellationToken token) + => NativeAsync.ZRemRangeByScoreAsync(setId, fromScore, toScore, token); - ValueTask IRedisClientAsync.RemoveRangeFromSortedSetByScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) - => NativeAsync.ZRemRangeByScoreAsync(setId, fromScore, toScore, cancellationToken); + ValueTask IRedisClientAsync.RemoveRangeFromSortedSetByScoreAsync(string setId, long fromScore, long toScore, CancellationToken token) + => NativeAsync.ZRemRangeByScoreAsync(setId, fromScore, toScore, token); - ValueTask IRedisClientAsync.StoreIntersectFromSortedSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) - => NativeAsync.ZInterStoreAsync(intoSetId, setIds, cancellationToken); + ValueTask IRedisClientAsync.StoreIntersectFromSortedSetsAsync(string intoSetId, string[] setIds, CancellationToken token) + => NativeAsync.ZInterStoreAsync(intoSetId, setIds, token); - ValueTask IRedisClientAsync.StoreIntersectFromSortedSetsAsync(string intoSetId, string[] setIds, string[] args, CancellationToken cancellationToken) - => base.ZInterStoreAsync(intoSetId, setIds, args, cancellationToken); + ValueTask IRedisClientAsync.StoreIntersectFromSortedSetsAsync(string intoSetId, string[] setIds, string[] args, CancellationToken token) + => base.ZInterStoreAsync(intoSetId, setIds, args, token); - ValueTask IRedisClientAsync.StoreUnionFromSortedSetsAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) - => NativeAsync.ZUnionStoreAsync(intoSetId, setIds, cancellationToken); + ValueTask IRedisClientAsync.StoreUnionFromSortedSetsAsync(string intoSetId, string[] setIds, CancellationToken token) + => NativeAsync.ZUnionStoreAsync(intoSetId, setIds, token); - ValueTask IRedisClientAsync.StoreUnionFromSortedSetsAsync(string intoSetId, string[] setIds, string[] args, CancellationToken cancellationToken) - => base.ZUnionStoreAsync(intoSetId, setIds, args, cancellationToken); + ValueTask IRedisClientAsync.StoreUnionFromSortedSetsAsync(string intoSetId, string[] setIds, string[] args, CancellationToken token) + => base.ZUnionStoreAsync(intoSetId, setIds, args, token); - ValueTask IRedisClientAsync.HashContainsEntryAsync(string hashId, string key, CancellationToken cancellationToken) - => NativeAsync.HExistsAsync(hashId, key.ToUtf8Bytes(), cancellationToken).IsSuccessAsync(); + ValueTask IRedisClientAsync.HashContainsEntryAsync(string hashId, string key, CancellationToken token) + => NativeAsync.HExistsAsync(hashId, key.ToUtf8Bytes(), token).IsSuccessAsync(); - ValueTask IRedisClientAsync.SetEntryInHashIfNotExistsAsync(string hashId, string key, string value, CancellationToken cancellationToken) - => NativeAsync.HSetNXAsync(hashId, key.ToUtf8Bytes(), value.ToUtf8Bytes(), cancellationToken).IsSuccessAsync(); + ValueTask IRedisClientAsync.SetEntryInHashIfNotExistsAsync(string hashId, string key, string value, CancellationToken token) + => NativeAsync.HSetNXAsync(hashId, key.ToUtf8Bytes(), value.ToUtf8Bytes(), token).IsSuccessAsync(); - ValueTask IRedisClientAsync.SetRangeInHashAsync(string hashId, IEnumerable> keyValuePairs, CancellationToken cancellationToken) - => SetRangeInHashPrepare(keyValuePairs, out var keys, out var values) ? NativeAsync.HMSetAsync(hashId, keys, values, cancellationToken) : default; + ValueTask IRedisClientAsync.SetRangeInHashAsync(string hashId, IEnumerable> keyValuePairs, CancellationToken token) + => SetRangeInHashPrepare(keyValuePairs, out var keys, out var values) ? NativeAsync.HMSetAsync(hashId, keys, values, token) : default; - ValueTask IRedisClientAsync.IncrementValueInHashAsync(string hashId, string key, int incrementBy, CancellationToken cancellationToken) - => NativeAsync.HIncrbyAsync(hashId, key.ToUtf8Bytes(), incrementBy, cancellationToken); + ValueTask IRedisClientAsync.IncrementValueInHashAsync(string hashId, string key, int incrementBy, CancellationToken token) + => NativeAsync.HIncrbyAsync(hashId, key.ToUtf8Bytes(), incrementBy, token); - ValueTask IRedisClientAsync.IncrementValueInHashAsync(string hashId, string key, double incrementBy, CancellationToken cancellationToken) - => NativeAsync.HIncrbyFloatAsync(hashId, key.ToUtf8Bytes(), incrementBy, cancellationToken); + ValueTask IRedisClientAsync.IncrementValueInHashAsync(string hashId, string key, double incrementBy, CancellationToken token) + => NativeAsync.HIncrbyFloatAsync(hashId, key.ToUtf8Bytes(), incrementBy, token); - ValueTask IRedisClientAsync.GetValueFromHashAsync(string hashId, string key, CancellationToken cancellationToken) - => NativeAsync.HGetAsync(hashId, key.ToUtf8Bytes(), cancellationToken).FromUtf8BytesAsync(); + ValueTask IRedisClientAsync.GetValueFromHashAsync(string hashId, string key, CancellationToken token) + => NativeAsync.HGetAsync(hashId, key.ToUtf8Bytes(), token).FromUtf8BytesAsync(); - ValueTask> IRedisClientAsync.GetValuesFromHashAsync(string hashId, string[] keys, CancellationToken cancellationToken) + ValueTask> IRedisClientAsync.GetValuesFromHashAsync(string hashId, string[] keys, CancellationToken token) { if (keys.Length == 0) return new List().AsValueTaskResult(); var keyBytes = ConvertToBytes(keys); - return NativeAsync.HMGetAsync(hashId, keyBytes, cancellationToken).ToStringListAsync(); + return NativeAsync.HMGetAsync(hashId, keyBytes, token).ToStringListAsync(); } - ValueTask IRedisClientAsync.RemoveEntryFromHashAsync(string hashId, string key, CancellationToken cancellationToken) - => NativeAsync.HDelAsync(hashId, key.ToUtf8Bytes(), cancellationToken).IsSuccessAsync(); + ValueTask IRedisClientAsync.RemoveEntryFromHashAsync(string hashId, string key, CancellationToken token) + => NativeAsync.HDelAsync(hashId, key.ToUtf8Bytes(), token).IsSuccessAsync(); - ValueTask> IRedisClientAsync.GetHashKeysAsync(string hashId, CancellationToken cancellationToken) - => NativeAsync.HKeysAsync(hashId, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetHashKeysAsync(string hashId, CancellationToken token) + => NativeAsync.HKeysAsync(hashId, token).ToStringListAsync(); - ValueTask> IRedisClientAsync.GetHashValuesAsync(string hashId, CancellationToken cancellationToken) - => NativeAsync.HValsAsync(hashId, cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.GetHashValuesAsync(string hashId, CancellationToken token) + => NativeAsync.HValsAsync(hashId, token).ToStringListAsync(); - ValueTask> IRedisClientAsync.GetAllEntriesFromHashAsync(string hashId, CancellationToken cancellationToken) - => NativeAsync.HGetAllAsync(hashId, cancellationToken).Await(ret => ret.ToStringDictionary()); + ValueTask> IRedisClientAsync.GetAllEntriesFromHashAsync(string hashId, CancellationToken token) + => NativeAsync.HGetAllAsync(hashId, token).Await(ret => ret.ToStringDictionary()); - ValueTask IRedisClientAsync.ExecLuaAsync(string body, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalCommandAsync(body, 0, args.ToMultiByteArray(), cancellationToken).Await(ret => ret.ToRedisText()); + ValueTask IRedisClientAsync.ExecLuaAsync(string body, string[] args, CancellationToken token) + => NativeAsync.EvalCommandAsync(body, 0, args.ToMultiByteArray(), token).Await(ret => ret.ToRedisText()); - ValueTask IRedisClientAsync.ExecLuaShaAsync(string sha1, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalShaCommandAsync(sha1, 0, args.ToMultiByteArray(), cancellationToken).Await(ret => ret.ToRedisText()); + ValueTask IRedisClientAsync.ExecLuaShaAsync(string sha1, string[] args, CancellationToken token) + => NativeAsync.EvalShaCommandAsync(sha1, 0, args.ToMultiByteArray(), token).Await(ret => ret.ToRedisText()); - ValueTask IRedisClientAsync.ExecLuaAsStringAsync(string body, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalStrAsync(body, 0, args.ToMultiByteArray(), cancellationToken); + ValueTask IRedisClientAsync.ExecLuaAsStringAsync(string body, string[] args, CancellationToken token) + => NativeAsync.EvalStrAsync(body, 0, args.ToMultiByteArray(), token); - ValueTask IRedisClientAsync.ExecLuaShaAsStringAsync(string sha1, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalShaStrAsync(sha1, 0, args.ToMultiByteArray(), cancellationToken); + ValueTask IRedisClientAsync.ExecLuaShaAsStringAsync(string sha1, string[] args, CancellationToken token) + => NativeAsync.EvalShaStrAsync(sha1, 0, args.ToMultiByteArray(), token); - ValueTask IRedisClientAsync.ExecLuaAsIntAsync(string body, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalIntAsync(body, 0, args.ToMultiByteArray(), cancellationToken); + ValueTask IRedisClientAsync.ExecLuaAsIntAsync(string body, string[] args, CancellationToken token) + => NativeAsync.EvalIntAsync(body, 0, args.ToMultiByteArray(), token); - ValueTask IRedisClientAsync.ExecLuaAsIntAsync(string body, string[] keys, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalIntAsync(body, keys.Length, MergeAndConvertToBytes(keys, args), cancellationToken); + ValueTask IRedisClientAsync.ExecLuaAsIntAsync(string body, string[] keys, string[] args, CancellationToken token) + => NativeAsync.EvalIntAsync(body, keys.Length, MergeAndConvertToBytes(keys, args), token); - ValueTask IRedisClientAsync.ExecLuaShaAsIntAsync(string sha1, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalShaIntAsync(sha1, 0, args.ToMultiByteArray(), cancellationToken); + ValueTask IRedisClientAsync.ExecLuaShaAsIntAsync(string sha1, string[] args, CancellationToken token) + => NativeAsync.EvalShaIntAsync(sha1, 0, args.ToMultiByteArray(), token); - ValueTask IRedisClientAsync.ExecLuaShaAsIntAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalShaIntAsync(sha1, keys.Length, MergeAndConvertToBytes(keys, args), cancellationToken); + ValueTask IRedisClientAsync.ExecLuaShaAsIntAsync(string sha1, string[] keys, string[] args, CancellationToken token) + => NativeAsync.EvalShaIntAsync(sha1, keys.Length, MergeAndConvertToBytes(keys, args), token); - ValueTask> IRedisClientAsync.ExecLuaAsListAsync(string body, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalAsync(body, 0, args.ToMultiByteArray(), cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.ExecLuaAsListAsync(string body, string[] args, CancellationToken token) + => NativeAsync.EvalAsync(body, 0, args.ToMultiByteArray(), token).ToStringListAsync(); - ValueTask> IRedisClientAsync.ExecLuaAsListAsync(string body, string[] keys, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalAsync(body, keys.Length, MergeAndConvertToBytes(keys, args), cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.ExecLuaAsListAsync(string body, string[] keys, string[] args, CancellationToken token) + => NativeAsync.EvalAsync(body, keys.Length, MergeAndConvertToBytes(keys, args), token).ToStringListAsync(); - ValueTask> IRedisClientAsync.ExecLuaShaAsListAsync(string sha1, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalShaAsync(sha1, 0, args.ToMultiByteArray(), cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.ExecLuaShaAsListAsync(string sha1, string[] args, CancellationToken token) + => NativeAsync.EvalShaAsync(sha1, 0, args.ToMultiByteArray(), token).ToStringListAsync(); - ValueTask> IRedisClientAsync.ExecLuaShaAsListAsync(string sha1, string[] keys, string[] args, CancellationToken cancellationToken) - => NativeAsync.EvalShaAsync(sha1, keys.Length, MergeAndConvertToBytes(keys, args), cancellationToken).ToStringListAsync(); + ValueTask> IRedisClientAsync.ExecLuaShaAsListAsync(string sha1, string[] keys, string[] args, CancellationToken token) + => NativeAsync.EvalShaAsync(sha1, keys.Length, MergeAndConvertToBytes(keys, args), token).ToStringListAsync(); - ValueTask IRedisClientAsync.CalculateSha1Async(string luaBody, CancellationToken cancellationToken) + ValueTask IRedisClientAsync.CalculateSha1Async(string luaBody, CancellationToken token) => CalculateSha1(luaBody).AsValueTaskResult(); - async ValueTask IRedisClientAsync.HasLuaScriptAsync(string sha1Ref, CancellationToken cancellationToken) + async ValueTask IRedisClientAsync.HasLuaScriptAsync(string sha1Ref, CancellationToken token) { - var map = await AsAsync().WhichLuaScriptsExistsAsync(new[] { sha1Ref }, cancellationToken).ConfigureAwait(false); + var map = await AsAsync().WhichLuaScriptsExistsAsync(new[] { sha1Ref }, token).ConfigureAwait(false); return map[sha1Ref]; } - async ValueTask> IRedisClientAsync.WhichLuaScriptsExistsAsync(string[] sha1Refs, CancellationToken cancellationToken) + async ValueTask> IRedisClientAsync.WhichLuaScriptsExistsAsync(string[] sha1Refs, CancellationToken token) { var intFlags = await NativeAsync.ScriptExistsAsync(sha1Refs.ToMultiByteArray()).ConfigureAwait(false); return WhichLuaScriptsExistsParseResult(sha1Refs, intFlags); } - ValueTask IRedisClientAsync.RemoveAllLuaScriptsAsync(CancellationToken cancellationToken) - => NativeAsync.ScriptFlushAsync(cancellationToken); + ValueTask IRedisClientAsync.RemoveAllLuaScriptsAsync(CancellationToken token) + => NativeAsync.ScriptFlushAsync(token); - ValueTask IRedisClientAsync.KillRunningLuaScriptAsync(CancellationToken cancellationToken) - => NativeAsync.ScriptKillAsync(cancellationToken); + ValueTask IRedisClientAsync.KillRunningLuaScriptAsync(CancellationToken token) + => NativeAsync.ScriptKillAsync(token); ValueTask IRedisClientAsync.CustomAsync(params object[] cmdWithArgs) - => AsAsync().CustomAsync(cmdWithArgs, cancellationToken: default); + => AsAsync().CustomAsync(cmdWithArgs, token: default); ValueTask IRedisClientAsync.RemoveEntryAsync(params string[] args) - => AsAsync().RemoveEntryAsync(args, cancellationToken: default); + => AsAsync().RemoveEntryAsync(args, token: default); ValueTask IRedisClientAsync.AddToHyperLogAsync(string key, params string[] elements) - => AsAsync().AddToHyperLogAsync(key, elements, cancellationToken: default); + => AsAsync().AddToHyperLogAsync(key, elements, token: default); ValueTask IRedisClientAsync.MergeHyperLogsAsync(string toKey, params string[] fromKeys) - => AsAsync().MergeHyperLogsAsync(toKey, fromKeys, cancellationToken: default); + => AsAsync().MergeHyperLogsAsync(toKey, fromKeys, token: default); ValueTask IRedisClientAsync.AddGeoMembersAsync(string key, params RedisGeo[] geoPoints) - => AsAsync().AddGeoMembersAsync(key, geoPoints, cancellationToken: default); + => AsAsync().AddGeoMembersAsync(key, geoPoints, token: default); ValueTask IRedisClientAsync.GetGeohashesAsync(string key, params string[] members) - => AsAsync().GetGeohashesAsync(key, members, cancellationToken: default); + => AsAsync().GetGeohashesAsync(key, members, token: default); ValueTask> IRedisClientAsync.GetGeoCoordinatesAsync(string key, params string[] members) - => AsAsync().GetGeoCoordinatesAsync(key, members, cancellationToken: default); + => AsAsync().GetGeoCoordinatesAsync(key, members, token: default); ValueTask IRedisClientAsync.WatchAsync(params string[] keys) - => AsAsync().WatchAsync(keys, cancellationToken: default); + => AsAsync().WatchAsync(keys, token: default); ValueTask> IRedisClientAsync.GetIntersectFromSetsAsync(params string[] setIds) - => AsAsync().GetIntersectFromSetsAsync(setIds, cancellationToken: default); + => AsAsync().GetIntersectFromSetsAsync(setIds, token: default); ValueTask IRedisClientAsync.StoreIntersectFromSetsAsync(string intoSetId, params string[] setIds) - => AsAsync().StoreIntersectFromSetsAsync(intoSetId, setIds, cancellationToken: default); + => AsAsync().StoreIntersectFromSetsAsync(intoSetId, setIds, token: default); ValueTask> IRedisClientAsync.GetUnionFromSetsAsync(params string[] setIds) - => AsAsync().GetUnionFromSetsAsync(setIds, cancellationToken: default); + => AsAsync().GetUnionFromSetsAsync(setIds, token: default); ValueTask IRedisClientAsync.StoreUnionFromSetsAsync(string intoSetId, params string[] setIds) - => AsAsync().StoreUnionFromSetsAsync(intoSetId, setIds, cancellationToken: default); + => AsAsync().StoreUnionFromSetsAsync(intoSetId, setIds, token: default); ValueTask> IRedisClientAsync.GetDifferencesFromSetAsync(string fromSetId, params string[] withSetIds) - => AsAsync().GetDifferencesFromSetAsync(fromSetId, withSetIds, cancellationToken: default); + => AsAsync().GetDifferencesFromSetAsync(fromSetId, withSetIds, token: default); ValueTask IRedisClientAsync.StoreDifferencesFromSetAsync(string intoSetId, string fromSetId, params string[] withSetIds) - => AsAsync().StoreDifferencesFromSetAsync(intoSetId, fromSetId, withSetIds, cancellationToken: default); + => AsAsync().StoreDifferencesFromSetAsync(intoSetId, fromSetId, withSetIds, token: default); ValueTask IRedisClientAsync.StoreIntersectFromSortedSetsAsync(string intoSetId, params string[] setIds) - => AsAsync().StoreIntersectFromSortedSetsAsync(intoSetId, setIds, cancellationToken: default); + => AsAsync().StoreIntersectFromSortedSetsAsync(intoSetId, setIds, token: default); ValueTask IRedisClientAsync.StoreUnionFromSortedSetsAsync(string intoSetId, params string[] setIds) - => AsAsync().StoreUnionFromSortedSetsAsync(intoSetId, setIds, cancellationToken: default); + => AsAsync().StoreUnionFromSortedSetsAsync(intoSetId, setIds, token: default); ValueTask> IRedisClientAsync.GetValuesFromHashAsync(string hashId, params string[] keys) - => AsAsync().GetValuesFromHashAsync(hashId, keys, cancellationToken: default); + => AsAsync().GetValuesFromHashAsync(hashId, keys, token: default); ValueTask IRedisClientAsync.ExecLuaAsync(string body, params string[] args) - => AsAsync().ExecLuaAsync(body, args, cancellationToken: default); + => AsAsync().ExecLuaAsync(body, args, token: default); ValueTask IRedisClientAsync.ExecLuaShaAsync(string sha1, params string[] args) - => AsAsync().ExecLuaShaAsync(sha1, args, cancellationToken: default); + => AsAsync().ExecLuaShaAsync(sha1, args, token: default); ValueTask IRedisClientAsync.ExecLuaAsStringAsync(string luaBody, params string[] args) - => AsAsync().ExecLuaAsStringAsync(luaBody, args, cancellationToken: default); + => AsAsync().ExecLuaAsStringAsync(luaBody, args, token: default); ValueTask IRedisClientAsync.ExecLuaShaAsStringAsync(string sha1, params string[] args) - => AsAsync().ExecLuaShaAsStringAsync(sha1, args, cancellationToken: default); + => AsAsync().ExecLuaShaAsStringAsync(sha1, args, token: default); ValueTask IRedisClientAsync.ExecLuaAsIntAsync(string luaBody, params string[] args) - => AsAsync().ExecLuaAsIntAsync(luaBody, args, cancellationToken: default); + => AsAsync().ExecLuaAsIntAsync(luaBody, args, token: default); ValueTask IRedisClientAsync.ExecLuaShaAsIntAsync(string sha1, params string[] args) - => AsAsync().ExecLuaShaAsIntAsync(sha1, args, cancellationToken: default); + => AsAsync().ExecLuaShaAsIntAsync(sha1, args, token: default); ValueTask> IRedisClientAsync.ExecLuaAsListAsync(string luaBody, params string[] args) - => AsAsync().ExecLuaAsListAsync(luaBody, args, cancellationToken: default); + => AsAsync().ExecLuaAsListAsync(luaBody, args, token: default); ValueTask> IRedisClientAsync.ExecLuaShaAsListAsync(string sha1, params string[] args) - => AsAsync().ExecLuaShaAsListAsync(sha1, args, cancellationToken: default); + => AsAsync().ExecLuaShaAsListAsync(sha1, args, token: default); ValueTask> IRedisClientAsync.WhichLuaScriptsExistsAsync(params string[] sha1Refs) - => AsAsync().WhichLuaScriptsExistsAsync(sha1Refs, cancellationToken: default); + => AsAsync().WhichLuaScriptsExistsAsync(sha1Refs, token: default); } } diff --git a/src/ServiceStack.Redis/RedisClientHash.Async.cs b/src/ServiceStack.Redis/RedisClientHash.Async.cs index 049a6e19..2fed5957 100644 --- a/src/ServiceStack.Redis/RedisClientHash.Async.cs +++ b/src/ServiceStack.Redis/RedisClientHash.Async.cs @@ -22,34 +22,34 @@ internal partial class RedisClientHash { private IRedisClientAsync AsyncClient => client; - ValueTask IRedisHashAsync.AddAsync(KeyValuePair item, CancellationToken cancellationToken) - => AsyncClient.SetEntryInHashAsync(hashId, item.Key, item.Value, cancellationToken).Await(); + ValueTask IRedisHashAsync.AddAsync(KeyValuePair item, CancellationToken token) + => AsyncClient.SetEntryInHashAsync(hashId, item.Key, item.Value, token).Await(); - ValueTask IRedisHashAsync.AddAsync(string key, string value, CancellationToken cancellationToken) - => AsyncClient.SetEntryInHashAsync(hashId, key, value, cancellationToken).Await(); + ValueTask IRedisHashAsync.AddAsync(string key, string value, CancellationToken token) + => AsyncClient.SetEntryInHashAsync(hashId, key, value, token).Await(); - ValueTask IRedisHashAsync.AddIfNotExistsAsync(KeyValuePair item, CancellationToken cancellationToken) - => AsyncClient.SetEntryInHashIfNotExistsAsync(hashId, item.Key, item.Value, cancellationToken); + ValueTask IRedisHashAsync.AddIfNotExistsAsync(KeyValuePair item, CancellationToken token) + => AsyncClient.SetEntryInHashIfNotExistsAsync(hashId, item.Key, item.Value, token); - ValueTask IRedisHashAsync.AddRangeAsync(IEnumerable> items, CancellationToken cancellationToken) - => AsyncClient.SetRangeInHashAsync(hashId, items, cancellationToken); + ValueTask IRedisHashAsync.AddRangeAsync(IEnumerable> items, CancellationToken token) + => AsyncClient.SetRangeInHashAsync(hashId, items, token); - ValueTask IRedisHashAsync.ClearAsync(CancellationToken cancellationToken) - => new ValueTask(AsyncClient.RemoveAsync(hashId, cancellationToken)); + ValueTask IRedisHashAsync.ClearAsync(CancellationToken token) + => new ValueTask(AsyncClient.RemoveAsync(hashId, token)); - ValueTask IRedisHashAsync.ContainsKeyAsync(string key, CancellationToken cancellationToken) - => AsyncClient.HashContainsEntryAsync(hashId, key, cancellationToken); + ValueTask IRedisHashAsync.ContainsKeyAsync(string key, CancellationToken token) + => AsyncClient.HashContainsEntryAsync(hashId, key, token); - ValueTask IRedisHashAsync.CountAsync(CancellationToken cancellationToken) - => AsyncClient.GetHashCountAsync(hashId, cancellationToken).AsInt32(); + ValueTask IRedisHashAsync.CountAsync(CancellationToken token) + => AsyncClient.GetHashCountAsync(hashId, token).AsInt32(); - IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) - => AsyncClient.ScanAllHashEntriesAsync(hashId).GetAsyncEnumerator(cancellationToken); // note: we're using HSCAN here, not HGETALL + IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken token) + => AsyncClient.ScanAllHashEntriesAsync(hashId).GetAsyncEnumerator(token); // note: we're using HSCAN here, not HGETALL - ValueTask IRedisHashAsync.IncrementValueAsync(string key, int incrementBy, CancellationToken cancellationToken) - => AsyncClient.IncrementValueInHashAsync(hashId, key, incrementBy, cancellationToken); + ValueTask IRedisHashAsync.IncrementValueAsync(string key, int incrementBy, CancellationToken token) + => AsyncClient.IncrementValueInHashAsync(hashId, key, incrementBy, token); - ValueTask IRedisHashAsync.RemoveAsync(string key, CancellationToken cancellationToken) - => AsyncClient.RemoveEntryFromHashAsync(hashId, key, cancellationToken); + ValueTask IRedisHashAsync.RemoveAsync(string key, CancellationToken token) + => AsyncClient.RemoveEntryFromHashAsync(hashId, key, token); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientList.Async.cs b/src/ServiceStack.Redis/RedisClientList.Async.cs index a79b4bd4..adb1137e 100644 --- a/src/ServiceStack.Redis/RedisClientList.Async.cs +++ b/src/ServiceStack.Redis/RedisClientList.Async.cs @@ -24,37 +24,37 @@ internal partial class RedisClientList private IRedisClientAsync AsyncClient => client; private IRedisListAsync AsAsync() => this; - ValueTask IRedisListAsync.AppendAsync(string value, CancellationToken cancellationToken) - => AsyncClient.AddItemToListAsync(listId, value, cancellationToken); + ValueTask IRedisListAsync.AppendAsync(string value, CancellationToken token) + => AsyncClient.AddItemToListAsync(listId, value, token); - ValueTask IRedisListAsync.BlockingDequeueAsync(TimeSpan? timeOut, CancellationToken cancellationToken) - => AsyncClient.BlockingDequeueItemFromListAsync(listId, timeOut, cancellationToken); + ValueTask IRedisListAsync.BlockingDequeueAsync(TimeSpan? timeOut, CancellationToken token) + => AsyncClient.BlockingDequeueItemFromListAsync(listId, timeOut, token); - ValueTask IRedisListAsync.BlockingPopAsync(TimeSpan? timeOut, CancellationToken cancellationToken) - => AsyncClient.BlockingPopItemFromListAsync(listId, timeOut, cancellationToken); + ValueTask IRedisListAsync.BlockingPopAsync(TimeSpan? timeOut, CancellationToken token) + => AsyncClient.BlockingPopItemFromListAsync(listId, timeOut, token); - ValueTask IRedisListAsync.BlockingRemoveStartAsync(TimeSpan? timeOut, CancellationToken cancellationToken) - => AsyncClient.BlockingRemoveStartFromListAsync(listId, timeOut, cancellationToken); + ValueTask IRedisListAsync.BlockingRemoveStartAsync(TimeSpan? timeOut, CancellationToken token) + => AsyncClient.BlockingRemoveStartFromListAsync(listId, timeOut, token); - ValueTask IRedisListAsync.CountAsync(CancellationToken cancellationToken) - => AsyncClient.GetListCountAsync(listId, cancellationToken).AsInt32(); + ValueTask IRedisListAsync.CountAsync(CancellationToken token) + => AsyncClient.GetListCountAsync(listId, token).AsInt32(); - ValueTask IRedisListAsync.DequeueAsync(CancellationToken cancellationToken) + ValueTask IRedisListAsync.DequeueAsync(CancellationToken token) => AsyncClient.DequeueItemFromListAsync(listId); - ValueTask IRedisListAsync.EnqueueAsync(string value, CancellationToken cancellationToken) - => AsyncClient.EnqueueItemOnListAsync(listId, value, cancellationToken); + ValueTask IRedisListAsync.EnqueueAsync(string value, CancellationToken token) + => AsyncClient.EnqueueItemOnListAsync(listId, value, token); - ValueTask> IRedisListAsync.GetAllAsync(CancellationToken cancellationToken) - => AsyncClient.GetAllItemsFromListAsync(listId, cancellationToken); + ValueTask> IRedisListAsync.GetAllAsync(CancellationToken token) + => AsyncClient.GetAllItemsFromListAsync(listId, token); - async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) + async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken token) { - var count = await AsAsync().CountAsync(cancellationToken).ConfigureAwait(false); + var count = await AsAsync().CountAsync(token).ConfigureAwait(false); if (count <= PageLimit) { - var all = await AsyncClient.GetAllItemsFromListAsync(listId, cancellationToken).ConfigureAwait(false); + var all = await AsyncClient.GetAllItemsFromListAsync(listId, token).ConfigureAwait(false); foreach (var item in all) { yield return item; @@ -67,7 +67,7 @@ async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(Cance List pageResults; do { - pageResults = await AsyncClient.GetRangeFromListAsync(listId, skip, skip + PageLimit - 1, cancellationToken).ConfigureAwait(false); + pageResults = await AsyncClient.GetRangeFromListAsync(listId, skip, skip + PageLimit - 1, token).ConfigureAwait(false); foreach (var result in pageResults) { yield return result; @@ -77,74 +77,74 @@ async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(Cance } } - ValueTask> IRedisListAsync.GetRangeAsync(int startingFrom, int endingAt, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromListAsync(listId, startingFrom, endingAt, cancellationToken); + ValueTask> IRedisListAsync.GetRangeAsync(int startingFrom, int endingAt, CancellationToken token) + => AsyncClient.GetRangeFromListAsync(listId, startingFrom, endingAt, token); - ValueTask> IRedisListAsync.GetRangeFromSortedListAsync(int startingFrom, int endingAt, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedListAsync(listId, startingFrom, endingAt, cancellationToken); + ValueTask> IRedisListAsync.GetRangeFromSortedListAsync(int startingFrom, int endingAt, CancellationToken token) + => AsyncClient.GetRangeFromSortedListAsync(listId, startingFrom, endingAt, token); - ValueTask IRedisListAsync.PopAndPushAsync(IRedisListAsync toList, CancellationToken cancellationToken) - => AsyncClient.PopAndPushItemBetweenListsAsync(listId, toList.Id, cancellationToken); + ValueTask IRedisListAsync.PopAndPushAsync(IRedisListAsync toList, CancellationToken token) + => AsyncClient.PopAndPushItemBetweenListsAsync(listId, toList.Id, token); - ValueTask IRedisListAsync.PopAsync(CancellationToken cancellationToken) - => AsyncClient.PopItemFromListAsync(listId, cancellationToken); + ValueTask IRedisListAsync.PopAsync(CancellationToken token) + => AsyncClient.PopItemFromListAsync(listId, token); - ValueTask IRedisListAsync.PrependAsync(string value, CancellationToken cancellationToken) - => AsyncClient.PrependItemToListAsync(listId, value, cancellationToken); + ValueTask IRedisListAsync.PrependAsync(string value, CancellationToken token) + => AsyncClient.PrependItemToListAsync(listId, value, token); - ValueTask IRedisListAsync.PushAsync(string value, CancellationToken cancellationToken) - => AsyncClient.PushItemToListAsync(listId, value, cancellationToken); + ValueTask IRedisListAsync.PushAsync(string value, CancellationToken token) + => AsyncClient.PushItemToListAsync(listId, value, token); - ValueTask IRedisListAsync.RemoveAllAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveAllFromListAsync(listId, cancellationToken); + ValueTask IRedisListAsync.RemoveAllAsync(CancellationToken token) + => AsyncClient.RemoveAllFromListAsync(listId, token); - ValueTask IRedisListAsync.RemoveEndAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveEndFromListAsync(listId, cancellationToken); + ValueTask IRedisListAsync.RemoveEndAsync(CancellationToken token) + => AsyncClient.RemoveEndFromListAsync(listId, token); - ValueTask IRedisListAsync.RemoveStartAsync(CancellationToken cancellationToken) - => AsyncClient.RemoveStartFromListAsync(listId, cancellationToken); + ValueTask IRedisListAsync.RemoveStartAsync(CancellationToken token) + => AsyncClient.RemoveStartFromListAsync(listId, token); - ValueTask IRedisListAsync.RemoveValueAsync(string value, CancellationToken cancellationToken) - => AsyncClient.RemoveItemFromListAsync(listId, value, cancellationToken); + ValueTask IRedisListAsync.RemoveValueAsync(string value, CancellationToken token) + => AsyncClient.RemoveItemFromListAsync(listId, value, token); - ValueTask IRedisListAsync.RemoveValueAsync(string value, int noOfMatches, CancellationToken cancellationToken) - => AsyncClient.RemoveItemFromListAsync(listId, value, noOfMatches, cancellationToken); + ValueTask IRedisListAsync.RemoveValueAsync(string value, int noOfMatches, CancellationToken token) + => AsyncClient.RemoveItemFromListAsync(listId, value, noOfMatches, token); - ValueTask IRedisListAsync.TrimAsync(int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken) - => AsyncClient.TrimListAsync(listId, keepStartingFrom, keepEndingAt, cancellationToken); + ValueTask IRedisListAsync.TrimAsync(int keepStartingFrom, int keepEndingAt, CancellationToken token) + => AsyncClient.TrimListAsync(listId, keepStartingFrom, keepEndingAt, token); - async ValueTask IRedisListAsync.RemoveAsync(string value, CancellationToken cancellationToken) - => (await AsyncClient.RemoveItemFromListAsync(listId, value, cancellationToken).ConfigureAwait(false)) > 0; + async ValueTask IRedisListAsync.RemoveAsync(string value, CancellationToken token) + => (await AsyncClient.RemoveItemFromListAsync(listId, value, token).ConfigureAwait(false)) > 0; - ValueTask IRedisListAsync.AddAsync(string value, CancellationToken cancellationToken) - => AsyncClient.AddItemToListAsync(listId, value, cancellationToken); + ValueTask IRedisListAsync.AddAsync(string value, CancellationToken token) + => AsyncClient.AddItemToListAsync(listId, value, token); - async ValueTask IRedisListAsync.RemoveAtAsync(int index, CancellationToken cancellationToken) + async ValueTask IRedisListAsync.RemoveAtAsync(int index, CancellationToken token) { //TODO: replace with native implementation when one exists var markForDelete = Guid.NewGuid().ToString(); - await AsyncClient.SetItemInListAsync(listId, index, markForDelete, cancellationToken).ConfigureAwait(false); - await AsyncClient.RemoveItemFromListAsync(listId, markForDelete, cancellationToken).ConfigureAwait(false); + await AsyncClient.SetItemInListAsync(listId, index, markForDelete, token).ConfigureAwait(false); + await AsyncClient.RemoveItemFromListAsync(listId, markForDelete, token).ConfigureAwait(false); } - async ValueTask IRedisListAsync.ContainsAsync(string value, CancellationToken cancellationToken) + async ValueTask IRedisListAsync.ContainsAsync(string value, CancellationToken token) { //TODO: replace with native implementation when exists - await foreach (var existingItem in this.ConfigureAwait(false).WithCancellation(cancellationToken)) + await foreach (var existingItem in this.ConfigureAwait(false).WithCancellation(token)) { if (existingItem == value) return true; } return false; } - ValueTask IRedisListAsync.ClearAsync(CancellationToken cancellationToken) + ValueTask IRedisListAsync.ClearAsync(CancellationToken token) => AsyncClient.RemoveAllFromListAsync(listId); - async ValueTask IRedisListAsync.IndexOfAsync(string value, CancellationToken cancellationToken) + async ValueTask IRedisListAsync.IndexOfAsync(string value, CancellationToken token) { //TODO: replace with native implementation when exists var i = 0; - await foreach (var existingItem in this.ConfigureAwait(false).WithCancellation(cancellationToken)) + await foreach (var existingItem in this.ConfigureAwait(false).WithCancellation(token)) { if (existingItem == value) return i; i++; @@ -152,10 +152,10 @@ async ValueTask IRedisListAsync.IndexOfAsync(string value, CancellationToke return -1; } - ValueTask IRedisListAsync.ElementAtAsync(int index, CancellationToken cancellationToken) + ValueTask IRedisListAsync.ElementAtAsync(int index, CancellationToken token) => AsyncClient.GetItemFromListAsync(listId, index); - ValueTask IRedisListAsync.SetValueAsync(int index, string value, CancellationToken cancellationToken) + ValueTask IRedisListAsync.SetValueAsync(int index, string value, CancellationToken token) => AsyncClient.SetItemInListAsync(listId, index, value); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs b/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs index 399f6d8e..5febd0bd 100644 --- a/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs @@ -15,151 +15,151 @@ ValueTask IAsyncDisposable.DisposeAsync() return default; } - private ValueTask GetClientAsync(in CancellationToken cancellationToken) + private ValueTask GetClientAsync(in CancellationToken token) { AssertNotReadOnly(); - return redisManager.GetClientAsync(cancellationToken); + return redisManager.GetClientAsync(token); } - async Task ICacheClientAsync.GetAsync(string key, CancellationToken cancellationToken) + async Task ICacheClientAsync.GetAsync(string key, CancellationToken token) { - await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.GetAsync(key, cancellationToken).ConfigureAwait(false); + await using var client = await redisManager.GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); + return await client.GetAsync(key, token).ConfigureAwait(false); } - async Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.SetAsync(key, value, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + return await client.SetAsync(key, value, token).ConfigureAwait(false); } - async Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.SetAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresAt, token).ConfigureAwait(false); } - async Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.SetAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresIn, token).ConfigureAwait(false); } - async Task ICacheClientAsync.FlushAllAsync(CancellationToken cancellationToken) + async Task ICacheClientAsync.FlushAllAsync(CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await client.FlushAllAsync(cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + await client.FlushAllAsync(token).ConfigureAwait(false); } - async Task> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken cancellationToken) + async Task> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken token) { - await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.GetAllAsync(keys, cancellationToken).ConfigureAwait(false); + await using var client = await redisManager.GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); + return await client.GetAllAsync(keys, token).ConfigureAwait(false); } - async Task ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken cancellationToken) + async Task ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await client.SetAllAsync(values, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + await client.SetAllAsync(values, token).ConfigureAwait(false); } - async Task ICacheClientAsync.RemoveAsync(string key, CancellationToken cancellationToken) + async Task ICacheClientAsync.RemoveAsync(string key, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.RemoveAsync(key, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + return await client.RemoveAsync(key, token).ConfigureAwait(false); } - async Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken cancellationToken) + async Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken token) { - await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - return await client.GetTimeToLiveAsync(key, cancellationToken).ConfigureAwait(false); + await using var client = await redisManager.GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); + return await client.GetTimeToLiveAsync(key, token).ConfigureAwait(false); } - async IAsyncEnumerable ICacheClientAsync.GetKeysByPatternAsync(string pattern, [EnumeratorCancellation] CancellationToken cancellationToken) + async IAsyncEnumerable ICacheClientAsync.GetKeysByPatternAsync(string pattern, [EnumeratorCancellation] CancellationToken token) { - await using var client = await redisManager.GetReadOnlyCacheClientAsync(cancellationToken).ConfigureAwait(false); - await foreach (var key in client.GetKeysByPatternAsync(pattern, cancellationToken).ConfigureAwait(false).WithCancellation(cancellationToken)) + await using var client = await redisManager.GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); + await foreach (var key in client.GetKeysByPatternAsync(pattern, token).ConfigureAwait(false).WithCancellation(token)) { yield return key; } } - Task ICacheClientAsync.RemoveExpiredEntriesAsync(CancellationToken cancellationToken) + Task ICacheClientAsync.RemoveExpiredEntriesAsync(CancellationToken token) { //Redis automatically removed expired Cache Entries return Task.CompletedTask; } - async Task IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, CancellationToken cancellationToken) + async Task IRemoveByPatternAsync.RemoveByPatternAsync(string pattern, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); if (client is IRemoveByPatternAsync redisClient) { - await redisClient.RemoveByPatternAsync(pattern, cancellationToken).ConfigureAwait(false); + await redisClient.RemoveByPatternAsync(pattern, token).ConfigureAwait(false); } } - async Task IRemoveByPatternAsync.RemoveByRegexAsync(string regex, CancellationToken cancellationToken) + async Task IRemoveByPatternAsync.RemoveByRegexAsync(string regex, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); if (client is IRemoveByPatternAsync redisClient) { - await redisClient.RemoveByRegexAsync(regex, cancellationToken).ConfigureAwait(false); + await redisClient.RemoveByRegexAsync(regex, token).ConfigureAwait(false); } } - async Task ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken cancellationToken) + async Task ICacheClientAsync.RemoveAllAsync(IEnumerable keys, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - await client.RemoveAllAsync(keys, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + await client.RemoveAllAsync(keys, token).ConfigureAwait(false); } - async Task ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken cancellationToken) + async Task ICacheClientAsync.IncrementAsync(string key, uint amount, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.IncrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + return await client.IncrementAsync(key, amount, token).ConfigureAwait(false); } - async Task ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken cancellationToken) + async Task ICacheClientAsync.DecrementAsync(string key, uint amount, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.DecrementAsync(key, amount, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + return await client.DecrementAsync(key, amount, token).ConfigureAwait(false); } - async Task ICacheClientAsync.AddAsync(string key, T value, CancellationToken cancellationToken) + async Task ICacheClientAsync.AddAsync(string key, T value, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.AddAsync(key, value, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + return await client.AddAsync(key, value, token).ConfigureAwait(false); } - async Task ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken cancellationToken) + async Task ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, token).ConfigureAwait(false); } - async Task ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + async Task ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.AddAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresAt, token).ConfigureAwait(false); } - async Task ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken cancellationToken) + async Task ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, expiresAt, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresAt, token).ConfigureAwait(false); } - async Task ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + async Task ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.AddAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresIn, token).ConfigureAwait(false); } - async Task ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken cancellationToken) + async Task ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) { - await using var client = await GetClientAsync(cancellationToken).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, expiresIn, cancellationToken).ConfigureAwait(false); + await using var client = await GetClientAsync(token).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresIn, token).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientSet.Async.cs b/src/ServiceStack.Redis/RedisClientSet.Async.cs index 7d7aafcb..c1339f6d 100644 --- a/src/ServiceStack.Redis/RedisClientSet.Async.cs +++ b/src/ServiceStack.Redis/RedisClientSet.Async.cs @@ -24,40 +24,40 @@ internal partial class RedisClientSet private IRedisSetAsync AsAsync() => this; private IRedisClientAsync AsyncClient => client; - ValueTask IRedisSetAsync.AddAsync(string item, CancellationToken cancellationToken) - => AsyncClient.AddItemToSetAsync(setId, item, cancellationToken); + ValueTask IRedisSetAsync.AddAsync(string item, CancellationToken token) + => AsyncClient.AddItemToSetAsync(setId, item, token); - ValueTask IRedisSetAsync.ClearAsync(CancellationToken cancellationToken) - => new ValueTask(AsyncClient.RemoveAsync(setId, cancellationToken)); + ValueTask IRedisSetAsync.ClearAsync(CancellationToken token) + => new ValueTask(AsyncClient.RemoveAsync(setId, token)); - ValueTask IRedisSetAsync.ContainsAsync(string item, CancellationToken cancellationToken) - => AsyncClient.SetContainsItemAsync(setId, item, cancellationToken); + ValueTask IRedisSetAsync.ContainsAsync(string item, CancellationToken token) + => AsyncClient.SetContainsItemAsync(setId, item, token); - ValueTask IRedisSetAsync.CountAsync(CancellationToken cancellationToken) - => AsyncClient.GetSetCountAsync(setId, cancellationToken).AsInt32(); + ValueTask IRedisSetAsync.CountAsync(CancellationToken token) + => AsyncClient.GetSetCountAsync(setId, token).AsInt32(); - ValueTask> IRedisSetAsync.DiffAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken) + ValueTask> IRedisSetAsync.DiffAsync(IRedisSetAsync[] withSets, CancellationToken token) { var withSetIds = withSets.ToList().ConvertAll(x => x.Id).ToArray(); - return AsyncClient.GetDifferencesFromSetAsync(setId, withSetIds, cancellationToken); + return AsyncClient.GetDifferencesFromSetAsync(setId, withSetIds, token); } - ValueTask> IRedisSetAsync.GetAllAsync(CancellationToken cancellationToken) - => AsyncClient.GetAllItemsFromSetAsync(setId, cancellationToken); + ValueTask> IRedisSetAsync.GetAllAsync(CancellationToken token) + => AsyncClient.GetAllItemsFromSetAsync(setId, token); - IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) - => AsyncClient.ScanAllSetItemsAsync(setId).GetAsyncEnumerator(cancellationToken); // uses SSCAN + IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken token) + => AsyncClient.ScanAllSetItemsAsync(setId).GetAsyncEnumerator(token); // uses SSCAN - ValueTask IRedisSetAsync.GetRandomEntryAsync(CancellationToken cancellationToken) - => AsyncClient.GetRandomItemFromSetAsync(setId, cancellationToken); + ValueTask IRedisSetAsync.GetRandomEntryAsync(CancellationToken token) + => AsyncClient.GetRandomItemFromSetAsync(setId, token); - ValueTask> IRedisSetAsync.GetRangeFromSortedSetAsync(int startingFrom, int endingAt, CancellationToken cancellationToken) - => AsyncClient.GetSortedEntryValuesAsync(setId, startingFrom, endingAt, cancellationToken); + ValueTask> IRedisSetAsync.GetRangeFromSortedSetAsync(int startingFrom, int endingAt, CancellationToken token) + => AsyncClient.GetSortedEntryValuesAsync(setId, startingFrom, endingAt, token); - ValueTask> IRedisSetAsync.IntersectAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken) + ValueTask> IRedisSetAsync.IntersectAsync(IRedisSetAsync[] withSets, CancellationToken token) { var allSetIds = MergeSetIds(withSets); - return AsyncClient.GetIntersectFromSetsAsync(allSetIds.ToArray(), cancellationToken); + return AsyncClient.GetIntersectFromSetsAsync(allSetIds.ToArray(), token); } ValueTask> IRedisSetAsync.IntersectAsync(params IRedisSetAsync[] withSets) @@ -70,46 +70,46 @@ private List MergeSetIds(IRedisSetAsync[] withSets) return allSetIds; } - ValueTask IRedisSetAsync.MoveAsync(string value, IRedisSetAsync toSet, CancellationToken cancellationToken) - => AsyncClient.MoveBetweenSetsAsync(setId, toSet.Id, value, cancellationToken); + ValueTask IRedisSetAsync.MoveAsync(string value, IRedisSetAsync toSet, CancellationToken token) + => AsyncClient.MoveBetweenSetsAsync(setId, toSet.Id, value, token); - ValueTask IRedisSetAsync.PopAsync(CancellationToken cancellationToken) - => AsyncClient.PopItemFromSetAsync(setId, cancellationToken); + ValueTask IRedisSetAsync.PopAsync(CancellationToken token) + => AsyncClient.PopItemFromSetAsync(setId, token); - ValueTask IRedisSetAsync.RemoveAsync(string item, CancellationToken cancellationToken) - => AsyncClient.RemoveItemFromSetAsync(setId, item, cancellationToken).AwaitAsTrue(); // see Remove for why true + ValueTask IRedisSetAsync.RemoveAsync(string item, CancellationToken token) + => AsyncClient.RemoveItemFromSetAsync(setId, item, token).AwaitAsTrue(); // see Remove for why true - ValueTask IRedisSetAsync.StoreDiffAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken cancellationToken) + ValueTask IRedisSetAsync.StoreDiffAsync(IRedisSetAsync fromSet, IRedisSetAsync[] withSets, CancellationToken token) { var withSetIds = withSets.ToList().ConvertAll(x => x.Id).ToArray(); - return AsyncClient.StoreDifferencesFromSetAsync(setId, fromSet.Id, withSetIds, cancellationToken); + return AsyncClient.StoreDifferencesFromSetAsync(setId, fromSet.Id, withSetIds, token); } ValueTask IRedisSetAsync.StoreDiffAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets) => AsAsync().StoreDiffAsync(fromSet, withSets, cancellationToken: default); - ValueTask IRedisSetAsync.StoreIntersectAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken) + ValueTask IRedisSetAsync.StoreIntersectAsync(IRedisSetAsync[] withSets, CancellationToken token) { var withSetIds = withSets.ToList().ConvertAll(x => x.Id).ToArray(); - return AsyncClient.StoreIntersectFromSetsAsync(setId, withSetIds, cancellationToken); + return AsyncClient.StoreIntersectFromSetsAsync(setId, withSetIds, token); } ValueTask IRedisSetAsync.StoreIntersectAsync(params IRedisSetAsync[] withSets) => AsAsync().StoreIntersectAsync(withSets, cancellationToken: default); - ValueTask IRedisSetAsync.StoreUnionAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken) + ValueTask IRedisSetAsync.StoreUnionAsync(IRedisSetAsync[] withSets, CancellationToken token) { var withSetIds = withSets.ToList().ConvertAll(x => x.Id).ToArray(); - return AsyncClient.StoreUnionFromSetsAsync(setId, withSetIds, cancellationToken); + return AsyncClient.StoreUnionFromSetsAsync(setId, withSetIds, token); } ValueTask IRedisSetAsync.StoreUnionAsync(params IRedisSetAsync[] withSets) => AsAsync().StoreUnionAsync(withSets, cancellationToken: default); - ValueTask> IRedisSetAsync.UnionAsync(IRedisSetAsync[] withSets, CancellationToken cancellationToken) + ValueTask> IRedisSetAsync.UnionAsync(IRedisSetAsync[] withSets, CancellationToken token) { var allSetIds = MergeSetIds(withSets); - return AsyncClient.GetUnionFromSetsAsync(allSetIds.ToArray(), cancellationToken); + return AsyncClient.GetUnionFromSetsAsync(allSetIds.ToArray(), token); } ValueTask> IRedisSetAsync.UnionAsync(params IRedisSetAsync[] withSets) diff --git a/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs b/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs index 692242dd..06c2e43f 100644 --- a/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs +++ b/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs @@ -22,81 +22,81 @@ internal partial class RedisClientSortedSet { private IRedisClientAsync AsyncClient => client; - ValueTask IRedisSortedSetAsync.AddAsync(string value, CancellationToken cancellationToken) - => AsyncClient.AddItemToSortedSetAsync(setId, value, cancellationToken).Await(); + ValueTask IRedisSortedSetAsync.AddAsync(string value, CancellationToken token) + => AsyncClient.AddItemToSortedSetAsync(setId, value, token).Await(); private IRedisSortedSetAsync AsAsync() => this; - ValueTask IRedisSortedSetAsync.ClearAsync(CancellationToken cancellationToken) - => new ValueTask(AsyncClient.RemoveAsync(setId, cancellationToken)); + ValueTask IRedisSortedSetAsync.ClearAsync(CancellationToken token) + => new ValueTask(AsyncClient.RemoveAsync(setId, token)); - ValueTask IRedisSortedSetAsync.ContainsAsync(string value, CancellationToken cancellationToken) - => AsyncClient.SortedSetContainsItemAsync(setId, value, cancellationToken); + ValueTask IRedisSortedSetAsync.ContainsAsync(string value, CancellationToken token) + => AsyncClient.SortedSetContainsItemAsync(setId, value, token); - ValueTask IRedisSortedSetAsync.CountAsync(CancellationToken cancellationToken) - => AsyncClient.GetSortedSetCountAsync(setId, cancellationToken).AsInt32(); + ValueTask IRedisSortedSetAsync.CountAsync(CancellationToken token) + => AsyncClient.GetSortedSetCountAsync(setId, token).AsInt32(); - ValueTask> IRedisSortedSetAsync.GetAllAsync(CancellationToken cancellationToken) - => AsyncClient.GetAllItemsFromSortedSetAsync(setId, cancellationToken); + ValueTask> IRedisSortedSetAsync.GetAllAsync(CancellationToken token) + => AsyncClient.GetAllItemsFromSortedSetAsync(setId, token); - async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) + async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken token) { // uses ZSCAN - await foreach (var pair in AsyncClient.ScanAllSortedSetItemsAsync(setId, cancellationToken: cancellationToken).ConfigureAwait(false)) + await foreach (var pair in AsyncClient.ScanAllSortedSetItemsAsync(setId, token: token).ConfigureAwait(false)) { yield return pair.Key; } } - ValueTask IRedisSortedSetAsync.GetItemIndexAsync(string value, CancellationToken cancellationToken) - => AsyncClient.GetItemIndexInSortedSetAsync(setId, value, cancellationToken); + ValueTask IRedisSortedSetAsync.GetItemIndexAsync(string value, CancellationToken token) + => AsyncClient.GetItemIndexInSortedSetAsync(setId, value, token); - ValueTask IRedisSortedSetAsync.GetItemScoreAsync(string value, CancellationToken cancellationToken) - => AsyncClient.GetItemScoreInSortedSetAsync(setId, value, cancellationToken); + ValueTask IRedisSortedSetAsync.GetItemScoreAsync(string value, CancellationToken token) + => AsyncClient.GetItemScoreInSortedSetAsync(setId, value, token); - ValueTask> IRedisSortedSetAsync.GetRangeAsync(int startingRank, int endingRank, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetAsync(setId, startingRank, endingRank, cancellationToken); + ValueTask> IRedisSortedSetAsync.GetRangeAsync(int startingRank, int endingRank, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetAsync(setId, startingRank, endingRank, token); - ValueTask> IRedisSortedSetAsync.GetRangeByScoreAsync(string fromStringScore, string toStringScore, CancellationToken cancellationToken) - => AsAsync().GetRangeByScoreAsync(fromStringScore, toStringScore, null, null, cancellationToken); + ValueTask> IRedisSortedSetAsync.GetRangeByScoreAsync(string fromStringScore, string toStringScore, CancellationToken token) + => AsAsync().GetRangeByScoreAsync(fromStringScore, toStringScore, null, null, token); - ValueTask> IRedisSortedSetAsync.GetRangeByScoreAsync(string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(setId, fromStringScore, toStringScore, skip, take, cancellationToken); + ValueTask> IRedisSortedSetAsync.GetRangeByScoreAsync(string fromStringScore, string toStringScore, int? skip, int? take, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(setId, fromStringScore, toStringScore, skip, take, token); - ValueTask> IRedisSortedSetAsync.GetRangeByScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken) - => AsAsync().GetRangeByScoreAsync(fromScore, toScore, null, null, cancellationToken); + ValueTask> IRedisSortedSetAsync.GetRangeByScoreAsync(double fromScore, double toScore, CancellationToken token) + => AsAsync().GetRangeByScoreAsync(fromScore, toScore, null, null, token); - ValueTask> IRedisSortedSetAsync.GetRangeByScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken cancellationToken) - => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, skip, take, cancellationToken); + ValueTask> IRedisSortedSetAsync.GetRangeByScoreAsync(double fromScore, double toScore, int? skip, int? take, CancellationToken token) + => AsyncClient.GetRangeFromSortedSetByLowestScoreAsync(setId, fromScore, toScore, skip, take, token); - ValueTask IRedisSortedSetAsync.IncrementItemScoreAsync(string value, double incrementByScore, CancellationToken cancellationToken) - => AsyncClient.IncrementItemInSortedSetAsync(setId, value, incrementByScore, cancellationToken).Await(); + ValueTask IRedisSortedSetAsync.IncrementItemScoreAsync(string value, double incrementByScore, CancellationToken token) + => AsyncClient.IncrementItemInSortedSetAsync(setId, value, incrementByScore, token).Await(); - ValueTask IRedisSortedSetAsync.PopItemWithHighestScoreAsync(CancellationToken cancellationToken) - => AsyncClient.PopItemWithHighestScoreFromSortedSetAsync(setId, cancellationToken); + ValueTask IRedisSortedSetAsync.PopItemWithHighestScoreAsync(CancellationToken token) + => AsyncClient.PopItemWithHighestScoreFromSortedSetAsync(setId, token); - ValueTask IRedisSortedSetAsync.PopItemWithLowestScoreAsync(CancellationToken cancellationToken) - => AsyncClient.PopItemWithLowestScoreFromSortedSetAsync(setId, cancellationToken); + ValueTask IRedisSortedSetAsync.PopItemWithLowestScoreAsync(CancellationToken token) + => AsyncClient.PopItemWithLowestScoreFromSortedSetAsync(setId, token); - ValueTask IRedisSortedSetAsync.RemoveAsync(string value, CancellationToken cancellationToken) - => AsyncClient.RemoveItemFromSortedSetAsync(setId, value, cancellationToken).AwaitAsTrue(); // see Remove() for why "true" + ValueTask IRedisSortedSetAsync.RemoveAsync(string value, CancellationToken token) + => AsyncClient.RemoveItemFromSortedSetAsync(setId, value, token).AwaitAsTrue(); // see Remove() for why "true" - ValueTask IRedisSortedSetAsync.RemoveRangeAsync(int fromRank, int toRank, CancellationToken cancellationToken) - => AsyncClient.RemoveRangeFromSortedSetAsync(setId, fromRank, toRank, cancellationToken).Await(); + ValueTask IRedisSortedSetAsync.RemoveRangeAsync(int fromRank, int toRank, CancellationToken token) + => AsyncClient.RemoveRangeFromSortedSetAsync(setId, fromRank, toRank, token).Await(); - ValueTask IRedisSortedSetAsync.RemoveRangeByScoreAsync(double fromScore, double toScore, CancellationToken cancellationToken) - => AsyncClient.RemoveRangeFromSortedSetByScoreAsync(setId, fromScore, toScore, cancellationToken).Await(); + ValueTask IRedisSortedSetAsync.RemoveRangeByScoreAsync(double fromScore, double toScore, CancellationToken token) + => AsyncClient.RemoveRangeFromSortedSetByScoreAsync(setId, fromScore, toScore, token).Await(); - ValueTask IRedisSortedSetAsync.StoreFromIntersectAsync(IRedisSortedSetAsync[] ofSets, CancellationToken cancellationToken) - => AsyncClient.StoreIntersectFromSortedSetsAsync(setId, ofSets.GetIds(), cancellationToken).Await(); + ValueTask IRedisSortedSetAsync.StoreFromIntersectAsync(IRedisSortedSetAsync[] ofSets, CancellationToken token) + => AsyncClient.StoreIntersectFromSortedSetsAsync(setId, ofSets.GetIds(), token).Await(); ValueTask IRedisSortedSetAsync.StoreFromIntersectAsync(params IRedisSortedSetAsync[] ofSets) - => AsAsync().StoreFromIntersectAsync(ofSets, cancellationToken: default); + => AsAsync().StoreFromIntersectAsync(ofSets, token: default); - ValueTask IRedisSortedSetAsync.StoreFromUnionAsync(IRedisSortedSetAsync[] ofSets, CancellationToken cancellationToken) - => AsyncClient.StoreUnionFromSortedSetsAsync(setId, ofSets.GetIds(), cancellationToken).Await(); + ValueTask IRedisSortedSetAsync.StoreFromUnionAsync(IRedisSortedSetAsync[] ofSets, CancellationToken token) + => AsyncClient.StoreUnionFromSortedSetsAsync(setId, ofSets.GetIds(), token).Await(); ValueTask IRedisSortedSetAsync.StoreFromUnionAsync(params IRedisSortedSetAsync[] ofSets) - => AsAsync().StoreFromUnionAsync(ofSets, cancellationToken: default); + => AsAsync().StoreFromUnionAsync(ofSets, token: default); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs b/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs index a8b917c3..123396ac 100644 --- a/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs +++ b/src/ServiceStack.Redis/RedisClientsManagerExtensions.Async.cs @@ -40,31 +40,31 @@ public static partial class RedisClientsManagerExtensions private static T InvalidAsyncClient(IRedisClientsManager manager, string method) where T : class => throw new NotSupportedException($"The client returned from '{manager?.GetType().FullName ?? "(null)"}.{method}()' does not implement {typeof(T).Name}"); - public static ValueTask GetClientAsync(this IRedisClientsManager redisManager, CancellationToken cancellationToken = default) + public static ValueTask GetClientAsync(this IRedisClientsManager redisManager, CancellationToken token = default) { return redisManager is IRedisClientsManagerAsync asyncManager - ? asyncManager.GetClientAsync(cancellationToken) + ? asyncManager.GetClientAsync(token) : (redisManager.GetClient() as IRedisClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetClient))).AsValueTaskResult(); } - public static ValueTask GetReadOnlyClientAsync(this IRedisClientsManager redisManager, CancellationToken cancellationToken = default) + public static ValueTask GetReadOnlyClientAsync(this IRedisClientsManager redisManager, CancellationToken token = default) { return redisManager is IRedisClientsManagerAsync asyncManager - ? asyncManager.GetReadOnlyClientAsync(cancellationToken) + ? asyncManager.GetReadOnlyClientAsync(token) : (redisManager.GetReadOnlyClient() as IRedisClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetReadOnlyClient))).AsValueTaskResult(); } - public static ValueTask GetCacheClientAsync(this IRedisClientsManager redisManager, CancellationToken cancellationToken = default) + public static ValueTask GetCacheClientAsync(this IRedisClientsManager redisManager, CancellationToken token = default) { return redisManager is IRedisClientsManagerAsync asyncManager - ? asyncManager.GetCacheClientAsync(cancellationToken) + ? asyncManager.GetCacheClientAsync(token) : (redisManager.GetCacheClient() as ICacheClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetCacheClient))).AsValueTaskResult(); } - public static ValueTask GetReadOnlyCacheClientAsync(this IRedisClientsManager redisManager, CancellationToken cancellationToken = default) + public static ValueTask GetReadOnlyCacheClientAsync(this IRedisClientsManager redisManager, CancellationToken token = default) { return redisManager is IRedisClientsManagerAsync asyncManager - ? asyncManager.GetReadOnlyCacheClientAsync(cancellationToken) + ? asyncManager.GetReadOnlyCacheClientAsync(token) : (redisManager.GetReadOnlyCacheClient() as ICacheClientAsync ?? InvalidAsyncClient(redisManager, nameof(redisManager.GetCacheClient))).AsValueTaskResult(); } diff --git a/src/ServiceStack.Redis/RedisLock.Async.cs b/src/ServiceStack.Redis/RedisLock.Async.cs index 2b026b6a..8466c11f 100644 --- a/src/ServiceStack.Redis/RedisLock.Async.cs +++ b/src/ServiceStack.Redis/RedisLock.Async.cs @@ -10,24 +10,24 @@ public partial class RedisLock : IAsyncDisposable { internal static ValueTask CreateAsync(IRedisClientAsync redisClient, string key, - TimeSpan? timeOut = default, CancellationToken cancellationToken = default) + TimeSpan? timeOut = default, CancellationToken token = default) { var obj = new RedisLock(redisClient, key); - return obj.AcquireAsync(timeOut, cancellationToken).Await(obj); + return obj.AcquireAsync(timeOut, token).Await(obj); } // async version of ExecUtils.RetryUntilTrue private static async ValueTask RetryUntilTrue(Func> action, - TimeSpan? timeOut = null, CancellationToken cancellationToken = default) + TimeSpan? timeOut = null, CancellationToken token = default) { var i = 0; var firstAttempt = DateTime.UtcNow; while (timeOut == null || DateTime.UtcNow - firstAttempt < timeOut.Value) { - cancellationToken.ThrowIfCancellationRequested(); + token.ThrowIfCancellationRequested(); i++; - if (await action(cancellationToken).ConfigureAwait(false)) + if (await action(token).ConfigureAwait(false)) { return; } @@ -38,7 +38,7 @@ private static async ValueTask RetryUntilTrue(Func r.SetValueAsync(key, lockString)); return await trans.CommitAsync(ct).ConfigureAwait(false); //returns false if Transaction failed }, - timeOut, cancellationToken + timeOut, token ).ConfigureAwait(false); } diff --git a/src/ServiceStack.Redis/RedisManagerPool.Async.cs b/src/ServiceStack.Redis/RedisManagerPool.Async.cs index 74a48d51..aaf5e33b 100644 --- a/src/ServiceStack.Redis/RedisManagerPool.Async.cs +++ b/src/ServiceStack.Redis/RedisManagerPool.Async.cs @@ -12,16 +12,16 @@ namespace ServiceStack.Redis public partial class RedisManagerPool : IRedisClientsManagerAsync { - ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(CancellationToken cancellationToken) + ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(CancellationToken token) => new RedisClientManagerCacheClient(this).AsValueTaskResult(); - ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken cancellationToken) + ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken token) => GetClient(true).AsValueTaskResult(); - ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken cancellationToken) + ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken token) => new RedisClientManagerCacheClient(this) { ReadOnly = true }.AsValueTaskResult(); - ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken cancellationToken) + ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken token) => GetClient(true).AsValueTaskResult(); ValueTask IAsyncDisposable.DisposeAsync() diff --git a/src/ServiceStack.Redis/RedisNativeClient.Async.cs b/src/ServiceStack.Redis/RedisNativeClient.Async.cs index 6ff971fc..cb915664 100644 --- a/src/ServiceStack.Redis/RedisNativeClient.Async.cs +++ b/src/ServiceStack.Redis/RedisNativeClient.Async.cs @@ -33,16 +33,16 @@ ValueTask IAsyncDisposable.DisposeAsync() return default; } - ValueTask IRedisNativeClientAsync.TimeAsync(CancellationToken cancellationToken) - => SendExpectMultiDataAsync(cancellationToken, Commands.Time); + ValueTask IRedisNativeClientAsync.TimeAsync(CancellationToken token) + => SendExpectMultiDataAsync(token, Commands.Time); - ValueTask IRedisNativeClientAsync.ExistsAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ExistsAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.Exists, key.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.Exists, key.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.SetAsync(string key, byte[] value, bool exists, long expirySeconds, long expiryMilliseconds, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SetAsync(string key, byte[] value, bool exists, long expirySeconds, long expiryMilliseconds, CancellationToken token) { AssertNotNull(key); value ??= TypeConstants.EmptyByteArray; @@ -65,9 +65,9 @@ ValueTask IRedisNativeClientAsync.SetAsync(string key, byte[] value, bool args = new[] { Commands.Set, key.ToUtf8Bytes(), value, entryExists }; } - return IsString(SendExpectStringAsync(cancellationToken, args), OK); + return IsString(SendExpectStringAsync(token, args), OK); } - ValueTask IRedisNativeClientAsync.SetAsync(string key, byte[] value, long expirySeconds, long expiryMilliseconds, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SetAsync(string key, byte[] value, long expirySeconds, long expiryMilliseconds, CancellationToken token) { AssertNotNull(key); value ??= TypeConstants.EmptyByteArray; @@ -89,154 +89,154 @@ ValueTask IRedisNativeClientAsync.SetAsync(string key, byte[] value, long expiry args = new[] { Commands.Set, key.ToUtf8Bytes(), value }; } - return SendExpectSuccessAsync(cancellationToken, args); + return SendExpectSuccessAsync(token, args); } - ValueTask IRedisNativeClientAsync.GetAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.GetAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectDataAsync(cancellationToken, Commands.Get, key.ToUtf8Bytes()); + return SendExpectDataAsync(token, Commands.Get, key.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.DelAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.DelAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.Del, key.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.Del, key.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.ScanAsync(ulong cursor, int count, string match, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ScanAsync(ulong cursor, int count, string match, CancellationToken token) { if (match == null) - return SendExpectScanResultAsync(cancellationToken, Commands.Scan, cursor.ToUtf8Bytes(), + return SendExpectScanResultAsync(token, Commands.Scan, cursor.ToUtf8Bytes(), Commands.Count, count.ToUtf8Bytes()); - return SendExpectScanResultAsync(cancellationToken, Commands.Scan, cursor.ToUtf8Bytes(), + return SendExpectScanResultAsync(token, Commands.Scan, cursor.ToUtf8Bytes(), Commands.Match, match.ToUtf8Bytes(), Commands.Count, count.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.TypeAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.TypeAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectCodeAsync(cancellationToken, Commands.Type, key.ToUtf8Bytes()); + return SendExpectCodeAsync(token, Commands.Type, key.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.RPushAsync(string listId, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.RPushAsync(string listId, byte[] value, CancellationToken token) { AssertListIdAndValue(listId, value); - return SendExpectLongAsync(cancellationToken, Commands.RPush, listId.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.RPush, listId.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.SAddAsync(string setId, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SAddAsync(string setId, byte[] value, CancellationToken token) { AssertSetIdAndValue(setId, value); - return SendExpectLongAsync(cancellationToken, Commands.SAdd, setId.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.SAdd, setId.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.ZAddAsync(string setId, double score, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZAddAsync(string setId, double score, byte[] value, CancellationToken token) { AssertSetIdAndValue(setId, value); - return SendExpectLongAsync(cancellationToken, Commands.ZAdd, setId.ToUtf8Bytes(), score.ToFastUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.ZAdd, setId.ToUtf8Bytes(), score.ToFastUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.ZAddAsync(string setId, long score, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZAddAsync(string setId, long score, byte[] value, CancellationToken token) { AssertSetIdAndValue(setId, value); - return SendExpectLongAsync(cancellationToken, Commands.ZAdd, setId.ToUtf8Bytes(), score.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.ZAdd, setId.ToUtf8Bytes(), score.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.HSetAsync(string hashId, byte[] key, byte[] value, CancellationToken cancellationToken) - => HSetAsync(hashId.ToUtf8Bytes(), key, value, cancellationToken); + ValueTask IRedisNativeClientAsync.HSetAsync(string hashId, byte[] key, byte[] value, CancellationToken token) + => HSetAsync(hashId.ToUtf8Bytes(), key, value, token); - internal ValueTask HSetAsync(byte[] hashId, byte[] key, byte[] value, CancellationToken cancellationToken = default) + internal ValueTask HSetAsync(byte[] hashId, byte[] key, byte[] value, CancellationToken token = default) { AssertHashIdAndKey(hashId, key); - return SendExpectLongAsync(cancellationToken, Commands.HSet, hashId, key, value); + return SendExpectLongAsync(token, Commands.HSet, hashId, key, value); } - ValueTask IRedisNativeClientAsync.RandomKeyAsync(CancellationToken cancellationToken) - => SendExpectDataAsync(cancellationToken, Commands.RandomKey).FromUtf8BytesAsync(); + ValueTask IRedisNativeClientAsync.RandomKeyAsync(CancellationToken token) + => SendExpectDataAsync(token, Commands.RandomKey).FromUtf8BytesAsync(); - ValueTask IRedisNativeClientAsync.RenameAsync(string oldKeyname, string newKeyname, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.RenameAsync(string oldKeyname, string newKeyname, CancellationToken token) { CheckRenameKeys(oldKeyname, newKeyname); - return SendExpectSuccessAsync(cancellationToken, Commands.Rename, oldKeyname.ToUtf8Bytes(), newKeyname.ToUtf8Bytes()); + return SendExpectSuccessAsync(token, Commands.Rename, oldKeyname.ToUtf8Bytes(), newKeyname.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.RenameNxAsync(string oldKeyname, string newKeyname, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.RenameNxAsync(string oldKeyname, string newKeyname, CancellationToken token) { CheckRenameKeys(oldKeyname, newKeyname); - return SendExpectLongAsync(cancellationToken, Commands.RenameNx, oldKeyname.ToUtf8Bytes(), newKeyname.ToUtf8Bytes()).IsSuccessAsync(); + return SendExpectLongAsync(token, Commands.RenameNx, oldKeyname.ToUtf8Bytes(), newKeyname.ToUtf8Bytes()).IsSuccessAsync(); } - ValueTask IRedisNativeClientAsync.MSetAsync(byte[][] keys, byte[][] values, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.MSetAsync(byte[][] keys, byte[][] values, CancellationToken token) { var keysAndValues = MergeCommandWithKeysAndValues(Commands.MSet, keys, values); - return SendExpectSuccessAsync(cancellationToken, keysAndValues); + return SendExpectSuccessAsync(token, keysAndValues); } - ValueTask IRedisNativeClientAsync.MSetAsync(string[] keys, byte[][] values, CancellationToken cancellationToken) - => ((IRedisNativeClientAsync)this).MSetAsync(keys.ToMultiByteArray(), values, cancellationToken); + ValueTask IRedisNativeClientAsync.MSetAsync(string[] keys, byte[][] values, CancellationToken token) + => ((IRedisNativeClientAsync)this).MSetAsync(keys.ToMultiByteArray(), values, token); - ValueTask IRedisNativeClientAsync.SelectAsync(long db, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SelectAsync(long db, CancellationToken token) { this.db = db; - return SendExpectSuccessAsync(cancellationToken, Commands.Select, db.ToUtf8Bytes()); + return SendExpectSuccessAsync(token, Commands.Select, db.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.DelAsync(string[] keys, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.DelAsync(string[] keys, CancellationToken token) { AssertNotNull(keys, nameof(keys)); var cmdWithArgs = MergeCommandWithArgs(Commands.Del, keys); - return SendExpectLongAsync(cancellationToken, cmdWithArgs); + return SendExpectLongAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.ExpireAsync(string key, int seconds, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ExpireAsync(string key, int seconds, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.Expire, key.ToUtf8Bytes(), seconds.ToUtf8Bytes()).IsSuccessAsync(); + return SendExpectLongAsync(token, Commands.Expire, key.ToUtf8Bytes(), seconds.ToUtf8Bytes()).IsSuccessAsync(); } - ValueTask IRedisNativeClientAsync.PExpireAsync(string key, long ttlMs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.PExpireAsync(string key, long ttlMs, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.PExpire, key.ToUtf8Bytes(), ttlMs.ToUtf8Bytes()).IsSuccessAsync(); + return SendExpectLongAsync(token, Commands.PExpire, key.ToUtf8Bytes(), ttlMs.ToUtf8Bytes()).IsSuccessAsync(); } - ValueTask IRedisNativeClientAsync.ExpireAtAsync(string key, long unixTime, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ExpireAtAsync(string key, long unixTime, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.ExpireAt, key.ToUtf8Bytes(), unixTime.ToUtf8Bytes()).IsSuccessAsync(); + return SendExpectLongAsync(token, Commands.ExpireAt, key.ToUtf8Bytes(), unixTime.ToUtf8Bytes()).IsSuccessAsync(); } - ValueTask IRedisNativeClientAsync.PExpireAtAsync(string key, long unixTimeMs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.PExpireAtAsync(string key, long unixTimeMs, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.PExpireAt, key.ToUtf8Bytes(), unixTimeMs.ToUtf8Bytes()).IsSuccessAsync(); + return SendExpectLongAsync(token, Commands.PExpireAt, key.ToUtf8Bytes(), unixTimeMs.ToUtf8Bytes()).IsSuccessAsync(); } - ValueTask IRedisNativeClientAsync.TtlAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.TtlAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.Ttl, key.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.Ttl, key.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.PTtlAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.PTtlAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.PTtl, key.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.PTtl, key.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.PingAsync(CancellationToken cancellationToken) - => IsString(SendExpectCodeAsync(cancellationToken, Commands.Ping), "PONG"); + ValueTask IRedisNativeClientAsync.PingAsync(CancellationToken token) + => IsString(SendExpectCodeAsync(token, Commands.Ping), "PONG"); private static ValueTask IsString(ValueTask pending, string expected) { @@ -247,51 +247,51 @@ static async ValueTask Awaited(ValueTask pending, string expected) => await pending.ConfigureAwait(false) == expected; } - ValueTask IRedisNativeClientAsync.EchoAsync(string text, CancellationToken cancellationToken) - => SendExpectDataAsync(cancellationToken, Commands.Echo, text.ToUtf8Bytes()).FromUtf8BytesAsync(); + ValueTask IRedisNativeClientAsync.EchoAsync(string text, CancellationToken token) + => SendExpectDataAsync(token, Commands.Echo, text.ToUtf8Bytes()).FromUtf8BytesAsync(); - ValueTask IRedisNativeClientAsync.DbSizeAsync(CancellationToken cancellationToken) - => SendExpectLongAsync(cancellationToken, Commands.DbSize); + ValueTask IRedisNativeClientAsync.DbSizeAsync(CancellationToken token) + => SendExpectLongAsync(token, Commands.DbSize); - ValueTask IRedisNativeClientAsync.LastSaveAsync(CancellationToken cancellationToken) - => SendExpectLongAsync(cancellationToken, Commands.LastSave).Await(t => t.FromUnixTime()); + ValueTask IRedisNativeClientAsync.LastSaveAsync(CancellationToken token) + => SendExpectLongAsync(token, Commands.LastSave).Await(t => t.FromUnixTime()); - ValueTask IRedisNativeClientAsync.SaveAsync(CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.Save); + ValueTask IRedisNativeClientAsync.SaveAsync(CancellationToken token) + => SendExpectSuccessAsync(token, Commands.Save); - ValueTask IRedisNativeClientAsync.BgSaveAsync(CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.BgSave); + ValueTask IRedisNativeClientAsync.BgSaveAsync(CancellationToken token) + => SendExpectSuccessAsync(token, Commands.BgSave); - ValueTask IRedisNativeClientAsync.ShutdownAsync(bool noSave, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ShutdownAsync(bool noSave, CancellationToken token) => noSave - ? SendWithoutReadAsync(cancellationToken, Commands.Shutdown, Commands.NoSave) - : SendWithoutReadAsync(cancellationToken, Commands.Shutdown); + ? SendWithoutReadAsync(token, Commands.Shutdown, Commands.NoSave) + : SendWithoutReadAsync(token, Commands.Shutdown); - ValueTask IRedisNativeClientAsync.BgRewriteAofAsync(CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.BgRewriteAof); + ValueTask IRedisNativeClientAsync.BgRewriteAofAsync(CancellationToken token) + => SendExpectSuccessAsync(token, Commands.BgRewriteAof); - ValueTask IRedisNativeClientAsync.QuitAsync(CancellationToken cancellationToken) - => SendWithoutReadAsync(cancellationToken, Commands.Quit); + ValueTask IRedisNativeClientAsync.QuitAsync(CancellationToken token) + => SendWithoutReadAsync(token, Commands.Quit); - ValueTask IRedisNativeClientAsync.FlushDbAsync(CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.FlushDb); + ValueTask IRedisNativeClientAsync.FlushDbAsync(CancellationToken token) + => SendExpectSuccessAsync(token, Commands.FlushDb); - ValueTask IRedisNativeClientAsync.FlushAllAsync(CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.FlushAll); + ValueTask IRedisNativeClientAsync.FlushAllAsync(CancellationToken token) + => SendExpectSuccessAsync(token, Commands.FlushAll); - ValueTask IRedisNativeClientAsync.SlaveOfAsync(string hostname, int port, CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.SlaveOf, hostname.ToUtf8Bytes(), port.ToUtf8Bytes()); + ValueTask IRedisNativeClientAsync.SlaveOfAsync(string hostname, int port, CancellationToken token) + => SendExpectSuccessAsync(token, Commands.SlaveOf, hostname.ToUtf8Bytes(), port.ToUtf8Bytes()); - ValueTask IRedisNativeClientAsync.SlaveOfNoOneAsync(CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.SlaveOf, Commands.No, Commands.One); + ValueTask IRedisNativeClientAsync.SlaveOfNoOneAsync(CancellationToken token) + => SendExpectSuccessAsync(token, Commands.SlaveOf, Commands.No, Commands.One); - ValueTask IRedisNativeClientAsync.KeysAsync(string pattern, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.KeysAsync(string pattern, CancellationToken token) { AssertNotNull(pattern, nameof(pattern)); - return SendExpectMultiDataAsync(cancellationToken, Commands.Keys, pattern.ToUtf8Bytes()); + return SendExpectMultiDataAsync(token, Commands.Keys, pattern.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.MGetAsync(string[] keys, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.MGetAsync(string[] keys, CancellationToken token) { AssertNotNull(keys, nameof(keys)); if (keys.Length == 0) @@ -299,10 +299,10 @@ ValueTask IRedisNativeClientAsync.MGetAsync(string[] keys, Cancellatio var cmdWithArgs = MergeCommandWithArgs(Commands.MGet, keys); - return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + return SendExpectMultiDataAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.SetExAsync(string key, int expireInSeconds, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SetExAsync(string key, int expireInSeconds, byte[] value, CancellationToken token) { AssertNotNull(key); value ??= TypeConstants.EmptyByteArray; @@ -310,10 +310,10 @@ ValueTask IRedisNativeClientAsync.SetExAsync(string key, int expireInSeconds, by if (value.Length > OneGb) throw new ArgumentException("value exceeds 1G", "value"); - return SendExpectSuccessAsync(cancellationToken, Commands.SetEx, key.ToUtf8Bytes(), expireInSeconds.ToUtf8Bytes(), value); + return SendExpectSuccessAsync(token, Commands.SetEx, key.ToUtf8Bytes(), expireInSeconds.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.WatchAsync(string[] keys, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.WatchAsync(string[] keys, CancellationToken token) { AssertNotNull(keys, nameof(keys)); if (keys.Length == 0) @@ -321,57 +321,57 @@ ValueTask IRedisNativeClientAsync.WatchAsync(string[] keys, CancellationToken ca var cmdWithArgs = MergeCommandWithArgs(Commands.Watch, keys); - return SendExpectCodeAsync(cancellationToken, cmdWithArgs).Await(); + return SendExpectCodeAsync(token, cmdWithArgs).Await(); } - ValueTask IRedisNativeClientAsync.UnWatchAsync(CancellationToken cancellationToken) - => SendExpectCodeAsync(cancellationToken, Commands.UnWatch).Await(); + ValueTask IRedisNativeClientAsync.UnWatchAsync(CancellationToken token) + => SendExpectCodeAsync(token, Commands.UnWatch).Await(); - ValueTask IRedisNativeClientAsync.AppendAsync(string key, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.AppendAsync(string key, byte[] value, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.Append, key.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.Append, key.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.GetRangeAsync(string key, int fromIndex, int toIndex, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.GetRangeAsync(string key, int fromIndex, int toIndex, CancellationToken token) { AssertNotNull(key); - return SendExpectDataAsync(cancellationToken, Commands.GetRange, key.ToUtf8Bytes(), fromIndex.ToUtf8Bytes(), toIndex.ToUtf8Bytes()); + return SendExpectDataAsync(token, Commands.GetRange, key.ToUtf8Bytes(), fromIndex.ToUtf8Bytes(), toIndex.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.SetRangeAsync(string key, int offset, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SetRangeAsync(string key, int offset, byte[] value, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.SetRange, key.ToUtf8Bytes(), offset.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.SetRange, key.ToUtf8Bytes(), offset.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.GetBitAsync(string key, int offset, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.GetBitAsync(string key, int offset, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.GetBit, key.ToUtf8Bytes(), offset.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.GetBit, key.ToUtf8Bytes(), offset.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.SetBitAsync(string key, int offset, int value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SetBitAsync(string key, int offset, int value, CancellationToken token) { AssertNotNull(key); if (value > 1 || value < 0) throw new ArgumentOutOfRangeException(nameof(value), "value is out of range"); - return SendExpectLongAsync(cancellationToken, Commands.SetBit, key.ToUtf8Bytes(), offset.ToUtf8Bytes(), value.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.SetBit, key.ToUtf8Bytes(), offset.ToUtf8Bytes(), value.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.PersistAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.PersistAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.Persist, key.ToUtf8Bytes()).IsSuccessAsync(); + return SendExpectLongAsync(token, Commands.Persist, key.ToUtf8Bytes()).IsSuccessAsync(); } - ValueTask IRedisNativeClientAsync.PSetExAsync(string key, long expireInMs, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.PSetExAsync(string key, long expireInMs, byte[] value, CancellationToken token) { AssertNotNull(key); - return SendExpectSuccessAsync(cancellationToken, Commands.PSetEx, key.ToUtf8Bytes(), expireInMs.ToUtf8Bytes(), value); + return SendExpectSuccessAsync(token, Commands.PSetEx, key.ToUtf8Bytes(), expireInMs.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.SetNXAsync(string key, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SetNXAsync(string key, byte[] value, CancellationToken token) { AssertNotNull(key); value ??= TypeConstants.EmptyByteArray; @@ -379,51 +379,51 @@ ValueTask IRedisNativeClientAsync.SetNXAsync(string key, byte[] value, Can if (value.Length > OneGb) throw new ArgumentException("value exceeds 1G", "value"); - return SendExpectLongAsync(cancellationToken, Commands.SetNx, key.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.SetNx, key.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.SPopAsync(string setId, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SPopAsync(string setId, CancellationToken token) { AssertNotNull(setId, nameof(setId)); - return SendExpectDataAsync(cancellationToken, Commands.SPop, setId.ToUtf8Bytes()); + return SendExpectDataAsync(token, Commands.SPop, setId.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.SPopAsync(string setId, int count, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SPopAsync(string setId, int count, CancellationToken token) { AssertNotNull(setId, nameof(setId)); - return SendExpectMultiDataAsync(cancellationToken, Commands.SPop, setId.ToUtf8Bytes(), count.ToUtf8Bytes()); + return SendExpectMultiDataAsync(token, Commands.SPop, setId.ToUtf8Bytes(), count.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.SlowlogResetAsync(CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.Slowlog, "RESET".ToUtf8Bytes()); + ValueTask IRedisNativeClientAsync.SlowlogResetAsync(CancellationToken token) + => SendExpectSuccessAsync(token, Commands.Slowlog, "RESET".ToUtf8Bytes()); - ValueTask IRedisNativeClientAsync.SlowlogGetAsync(int? top, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SlowlogGetAsync(int? top, CancellationToken token) { if (top.HasValue) - return SendExpectDeeplyNestedMultiDataAsync(cancellationToken, Commands.Slowlog, Commands.Get, top.Value.ToUtf8Bytes()); + return SendExpectDeeplyNestedMultiDataAsync(token, Commands.Slowlog, Commands.Get, top.Value.ToUtf8Bytes()); else - return SendExpectDeeplyNestedMultiDataAsync(cancellationToken, Commands.Slowlog, Commands.Get); + return SendExpectDeeplyNestedMultiDataAsync(token, Commands.Slowlog, Commands.Get); } - ValueTask IRedisNativeClientAsync.ZCardAsync(string setId, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZCardAsync(string setId, CancellationToken token) { AssertNotNull(setId, nameof(setId)); - return SendExpectLongAsync(cancellationToken, Commands.ZCard, setId.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.ZCard, setId.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.ZCountAsync(string setId, double min, double max, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZCountAsync(string setId, double min, double max, CancellationToken token) { AssertNotNull(setId, nameof(setId)); - return SendExpectLongAsync(cancellationToken, Commands.ZCount, setId.ToUtf8Bytes(), min.ToUtf8Bytes(), max.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.ZCount, setId.ToUtf8Bytes(), min.ToUtf8Bytes(), max.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.ZScoreAsync(string setId, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZScoreAsync(string setId, byte[] value, CancellationToken token) { AssertNotNull(setId, nameof(setId)); - return SendExpectDoubleAsync(cancellationToken, Commands.ZScore, setId.ToUtf8Bytes(), value); + return SendExpectDoubleAsync(token, Commands.ZScore, setId.ToUtf8Bytes(), value); } - protected ValueTask RawCommandAsync(CancellationToken cancellationToken, params object[] cmdWithArgs) + protected ValueTask RawCommandAsync(CancellationToken token, params object[] cmdWithArgs) { var byteArgs = new List(); @@ -451,32 +451,32 @@ protected ValueTask RawCommandAsync(CancellationToken cancellationTok } } - return SendExpectComplexResponseAsync(cancellationToken, byteArgs.ToArray()); + return SendExpectComplexResponseAsync(token, byteArgs.ToArray()); } - ValueTask> IRedisNativeClientAsync.InfoAsync(CancellationToken cancellationToken) - => SendExpectStringAsync(cancellationToken, Commands.Info).Await(info => ParseInfoResult(info)); + ValueTask> IRedisNativeClientAsync.InfoAsync(CancellationToken token) + => SendExpectStringAsync(token, Commands.Info).Await(info => ParseInfoResult(info)); - ValueTask IRedisNativeClientAsync.ZRangeByLexAsync(string setId, string min, string max, int? skip, int? take, CancellationToken cancellationToken) - => SendExpectMultiDataAsync(cancellationToken, GetZRangeByLexArgs(setId, min, max, skip, take)); + ValueTask IRedisNativeClientAsync.ZRangeByLexAsync(string setId, string min, string max, int? skip, int? take, CancellationToken token) + => SendExpectMultiDataAsync(token, GetZRangeByLexArgs(setId, min, max, skip, take)); - ValueTask IRedisNativeClientAsync.ZLexCountAsync(string setId, string min, string max, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZLexCountAsync(string setId, string min, string max, CancellationToken token) { AssertNotNull(setId, nameof(setId)); - return SendExpectLongAsync(cancellationToken, + return SendExpectLongAsync(token, Commands.ZLexCount, setId.ToUtf8Bytes(), min.ToUtf8Bytes(), max.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.ZRemRangeByLexAsync(string setId, string min, string max, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZRemRangeByLexAsync(string setId, string min, string max, CancellationToken token) { AssertNotNull(setId, nameof(setId)); - return SendExpectLongAsync(cancellationToken, + return SendExpectLongAsync(token, Commands.ZRemRangeByLex, setId.ToUtf8Bytes(), min.ToUtf8Bytes(), max.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.CalculateSha1Async(string luaBody, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.CalculateSha1Async(string luaBody, CancellationToken token) { AssertNotNull(luaBody, nameof(luaBody)); @@ -484,118 +484,118 @@ ValueTask IRedisNativeClientAsync.CalculateSha1Async(string luaBody, Can return BitConverter.ToString(buffer.ToSha1Hash()).Replace("-", "").AsValueTaskResult(); } - ValueTask IRedisNativeClientAsync.ScriptExistsAsync(byte[][] sha1Refs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ScriptExistsAsync(byte[][] sha1Refs, CancellationToken token) { var keysAndValues = MergeCommandWithArgs(Commands.Script, Commands.Exists, sha1Refs); - return SendExpectMultiDataAsync(cancellationToken, keysAndValues); + return SendExpectMultiDataAsync(token, keysAndValues); } - ValueTask IRedisNativeClientAsync.ScriptFlushAsync(CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.Script, Commands.Flush); + ValueTask IRedisNativeClientAsync.ScriptFlushAsync(CancellationToken token) + => SendExpectSuccessAsync(token, Commands.Script, Commands.Flush); - ValueTask IRedisNativeClientAsync.ScriptKillAsync(CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.Script, Commands.Kill); + ValueTask IRedisNativeClientAsync.ScriptKillAsync(CancellationToken token) + => SendExpectSuccessAsync(token, Commands.Script, Commands.Kill); - ValueTask IRedisNativeClientAsync.ScriptLoadAsync(string body, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ScriptLoadAsync(string body, CancellationToken token) { AssertNotNull(body, nameof(body)); var cmdArgs = MergeCommandWithArgs(Commands.Script, Commands.Load, body.ToUtf8Bytes()); - return SendExpectDataAsync(cancellationToken, cmdArgs); + return SendExpectDataAsync(token, cmdArgs); } - ValueTask IRedisNativeClientAsync.StrLenAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.StrLenAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.StrLen, key.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.StrLen, key.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.LLenAsync(string listId, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.LLenAsync(string listId, CancellationToken token) { AssertNotNull(listId, nameof(listId)); - return SendExpectLongAsync(cancellationToken, Commands.LLen, listId.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.LLen, listId.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.SCardAsync(string setId, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SCardAsync(string setId, CancellationToken token) { AssertNotNull(setId, nameof(setId)); - return SendExpectLongAsync(cancellationToken, Commands.SCard, setId.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.SCard, setId.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.HLenAsync(string hashId, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.HLenAsync(string hashId, CancellationToken token) { AssertNotNull(hashId, nameof(hashId)); - return SendExpectLongAsync(cancellationToken, Commands.HLen, hashId.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.HLen, hashId.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.EvalCommandAsync(string luaBody, int numberKeysInArgs, byte[][] keys, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.EvalCommandAsync(string luaBody, int numberKeysInArgs, byte[][] keys, CancellationToken token) { AssertNotNull(luaBody, nameof(luaBody)); var cmdArgs = MergeCommandWithArgs(Commands.Eval, luaBody.ToUtf8Bytes(), keys.PrependInt(numberKeysInArgs)); - return RawCommandAsync(cancellationToken, cmdArgs); + return RawCommandAsync(token, cmdArgs); } - ValueTask IRedisNativeClientAsync.EvalShaCommandAsync(string sha1, int numberKeysInArgs, byte[][] keys, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.EvalShaCommandAsync(string sha1, int numberKeysInArgs, byte[][] keys, CancellationToken token) { AssertNotNull(sha1, nameof(sha1)); var cmdArgs = MergeCommandWithArgs(Commands.EvalSha, sha1.ToUtf8Bytes(), keys.PrependInt(numberKeysInArgs)); - return RawCommandAsync(cancellationToken, cmdArgs); + return RawCommandAsync(token, cmdArgs); } - ValueTask IRedisNativeClientAsync.EvalAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.EvalAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken token) { AssertNotNull(luaBody, nameof(luaBody)); var cmdArgs = MergeCommandWithArgs(Commands.Eval, luaBody.ToUtf8Bytes(), keysAndArgs.PrependInt(numberOfKeys)); - return SendExpectMultiDataAsync(cancellationToken, cmdArgs); + return SendExpectMultiDataAsync(token, cmdArgs); } - ValueTask IRedisNativeClientAsync.EvalShaAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.EvalShaAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken token) { AssertNotNull(sha1, nameof(sha1)); var cmdArgs = MergeCommandWithArgs(Commands.EvalSha, sha1.ToUtf8Bytes(), keysAndArgs.PrependInt(numberOfKeys)); - return SendExpectMultiDataAsync(cancellationToken, cmdArgs); + return SendExpectMultiDataAsync(token, cmdArgs); } - ValueTask IRedisNativeClientAsync.EvalIntAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.EvalIntAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken token) { AssertNotNull(luaBody, nameof(luaBody)); var cmdArgs = MergeCommandWithArgs(Commands.Eval, luaBody.ToUtf8Bytes(), keysAndArgs.PrependInt(numberOfKeys)); - return SendExpectLongAsync(cancellationToken, cmdArgs); + return SendExpectLongAsync(token, cmdArgs); } - ValueTask IRedisNativeClientAsync.EvalShaIntAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.EvalShaIntAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken token) { AssertNotNull(sha1, nameof(sha1)); var cmdArgs = MergeCommandWithArgs(Commands.EvalSha, sha1.ToUtf8Bytes(), keysAndArgs.PrependInt(numberOfKeys)); - return SendExpectLongAsync(cancellationToken, cmdArgs); + return SendExpectLongAsync(token, cmdArgs); } - ValueTask IRedisNativeClientAsync.EvalStrAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.EvalStrAsync(string luaBody, int numberOfKeys, byte[][] keysAndArgs, CancellationToken token) { AssertNotNull(luaBody, nameof(luaBody)); var cmdArgs = MergeCommandWithArgs(Commands.Eval, luaBody.ToUtf8Bytes(), keysAndArgs.PrependInt(numberOfKeys)); - return SendExpectDataAsync(cancellationToken, cmdArgs).FromUtf8BytesAsync(); + return SendExpectDataAsync(token, cmdArgs).FromUtf8BytesAsync(); } - ValueTask IRedisNativeClientAsync.EvalShaStrAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.EvalShaStrAsync(string sha1, int numberOfKeys, byte[][] keysAndArgs, CancellationToken token) { AssertNotNull(sha1, nameof(sha1)); var cmdArgs = MergeCommandWithArgs(Commands.EvalSha, sha1.ToUtf8Bytes(), keysAndArgs.PrependInt(numberOfKeys)); - return SendExpectDataAsync(cancellationToken, cmdArgs).FromUtf8BytesAsync(); + return SendExpectDataAsync(token, cmdArgs).FromUtf8BytesAsync(); } - ValueTask IRedisNativeClientAsync.SMembersAsync(string setId, CancellationToken cancellationToken) - => SendExpectMultiDataAsync(cancellationToken, Commands.SMembers, setId.ToUtf8Bytes()); + ValueTask IRedisNativeClientAsync.SMembersAsync(string setId, CancellationToken token) + => SendExpectMultiDataAsync(token, Commands.SMembers, setId.ToUtf8Bytes()); - ValueTask IRedisNativeClientAsync.SAddAsync(string setId, byte[][] values, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SAddAsync(string setId, byte[][] values, CancellationToken token) { AssertNotNull(setId, nameof(setId)); AssertNotNull(values, nameof(values)); @@ -603,421 +603,421 @@ ValueTask IRedisNativeClientAsync.SAddAsync(string setId, byte[][] values, throw new ArgumentException(nameof(values)); var cmdWithArgs = MergeCommandWithArgs(Commands.SAdd, setId.ToUtf8Bytes(), values); - return SendExpectLongAsync(cancellationToken, cmdWithArgs); + return SendExpectLongAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.SRemAsync(string setId, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SRemAsync(string setId, byte[] value, CancellationToken token) { AssertSetIdAndValue(setId, value); - return SendExpectLongAsync(cancellationToken, Commands.SRem, setId.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.SRem, setId.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.IncrByAsync(string key, long count, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.IncrByAsync(string key, long count, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.IncrBy, key.ToUtf8Bytes(), count.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.IncrBy, key.ToUtf8Bytes(), count.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.IncrByFloatAsync(string key, double incrBy, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.IncrByFloatAsync(string key, double incrBy, CancellationToken token) { AssertNotNull(key); - return SendExpectDoubleAsync(cancellationToken, Commands.IncrByFloat, key.ToUtf8Bytes(), incrBy.ToUtf8Bytes()); + return SendExpectDoubleAsync(token, Commands.IncrByFloat, key.ToUtf8Bytes(), incrBy.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.IncrAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.IncrAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.Incr, key.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.Incr, key.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.DecrAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.DecrAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.Decr, key.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.Decr, key.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.DecrByAsync(string key, long count, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.DecrByAsync(string key, long count, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.DecrBy, key.ToUtf8Bytes(), count.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.DecrBy, key.ToUtf8Bytes(), count.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.ConfigGetAsync(string pattern, CancellationToken cancellationToken) - => SendExpectMultiDataAsync(cancellationToken, Commands.Config, Commands.Get, pattern.ToUtf8Bytes()); + ValueTask IRedisNativeClientAsync.ConfigGetAsync(string pattern, CancellationToken token) + => SendExpectMultiDataAsync(token, Commands.Config, Commands.Get, pattern.ToUtf8Bytes()); - ValueTask IRedisNativeClientAsync.ConfigSetAsync(string item, byte[] value, CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.Config, Commands.Set, item.ToUtf8Bytes(), value); + ValueTask IRedisNativeClientAsync.ConfigSetAsync(string item, byte[] value, CancellationToken token) + => SendExpectSuccessAsync(token, Commands.Config, Commands.Set, item.ToUtf8Bytes(), value); - ValueTask IRedisNativeClientAsync.ConfigResetStatAsync(CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.Config, Commands.ResetStat); + ValueTask IRedisNativeClientAsync.ConfigResetStatAsync(CancellationToken token) + => SendExpectSuccessAsync(token, Commands.Config, Commands.ResetStat); - ValueTask IRedisNativeClientAsync.ConfigRewriteAsync(CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.Config, Commands.Rewrite); + ValueTask IRedisNativeClientAsync.ConfigRewriteAsync(CancellationToken token) + => SendExpectSuccessAsync(token, Commands.Config, Commands.Rewrite); - ValueTask IRedisNativeClientAsync.DebugSegfaultAsync(CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.Debug, Commands.Segfault); + ValueTask IRedisNativeClientAsync.DebugSegfaultAsync(CancellationToken token) + => SendExpectSuccessAsync(token, Commands.Debug, Commands.Segfault); - ValueTask IRedisNativeClientAsync.DumpAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.DumpAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectDataAsync(cancellationToken, Commands.Dump, key.ToUtf8Bytes()); + return SendExpectDataAsync(token, Commands.Dump, key.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.RestoreAsync(string key, long expireMs, byte[] dumpValue, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.RestoreAsync(string key, long expireMs, byte[] dumpValue, CancellationToken token) { AssertNotNull(key); - return SendExpectDataAsync(cancellationToken, Commands.Restore, key.ToUtf8Bytes(), expireMs.ToUtf8Bytes(), dumpValue); + return SendExpectDataAsync(token, Commands.Restore, key.ToUtf8Bytes(), expireMs.ToUtf8Bytes(), dumpValue); } - ValueTask IRedisNativeClientAsync.MigrateAsync(string host, int port, string key, int destinationDb, long timeoutMs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.MigrateAsync(string host, int port, string key, int destinationDb, long timeoutMs, CancellationToken token) { AssertNotNull(key); - return SendExpectSuccessAsync(cancellationToken, Commands.Migrate, host.ToUtf8Bytes(), port.ToUtf8Bytes(), key.ToUtf8Bytes(), destinationDb.ToUtf8Bytes(), timeoutMs.ToUtf8Bytes()); + return SendExpectSuccessAsync(token, Commands.Migrate, host.ToUtf8Bytes(), port.ToUtf8Bytes(), key.ToUtf8Bytes(), destinationDb.ToUtf8Bytes(), timeoutMs.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.MoveAsync(string key, int db, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.MoveAsync(string key, int db, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.Move, key.ToUtf8Bytes(), db.ToUtf8Bytes()).IsSuccessAsync(); + return SendExpectLongAsync(token, Commands.Move, key.ToUtf8Bytes(), db.ToUtf8Bytes()).IsSuccessAsync(); } - ValueTask IRedisNativeClientAsync.ObjectIdleTimeAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ObjectIdleTimeAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.Object, Commands.IdleTime, key.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.Object, Commands.IdleTime, key.ToUtf8Bytes()); } - async ValueTask IRedisNativeClientAsync.RoleAsync(CancellationToken cancellationToken) - => (await SendExpectComplexResponseAsync(cancellationToken, Commands.Role).ConfigureAwait(false)).ToRedisText(); + async ValueTask IRedisNativeClientAsync.RoleAsync(CancellationToken token) + => (await SendExpectComplexResponseAsync(token, Commands.Role).ConfigureAwait(false)).ToRedisText(); - ValueTask IRedisNativeClientAsync.RawCommandAsync(object[] cmdWithArgs, CancellationToken cancellationToken) - => SendExpectComplexResponseAsync(cancellationToken, PrepareRawCommand(cmdWithArgs)); + ValueTask IRedisNativeClientAsync.RawCommandAsync(object[] cmdWithArgs, CancellationToken token) + => SendExpectComplexResponseAsync(token, PrepareRawCommand(cmdWithArgs)); - ValueTask IRedisNativeClientAsync.RawCommandAsync(byte[][] cmdWithBinaryArgs, CancellationToken cancellationToken) - => SendExpectComplexResponseAsync(cancellationToken, cmdWithBinaryArgs); + ValueTask IRedisNativeClientAsync.RawCommandAsync(byte[][] cmdWithBinaryArgs, CancellationToken token) + => SendExpectComplexResponseAsync(token, cmdWithBinaryArgs); - ValueTask IRedisNativeClientAsync.ClientGetNameAsync(CancellationToken cancellationToken) - => SendExpectStringAsync(cancellationToken, Commands.Client, Commands.GetName); + ValueTask IRedisNativeClientAsync.ClientGetNameAsync(CancellationToken token) + => SendExpectStringAsync(token, Commands.Client, Commands.GetName); - ValueTask IRedisNativeClientAsync.ClientSetNameAsync(string name, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ClientSetNameAsync(string name, CancellationToken token) { ClientValidateName(name); - return SendExpectSuccessAsync(cancellationToken, Commands.Client, Commands.SetName, name.ToUtf8Bytes()); + return SendExpectSuccessAsync(token, Commands.Client, Commands.SetName, name.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.ClientKillAsync(string clientAddr, CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.Client, Commands.Kill, clientAddr.ToUtf8Bytes()); + ValueTask IRedisNativeClientAsync.ClientKillAsync(string clientAddr, CancellationToken token) + => SendExpectSuccessAsync(token, Commands.Client, Commands.Kill, clientAddr.ToUtf8Bytes()); - ValueTask IRedisNativeClientAsync.ClientKillAsync(string addr, string id, string type, string skipMe, CancellationToken cancellationToken) - => SendExpectLongAsync(cancellationToken, ClientKillPerpareArgs(addr, id, type, skipMe)); + ValueTask IRedisNativeClientAsync.ClientKillAsync(string addr, string id, string type, string skipMe, CancellationToken token) + => SendExpectLongAsync(token, ClientKillPerpareArgs(addr, id, type, skipMe)); - ValueTask IRedisNativeClientAsync.ClientListAsync(CancellationToken cancellationToken) - => SendExpectDataAsync(cancellationToken, Commands.Client, Commands.List); + ValueTask IRedisNativeClientAsync.ClientListAsync(CancellationToken token) + => SendExpectDataAsync(token, Commands.Client, Commands.List); - ValueTask IRedisNativeClientAsync.ClientPauseAsync(int timeOutMs, CancellationToken cancellationToken) - => SendExpectSuccessAsync(cancellationToken, Commands.Client, Commands.Pause, timeOutMs.ToUtf8Bytes()); + ValueTask IRedisNativeClientAsync.ClientPauseAsync(int timeOutMs, CancellationToken token) + => SendExpectSuccessAsync(token, Commands.Client, Commands.Pause, timeOutMs.ToUtf8Bytes()); - ValueTask IRedisNativeClientAsync.MSetNxAsync(byte[][] keys, byte[][] values, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.MSetNxAsync(byte[][] keys, byte[][] values, CancellationToken token) { var keysAndValues = MergeCommandWithKeysAndValues(Commands.MSet, keys, values); - return SendExpectLongAsync(cancellationToken, keysAndValues).IsSuccessAsync(); + return SendExpectLongAsync(token, keysAndValues).IsSuccessAsync(); } - ValueTask IRedisNativeClientAsync.MSetNxAsync(string[] keys, byte[][] values, CancellationToken cancellationToken) - => AsAsync().MSetNxAsync(keys.ToMultiByteArray(), values, cancellationToken); + ValueTask IRedisNativeClientAsync.MSetNxAsync(string[] keys, byte[][] values, CancellationToken token) + => AsAsync().MSetNxAsync(keys.ToMultiByteArray(), values, token); - ValueTask IRedisNativeClientAsync.GetSetAsync(string key, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.GetSetAsync(string key, byte[] value, CancellationToken token) { GetSetAssertArgs(key, ref value); - return SendExpectDataAsync(cancellationToken, Commands.GetSet, key.ToUtf8Bytes(), value); + return SendExpectDataAsync(token, Commands.GetSet, key.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.MGetAsync(byte[][] keys, CancellationToken cancellationToken) - => SendExpectMultiDataAsync(cancellationToken, MGetPrepareArgs(keys)); + ValueTask IRedisNativeClientAsync.MGetAsync(byte[][] keys, CancellationToken token) + => SendExpectMultiDataAsync(token, MGetPrepareArgs(keys)); - ValueTask IRedisNativeClientAsync.SScanAsync(string setId, ulong cursor, int count, string match, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SScanAsync(string setId, ulong cursor, int count, string match, CancellationToken token) { if (match == null) { - return SendExpectScanResultAsync(cancellationToken, Commands.SScan, + return SendExpectScanResultAsync(token, Commands.SScan, setId.ToUtf8Bytes(), cursor.ToUtf8Bytes(), Commands.Count, count.ToUtf8Bytes()); } - return SendExpectScanResultAsync(cancellationToken, Commands.SScan, + return SendExpectScanResultAsync(token, Commands.SScan, setId.ToUtf8Bytes(), cursor.ToUtf8Bytes(), Commands.Match, match.ToUtf8Bytes(), Commands.Count, count.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.ZScanAsync(string setId, ulong cursor, int count, string match, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZScanAsync(string setId, ulong cursor, int count, string match, CancellationToken token) { if (match == null) { - return SendExpectScanResultAsync(cancellationToken, Commands.ZScan, + return SendExpectScanResultAsync(token, Commands.ZScan, setId.ToUtf8Bytes(), cursor.ToUtf8Bytes(), Commands.Count, count.ToUtf8Bytes()); } - return SendExpectScanResultAsync(cancellationToken, Commands.ZScan, + return SendExpectScanResultAsync(token, Commands.ZScan, setId.ToUtf8Bytes(), cursor.ToUtf8Bytes(), Commands.Match, match.ToUtf8Bytes(), Commands.Count, count.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.HScanAsync(string hashId, ulong cursor, int count, string match, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.HScanAsync(string hashId, ulong cursor, int count, string match, CancellationToken token) { if (match == null) { - return SendExpectScanResultAsync(cancellationToken, Commands.HScan, + return SendExpectScanResultAsync(token, Commands.HScan, hashId.ToUtf8Bytes(), cursor.ToUtf8Bytes(), Commands.Count, count.ToUtf8Bytes()); } - return SendExpectScanResultAsync(cancellationToken, Commands.HScan, + return SendExpectScanResultAsync(token, Commands.HScan, hashId.ToUtf8Bytes(), cursor.ToUtf8Bytes(), Commands.Match, match.ToUtf8Bytes(), Commands.Count, count.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.PfAddAsync(string key, byte[][] elements, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.PfAddAsync(string key, byte[][] elements, CancellationToken token) { var cmdWithArgs = MergeCommandWithArgs(Commands.PfAdd, key.ToUtf8Bytes(), elements); - return SendExpectLongAsync(cancellationToken, cmdWithArgs).IsSuccessAsync(); + return SendExpectLongAsync(token, cmdWithArgs).IsSuccessAsync(); } - ValueTask IRedisNativeClientAsync.PfCountAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.PfCountAsync(string key, CancellationToken token) { var cmdWithArgs = MergeCommandWithArgs(Commands.PfCount, key.ToUtf8Bytes()); - return SendExpectLongAsync(cancellationToken, cmdWithArgs); + return SendExpectLongAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.PfMergeAsync(string toKeyId, string[] fromKeys, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.PfMergeAsync(string toKeyId, string[] fromKeys, CancellationToken token) { var fromKeyBytes = fromKeys.Map(x => x.ToUtf8Bytes()).ToArray(); var cmdWithArgs = MergeCommandWithArgs(Commands.PfMerge, toKeyId.ToUtf8Bytes(), fromKeyBytes); - return SendExpectSuccessAsync(cancellationToken, cmdWithArgs); + return SendExpectSuccessAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.SortAsync(string listOrSetId, SortOptions sortOptions, CancellationToken cancellationToken) - => SendExpectMultiDataAsync(cancellationToken, SortPrepareArgs(listOrSetId, sortOptions)); + ValueTask IRedisNativeClientAsync.SortAsync(string listOrSetId, SortOptions sortOptions, CancellationToken token) + => SendExpectMultiDataAsync(token, SortPrepareArgs(listOrSetId, sortOptions)); - ValueTask IRedisNativeClientAsync.LRangeAsync(string listId, int startingFrom, int endingAt, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.LRangeAsync(string listId, int startingFrom, int endingAt, CancellationToken token) { AssertNotNull(listId, nameof(listId)); - return SendExpectMultiDataAsync(cancellationToken, Commands.LRange, listId.ToUtf8Bytes(), startingFrom.ToUtf8Bytes(), endingAt.ToUtf8Bytes()); + return SendExpectMultiDataAsync(token, Commands.LRange, listId.ToUtf8Bytes(), startingFrom.ToUtf8Bytes(), endingAt.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.RPushXAsync(string listId, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.RPushXAsync(string listId, byte[] value, CancellationToken token) { AssertListIdAndValue(listId, value); - return SendExpectLongAsync(cancellationToken, Commands.RPush, listId.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.RPush, listId.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.LPushAsync(string listId, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.LPushAsync(string listId, byte[] value, CancellationToken token) { AssertListIdAndValue(listId, value); - return SendExpectLongAsync(cancellationToken, Commands.LPush, listId.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.LPush, listId.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.LPushXAsync(string listId, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.LPushXAsync(string listId, byte[] value, CancellationToken token) { AssertListIdAndValue(listId, value); - return SendExpectLongAsync(cancellationToken, Commands.LPushX, listId.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.LPushX, listId.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.LTrimAsync(string listId, int keepStartingFrom, int keepEndingAt, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.LTrimAsync(string listId, int keepStartingFrom, int keepEndingAt, CancellationToken token) { AssertNotNull(listId, nameof(listId)); - return SendExpectSuccessAsync(cancellationToken, Commands.LTrim, listId.ToUtf8Bytes(), keepStartingFrom.ToUtf8Bytes(), keepEndingAt.ToUtf8Bytes()); + return SendExpectSuccessAsync(token, Commands.LTrim, listId.ToUtf8Bytes(), keepStartingFrom.ToUtf8Bytes(), keepEndingAt.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.LRemAsync(string listId, int removeNoOfMatches, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.LRemAsync(string listId, int removeNoOfMatches, byte[] value, CancellationToken token) { AssertNotNull(listId, nameof(listId)); - return SendExpectLongAsync(cancellationToken, Commands.LRem, listId.ToUtf8Bytes(), removeNoOfMatches.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.LRem, listId.ToUtf8Bytes(), removeNoOfMatches.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.LIndexAsync(string listId, int listIndex, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.LIndexAsync(string listId, int listIndex, CancellationToken token) { AssertNotNull(listId, nameof(listId)); - return SendExpectDataAsync(cancellationToken, Commands.LIndex, listId.ToUtf8Bytes(), listIndex.ToUtf8Bytes()); + return SendExpectDataAsync(token, Commands.LIndex, listId.ToUtf8Bytes(), listIndex.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.LInsertAsync(string listId, bool insertBefore, byte[] pivot, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.LInsertAsync(string listId, bool insertBefore, byte[] pivot, byte[] value, CancellationToken token) { AssertNotNull(listId, nameof(listId)); var position = insertBefore ? Commands.Before : Commands.After; - return SendExpectSuccessAsync(cancellationToken, Commands.LInsert, listId.ToUtf8Bytes(), position, pivot, value); + return SendExpectSuccessAsync(token, Commands.LInsert, listId.ToUtf8Bytes(), position, pivot, value); } - ValueTask IRedisNativeClientAsync.LSetAsync(string listId, int listIndex, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.LSetAsync(string listId, int listIndex, byte[] value, CancellationToken token) { AssertNotNull(listId, nameof(listId)); - return SendExpectSuccessAsync(cancellationToken, Commands.LSet, listId.ToUtf8Bytes(), listIndex.ToUtf8Bytes(), value); + return SendExpectSuccessAsync(token, Commands.LSet, listId.ToUtf8Bytes(), listIndex.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.LPopAsync(string listId, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.LPopAsync(string listId, CancellationToken token) { AssertNotNull(listId, nameof(listId)); - return SendExpectDataAsync(cancellationToken, Commands.LPop, listId.ToUtf8Bytes()); + return SendExpectDataAsync(token, Commands.LPop, listId.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.RPopAsync(string listId, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.RPopAsync(string listId, CancellationToken token) { AssertNotNull(listId, nameof(listId)); - return SendExpectDataAsync(cancellationToken, Commands.RPop, listId.ToUtf8Bytes()); + return SendExpectDataAsync(token, Commands.RPop, listId.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.BLPopAsync(string listId, int timeOutSecs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.BLPopAsync(string listId, int timeOutSecs, CancellationToken token) { AssertNotNull(listId, nameof(listId)); - return SendExpectMultiDataAsync(cancellationToken, Commands.BLPop, listId.ToUtf8Bytes(), timeOutSecs.ToUtf8Bytes()); + return SendExpectMultiDataAsync(token, Commands.BLPop, listId.ToUtf8Bytes(), timeOutSecs.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.BLPopAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.BLPopAsync(string[] listIds, int timeOutSecs, CancellationToken token) { AssertNotNull(listIds, nameof(listIds)); var args = new List { Commands.BLPop }; args.AddRange(listIds.Select(listId => listId.ToUtf8Bytes())); args.Add(timeOutSecs.ToUtf8Bytes()); - return SendExpectMultiDataAsync(cancellationToken, args.ToArray()); + return SendExpectMultiDataAsync(token, args.ToArray()); } - async ValueTask IRedisNativeClientAsync.BLPopValueAsync(string listId, int timeOutSecs, CancellationToken cancellationToken) + async ValueTask IRedisNativeClientAsync.BLPopValueAsync(string listId, int timeOutSecs, CancellationToken token) { - var blockingResponse = await AsAsync().BLPopAsync(new[] { listId }, timeOutSecs, cancellationToken).ConfigureAwait(false); + var blockingResponse = await AsAsync().BLPopAsync(new[] { listId }, timeOutSecs, token).ConfigureAwait(false); return blockingResponse.Length == 0 ? null : blockingResponse[1]; } - async ValueTask IRedisNativeClientAsync.BLPopValueAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken) + async ValueTask IRedisNativeClientAsync.BLPopValueAsync(string[] listIds, int timeOutSecs, CancellationToken token) { - var blockingResponse = await AsAsync().BLPopAsync(listIds, timeOutSecs, cancellationToken).ConfigureAwait(false); + var blockingResponse = await AsAsync().BLPopAsync(listIds, timeOutSecs, token).ConfigureAwait(false); return blockingResponse.Length == 0 ? null : blockingResponse; } - ValueTask IRedisNativeClientAsync.BRPopAsync(string listId, int timeOutSecs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.BRPopAsync(string listId, int timeOutSecs, CancellationToken token) { AssertNotNull(listId, nameof(listId)); - return SendExpectMultiDataAsync(cancellationToken, Commands.BRPop, listId.ToUtf8Bytes(), timeOutSecs.ToUtf8Bytes()); + return SendExpectMultiDataAsync(token, Commands.BRPop, listId.ToUtf8Bytes(), timeOutSecs.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.BRPopAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.BRPopAsync(string[] listIds, int timeOutSecs, CancellationToken token) { AssertNotNull(listIds, nameof(listIds)); var args = new List { Commands.BRPop }; args.AddRange(listIds.Select(listId => listId.ToUtf8Bytes())); args.Add(timeOutSecs.ToUtf8Bytes()); - return SendExpectMultiDataAsync(cancellationToken, args.ToArray()); + return SendExpectMultiDataAsync(token, args.ToArray()); } - ValueTask IRedisNativeClientAsync.RPopLPushAsync(string fromListId, string toListId, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.RPopLPushAsync(string fromListId, string toListId, CancellationToken token) { AssertNotNull(fromListId, nameof(fromListId)); AssertNotNull(toListId, nameof(toListId)); - return SendExpectDataAsync(cancellationToken, Commands.RPopLPush, fromListId.ToUtf8Bytes(), toListId.ToUtf8Bytes()); + return SendExpectDataAsync(token, Commands.RPopLPush, fromListId.ToUtf8Bytes(), toListId.ToUtf8Bytes()); } - async ValueTask IRedisNativeClientAsync.BRPopValueAsync(string listId, int timeOutSecs, CancellationToken cancellationToken) + async ValueTask IRedisNativeClientAsync.BRPopValueAsync(string listId, int timeOutSecs, CancellationToken token) { - var blockingResponse = await AsAsync().BRPopAsync(new[] { listId }, timeOutSecs, cancellationToken).ConfigureAwait(false); + var blockingResponse = await AsAsync().BRPopAsync(new[] { listId }, timeOutSecs, token).ConfigureAwait(false); return blockingResponse.Length == 0 ? null : blockingResponse[1]; } - async ValueTask IRedisNativeClientAsync.BRPopValueAsync(string[] listIds, int timeOutSecs, CancellationToken cancellationToken) + async ValueTask IRedisNativeClientAsync.BRPopValueAsync(string[] listIds, int timeOutSecs, CancellationToken token) { - var blockingResponse = await AsAsync().BRPopAsync(listIds, timeOutSecs, cancellationToken).ConfigureAwait(false); + var blockingResponse = await AsAsync().BRPopAsync(listIds, timeOutSecs, token).ConfigureAwait(false); return blockingResponse.Length == 0 ? null : blockingResponse; } - async ValueTask IRedisNativeClientAsync.BRPopLPushAsync(string fromListId, string toListId, int timeOutSecs, CancellationToken cancellationToken) + async ValueTask IRedisNativeClientAsync.BRPopLPushAsync(string fromListId, string toListId, int timeOutSecs, CancellationToken token) { AssertNotNull(fromListId, nameof(fromListId)); AssertNotNull(toListId, nameof(toListId)); - byte[][] result = await SendExpectMultiDataAsync(cancellationToken, Commands.BRPopLPush, fromListId.ToUtf8Bytes(), toListId.ToUtf8Bytes(), timeOutSecs.ToUtf8Bytes()); + byte[][] result = await SendExpectMultiDataAsync(token, Commands.BRPopLPush, fromListId.ToUtf8Bytes(), toListId.ToUtf8Bytes(), timeOutSecs.ToUtf8Bytes()); return result.Length == 0 ? null : result[1]; } - ValueTask IRedisNativeClientAsync.SMoveAsync(string fromSetId, string toSetId, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SMoveAsync(string fromSetId, string toSetId, byte[] value, CancellationToken token) { AssertNotNull(fromSetId, nameof(fromSetId)); AssertNotNull(toSetId, nameof(toSetId)); - return SendExpectSuccessAsync(cancellationToken, Commands.SMove, fromSetId.ToUtf8Bytes(), toSetId.ToUtf8Bytes(), value); + return SendExpectSuccessAsync(token, Commands.SMove, fromSetId.ToUtf8Bytes(), toSetId.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.SIsMemberAsync(string setId, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SIsMemberAsync(string setId, byte[] value, CancellationToken token) { AssertNotNull(setId, nameof(setId)); - return SendExpectLongAsync(cancellationToken, Commands.SIsMember, setId.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.SIsMember, setId.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.SInterAsync(string[] setIds, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SInterAsync(string[] setIds, CancellationToken token) { var cmdWithArgs = MergeCommandWithArgs(Commands.SInter, setIds); - return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + return SendExpectMultiDataAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.SInterStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SInterStoreAsync(string intoSetId, string[] setIds, CancellationToken token) { var setIdsList = new List(setIds); setIdsList.Insert(0, intoSetId); var cmdWithArgs = MergeCommandWithArgs(Commands.SInterStore, setIdsList.ToArray()); - return SendExpectSuccessAsync(cancellationToken, cmdWithArgs); + return SendExpectSuccessAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.SUnionAsync(string[] setIds, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SUnionAsync(string[] setIds, CancellationToken token) { var cmdWithArgs = MergeCommandWithArgs(Commands.SUnion, setIds); - return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + return SendExpectMultiDataAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.SUnionStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SUnionStoreAsync(string intoSetId, string[] setIds, CancellationToken token) { var setIdsList = new List(setIds); setIdsList.Insert(0, intoSetId); var cmdWithArgs = MergeCommandWithArgs(Commands.SUnionStore, setIdsList.ToArray()); - return SendExpectSuccessAsync(cancellationToken, cmdWithArgs); + return SendExpectSuccessAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.SDiffAsync(string fromSetId, string[] withSetIds, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SDiffAsync(string fromSetId, string[] withSetIds, CancellationToken token) { var setIdsList = new List(withSetIds); setIdsList.Insert(0, fromSetId); var cmdWithArgs = MergeCommandWithArgs(Commands.SDiff, setIdsList.ToArray()); - return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + return SendExpectMultiDataAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.SDiffStoreAsync(string intoSetId, string fromSetId, string[] withSetIds, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SDiffStoreAsync(string intoSetId, string fromSetId, string[] withSetIds, CancellationToken token) { var setIdsList = new List(withSetIds); setIdsList.Insert(0, fromSetId); setIdsList.Insert(0, intoSetId); var cmdWithArgs = MergeCommandWithArgs(Commands.SDiffStore, setIdsList.ToArray()); - return SendExpectSuccessAsync(cancellationToken, cmdWithArgs); + return SendExpectSuccessAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.SRandMemberAsync(string setId, CancellationToken cancellationToken) - => SendExpectDataAsync(cancellationToken, Commands.SRandMember, setId.ToUtf8Bytes()); + ValueTask IRedisNativeClientAsync.SRandMemberAsync(string setId, CancellationToken token) + => SendExpectDataAsync(token, Commands.SRandMember, setId.ToUtf8Bytes()); - ValueTask IRedisNativeClientAsync.ZRemAsync(string setId, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZRemAsync(string setId, byte[] value, CancellationToken token) { AssertSetIdAndValue(setId, value); - return SendExpectLongAsync(cancellationToken, Commands.ZRem, setId.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.ZRem, setId.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.ZRemAsync(string setId, byte[][] values, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZRemAsync(string setId, byte[][] values, CancellationToken token) { AssertNotNull(setId, nameof(setId)); AssertNotNull(values, nameof(values)); @@ -1025,136 +1025,136 @@ ValueTask IRedisNativeClientAsync.ZRemAsync(string setId, byte[][] values, throw new ArgumentException("values"); var cmdWithArgs = MergeCommandWithArgs(Commands.ZRem, setId.ToUtf8Bytes(), values); - return SendExpectLongAsync(cancellationToken, cmdWithArgs); + return SendExpectLongAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.ZIncrByAsync(string setId, double incrBy, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZIncrByAsync(string setId, double incrBy, byte[] value, CancellationToken token) { AssertSetIdAndValue(setId, value); - return SendExpectDoubleAsync(cancellationToken, Commands.ZIncrBy, setId.ToUtf8Bytes(), incrBy.ToFastUtf8Bytes(), value); + return SendExpectDoubleAsync(token, Commands.ZIncrBy, setId.ToUtf8Bytes(), incrBy.ToFastUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.ZIncrByAsync(string setId, long incrBy, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZIncrByAsync(string setId, long incrBy, byte[] value, CancellationToken token) { AssertSetIdAndValue(setId, value); - return SendExpectDoubleAsync(cancellationToken, Commands.ZIncrBy, setId.ToUtf8Bytes(), incrBy.ToUtf8Bytes(), value); + return SendExpectDoubleAsync(token, Commands.ZIncrBy, setId.ToUtf8Bytes(), incrBy.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.ZRankAsync(string setId, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZRankAsync(string setId, byte[] value, CancellationToken token) { AssertSetIdAndValue(setId, value); - return SendExpectLongAsync(cancellationToken, Commands.ZRank, setId.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.ZRank, setId.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.ZRevRankAsync(string setId, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZRevRankAsync(string setId, byte[] value, CancellationToken token) { AssertSetIdAndValue(setId, value); - return SendExpectLongAsync(cancellationToken, Commands.ZRevRank, setId.ToUtf8Bytes(), value); + return SendExpectLongAsync(token, Commands.ZRevRank, setId.ToUtf8Bytes(), value); } - ValueTask IRedisNativeClientAsync.ZRangeAsync(string setId, int min, int max, CancellationToken cancellationToken) - => SendExpectMultiDataAsync(cancellationToken, Commands.ZRange, setId.ToUtf8Bytes(), min.ToUtf8Bytes(), max.ToUtf8Bytes()); + ValueTask IRedisNativeClientAsync.ZRangeAsync(string setId, int min, int max, CancellationToken token) + => SendExpectMultiDataAsync(token, Commands.ZRange, setId.ToUtf8Bytes(), min.ToUtf8Bytes(), max.ToUtf8Bytes()); - private ValueTask GetRangeAsync(byte[] commandBytes, string setId, int min, int max, bool withScores, CancellationToken cancellationToken) + private ValueTask GetRangeAsync(byte[] commandBytes, string setId, int min, int max, bool withScores, CancellationToken token) { var args = GetRangeArgs(commandBytes, setId, min, max, withScores); - return SendExpectMultiDataAsync(cancellationToken, args); + return SendExpectMultiDataAsync(token, args); } - ValueTask IRedisNativeClientAsync.ZRangeWithScoresAsync(string setId, int min, int max, CancellationToken cancellationToken) - => GetRangeAsync(Commands.ZRange, setId, min, max, true, cancellationToken); + ValueTask IRedisNativeClientAsync.ZRangeWithScoresAsync(string setId, int min, int max, CancellationToken token) + => GetRangeAsync(Commands.ZRange, setId, min, max, true, token); - ValueTask IRedisNativeClientAsync.ZRevRangeAsync(string setId, int min, int max, CancellationToken cancellationToken) - => GetRangeAsync(Commands.ZRevRange, setId, min, max, false, cancellationToken); + ValueTask IRedisNativeClientAsync.ZRevRangeAsync(string setId, int min, int max, CancellationToken token) + => GetRangeAsync(Commands.ZRevRange, setId, min, max, false, token); - ValueTask IRedisNativeClientAsync.ZRevRangeWithScoresAsync(string setId, int min, int max, CancellationToken cancellationToken) - => GetRangeAsync(Commands.ZRevRange, setId, min, max, true, cancellationToken); + ValueTask IRedisNativeClientAsync.ZRevRangeWithScoresAsync(string setId, int min, int max, CancellationToken token) + => GetRangeAsync(Commands.ZRevRange, setId, min, max, true, token); private ValueTask GetRangeByScoreAsync(byte[] commandBytes, - string setId, double min, double max, int? skip, int? take, bool withScores, CancellationToken cancellationToken) + string setId, double min, double max, int? skip, int? take, bool withScores, CancellationToken token) { var args = GetRangeByScoreArgs(commandBytes, setId, min, max, skip, take, withScores); - return SendExpectMultiDataAsync(cancellationToken, args); + return SendExpectMultiDataAsync(token, args); } - ValueTask IRedisNativeClientAsync.ZRangeByScoreAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken) - => GetRangeByScoreAsync(Commands.ZRangeByScore, setId, min, max, skip, take, false, cancellationToken); + ValueTask IRedisNativeClientAsync.ZRangeByScoreAsync(string setId, double min, double max, int? skip, int? take, CancellationToken token) + => GetRangeByScoreAsync(Commands.ZRangeByScore, setId, min, max, skip, take, false, token); - ValueTask IRedisNativeClientAsync.ZRangeByScoreAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken) - => GetRangeByScoreAsync(Commands.ZRangeByScore, setId, min, max, skip, take, false, cancellationToken); + ValueTask IRedisNativeClientAsync.ZRangeByScoreAsync(string setId, long min, long max, int? skip, int? take, CancellationToken token) + => GetRangeByScoreAsync(Commands.ZRangeByScore, setId, min, max, skip, take, false, token); - ValueTask IRedisNativeClientAsync.ZRangeByScoreWithScoresAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken) - => GetRangeByScoreAsync(Commands.ZRangeByScore, setId, min, max, skip, take, true, cancellationToken); + ValueTask IRedisNativeClientAsync.ZRangeByScoreWithScoresAsync(string setId, double min, double max, int? skip, int? take, CancellationToken token) + => GetRangeByScoreAsync(Commands.ZRangeByScore, setId, min, max, skip, take, true, token); - ValueTask IRedisNativeClientAsync.ZRangeByScoreWithScoresAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken) - => GetRangeByScoreAsync(Commands.ZRangeByScore, setId, min, max, skip, take, true, cancellationToken); + ValueTask IRedisNativeClientAsync.ZRangeByScoreWithScoresAsync(string setId, long min, long max, int? skip, int? take, CancellationToken token) + => GetRangeByScoreAsync(Commands.ZRangeByScore, setId, min, max, skip, take, true, token); - ValueTask IRedisNativeClientAsync.ZRevRangeByScoreAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZRevRangeByScoreAsync(string setId, double min, double max, int? skip, int? take, CancellationToken token) { //Note: http://redis.io/commands/zrevrangebyscore has max, min in the wrong other - return GetRangeByScoreAsync(Commands.ZRevRangeByScore, setId, max, min, skip, take, false, cancellationToken); + return GetRangeByScoreAsync(Commands.ZRevRangeByScore, setId, max, min, skip, take, false, token); } - ValueTask IRedisNativeClientAsync.ZRevRangeByScoreAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZRevRangeByScoreAsync(string setId, long min, long max, int? skip, int? take, CancellationToken token) { //Note: http://redis.io/commands/zrevrangebyscore has max, min in the wrong other - return GetRangeByScoreAsync(Commands.ZRevRangeByScore, setId, max, min, skip, take, false, cancellationToken); + return GetRangeByScoreAsync(Commands.ZRevRangeByScore, setId, max, min, skip, take, false, token); } - ValueTask IRedisNativeClientAsync.ZRevRangeByScoreWithScoresAsync(string setId, double min, double max, int? skip, int? take, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZRevRangeByScoreWithScoresAsync(string setId, double min, double max, int? skip, int? take, CancellationToken token) { //Note: http://redis.io/commands/zrevrangebyscore has max, min in the wrong other - return GetRangeByScoreAsync(Commands.ZRevRangeByScore, setId, max, min, skip, take, true, cancellationToken); + return GetRangeByScoreAsync(Commands.ZRevRangeByScore, setId, max, min, skip, take, true, token); } - ValueTask IRedisNativeClientAsync.ZRevRangeByScoreWithScoresAsync(string setId, long min, long max, int? skip, int? take, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZRevRangeByScoreWithScoresAsync(string setId, long min, long max, int? skip, int? take, CancellationToken token) { //Note: http://redis.io/commands/zrevrangebyscore has max, min in the wrong other - return GetRangeByScoreAsync(Commands.ZRevRangeByScore, setId, max, min, skip, take, true, cancellationToken); + return GetRangeByScoreAsync(Commands.ZRevRangeByScore, setId, max, min, skip, take, true, token); } - ValueTask IRedisNativeClientAsync.ZRemRangeByRankAsync(string setId, int min, int max, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZRemRangeByRankAsync(string setId, int min, int max, CancellationToken token) { AssertNotNull(setId, nameof(setId)); - return SendExpectLongAsync(cancellationToken, Commands.ZRemRangeByRank, setId.ToUtf8Bytes(), + return SendExpectLongAsync(token, Commands.ZRemRangeByRank, setId.ToUtf8Bytes(), min.ToUtf8Bytes(), max.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.ZRemRangeByScoreAsync(string setId, double fromScore, double toScore, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZRemRangeByScoreAsync(string setId, double fromScore, double toScore, CancellationToken token) { AssertNotNull(setId, nameof(setId)); - return SendExpectLongAsync(cancellationToken, Commands.ZRemRangeByScore, setId.ToUtf8Bytes(), + return SendExpectLongAsync(token, Commands.ZRemRangeByScore, setId.ToUtf8Bytes(), fromScore.ToFastUtf8Bytes(), toScore.ToFastUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.ZRemRangeByScoreAsync(string setId, long fromScore, long toScore, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZRemRangeByScoreAsync(string setId, long fromScore, long toScore, CancellationToken token) { AssertNotNull(setId, nameof(setId)); - return SendExpectLongAsync(cancellationToken, Commands.ZRemRangeByScore, setId.ToUtf8Bytes(), + return SendExpectLongAsync(token, Commands.ZRemRangeByScore, setId.ToUtf8Bytes(), fromScore.ToUtf8Bytes(), toScore.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.ZUnionStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZUnionStoreAsync(string intoSetId, string[] setIds, CancellationToken token) { var setIdsList = new List(setIds); setIdsList.Insert(0, setIds.Length.ToString()); setIdsList.Insert(0, intoSetId); var cmdWithArgs = MergeCommandWithArgs(Commands.ZUnionStore, setIdsList.ToArray()); - return SendExpectLongAsync(cancellationToken, cmdWithArgs); + return SendExpectLongAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.ZInterStoreAsync(string intoSetId, string[] setIds, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.ZInterStoreAsync(string intoSetId, string[] setIds, CancellationToken token) { var setIdsList = new List(setIds); setIdsList.Insert(0, setIds.Length.ToString()); setIdsList.Insert(0, intoSetId); var cmdWithArgs = MergeCommandWithArgs(Commands.ZInterStore, setIdsList.ToArray()); - return SendExpectLongAsync(cancellationToken, cmdWithArgs); + return SendExpectLongAsync(token, cmdWithArgs); } - internal ValueTask ZInterStoreAsync(string intoSetId, string[] setIds, string[] args, CancellationToken cancellationToken) + internal ValueTask ZInterStoreAsync(string intoSetId, string[] setIds, string[] args, CancellationToken token) { var totalArgs = new List(setIds); totalArgs.Insert(0, setIds.Length.ToString()); @@ -1162,10 +1162,10 @@ internal ValueTask ZInterStoreAsync(string intoSetId, string[] setIds, str totalArgs.AddRange(args); var cmdWithArgs = MergeCommandWithArgs(Commands.ZInterStore, totalArgs.ToArray()); - return SendExpectLongAsync(cancellationToken, cmdWithArgs); + return SendExpectLongAsync(token, cmdWithArgs); } - internal ValueTask ZUnionStoreAsync(string intoSetId, string[] setIds, string[] args, CancellationToken cancellationToken) + internal ValueTask ZUnionStoreAsync(string intoSetId, string[] setIds, string[] args, CancellationToken token) { var totalArgs = new List(setIds); totalArgs.Insert(0, setIds.Length.ToString()); @@ -1173,127 +1173,127 @@ internal ValueTask ZUnionStoreAsync(string intoSetId, string[] setIds, str totalArgs.AddRange(args); var cmdWithArgs = MergeCommandWithArgs(Commands.ZUnionStore, totalArgs.ToArray()); - return SendExpectLongAsync(cancellationToken, cmdWithArgs); + return SendExpectLongAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.HMSetAsync(string hashId, byte[][] keys, byte[][] values, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.HMSetAsync(string hashId, byte[][] keys, byte[][] values, CancellationToken token) { AssertNotNull(hashId, nameof(hashId)); var cmdArgs = MergeCommandWithKeysAndValues(Commands.HMSet, hashId.ToUtf8Bytes(), keys, values); - return SendExpectSuccessAsync(cancellationToken, cmdArgs); + return SendExpectSuccessAsync(token, cmdArgs); } - ValueTask IRedisNativeClientAsync.HSetNXAsync(string hashId, byte[] key, byte[] value, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.HSetNXAsync(string hashId, byte[] key, byte[] value, CancellationToken token) { AssertHashIdAndKey(hashId, key); - return SendExpectLongAsync(cancellationToken, Commands.HSetNx, hashId.ToUtf8Bytes(), key, value); + return SendExpectLongAsync(token, Commands.HSetNx, hashId.ToUtf8Bytes(), key, value); } - ValueTask IRedisNativeClientAsync.HIncrbyAsync(string hashId, byte[] key, int incrementBy, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.HIncrbyAsync(string hashId, byte[] key, int incrementBy, CancellationToken token) { AssertHashIdAndKey(hashId, key); - return SendExpectLongAsync(cancellationToken, Commands.HIncrBy, hashId.ToUtf8Bytes(), key, incrementBy.ToString().ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.HIncrBy, hashId.ToUtf8Bytes(), key, incrementBy.ToString().ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.HIncrbyFloatAsync(string hashId, byte[] key, double incrementBy, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.HIncrbyFloatAsync(string hashId, byte[] key, double incrementBy, CancellationToken token) { AssertHashIdAndKey(hashId, key); - return SendExpectDoubleAsync(cancellationToken, Commands.HIncrByFloat, hashId.ToUtf8Bytes(), key, incrementBy.ToString(CultureInfo.InvariantCulture).ToUtf8Bytes()); + return SendExpectDoubleAsync(token, Commands.HIncrByFloat, hashId.ToUtf8Bytes(), key, incrementBy.ToString(CultureInfo.InvariantCulture).ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.HGetAsync(string hashId, byte[] key, CancellationToken cancellationToken) - => HGetAsync(hashId.ToUtf8Bytes(), key, cancellationToken); + ValueTask IRedisNativeClientAsync.HGetAsync(string hashId, byte[] key, CancellationToken token) + => HGetAsync(hashId.ToUtf8Bytes(), key, token); - private ValueTask HGetAsync(byte[] hashId, byte[] key, CancellationToken cancellationToken) + private ValueTask HGetAsync(byte[] hashId, byte[] key, CancellationToken token) { AssertHashIdAndKey(hashId, key); - return SendExpectDataAsync(cancellationToken, Commands.HGet, hashId, key); + return SendExpectDataAsync(token, Commands.HGet, hashId, key); } - ValueTask IRedisNativeClientAsync.HMGetAsync(string hashId, byte[][] keys, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.HMGetAsync(string hashId, byte[][] keys, CancellationToken token) { AssertNotNull(hashId, nameof(hashId)); if (keys.Length == 0) throw new ArgumentNullException(nameof(keys)); var cmdArgs = MergeCommandWithArgs(Commands.HMGet, hashId.ToUtf8Bytes(), keys); - return SendExpectMultiDataAsync(cancellationToken, cmdArgs); + return SendExpectMultiDataAsync(token, cmdArgs); } - ValueTask IRedisNativeClientAsync.HDelAsync(string hashId, byte[] key, CancellationToken cancellationToken) - => HDelAsync(hashId.ToUtf8Bytes(), key, cancellationToken); + ValueTask IRedisNativeClientAsync.HDelAsync(string hashId, byte[] key, CancellationToken token) + => HDelAsync(hashId.ToUtf8Bytes(), key, token); - private ValueTask HDelAsync(byte[] hashId, byte[] key, CancellationToken cancellationToken) + private ValueTask HDelAsync(byte[] hashId, byte[] key, CancellationToken token) { AssertHashIdAndKey(hashId, key); - return SendExpectLongAsync(cancellationToken, Commands.HDel, hashId, key); + return SendExpectLongAsync(token, Commands.HDel, hashId, key); } - ValueTask IRedisNativeClientAsync.HExistsAsync(string hashId, byte[] key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.HExistsAsync(string hashId, byte[] key, CancellationToken token) { AssertHashIdAndKey(hashId, key); - return SendExpectLongAsync(cancellationToken, Commands.HExists, hashId.ToUtf8Bytes(), key); + return SendExpectLongAsync(token, Commands.HExists, hashId.ToUtf8Bytes(), key); } - ValueTask IRedisNativeClientAsync.HKeysAsync(string hashId, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.HKeysAsync(string hashId, CancellationToken token) { AssertNotNull(hashId, nameof(hashId)); - return SendExpectMultiDataAsync(cancellationToken, Commands.HKeys, hashId.ToUtf8Bytes()); + return SendExpectMultiDataAsync(token, Commands.HKeys, hashId.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.HValsAsync(string hashId, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.HValsAsync(string hashId, CancellationToken token) { AssertNotNull(hashId, nameof(hashId)); - return SendExpectMultiDataAsync(cancellationToken, Commands.HVals, hashId.ToUtf8Bytes()); + return SendExpectMultiDataAsync(token, Commands.HVals, hashId.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.HGetAllAsync(string hashId, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.HGetAllAsync(string hashId, CancellationToken token) { AssertNotNull(hashId, nameof(hashId)); - return SendExpectMultiDataAsync(cancellationToken, Commands.HGetAll, hashId.ToUtf8Bytes()); + return SendExpectMultiDataAsync(token, Commands.HGetAll, hashId.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.GeoAddAsync(string key, double longitude, double latitude, string member, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.GeoAddAsync(string key, double longitude, double latitude, string member, CancellationToken token) { AssertNotNull(key, nameof(key)); AssertNotNull(member, nameof(member)); - return SendExpectLongAsync(cancellationToken, Commands.GeoAdd, key.ToUtf8Bytes(), longitude.ToUtf8Bytes(), latitude.ToUtf8Bytes(), member.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.GeoAdd, key.ToUtf8Bytes(), longitude.ToUtf8Bytes(), latitude.ToUtf8Bytes(), member.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.GeoAddAsync(string key, RedisGeo[] geoPoints, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.GeoAddAsync(string key, RedisGeo[] geoPoints, CancellationToken token) { var cmdWithArgs = GeoAddPrepareArgs(key, geoPoints); - return SendExpectLongAsync(cancellationToken, cmdWithArgs); + return SendExpectLongAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.GeoDistAsync(string key, string fromMember, string toMember, string unit, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.GeoDistAsync(string key, string fromMember, string toMember, string unit, CancellationToken token) { AssertNotNull(key, nameof(key)); return unit == null - ? SendExpectDoubleAsync(cancellationToken, Commands.GeoDist, key.ToUtf8Bytes(), fromMember.ToUtf8Bytes(), toMember.ToUtf8Bytes()) - : SendExpectDoubleAsync(cancellationToken, Commands.GeoDist, key.ToUtf8Bytes(), fromMember.ToUtf8Bytes(), toMember.ToUtf8Bytes(), unit.ToUtf8Bytes()); + ? SendExpectDoubleAsync(token, Commands.GeoDist, key.ToUtf8Bytes(), fromMember.ToUtf8Bytes(), toMember.ToUtf8Bytes()) + : SendExpectDoubleAsync(token, Commands.GeoDist, key.ToUtf8Bytes(), fromMember.ToUtf8Bytes(), toMember.ToUtf8Bytes(), unit.ToUtf8Bytes()); } - async ValueTask IRedisNativeClientAsync.GeoHashAsync(string key, string[] members, CancellationToken cancellationToken) + async ValueTask IRedisNativeClientAsync.GeoHashAsync(string key, string[] members, CancellationToken token) { AssertNotNull(key, nameof(key)); var cmdWithArgs = MergeCommandWithArgs(Commands.GeoHash, key.ToUtf8Bytes(), members.Map(x => x.ToUtf8Bytes()).ToArray()); - var result = await SendExpectMultiDataAsync(cancellationToken, cmdWithArgs).ConfigureAwait(false); + var result = await SendExpectMultiDataAsync(token, cmdWithArgs).ConfigureAwait(false); return result.ToStringArray(); } - async ValueTask> IRedisNativeClientAsync.GeoPosAsync(string key, string[] members, CancellationToken cancellationToken) + async ValueTask> IRedisNativeClientAsync.GeoPosAsync(string key, string[] members, CancellationToken token) { AssertNotNull(key, nameof(key)); var cmdWithArgs = MergeCommandWithArgs(Commands.GeoPos, key.ToUtf8Bytes(), members.Map(x => x.ToUtf8Bytes()).ToArray()); - var data = await SendExpectComplexResponseAsync(cancellationToken, cmdWithArgs).ConfigureAwait(false); + var data = await SendExpectComplexResponseAsync(token, cmdWithArgs).ConfigureAwait(false); return GeoPosParseResult(members, data); } - async ValueTask> IRedisNativeClientAsync.GeoRadiusAsync(string key, double longitude, double latitude, double radius, string unit, bool withCoords, bool withDist, bool withHash, int? count, bool? asc, CancellationToken cancellationToken) + async ValueTask> IRedisNativeClientAsync.GeoRadiusAsync(string key, double longitude, double latitude, double radius, string unit, bool withCoords, bool withDist, bool withHash, int? count, bool? asc, CancellationToken token) { var cmdWithArgs = GeoRadiusPrepareArgs(key, longitude, latitude, radius, unit, withCoords, withDist, withHash, count, asc); @@ -1302,7 +1302,7 @@ async ValueTask> IRedisNativeClientAsync.GeoRadiusAsync(str if (!(withCoords || withDist || withHash)) { - var members = (await SendExpectMultiDataAsync(cancellationToken, cmdWithArgs).ConfigureAwait(false)).ToStringArray(); + var members = (await SendExpectMultiDataAsync(token, cmdWithArgs).ConfigureAwait(false)).ToStringArray(); foreach (var member in members) { to.Add(new RedisGeoResult { Member = member }); @@ -1310,14 +1310,14 @@ async ValueTask> IRedisNativeClientAsync.GeoRadiusAsync(str } else { - var data = await SendExpectComplexResponseAsync(cancellationToken, cmdWithArgs).ConfigureAwait(false); + var data = await SendExpectComplexResponseAsync(token, cmdWithArgs).ConfigureAwait(false); GetRadiusParseResult(unit, withCoords, withDist, withHash, to, data); } return to; } - async ValueTask> IRedisNativeClientAsync.GeoRadiusByMemberAsync(string key, string member, double radius, string unit, bool withCoords, bool withDist, bool withHash, int? count, bool? asc, CancellationToken cancellationToken) + async ValueTask> IRedisNativeClientAsync.GeoRadiusByMemberAsync(string key, string member, double radius, string unit, bool withCoords, bool withDist, bool withHash, int? count, bool? asc, CancellationToken token) { var cmdWithArgs = GeoRadiusByMemberPrepareArgs(key, member, radius, unit, withCoords, withDist, withHash, count, asc); @@ -1325,7 +1325,7 @@ async ValueTask> IRedisNativeClientAsync.GeoRadiusByMemberA if (!(withCoords || withDist || withHash)) { - var members = (await SendExpectMultiDataAsync(cancellationToken, cmdWithArgs).ConfigureAwait(false)).ToStringArray(); + var members = (await SendExpectMultiDataAsync(token, cmdWithArgs).ConfigureAwait(false)).ToStringArray(); foreach (var x in members) { to.Add(new RedisGeoResult { Member = x }); @@ -1333,56 +1333,56 @@ async ValueTask> IRedisNativeClientAsync.GeoRadiusByMemberA } else { - var data = await SendExpectComplexResponseAsync(cancellationToken, cmdWithArgs).ConfigureAwait(false); + var data = await SendExpectComplexResponseAsync(token, cmdWithArgs).ConfigureAwait(false); GeoRadiusByMemberParseResult(unit, withCoords, withDist, withHash, to, data); } return to; } - ValueTask IRedisNativeClientAsync.PublishAsync(string toChannel, byte[] message, CancellationToken cancellationToken) - => SendExpectLongAsync(cancellationToken, Commands.Publish, toChannel.ToUtf8Bytes(), message); + ValueTask IRedisNativeClientAsync.PublishAsync(string toChannel, byte[] message, CancellationToken token) + => SendExpectLongAsync(token, Commands.Publish, toChannel.ToUtf8Bytes(), message); - ValueTask IRedisNativeClientAsync.SubscribeAsync(string[] toChannels, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.SubscribeAsync(string[] toChannels, CancellationToken token) { if (toChannels.Length == 0) throw new ArgumentNullException(nameof(toChannels)); var cmdWithArgs = MergeCommandWithArgs(Commands.Subscribe, toChannels); - return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + return SendExpectMultiDataAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.UnSubscribeAsync(string[] fromChannels, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.UnSubscribeAsync(string[] fromChannels, CancellationToken token) { var cmdWithArgs = MergeCommandWithArgs(Commands.UnSubscribe, fromChannels); - return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + return SendExpectMultiDataAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.PSubscribeAsync(string[] toChannelsMatchingPatterns, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.PSubscribeAsync(string[] toChannelsMatchingPatterns, CancellationToken token) { if (toChannelsMatchingPatterns.Length == 0) throw new ArgumentNullException(nameof(toChannelsMatchingPatterns)); var cmdWithArgs = MergeCommandWithArgs(Commands.PSubscribe, toChannelsMatchingPatterns); - return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + return SendExpectMultiDataAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.PUnSubscribeAsync(string[] fromChannelsMatchingPatterns, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.PUnSubscribeAsync(string[] fromChannelsMatchingPatterns, CancellationToken token) { var cmdWithArgs = MergeCommandWithArgs(Commands.PUnSubscribe, fromChannelsMatchingPatterns); - return SendExpectMultiDataAsync(cancellationToken, cmdWithArgs); + return SendExpectMultiDataAsync(token, cmdWithArgs); } - ValueTask IRedisNativeClientAsync.ReceiveMessagesAsync(CancellationToken cancellationToken) - => ReadMultiDataAsync(cancellationToken); + ValueTask IRedisNativeClientAsync.ReceiveMessagesAsync(CancellationToken token) + => ReadMultiDataAsync(token); - ValueTask IRedisNativeClientAsync.CreateSubscriptionAsync(CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.CreateSubscriptionAsync(CancellationToken token) => new RedisSubscription(this).AsValueTaskResult(); - ValueTask IRedisNativeClientAsync.BitCountAsync(string key, CancellationToken cancellationToken) + ValueTask IRedisNativeClientAsync.BitCountAsync(string key, CancellationToken token) { AssertNotNull(key); - return SendExpectLongAsync(cancellationToken, Commands.BitCount, key.ToUtf8Bytes()); + return SendExpectLongAsync(token, Commands.BitCount, key.ToUtf8Bytes()); } ValueTask IRedisNativeClientAsync.DelAsync(params string[] keys) diff --git a/src/ServiceStack.Redis/RedisNativeClient_Utils.Async.cs b/src/ServiceStack.Redis/RedisNativeClient_Utils.Async.cs index 969d6167..ede6855b 100644 --- a/src/ServiceStack.Redis/RedisNativeClient_Utils.Async.cs +++ b/src/ServiceStack.Redis/RedisNativeClient_Utils.Async.cs @@ -15,75 +15,75 @@ namespace ServiceStack.Redis { partial class RedisNativeClient { - private async ValueTask SendExpectMultiDataAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + private async ValueTask SendExpectMultiDataAsync(CancellationToken token, params byte[][] cmdWithBinaryArgs) { - return (await SendReceiveAsync(cmdWithBinaryArgs, ReadMultiDataAsync, cancellationToken, + return (await SendReceiveAsync(cmdWithBinaryArgs, ReadMultiDataAsync, token, PipelineAsync != null ? PipelineAsync.CompleteMultiBytesQueuedCommandAsync : (Action>>)null).ConfigureAwait(false)) ?? TypeConstants.EmptyByteArrayArray; } - protected ValueTask SendWithoutReadAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) - => SendReceiveAsync(cmdWithBinaryArgs, null, cancellationToken, null, sendWithoutRead: true).Await(); + protected ValueTask SendWithoutReadAsync(CancellationToken token, params byte[][] cmdWithBinaryArgs) + => SendReceiveAsync(cmdWithBinaryArgs, null, token, null, sendWithoutRead: true).Await(); - private ValueTask SendExpectLongAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + private ValueTask SendExpectLongAsync(CancellationToken token, params byte[][] cmdWithBinaryArgs) { - return SendReceiveAsync(cmdWithBinaryArgs, ReadLongAsync, cancellationToken, + return SendReceiveAsync(cmdWithBinaryArgs, ReadLongAsync, token, PipelineAsync != null ? PipelineAsync.CompleteLongQueuedCommandAsync : (Action>>)null); } - private ValueTask SendExpectDoubleAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + private ValueTask SendExpectDoubleAsync(CancellationToken token, params byte[][] cmdWithBinaryArgs) { - return SendReceiveAsync(cmdWithBinaryArgs, ReadDoubleAsync, cancellationToken, + return SendReceiveAsync(cmdWithBinaryArgs, ReadDoubleAsync, token, PipelineAsync != null ? PipelineAsync.CompleteDoubleQueuedCommandAsync : (Action>>)null); } - protected ValueTask SendExpectStringAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) - => SendExpectDataAsync(cancellationToken, cmdWithBinaryArgs).FromUtf8BytesAsync(); + protected ValueTask SendExpectStringAsync(CancellationToken token, params byte[][] cmdWithBinaryArgs) + => SendExpectDataAsync(token, cmdWithBinaryArgs).FromUtf8BytesAsync(); - private ValueTask SendExpectSuccessAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + private ValueTask SendExpectSuccessAsync(CancellationToken token, params byte[][] cmdWithBinaryArgs) { //Turn Action into Func Hack Action>> completePipelineFn = null; if (PipelineAsync != null) completePipelineFn = f => { PipelineAsync.CompleteVoidQueuedCommandAsync(ct => f(ct).Await()); }; - return SendReceiveAsync(cmdWithBinaryArgs, ExpectSuccessFnAsync, cancellationToken, completePipelineFn).Await(); + return SendReceiveAsync(cmdWithBinaryArgs, ExpectSuccessFnAsync, token, completePipelineFn).Await(); } - private ValueTask SendExpectDataAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + private ValueTask SendExpectDataAsync(CancellationToken token, params byte[][] cmdWithBinaryArgs) { - return SendReceiveAsync(cmdWithBinaryArgs, ReadDataAsync, cancellationToken, PipelineAsync != null ? PipelineAsync.CompleteBytesQueuedCommandAsync : (Action>>)null); + return SendReceiveAsync(cmdWithBinaryArgs, ReadDataAsync, token, PipelineAsync != null ? PipelineAsync.CompleteBytesQueuedCommandAsync : (Action>>)null); } - private ValueTask SendExpectCodeAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + private ValueTask SendExpectCodeAsync(CancellationToken token, params byte[][] cmdWithBinaryArgs) { - return SendReceiveAsync(cmdWithBinaryArgs, ExpectCodeAsync, cancellationToken, PipelineAsync != null ? PipelineAsync.CompleteStringQueuedCommandAsync : (Action>>)null); + return SendReceiveAsync(cmdWithBinaryArgs, ExpectCodeAsync, token, PipelineAsync != null ? PipelineAsync.CompleteStringQueuedCommandAsync : (Action>>)null); } - private ValueTask SendExpectScanResultAsync(CancellationToken cancellationToken, byte[] cmd, params byte[][] args) + private ValueTask SendExpectScanResultAsync(CancellationToken token, byte[] cmd, params byte[][] args) { var cmdWithArgs = MergeCommandWithArgs(cmd, args); - return SendExpectDeeplyNestedMultiDataAsync(cancellationToken, cmdWithArgs).Await(multiData => ParseScanResult(multiData)); + return SendExpectDeeplyNestedMultiDataAsync(token, cmdWithArgs).Await(multiData => ParseScanResult(multiData)); } - private ValueTask SendExpectDeeplyNestedMultiDataAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) - => SendReceiveAsync(cmdWithBinaryArgs, ReadDeeplyNestedMultiDataAsync, cancellationToken); + private ValueTask SendExpectDeeplyNestedMultiDataAsync(CancellationToken token, params byte[][] cmdWithBinaryArgs) + => SendReceiveAsync(cmdWithBinaryArgs, ReadDeeplyNestedMultiDataAsync, token); - private ValueTask ReadDeeplyNestedMultiDataAsync(CancellationToken cancellationToken) - => ReadDeeplyNestedMultiDataItemAsync(cancellationToken).Await(result => (object[])result); + private ValueTask ReadDeeplyNestedMultiDataAsync(CancellationToken token) + => ReadDeeplyNestedMultiDataItemAsync(token).Await(result => (object[])result); - private async ValueTask ReadDeeplyNestedMultiDataItemAsync(CancellationToken cancellationToken) + private async ValueTask ReadDeeplyNestedMultiDataItemAsync(CancellationToken token) { - int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + int c = await SafeReadByteAsync(token).ConfigureAwait(false); if (c == -1) throw CreateNoMoreDataError(); - var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + var s = await ReadLineAsync(token).ConfigureAwait(false); if (log.IsDebugEnabled) Log("R: {0}", s); switch (c) { case '$': - return await ParseSingleLineAsync(string.Concat(char.ToString((char)c), s), cancellationToken).ConfigureAwait(false); + return await ParseSingleLineAsync(string.Concat(char.ToString((char)c), s), token).ConfigureAwait(false); case '-': throw CreateResponseError(s.StartsWith("ERR") ? s.Substring(4) : s); @@ -94,7 +94,7 @@ private async ValueTask ReadDeeplyNestedMultiDataItemAsync(CancellationT var array = new object[count]; for (int i = 0; i < count; i++) { - array[i] = await ReadDeeplyNestedMultiDataItemAsync(cancellationToken).ConfigureAwait(false); + array[i] = await ReadDeeplyNestedMultiDataItemAsync(token).ConfigureAwait(false); } return array; @@ -108,19 +108,19 @@ private async ValueTask ReadDeeplyNestedMultiDataItemAsync(CancellationT throw CreateResponseError("Unknown reply on multi-request: " + ((char)c) + s); // c here is the protocol prefix } - protected ValueTask SendExpectComplexResponseAsync(CancellationToken cancellationToken, params byte[][] cmdWithBinaryArgs) + protected ValueTask SendExpectComplexResponseAsync(CancellationToken token, params byte[][] cmdWithBinaryArgs) { - return SendReceiveAsync(cmdWithBinaryArgs, ReadComplexResponseAsync, cancellationToken, + return SendReceiveAsync(cmdWithBinaryArgs, ReadComplexResponseAsync, token, PipelineAsync != null ? PipelineAsync.CompleteRedisDataQueuedCommandAsync : (Action>>)null); } - private async ValueTask ReadComplexResponseAsync(CancellationToken cancellationToken) + private async ValueTask ReadComplexResponseAsync(CancellationToken token) { - int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + int c = await SafeReadByteAsync(token).ConfigureAwait(false); if (c == -1) throw CreateNoMoreDataError(); - var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + var s = await ReadLineAsync(token).ConfigureAwait(false); if (log.IsDebugEnabled) Log("R: {0}", s); @@ -129,7 +129,7 @@ private async ValueTask ReadComplexResponseAsync(CancellationToken ca case '$': return new RedisData { - Data = await ParseSingleLineAsync(string.Concat(char.ToString((char)c), s), cancellationToken).ConfigureAwait(false) + Data = await ParseSingleLineAsync(string.Concat(char.ToString((char)c), s), token).ConfigureAwait(false) }; case '-': @@ -141,7 +141,7 @@ private async ValueTask ReadComplexResponseAsync(CancellationToken ca var ret = new RedisData { Children = new List() }; for (var i = 0; i < count; i++) { - ret.Children.Add(await ReadComplexResponseAsync(cancellationToken).ConfigureAwait(false)); + ret.Children.Add(await ReadComplexResponseAsync(token).ConfigureAwait(false)); } return ret; @@ -157,7 +157,7 @@ private async ValueTask ReadComplexResponseAsync(CancellationToken ca private async ValueTask SendReceiveAsync(byte[][] cmdWithBinaryArgs, Func> fn, - CancellationToken cancellationToken, + CancellationToken token, Action>> completePipelineFn = null, bool sendWithoutRead = false) { @@ -177,7 +177,7 @@ private async ValueTask SendReceiveAsync(byte[][] cmdWithBinaryArgs, { // this is deliberately *before* the try, so we never retry // if we've been cancelled - cancellationToken.ThrowIfCancellationRequested(); + token.ThrowIfCancellationRequested(); try { if (TryConnectIfNeeded()) // TODO: asyncify @@ -194,7 +194,7 @@ private async ValueTask SendReceiveAsync(byte[][] cmdWithBinaryArgs, if (PipelineAsync == null) //pipeline will handle flush if in pipeline { - await FlushSendBufferAsync(cancellationToken).ConfigureAwait(false); + await FlushSendBufferAsync(token).ConfigureAwait(false); } else if (!sendWithoutRead) { @@ -207,7 +207,7 @@ private async ValueTask SendReceiveAsync(byte[][] cmdWithBinaryArgs, var result = default(T); if (fn != null) - result = await fn(cancellationToken).ConfigureAwait(false); + result = await fn(token).ConfigureAwait(false); if (Pipeline == null) ResetSendBuffer(); @@ -255,7 +255,7 @@ private async ValueTask SendReceiveAsync(byte[][] cmdWithBinaryArgs, } } - internal ValueTask FlushSendBufferAsync(CancellationToken cancellationToken) + internal ValueTask FlushSendBufferAsync(CancellationToken token) { if (currentBufferIndex > 0) PushCurrentBuffer(); @@ -293,45 +293,45 @@ internal ValueTask FlushSendBufferAsync(CancellationToken cancellationToken) } else { - return WriteAsync(sslStream, cmdBuffer, cancellationToken); + return WriteAsync(sslStream, cmdBuffer, token); } } } return default; - static async ValueTask WriteAsync(Stream destination, List> buffer, CancellationToken cancellationToken) + static async ValueTask WriteAsync(Stream destination, List> buffer, CancellationToken token) { foreach (var segment in buffer) { #if ASYNC_MEMORY - await destination.WriteAsync(new ReadOnlyMemory(segment.Array, segment.Offset, segment.Count), cancellationToken).ConfigureAwait(false); + await destination.WriteAsync(new ReadOnlyMemory(segment.Array, segment.Offset, segment.Count), token).ConfigureAwait(false); #else - await destination.WriteAsync(segment.Array, segment.Offset, segment.Count, cancellationToken).ConfigureAwait(false); + await destination.WriteAsync(segment.Array, segment.Offset, segment.Count, token).ConfigureAwait(false); #endif } } } - private ValueTask SafeReadByteAsync(in CancellationToken cancellationToken, [CallerMemberName]string name = null) + private ValueTask SafeReadByteAsync(in CancellationToken token, [CallerMemberName]string name = null) { AssertNotDisposed(); if (log.IsDebugEnabled && RedisConfig.EnableVerboseLogging) logDebug(name + "()"); - return bufferedReader.ReadByteAsync(cancellationToken); + return bufferedReader.ReadByteAsync(token); } - private async ValueTask ReadLineAsync(CancellationToken cancellationToken) + private async ValueTask ReadLineAsync(CancellationToken token) { AssertNotDisposed(); var sb = StringBuilderCache.Allocate(); int c; - while ((c = await bufferedReader.ReadByteAsync(cancellationToken).ConfigureAwait(false)) != -1) + while ((c = await bufferedReader.ReadByteAsync(token).ConfigureAwait(false)) != -1) { if (c == '\r') continue; @@ -342,7 +342,7 @@ private async ValueTask ReadLineAsync(CancellationToken cancellationToke return StringBuilderCache.ReturnAndFree(sb); } - private async ValueTask ParseSingleLineAsync(string r, CancellationToken cancellationToken) + private async ValueTask ParseSingleLineAsync(string r, CancellationToken token) { if (log.IsDebugEnabled) Log("R: {0}", r); @@ -365,7 +365,7 @@ private async ValueTask ParseSingleLineAsync(string r, CancellationToken var offset = 0; while (count > 0) { - var readCount = await bufferedReader.ReadAsync(retbuf, offset, count, cancellationToken).ConfigureAwait(false); + var readCount = await bufferedReader.ReadAsync(retbuf, offset, count, token).ConfigureAwait(false); if (readCount <= 0) throw CreateResponseError("Unexpected end of Stream"); @@ -373,8 +373,8 @@ private async ValueTask ParseSingleLineAsync(string r, CancellationToken count -= readCount; } - if (await bufferedReader.ReadByteAsync(cancellationToken).ConfigureAwait(false) != '\r' - || await bufferedReader.ReadByteAsync(cancellationToken).ConfigureAwait(false) != '\n') + if (await bufferedReader.ReadByteAsync(token).ConfigureAwait(false) != '\r' + || await bufferedReader.ReadByteAsync(token).ConfigureAwait(false) != '\n') throw CreateResponseError("Invalid termination"); return retbuf; @@ -390,27 +390,27 @@ private async ValueTask ParseSingleLineAsync(string r, CancellationToken throw CreateResponseError("Unexpected reply: " + r); } - private ValueTask ReadDataAsync(CancellationToken cancellationToken) + private ValueTask ReadDataAsync(CancellationToken token) { - var pending = ReadLineAsync(cancellationToken); + var pending = ReadLineAsync(token); return pending.IsCompletedSuccessfully - ? ParseSingleLineAsync(pending.Result, cancellationToken) - : Awaited(this, pending, cancellationToken); + ? ParseSingleLineAsync(pending.Result, token) + : Awaited(this, pending, token); - static async ValueTask Awaited(RedisNativeClient @this, ValueTask pending, CancellationToken cancellationToken) + static async ValueTask Awaited(RedisNativeClient @this, ValueTask pending, CancellationToken token) { var r = await pending.ConfigureAwait(false); - return await @this.ParseSingleLineAsync(r, cancellationToken).ConfigureAwait(false); + return await @this.ParseSingleLineAsync(r, token).ConfigureAwait(false); } } - private async ValueTask ExpectCodeAsync(CancellationToken cancellationToken) + private async ValueTask ExpectCodeAsync(CancellationToken token) { - int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + int c = await SafeReadByteAsync(token).ConfigureAwait(false); if (c == -1) throw CreateNoMoreDataError(); - var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + var s = await ReadLineAsync(token).ConfigureAwait(false); if (log.IsDebugEnabled) Log((char)c + s); @@ -421,13 +421,13 @@ private async ValueTask ExpectCodeAsync(CancellationToken cancellationTo return s; } - private async ValueTask ReadMultiDataAsync(CancellationToken cancellationToken) + private async ValueTask ReadMultiDataAsync(CancellationToken token) { - int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + int c = await SafeReadByteAsync(token).ConfigureAwait(false); if (c == -1) throw CreateNoMoreDataError(); - var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + var s = await ReadLineAsync(token).ConfigureAwait(false); if (log.IsDebugEnabled) Log("R: {0}", s); @@ -436,7 +436,7 @@ private async ValueTask ReadMultiDataAsync(CancellationToken cancellat // Some commands like BRPOPLPUSH may return Bulk Reply instead of Multi-bulk case '$': var t = new byte[2][]; - t[1] = await ParseSingleLineAsync(string.Concat(char.ToString((char)c), s), cancellationToken).ConfigureAwait(false); + t[1] = await ParseSingleLineAsync(string.Concat(char.ToString((char)c), s), token).ConfigureAwait(false); return t; case '-': @@ -454,7 +454,7 @@ private async ValueTask ReadMultiDataAsync(CancellationToken cancellat var result = new byte[count][]; for (int i = 0; i < count; i++) - result[i] = await ReadDataAsync(cancellationToken).ConfigureAwait(false); + result[i] = await ReadDataAsync(token).ConfigureAwait(false); return result; } @@ -464,27 +464,27 @@ private async ValueTask ReadMultiDataAsync(CancellationToken cancellat throw CreateResponseError("Unknown reply on multi-request: " + ((char)c) + s); // c here is the protocol prefix } - internal async ValueTask ReadLongAsync(CancellationToken cancellationToken) + internal async ValueTask ReadLongAsync(CancellationToken token) { - int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + int c = await SafeReadByteAsync(token).ConfigureAwait(false); if (c == -1) throw CreateNoMoreDataError(); - return ParseLong(c, await ReadLineAsync(cancellationToken).ConfigureAwait(false)); + return ParseLong(c, await ReadLineAsync(token).ConfigureAwait(false)); } - private ValueTask ReadDoubleAsync(CancellationToken cancellationToken) - => ReadDataAsync(cancellationToken).Await(bytes => bytes == null ? double.NaN : ParseDouble(bytes)); + private ValueTask ReadDoubleAsync(CancellationToken token) + => ReadDataAsync(token).Await(bytes => bytes == null ? double.NaN : ParseDouble(bytes)); - internal ValueTask ExpectOkAsync(CancellationToken cancellationToken) - => ExpectWordAsync(OK, cancellationToken); + internal ValueTask ExpectOkAsync(CancellationToken token) + => ExpectWordAsync(OK, token); - internal ValueTask ExpectQueuedAsync(CancellationToken cancellationToken) - => ExpectWordAsync(QUEUED, cancellationToken); + internal ValueTask ExpectQueuedAsync(CancellationToken token) + => ExpectWordAsync(QUEUED, token); - internal ValueTask ExpectSuccessFnAsync(CancellationToken cancellationToken) + internal ValueTask ExpectSuccessFnAsync(CancellationToken token) { - var pending = ExpectSuccessAsync(cancellationToken); + var pending = ExpectSuccessAsync(token); return pending.IsCompletedSuccessfully ? default : Awaited(pending); static async ValueTask Awaited(ValueTask pending) @@ -494,13 +494,13 @@ static async ValueTask Awaited(ValueTask pending) } } - internal async ValueTask ExpectSuccessAsync(CancellationToken cancellationToken) + internal async ValueTask ExpectSuccessAsync(CancellationToken token) { - int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + int c = await SafeReadByteAsync(token).ConfigureAwait(false); if (c == -1) throw CreateNoMoreDataError(); - var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + var s = await ReadLineAsync(token).ConfigureAwait(false); if (log.IsDebugEnabled) Log((char)c + s); @@ -510,13 +510,13 @@ internal async ValueTask ExpectSuccessAsync(CancellationToken cancellationToken) } - private async ValueTask ExpectWordAsync(string word, CancellationToken cancellationToken) + private async ValueTask ExpectWordAsync(string word, CancellationToken token) { - int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + int c = await SafeReadByteAsync(token).ConfigureAwait(false); if (c == -1) throw CreateNoMoreDataError(); - var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + var s = await ReadLineAsync(token).ConfigureAwait(false); if (log.IsDebugEnabled) Log((char)c + s); @@ -528,13 +528,13 @@ private async ValueTask ExpectWordAsync(string word, CancellationToken cancellat throw CreateResponseError($"Expected '{word}' got '{s}'"); } - internal async ValueTask ReadMultiDataResultCountAsync(CancellationToken cancellationToken) + internal async ValueTask ReadMultiDataResultCountAsync(CancellationToken token) { - int c = await SafeReadByteAsync(cancellationToken).ConfigureAwait(false); + int c = await SafeReadByteAsync(token).ConfigureAwait(false); if (c == -1) throw CreateNoMoreDataError(); - var s = await ReadLineAsync(cancellationToken).ConfigureAwait(false); + var s = await ReadLineAsync(token).ConfigureAwait(false); if (log.IsDebugEnabled) Log("R: {0}", s); if (c == '-') diff --git a/src/ServiceStack.Redis/RedisSubscription.Async.cs b/src/ServiceStack.Redis/RedisSubscription.Async.cs index ae0027e8..95a861f9 100644 --- a/src/ServiceStack.Redis/RedisSubscription.Async.cs +++ b/src/ServiceStack.Redis/RedisSubscription.Async.cs @@ -45,11 +45,11 @@ private IRedisNativeClientAsync NativeAsync } } - private async ValueTask UnSubscribeFromAllChannelsMatchingAnyPatternsAsync(CancellationToken cancellationToken = default) + private async ValueTask UnSubscribeFromAllChannelsMatchingAnyPatternsAsync(CancellationToken token = default) { if (activeChannels.Count == 0) return; - var multiBytes = await NativeAsync.PUnSubscribeAsync(Array.Empty(), cancellationToken).ConfigureAwait(false); + var multiBytes = await NativeAsync.PUnSubscribeAsync(Array.Empty(), token).ConfigureAwait(false); await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); this.activeChannels = new List(); @@ -59,49 +59,49 @@ ValueTask IAsyncDisposable.DisposeAsync() => IsPSubscription ? UnSubscribeFromAllChannelsMatchingAnyPatternsAsync() : AsAsync().UnSubscribeFromAllChannelsAsync(); - async ValueTask IRedisSubscriptionAsync.SubscribeToChannelsAsync(string[] channels, CancellationToken cancellationToken) + async ValueTask IRedisSubscriptionAsync.SubscribeToChannelsAsync(string[] channels, CancellationToken token) { - var multiBytes = await NativeAsync.SubscribeAsync(channels, cancellationToken).ConfigureAwait(false); + var multiBytes = await NativeAsync.SubscribeAsync(channels, token).ConfigureAwait(false); await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); while (this.SubscriptionCount > 0) { - multiBytes = await NativeAsync.ReceiveMessagesAsync(cancellationToken).ConfigureAwait(false); + multiBytes = await NativeAsync.ReceiveMessagesAsync(token).ConfigureAwait(false); await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); } } - async ValueTask IRedisSubscriptionAsync.SubscribeToChannelsMatchingAsync(string[] patterns, CancellationToken cancellationToken) + async ValueTask IRedisSubscriptionAsync.SubscribeToChannelsMatchingAsync(string[] patterns, CancellationToken token) { - var multiBytes = await NativeAsync.PSubscribeAsync(patterns, cancellationToken).ConfigureAwait(false); + var multiBytes = await NativeAsync.PSubscribeAsync(patterns, token).ConfigureAwait(false); await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); while (this.SubscriptionCount > 0) { - multiBytes = await NativeAsync.ReceiveMessagesAsync(cancellationToken).ConfigureAwait(false); + multiBytes = await NativeAsync.ReceiveMessagesAsync(token).ConfigureAwait(false); await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); } } - async ValueTask IRedisSubscriptionAsync.UnSubscribeFromAllChannelsAsync(CancellationToken cancellationToken) + async ValueTask IRedisSubscriptionAsync.UnSubscribeFromAllChannelsAsync(CancellationToken token) { if (activeChannels.Count == 0) return; - var multiBytes = await NativeAsync.UnSubscribeAsync(Array.Empty(), cancellationToken).ConfigureAwait(false); + var multiBytes = await NativeAsync.UnSubscribeAsync(Array.Empty(), token).ConfigureAwait(false); await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); this.activeChannels = new List(); } - async ValueTask IRedisSubscriptionAsync.UnSubscribeFromChannelsAsync(string[] channels, CancellationToken cancellationToken) + async ValueTask IRedisSubscriptionAsync.UnSubscribeFromChannelsAsync(string[] channels, CancellationToken token) { - var multiBytes = await NativeAsync.UnSubscribeAsync(channels, cancellationToken).ConfigureAwait(false); + var multiBytes = await NativeAsync.UnSubscribeAsync(channels, token).ConfigureAwait(false); await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); } - async ValueTask IRedisSubscriptionAsync.UnSubscribeFromChannelsMatchingAsync(string[] patterns, CancellationToken cancellationToken) + async ValueTask IRedisSubscriptionAsync.UnSubscribeFromChannelsMatchingAsync(string[] patterns, CancellationToken token) { - var multiBytes = await NativeAsync.PUnSubscribeAsync(patterns, cancellationToken).ConfigureAwait(false); + var multiBytes = await NativeAsync.PUnSubscribeAsync(patterns, token).ConfigureAwait(false); await ParseSubscriptionResultsAsync(multiBytes).ConfigureAwait(false); } @@ -170,15 +170,15 @@ private async ValueTask ParseSubscriptionResultsAsync(byte[][] multiBytes) } ValueTask IRedisSubscriptionAsync.SubscribeToChannelsAsync(params string[] channels) - => AsAsync().SubscribeToChannelsAsync(channels, cancellationToken: default); + => AsAsync().SubscribeToChannelsAsync(channels, token: default); ValueTask IRedisSubscriptionAsync.SubscribeToChannelsMatchingAsync(params string[] patterns) - => AsAsync().SubscribeToChannelsMatchingAsync(patterns, cancellationToken: default); + => AsAsync().SubscribeToChannelsMatchingAsync(patterns, token: default); ValueTask IRedisSubscriptionAsync.UnSubscribeFromChannelsAsync(params string[] channels) - => AsAsync().UnSubscribeFromChannelsAsync(channels, cancellationToken: default); + => AsAsync().UnSubscribeFromChannelsAsync(channels, token: default); ValueTask IRedisSubscriptionAsync.UnSubscribeFromChannelsMatchingAsync(params string[] patterns) - => AsAsync().UnSubscribeFromChannelsMatchingAsync(patterns, cancellationToken: default); + => AsAsync().UnSubscribeFromChannelsMatchingAsync(patterns, token: default); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/Support/Locking/DistributedLock.Async.cs b/src/ServiceStack.Redis/Support/Locking/DistributedLock.Async.cs index 7c32db0c..4f79a42b 100644 --- a/src/ServiceStack.Redis/Support/Locking/DistributedLock.Async.cs +++ b/src/ServiceStack.Redis/Support/Locking/DistributedLock.Async.cs @@ -9,7 +9,7 @@ partial class DistributedLock : IDistributedLockAsync { public IDistributedLockAsync AsAsync() => this; - async ValueTask IDistributedLockAsync.LockAsync(string key, int acquisitionTimeout, int lockTimeout, IRedisClientAsync client, CancellationToken cancellationToken) + async ValueTask IDistributedLockAsync.LockAsync(string key, int acquisitionTimeout, int lockTimeout, IRedisClientAsync client, CancellationToken token) { long lockExpire = 0; @@ -25,7 +25,7 @@ async ValueTask IDistributedLockAsync.LockAsync(string key, int acqui var newLockExpire = CalculateLockExpire(ts, lockTimeout); var nativeClient = (IRedisNativeClientAsync)client; - long wasSet = await nativeClient.SetNXAsync(key, BitConverter.GetBytes(newLockExpire), cancellationToken).ConfigureAwait(false); + long wasSet = await nativeClient.SetNXAsync(key, BitConverter.GetBytes(newLockExpire), token).ConfigureAwait(false); int totalTime = 0; while (wasSet == LOCK_NOT_ACQUIRED && totalTime < acquisitionTimeout) { @@ -36,7 +36,7 @@ async ValueTask IDistributedLockAsync.LockAsync(string key, int acqui totalTime += sleepIfLockSet; ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)); newLockExpire = CalculateLockExpire(ts, lockTimeout); - wasSet = await nativeClient.SetNXAsync(key, BitConverter.GetBytes(newLockExpire), cancellationToken).ConfigureAwait(false); + wasSet = await nativeClient.SetNXAsync(key, BitConverter.GetBytes(newLockExpire), token).ConfigureAwait(false); count++; } // acquired lock! @@ -47,9 +47,9 @@ async ValueTask IDistributedLockAsync.LockAsync(string key, int acqui await using (pipe.ConfigureAwait(false)) { long lockValue = 0; - pipe.QueueCommand(r => ((IRedisNativeClientAsync)r).WatchAsync(new[] { key }, cancellationToken)); - pipe.QueueCommand(r => ((IRedisNativeClientAsync)r).GetAsync(key, cancellationToken), x => lockValue = (x != null) ? BitConverter.ToInt64(x, 0) : 0); - await pipe.FlushAsync(cancellationToken).ConfigureAwait(false); + pipe.QueueCommand(r => ((IRedisNativeClientAsync)r).WatchAsync(new[] { key }, token)); + pipe.QueueCommand(r => ((IRedisNativeClientAsync)r).GetAsync(key, token), x => lockValue = (x != null) ? BitConverter.ToInt64(x, 0) : 0); + await pipe.FlushAsync(token).ConfigureAwait(false); // if lock value is 0 (key is empty), or expired, then we can try to acquire it ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)); @@ -57,18 +57,18 @@ async ValueTask IDistributedLockAsync.LockAsync(string key, int acqui { ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)); newLockExpire = CalculateLockExpire(ts, lockTimeout); - var trans = await client.CreateTransactionAsync(cancellationToken).ConfigureAwait(false); + var trans = await client.CreateTransactionAsync(token).ConfigureAwait(false); await using (trans.ConfigureAwait(false)) { var expire = newLockExpire; - trans.QueueCommand(r => ((IRedisNativeClientAsync)r).SetAsync(key, BitConverter.GetBytes(expire), cancellationToken: cancellationToken)); - if (await trans.CommitAsync(cancellationToken).ConfigureAwait(false)) + trans.QueueCommand(r => ((IRedisNativeClientAsync)r).SetAsync(key, BitConverter.GetBytes(expire), token: token)); + if (await trans.CommitAsync(token).ConfigureAwait(false)) wasSet = LOCK_RECOVERED; //recovered lock! } } else { - await nativeClient.UnWatchAsync(cancellationToken).ConfigureAwait(false); + await nativeClient.UnWatchAsync(token).ConfigureAwait(false); } } if (wasSet != LOCK_NOT_ACQUIRED) break; @@ -82,7 +82,7 @@ async ValueTask IDistributedLockAsync.LockAsync(string key, int acqui return new LockState(wasSet, lockExpire); } - async ValueTask IDistributedLockAsync.UnlockAsync(string key, long lockExpire, IRedisClientAsync client, CancellationToken cancellationToken) + async ValueTask IDistributedLockAsync.UnlockAsync(string key, long lockExpire, IRedisClientAsync client, CancellationToken token) { if (lockExpire <= 0) return false; @@ -91,10 +91,10 @@ async ValueTask IDistributedLockAsync.UnlockAsync(string key, long lockExp var pipe = client.CreatePipeline(); await using (pipe.ConfigureAwait(false)) { - pipe.QueueCommand(r => ((IRedisNativeClientAsync)r).WatchAsync(new[] { key }, cancellationToken)); - pipe.QueueCommand(r => ((IRedisNativeClientAsync)r).GetAsync(key, cancellationToken), + pipe.QueueCommand(r => ((IRedisNativeClientAsync)r).WatchAsync(new[] { key }, token)); + pipe.QueueCommand(r => ((IRedisNativeClientAsync)r).GetAsync(key, token), x => lockVal = (x != null) ? BitConverter.ToInt64(x, 0) : 0); - await pipe.FlushAsync(cancellationToken).ConfigureAwait(false); + await pipe.FlushAsync(token).ConfigureAwait(false); } if (lockVal != lockExpire) @@ -103,15 +103,15 @@ async ValueTask IDistributedLockAsync.UnlockAsync(string key, long lockExp Debug.WriteLine($"Unlock(): Failed to unlock key {key}; lock has been acquired by another client "); else Debug.WriteLine($"Unlock(): Failed to unlock key {key}; lock has been identifed as a zombie and harvested "); - await nativeClient.UnWatchAsync(cancellationToken).ConfigureAwait(false); + await nativeClient.UnWatchAsync(token).ConfigureAwait(false); return false; } - var trans = await client.CreateTransactionAsync(cancellationToken).ConfigureAwait(false); + var trans = await client.CreateTransactionAsync(token).ConfigureAwait(false); await using (trans.ConfigureAwait(false)) { - trans.QueueCommand(r => ((IRedisNativeClientAsync)r).DelAsync(key, cancellationToken)); - var rc = await trans.CommitAsync(cancellationToken).ConfigureAwait(false); + trans.QueueCommand(r => ((IRedisNativeClientAsync)r).DelAsync(key, token)); + var rc = await trans.CommitAsync(token).ConfigureAwait(false); if (!rc) Debug.WriteLine($"Unlock(): Failed to delete key {key}; lock has been acquired by another client "); return rc; diff --git a/src/ServiceStack.Redis/Support/Locking/IDistributedLock.Async.cs b/src/ServiceStack.Redis/Support/Locking/IDistributedLock.Async.cs index c658a00e..7b548b82 100644 --- a/src/ServiceStack.Redis/Support/Locking/IDistributedLock.Async.cs +++ b/src/ServiceStack.Redis/Support/Locking/IDistributedLock.Async.cs @@ -10,8 +10,8 @@ namespace ServiceStack.Redis.Support.Locking public interface IDistributedLockAsync { // note: can't use "out" with async, so return LockState instead - ValueTask LockAsync(string key, int acquisitionTimeout, int lockTimeout, IRedisClientAsync client, CancellationToken cancellationToken = default); - ValueTask UnlockAsync(string key, long lockExpire, IRedisClientAsync client, CancellationToken cancellationToken = default); + ValueTask LockAsync(string key, int acquisitionTimeout, int lockTimeout, IRedisClientAsync client, CancellationToken token = default); + ValueTask UnlockAsync(string key, long lockExpire, IRedisClientAsync client, CancellationToken token = default); } public readonly struct LockState diff --git a/src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs b/src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs index 2ae1858c..287abc3a 100644 --- a/src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs +++ b/src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs @@ -26,10 +26,10 @@ public partial class RedisTransaction /// /// Issue exec command (not queued) /// - private async ValueTask ExecAsync(CancellationToken cancellationToken) + private async ValueTask ExecAsync(CancellationToken token) { RedisClient.Exec(); - await RedisClient.FlushSendBufferAsync(cancellationToken).ConfigureAwait(false); + await RedisClient.FlushSendBufferAsync(token).ConfigureAwait(false); RedisClient.ResetSendBuffer(); } @@ -44,7 +44,7 @@ partial void QueueExpectQueuedAsync() }.WithAsyncReadCommand(RedisClient.ExpectQueuedAsync)); } - async ValueTask IRedisTransactionAsync.CommitAsync(CancellationToken cancellationToken) + async ValueTask IRedisTransactionAsync.CommitAsync(CancellationToken token) { bool rc = true; try @@ -67,15 +67,15 @@ async ValueTask IRedisTransactionAsync.CommitAsync(CancellationToken cance // add Exec command at end (not queued) QueuedCommands.Add(new RedisCommand { - }.WithAsyncReturnCommand(r => ExecAsync(cancellationToken))); + }.WithAsyncReturnCommand(r => ExecAsync(token))); //execute transaction - await ExecAsync(cancellationToken).ConfigureAwait(false); + await ExecAsync(token).ConfigureAwait(false); //receive expected results foreach (var queuedCommand in QueuedCommands) { - await queuedCommand.ProcessResultAsync(cancellationToken).ConfigureAwait(false); + await queuedCommand.ProcessResultAsync(token).ConfigureAwait(false); } } catch (RedisTransactionFailedException) @@ -86,12 +86,12 @@ async ValueTask IRedisTransactionAsync.CommitAsync(CancellationToken cance { RedisClient.Transaction = null; ClosePipeline(); - await RedisClient.AddTypeIdsRegisteredDuringPipelineAsync(cancellationToken).ConfigureAwait(false); + await RedisClient.AddTypeIdsRegisteredDuringPipelineAsync(token).ConfigureAwait(false); } return rc; } - ValueTask IRedisTransactionAsync.RollbackAsync(CancellationToken cancellationToken) + ValueTask IRedisTransactionAsync.RollbackAsync(CancellationToken token) { Rollback(); // not currently anything different to do on the async path return default; @@ -100,7 +100,7 @@ ValueTask IRedisTransactionAsync.RollbackAsync(CancellationToken cancellationTok // splitting, we would need to override DisposeAsync and split the code, too - private protected override async ValueTask ReplayAsync(CancellationToken cancellationToken) + private protected override async ValueTask ReplayAsync(CancellationToken token) { bool rc = true; try @@ -110,7 +110,7 @@ private protected override async ValueTask ReplayAsync(CancellationToken c //receive expected results foreach (var queuedCommand in QueuedCommands) { - await queuedCommand.ProcessResultAsync(cancellationToken).ConfigureAwait(false); + await queuedCommand.ProcessResultAsync(token).ConfigureAwait(false); } } catch (RedisTransactionFailedException) @@ -121,7 +121,7 @@ private protected override async ValueTask ReplayAsync(CancellationToken c { RedisClient.Transaction = null; ClosePipeline(); - await RedisClient.AddTypeIdsRegisteredDuringPipelineAsync(cancellationToken).ConfigureAwait(false); + await RedisClient.AddTypeIdsRegisteredDuringPipelineAsync(token).ConfigureAwait(false); } return rc; } diff --git a/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs b/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs index 005a2e01..76091437 100644 --- a/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs @@ -83,9 +83,7 @@ public void TestSameAPI(Type syncInterface, Type asyncInterface) } var expected = new List(); - ParameterToken cancellationToken = new ParameterToken( - (asyncInterface == typeof(IRemoveByPatternAsync) || asyncInterface == typeof(ICacheClientAsync)) - ? "token" : "cancellationToken", typeof(CancellationToken), ParameterAttributes.Optional); + ParameterToken cancellationToken = new ParameterToken("token", typeof(CancellationToken), ParameterAttributes.Optional); foreach (var method in syncInterface.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { AddExpected(method); @@ -150,14 +148,14 @@ public void TestSameAPI(Type syncInterface, Type asyncInterface) AddFrom(typeof(RedisClient), nameof(RedisClient.BitCount)); AddFromTyped(typeof(RedisClient), nameof(RedisClient.ZCount), typeof(string), typeof(double), typeof(double)); // can't expose as SlowlogItem because of interface locations - expected.Add("ValueTask SlowlogGetAsync(int? top = default, CancellationToken cancellationToken = default)"); + expected.Add("ValueTask SlowlogGetAsync(int? top = default, CancellationToken token = default)"); // adding missing "exists" capability - expected.Add("ValueTask SetAsync(string key, byte[] value, bool exists, long expirySeconds = 0, long expiryMilliseconds = 0, CancellationToken cancellationToken = default)"); + expected.Add("ValueTask SetAsync(string key, byte[] value, bool exists, long expirySeconds = 0, long expiryMilliseconds = 0, CancellationToken token = default)"); } else if (asyncInterface == typeof(IRedisClientAsync)) { - expected.Add("ValueTask GetSlowlogAsync(int? numberOfRecords = default, CancellationToken cancellationToken = default)"); - expected.Add("ValueTask SlowlogResetAsync(CancellationToken cancellationToken = default)"); + expected.Add("ValueTask GetSlowlogAsync(int? numberOfRecords = default, CancellationToken token = default)"); + expected.Add("ValueTask SlowlogResetAsync(CancellationToken token = default)"); } else if (asyncInterface == typeof(ICacheClientAsync)) { diff --git a/tests/ServiceStack.Redis.Tests/RedisPipelineTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisPipelineTests.Async.cs index 32c528de..6eea8ed1 100644 --- a/tests/ServiceStack.Redis.Tests/RedisPipelineTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisPipelineTests.Async.cs @@ -29,7 +29,7 @@ public async Task Can_call_single_operation_in_pipeline() { pipeline.QueueCommand(r => r.IncrementValueAsync(Key)); var map = new Dictionary(); - pipeline.QueueCommand(r => new ValueTask(r.GetAsync(Key)), y => map[Key] = y); + pipeline.QueueCommand(r => r.GetAsync(Key).AsValueTask(), y => map[Key] = y); await pipeline.FlushAsync(); } @@ -272,7 +272,7 @@ public async Task Can_call_AddRangeToSet_in_pipeline() await using var pipeline = RedisAsync.CreatePipeline(); var key = "pipeline-test"; - pipeline.QueueCommand(r => new ValueTask(r.RemoveAsync(key))); + pipeline.QueueCommand(r => r.RemoveAsync(key).AsValueTask()); pipeline.QueueCommand(r => r.AddRangeToSetAsync(key, new[] { "A", "B", "C" }.ToList())); await pipeline.FlushAsync(); diff --git a/tests/ServiceStack.Redis.Tests/RedisTransactionTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisTransactionTests.Async.cs index 300f34a2..3d11b6b4 100644 --- a/tests/ServiceStack.Redis.Tests/RedisTransactionTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisTransactionTests.Async.cs @@ -30,7 +30,7 @@ public async Task Can_call_single_operation_in_transaction() { trans.QueueCommand(r => r.IncrementValueAsync(Key)); var map = new Dictionary(); - trans.QueueCommand(r => new ValueTask(r.GetAsync(Key)), y => map[Key] = y); + trans.QueueCommand(r => r.GetAsync(Key).AsValueTask(), y => map[Key] = y); await trans.CommitAsync(); } @@ -59,7 +59,7 @@ public async Task Watch_aborts_transaction() await RedisAsync.WatchAsync(new[] { Key }); await RedisAsync.SetAsync(Key, value1); await using var trans = await RedisAsync.CreateTransactionAsync(); - trans.QueueCommand(r => new ValueTask(r.SetAsync(Key, value1))); + trans.QueueCommand(r => r.SetAsync(Key, value1).AsValueTask()); var success = await trans.CommitAsync(); Assert.False(success); Assert.AreEqual(value1, await RedisAsync.GetAsync(Key)); @@ -251,8 +251,8 @@ public async Task Transaction_can_issue_watch() await using (var trans = await RedisAsync.CreateTransactionAsync()) { - trans.QueueCommand(r => new ValueTask(r.SetAsync(Key, 1))); - trans.QueueCommand(r => new ValueTask(r.SetAsync(KeySquared, 2))); + trans.QueueCommand(r => r.SetAsync(Key, 1).AsValueTask()); + trans.QueueCommand(r => r.SetAsync(KeySquared, 2).AsValueTask()); await trans.CommitAsync(); } @@ -270,8 +270,8 @@ public async Task Can_set_Expiry_on_key_in_transaction() await using (var trans = await RedisAsync.CreateTransactionAsync()) { - trans.QueueCommand(r => new ValueTask(r.AddAsync(key, "Foo"))); - trans.QueueCommand(r => new ValueTask(r.AddAsync(keyWithTtl, "Bar", expiresIn))); + trans.QueueCommand(r => r.AddAsync(key, "Foo").AsValueTask()); + trans.QueueCommand(r => r.AddAsync(keyWithTtl, "Bar", expiresIn).AsValueTask()); if (!await trans.CommitAsync()) throw new Exception("Transaction Failed"); @@ -294,7 +294,7 @@ public async Task Does_not_set_Expiry_on_existing_key_in_transaction() await using (var trans = await RedisAsync.CreateTransactionAsync()) { - trans.QueueCommand(r => new ValueTask(r.AddAsync(key, "Bar", expiresIn))); + trans.QueueCommand(r => r.AddAsync(key, "Bar", expiresIn).AsValueTask()); if (!await trans.CommitAsync()) throw new Exception("Transaction Failed"); From 910fde8e1e53444ecd852291ecf9b695541fd55d Mon Sep 17 00:00:00 2001 From: mgravell Date: Thu, 3 Sep 2020 10:51:28 +0100 Subject: [PATCH 012/107] pre-empt final token renames (won't compile until CI completes) --- src/ServiceStack.Redis/RedisClientSet.Async.cs | 10 +++++----- .../AsyncImplementationsTests.Async.cs | 10 +++++----- .../RedisClientTestsBase.Async.cs | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ServiceStack.Redis/RedisClientSet.Async.cs b/src/ServiceStack.Redis/RedisClientSet.Async.cs index c1339f6d..3445ac70 100644 --- a/src/ServiceStack.Redis/RedisClientSet.Async.cs +++ b/src/ServiceStack.Redis/RedisClientSet.Async.cs @@ -61,7 +61,7 @@ ValueTask> IRedisSetAsync.IntersectAsync(IRedisSetAsync[] withSe } ValueTask> IRedisSetAsync.IntersectAsync(params IRedisSetAsync[] withSets) - => AsAsync().IntersectAsync(withSets, cancellationToken: default); + => AsAsync().IntersectAsync(withSets, token: default); private List MergeSetIds(IRedisSetAsync[] withSets) { @@ -86,7 +86,7 @@ ValueTask IRedisSetAsync.StoreDiffAsync(IRedisSetAsync fromSet, IRedisSetAsync[] } ValueTask IRedisSetAsync.StoreDiffAsync(IRedisSetAsync fromSet, params IRedisSetAsync[] withSets) - => AsAsync().StoreDiffAsync(fromSet, withSets, cancellationToken: default); + => AsAsync().StoreDiffAsync(fromSet, withSets, token: default); ValueTask IRedisSetAsync.StoreIntersectAsync(IRedisSetAsync[] withSets, CancellationToken token) { @@ -95,7 +95,7 @@ ValueTask IRedisSetAsync.StoreIntersectAsync(IRedisSetAsync[] withSets, Cancella } ValueTask IRedisSetAsync.StoreIntersectAsync(params IRedisSetAsync[] withSets) - => AsAsync().StoreIntersectAsync(withSets, cancellationToken: default); + => AsAsync().StoreIntersectAsync(withSets, token: default); ValueTask IRedisSetAsync.StoreUnionAsync(IRedisSetAsync[] withSets, CancellationToken token) { @@ -104,7 +104,7 @@ ValueTask IRedisSetAsync.StoreUnionAsync(IRedisSetAsync[] withSets, Cancellation } ValueTask IRedisSetAsync.StoreUnionAsync(params IRedisSetAsync[] withSets) - => AsAsync().StoreUnionAsync(withSets, cancellationToken: default); + => AsAsync().StoreUnionAsync(withSets, token: default); ValueTask> IRedisSetAsync.UnionAsync(IRedisSetAsync[] withSets, CancellationToken token) { @@ -113,6 +113,6 @@ ValueTask> IRedisSetAsync.UnionAsync(IRedisSetAsync[] withSets, } ValueTask> IRedisSetAsync.UnionAsync(params IRedisSetAsync[] withSets) - => AsAsync().UnionAsync(withSets, cancellationToken: default); + => AsAsync().UnionAsync(withSets, token: default); } } \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs b/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs index 76091437..3b835c38 100644 --- a/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/AsyncImplementationsTests.Async.cs @@ -83,7 +83,7 @@ public void TestSameAPI(Type syncInterface, Type asyncInterface) } var expected = new List(); - ParameterToken cancellationToken = new ParameterToken("token", typeof(CancellationToken), ParameterAttributes.Optional); + ParameterToken cancellationParameter = new ParameterToken("token", typeof(CancellationToken), ParameterAttributes.Optional); foreach (var method in syncInterface.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { AddExpected(method); @@ -367,11 +367,11 @@ static bool IsParams(in MethodToken tok) if (IsParams(tok)) { - // include it with params but without cancellationToken + // include it with params but without CancellationToken tok = new MethodToken(name, returnType, parameters, tok.IsGenericMethod, tok.IsGenericMethodDefinition, tok.GetGenericArguments(), tok.AllAttributes()); expected.Add(GetSignature(tok)); - // and now remove the params so we can get with cancellationToken + // and now remove the params so we can get with CancellationToken ref ParameterToken p = ref parameters[parameters.Length - 1]; p = p.WithAllAttributes(p.AllAttributes().Where(a => !(a is ParamArrayAttribute)).ToArray()); } @@ -483,11 +483,11 @@ static void Insert(ref ParameterToken[] parameters, int index, ParameterToken va } } - // append optional cancellationToken + // append optional CancellationToken if (addCancellation) { Array.Resize(ref parameters, parameters.Length + 1); - parameters[parameters.Length - 1] = cancellationToken; + parameters[parameters.Length - 1] = cancellationParameter; } tok = new MethodToken(name, returnType, parameters, tok.IsGenericMethod, tok.IsGenericMethodDefinition, tok.GetGenericArguments(), tok.AllAttributes()); expected.Add(GetSignature(tok)); diff --git a/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs b/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs index d7c6244e..40d56a42 100644 --- a/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisClientTestsBase.Async.cs @@ -113,10 +113,10 @@ public override void OnAfterEachTest() base.OnAfterEachTest(); } - protected static async ValueTask> ToListAsync(IAsyncEnumerable source, CancellationToken cancellationToken = default) + protected static async ValueTask> ToListAsync(IAsyncEnumerable source, CancellationToken token = default) { var list = new List(); - await foreach (var value in source.ConfigureAwait(false).WithCancellation(cancellationToken)) + await foreach (var value in source.ConfigureAwait(false).WithCancellation(token)) { list.Add(value); } From b9729dddda548fb69936a0e30ab6b8550e487606 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Thu, 3 Sep 2020 21:57:39 +0800 Subject: [PATCH 013/107] Update RedisBasicPersistenceProviderTests.Async.cs --- .../RedisBasicPersistenceProviderTests.Async.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ServiceStack.Redis.Tests/RedisBasicPersistenceProviderTests.Async.cs b/tests/ServiceStack.Redis.Tests/RedisBasicPersistenceProviderTests.Async.cs index e2e447b1..53bab942 100644 --- a/tests/ServiceStack.Redis.Tests/RedisBasicPersistenceProviderTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/RedisBasicPersistenceProviderTests.Async.cs @@ -218,12 +218,16 @@ public async Task Can_As_DeleteAll_with_script() }.Init(); var type = typeof(TestModel).FullName; +#if DEBUG RedisRaw.DebugAllowSync = true; // not reasonable to allow async from Lisp +#endif context.EvaluateCode($"redis.call('DeleteAll<{type}>') |> return"); context.EvaluateCode($"redis.call('As<{type}>').call('DeleteAll') |> return"); context.RenderLisp($"(call redis \"DeleteAll<{type}>\")"); context.RenderLisp($"(call (call redis \"As<{type}>\") \"DeleteAll\")"); +#if DEBUG RedisRaw.DebugAllowSync = false; +#endif var allModels = await RedisAsync.As().GetAllAsync(); Assert.That(allModels, Is.Empty); From e4074483d5625d5c4156c1dde115da126417603a Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Thu, 3 Sep 2020 22:28:03 +0800 Subject: [PATCH 014/107] Add 1-pass of Rider quick fixes across *Async.cs classes --- .../Generic/RedisClientSet.Generic.Async.cs | 2 +- .../RedisClientSortedSet.Generic.Async.cs | 6 +++--- .../Generic/RedisTypedClient.Async.cs | 8 ++++---- src/ServiceStack.Redis/RedisClient.Async.cs | 16 ++++++++------- .../RedisClientList.Async.cs | 8 ++++---- .../RedisClientManagerCacheClient.Async.cs | 20 +++++++++---------- .../RedisClientSet.Async.cs | 2 +- .../RedisNativeClient.Async.cs | 20 +++++++++---------- .../RedisNativeClient_Utils.Async.cs | 2 +- .../ServiceStack.Redis.csproj | 3 --- .../ValueTask_Utils.Async.cs | 20 +++++++++---------- 11 files changed, 53 insertions(+), 54 deletions(-) diff --git a/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs index 9d3c7818..3c258204 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs @@ -57,7 +57,7 @@ async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationTok List pageResults; do { - pageResults = await AsyncClient.GetSortedEntryValuesAsync(this, skip, skip + PageLimit - 1).ConfigureAwait(false); + pageResults = await AsyncClient.GetSortedEntryValuesAsync(this, skip, skip + PageLimit - 1, token).ConfigureAwait(false); foreach (var result in pageResults) { yield return result; diff --git a/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs index d0973d66..6963eda2 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs @@ -25,16 +25,16 @@ internal partial class RedisClientSortedSet IRedisSortedSetAsync AsAsync() => this; ValueTask IRedisSortedSetAsync.AddAsync(T item, double score, CancellationToken token) - => AsyncClient.AddItemToSortedSetAsync(this, item, score); + => AsyncClient.AddItemToSortedSetAsync(this, item, score, token); ValueTask IRedisSortedSetAsync.CountAsync(CancellationToken token) => AsyncClient.GetSortedSetCountAsync(this, token).AsInt32(); ValueTask> IRedisSortedSetAsync.GetAllAsync(CancellationToken token) - => AsyncClient.GetAllItemsFromSortedSetAsync(this); + => AsyncClient.GetAllItemsFromSortedSetAsync(this, token); ValueTask> IRedisSortedSetAsync.GetAllDescendingAsync(CancellationToken token) - => AsyncClient.GetAllItemsFromSortedSetDescAsync(this); + => AsyncClient.GetAllItemsFromSortedSetDescAsync(this, token); async IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken token) { diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs index f4d7b67b..28e11293 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs @@ -200,20 +200,20 @@ static void AssertNotNull(object obj, string name = "key") async ValueTask IRedisTypedClientAsync.SetValueAsync(string key, T entity, TimeSpan expireIn, CancellationToken token) { AssertNotNull(key); - await AsyncClient.SetAsync(key, SerializeValue(entity)).ConfigureAwait(false); + await AsyncClient.SetAsync(key, SerializeValue(entity), token).ConfigureAwait(false); await client.RegisterTypeIdAsync(entity, token).ConfigureAwait(false); } async ValueTask IRedisTypedClientAsync.SetValueIfNotExistsAsync(string key, T entity, CancellationToken token) { - var success = await AsyncNative.SetNXAsync(key, SerializeValue(entity)).IsSuccessAsync().ConfigureAwait(false); + var success = await AsyncNative.SetNXAsync(key, SerializeValue(entity), token).IsSuccessAsync().ConfigureAwait(false); if (success) await client.RegisterTypeIdAsync(entity, token).ConfigureAwait(false); return success; } async ValueTask IRedisTypedClientAsync.SetValueIfExistsAsync(string key, T entity, CancellationToken token) { - var success = await AsyncNative.SetAsync(key, SerializeValue(entity), exists: true).ConfigureAwait(false); + var success = await AsyncNative.SetAsync(key, SerializeValue(entity), exists: true, token: token).ConfigureAwait(false); if (success) await client.RegisterTypeIdAsync(entity, token).ConfigureAwait(false); return success; } @@ -365,7 +365,7 @@ ValueTask IRedisTypedClientAsync.GetSetCountAsync(IRedisSetAsync set => AsyncNative.SCardAsync(set.Id, token); ValueTask IRedisTypedClientAsync.SetContainsItemAsync(IRedisSetAsync set, T item, CancellationToken token) - => AsyncNative.SIsMemberAsync(set.Id, SerializeValue(item)).IsSuccessAsync(); + => AsyncNative.SIsMemberAsync(set.Id, SerializeValue(item), token).IsSuccessAsync(); async ValueTask> IRedisTypedClientAsync.GetIntersectFromSetsAsync(IRedisSetAsync[] sets, CancellationToken token) { diff --git a/src/ServiceStack.Redis/RedisClient.Async.cs b/src/ServiceStack.Redis/RedisClient.Async.cs index d0cfc466..4d19f225 100644 --- a/src/ServiceStack.Redis/RedisClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClient.Async.cs @@ -252,7 +252,7 @@ ValueTask> IRedisClientAsync.GetValuesAsync(List keys, Canc if (keys == null) throw new ArgumentNullException(nameof(keys)); if (keys.Count == 0) return new List().AsValueTaskResult(); - return NativeAsync.MGetAsync(keys.ToArray(), token).Await(val => ParseGetValuesResult(val)); + return NativeAsync.MGetAsync(keys.ToArray(), token).Await(ParseGetValuesResult); } ValueTask> IRedisClientAsync.GetValuesAsync(List keys, CancellationToken token) @@ -260,7 +260,7 @@ ValueTask> IRedisClientAsync.GetValuesAsync(List keys, Cancel if (keys == null) throw new ArgumentNullException(nameof(keys)); if (keys.Count == 0) return new List().AsValueTaskResult(); - return NativeAsync.MGetAsync(keys.ToArray(), token).Await(value => ParseGetValuesResult(value)); + return NativeAsync.MGetAsync(keys.ToArray(), token).Await(ParseGetValuesResult); } ValueTask> IRedisClientAsync.GetValuesMapAsync(List keys, CancellationToken token) @@ -407,19 +407,21 @@ Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt AssertNotInTransaction(); return ExecAsync(async r => { - await r.SetAsync(key, value).ConfigureAwait(false); - await r.ExpireEntryAtAsync(key, ConvertToServerDate(expiresAt)).ConfigureAwait(false); + await r.SetAsync(key, value, token).ConfigureAwait(false); + await r.ExpireEntryAtAsync(key, ConvertToServerDate(expiresAt), token).ConfigureAwait(false); }).AwaitAsTrueTask(); } Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) { if (AssertServerVersionNumber() >= 2600) { - return ExecAsync(r => ((IRedisNativeClientAsync)r).SetAsync(key, ToBytes(value), 0, expiryMilliseconds: (long)expiresIn.TotalMilliseconds)).AwaitAsTrueTask(); + return ExecAsync(r => ((IRedisNativeClientAsync)r) + .SetAsync(key, ToBytes(value), 0, expiryMilliseconds: (long)expiresIn.TotalMilliseconds, token)).AwaitAsTrueTask(); } else { - return ExecAsync(r => ((IRedisNativeClientAsync)r).SetExAsync(key, (int)expiresIn.TotalSeconds, ToBytes(value))).AwaitAsTrueTask(); + return ExecAsync(r => ((IRedisNativeClientAsync)r) + .SetExAsync(key, (int)expiresIn.TotalSeconds, ToBytes(value), token)).AwaitAsTrueTask(); } } @@ -640,7 +642,7 @@ async Task IEntityStoreAsync.DeleteByIdsAsync(ICollection ids, CancellationTo { if (ids == null || ids.Count == 0) return; - var idsList = ids.Cast(); + var idsList = ids.Cast().ToList(); var urnKeys = idsList.Map(UrnKey); await AsAsync().RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); await this.RemoveTypeIdsAsync(idsList.Map(x => x.ToString()).ToArray(), token).ConfigureAwait(false); diff --git a/src/ServiceStack.Redis/RedisClientList.Async.cs b/src/ServiceStack.Redis/RedisClientList.Async.cs index adb1137e..d14d57b3 100644 --- a/src/ServiceStack.Redis/RedisClientList.Async.cs +++ b/src/ServiceStack.Redis/RedisClientList.Async.cs @@ -40,7 +40,7 @@ ValueTask IRedisListAsync.CountAsync(CancellationToken token) => AsyncClient.GetListCountAsync(listId, token).AsInt32(); ValueTask IRedisListAsync.DequeueAsync(CancellationToken token) - => AsyncClient.DequeueItemFromListAsync(listId); + => AsyncClient.DequeueItemFromListAsync(listId, token); ValueTask IRedisListAsync.EnqueueAsync(string value, CancellationToken token) => AsyncClient.EnqueueItemOnListAsync(listId, value, token); @@ -138,7 +138,7 @@ async ValueTask IRedisListAsync.ContainsAsync(string value, CancellationTo } ValueTask IRedisListAsync.ClearAsync(CancellationToken token) - => AsyncClient.RemoveAllFromListAsync(listId); + => AsyncClient.RemoveAllFromListAsync(listId, token); async ValueTask IRedisListAsync.IndexOfAsync(string value, CancellationToken token) { @@ -153,9 +153,9 @@ async ValueTask IRedisListAsync.IndexOfAsync(string value, CancellationToke } ValueTask IRedisListAsync.ElementAtAsync(int index, CancellationToken token) - => AsyncClient.GetItemFromListAsync(listId, index); + => AsyncClient.GetItemFromListAsync(listId, index, token); ValueTask IRedisListAsync.SetValueAsync(int index, string value, CancellationToken token) - => AsyncClient.SetItemInListAsync(listId, index, value); + => AsyncClient.SetItemInListAsync(listId, index, value, token); } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs b/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs index 5febd0bd..3a0c9290 100644 --- a/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs @@ -30,19 +30,19 @@ async Task ICacheClientAsync.GetAsync(string key, CancellationToken token) async Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken token) { await using var client = await GetClientAsync(token).ConfigureAwait(false); - return await client.SetAsync(key, value, token).ConfigureAwait(false); + return await client.SetAsync(key, value, token).ConfigureAwait(false); } async Task ICacheClientAsync.SetAsync(string key, T value, DateTime expiresAt, CancellationToken token) { await using var client = await GetClientAsync(token).ConfigureAwait(false); - return await client.SetAsync(key, value, expiresAt, token).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresAt, token).ConfigureAwait(false); } async Task ICacheClientAsync.SetAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) { await using var client = await GetClientAsync(token).ConfigureAwait(false); - return await client.SetAsync(key, value, expiresIn, token).ConfigureAwait(false); + return await client.SetAsync(key, value, expiresIn, token).ConfigureAwait(false); } async Task ICacheClientAsync.FlushAllAsync(CancellationToken token) @@ -60,7 +60,7 @@ async Task> ICacheClientAsync.GetAllAsync(IEnumerable< async Task ICacheClientAsync.SetAllAsync(IDictionary values, CancellationToken token) { await using var client = await GetClientAsync(token).ConfigureAwait(false); - await client.SetAllAsync(values, token).ConfigureAwait(false); + await client.SetAllAsync(values, token).ConfigureAwait(false); } async Task ICacheClientAsync.RemoveAsync(string key, CancellationToken token) @@ -129,37 +129,37 @@ async Task ICacheClientAsync.DecrementAsync(string key, uint amount, Cance async Task ICacheClientAsync.AddAsync(string key, T value, CancellationToken token) { await using var client = await GetClientAsync(token).ConfigureAwait(false); - return await client.AddAsync(key, value, token).ConfigureAwait(false); + return await client.AddAsync(key, value, token).ConfigureAwait(false); } async Task ICacheClientAsync.ReplaceAsync(string key, T value, CancellationToken token) { await using var client = await GetClientAsync(token).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, token).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, token).ConfigureAwait(false); } async Task ICacheClientAsync.AddAsync(string key, T value, DateTime expiresAt, CancellationToken token) { await using var client = await GetClientAsync(token).ConfigureAwait(false); - return await client.AddAsync(key, value, expiresAt, token).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresAt, token).ConfigureAwait(false); } async Task ICacheClientAsync.ReplaceAsync(string key, T value, DateTime expiresAt, CancellationToken token) { await using var client = await GetClientAsync(token).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, expiresAt, token).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresAt, token).ConfigureAwait(false); } async Task ICacheClientAsync.AddAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) { await using var client = await GetClientAsync(token).ConfigureAwait(false); - return await client.AddAsync(key, value, expiresIn, token).ConfigureAwait(false); + return await client.AddAsync(key, value, expiresIn, token).ConfigureAwait(false); } async Task ICacheClientAsync.ReplaceAsync(string key, T value, TimeSpan expiresIn, CancellationToken token) { await using var client = await GetClientAsync(token).ConfigureAwait(false); - return await client.ReplaceAsync(key, value, expiresIn, token).ConfigureAwait(false); + return await client.ReplaceAsync(key, value, expiresIn, token).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/RedisClientSet.Async.cs b/src/ServiceStack.Redis/RedisClientSet.Async.cs index 3445ac70..3ecb5b94 100644 --- a/src/ServiceStack.Redis/RedisClientSet.Async.cs +++ b/src/ServiceStack.Redis/RedisClientSet.Async.cs @@ -46,7 +46,7 @@ ValueTask> IRedisSetAsync.GetAllAsync(CancellationToken token) => AsyncClient.GetAllItemsFromSetAsync(setId, token); IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken token) - => AsyncClient.ScanAllSetItemsAsync(setId).GetAsyncEnumerator(token); // uses SSCAN + => AsyncClient.ScanAllSetItemsAsync(setId, token: token).GetAsyncEnumerator(token); // uses SSCAN ValueTask IRedisSetAsync.GetRandomEntryAsync(CancellationToken token) => AsyncClient.GetRandomItemFromSetAsync(setId, token); diff --git a/src/ServiceStack.Redis/RedisNativeClient.Async.cs b/src/ServiceStack.Redis/RedisNativeClient.Async.cs index cb915664..c7ff1e89 100644 --- a/src/ServiceStack.Redis/RedisNativeClient.Async.cs +++ b/src/ServiceStack.Redis/RedisNativeClient.Async.cs @@ -48,7 +48,7 @@ ValueTask IRedisNativeClientAsync.SetAsync(string key, byte[] value, bool value ??= TypeConstants.EmptyByteArray; if (value.Length > OneGb) - throw new ArgumentException("value exceeds 1G", "value"); + throw new ArgumentException("value exceeds 1G", nameof(value)); var entryExists = exists ? Commands.Xx : Commands.Nx; byte[][] args; @@ -73,7 +73,7 @@ ValueTask IRedisNativeClientAsync.SetAsync(string key, byte[] value, long expiry value ??= TypeConstants.EmptyByteArray; if (value.Length > OneGb) - throw new ArgumentException("value exceeds 1G", "value"); + throw new ArgumentException("value exceeds 1G", nameof(value)); byte[][] args; if (expiryMilliseconds != 0) @@ -162,16 +162,16 @@ internal ValueTask HSetAsync(byte[] hashId, byte[] key, byte[] value, Canc ValueTask IRedisNativeClientAsync.RandomKeyAsync(CancellationToken token) => SendExpectDataAsync(token, Commands.RandomKey).FromUtf8BytesAsync(); - ValueTask IRedisNativeClientAsync.RenameAsync(string oldKeyname, string newKeyname, CancellationToken token) + ValueTask IRedisNativeClientAsync.RenameAsync(string oldKeyName, string newKeyName, CancellationToken token) { - CheckRenameKeys(oldKeyname, newKeyname); - return SendExpectSuccessAsync(token, Commands.Rename, oldKeyname.ToUtf8Bytes(), newKeyname.ToUtf8Bytes()); + CheckRenameKeys(oldKeyName, newKeyName); + return SendExpectSuccessAsync(token, Commands.Rename, oldKeyName.ToUtf8Bytes(), newKeyName.ToUtf8Bytes()); } - ValueTask IRedisNativeClientAsync.RenameNxAsync(string oldKeyname, string newKeyname, CancellationToken token) + ValueTask IRedisNativeClientAsync.RenameNxAsync(string oldKeyName, string newKeyName, CancellationToken token) { - CheckRenameKeys(oldKeyname, newKeyname); - return SendExpectLongAsync(token, Commands.RenameNx, oldKeyname.ToUtf8Bytes(), newKeyname.ToUtf8Bytes()).IsSuccessAsync(); + CheckRenameKeys(oldKeyName, newKeyName); + return SendExpectLongAsync(token, Commands.RenameNx, oldKeyName.ToUtf8Bytes(), newKeyName.ToUtf8Bytes()).IsSuccessAsync(); } ValueTask IRedisNativeClientAsync.MSetAsync(byte[][] keys, byte[][] values, CancellationToken token) @@ -308,7 +308,7 @@ ValueTask IRedisNativeClientAsync.SetExAsync(string key, int expireInSeconds, by value ??= TypeConstants.EmptyByteArray; if (value.Length > OneGb) - throw new ArgumentException("value exceeds 1G", "value"); + throw new ArgumentException("value exceeds 1G", nameof(value)); return SendExpectSuccessAsync(token, Commands.SetEx, key.ToUtf8Bytes(), expireInSeconds.ToUtf8Bytes(), value); } @@ -455,7 +455,7 @@ protected ValueTask RawCommandAsync(CancellationToken token, params o } ValueTask> IRedisNativeClientAsync.InfoAsync(CancellationToken token) - => SendExpectStringAsync(token, Commands.Info).Await(info => ParseInfoResult(info)); + => SendExpectStringAsync(token, Commands.Info).Await(ParseInfoResult); ValueTask IRedisNativeClientAsync.ZRangeByLexAsync(string setId, string min, string max, int? skip, int? take, CancellationToken token) => SendExpectMultiDataAsync(token, GetZRangeByLexArgs(setId, min, max, skip, take)); diff --git a/src/ServiceStack.Redis/RedisNativeClient_Utils.Async.cs b/src/ServiceStack.Redis/RedisNativeClient_Utils.Async.cs index ede6855b..966dbbf1 100644 --- a/src/ServiceStack.Redis/RedisNativeClient_Utils.Async.cs +++ b/src/ServiceStack.Redis/RedisNativeClient_Utils.Async.cs @@ -250,7 +250,7 @@ private async ValueTask SendReceiveAsync(byte[][] cmdWithBinaryArgs, } Interlocked.Increment(ref RedisState.TotalRetryCount); - await Task.Delay(GetBackOffMultiplier(++i)).ConfigureAwait(false); + await Task.Delay(GetBackOffMultiplier(++i), token).ConfigureAwait(false); } } } diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.csproj index fa08d197..4808134c 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.csproj @@ -42,8 +42,5 @@ - - - \ No newline at end of file diff --git a/src/ServiceStack.Redis/ValueTask_Utils.Async.cs b/src/ServiceStack.Redis/ValueTask_Utils.Async.cs index 78306d73..87f6c917 100644 --- a/src/ServiceStack.Redis/ValueTask_Utils.Async.cs +++ b/src/ServiceStack.Redis/ValueTask_Utils.Async.cs @@ -19,7 +19,7 @@ internal static ValueTask Await(this ValueTask pending) { return Awaited(pending); } - async static ValueTask Awaited(ValueTask pending) + static async ValueTask Awaited(ValueTask pending) => await pending.ConfigureAwait(false); } @@ -27,7 +27,7 @@ async static ValueTask Awaited(ValueTask pending) internal static ValueTask Await(this ValueTask pending, Func projection) { return pending.IsCompletedSuccessfully ? projection(pending.Result).AsValueTaskResult() : Awaited(pending, projection); - async static ValueTask Awaited(ValueTask pending, Func projection) + static async ValueTask Awaited(ValueTask pending, Func projection) => projection(await pending.ConfigureAwait(false)); } @@ -35,7 +35,7 @@ async static ValueTask Awaited(ValueTask pending, Func p internal static ValueTask AsInt32(this ValueTask pending) { return pending.IsCompletedSuccessfully ? (checked((int)pending.Result)).AsValueTaskResult() : Awaited(pending); - async static ValueTask Awaited(ValueTask pending) + static async ValueTask Awaited(ValueTask pending) => checked((int)await pending.ConfigureAwait(false)); } @@ -43,7 +43,7 @@ async static ValueTask Awaited(ValueTask pending) internal static ValueTask Await(this ValueTask pending, Func projection, TState state) { return pending.IsCompletedSuccessfully ? projection(pending.Result, state).AsValueTaskResult() : Awaited(pending, projection, state); - async static ValueTask Awaited(ValueTask pending, Func projection, TState state) + static async ValueTask Awaited(ValueTask pending, Func projection, TState state) => projection(await pending.ConfigureAwait(false), state); } @@ -59,7 +59,7 @@ internal static ValueTask AwaitAsTrue(this ValueTask pending) { return Awaited(pending); } - async static ValueTask Awaited(ValueTask pending) + static async ValueTask Awaited(ValueTask pending) { await pending.ConfigureAwait(false); return true; @@ -78,7 +78,7 @@ internal static Task AwaitAsTrueTask(this ValueTask pending) { return Awaited(pending); } - async static Task Awaited(ValueTask pending) + static async Task Awaited(ValueTask pending) { await pending.ConfigureAwait(false); return true; @@ -97,7 +97,7 @@ internal static ValueTask AwaitAsTrue(this ValueTask pending) { return Awaited(pending); } - async static ValueTask Awaited(ValueTask pending) + static async ValueTask Awaited(ValueTask pending) { await pending.ConfigureAwait(false); return true; @@ -108,7 +108,7 @@ async static ValueTask Awaited(ValueTask pending) internal static ValueTask IsSuccessAsync(this ValueTask pending) { return pending.IsCompletedSuccessfully ? (pending.Result == RedisNativeClient.Success).AsValueTaskResult() : Awaited(pending); - async static ValueTask Awaited(ValueTask pending) + static async ValueTask Awaited(ValueTask pending) => (await pending.ConfigureAwait(false)) == RedisNativeClient.Success; } @@ -116,7 +116,7 @@ async static ValueTask Awaited(ValueTask pending) internal static Task IsSuccessTaskAsync(this ValueTask pending) { return pending.IsCompletedSuccessfully ? (pending.Result == RedisNativeClient.Success ? s_TaskTrue : s_TaskFalse) : Awaited(pending); - async static Task Awaited(ValueTask pending) + static async Task Awaited(ValueTask pending) => (await pending.ConfigureAwait(false)) == RedisNativeClient.Success; } @@ -145,7 +145,7 @@ static async ValueTask> Awaited(ValueTask pending) internal static ValueTask Await(this ValueTask pending, T result) { return pending.IsCompletedSuccessfully ? result.AsValueTaskResult() : Awaited(pending, result); - async static ValueTask Awaited(ValueTask pending, T result) + static async ValueTask Awaited(ValueTask pending, T result) { await pending.ConfigureAwait(false); return result; From c54789e290f1f9fec9106aff9ff6ecaa49bc85f7 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Thu, 3 Sep 2020 23:03:21 +0800 Subject: [PATCH 015/107] Update RedisNativeClient.cs --- src/ServiceStack.Redis/RedisNativeClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ServiceStack.Redis/RedisNativeClient.cs b/src/ServiceStack.Redis/RedisNativeClient.cs index d4fce91f..90fb0224 100644 --- a/src/ServiceStack.Redis/RedisNativeClient.cs +++ b/src/ServiceStack.Redis/RedisNativeClient.cs @@ -1757,7 +1757,7 @@ private byte[][] GetRangeByScore(byte[] commandBytes, string setId, double min, double max, int? skip, int? take, bool withScores) { var args = GetRangeByScoreArgs(commandBytes, setId, min, max, skip, take, withScores); - return SendExpectMultiData(); + return SendExpectMultiData(args); } private static byte[][] GetRangeByScoreArgs(byte[] commandBytes, From d3b7d68dfcd90048f2682476553edfa1aea88c38 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Sun, 13 Sep 2020 21:31:55 +0800 Subject: [PATCH 016/107] Update ServiceStack.Redis.Source.csproj --- .../ServiceStack.Redis.Source.csproj | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj index e9780a5f..ac2a4be5 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj @@ -2,7 +2,7 @@ ServiceStack.Redis ServiceStack.Redis - net45;netstandard2.0 + net45;net472;netstandard2.0;netstandard2.1 C# Redis client for the Redis NoSQL DB C# Redis Client for the worlds fastest distributed NoSQL datastore. @@ -11,11 +11,25 @@ Redis;NoSQL;Client;Distributed;Cache;PubSub;Messaging;Transactions + + + $(DefineConstants);ASYNC_MEMORY + + + + + + + + + + + @@ -24,6 +38,9 @@ + + + \ No newline at end of file From 65646a3152f16cc3a6e0f5cbc5b7a9b904242a18 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 28 Oct 2020 04:45:32 +0800 Subject: [PATCH 017/107] fix typo --- src/ServiceStack.Redis/PooledRedisClientManager.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.cs b/src/ServiceStack.Redis/PooledRedisClientManager.cs index 9635b4b6..81fc0fa1 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.cs @@ -104,8 +104,8 @@ public PooledRedisClientManager( public PooledRedisClientManager( IEnumerable readWriteHosts, IEnumerable readOnlyHosts, - long initalDb) - : this(readWriteHosts, readOnlyHosts, null, initalDb, null, null) + long initialDb) + : this(readWriteHosts, readOnlyHosts, null, initialDb, null, null) { } @@ -113,13 +113,13 @@ public PooledRedisClientManager( IEnumerable readWriteHosts, IEnumerable readOnlyHosts, RedisClientManagerConfig config, - long? initalDb, + long? initialDb, int? poolSizeMultiplier, int? poolTimeOutSeconds) { this.Db = config != null - ? config.DefaultDb ?? initalDb - : initalDb; + ? config.DefaultDb ?? initialDb + : initialDb; var masters = (readWriteHosts ?? TypeConstants.EmptyStringArray).ToArray(); var replicas = (readOnlyHosts ?? TypeConstants.EmptyStringArray).ToArray(); From 8e4c2f53e1374d0cc31db423e02e921bc163b68b Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 28 Oct 2020 04:47:07 +0800 Subject: [PATCH 018/107] Implement RetryReconnectOnFailedMasters & enable by default --- src/ServiceStack.Redis/RedisConfig.cs | 7 ++ src/ServiceStack.Redis/RedisManagerPool.cs | 8 +- src/ServiceStack.Redis/RedisResolver.cs | 99 ++++++++++++++-------- 3 files changed, 74 insertions(+), 40 deletions(-) diff --git a/src/ServiceStack.Redis/RedisConfig.cs b/src/ServiceStack.Redis/RedisConfig.cs index d06e4e18..2105cafe 100644 --- a/src/ServiceStack.Redis/RedisConfig.cs +++ b/src/ServiceStack.Redis/RedisConfig.cs @@ -72,6 +72,12 @@ public class RedisConfig /// public static bool VerifyMasterConnections = true; + /// + /// Whether to retry re-connecting on same connection if not a master instance (default true) + /// For Managed Services (e.g. AWS ElastiCache) which eventually restores master instances on same host + /// + public static bool RetryReconnectOnFailedMasters = true; + /// /// The ConnectTimeout on clients used to find the next available host (default 200ms) /// @@ -126,6 +132,7 @@ public static void Reset() BackOffMultiplier = 10; BufferPoolMaxSize = 500000; VerifyMasterConnections = true; + RetryReconnectOnFailedMasters = true; HostLookupTimeoutMs = 200; AssumeServerVersion = null; DeactivatedClientsExpiry = TimeSpan.Zero; diff --git a/src/ServiceStack.Redis/RedisManagerPool.cs b/src/ServiceStack.Redis/RedisManagerPool.cs index 43141801..c71f52b2 100644 --- a/src/ServiceStack.Redis/RedisManagerPool.cs +++ b/src/ServiceStack.Redis/RedisManagerPool.cs @@ -34,7 +34,7 @@ public RedisPoolConfig() } /// - /// Provides thread-safe pooling of redis client connections. All connections are treaded as read and write hosts. + /// Provides thread-safe pooling of redis client connections. All connections are treated as read and write hosts. /// public partial class RedisManagerPool : IRedisClientsManager, IRedisFailover, IHandleClientDispose, IHasRedisResolver, IRedisClientCacheManager @@ -178,8 +178,8 @@ private RedisClient GetClient(bool forAsync) lock (clients) { //Create new client outside of pool when max pool size exceeded - //Reverting free-slot not needed when -1 since slwo wasn't reserved or - //when existingClient changed (failover) since no longer reserver + //Reverting free-slot not needed when -1 since slow wasn't reserved or + //when existingClient changed (failover) since no longer reserved var stillReserved = inactivePoolIndex >= 0 && inactivePoolIndex < clients.Length && clients[inactivePoolIndex] == existingClient; if (inactivePoolIndex == -1 || !stillReserved) @@ -246,7 +246,7 @@ public override void Dispose() { } private int GetInActiveClient(out RedisClient inactiveClient) { //this will loop through all hosts in readClients once even though there are 2 for loops - //both loops are used to try to get the prefered host according to the round robin algorithm + //both loops are used to try to get the preferred host according to the round robin algorithm var readWriteTotal = RedisResolver.ReadWriteHostsCount; var desiredIndex = poolIndex % clients.Length; for (int x = 0; x < readWriteTotal; x++) diff --git a/src/ServiceStack.Redis/RedisResolver.cs b/src/ServiceStack.Redis/RedisResolver.cs index b74a1b5d..c1df74a6 100644 --- a/src/ServiceStack.Redis/RedisResolver.cs +++ b/src/ServiceStack.Redis/RedisResolver.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using ServiceStack.Logging; @@ -77,51 +78,77 @@ public virtual RedisClient CreateRedisClient(RedisEndpoint config, bool master) if (master && RedisConfig.VerifyMasterConnections) { - var role = client.GetServerRole(); - if (role != RedisServerRole.Master) + var firstAttempt = DateTime.UtcNow; + Exception firstEx = null; + var retryTimeSpan = TimeSpan.FromMilliseconds(config.RetryTimeout); + var i = 0; + while (DateTime.UtcNow - firstAttempt < retryTimeSpan) { - Interlocked.Increment(ref RedisState.TotalInvalidMasters); - log.Error("Redis Master Host '{0}' is {1}. Resetting allHosts...".Fmt(config.GetHostString(), role)); - var newMasters = new List(); - var newReplicas = new List(); - RedisClient masterClient = null; - foreach (var hostConfig in allHosts) + try { - try - { - var testClient = ClientFactory(hostConfig); - testClient.ConnectTimeout = RedisConfig.HostLookupTimeoutMs; - var testRole = testClient.GetServerRole(); - switch (testRole) - { - case RedisServerRole.Master: - newMasters.Add(hostConfig); - if (masterClient == null) - masterClient = testClient; - break; - case RedisServerRole.Slave: - newReplicas.Add(hostConfig); - break; - } - - } - catch { /* skip */ } + client = GetValidMaster(client, config); + return client; + } + catch (Exception ex) + { + if (!RedisConfig.RetryReconnectOnFailedMasters) + throw; + + firstEx ??= ex; + ExecUtils.SleepBackOffMultiplier(++i); } + } + throw new TimeoutException($"Could not resolve master instance within {config.RetryTimeout}ms RetryTimeout", firstEx); + } + + return client; + } - if (masterClient == null) + protected RedisClient GetValidMaster(RedisClient client, RedisEndpoint config) + { + var role = client.GetServerRole(); + if (role != RedisServerRole.Master) + { + Interlocked.Increment(ref RedisState.TotalInvalidMasters); + log.Error("Redis Master Host '{0}' is {1}. Resetting allHosts...".Fmt(config.GetHostString(), role)); + var newMasters = new List(); + var newReplicas = new List(); + RedisClient masterClient = null; + foreach (var hostConfig in allHosts) + { + try { - Interlocked.Increment(ref RedisState.TotalNoMastersFound); - var errorMsg = "No master found in: " + string.Join(", ", allHosts.Map(x => x.GetHostString())); - log.Error(errorMsg); - throw new Exception(errorMsg); + var testClient = ClientFactory(hostConfig); + testClient.ConnectTimeout = RedisConfig.HostLookupTimeoutMs; + var testRole = testClient.GetServerRole(); + switch (testRole) + { + case RedisServerRole.Master: + newMasters.Add(hostConfig); + if (masterClient == null) + masterClient = testClient; + break; + case RedisServerRole.Slave: + newReplicas.Add(hostConfig); + break; + } + } + catch { /* skip */ } + } - ResetMasters(newMasters); - ResetSlaves(newReplicas); - return masterClient; + if (masterClient == null) + { + Interlocked.Increment(ref RedisState.TotalNoMastersFound); + var errorMsg = "No master found in: " + string.Join(", ", allHosts.Map(x => x.GetHostString())); + log.Error(errorMsg); + throw new InvalidDataException(errorMsg); } - } + ResetMasters(newMasters); + ResetSlaves(newReplicas); + return masterClient; + } return client; } From e4a5b8709b128743bb5b0744bff864c3398b6892 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 28 Oct 2020 21:07:45 +0800 Subject: [PATCH 019/107] Resolve new client when attempting to resolve Master client. --- src/ServiceStack.Redis/RedisResolver.cs | 2 ++ .../ServiceStack.Redis.Tests/RedisPasswordTests.cs | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/ServiceStack.Redis/RedisResolver.cs b/src/ServiceStack.Redis/RedisResolver.cs index c1df74a6..a49e0f01 100644 --- a/src/ServiceStack.Redis/RedisResolver.cs +++ b/src/ServiceStack.Redis/RedisResolver.cs @@ -96,6 +96,8 @@ public virtual RedisClient CreateRedisClient(RedisEndpoint config, bool master) firstEx ??= ex; ExecUtils.SleepBackOffMultiplier(++i); + client?.Dispose(); + client = ClientFactory(config); } } throw new TimeoutException($"Could not resolve master instance within {config.RetryTimeout}ms RetryTimeout", firstEx); diff --git a/tests/ServiceStack.Redis.Tests/RedisPasswordTests.cs b/tests/ServiceStack.Redis.Tests/RedisPasswordTests.cs index fd928f8c..db10e714 100644 --- a/tests/ServiceStack.Redis.Tests/RedisPasswordTests.cs +++ b/tests/ServiceStack.Redis.Tests/RedisPasswordTests.cs @@ -1,4 +1,5 @@ -using NUnit.Framework; +using System; +using NUnit.Framework; namespace ServiceStack.Redis.Tests { @@ -29,8 +30,9 @@ public void Passwords_are_not_leaked_in_exception_messages() Assert.Throws(() => { try { - var factory = new PooledRedisClientManager(password + "@" + - TestConfig.SingleHost); // redis will throw when using password and it's not configured + var connString = password + "@" + TestConfig.SingleHost + "?RetryTimeout=2000"; + // redis will throw when using password and it's not configured + var factory = new PooledRedisClientManager(connString); using var redis = factory.GetClient(); redis.SetValue("Foo", "Bar"); } @@ -39,6 +41,11 @@ public void Passwords_are_not_leaked_in_exception_messages() Assert.That(ex.Message, Is.Not.Contains(password)); throw; } + catch (TimeoutException tex) + { + Assert.That(tex.InnerException.Message, Is.Not.Contains(password)); + throw tex.InnerException; + } }, "Expected an exception after Redis AUTH command; try using a password that doesn't match."); } From 607a81300cda1f7844acc2908a6141e5c8a3442d Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Thu, 29 Oct 2020 21:38:04 +0800 Subject: [PATCH 020/107] Update RedisPubSubServerTests.cs --- .../RedisPubSubServerTests.cs | 56 +++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/tests/ServiceStack.Redis.Tests/RedisPubSubServerTests.cs b/tests/ServiceStack.Redis.Tests/RedisPubSubServerTests.cs index 2b51c6ea..4984228c 100644 --- a/tests/ServiceStack.Redis.Tests/RedisPubSubServerTests.cs +++ b/tests/ServiceStack.Redis.Tests/RedisPubSubServerTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using NUnit.Framework; using ServiceStack.Text; @@ -9,16 +10,26 @@ namespace ServiceStack.Redis.Tests [TestFixture] public class RedisPubSubServerTests { - private static RedisPubSubServer CreatePubSubServer( - int intervalSecs = 1, int timeoutSecs = 3) + RedisManagerPool clientsManager = new RedisManagerPool(TestConfig.MasterHosts); + + [OneTimeTearDown] + public void OneTimeTearDown() + { + clientsManager.Dispose(); + } + + private RedisPubSubServer CreatePubSubServer( + int intervalSecs = 1, int timeoutSecs = 3, params string[] channels) { - var clientsManager = new RedisManagerPool(TestConfig.MasterHosts); using (var redis = clientsManager.GetClient()) redis.FlushAll(); + + if (channels.Length == 0) + channels = new[] {"topic:test"}; var pubSub = new RedisPubSubServer( clientsManager, - "topic:test") + channels) { HeartbeatInterval = TimeSpan.FromSeconds(intervalSecs), HeartbeatTimeout = TimeSpan.FromSeconds(timeoutSecs) @@ -97,5 +108,42 @@ public void Does_send_heartbeat_pulses_to_multiple_PubSubServers() pubSubs.Each(x => x.Dispose()); } + + [Test] + public void Can_restart_and_subscribe_to_more_channels() + { + var a = new List(); + var b = new List(); + var pubSub = CreatePubSubServer(intervalSecs: 20, timeoutSecs: 30, "topic:a"); + pubSub.OnMessage = (channel, msg) => { + if (channel == "topic:a") + a.Add(msg); + else if (channel == "topic:b") + b.Add(msg); + }; + pubSub.Start(); + Thread.Sleep(100); + + var client = clientsManager.GetClient(); + var i = 0; + client.PublishMessage("topic:a", $"msg: ${++i}"); + client.PublishMessage("topic:b", $"msg: ${++i}"); + + Thread.Sleep(100); + Assert.That(a.Count, Is.EqualTo(1)); + Assert.That(b.Count, Is.EqualTo(0)); + + pubSub.Channels = new[] {"topic:a", "topic:b"}; + pubSub.Restart(); + Thread.Sleep(100); + + client.PublishMessage("topic:a", $"msg: ${++i}"); + client.PublishMessage("topic:b", $"msg: ${++i}"); + + + Thread.Sleep(100); + Assert.That(a.Count, Is.EqualTo(2)); + Assert.That(b.Count, Is.EqualTo(1)); + } } } \ No newline at end of file From dd1249214c64e506348e92f54e2fa5b33756a7c9 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Thu, 29 Oct 2020 23:16:58 +0800 Subject: [PATCH 021/107] Change DefaultPoolSizeMultiplier to 50 --- src/ServiceStack.Redis/PooledRedisClientManager.cs | 2 +- src/ServiceStack.Redis/RedisConfig.cs | 5 +++++ src/ServiceStack.Redis/RedisManagerPool.cs | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.cs b/src/ServiceStack.Redis/PooledRedisClientManager.cs index 81fc0fa1..74bd92c2 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.cs @@ -126,7 +126,7 @@ public PooledRedisClientManager( RedisResolver = new RedisResolver(masters, replicas); - this.PoolSizeMultiplier = poolSizeMultiplier ?? 20; + this.PoolSizeMultiplier = poolSizeMultiplier ?? RedisConfig.DefaultPoolSizeMultiplier; this.Config = config ?? new RedisClientManagerConfig { diff --git a/src/ServiceStack.Redis/RedisConfig.cs b/src/ServiceStack.Redis/RedisConfig.cs index 2105cafe..cad76d9c 100644 --- a/src/ServiceStack.Redis/RedisConfig.cs +++ b/src/ServiceStack.Redis/RedisConfig.cs @@ -52,6 +52,11 @@ public class RedisConfig /// public static int? DefaultMaxPoolSize; + /// + /// The default pool size multiplier if no pool size is specified (default 50) + /// + public static int DefaultPoolSizeMultiplier = 50; + /// /// The BackOff multiplier failed Auto Retries starts from (default 10ms) /// diff --git a/src/ServiceStack.Redis/RedisManagerPool.cs b/src/ServiceStack.Redis/RedisManagerPool.cs index c71f52b2..18b1f1c3 100644 --- a/src/ServiceStack.Redis/RedisManagerPool.cs +++ b/src/ServiceStack.Redis/RedisManagerPool.cs @@ -28,7 +28,7 @@ public RedisPoolConfig() } /// - /// Maximum ammount of s created by the . + /// Maximum amount of s created by the . /// public int MaxPoolSize { get; set; } } From 0c3947687c01b6283a94da1094116f38d5f928f6 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Thu, 29 Oct 2020 23:17:10 +0800 Subject: [PATCH 022/107] Rider clean up pub/sub server --- src/ServiceStack.Redis/RedisPubSubServer.cs | 178 ++++++++++---------- 1 file changed, 86 insertions(+), 92 deletions(-) diff --git a/src/ServiceStack.Redis/RedisPubSubServer.cs b/src/ServiceStack.Redis/RedisPubSubServer.cs index 728d4421..7db29bac 100644 --- a/src/ServiceStack.Redis/RedisPubSubServer.cs +++ b/src/ServiceStack.Redis/RedisPubSubServer.cs @@ -164,7 +164,7 @@ private void Init() if (HeartbeatInterval != null) { heartbeatTimer = new Timer(SendHeartbeat, null, - TimeSpan.FromMilliseconds(0), HeartbeatInterval.Value); + TimeSpan.FromMilliseconds(0), HeartbeatInterval.GetValueOrDefault()); } Interlocked.CompareExchange(ref lastHeartbeatTicks, DateTime.UtcNow.Ticks, lastHeartbeatTicks); @@ -178,7 +178,7 @@ void SendHeartbeat(object state) if (currentStatus != Status.Started) return; - if (DateTime.UtcNow - new DateTime(lastHeartbeatTicks) < HeartbeatInterval.Value) + if (DateTime.UtcNow - new DateTime(lastHeartbeatTicks) < HeartbeatInterval.GetValueOrDefault()) return; OnHeartbeatSent?.Invoke(); @@ -229,97 +229,95 @@ private void RunLoop() //RESET while (Interlocked.CompareExchange(ref status, 0, 0) == Status.Started) { - using (var redis = ClientsManager.GetReadOnlyClient()) - { - masterClient = redis; + using var redis = ClientsManager.GetReadOnlyClient(); + masterClient = redis; - //Record that we had a good run... - Interlocked.CompareExchange(ref noOfContinuousErrors, 0, noOfContinuousErrors); + //Record that we had a good run... + Interlocked.CompareExchange(ref noOfContinuousErrors, 0, noOfContinuousErrors); - using (var subscription = redis.CreateSubscription()) - { - subscription.OnUnSubscribe = HandleUnSubscribe; + using var subscription = redis.CreateSubscription(); + subscription.OnUnSubscribe = HandleUnSubscribe; - if (OnMessageBytes != null) - { - bool IsCtrlMessage(byte[] msg) - { - if (msg.Length < 4) - return false; - return msg[0] == 'C' && msg[1] == 'T' && msg[0] == 'R' && msg[0] == 'L'; - } + if (OnMessageBytes != null) + { + bool IsCtrlMessage(byte[] msg) + { + if (msg.Length < 4) + return false; + return msg[0] == 'C' && msg[1] == 'T' && msg[0] == 'R' && msg[0] == 'L'; + } - ((RedisSubscription)subscription).OnMessageBytes = (channel, msg) => { - if (IsCtrlMessage(msg)) - return; + ((RedisSubscription)subscription).OnMessageBytes = (channel, msg) => { + if (IsCtrlMessage(msg)) + return; - OnMessageBytes(channel, msg); - }; - } + OnMessageBytes(channel, msg); + }; + } - subscription.OnMessage = (channel, msg) => - { - if (string.IsNullOrEmpty(msg)) - return; + subscription.OnMessage = (channel, msg) => + { + if (string.IsNullOrEmpty(msg)) + return; - var ctrlMsg = msg.LeftPart(':'); - if (ctrlMsg == ControlCommand.Control) - { - var op = Interlocked.CompareExchange(ref doOperation, Operation.NoOp, doOperation); + var ctrlMsg = msg.LeftPart(':'); + if (ctrlMsg == ControlCommand.Control) + { + var op = Interlocked.CompareExchange(ref doOperation, Operation.NoOp, doOperation); - var msgType = msg.IndexOf(':') >= 0 - ? msg.RightPart(':') - : null; + var msgType = msg.IndexOf(':') >= 0 + ? msg.RightPart(':') + : null; - OnControlCommand?.Invoke(msgType ?? Operation.GetName(op)); + OnControlCommand?.Invoke(msgType ?? Operation.GetName(op)); - switch (op) + switch (op) + { + case Operation.Stop: + if (Log.IsDebugEnabled) + Log.Debug("Stop Command Issued"); + + Interlocked.CompareExchange(ref status, Status.Stopping, Status.Started); + try { - case Operation.Stop: - if (Log.IsDebugEnabled) - Log.Debug("Stop Command Issued"); - - Interlocked.CompareExchange(ref status, Status.Stopping, Status.Started); - try - { - if (Log.IsDebugEnabled) - Log.Debug("UnSubscribe From All Channels..."); - - subscription.UnSubscribeFromAllChannels(); //Un block thread. - } - finally - { - Interlocked.CompareExchange(ref status, Status.Stopped, Status.Stopping); - } - return; - - case Operation.Reset: - subscription.UnSubscribeFromAllChannels(); //Un block thread. - return; - } + if (Log.IsDebugEnabled) + Log.Debug("UnSubscribe From All Channels..."); - switch (msgType) + // ReSharper disable once AccessToDisposedClosure + subscription.UnSubscribeFromAllChannels(); //Un block thread. + } + finally { - case ControlCommand.Pulse: - Pulse(); - break; + Interlocked.CompareExchange(ref status, Status.Stopped, Status.Stopping); } - } - else - { - OnMessage(channel, msg); - } - }; - - //blocks thread - if (ChannelsMatching != null && ChannelsMatching.Length > 0) - subscription.SubscribeToChannelsMatching(ChannelsMatching); - else - subscription.SubscribeToChannels(Channels); - - masterClient = null; + return; + + case Operation.Reset: + // ReSharper disable once AccessToDisposedClosure + subscription.UnSubscribeFromAllChannels(); //Un block thread. + return; + } + + switch (msgType) + { + case ControlCommand.Pulse: + Pulse(); + break; + } } - } + else + { + OnMessage(channel, msg); + } + }; + + //blocks thread + if (ChannelsMatching != null && ChannelsMatching.Length > 0) + subscription.SubscribeToChannelsMatching(ChannelsMatching); + else + subscription.SubscribeToChannels(Channels); + + masterClient = null; } OnStop?.Invoke(); @@ -363,7 +361,7 @@ private void Stop(bool shouldRestart) if (Log.IsDebugEnabled) Log.Debug("Stopping RedisPubSubServer..."); - //Unblock current bgthread by issuing StopCommand + //Unblock current bg thread by issuing StopCommand SendControlCommand(Operation.Stop); } } @@ -382,12 +380,10 @@ private void NotifyAllSubscribers(string commandType=null) try { - using (var redis = ClientsManager.GetClient()) + using var redis = ClientsManager.GetClient(); + foreach (var channel in Channels) { - foreach (var channel in Channels) - { - redis.PublishMessage(channel, msg); - } + redis.PublishMessage(channel, msg); } } catch (Exception ex) @@ -406,13 +402,11 @@ private void HandleFailover(IRedisClientsManager clientsManager) if (masterClient != null) { //New thread-safe client with same connection info as connected master - using (var currentlySubscribedClient = ((RedisClient)masterClient).CloneClient()) + using var currentlySubscribedClient = ((RedisClient)masterClient).CloneClient(); + Interlocked.CompareExchange(ref doOperation, Operation.Reset, doOperation); + foreach (var channel in Channels) { - Interlocked.CompareExchange(ref doOperation, Operation.Reset, doOperation); - foreach (var channel in Channels) - { - currentlySubscribedClient.PublishMessage(channel, ControlCommand.Control); - } + currentlySubscribedClient.PublishMessage(channel, ControlCommand.Control); } } else @@ -466,12 +460,12 @@ private void KillBgThreadIfExists() private void SleepBackOffMultiplier(int continuousErrorsCount) { if (continuousErrorsCount == 0) return; - const int MaxSleepMs = 60 * 1000; + const int maxSleepMs = 60 * 1000; //exponential/random retry back-off. var nextTry = Math.Min( rand.Next((int)Math.Pow(continuousErrorsCount, 3), (int)Math.Pow(continuousErrorsCount + 1, 3) + 1), - MaxSleepMs); + maxSleepMs); if (Log.IsDebugEnabled) Log.Debug("Sleeping for {0}ms after {1} continuous errors".Fmt(nextTry, continuousErrorsCount)); From b67c7723202f71927c17dff2590beaf79a457875 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 6 Nov 2020 19:13:10 +0800 Subject: [PATCH 023/107] clean up --- .../BasicRedisClientManager.Async.cs | 2 +- .../BasicRedisClientManager.ICacheClient.cs | 117 ++++++------------ 2 files changed, 38 insertions(+), 81 deletions(-) diff --git a/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs b/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs index 0883ae3b..4bb20247 100644 --- a/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs +++ b/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs @@ -57,7 +57,7 @@ ValueTask IAsyncDisposable.DisposeAsync() async Task ICacheClientAsync.GetAsync(string key, CancellationToken token) { await using var client = await GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); - return await client.GetAsync(key).ConfigureAwait(false); + return await client.GetAsync(key, token).ConfigureAwait(false); } async Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken token) diff --git a/src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs b/src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs index f38853c0..1409d8a3 100644 --- a/src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs +++ b/src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs @@ -30,152 +30,109 @@ namespace ServiceStack.Redis public partial class BasicRedisClientManager : ICacheClient { - public ICacheClient GetCacheClient() - { - return new RedisClientManagerCacheClient(this); - } + public ICacheClient GetCacheClient() => + new RedisClientManagerCacheClient(this); - public ICacheClient GetReadOnlyCacheClient() - { - return ConfigureRedisClient(this.GetReadOnlyClientImpl()); - } + public ICacheClient GetReadOnlyCacheClient() => + ConfigureRedisClient(this.GetReadOnlyClientImpl()); - private ICacheClient ConfigureRedisClient(IRedisClient client) - { - return client; - } - - #region Implementation of ICacheClient + private ICacheClient ConfigureRedisClient(IRedisClient client) => client; public bool Remove(string key) { - using (var client = GetReadOnlyCacheClient()) - { - return client.Remove(key); - } + using var client = GetReadOnlyCacheClient(); + return client.Remove(key); } public void RemoveAll(IEnumerable keys) { - using (var client = GetCacheClient()) - { - client.RemoveAll(keys); - } + using var client = GetCacheClient(); + client.RemoveAll(keys); } public T Get(string key) { - using (var client = GetReadOnlyCacheClient()) - { - return client.Get(key); - } + using var client = GetReadOnlyCacheClient(); + return client.Get(key); } public long Increment(string key, uint amount) { - using (var client = GetCacheClient()) - { - return client.Increment(key, amount); - } + using var client = GetCacheClient(); + return client.Increment(key, amount); } public long Decrement(string key, uint amount) { - using (var client = GetCacheClient()) - { - return client.Decrement(key, amount); - } + using var client = GetCacheClient(); + return client.Decrement(key, amount); } public bool Add(string key, T value) { - using (var client = GetCacheClient()) - { - return client.Add(key, value); - } + using var client = GetCacheClient(); + return client.Add(key, value); } public bool Set(string key, T value) { - using (var client = GetCacheClient()) - { - return client.Set(key, value); - } + using var client = GetCacheClient(); + return client.Set(key, value); } public bool Replace(string key, T value) { - using (var client = GetCacheClient()) - { - return client.Replace(key, value); - } + using var client = GetCacheClient(); + return client.Replace(key, value); } public bool Add(string key, T value, DateTime expiresAt) { - using (var client = GetCacheClient()) - { - return client.Add(key, value, expiresAt); - } + using var client = GetCacheClient(); + return client.Add(key, value, expiresAt); } public bool Set(string key, T value, DateTime expiresAt) { - using (var client = GetCacheClient()) - { - return client.Set(key, value, expiresAt); - } + using var client = GetCacheClient(); + return client.Set(key, value, expiresAt); } public bool Replace(string key, T value, DateTime expiresAt) { - using (var client = GetCacheClient()) - { - return client.Replace(key, value, expiresAt); - } + using var client = GetCacheClient(); + return client.Replace(key, value, expiresAt); } public bool Add(string key, T value, TimeSpan expiresIn) { - using (var client = GetCacheClient()) - { - return client.Add(key, value, expiresIn); - } + using var client = GetCacheClient(); + return client.Add(key, value, expiresIn); } public bool Set(string key, T value, TimeSpan expiresIn) { - using (var client = GetCacheClient()) - { - return client.Set(key, value, expiresIn); - } + using var client = GetCacheClient(); + return client.Set(key, value, expiresIn); } public bool Replace(string key, T value, TimeSpan expiresIn) { - using (var client = GetCacheClient()) - { - return client.Replace(key, value, expiresIn); - } + using var client = GetCacheClient(); + return client.Replace(key, value, expiresIn); } public void FlushAll() { - using (var client = GetCacheClient()) - { - client.FlushAll(); - } + using var client = GetCacheClient(); + client.FlushAll(); } public IDictionary GetAll(IEnumerable keys) { - using (var client = GetReadOnlyCacheClient()) - { - return client.GetAll(keys); - } + using var client = GetReadOnlyCacheClient(); + return client.GetAll(keys); } - - #endregion } From 139e0bc3366ae1508dcefa1f1130278b1852420d Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 10 Nov 2020 23:31:47 +0800 Subject: [PATCH 024/107] Set deps to v5.10.1 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 058f372d..a3fd2dfe 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.9.3 + 5.10.1 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index fcf9af62..d6802681 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.9.3 + 5.10.1 latest false From f30950830c7aa9b9ce5a2b1281f909e4215e7948 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 13 Nov 2020 11:11:20 +0800 Subject: [PATCH 025/107] Update README.md --- README.md | 432 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 283 insertions(+), 149 deletions(-) diff --git a/README.md b/README.md index ebdf8f15..3a805493 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,13 @@ Follow [@ServiceStack](https://twitter.com/servicestack) or [view the docs](http There are a number of different APIs available with the `RedisClient` implementing the following interfaces: - * [ICacheClient](http://docs.servicestack.net/caching) - If you are using Redis solely as a cache, you should bind to the ServiceStack's common interface as there already are In-Memory an Memcached implementations available in ServiceStack, allowing you to easily switch providers - * [IRedisNativeClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisNativeClient.cs) - For those wanting a low-level raw byte access (where you can control your own serialization/deserialization) that map 1:1 with Redis operations of the same name. + * [Caching Provider](http://docs.servicestack.net/caching) - If you are using Redis solely as a cache, you should bind to the ServiceStack's common interface as there already are In-Memory an Memcached implementations available in ServiceStack, allowing you to easily switch providers + * [IRedisNativeClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisNativeClient.cs) / [Async](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisNativeClientAsync.cs) - For those wanting a low-level raw byte access (where you can control your own serialization/deserialization) that map 1:1 with Redis operations of the same name. For most cases if you require access to Redis specific functionality you would want to bind to the interface below: - * [IRedisClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClient.cs) - Provides a friendlier, more descriptive API that lets you store values as strings (UTF8 encoding). - * [IRedisTypedClient](https://github.com/ServiceStack/ServiceStack/tree/master/src/ServiceStack.Interfaces/Redis/Generic) - created with `IRedisClient.As()` - it returns a 'strongly-typed client' that provides a typed-interface for all redis value operations that works against any C#/.NET POCO type. + * [IRedisClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClient.cs) / [Async](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClientAsync.cs) - Provides a friendlier, more descriptive API that lets you store values as strings (UTF8 encoding). + * [Redis generic client APIs](https://github.com/ServiceStack/ServiceStack/tree/master/src/ServiceStack.Interfaces/Redis/Generic) - created with `redis.As()` - returns a 'strongly-typed client' that provides a typed-interface for all redis value operations that works against any C#/.NET POCO type. The interfaces works cleanly with any IOC and allows your app logic to bind to implementation-free interfaces which can easily be mocked and substituted. @@ -24,7 +24,7 @@ With each client providing different layers of abstraction: * The RedisNativeClient exposes raw `byte[]` apis and does no marshalling and passes all values directly to redis. * The RedisClient assumes `string` values and simply converts strings to UTF8 bytes before sending to Redis - * The RedisTypedClient provides a generic interface allowing you to add POCO values. The POCO types are serialized using [.NETs fastest JSON Serializer](https://github.com/ServiceStackV3/mythz_blog/blob/master/pages/344.md) which is then converted to UTF8 bytes and sent to Redis. + * The RedisTypedClient provides a generic interface allowing you to add POCO values. POCOs are serialized using [ServiceStack.Text](https://github.com/ServiceStack/ServiceStack.Text) which is then converted to UTF8 bytes and sent to Redis. ### API Overview @@ -170,34 +170,157 @@ Once registered, accessing the RedisClient is the same in all Client Managers, e ```csharp var clientsManager = container.Resolve(); -using (IRedisClient redis = clientsManager.GetClient()) -{ - redis.IncrementValue("counter"); - List days = redis.GetAllItemsFromList("days"); +using var redis = clientsManager.GetClient(); - //Access Typed API - var redisTodos = redis.As(); +redis.IncrementValue("counter"); +List days = redis.GetAllItemsFromList("days"); - redisTodos.Store(new Todo { - Id = redisTodos.GetNextSequence(), - Content = "Learn Redis", - }); +//Access Typed API +var redisTodos = redis.As(); - var todo = redisTodos.GetById(1); +redisTodos.Store(new Todo { + Id = redisTodos.GetNextSequence(), + Content = "Learn Redis", +}); - //Access Native Client - var redisNative = (IRedisNativeClient)redis; +var todo = redisTodos.GetById(1); - redisNative.Incr("counter"); - List days = redisNative.LRange("days", 0, -1); -} +//Access Native Client +var redisNative = (IRedisNativeClient)redis; + +redisNative.Incr("counter"); +List days = redisNative.LRange("days", 0, -1); ``` A more detailed list of the available RedisClient APIs used in the example can be seen in the C# interfaces below: + - [IRedisClientsManager](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClientsManager.cs) - [IRedisClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClient.cs) - - [IRedisTypedClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisTypedClient.cs) - [IRedisNativeClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisNativeClient.cs) + - [IRedisSubscription](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisSubscription.cs) + +#### Pipeline & Transaction APIs + + - [IRedisTransaction](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisTransaction.cs) + - [IRedisPipelineShared](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Pipeline/IRedisPipelineShared.cs) + - [IRedisQueueableOperation](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Pipeline/IRedisQueueableOperation.cs) + - [IRedisQueueCompletableOperation](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Pipeline/IRedisQueueCompletableOperation.cs) + +#### Generic Client APIs + + - [IRedisTypedClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisTypedClient.cs) + - [IRedisHash](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisHash.Generic.cs) + - [IRedisList](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisList.Generic.cs) + - [IRedisSet](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisSet.Generic.cs) + - [IRedisSortedSet](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisSortedSet.Generic.cs) + - [IRedisTypedQueueableOperation](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisTypedQueueableOperation.cs) + +#### Server Collection APIs + + - [IRedisHash](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisHash.cs) + - [IRedisList](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisList.cs) + - [IRedisSet](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisSet.cs) + - [IRedisSortedSet](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisSortedSet.cs) + +### Async Redis + +Async support in ServiceStack.Redis is available to .NET Core (**.NET Standard 2.0**) or **.NET Framework v4.7.2+** projects where there's async API equivalents for most sync APIs as contained within the Async Redis interfaces below: + + - [IRedisClientsManagerAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClientsManagerAsync.cs) + - [IRedisClientAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClientAsync.cs) + - [IRedisNativeClientAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisNativeClientAsync.cs) + - [IRedisSubscriptionAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisSubscriptionAsync.cs) + +#### Async Pipeline & Transaction APIs + + - [IRedisTransactionAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisTransactionAsync.cs) + - [IRedisPipelineSharedAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Pipeline/IRedisPipelineSharedAsync.cs) + - [IRedisQueueableOperationAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Pipeline/IRedisQueueableOperationAsync.cs) + - [IRedisQueueCompletableOperationAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Pipeline/IRedisQueueCompletableOperationAsync.cs) + +#### Async Generic Client APIs + + - [IRedisTypedClientAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisTypedClientAsync.cs) + - [IRedisHashAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisHash.Generic.Async.cs) + - [IRedisListAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisList.Generic.Async.cs) + - [IRedisSetAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisSet.Generic.Async.cs) + - [IRedisSortedSetAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisSortedSet.Generic.Async.cs) + - [IRedisTypedTransactionAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisTypedTransactionAsync.cs) + - [IRedisTypedQueueableOperationAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisTypedQueueableOperationAsync.cs) + +#### Async Server Collection APIs + + - [IRedisHashAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisHashAsync.cs) + - [IRedisListAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisListAsync.cs) + - [IRedisSetAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisSetAsync.cs) + - [IRedisSortedSetAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisSortedSetAsync.cs) + + +### Redis Async Usage + +All Redis Client Managers implement both `IRedisClientsManager` and `IRedisClientsManagerAsync` so IOC registrations remain the same which can continue to register against the existing `IRedisClientsManager` interface, e.g: + +```csharp +container.Register(c => + new RedisManagerPool(redisConnectionString)); +``` + +Where it can be used to resolve both sync `IRedisClient` and async `IRedisClientAsync` clients, e.g: + +```csharp +using var syncRedis = container.Resolve().GetClient(); +await using var asyncRedis = await container.Resolve().GetClientAsync(); +``` + +The async support in ServiceStack.Redis is designed for optimal efficiency and uses `ValueTask` & other modern Async APIs. + +If you want to force async-only API usage could choose to just register `IRedisClientsManagerAsync` where it only lets you resolve async only `IRedisClientAsync` and `ICacheClientAsync` clients, e.g: + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + services.AddSingleton(c => new RedisManagerPool()); +} + +//... + +public class MyDep +{ + private IRedisClientsManagerAsync manager; + public MyDep(IRedisClientsManagerAsync manager) => this.manager = manager; + + public async Task Incr(string key, uint value) + { + await using var redis = await manager.GetClientAsync(); + return await redis.IncrementAsync(key, value); + } +} +``` + +### Usage in ServiceStack + +Inside ServiceStack Services & Controllers we recommend using `GetRedisAsync()` to resolve an `IRedisClientAsync`, e.g: + +```csharp +public class MyService : Service +{ + public async Task Any(MyRequest request) + { + await using var redis = await GetRedisAsync(); + await redis.IncrementAsync(nameof(MyRequest), 1); + } +} + +public class HomeController : ServiceStackController +{ + public async Task Index() + { + await using var redis = await GetRedisAsync(); + await redis.IncrementAsync(nameof(HomeController), 1); + } +} +``` + ## [Redis React Browser](https://servicestack.net/redis-react) @@ -307,10 +430,8 @@ Support for changing the Ssl Protocols used for encrypted SSL connections can be ```csharp var connString = $"redis://{Host}?ssl=true&sslprotocols=Tls12&password={Password.UrlEncode()}"; var redisManager = new RedisManagerPool(connString); -using (var client = redisManager.GetClient()) -{ - //... -} +using var client = redisManager.GetClient(); +//... ``` ### [Connecting to Azure Redis](http://docs.servicestack.net/ssl-redis-azure) @@ -558,6 +679,14 @@ public interface IRedisClient IEnumerable> ScanAllHashEntries(string hashId, string pattern = null, int pageSize = 1000); } +public interface IRedisClientAsync +{ + IAsyncEnumerable ScanAllKeysAsync(string pattern = null, int pageSize, CancellationToken ct); + IAsyncEnumerable ScanAllSetItemsAsync(string setId, string pattern = null, int pageSize, CancellationToken ct); + IAsyncEnumerable> ScanAllSortedSetItemsAsync(string setId, string pattern = null, int pageSize, ct); + IAsyncEnumerable> ScanAllHashEntriesAsync(string hashId, string pattern = null, int pageSize, ct); +} + //Low-level API public interface IRedisNativeClient { @@ -567,6 +696,14 @@ public interface IRedisNativeClient ScanResult ZScan(string setId, ulong cursor, int count = 10, string match = null); ScanResult HScan(string hashId, ulong cursor, int count = 10, string match = null); } + +public interface IRedisNativeClientAsync +{ + ValueTask ScanAsync(ulong cursor, int count = 10, string match = null, CancellationToken ct); + ValueTask SScanAsync(string setId, ulong cursor, int count = 10, string match = null, CancellationToken ct); + ValueTask ZScanAsync(string setId, ulong cursor, int count = 10, string match = null, CancellationToken ct); + ValueTask HScanAsync(string hashId, ulong cursor, int count = 10, string match = null, CancellationToken ct); +} ``` The `IRedisClient` provides a higher-level API that abstracts away the client cursor to expose a lazy Enumerable sequence to provide an optimal way to stream scanned results that integrates nicely with LINQ, e.g: @@ -621,7 +758,9 @@ The `ExecLua` API returns this complex LUA table response in the `Children` coll Another way to return complex data structures in a LUA operation is to serialize the result as JSON - return cjson.encode(results) +```lua +return cjson.encode(results) +``` Which you can access as raw JSON by parsing the response as a String with: @@ -707,7 +846,7 @@ public interface IRedisClient ### Usage Examples -Here's how you can implement a ZPOP in Lua to remove the items with the lowest rank from a sorted set: +Here's how you can implement a **ZPOP** in Lua to remove the items with the lowest rank from a sorted set: ```csharp var luaBody = @" @@ -724,7 +863,7 @@ var letters = Redis.ExecLuaAsList(luaBody, keys: new[] { "zalphabet" }, args: ne letters.PrintDump(); //[A, B, C] ``` -And how to implement ZREVPOP to remove items with the highest rank from a sorted set: +And how to implement **ZREVPOP** to remove items with the highest rank from a sorted set: ```csharp var luaBody = @" @@ -745,21 +884,21 @@ letters.PrintDump(); //[X, Y, Z] ### Other examples -Returning an int: +Returning an `int`: ```csharp int intVal = Redis.ExecLuaAsInt("return 123"); //123 int intVal = Redis.ExecLuaAsInt("return ARGV[1] + ARGV[2]", "10", "20"); //30 ``` -Returning an string: +Returning an `string`: ```csharp //Hello, Redis Lua! var strVal = Redis.ExecLuaAsString(@"return 'Hello, ' .. ARGV[1] .. '!'", "Redis Lua"); ``` -Returning a List of strings: +Returning a `List` of strings: ```csharp Enum.GetNames(typeof(DayOfWeek)).ToList() @@ -769,8 +908,7 @@ var daysOfWeek = Redis.ExecLuaAsList("return redis.call('LRANGE', 'DaysOfWeek', daysOfWeek.PrintDump(); //[Sunday, Monday, Tuesday, ...] ``` -More examples can be found in the [Redis Eval Lua tests](https://github.com/ServiceStack/ServiceStack.Redis/blob/master/tests/ServiceStack.Redis.Tests/RedisClientEvalTests.cs -) +More examples can be found in the [Redis Eval Lua tests](https://github.com/ServiceStack/ServiceStack.Redis/blob/master/tests/ServiceStack.Redis.Tests/RedisClientEvalTests.cs) ### Debugging Data Corruption Issues @@ -779,10 +917,8 @@ Typically this is a result of using `IRedisClient` field in a singleton instance uses Redis should retrieve the redis client within a using statement, e.g: ```csharp -using (var redis = redisManager.GetClient()) -{ - //.. -} +using var redis = redisManager.GetClient(); +//... ``` Unfortunately the call-site which returns the corrupted response or runtime Exception doesn't identify where else the Redis client instance was being used. @@ -846,76 +982,75 @@ Contains all the examples, tutorials and resources you need to get you up to spe Below is a simple example to give you a flavour of how easy it is to use some of Redis's advanced data structures - in this case Redis Lists: _Full source code of this example is [viewable online](https://github.com/ServiceStack/ServiceStack.Redis/blob/master/tests/ServiceStack.Redis.Tests/ShippersExample.cs)_ - using (var redisClient = new RedisClient()) - { - //Create a 'strongly-typed' API that makes all Redis Value operations to apply against Shippers - IRedisTypedClient redis = redisClient.As(); - - //Redis lists implement IList while Redis sets implement ICollection - var currentShippers = redis.Lists["urn:shippers:current"]; - var prospectiveShippers = redis.Lists["urn:shippers:prospective"]; - - currentShippers.Add( - new Shipper { - Id = redis.GetNextSequence(), - CompanyName = "Trains R Us", - DateCreated = DateTime.UtcNow, - ShipperType = ShipperType.Trains, - UniqueRef = Guid.NewGuid() - }); - - currentShippers.Add( - new Shipper { - Id = redis.GetNextSequence(), - CompanyName = "Planes R Us", - DateCreated = DateTime.UtcNow, - ShipperType = ShipperType.Planes, - UniqueRef = Guid.NewGuid() - }); - - var lameShipper = new Shipper { - Id = redis.GetNextSequence(), - CompanyName = "We do everything!", - DateCreated = DateTime.UtcNow, - ShipperType = ShipperType.All, - UniqueRef = Guid.NewGuid() - }; - - currentShippers.Add(lameShipper); - - Dump("ADDED 3 SHIPPERS:", currentShippers); - - currentShippers.Remove(lameShipper); - - Dump("REMOVED 1:", currentShippers); - - prospectiveShippers.Add( - new Shipper { - Id = redis.GetNextSequence(), - CompanyName = "Trucks R Us", - DateCreated = DateTime.UtcNow, - ShipperType = ShipperType.Automobiles, - UniqueRef = Guid.NewGuid() - }); - - Dump("ADDED A PROSPECTIVE SHIPPER:", prospectiveShippers); - - redis.PopAndPushBetweenLists(prospectiveShippers, currentShippers); - - Dump("CURRENT SHIPPERS AFTER POP n' PUSH:", currentShippers); - Dump("PROSPECTIVE SHIPPERS AFTER POP n' PUSH:", prospectiveShippers); - - var poppedShipper = redis.PopFromList(currentShippers); - Dump("POPPED a SHIPPER:", poppedShipper); - Dump("CURRENT SHIPPERS AFTER POP:", currentShippers); - - //reset sequence and delete all lists - redis.SetSequence(0); - redis.Remove(currentShippers, prospectiveShippers); - Dump("DELETING CURRENT AND PROSPECTIVE SHIPPERS:", currentShippers); - } +```csharp +using var redisClient = new RedisClient(); +//Create a 'strongly-typed' API that makes all Redis Value operations to apply against Shippers +IRedisTypedClient redis = redisClient.As(); + +//Redis lists implement IList while Redis sets implement ICollection +var currentShippers = redis.Lists["urn:shippers:current"]; +var prospectiveShippers = redis.Lists["urn:shippers:prospective"]; + +currentShippers.Add( + new Shipper { + Id = redis.GetNextSequence(), + CompanyName = "Trains R Us", + DateCreated = DateTime.UtcNow, + ShipperType = ShipperType.Trains, + UniqueRef = Guid.NewGuid() + }); + +currentShippers.Add( + new Shipper { + Id = redis.GetNextSequence(), + CompanyName = "Planes R Us", + DateCreated = DateTime.UtcNow, + ShipperType = ShipperType.Planes, + UniqueRef = Guid.NewGuid() + }); + +var lameShipper = new Shipper { + Id = redis.GetNextSequence(), + CompanyName = "We do everything!", + DateCreated = DateTime.UtcNow, + ShipperType = ShipperType.All, + UniqueRef = Guid.NewGuid() +}; + +currentShippers.Add(lameShipper); + +Dump("ADDED 3 SHIPPERS:", currentShippers); + +currentShippers.Remove(lameShipper); + +Dump("REMOVED 1:", currentShippers); + +prospectiveShippers.Add( + new Shipper { + Id = redis.GetNextSequence(), + CompanyName = "Trucks R Us", + DateCreated = DateTime.UtcNow, + ShipperType = ShipperType.Automobiles, + UniqueRef = Guid.NewGuid() + }); + +Dump("ADDED A PROSPECTIVE SHIPPER:", prospectiveShippers); + +redis.PopAndPushBetweenLists(prospectiveShippers, currentShippers); + +Dump("CURRENT SHIPPERS AFTER POP n' PUSH:", currentShippers); +Dump("PROSPECTIVE SHIPPERS AFTER POP n' PUSH:", prospectiveShippers); + +var poppedShipper = redis.PopFromList(currentShippers); +Dump("POPPED a SHIPPER:", poppedShipper); +Dump("CURRENT SHIPPERS AFTER POP:", currentShippers); + +//reset sequence and delete all lists +redis.SetSequence(0); +redis.Remove(currentShippers, prospectiveShippers); +Dump("DELETING CURRENT AND PROSPECTIVE SHIPPERS:", currentShippers); +``` - /* == EXAMPLE OUTPUT == ADDED 3 SHIPPERS: @@ -945,7 +1080,6 @@ _Full source code of this example is [viewable online](https://github.com/Servic Id:1,CompanyName:Trains R Us,ShipperType:Trains,DateCreated:2010-01-31T11:53:37.7169323Z,UniqueRef:d17c5db0415b44b2ac5da7b6ebd780f5 DELETING CURRENT AND PROSPECTIVE SHIPPERS: - */ More examples are available in the [RedisExamples Redis examples page] and in the comprehensive [test suite](https://github.com/ServiceStack/ServiceStack.Redis/tree/master/tests/ServiceStack.Redis.Tests) @@ -959,47 +1093,47 @@ below stores and gets the entire [Northwind database](http://code.google.com/p/s _(Running inside a VS.NET/R# unit test on a 3 year old iMac)_ - using (var client = new RedisClient()) - { - var before = DateTime.Now; - client.StoreAll(NorthwindData.Categories); - client.StoreAll(NorthwindData.Customers); - client.StoreAll(NorthwindData.Employees); - client.StoreAll(NorthwindData.Shippers); - client.StoreAll(NorthwindData.Orders); - client.StoreAll(NorthwindData.Products); - client.StoreAll(NorthwindData.OrderDetails); - client.StoreAll(NorthwindData.CustomerCustomerDemos); - client.StoreAll(NorthwindData.Regions); - client.StoreAll(NorthwindData.Territories); - client.StoreAll(NorthwindData.EmployeeTerritories); - - Console.WriteLine("Took {0}ms to store the entire Northwind database ({1} records)", - (DateTime.Now - before).TotalMilliseconds, totalRecords); - - - before = DateTime.Now; - var categories = client.GetAll(); - var customers = client.GetAll(); - var employees = client.GetAll(); - var shippers = client.GetAll(); - var orders = client.GetAll(); - var products = client.GetAll(); - var orderDetails = client.GetAll(); - var customerCustomerDemos = client.GetAll(); - var regions = client.GetAll(); - var territories = client.GetAll(); - var employeeTerritories = client.GetAll(); - - Console.WriteLine("Took {0}ms to get the entire Northwind database ({1} records)", - (DateTime.Now - before).TotalMilliseconds, totalRecords); - } - /* - == EXAMPLE OUTPUT == - - Took 1020.0583ms to store the entire Northwind database (3202 records) - Took 132.0076ms to get the entire Northwind database (3202 records) - */ +```csharp +using var client = new RedisClient(); + +var before = DateTime.Now; +client.StoreAll(NorthwindData.Categories); +client.StoreAll(NorthwindData.Customers); +client.StoreAll(NorthwindData.Employees); +client.StoreAll(NorthwindData.Shippers); +client.StoreAll(NorthwindData.Orders); +client.StoreAll(NorthwindData.Products); +client.StoreAll(NorthwindData.OrderDetails); +client.StoreAll(NorthwindData.CustomerCustomerDemos); +client.StoreAll(NorthwindData.Regions); +client.StoreAll(NorthwindData.Territories); +client.StoreAll(NorthwindData.EmployeeTerritories); + +Console.WriteLine("Took {0}ms to store the entire Northwind database ({1} records)", + (DateTime.Now - before).TotalMilliseconds, totalRecords); + +before = DateTime.Now; +var categories = client.GetAll(); +var customers = client.GetAll(); +var employees = client.GetAll(); +var shippers = client.GetAll(); +var orders = client.GetAll(); +var products = client.GetAll(); +var orderDetails = client.GetAll(); +var customerCustomerDemos = client.GetAll(); +var regions = client.GetAll(); +var territories = client.GetAll(); +var employeeTerritories = client.GetAll(); + +Console.WriteLine("Took {0}ms to get the entire Northwind database ({1} records)", + (DateTime.Now - before).TotalMilliseconds, totalRecords); +/* +== EXAMPLE OUTPUT == + +Took 1020.0583ms to store the entire Northwind database (3202 records) +Took 132.0076ms to get the entire Northwind database (3202 records) +*/ +``` Note: The total time taken includes an extra Redis operation for each record to store the id in a Redis set for each From 186a977803a6fafc79353f2e45e04fc206e8a93b Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 13 Nov 2020 11:12:48 +0800 Subject: [PATCH 026/107] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a805493..7a38c56d 100644 --- a/README.md +++ b/README.md @@ -299,7 +299,7 @@ public class MyDep ### Usage in ServiceStack -Inside ServiceStack Services & Controllers we recommend using `GetRedisAsync()` to resolve an `IRedisClientAsync`, e.g: +Inside ServiceStack Services & Controllers we recommend using `GetRedisAsync()` to resolve an `IRedisClientAsync`: ```csharp public class MyService : Service From b2a4d7018f493bb0a63c8fca97d378c06fecd7f5 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 13 Nov 2020 11:18:46 +0800 Subject: [PATCH 027/107] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 7a38c56d..76da727f 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ A more detailed list of the available RedisClient APIs used in the example can b ### Async Redis -Async support in ServiceStack.Redis is available to .NET Core (**.NET Standard 2.0**) or **.NET Framework v4.7.2+** projects where there's async API equivalents for most sync APIs as contained within the Async Redis interfaces below: +The async support in ServiceStack.Redis is designed for optimal efficiency and uses `ValueTask` & other modern Async APIs only available in **.NET Standard 2.0** and **.NET Framework v4.7.2+** projects where there's async API equivalents for most sync APIs as contained within the Async Redis interfaces below: - [IRedisClientsManagerAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClientsManagerAsync.cs) - [IRedisClientAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClientAsync.cs) @@ -272,8 +272,6 @@ using var syncRedis = container.Resolve().GetClient(); await using var asyncRedis = await container.Resolve().GetClientAsync(); ``` -The async support in ServiceStack.Redis is designed for optimal efficiency and uses `ValueTask` & other modern Async APIs. - If you want to force async-only API usage could choose to just register `IRedisClientsManagerAsync` where it only lets you resolve async only `IRedisClientAsync` and `ICacheClientAsync` clients, e.g: ```csharp From 4f588a411a6e56d7dac8c4a76e46e551d75fc311 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 13 Nov 2020 19:16:10 +0800 Subject: [PATCH 028/107] Update Directory.Build.props --- src/Directory.Build.props | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index a3fd2dfe..88d6b092 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -24,24 +24,27 @@ true - - $(DefineConstants);NET45 + + $(DefineConstants);NETFX;NET45 True False ../servicestack.snk - true + + + + $(DefineConstants);NETFX;NET472 - $(DefineConstants);NETSTANDARD2_0 + $(DefineConstants);NETSTANDARD;NETSTANDARD2_0 + + + + $(DefineConstants);NETSTANDARD;NETSTANDARD2_1 - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - From 1320326fef8d327807f134b7153a4e8f228baacd Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Sun, 15 Nov 2020 11:00:12 +0930 Subject: [PATCH 029/107] Fix cyclical recursive error --- .../RedisClientManagerCacheClient.Async.cs | 8 +++---- .../CacheClientTests.Async.cs | 22 +++++++++++++++++++ .../CacheClientTests.cs | 22 +++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 tests/ServiceStack.Redis.Tests/CacheClientTests.Async.cs create mode 100644 tests/ServiceStack.Redis.Tests/CacheClientTests.cs diff --git a/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs b/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs index 3a0c9290..f77928b3 100644 --- a/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClientManagerCacheClient.Async.cs @@ -23,7 +23,7 @@ private ValueTask GetClientAsync(in CancellationToken token) async Task ICacheClientAsync.GetAsync(string key, CancellationToken token) { - await using var client = await redisManager.GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); + await using var client = await redisManager.GetReadOnlyClientAsync(token).ConfigureAwait(false); return await client.GetAsync(key, token).ConfigureAwait(false); } @@ -53,7 +53,7 @@ async Task ICacheClientAsync.FlushAllAsync(CancellationToken token) async Task> ICacheClientAsync.GetAllAsync(IEnumerable keys, CancellationToken token) { - await using var client = await redisManager.GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); + await using var client = await redisManager.GetReadOnlyClientAsync(token).ConfigureAwait(false); return await client.GetAllAsync(keys, token).ConfigureAwait(false); } @@ -71,13 +71,13 @@ async Task ICacheClientAsync.RemoveAsync(string key, CancellationToken tok async Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken token) { - await using var client = await redisManager.GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); + await using var client = await redisManager.GetReadOnlyClientAsync(token).ConfigureAwait(false); return await client.GetTimeToLiveAsync(key, token).ConfigureAwait(false); } async IAsyncEnumerable ICacheClientAsync.GetKeysByPatternAsync(string pattern, [EnumeratorCancellation] CancellationToken token) { - await using var client = await redisManager.GetReadOnlyCacheClientAsync(token).ConfigureAwait(false); + await using var client = await redisManager.GetReadOnlyClientAsync(token).ConfigureAwait(false); await foreach (var key in client.GetKeysByPatternAsync(pattern, token).ConfigureAwait(false).WithCancellation(token)) { yield return key; diff --git a/tests/ServiceStack.Redis.Tests/CacheClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/CacheClientTests.Async.cs new file mode 100644 index 00000000..032d2edb --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/CacheClientTests.Async.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using NUnit.Framework; + +namespace ServiceStack.Redis.Tests +{ + public class CacheClientTestsAsync + : RedisClientTestsBaseAsync + { + IRedisClientsManagerAsync redisManager = new RedisManagerPool(TestConfig.SingleHost); + + [Test] + public async Task Can_get_set_CacheClient_Async() + { + await using var cache = await redisManager.GetCacheClientAsync(); + await cache.FlushAllAsync(); + + await cache.SetAsync("key", "A"); + var result = await cache.GetAsync("key"); + Assert.That(result, Is.EqualTo("A")); + } + } +} \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/CacheClientTests.cs b/tests/ServiceStack.Redis.Tests/CacheClientTests.cs new file mode 100644 index 00000000..4d6f5b3d --- /dev/null +++ b/tests/ServiceStack.Redis.Tests/CacheClientTests.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using NUnit.Framework; + +namespace ServiceStack.Redis.Tests +{ + public class CacheClientTests + : RedisClientTestsBase + { + IRedisClientsManager redisManager = new RedisManagerPool(TestConfig.SingleHost); + + [Test] + public void Can_get_set_CacheClient() + { + var cache = redisManager.GetCacheClient(); + cache.FlushAll(); + + cache.Set("key", "A"); + var result = cache.Get("key"); + Assert.That(result, Is.EqualTo("A")); + } + } +} \ No newline at end of file From 8d382270e21ca5f1e658ce25bcabcd184590b197 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Mon, 16 Nov 2020 17:29:29 +0800 Subject: [PATCH 030/107] Drain any buffers before returning pooled client --- src/ServiceStack.Redis/BufferedReader.cs | 5 ++++ .../PooledRedisClientManager.cs | 16 +++++----- src/ServiceStack.Redis/RedisClient.Async.cs | 29 ++++++++++++------- src/ServiceStack.Redis/RedisManagerPool.cs | 8 ++--- src/ServiceStack.Redis/RedisNativeClient.cs | 2 +- .../RedisNativeClient_Utils.cs | 25 ++++++++++++++++ 6 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/ServiceStack.Redis/BufferedReader.cs b/src/ServiceStack.Redis/BufferedReader.cs index 9bf2d573..b5aa67df 100644 --- a/src/ServiceStack.Redis/BufferedReader.cs +++ b/src/ServiceStack.Redis/BufferedReader.cs @@ -28,6 +28,11 @@ internal BufferedReader(Stream source, int bufferSize) { _source = source; _buffer = new byte[bufferSize]; + Reset(); + } + + internal void Reset() + { _offset = _available = 0; } diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.cs b/src/ServiceStack.Redis/PooledRedisClientManager.cs index 74bd92c2..6428dd34 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.cs @@ -250,7 +250,7 @@ private RedisClient GetClient(bool forAsync) if (inActiveClient != null) { WritePoolIndex++; - inActiveClient.Active = true; + inActiveClient.Activate(); InitClient(inActiveClient); @@ -400,7 +400,7 @@ private RedisClient GetReadOnlyClient(bool forAsync) if (inActiveClient != null) { ReadPoolIndex++; - inActiveClient.Active = true; + inActiveClient.Activate(); InitClient(inActiveClient); @@ -468,7 +468,7 @@ private int GetInActiveReadClient(out RedisClient inactiveClient) { var desiredIndex = ReadPoolIndex % readClients.Length; //this will loop through all hosts in readClients once even though there are 2 for loops - //both loops are used to try to get the prefered host according to the round robin algorithm + //both loops are used to try to get the preferred host according to the round robin algorithm var readOnlyTotal = RedisResolver.ReadOnlyHostsCount; for (int x = 0; x < readOnlyTotal; x++) { @@ -502,7 +502,7 @@ private int GetInActiveReadClient(out RedisClient inactiveClient) private RedisClient InitNewClient(RedisClient client) { client.Id = Interlocked.Increment(ref RedisClientCounter); - client.Active = true; + client.Activate(newClient:true); client.ClientManager = this; client.ConnectionFilter = ConnectionFilter; if (NamespacePrefix != null) @@ -543,7 +543,7 @@ public void DisposeClient(RedisNativeClient client) else { client.TrackThread = null; - client.Active = false; + client.Deactivate(); } Monitor.PulseAll(readClients); @@ -564,7 +564,7 @@ public void DisposeClient(RedisNativeClient client) else { client.TrackThread = null; - client.Active = false; + client.Deactivate(); } Monitor.PulseAll(writeClients); @@ -587,7 +587,7 @@ public void DisposeReadOnlyClient(RedisNativeClient client) { lock (readClients) { - client.Active = false; + client.Deactivate(); Monitor.PulseAll(readClients); } } @@ -600,7 +600,7 @@ public void DisposeWriteClient(RedisNativeClient client) { lock (writeClients) { - client.Active = false; + client.Deactivate(); Monitor.PulseAll(writeClients); } } diff --git a/src/ServiceStack.Redis/RedisClient.Async.cs b/src/ServiceStack.Redis/RedisClient.Async.cs index 4d19f225..a0c9c36a 100644 --- a/src/ServiceStack.Redis/RedisClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClient.Async.cs @@ -105,7 +105,8 @@ private async ValueTask ExecAsync(Func> ac { using (JsConfig.With(new Text.Config { ExcludeTypeInfo = false })) { - return await action(this).ConfigureAwait(false); + var ret = await action(this).ConfigureAwait(false); + return ret; } } @@ -120,11 +121,19 @@ ValueTask IRedisClientAsync.GetValueAsync(string key, CancellationToken Task ICacheClientAsync.GetAsync(string key, CancellationToken token) { - return ExecAsync(r => - typeof(T) == typeof(byte[]) - ? ((IRedisNativeClientAsync)r).GetAsync(key, token).Await(val => (T)(object)val) - : r.GetValueAsync(key, token).Await(val => JsonSerializer.DeserializeFromString(val)) - ).AsTask(); + return ExecAsync(async r => { + if (typeof(T) == typeof(byte[])) + { + var ret = await ((IRedisNativeClientAsync) r).GetAsync(key, token).ConfigureAwait(false); + return (T) (object) ret; + } + else + { + var val = await r.GetValueAsync(key, token).ConfigureAwait(false); + var ret = JsonSerializer.DeserializeFromString(val); + return ret; + } + }).AsTask(); } async ValueTask> IRedisClientAsync.SearchKeysAsync(string pattern, CancellationToken token) @@ -169,10 +178,10 @@ ValueTask IRedisClientAsync.AddItemToSortedSetAsync(string setId, string v => ((IRedisClientAsync)this).AddItemToSortedSetAsync(setId, value, GetLexicalScore(value), token); ValueTask IRedisClientAsync.AddItemToSortedSetAsync(string setId, string value, double score, CancellationToken token) - => NativeAsync.ZAddAsync(setId, score, value.ToUtf8Bytes()).IsSuccessAsync(); + => NativeAsync.ZAddAsync(setId, score, value.ToUtf8Bytes(), token).IsSuccessAsync(); ValueTask IRedisClientAsync.SetEntryInHashAsync(string hashId, string key, string value, CancellationToken token) - => NativeAsync.HSetAsync(hashId, key.ToUtf8Bytes(), value.ToUtf8Bytes()).IsSuccessAsync(); + => NativeAsync.HSetAsync(hashId, key.ToUtf8Bytes(), value.ToUtf8Bytes(), token).IsSuccessAsync(); ValueTask IRedisClientAsync.SetAllAsync(IDictionary map, CancellationToken token) => GetSetAllBytes(map, out var keyBytes, out var valBytes) ? NativeAsync.MSetAsync(keyBytes, valBytes, token) : default; @@ -221,7 +230,7 @@ ValueTask IRedisClientAsync.ExpireEntryAtAsync(string key, DateTime expire : NativeAsync.ExpireAtAsync(key, ConvertToServerDate(expireAt).ToUnixTime(), token); Task ICacheClientAsync.GetTimeToLiveAsync(string key, CancellationToken token) - => NativeAsync.TtlAsync(key, token).Await(ttlSecs => ParseTimeToLiveResult(ttlSecs)).AsTask(); + => NativeAsync.TtlAsync(key, token).Await(ParseTimeToLiveResult).AsTask(); ValueTask IRedisClientAsync.PingAsync(CancellationToken token) => NativeAsync.PingAsync(token); @@ -365,7 +374,7 @@ ValueTask IRedisClientAsync.SlowlogResetAsync(CancellationToken token) => NativeAsync.SlowlogResetAsync(token); ValueTask IRedisClientAsync.GetSlowlogAsync(int? numberOfRecords, CancellationToken token) - => NativeAsync.SlowlogGetAsync(numberOfRecords, token).Await(data => ParseSlowlog(data)); + => NativeAsync.SlowlogGetAsync(numberOfRecords, token).Await(ParseSlowlog); Task ICacheClientAsync.SetAsync(string key, T value, CancellationToken token) diff --git a/src/ServiceStack.Redis/RedisManagerPool.cs b/src/ServiceStack.Redis/RedisManagerPool.cs index 18b1f1c3..b5fe4144 100644 --- a/src/ServiceStack.Redis/RedisManagerPool.cs +++ b/src/ServiceStack.Redis/RedisManagerPool.cs @@ -151,7 +151,7 @@ private RedisClient GetClient(bool forAsync) if (inActiveClient != null) { poolIndex++; - inActiveClient.Active = true; + inActiveClient.Activate(); return !AssertAccessOnlyOnSameThread ? inActiveClient @@ -281,7 +281,7 @@ private int GetInActiveClient(out RedisClient inactiveClient) private RedisClient InitNewClient(RedisClient client) { client.Id = Interlocked.Increment(ref RedisClientCounter); - client.Active = true; + client.Activate(newClient:true); client.ClientManager = this; client.ConnectionFilter = ConnectionFilter; @@ -303,7 +303,7 @@ public void DisposeClient(RedisNativeClient client) else { client.TrackThread = null; - client.Active = false; + client.Deactivate(); } Monitor.PulseAll(clients); @@ -320,7 +320,7 @@ public void DisposeWriteClient(RedisNativeClient client) { lock (clients) { - client.Active = false; + client.Deactivate(); } } diff --git a/src/ServiceStack.Redis/RedisNativeClient.cs b/src/ServiceStack.Redis/RedisNativeClient.cs index 90fb0224..4493cc6c 100644 --- a/src/ServiceStack.Redis/RedisNativeClient.cs +++ b/src/ServiceStack.Redis/RedisNativeClient.cs @@ -83,7 +83,7 @@ public DateTime? DeactivatedAt internal bool Active { get => Interlocked.CompareExchange(ref active, 0, 0) == YES; - set => Interlocked.Exchange(ref active, value ? YES : NO); + private set => Interlocked.Exchange(ref active, value ? YES : NO); } internal IHandleClientDispose ClientManager { get; set; } diff --git a/src/ServiceStack.Redis/RedisNativeClient_Utils.cs b/src/ServiceStack.Redis/RedisNativeClient_Utils.cs index 5ebb894d..1d17dd25 100644 --- a/src/ServiceStack.Redis/RedisNativeClient_Utils.cs +++ b/src/ServiceStack.Redis/RedisNativeClient_Utils.cs @@ -557,6 +557,31 @@ private void SendDirectToSocket(ArraySegment segment) } } + /// + /// Called before returning pooled client/socket + /// + internal void Activate(bool newClient=false) + { + if (!newClient) + { + //Drain any existing buffers + ResetSendBuffer(); + bufferedReader.Reset(); + if (socket?.Available > 0) + { + logDebug($"Draining existing socket of {socket.Available} bytes"); + var buff = new byte[socket.Available]; + socket.Receive(buff, SocketFlags.None); + } + } + Active = true; + } + + internal void Deactivate() + { + Active = false; + } + /// /// reset buffer index in send buffer /// From 61e2ec430fb3ea8fc8dd36042446d551458955c7 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Mon, 16 Nov 2020 18:30:11 +0800 Subject: [PATCH 031/107] Fix NRE --- src/ServiceStack.Redis/RedisNativeClient_Utils.cs | 2 +- tests/ServiceStack.Redis.Tests/CacheClientTests.Async.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ServiceStack.Redis/RedisNativeClient_Utils.cs b/src/ServiceStack.Redis/RedisNativeClient_Utils.cs index 1d17dd25..fc06af58 100644 --- a/src/ServiceStack.Redis/RedisNativeClient_Utils.cs +++ b/src/ServiceStack.Redis/RedisNativeClient_Utils.cs @@ -566,7 +566,7 @@ internal void Activate(bool newClient=false) { //Drain any existing buffers ResetSendBuffer(); - bufferedReader.Reset(); + bufferedReader?.Reset(); if (socket?.Available > 0) { logDebug($"Draining existing socket of {socket.Available} bytes"); diff --git a/tests/ServiceStack.Redis.Tests/CacheClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/CacheClientTests.Async.cs index 032d2edb..e63437a0 100644 --- a/tests/ServiceStack.Redis.Tests/CacheClientTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/CacheClientTests.Async.cs @@ -4,7 +4,6 @@ namespace ServiceStack.Redis.Tests { public class CacheClientTestsAsync - : RedisClientTestsBaseAsync { IRedisClientsManagerAsync redisManager = new RedisManagerPool(TestConfig.SingleHost); From d391ae9dfa656d93ff77d87008a20e3763dc354a Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Mon, 16 Nov 2020 18:34:14 +0800 Subject: [PATCH 032/107] bump to v5.10.2 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 88d6b092..aab521c8 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.10.1 + 5.10.2 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index d6802681..273dcc86 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.10.1 + 5.10.2 latest false From 52f25b40873da391ea0a07ab3ac3a7486dfb44bf Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Mon, 16 Nov 2020 23:27:21 +0800 Subject: [PATCH 033/107] bump to v5.10.3 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index aab521c8..7320e64d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.10.2 + 5.10.3 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 273dcc86..c8651699 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.10.2 + 5.10.3 latest false From f247bb6929878bd618c27fa07f2c6410352466ea Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 17 Nov 2020 18:36:07 +0800 Subject: [PATCH 034/107] Also use ConnectTimeout when establishing an SSL connection --- .../RedisNativeClient_Utils.cs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/ServiceStack.Redis/RedisNativeClient_Utils.cs b/src/ServiceStack.Redis/RedisNativeClient_Utils.cs index fc06af58..c8b5d19c 100644 --- a/src/ServiceStack.Redis/RedisNativeClient_Utils.cs +++ b/src/ServiceStack.Redis/RedisNativeClient_Utils.cs @@ -144,7 +144,7 @@ private void Connect() } else { -#if NETSTANDARD2_0 +#if NETSTANDARD || NET472 sslStream = new SslStream(networkStream, leaveInnerStreamOpen: false, userCertificateValidationCallback: RedisConfig.CertificateValidationCallback, @@ -167,17 +167,26 @@ private void Connect() #endif } -#if NETSTANDARD2_0 - sslStream.AuthenticateAsClientAsync(Host).Wait(); +#if NETSTANDARD || NET472 + var task = sslStream.AuthenticateAsClientAsync(Host); + if (ConnectTimeout > 0) + { + task.Wait(ConnectTimeout); + } + else + { + task.Wait(); + } #else if (SslProtocols != null) { - sslStream.AuthenticateAsClient(Host, new X509CertificateCollection(), SslProtocols ?? System.Security.Authentication.SslProtocols.Default, checkCertificateRevocation: true); - } else + sslStream.AuthenticateAsClient(Host, new X509CertificateCollection(), + SslProtocols ?? System.Security.Authentication.SslProtocols.None, checkCertificateRevocation: true); + } + else { sslStream.AuthenticateAsClient(Host); } - #endif if (!sslStream.IsEncrypted) From b13d4f1700911ca3130057d4b6ab11be8697f857 Mon Sep 17 00:00:00 2001 From: Benedetto Proietti Date: Thu, 17 Dec 2020 12:04:54 +0100 Subject: [PATCH 035/107] Removing MS Open Tech because abandoned; adding Memurai which derives from MS OpenTech. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76da727f..0994bf8b 100644 --- a/README.md +++ b/README.md @@ -953,7 +953,7 @@ Contributors need to approve the [Contributor License Agreement](https://docs.go ### Redis Server builds for Windows * [Redis on Windows](https://github.com/ServiceStack/redis-windows) - * [MS Open Tech - Redis on Windows](https://github.com/MSOpenTech/Redis) + * [Memurai - Redis on Windows, derived from MS Open Tech, commercial with free developer edition](https://www.memurai.com) * [Downloads for Cygwin 32bit Redis Server Windows builds](http://code.google.com/p/servicestack/wiki/RedisWindowsDownload). * [Project that lets you run Redis as a Windows Service](https://github.com/rgl/redis) * [Another Redis as a Windows Service project, which allows you to run separate service for each Redis instance](https://github.com/kcherenkov/redis-windows-service) From 9516732674249aa341402687bbf457261706b5eb Mon Sep 17 00:00:00 2001 From: Benedetto Proietti Date: Thu, 17 Dec 2020 12:37:55 +0100 Subject: [PATCH 036/107] Removing MS Open Tech because abandoned; adding Memurai which derives from MS OpenTech. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0994bf8b..82ba7bbc 100644 --- a/README.md +++ b/README.md @@ -953,7 +953,7 @@ Contributors need to approve the [Contributor License Agreement](https://docs.go ### Redis Server builds for Windows * [Redis on Windows](https://github.com/ServiceStack/redis-windows) - * [Memurai - Redis on Windows, derived from MS Open Tech, commercial with free developer edition](https://www.memurai.com) + * [Memurai - Redis on Windows, derived from MS Open Tech (now defunct); commercial with free developer edition](https://www.memurai.com) * [Downloads for Cygwin 32bit Redis Server Windows builds](http://code.google.com/p/servicestack/wiki/RedisWindowsDownload). * [Project that lets you run Redis as a Windows Service](https://github.com/rgl/redis) * [Another Redis as a Windows Service project, which allows you to run separate service for each Redis instance](https://github.com/kcherenkov/redis-windows-service) From 82488593d86b8cba7e7b51579bd1f1cbd16e76c8 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 18 Dec 2020 13:59:23 +0800 Subject: [PATCH 037/107] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 82ba7bbc..d92a21c8 100644 --- a/README.md +++ b/README.md @@ -953,6 +953,7 @@ Contributors need to approve the [Contributor License Agreement](https://docs.go ### Redis Server builds for Windows * [Redis on Windows](https://github.com/ServiceStack/redis-windows) + * [Redis 5.0.10 for Windows - maintained OSS port of MS Open Tech](https://github.com/tporadowski/redis/) * [Memurai - Redis on Windows, derived from MS Open Tech (now defunct); commercial with free developer edition](https://www.memurai.com) * [Downloads for Cygwin 32bit Redis Server Windows builds](http://code.google.com/p/servicestack/wiki/RedisWindowsDownload). * [Project that lets you run Redis as a Windows Service](https://github.com/rgl/redis) From 51f1d2243884d3beeb9ae706ba8f692442730474 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Sat, 2 Jan 2021 15:35:41 +0800 Subject: [PATCH 038/107] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index d92a21c8..9378c7d9 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,14 @@ container.Register(c => The `PooledRedisClientManager` imposes a maximum connection limit and when its maximum pool size has been reached will instead block on any new connection requests until the next `RedisClient` is released back into the pool. If no client became available within `PoolTimeout`, a Pool `TimeoutException` will be thrown. +#### Read Only Clients + +By default resolving a RedisClient with `GetRedisClient()` or `GetRedisClientAsync()` will return a client connected to the configured primary (master) host, if you also have replica (slave) hosts configured, you can access it with the `GetReadOnlyClient()` or `GetReadOnlyClientAsync()` APIs, e.g: + +```csharp +using var redisReadOnly = clientsManager.GetReadOnlyClient(); +``` + ### BasicRedisClientManager If don't want to use connection pooling (i.e. you're accessing a local redis-server instance) you can use a basic (non-pooled) Clients Manager which creates a new `RedisClient` instance each time: From 7e30bb0c17e2dbebe17c05632cc646b08ce8b12f Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 12 Jan 2021 17:48:11 +0800 Subject: [PATCH 039/107] bump to v5.10.4 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 7320e64d..c98d9bd6 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.10.3 + 5.10.4 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index c8651699..2ea8ddf2 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.10.3 + 5.10.4 latest false From 4a7555936999adc29102a1d9aaba1ee6150e64ab Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Thu, 14 Jan 2021 15:31:24 +0800 Subject: [PATCH 040/107] bump to v5.10.5 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index c98d9bd6..f1b2141c 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.10.4 + 5.10.5 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 2ea8ddf2..5372a1ee 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.10.4 + 5.10.5 latest false From e2373b9bd162fd172a853a04b98102502f069a37 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 15 Jan 2021 22:21:09 +0800 Subject: [PATCH 041/107] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9378c7d9..bd9dd1d5 100644 --- a/README.md +++ b/README.md @@ -350,7 +350,7 @@ var sentinelHosts = new[]{ "sentinel1", "sentinel2:6390", "sentinel3" }; var sentinel = new RedisSentinel(sentinelHosts, masterName: "mymaster"); ``` -This configues a `RedisSentinel` with 3 sentinel hosts looking at **mymaster** group. +This configures a `RedisSentinel` with 3 sentinel hosts looking at **mymaster** group. As the default port for sentinels when unspecified is **26379** and how RedisSentinel is able to auto-discover other sentinels, the minimum configuration required is with a single Sentinel host: @@ -365,7 +365,7 @@ flexibility of [Redis Connection Strings](#redis-connection-strings) to apply co individual Redis Clients you need to register a custom `HostFilter`: ```csharp -sentinel.HostFilter = host => "{0}?db=1&RetryTimeout=5000".Fmt(host); +sentinel.HostFilter = host => "{host}?db=1&RetryTimeout=5000"; ``` An alternative to using connection strings for configuring clients is to modify From 588679a7af99649b8e555b7ea0d7a1da68450dfe Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 15 Jan 2021 22:27:04 +0800 Subject: [PATCH 042/107] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd9dd1d5..7b6b18fd 100644 --- a/README.md +++ b/README.md @@ -365,7 +365,7 @@ flexibility of [Redis Connection Strings](#redis-connection-strings) to apply co individual Redis Clients you need to register a custom `HostFilter`: ```csharp -sentinel.HostFilter = host => "{host}?db=1&RetryTimeout=5000"; +sentinel.HostFilter = host => $"{host}?db=1&RetryTimeout=5000"; ``` An alternative to using connection strings for configuring clients is to modify From 8e6604dddff2b8a1352ab193adb39f08e836a7a7 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Sat, 16 Jan 2021 14:36:27 +0800 Subject: [PATCH 043/107] Update Build.props --- src/Directory.Build.props | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index f1b2141c..abe92e64 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -43,6 +43,14 @@ $(DefineConstants);NETSTANDARD;NETSTANDARD2_1 + + $(DefineConstants);NET50 + + + + $(DefineConstants);NETCORE_SUPPORT;NETCORE + + From 11a48e3fa6b5d9164a2cfc01eb21dd30a3b35620 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Sat, 16 Jan 2021 14:36:42 +0800 Subject: [PATCH 044/107] Update BlockingPop.cs --- tests/Console.Tests/BlockingPop.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Console.Tests/BlockingPop.cs b/tests/Console.Tests/BlockingPop.cs index 5d1b5ec2..90aef4c5 100644 --- a/tests/Console.Tests/BlockingPop.cs +++ b/tests/Console.Tests/BlockingPop.cs @@ -49,7 +49,7 @@ public void Execute() // add items to list for (int i = 1; i <= items; i++) { - redis.PushItemToList(listId, "item {0}".Fmt(i)); + redis.PushItemToList(listId, $"item {i}"); } do @@ -57,7 +57,7 @@ public void Execute() var item = redis.BlockingPopItemFromList(listId, null); // log the popped item. if BRPOP timeout is null and list empty, I do not expect to print anything - log.InfoFormat("{0}", item.IsNullOrEmpty() ? " list empty " : item); + log.InfoFormat("{0}", string.IsNullOrEmpty(item) ? " list empty " : item); System.Threading.Thread.Sleep(1000); From 45ff22bd6baa56597a448a410e32738a75e5c034 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Sat, 16 Jan 2021 14:41:15 +0800 Subject: [PATCH 045/107] Convert Console.Tests to .NET 5 Console App --- src/ServiceStack.Redis.sln | 26 ++-- tests/Console.Tests/App.config | 22 --- tests/Console.Tests/Console.Tests.csproj | 132 +++--------------- .../Console.Tests/Properties/AssemblyInfo.cs | 36 ----- tests/Console.Tests/packages.config | 8 -- 5 files changed, 31 insertions(+), 193 deletions(-) delete mode 100644 tests/Console.Tests/App.config delete mode 100644 tests/Console.Tests/Properties/AssemblyInfo.cs delete mode 100644 tests/Console.Tests/packages.config diff --git a/src/ServiceStack.Redis.sln b/src/ServiceStack.Redis.sln index 3c500eee..c2ea43b7 100644 --- a/src/ServiceStack.Redis.sln +++ b/src/ServiceStack.Redis.sln @@ -19,12 +19,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceStack.Redis", "Servi EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceStack.Redis.Tests", "..\tests\ServiceStack.Redis.Tests\ServiceStack.Redis.Tests.csproj", "{951D28EE-5D22-4C62-AC0F-1661A8CEEC5A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console.Tests", "..\tests\Console.Tests\Console.Tests.csproj", "{8368C965-B4F6-4263-9ABB-731A175B2E77}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceStack.Redis.Tests.Sentinel", "..\tests\ServiceStack.Redis.Tests.Sentinel\ServiceStack.Redis.Tests.Sentinel.csproj", "{91C55091-A946-49B5-9517-8794EBCC5784}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack.Redis.Benchmark", "..\tests\ServiceStack.Redis.Benchmark\ServiceStack.Redis.Benchmark.csproj", "{959CA5FE-6525-4EEF-86CA-F4978BEFF14F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console.Tests", "..\tests\Console.Tests\Console.Tests.csproj", "{56DEDC64-B349-4150-BE9C-5805D831678D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -59,16 +59,6 @@ Global {951D28EE-5D22-4C62-AC0F-1661A8CEEC5A}.Release|x64.Build.0 = Release|Any CPU {951D28EE-5D22-4C62-AC0F-1661A8CEEC5A}.Release|x86.ActiveCfg = Release|Any CPU {951D28EE-5D22-4C62-AC0F-1661A8CEEC5A}.Release|x86.Build.0 = Release|Any CPU - {8368C965-B4F6-4263-9ABB-731A175B2E77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8368C965-B4F6-4263-9ABB-731A175B2E77}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8368C965-B4F6-4263-9ABB-731A175B2E77}.Debug|x64.ActiveCfg = Debug|Any CPU - {8368C965-B4F6-4263-9ABB-731A175B2E77}.Debug|x64.Build.0 = Debug|Any CPU - {8368C965-B4F6-4263-9ABB-731A175B2E77}.Debug|x86.ActiveCfg = Debug|Any CPU - {8368C965-B4F6-4263-9ABB-731A175B2E77}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8368C965-B4F6-4263-9ABB-731A175B2E77}.Release|Any CPU.Build.0 = Release|Any CPU - {8368C965-B4F6-4263-9ABB-731A175B2E77}.Release|x64.ActiveCfg = Release|Any CPU - {8368C965-B4F6-4263-9ABB-731A175B2E77}.Release|x64.Build.0 = Release|Any CPU - {8368C965-B4F6-4263-9ABB-731A175B2E77}.Release|x86.ActiveCfg = Release|Any CPU {91C55091-A946-49B5-9517-8794EBCC5784}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91C55091-A946-49B5-9517-8794EBCC5784}.Debug|Any CPU.Build.0 = Debug|Any CPU {91C55091-A946-49B5-9517-8794EBCC5784}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -91,6 +81,18 @@ Global {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Release|x64.Build.0 = Release|Any CPU {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Release|x86.ActiveCfg = Release|Any CPU {959CA5FE-6525-4EEF-86CA-F4978BEFF14F}.Release|x86.Build.0 = Release|Any CPU + {56DEDC64-B349-4150-BE9C-5805D831678D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {56DEDC64-B349-4150-BE9C-5805D831678D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56DEDC64-B349-4150-BE9C-5805D831678D}.Debug|x64.ActiveCfg = Debug|Any CPU + {56DEDC64-B349-4150-BE9C-5805D831678D}.Debug|x64.Build.0 = Debug|Any CPU + {56DEDC64-B349-4150-BE9C-5805D831678D}.Debug|x86.ActiveCfg = Debug|Any CPU + {56DEDC64-B349-4150-BE9C-5805D831678D}.Debug|x86.Build.0 = Debug|Any CPU + {56DEDC64-B349-4150-BE9C-5805D831678D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {56DEDC64-B349-4150-BE9C-5805D831678D}.Release|Any CPU.Build.0 = Release|Any CPU + {56DEDC64-B349-4150-BE9C-5805D831678D}.Release|x64.ActiveCfg = Release|Any CPU + {56DEDC64-B349-4150-BE9C-5805D831678D}.Release|x64.Build.0 = Release|Any CPU + {56DEDC64-B349-4150-BE9C-5805D831678D}.Release|x86.ActiveCfg = Release|Any CPU + {56DEDC64-B349-4150-BE9C-5805D831678D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tests/Console.Tests/App.config b/tests/Console.Tests/App.config deleted file mode 100644 index 13a97f50..00000000 --- a/tests/Console.Tests/App.config +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/Console.Tests/Console.Tests.csproj b/tests/Console.Tests/Console.Tests.csproj index ea8e970e..cfd8fff7 100644 --- a/tests/Console.Tests/Console.Tests.csproj +++ b/tests/Console.Tests/Console.Tests.csproj @@ -1,115 +1,17 @@ - - - - - Debug - AnyCPU - {8368C965-B4F6-4263-9ABB-731A175B2E77} - Exe - Properties - ConsoleTests - Console.Tests - v4.5 - 512 - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - - - ..\..\src\packages\ServiceStack.Common.5.6.1\lib\net45\ServiceStack.Common.dll - True - - - ..\..\src\packages\ServiceStack.Interfaces.5.6.1\lib\net45\ServiceStack.Interfaces.dll - True - - - ..\..\src\packages\ServiceStack.Text.5.7.1\lib\net45\ServiceStack.Text.dll - True - - - - ..\..\src\packages\System.Buffers.4.5.0\lib\netstandard1.1\System.Buffers.dll - True - - - - - True - - - - ..\..\src\packages\System.Runtime.CompilerServices.Unsafe.4.6.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll - True - - - - - - ..\..\src\packages\System.ValueTuple.4.5.0\lib\netstandard1.0\System.ValueTuple.dll - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - {AF99F19B-4C04-4F58-81EF-B092F1FCC540} - ServiceStack.Redis - - - - - - - - - \ No newline at end of file + + + + Exe + net5.0 + Console.Tests + + + + + + + + + + + diff --git a/tests/Console.Tests/Properties/AssemblyInfo.cs b/tests/Console.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index d188967b..00000000 --- a/tests/Console.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Console.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Console.Tests")] -[assembly: AssemblyCopyright("Copyright © 2014")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("33cdad3a-8f89-437f-9a64-261c162888aa")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/tests/Console.Tests/packages.config b/tests/Console.Tests/packages.config deleted file mode 100644 index 57db9cf1..00000000 --- a/tests/Console.Tests/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file From dea69dc26b591e925700ac8f51f96bfd61e03b5f Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Sat, 16 Jan 2021 15:04:05 +0800 Subject: [PATCH 046/107] Update test projects to v4.7.2 / net5.0 --- .../ServiceStack.Redis.Benchmark.csproj | 2 +- .../ServiceStack.Redis.Tests.Sentinel.csproj | 10 +++++----- .../ServiceStack.Redis.Tests.csproj | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/ServiceStack.Redis.Benchmark/ServiceStack.Redis.Benchmark.csproj b/tests/ServiceStack.Redis.Benchmark/ServiceStack.Redis.Benchmark.csproj index 3c0feb32..815d5617 100644 --- a/tests/ServiceStack.Redis.Benchmark/ServiceStack.Redis.Benchmark.csproj +++ b/tests/ServiceStack.Redis.Benchmark/ServiceStack.Redis.Benchmark.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1;net472 + net5.0;net472 8 diff --git a/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj b/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj index ba28f9f7..ad5f7bdb 100644 --- a/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj +++ b/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1;net45 + net472;net5.0 portable ServiceStack.Redis.Tests.Sentinel Library @@ -27,11 +27,11 @@ - + $(DefineConstants);NET45 - + @@ -41,11 +41,11 @@ - + $(DefineConstants);NETCORE_SUPPORT;NETCORE - + diff --git a/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj b/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj index 1bde00c3..29196776 100644 --- a/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj +++ b/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj @@ -5,9 +5,9 @@ - net46 is .NET Framework without async - net472 is .NET Framework with async - netcoreapp2.1 is .NET Core with async, but without some features like memory/stream APIs - - netcoreapp3.1 is .NET Core with all the bells and whistles + - net5.0 is .NET Core with all the bells and whistles --> - net46;net472;netcoreapp2.1;netcoreapp3.1 + net46;net472;netcoreapp2.1;net5.0 portable ServiceStack.Redis.Tests Library @@ -36,7 +36,7 @@ - + $(DefineConstants);NETCORE From 64a91e9d446cb77937c1ef1b93c135a1e49b2430 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 20 Jan 2021 16:33:34 +0800 Subject: [PATCH 047/107] Change to use non-deprecated ToSet() --- src/ServiceStack.Redis/Generic/RedisTypedCommandQueue.cs | 4 ++-- src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs | 4 ++-- .../Pipeline/RedisAllPurposePipeline.Async.cs | 2 +- src/ServiceStack.Redis/Pipeline/RedisCommandQueue.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ServiceStack.Redis/Generic/RedisTypedCommandQueue.cs b/src/ServiceStack.Redis/Generic/RedisTypedCommandQueue.cs index 0128b55a..00989112 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedCommandQueue.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedCommandQueue.cs @@ -273,7 +273,7 @@ public void QueueCommand(Func, HashSet> command, Ac BeginQueuedCommand(new QueuedRedisTypedCommand { MultiStringReturnCommand = r => command(r).ToList(), - OnSuccessMultiStringCallback = list => onSuccessCallback(list.ToHashSet()), + OnSuccessMultiStringCallback = list => onSuccessCallback(list.ToSet()), OnErrorCallback = onErrorCallback }); command(RedisClient); @@ -294,7 +294,7 @@ public void QueueCommand(Func, HashSet> command, Action< BeginQueuedCommand(new QueuedRedisTypedCommand { MultiObjectReturnCommand = r => command(r).ToList(), - OnSuccessMultiTypeCallback = x => onSuccessCallback(x.ConvertAll(y => JsonSerializer.DeserializeFromString(y)).ToHashSet()), + OnSuccessMultiTypeCallback = x => onSuccessCallback(x.ConvertAll(JsonSerializer.DeserializeFromString).ToSet()), OnErrorCallback = onErrorCallback }); command(RedisClient); diff --git a/src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs index 5f7908b8..2b571235 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedPipeline.Async.cs @@ -229,7 +229,7 @@ void IRedisTypedQueueableOperationAsync.QueueCommand(Func { - OnSuccessMultiStringCallback = list => onSuccessCallback(list.ToHashSet()), + OnSuccessMultiStringCallback = list => onSuccessCallback(list.ToSet()), OnErrorCallback = onErrorCallback }.WithAsyncReturnCommand(async r => { @@ -243,7 +243,7 @@ void IRedisTypedQueueableOperationAsync.QueueCommand(Func { - OnSuccessMultiTypeCallback = x => onSuccessCallback(x.ConvertAll(y => JsonSerializer.DeserializeFromString(y))), + OnSuccessMultiTypeCallback = x => onSuccessCallback(x.ConvertAll(JsonSerializer.DeserializeFromString)), OnErrorCallback = onErrorCallback }.WithAsyncReturnCommand(command)); RedisAllPurposePipeline.AssertSync(command(RedisClient)); diff --git a/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.Async.cs b/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.Async.cs index 2b346e74..992bb480 100644 --- a/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.Async.cs +++ b/src/ServiceStack.Redis/Pipeline/RedisAllPurposePipeline.Async.cs @@ -196,7 +196,7 @@ void IRedisQueueableOperationAsync.QueueCommand(Func onSuccessCallback(list.ToHashSet()), + OnSuccessMultiStringCallback = list => onSuccessCallback(list.ToSet()), OnErrorCallback = onErrorCallback }.WithAsyncReturnCommand(async r => { diff --git a/src/ServiceStack.Redis/Pipeline/RedisCommandQueue.cs b/src/ServiceStack.Redis/Pipeline/RedisCommandQueue.cs index 0a5f3abf..f351317f 100644 --- a/src/ServiceStack.Redis/Pipeline/RedisCommandQueue.cs +++ b/src/ServiceStack.Redis/Pipeline/RedisCommandQueue.cs @@ -242,7 +242,7 @@ public virtual void QueueCommand(Func> command, Ac BeginQueuedCommand(new QueuedRedisCommand { MultiStringReturnCommand = r => command(r).ToList(), - OnSuccessMultiStringCallback = list => onSuccessCallback(list.ToHashSet()), + OnSuccessMultiStringCallback = list => onSuccessCallback(list.ToSet()), OnErrorCallback = onErrorCallback }); command(RedisClient); From dbc2e46b2fb1947e821bfa7fc116f8ec71f1f670 Mon Sep 17 00:00:00 2001 From: Pete Ness Date: Wed, 24 Feb 2021 17:17:49 -0700 Subject: [PATCH 048/107] Improved GetClientAsync so it uses async locks instead of thread locks. --- .../PooledRedisClientManager.Async.cs | 2 +- .../PooledRedisClientManager.cs | 135 +++++++++++++++++- .../ServiceStack.Redis.Core.csproj | 1 + 3 files changed, 134 insertions(+), 4 deletions(-) diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs b/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs index a7bb0ab2..36fcd4d0 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs @@ -25,7 +25,7 @@ ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(Cance => new RedisClientManagerCacheClient(this).AsValueTaskResult(); ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken token) - => GetClient(true).AsValueTaskResult(); + => GetClientAsync(); ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken token) => new RedisClientManagerCacheClient(this) { ReadOnly = true }.AsValueTaskResult(); diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.cs b/src/ServiceStack.Redis/PooledRedisClientManager.cs index 6428dd34..b91df72e 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.cs @@ -15,6 +15,8 @@ using System.IO; using System.Linq; using System.Threading; +using System.Threading.Tasks; +using Cleary.AsyncExtensions; using ServiceStack.Caching; using ServiceStack.Logging; using ServiceStack.Text; @@ -34,6 +36,9 @@ public partial class PooledRedisClientManager private const string PoolTimeoutError = "Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use."; + private AsyncMonitor readMonitor; + private AsyncMonitor writeMonitor; + protected readonly int PoolSizeMultiplier = 20; public int RecheckPoolAfterMs = 100; public int? PoolTimeout { get; set; } @@ -42,7 +47,7 @@ public partial class PooledRedisClientManager public int? SocketReceiveTimeout { get; set; } public int? IdleTimeOutSecs { get; set; } public bool AssertAccessOnlyOnSameThread { get; set; } - + /// /// Gets or sets object key prefix. /// @@ -211,6 +216,17 @@ protected virtual void OnStart() { this.Start(); } + + private async Task waitForWriter(int msTimeout) + { + writeMonitor = new AsyncMonitor(); + var cts = new CancellationTokenSource(); + cts.CancelAfter(msTimeout); + await writeMonitor.WaitAsync(cts.Token); + if (cts.IsCancellationRequested) return false; + cts.Cancel(); + return true; + } /// /// Returns a Read/Write client (The default) using the hosts defined in ReadWriteHosts @@ -218,8 +234,110 @@ protected virtual void OnStart() /// public IRedisClient GetClient() => GetClient(false); + private async ValueTask GetClientAsync() + { + try + { + var poolTimedOut = false; + var inactivePoolIndex = -1; + + do + { + RedisClient inActiveClient; + lock (writeClients) + { + AssertValidReadWritePool(); + + // If it's -1, then we want to try again after a delay of some kind. So if it's NOT negative one, process it... + if ((inactivePoolIndex = GetInActiveWriteClient(out inActiveClient)) != -1) + { + //inActiveClient != null only for Valid InActive Clients + if (inActiveClient != null) + { + WritePoolIndex++; + inActiveClient.Activate(); + + InitClient(inActiveClient); + + return inActiveClient; + } + else + { + // Still need to be in lock for this! + break; + } + } + } + + // Didn't get one, so let's wait a bit for a new one. + if (PoolTimeout.HasValue) + { + if (!await waitForWriter(PoolTimeout.Value)) + { + poolTimedOut = true; + break; + } + } + else + { + await waitForWriter(RecheckPoolAfterMs); + } + } while (true); // Just keep repeating until we get a + + if (poolTimedOut) + throw new TimeoutException(PoolTimeoutError); + + //Reaches here when there's no Valid InActive Clients + try + { + //inactivePoolIndex = index of reservedSlot || index of invalid client + var existingClient = writeClients[inactivePoolIndex]; + if (existingClient != null && existingClient != reservedSlot && existingClient.HadExceptions) + { + RedisState.DeactivateClient(existingClient); + } + + var newClient = InitNewClient(RedisResolver.CreateMasterClient(inactivePoolIndex)); + + //Put all blocking I/O or potential Exceptions before lock + lock (writeClients) + { + //If existingClient at inactivePoolIndex changed (failover) return new client outside of pool + if (writeClients[inactivePoolIndex] != existingClient) + { + if (Log.IsDebugEnabled) + Log.Debug("writeClients[inactivePoolIndex] != existingClient: {0}".Fmt(writeClients[inactivePoolIndex])); + + return newClient; //return client outside of pool + } + + WritePoolIndex++; + writeClients[inactivePoolIndex] = newClient; + + return (!AssertAccessOnlyOnSameThread) + ? newClient + : newClient.LimitAccessToThread(Thread.CurrentThread.ManagedThreadId, Environment.StackTrace); + } + } + catch + { + //Revert free-slot for any I/O exceptions that can throw (before lock) + lock (writeClients) + { + writeClients[inactivePoolIndex] = null; //free slot + } + throw; + } + } + finally + { + RedisState.DisposeExpiredClients(); + } + } + private RedisClient GetClient(bool forAsync) { + if (forAsync) throw new Exception("Call GetClientAsync instead"); try { var poolTimedOut = false; @@ -547,6 +665,7 @@ public void DisposeClient(RedisNativeClient client) } Monitor.PulseAll(readClients); + readMonitor?.PulseAll(); return; } } @@ -568,15 +687,23 @@ public void DisposeClient(RedisNativeClient client) } Monitor.PulseAll(writeClients); + writeMonitor?.PulseAll(); return; } } //Client not found in any pool, pulse both pools. lock (readClients) - Monitor.PulseAll(readClients); + { + Monitor.PulseAll(readClients); + readMonitor?.PulseAll(); + } + lock (writeClients) - Monitor.PulseAll(writeClients); + { + Monitor.PulseAll(writeClients); + writeMonitor?.PulseAll(); + } } /// @@ -589,6 +716,7 @@ public void DisposeReadOnlyClient(RedisNativeClient client) { client.Deactivate(); Monitor.PulseAll(readClients); + readMonitor?.PulseAll(); } } @@ -602,6 +730,7 @@ public void DisposeWriteClient(RedisNativeClient client) { client.Deactivate(); Monitor.PulseAll(writeClients); + writeMonitor?.PulseAll(); } } diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj index 2f611d71..3a04401d 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj @@ -11,6 +11,7 @@ Redis;NoSQL;Client;Distributed;Cache;PubSub;Messaging;Transactions + From c00f67977853220b165daf77ac26dd7072484a1b Mon Sep 17 00:00:00 2001 From: Pete Ness Date: Wed, 24 Feb 2021 20:06:22 -0700 Subject: [PATCH 049/107] Fix waitForWriter to actually wait (and not throw). --- src/ServiceStack.Redis/PooledRedisClientManager.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.cs b/src/ServiceStack.Redis/PooledRedisClientManager.cs index b91df72e..024d051e 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.cs @@ -216,15 +216,14 @@ protected virtual void OnStart() { this.Start(); } - + private async Task waitForWriter(int msTimeout) { - writeMonitor = new AsyncMonitor(); - var cts = new CancellationTokenSource(); - cts.CancelAfter(msTimeout); - await writeMonitor.WaitAsync(cts.Token); - if (cts.IsCancellationRequested) return false; - cts.Cancel(); + if (writeMonitor == null) + writeMonitor = new AsyncMonitor(); + var delayTask = Task.Delay(msTimeout); + var result = await Task.WhenAny(writeMonitor.WaitAsync(), delayTask); + if (result == delayTask) return false; return true; } From 3811e090fd0e92c944716f49abd60ffc63c4c99f Mon Sep 17 00:00:00 2001 From: Pete Ness Date: Wed, 24 Feb 2021 20:37:50 -0700 Subject: [PATCH 050/107] TEMPORARY WORKAROUND - force 10ms retries. Monitor isn't working the way we expect. --- .../PooledRedisClientManager.cs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.cs b/src/ServiceStack.Redis/PooledRedisClientManager.cs index 024d051e..1ba2ed0c 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.cs @@ -240,6 +240,7 @@ private async ValueTask GetClientAsync() var poolTimedOut = false; var inactivePoolIndex = -1; + var timeoutsTests = 300; do { RedisClient inActiveClient; @@ -268,19 +269,27 @@ private async ValueTask GetClientAsync() } } - // Didn't get one, so let's wait a bit for a new one. - if (PoolTimeout.HasValue) + timeoutsTests--; + if (timeoutsTests <= 0) { - if (!await waitForWriter(PoolTimeout.Value)) - { - poolTimedOut = true; - break; - } - } - else - { - await waitForWriter(RecheckPoolAfterMs); + poolTimedOut = true; + break; } + await Task.Delay(10); + + // // Didn't get one, so let's wait a bit for a new one. + // if (PoolTimeout.HasValue) + // { + // if (!await waitForWriter(PoolTimeout.Value)) + // { + // poolTimedOut = true; + // break; + // } + // } + // else + // { + // await waitForWriter(RecheckPoolAfterMs); + // } } while (true); // Just keep repeating until we get a if (poolTimedOut) From 171cbd226a950325f0d46f916886808d070b0ce0 Mon Sep 17 00:00:00 2001 From: Pete Ness Date: Thu, 25 Feb 2021 13:56:44 -0700 Subject: [PATCH 051/107] Switch from AsyncMonitor to AsyncManualResetEvent to support releasing all waiting "locks" better. --- .../PooledRedisClientManager.cs | 100 +++++++++--------- .../ServiceStack.Redis.Core.csproj | 2 +- 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.cs b/src/ServiceStack.Redis/PooledRedisClientManager.cs index 1ba2ed0c..64449092 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.cs @@ -16,7 +16,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Cleary.AsyncExtensions; +using Nito.AsyncEx; using ServiceStack.Caching; using ServiceStack.Logging; using ServiceStack.Text; @@ -36,8 +36,8 @@ public partial class PooledRedisClientManager private const string PoolTimeoutError = "Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use."; - private AsyncMonitor readMonitor; - private AsyncMonitor writeMonitor; + private AsyncManualResetEvent readAsyncEvent; + private AsyncManualResetEvent writeAsyncEvent; protected readonly int PoolSizeMultiplier = 20; public int RecheckPoolAfterMs = 100; @@ -217,13 +217,30 @@ protected virtual void OnStart() this.Start(); } + private void pulseAllRead() + { + readAsyncEvent?.Set(); + readAsyncEvent?.Reset(); + Monitor.PulseAll(readClients); + } + + private void pulseAllWrite() + { + writeAsyncEvent?.Set(); + writeAsyncEvent?.Reset(); + Monitor.PulseAll(writeClients); + } + private async Task waitForWriter(int msTimeout) { - if (writeMonitor == null) - writeMonitor = new AsyncMonitor(); - var delayTask = Task.Delay(msTimeout); - var result = await Task.WhenAny(writeMonitor.WaitAsync(), delayTask); - if (result == delayTask) return false; + if (writeAsyncEvent == null) // If we're not doing async, no need to create this till we need it. + writeAsyncEvent = new AsyncManualResetEvent(false); + var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(msTimeout)); + try + { + await writeAsyncEvent.WaitAsync(cts.Token); + } + catch (OperationCanceledException) { return false; } return true; } @@ -231,16 +248,13 @@ private async Task waitForWriter(int msTimeout) /// Returns a Read/Write client (The default) using the hosts defined in ReadWriteHosts /// /// - public IRedisClient GetClient() => GetClient(false); + public IRedisClient GetClient() => GetClientBlocking(); private async ValueTask GetClientAsync() { try { - var poolTimedOut = false; var inactivePoolIndex = -1; - - var timeoutsTests = 300; do { RedisClient inActiveClient; @@ -269,33 +283,22 @@ private async ValueTask GetClientAsync() } } - timeoutsTests--; - if (timeoutsTests <= 0) + if (PoolTimeout.HasValue) + { + // We have a timeout value set - so try to not wait longer than this. + if (!await waitForWriter(PoolTimeout.Value)) + { + throw new TimeoutException(PoolTimeoutError); + } + } + else { - poolTimedOut = true; - break; + // Wait forever, so just retry till we get one. + await waitForWriter(RecheckPoolAfterMs); } - await Task.Delay(10); - - // // Didn't get one, so let's wait a bit for a new one. - // if (PoolTimeout.HasValue) - // { - // if (!await waitForWriter(PoolTimeout.Value)) - // { - // poolTimedOut = true; - // break; - // } - // } - // else - // { - // await waitForWriter(RecheckPoolAfterMs); - // } - } while (true); // Just keep repeating until we get a + } while (true); // Just keep repeating until we get a slot. - if (poolTimedOut) - throw new TimeoutException(PoolTimeoutError); - - //Reaches here when there's no Valid InActive Clients + //Reaches here when there's no Valid InActive Clients, but we have a slot for one! try { //inactivePoolIndex = index of reservedSlot || index of invalid client @@ -343,9 +346,8 @@ private async ValueTask GetClientAsync() } } - private RedisClient GetClient(bool forAsync) + private RedisClient GetClientBlocking() { - if (forAsync) throw new Exception("Call GetClientAsync instead"); try { var poolTimedOut = false; @@ -380,7 +382,7 @@ private RedisClient GetClient(bool forAsync) InitClient(inActiveClient); - return (!AssertAccessOnlyOnSameThread || forAsync) + return (!AssertAccessOnlyOnSameThread) ? inActiveClient : inActiveClient.LimitAccessToThread(Thread.CurrentThread.ManagedThreadId, Environment.StackTrace); } @@ -416,7 +418,7 @@ private RedisClient GetClient(bool forAsync) WritePoolIndex++; writeClients[inactivePoolIndex] = newClient; - return (!AssertAccessOnlyOnSameThread || forAsync) + return (!AssertAccessOnlyOnSameThread) ? newClient : newClient.LimitAccessToThread(Thread.CurrentThread.ManagedThreadId, Environment.StackTrace); } @@ -672,8 +674,7 @@ public void DisposeClient(RedisNativeClient client) client.Deactivate(); } - Monitor.PulseAll(readClients); - readMonitor?.PulseAll(); + pulseAllRead(); return; } } @@ -694,8 +695,7 @@ public void DisposeClient(RedisNativeClient client) client.Deactivate(); } - Monitor.PulseAll(writeClients); - writeMonitor?.PulseAll(); + pulseAllWrite(); return; } } @@ -703,14 +703,12 @@ public void DisposeClient(RedisNativeClient client) //Client not found in any pool, pulse both pools. lock (readClients) { - Monitor.PulseAll(readClients); - readMonitor?.PulseAll(); + pulseAllRead(); } lock (writeClients) { - Monitor.PulseAll(writeClients); - writeMonitor?.PulseAll(); + pulseAllWrite(); } } @@ -723,8 +721,7 @@ public void DisposeReadOnlyClient(RedisNativeClient client) lock (readClients) { client.Deactivate(); - Monitor.PulseAll(readClients); - readMonitor?.PulseAll(); + pulseAllRead(); } } @@ -737,8 +734,7 @@ public void DisposeWriteClient(RedisNativeClient client) lock (writeClients) { client.Deactivate(); - Monitor.PulseAll(writeClients); - writeMonitor?.PulseAll(); + pulseAllWrite(); } } diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj index 3a04401d..08a3aa26 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj @@ -11,7 +11,7 @@ Redis;NoSQL;Client;Distributed;Cache;PubSub;Messaging;Transactions - + From d6a773eb661a0e356f7d3323155d1864141a5b60 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 26 Feb 2021 08:05:49 +0800 Subject: [PATCH 052/107] Switch to use interned AsyncManualResetEvent + refactor into *Async.cs --- .../PooledRedisClientManager.Async.cs | 248 +++++++++++++++++- .../PooledRedisClientManager.cs | 167 ++---------- .../ServiceStack.Redis.Core.csproj | 1 - 3 files changed, 269 insertions(+), 147 deletions(-) diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs b/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs index 36fcd4d0..01bf36d1 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs @@ -15,29 +15,269 @@ using System; using System.Threading; using System.Threading.Tasks; +using ServiceStack.AsyncEx; namespace ServiceStack.Redis { public partial class PooledRedisClientManager : IRedisClientsManagerAsync { + /// + /// Use previous client resolving behavior + /// + public static bool UseGetClientBlocking = false; + ValueTask IRedisClientsManagerAsync.GetCacheClientAsync(CancellationToken token) => new RedisClientManagerCacheClient(this).AsValueTaskResult(); - ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken token) - => GetClientAsync(); + ValueTask IRedisClientsManagerAsync.GetClientAsync(CancellationToken token) => UseGetClientBlocking + ? GetClientBlocking().AsValueTaskResult() + : GetClientAsync(); ValueTask IRedisClientsManagerAsync.GetReadOnlyCacheClientAsync(CancellationToken token) => new RedisClientManagerCacheClient(this) { ReadOnly = true }.AsValueTaskResult(); - ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken token) - => GetReadOnlyClient(true).AsValueTaskResult(); + ValueTask IRedisClientsManagerAsync.GetReadOnlyClientAsync(CancellationToken token) => UseGetClientBlocking + ? GetReadOnlyClientBlocking().AsValueTaskResult() + : GetReadOnlyClientAsync(); ValueTask IAsyncDisposable.DisposeAsync() { Dispose(); return default; } + + private AsyncManualResetEvent readAsyncEvent; + partial void PulseAllReadAsync() + { + readAsyncEvent?.Set(); + readAsyncEvent?.Reset(); + } + + private AsyncManualResetEvent writeAsyncEvent; + partial void PulseAllWriteAsync() + { + writeAsyncEvent?.Set(); + writeAsyncEvent?.Reset(); + } + + private async Task WaitForWriter(int msTimeout) + { + // If we're not doing async, no need to create this till we need it. + writeAsyncEvent ??= new AsyncManualResetEvent(false); + var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(msTimeout)); + try + { + await writeAsyncEvent.WaitAsync(cts.Token); + } + catch (OperationCanceledException) { return false; } + return true; + } + + private async ValueTask GetClientAsync() + { + try + { + var inactivePoolIndex = -1; + do + { + lock (writeClients) + { + AssertValidReadWritePool(); + + // If it's -1, then we want to try again after a delay of some kind. So if it's NOT negative one, process it... + if ((inactivePoolIndex = GetInActiveWriteClient(out var inActiveClient)) != -1) + { + //inActiveClient != null only for Valid InActive Clients + if (inActiveClient != null) + { + WritePoolIndex++; + inActiveClient.Activate(); + + InitClient(inActiveClient); + + return inActiveClient; + } + else + { + // Still need to be in lock for this! + break; + } + } + } + + if (PoolTimeout.HasValue) + { + // We have a timeout value set - so try to not wait longer than this. + if (!await WaitForWriter(PoolTimeout.Value)) + { + throw new TimeoutException(PoolTimeoutError); + } + } + else + { + // Wait forever, so just retry till we get one. + await WaitForWriter(RecheckPoolAfterMs); + } + } while (true); // Just keep repeating until we get a slot. + + //Reaches here when there's no Valid InActive Clients, but we have a slot for one! + try + { + //inactivePoolIndex = index of reservedSlot || index of invalid client + var existingClient = writeClients[inactivePoolIndex]; + if (existingClient != null && existingClient != reservedSlot && existingClient.HadExceptions) + { + RedisState.DeactivateClient(existingClient); + } + + var newClient = InitNewClient(RedisResolver.CreateMasterClient(inactivePoolIndex)); + + //Put all blocking I/O or potential Exceptions before lock + lock (writeClients) + { + //If existingClient at inactivePoolIndex changed (failover) return new client outside of pool + if (writeClients[inactivePoolIndex] != existingClient) + { + if (Log.IsDebugEnabled) + Log.Debug("writeClients[inactivePoolIndex] != existingClient: {0}".Fmt(writeClients[inactivePoolIndex])); + + return newClient; //return client outside of pool + } + + WritePoolIndex++; + writeClients[inactivePoolIndex] = newClient; + + return !AssertAccessOnlyOnSameThread + ? newClient + : newClient.LimitAccessToThread(Thread.CurrentThread.ManagedThreadId, Environment.StackTrace); + } + } + catch + { + //Revert free-slot for any I/O exceptions that can throw (before lock) + lock (writeClients) + { + writeClients[inactivePoolIndex] = null; //free slot + } + throw; + } + } + finally + { + RedisState.DisposeExpiredClients(); + } + } + + private async Task WaitForReader(int msTimeout) + { + // If we're not doing async, no need to create this till we need it. + readAsyncEvent ??= new AsyncManualResetEvent(false); + var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(msTimeout)); + try + { + await readAsyncEvent.WaitAsync(cts.Token); + } + catch (OperationCanceledException) { return false; } + return true; + } + + private async ValueTask GetReadOnlyClientAsync() + { + try + { + var inactivePoolIndex = -1; + do + { + lock (readClients) + { + AssertValidReadOnlyPool(); + + // If it's -1, then we want to try again after a delay of some kind. So if it's NOT negative one, process it... + if ((inactivePoolIndex = GetInActiveReadClient(out var inActiveClient)) != -1) + { + //inActiveClient != null only for Valid InActive Clients + if (inActiveClient != null) + { + ReadPoolIndex++; + inActiveClient.Activate(); + + InitClient(inActiveClient); + + return inActiveClient; + } + else + { + // Still need to be in lock for this! + break; + } + } + } + + if (PoolTimeout.HasValue) + { + // We have a timeout value set - so try to not wait longer than this. + if (!await WaitForReader(PoolTimeout.Value)) + { + throw new TimeoutException(PoolTimeoutError); + } + } + else + { + // Wait forever, so just retry till we get one. + await WaitForReader(RecheckPoolAfterMs); + } + } while (true); // Just keep repeating until we get a slot. + + //Reaches here when there's no Valid InActive Clients + try + { + //inactivePoolIndex = index of reservedSlot || index of invalid client + var existingClient = readClients[inactivePoolIndex]; + if (existingClient != null && existingClient != reservedSlot && existingClient.HadExceptions) + { + RedisState.DeactivateClient(existingClient); + } + + var newClient = InitNewClient(RedisResolver.CreateSlaveClient(inactivePoolIndex)); + + //Put all blocking I/O or potential Exceptions before lock + lock (readClients) + { + //If existingClient at inactivePoolIndex changed (failover) return new client outside of pool + if (readClients[inactivePoolIndex] != existingClient) + { + if (Log.IsDebugEnabled) + Log.Debug("readClients[inactivePoolIndex] != existingClient: {0}".Fmt(readClients[inactivePoolIndex])); + + Interlocked.Increment(ref RedisState.TotalClientsCreatedOutsidePool); + + //Don't handle callbacks for new client outside pool + newClient.ClientManager = null; + return newClient; //return client outside of pool + } + + ReadPoolIndex++; + readClients[inactivePoolIndex] = newClient; + return newClient; + } + } + catch + { + //Revert free-slot for any I/O exceptions that can throw + lock (readClients) + { + readClients[inactivePoolIndex] = null; //free slot + } + throw; + } + } + finally + { + RedisState.DisposeExpiredClients(); + } + } + } } \ No newline at end of file diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.cs b/src/ServiceStack.Redis/PooledRedisClientManager.cs index 64449092..00c1f1a5 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.cs @@ -12,11 +12,8 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; -using System.Threading.Tasks; -using Nito.AsyncEx; using ServiceStack.Caching; using ServiceStack.Logging; using ServiceStack.Text; @@ -36,9 +33,6 @@ public partial class PooledRedisClientManager private const string PoolTimeoutError = "Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use."; - private AsyncManualResetEvent readAsyncEvent; - private AsyncManualResetEvent writeAsyncEvent; - protected readonly int PoolSizeMultiplier = 20; public int RecheckPoolAfterMs = 100; public int? PoolTimeout { get; set; } @@ -217,135 +211,12 @@ protected virtual void OnStart() this.Start(); } - private void pulseAllRead() - { - readAsyncEvent?.Set(); - readAsyncEvent?.Reset(); - Monitor.PulseAll(readClients); - } - - private void pulseAllWrite() - { - writeAsyncEvent?.Set(); - writeAsyncEvent?.Reset(); - Monitor.PulseAll(writeClients); - } - - private async Task waitForWriter(int msTimeout) - { - if (writeAsyncEvent == null) // If we're not doing async, no need to create this till we need it. - writeAsyncEvent = new AsyncManualResetEvent(false); - var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(msTimeout)); - try - { - await writeAsyncEvent.WaitAsync(cts.Token); - } - catch (OperationCanceledException) { return false; } - return true; - } - /// /// Returns a Read/Write client (The default) using the hosts defined in ReadWriteHosts /// /// public IRedisClient GetClient() => GetClientBlocking(); - private async ValueTask GetClientAsync() - { - try - { - var inactivePoolIndex = -1; - do - { - RedisClient inActiveClient; - lock (writeClients) - { - AssertValidReadWritePool(); - - // If it's -1, then we want to try again after a delay of some kind. So if it's NOT negative one, process it... - if ((inactivePoolIndex = GetInActiveWriteClient(out inActiveClient)) != -1) - { - //inActiveClient != null only for Valid InActive Clients - if (inActiveClient != null) - { - WritePoolIndex++; - inActiveClient.Activate(); - - InitClient(inActiveClient); - - return inActiveClient; - } - else - { - // Still need to be in lock for this! - break; - } - } - } - - if (PoolTimeout.HasValue) - { - // We have a timeout value set - so try to not wait longer than this. - if (!await waitForWriter(PoolTimeout.Value)) - { - throw new TimeoutException(PoolTimeoutError); - } - } - else - { - // Wait forever, so just retry till we get one. - await waitForWriter(RecheckPoolAfterMs); - } - } while (true); // Just keep repeating until we get a slot. - - //Reaches here when there's no Valid InActive Clients, but we have a slot for one! - try - { - //inactivePoolIndex = index of reservedSlot || index of invalid client - var existingClient = writeClients[inactivePoolIndex]; - if (existingClient != null && existingClient != reservedSlot && existingClient.HadExceptions) - { - RedisState.DeactivateClient(existingClient); - } - - var newClient = InitNewClient(RedisResolver.CreateMasterClient(inactivePoolIndex)); - - //Put all blocking I/O or potential Exceptions before lock - lock (writeClients) - { - //If existingClient at inactivePoolIndex changed (failover) return new client outside of pool - if (writeClients[inactivePoolIndex] != existingClient) - { - if (Log.IsDebugEnabled) - Log.Debug("writeClients[inactivePoolIndex] != existingClient: {0}".Fmt(writeClients[inactivePoolIndex])); - - return newClient; //return client outside of pool - } - - WritePoolIndex++; - writeClients[inactivePoolIndex] = newClient; - - return (!AssertAccessOnlyOnSameThread) - ? newClient - : newClient.LimitAccessToThread(Thread.CurrentThread.ManagedThreadId, Environment.StackTrace); - } - } - catch - { - //Revert free-slot for any I/O exceptions that can throw (before lock) - lock (writeClients) - { - writeClients[inactivePoolIndex] = null; //free slot - } - throw; - } - } - finally - { - RedisState.DisposeExpiredClients(); - } - } - private RedisClient GetClientBlocking() { try @@ -458,7 +329,7 @@ public override void Dispose() {} private int GetInActiveWriteClient(out RedisClient inactiveClient) { //this will loop through all hosts in readClients once even though there are 2 for loops - //both loops are used to try to get the prefered host according to the round robin algorithm + //both loops are used to try to get the preferred host according to the round robin algorithm var readWriteTotal = RedisResolver.ReadWriteHostsCount; var desiredIndex = WritePoolIndex % writeClients.Length; for (int x = 0; x < readWriteTotal; x++) @@ -494,9 +365,9 @@ private int GetInActiveWriteClient(out RedisClient inactiveClient) /// Returns a ReadOnly client using the hosts defined in ReadOnlyHosts. /// /// - public virtual IRedisClient GetReadOnlyClient() => GetReadOnlyClient(false); + public virtual IRedisClient GetReadOnlyClient() => GetReadOnlyClientBlocking(); - private RedisClient GetReadOnlyClient(bool forAsync) + private RedisClient GetReadOnlyClientBlocking() { try { @@ -656,6 +527,20 @@ private RedisClient InitClient(RedisClient client) return client; } + partial void PulseAllReadAsync(); + private void PulseAllRead() + { + PulseAllReadAsync(); + Monitor.PulseAll(readClients); + } + + partial void PulseAllWriteAsync(); + private void PulseAllWrite() + { + PulseAllWriteAsync(); + Monitor.PulseAll(writeClients); + } + public void DisposeClient(RedisNativeClient client) { lock (readClients) @@ -674,7 +559,7 @@ public void DisposeClient(RedisNativeClient client) client.Deactivate(); } - pulseAllRead(); + PulseAllRead(); return; } } @@ -695,7 +580,7 @@ public void DisposeClient(RedisNativeClient client) client.Deactivate(); } - pulseAllWrite(); + PulseAllWrite(); return; } } @@ -703,12 +588,12 @@ public void DisposeClient(RedisNativeClient client) //Client not found in any pool, pulse both pools. lock (readClients) { - pulseAllRead(); + PulseAllRead(); } lock (writeClients) { - pulseAllWrite(); + PulseAllWrite(); } } @@ -721,7 +606,7 @@ public void DisposeReadOnlyClient(RedisNativeClient client) lock (readClients) { client.Deactivate(); - pulseAllRead(); + PulseAllRead(); } } @@ -734,7 +619,7 @@ public void DisposeWriteClient(RedisNativeClient client) lock (writeClients) { client.Deactivate(); - pulseAllWrite(); + PulseAllWrite(); } } @@ -912,9 +797,7 @@ protected void Dispose(RedisClient redisClient) } catch (Exception ex) { - Log.Error(string.Format( - "Error when trying to dispose of RedisClient to host {0}:{1}", - redisClient.Host, redisClient.Port), ex); + Log.Error($"Error when trying to dispose of RedisClient to host {redisClient.Host}:{redisClient.Port}", ex); } } @@ -954,7 +837,7 @@ public DisposablePooledClient(PooledRedisClientManager clientManager) /// /// access the wrapped client /// - public T Client { get { return client; } } + public T Client => client; /// /// release the wrapped client back to the pool diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj index 08a3aa26..2f611d71 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj @@ -11,7 +11,6 @@ Redis;NoSQL;Client;Distributed;Cache;PubSub;Messaging;Transactions - From d5d1e8025bda1f3cc6b3bc0b728570a98fc08fca Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 26 Feb 2021 08:44:53 +0800 Subject: [PATCH 053/107] Update company name --- src/ServiceStack.Redis/BasicRedisClientManager.Async.cs | 2 +- src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs | 2 +- src/ServiceStack.Redis/BasicRedisClientManager.cs | 2 +- src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs | 2 +- src/ServiceStack.Redis/Generic/RedisClientHash.Generic.cs | 2 +- src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs | 2 +- src/ServiceStack.Redis/Generic/RedisClientList.Generic.cs | 2 +- src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs | 2 +- src/ServiceStack.Redis/Generic/RedisClientSet.Generic.cs | 2 +- .../Generic/RedisClientSortedSet.Generic.Async.cs | 2 +- src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.cs | 2 +- src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs | 2 +- src/ServiceStack.Redis/Generic/RedisTypedClient.cs | 2 +- src/ServiceStack.Redis/Generic/RedisTypedClient_Hash.cs | 2 +- src/ServiceStack.Redis/Generic/RedisTypedClient_List.Async.cs | 2 +- src/ServiceStack.Redis/Generic/RedisTypedClient_List.cs | 2 +- src/ServiceStack.Redis/Generic/RedisTypedClient_Set.Async.cs | 2 +- src/ServiceStack.Redis/Generic/RedisTypedClient_Set.cs | 2 +- .../Generic/RedisTypedClient_SortedSet.Async.cs | 2 +- src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.cs | 2 +- src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs | 2 +- src/ServiceStack.Redis/Generic/RedisTypedTransaction.cs | 2 +- src/ServiceStack.Redis/Pipeline/RedisCommandQueue.cs | 2 +- src/ServiceStack.Redis/PooledRedisClientManager.Async.cs | 2 +- src/ServiceStack.Redis/PooledRedisClientManager.cs | 2 +- src/ServiceStack.Redis/RedisClient.Async.cs | 2 +- src/ServiceStack.Redis/RedisClient.ICacheClient.cs | 2 +- src/ServiceStack.Redis/RedisClient.cs | 2 +- src/ServiceStack.Redis/RedisClientHash.Async.cs | 2 +- src/ServiceStack.Redis/RedisClientHash.cs | 2 +- src/ServiceStack.Redis/RedisClientList.Async.cs | 2 +- src/ServiceStack.Redis/RedisClientList.cs | 2 +- src/ServiceStack.Redis/RedisClientManagerConfig.cs | 2 +- src/ServiceStack.Redis/RedisClientSet.Async.cs | 2 +- src/ServiceStack.Redis/RedisClientSet.cs | 2 +- src/ServiceStack.Redis/RedisClientSortedSet.Async.cs | 2 +- src/ServiceStack.Redis/RedisClientSortedSet.cs | 2 +- src/ServiceStack.Redis/RedisClient_Admin.cs | 2 +- src/ServiceStack.Redis/RedisClient_Hash.Async.cs | 2 +- src/ServiceStack.Redis/RedisClient_Hash.cs | 2 +- src/ServiceStack.Redis/RedisClient_List.Async.cs | 2 +- src/ServiceStack.Redis/RedisClient_List.cs | 2 +- src/ServiceStack.Redis/RedisClient_Set.Async.cs | 2 +- src/ServiceStack.Redis/RedisClient_Set.cs | 2 +- src/ServiceStack.Redis/RedisClient_Slowlog.cs | 2 +- src/ServiceStack.Redis/RedisClient_SortedSet.Async.cs | 2 +- src/ServiceStack.Redis/RedisClient_SortedSet.cs | 2 +- src/ServiceStack.Redis/RedisExtensions.cs | 2 +- src/ServiceStack.Redis/RedisManagerPool.Async.cs | 2 +- src/ServiceStack.Redis/RedisManagerPool.cs | 2 +- src/ServiceStack.Redis/RedisResponseException.cs | 2 +- src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs | 2 +- src/ServiceStack.Redis/Transaction/RedisTransaction.cs | 2 +- 53 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs b/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs index 4bb20247..1b2bd382 100644 --- a/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs +++ b/src/ServiceStack.Redis/BasicRedisClientManager.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs b/src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs index 1409d8a3..aedf60eb 100644 --- a/src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs +++ b/src/ServiceStack.Redis/BasicRedisClientManager.ICacheClient.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/BasicRedisClientManager.cs b/src/ServiceStack.Redis/BasicRedisClientManager.cs index 1c21691a..d42c4f51 100644 --- a/src/ServiceStack.Redis/BasicRedisClientManager.cs +++ b/src/ServiceStack.Redis/BasicRedisClientManager.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs index dfd59db1..86de9a27 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.cs b/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.cs index 795e14fa..20fd91bc 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientHash.Generic.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs index b961d2e1..66b3e596 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientList.Generic.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisClientList.Generic.cs b/src/ServiceStack.Redis/Generic/RedisClientList.Generic.cs index 9ad4222f..61cd0049 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientList.Generic.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientList.Generic.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs index 3c258204..0d0ba19b 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.cs b/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.cs index 2ac86c8f..5c48dd6a 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientSet.Generic.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs b/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs index 6963eda2..fae91472 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.cs b/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.cs index e7f53937..5e7fdf13 100644 --- a/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.cs +++ b/src/ServiceStack.Redis/Generic/RedisClientSortedSet.Generic.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs index 28e11293..c2337b6e 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.cs index 04dde1d4..5e661160 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_Hash.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_Hash.cs index e3c0b3bf..e60432d0 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient_Hash.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_Hash.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_List.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_List.Async.cs index e3c98d05..023590f3 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient_List.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_List.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_List.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_List.cs index 3f4d15ce..144099e2 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient_List.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_List.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.Async.cs index b5b9430f..24889db6 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.cs index b723fa69..d8a0510e 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_Set.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.Async.cs index bd0cd64c..ab4ede2e 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.cs index 4c4e5d69..03155420 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient_SortedSet.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs index fdefb400..33375094 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedTransaction.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Generic/RedisTypedTransaction.cs b/src/ServiceStack.Redis/Generic/RedisTypedTransaction.cs index b3ddb03d..989ef1f4 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedTransaction.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedTransaction.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Pipeline/RedisCommandQueue.cs b/src/ServiceStack.Redis/Pipeline/RedisCommandQueue.cs index f351317f..e21d42ab 100644 --- a/src/ServiceStack.Redis/Pipeline/RedisCommandQueue.cs +++ b/src/ServiceStack.Redis/Pipeline/RedisCommandQueue.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs b/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs index 01bf36d1..79734a35 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/PooledRedisClientManager.cs b/src/ServiceStack.Redis/PooledRedisClientManager.cs index 00c1f1a5..481db23a 100644 --- a/src/ServiceStack.Redis/PooledRedisClientManager.cs +++ b/src/ServiceStack.Redis/PooledRedisClientManager.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient.Async.cs b/src/ServiceStack.Redis/RedisClient.Async.cs index a0c9c36a..4c666eeb 100644 --- a/src/ServiceStack.Redis/RedisClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClient.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient.ICacheClient.cs b/src/ServiceStack.Redis/RedisClient.ICacheClient.cs index 9c974922..21bd3372 100644 --- a/src/ServiceStack.Redis/RedisClient.ICacheClient.cs +++ b/src/ServiceStack.Redis/RedisClient.ICacheClient.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient.cs b/src/ServiceStack.Redis/RedisClient.cs index d1d0d7c4..3f4bd92b 100644 --- a/src/ServiceStack.Redis/RedisClient.cs +++ b/src/ServiceStack.Redis/RedisClient.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClientHash.Async.cs b/src/ServiceStack.Redis/RedisClientHash.Async.cs index 2fed5957..c1bab49f 100644 --- a/src/ServiceStack.Redis/RedisClientHash.Async.cs +++ b/src/ServiceStack.Redis/RedisClientHash.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClientHash.cs b/src/ServiceStack.Redis/RedisClientHash.cs index bc9c877e..5aad5911 100644 --- a/src/ServiceStack.Redis/RedisClientHash.cs +++ b/src/ServiceStack.Redis/RedisClientHash.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClientList.Async.cs b/src/ServiceStack.Redis/RedisClientList.Async.cs index d14d57b3..a995081b 100644 --- a/src/ServiceStack.Redis/RedisClientList.Async.cs +++ b/src/ServiceStack.Redis/RedisClientList.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClientList.cs b/src/ServiceStack.Redis/RedisClientList.cs index 88d0c855..1777723e 100644 --- a/src/ServiceStack.Redis/RedisClientList.cs +++ b/src/ServiceStack.Redis/RedisClientList.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClientManagerConfig.cs b/src/ServiceStack.Redis/RedisClientManagerConfig.cs index 8ca2a21e..e3052805 100644 --- a/src/ServiceStack.Redis/RedisClientManagerConfig.cs +++ b/src/ServiceStack.Redis/RedisClientManagerConfig.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClientSet.Async.cs b/src/ServiceStack.Redis/RedisClientSet.Async.cs index 3ecb5b94..b8d2d55f 100644 --- a/src/ServiceStack.Redis/RedisClientSet.Async.cs +++ b/src/ServiceStack.Redis/RedisClientSet.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClientSet.cs b/src/ServiceStack.Redis/RedisClientSet.cs index b923fa9b..daa58653 100644 --- a/src/ServiceStack.Redis/RedisClientSet.cs +++ b/src/ServiceStack.Redis/RedisClientSet.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs b/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs index 06c2e43f..a7afd56e 100644 --- a/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs +++ b/src/ServiceStack.Redis/RedisClientSortedSet.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClientSortedSet.cs b/src/ServiceStack.Redis/RedisClientSortedSet.cs index 118bb2fb..adde5d12 100644 --- a/src/ServiceStack.Redis/RedisClientSortedSet.cs +++ b/src/ServiceStack.Redis/RedisClientSortedSet.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient_Admin.cs b/src/ServiceStack.Redis/RedisClient_Admin.cs index dfa5fa5c..804df5f1 100644 --- a/src/ServiceStack.Redis/RedisClient_Admin.cs +++ b/src/ServiceStack.Redis/RedisClient_Admin.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient_Hash.Async.cs b/src/ServiceStack.Redis/RedisClient_Hash.Async.cs index f4a93100..9832b5d4 100644 --- a/src/ServiceStack.Redis/RedisClient_Hash.Async.cs +++ b/src/ServiceStack.Redis/RedisClient_Hash.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient_Hash.cs b/src/ServiceStack.Redis/RedisClient_Hash.cs index c2cd0a91..afc71b60 100644 --- a/src/ServiceStack.Redis/RedisClient_Hash.cs +++ b/src/ServiceStack.Redis/RedisClient_Hash.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient_List.Async.cs b/src/ServiceStack.Redis/RedisClient_List.Async.cs index 0f33447f..5b04ac05 100644 --- a/src/ServiceStack.Redis/RedisClient_List.Async.cs +++ b/src/ServiceStack.Redis/RedisClient_List.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient_List.cs b/src/ServiceStack.Redis/RedisClient_List.cs index 067705c7..d723b975 100644 --- a/src/ServiceStack.Redis/RedisClient_List.cs +++ b/src/ServiceStack.Redis/RedisClient_List.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient_Set.Async.cs b/src/ServiceStack.Redis/RedisClient_Set.Async.cs index 698d131f..3e6f3b21 100644 --- a/src/ServiceStack.Redis/RedisClient_Set.Async.cs +++ b/src/ServiceStack.Redis/RedisClient_Set.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient_Set.cs b/src/ServiceStack.Redis/RedisClient_Set.cs index 3a671a8d..c99a00c4 100644 --- a/src/ServiceStack.Redis/RedisClient_Set.cs +++ b/src/ServiceStack.Redis/RedisClient_Set.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient_Slowlog.cs b/src/ServiceStack.Redis/RedisClient_Slowlog.cs index 25df795d..ecde5244 100644 --- a/src/ServiceStack.Redis/RedisClient_Slowlog.cs +++ b/src/ServiceStack.Redis/RedisClient_Slowlog.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient_SortedSet.Async.cs b/src/ServiceStack.Redis/RedisClient_SortedSet.Async.cs index 72350cde..b3a89ff3 100644 --- a/src/ServiceStack.Redis/RedisClient_SortedSet.Async.cs +++ b/src/ServiceStack.Redis/RedisClient_SortedSet.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisClient_SortedSet.cs b/src/ServiceStack.Redis/RedisClient_SortedSet.cs index 00686295..55549114 100644 --- a/src/ServiceStack.Redis/RedisClient_SortedSet.cs +++ b/src/ServiceStack.Redis/RedisClient_SortedSet.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisExtensions.cs b/src/ServiceStack.Redis/RedisExtensions.cs index 2c07d7a7..b4ba79f7 100644 --- a/src/ServiceStack.Redis/RedisExtensions.cs +++ b/src/ServiceStack.Redis/RedisExtensions.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/RedisManagerPool.Async.cs b/src/ServiceStack.Redis/RedisManagerPool.Async.cs index aaf5e33b..25406123 100644 --- a/src/ServiceStack.Redis/RedisManagerPool.Async.cs +++ b/src/ServiceStack.Redis/RedisManagerPool.Async.cs @@ -1,4 +1,4 @@ -//Copyright (c) Service Stack LLC. All Rights Reserved. +//Copyright (c) ServiceStack, Inc. All Rights Reserved. //License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt using ServiceStack.Caching; diff --git a/src/ServiceStack.Redis/RedisManagerPool.cs b/src/ServiceStack.Redis/RedisManagerPool.cs index b5fe4144..6394f037 100644 --- a/src/ServiceStack.Redis/RedisManagerPool.cs +++ b/src/ServiceStack.Redis/RedisManagerPool.cs @@ -1,4 +1,4 @@ -//Copyright (c) Service Stack LLC. All Rights Reserved. +//Copyright (c) ServiceStack, Inc. All Rights Reserved. //License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt using System; diff --git a/src/ServiceStack.Redis/RedisResponseException.cs b/src/ServiceStack.Redis/RedisResponseException.cs index 00e5c537..c7dba956 100644 --- a/src/ServiceStack.Redis/RedisResponseException.cs +++ b/src/ServiceStack.Redis/RedisResponseException.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs b/src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs index 287abc3a..43ca79c8 100644 --- a/src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs +++ b/src/ServiceStack.Redis/Transaction/RedisTransaction.Async.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // diff --git a/src/ServiceStack.Redis/Transaction/RedisTransaction.cs b/src/ServiceStack.Redis/Transaction/RedisTransaction.cs index baedbe36..db2c36b2 100644 --- a/src/ServiceStack.Redis/Transaction/RedisTransaction.cs +++ b/src/ServiceStack.Redis/Transaction/RedisTransaction.cs @@ -5,7 +5,7 @@ // Authors: // Demis Bellot (demis.bellot@gmail.com) // -// Copyright 2013 Service Stack LLC. All Rights Reserved. +// Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. // From c0644069f089e89fd0ec0888f69449b236c1de6d Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 26 Feb 2021 09:06:19 +0800 Subject: [PATCH 054/107] clean up test restores --- build/build-core.proj | 4 ++++ build/build.proj | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/build/build-core.proj b/build/build-core.proj index 35e3f1f9..82110455 100644 --- a/build/build-core.proj +++ b/build/build-core.proj @@ -12,6 +12,7 @@ $(MSBuildProjectDirectory)/.. $(BuildSolutionDir)/src + $(BuildSolutionDir)/tests Release $(BuildSolutionDir)/NuGet/ $(MajorVersion).$(MinorVersion).$(PatchVersion) @@ -40,6 +41,9 @@ + + + diff --git a/build/build.proj b/build/build.proj index c84a1007..300a4087 100644 --- a/build/build.proj +++ b/build/build.proj @@ -12,6 +12,7 @@ $(MSBuildProjectDirectory)/.. $(BuildSolutionDir)/src + $(BuildSolutionDir)/tests Release $(BuildSolutionDir)/NuGet/ $(MajorVersion).$(MinorVersion).$(PatchVersion) @@ -39,6 +40,9 @@ + + + From b58dd1c17a00c3c0a85ca85313f48b6c7b645e59 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Thu, 4 Mar 2021 02:02:34 +0800 Subject: [PATCH 055/107] Fix SetValueAsync/StoreAsync expireIn APIs --- src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs index c2337b6e..8fad6092 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs @@ -200,7 +200,7 @@ static void AssertNotNull(object obj, string name = "key") async ValueTask IRedisTypedClientAsync.SetValueAsync(string key, T entity, TimeSpan expireIn, CancellationToken token) { AssertNotNull(key); - await AsyncClient.SetAsync(key, SerializeValue(entity), token).ConfigureAwait(false); + await AsyncClient.SetAsync(key, SerializeValue(entity), expireIn, token).ConfigureAwait(false); await client.RegisterTypeIdAsync(entity, token).ConfigureAwait(false); } @@ -221,7 +221,7 @@ async ValueTask IRedisTypedClientAsync.SetValueIfExistsAsync(string key async ValueTask IRedisTypedClientAsync.StoreAsync(T entity, TimeSpan expireIn, CancellationToken token) { var urnKey = client.UrnKey(entity); - await AsAsync().SetValueAsync(urnKey, entity, token).ConfigureAwait(false); + await AsAsync().SetValueAsync(urnKey, entity, expireIn, token).ConfigureAwait(false); return entity; } From e834a6aaaf6085e7bd6fcb85721603072e2b3687 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 26 Mar 2021 13:01:45 +0800 Subject: [PATCH 056/107] update link to https://redis.netcore.io --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b6b18fd..251f737e 100644 --- a/README.md +++ b/README.md @@ -452,7 +452,7 @@ The [release of Redis 3.2.0](http://antirez.com/news/104) brings it exciting new [GEO capabilities](http://redis.io/commands/geoadd) which will let you store Lat/Long coordinates in Redis and query locations within a specified radius. To demonstrate this functionality we've created a new [Redis GEO Live Demo](https://github.com/ServiceStackApps/redis-geo) which lets you click on anywhere in -the U.S. to find the list of nearest cities within a given radius, Live Demo at: http://redisgeo.servicestack.net +the U.S. to find the list of nearest cities within a given radius, Live Demo at: https://redis.netcore.io ## Generic APIs for calling Custom Redis commands From 38c88dc6aacccc1f28626823bc888e52639a0c93 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 26 Mar 2021 15:49:23 +0800 Subject: [PATCH 057/107] Update SO link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 251f737e..a956bb59 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Follow [@ServiceStack](https://twitter.com/servicestack) or [view the docs](https://docs.servicestack.net), use [StackOverflow](http://stackoverflow.com/questions/ask) or the [Customer Forums](https://forums.servicestack.net/) for support. +Follow [@ServiceStack](https://twitter.com/servicestack), [view the docs](https://docs.servicestack.net), use [StackOverflow](https://stackoverflow.com/questions/ask?tags=servicestack,servicestack.redis) or [Customer Forums](https://forums.servicestack.net/) for support. # C#/.NET Client for Redis From ae0a47079b83bec872af97f34c5a23ed05b57656 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 14 Apr 2021 23:41:41 +0800 Subject: [PATCH 058/107] Update RedisPubSubServer.cs --- src/ServiceStack.Redis/RedisPubSubServer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ServiceStack.Redis/RedisPubSubServer.cs b/src/ServiceStack.Redis/RedisPubSubServer.cs index 7db29bac..01208cc1 100644 --- a/src/ServiceStack.Redis/RedisPubSubServer.cs +++ b/src/ServiceStack.Redis/RedisPubSubServer.cs @@ -209,6 +209,9 @@ private void DisposeHeartbeatTimer() try { + if (Log.IsDebugEnabled) + Log.DebugFormat("RedisPubServer.DisposeHeartbeatTimer()"); + heartbeatTimer.Dispose(); } catch (Exception ex) @@ -548,6 +551,9 @@ public virtual void Dispose() { if (Interlocked.CompareExchange(ref status, 0, 0) == Status.Disposed) return; + + if (Log.IsDebugEnabled) + Log.Debug("RedisPubServer.Dispose()..."); Stop(); From 771870d7c65880c9bb461242cffd5b52ac57a170 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 20 Apr 2021 23:26:49 +0800 Subject: [PATCH 059/107] Add SentinelHostFilter for customizing Sentinel Host configuration strings --- src/ServiceStack.Redis/RedisSentinel.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/ServiceStack.Redis/RedisSentinel.cs b/src/ServiceStack.Redis/RedisSentinel.cs index 25965ef2..bdac92c8 100644 --- a/src/ServiceStack.Redis/RedisSentinel.cs +++ b/src/ServiceStack.Redis/RedisSentinel.cs @@ -42,10 +42,15 @@ public class RedisSentinel : IRedisSentinel public Func RedisManagerFactory { get; set; } /// - /// Configure the Redis Connection String to use for a Redis Client Host + /// Configure the Redis Connection String to use for a Redis Instance Host /// public Func HostFilter { get; set; } + /// + /// Configure the Redis Connection String to use for a Redis Sentinel Host + /// + public Func SentinelHostFilter { get; set; } + /// /// The configured Redis Client Manager this Sentinel managers /// @@ -185,15 +190,18 @@ public List GetActiveSentinelHosts(IEnumerable sentinelHosts) var endpoint = sentinelHost.ToRedisEndpoint(defaultPort: RedisConfig.DefaultPortSentinel); using (var sentinelWorker = new RedisSentinelWorker(this, endpoint)) { - var activeHosts = sentinelWorker.GetSentinelHosts(MasterName); - if (!activeSentinelHosts.Contains(sentinelHost)) activeSentinelHosts.Add(sentinelHost); + var activeHosts = sentinelWorker.GetSentinelHosts(MasterName); foreach (var activeHost in activeHosts) { if (!activeSentinelHosts.Contains(activeHost)) - activeSentinelHosts.Add(activeHost); + { + activeSentinelHosts.Add(SentinelHostFilter != null + ? SentinelHostFilter(activeHost) + : activeHost); + } } } From 78376b201e3b6306276b07d26cfbf7c8df4c5311 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 21 Apr 2021 00:22:56 +0800 Subject: [PATCH 060/107] Update RedisSentinel Password example --- .../MasterFailoverWithPassword.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/Console.Tests/MasterFailoverWithPassword.cs b/tests/Console.Tests/MasterFailoverWithPassword.cs index f9e0257b..a77b7093 100644 --- a/tests/Console.Tests/MasterFailoverWithPassword.cs +++ b/tests/Console.Tests/MasterFailoverWithPassword.cs @@ -9,23 +9,25 @@ public class MasterFailoverWithPassword { public void Execute() { + string AddPassword(string host) => $"password@{host}"; + var sentinelHosts = new[] { "127.0.0.1:26380", "127.0.0.1:26381", "127.0.0.1:26382" }; - var sentinel = new RedisSentinel(sentinelHosts, masterName: "mymaster"); - sentinel.HostFilter = host => "password@{0}".Fmt(host); + var sentinel = new RedisSentinel(sentinelHosts.Map(AddPassword), masterName: "mymaster") { + HostFilter = AddPassword, + SentinelHostFilter = AddPassword, + }; var manager = sentinel.Start(); - sentinel.OnWorkerError = exception => Console.WriteLine(exception); + sentinel.OnWorkerError = Console.WriteLine; while (true) { try { const string RedisKey = "my Name"; - using (var client = manager.GetClient()) - { - var result = client.Get(RedisKey); - Console.WriteLine("Redis Key: {0} \t Port: {1}", result, client.Port); - } + using var client = manager.GetClient(); + var result = client.Get(RedisKey); + Console.WriteLine("Redis Key: {0} \t Port: {1}", result, client.Port); } catch (Exception ex) { From fd307c747fb15a00c878ab13ad8bb5eeef3ec80a Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 21 Apr 2021 00:23:47 +0800 Subject: [PATCH 061/107] String interpolation + typos --- src/ServiceStack.Redis/RedisSentinelWorker.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ServiceStack.Redis/RedisSentinelWorker.cs b/src/ServiceStack.Redis/RedisSentinelWorker.cs index 4525a204..106b9898 100644 --- a/src/ServiceStack.Redis/RedisSentinelWorker.cs +++ b/src/ServiceStack.Redis/RedisSentinelWorker.cs @@ -12,8 +12,9 @@ internal class RedisSentinelWorker : IDisposable static int IdCounter = 0; public int Id { get; } - private readonly object oLock = new object(); + private readonly object oLock = new(); + private readonly RedisEndpoint sentinelEndpoint; private readonly RedisSentinel sentinel; private readonly RedisClient sentinelClient; private RedisPubSubServer sentinePubSub; @@ -32,7 +33,7 @@ public RedisSentinelWorker(RedisSentinel sentinel, RedisEndpoint sentinelEndpoin }; if (Log.IsDebugEnabled) - Log.Debug("Set up Redis Sentinel on {0}".Fmt(sentinelEndpoint)); + Log.Debug($"Set up Redis Sentinel on {sentinelEndpoint}"); } /// @@ -43,7 +44,7 @@ public RedisSentinelWorker(RedisSentinel sentinel, RedisEndpoint sentinelEndpoin private void SentinelMessageReceived(string channel, string message) { if (Log.IsDebugEnabled) - Log.Debug("Received '{0}' on channel '{1}' from Sentinel".Fmt(channel, message)); + Log.Debug($"Received '{channel}' on channel '{message}' from Sentinel"); // {+|-}sdown is the event for server coming up or down var c = channel.ToLower(); @@ -61,7 +62,7 @@ private void SentinelMessageReceived(string channel, string message) || (sentinel.ResetWhenObjectivelyDown && isObjectivelyDown)) { if (Log.IsDebugEnabled) - Log.Debug("Sentinel detected server down/up '{0}' with message: {1}".Fmt(channel, message)); + Log.Debug($"Sentinel detected server down/up '{channel}' with message: {message}"); sentinel.ResetClients(); } @@ -187,7 +188,7 @@ public void BeginListeningForConfigurationChanges() } } - this.sentinePubSub.Start(); + this.sentinelPubSub.Start(); } catch (Exception ex) { @@ -206,7 +207,7 @@ public void ForceMasterFailover(string masterName) public void Dispose() { - new IDisposable[] { this.sentinelClient, sentinePubSub }.Dispose(Log); + new IDisposable[] { this.sentinelClient, sentinelPubSub }.Dispose(Log); } } } From fe65bd6d0893cd0b05b6a42587cfa92f3b24f7e8 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 21 Apr 2021 00:24:29 +0800 Subject: [PATCH 062/107] only listen for messages from current sentinel host --- src/ServiceStack.Redis/RedisSentinelWorker.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ServiceStack.Redis/RedisSentinelWorker.cs b/src/ServiceStack.Redis/RedisSentinelWorker.cs index 106b9898..978ebf37 100644 --- a/src/ServiceStack.Redis/RedisSentinelWorker.cs +++ b/src/ServiceStack.Redis/RedisSentinelWorker.cs @@ -17,7 +17,7 @@ internal class RedisSentinelWorker : IDisposable private readonly RedisEndpoint sentinelEndpoint; private readonly RedisSentinel sentinel; private readonly RedisClient sentinelClient; - private RedisPubSubServer sentinePubSub; + private RedisPubSubServer sentinelPubSub; public Action OnSentinelError; @@ -25,6 +25,7 @@ public RedisSentinelWorker(RedisSentinel sentinel, RedisEndpoint sentinelEndpoin { this.Id = Interlocked.Increment(ref IdCounter); this.sentinel = sentinel; + this.sentinelEndpoint = sentinelEndpoint; this.sentinelClient = new RedisClient(sentinelEndpoint) { Db = 0, //Sentinel Servers doesn't support DB, reset to 0 ConnectTimeout = sentinel.SentinelWorkerConnectTimeoutMs, @@ -167,18 +168,19 @@ public void BeginListeningForConfigurationChanges() { lock (oLock) { - if (this.sentinePubSub == null) + if (this.sentinelPubSub == null) { - var sentinelManager = new BasicRedisClientManager(sentinel.SentinelHosts, sentinel.SentinelHosts) + var currentSentinelHost = new[] {sentinelEndpoint}; + var sentinelManager = new BasicRedisClientManager(currentSentinelHost, currentSentinelHost) { //Use BasicRedisResolver which doesn't validate non-Master Sentinel instances - RedisResolver = new BasicRedisResolver(sentinel.SentinelEndpoints, sentinel.SentinelEndpoints) + RedisResolver = new BasicRedisResolver(currentSentinelHost, currentSentinelHost) }; if (Log.IsDebugEnabled) Log.Debug($"Starting subscription to {sentinel.SentinelHosts.ToArray()}, replicas: {sentinel.SentinelHosts.ToArray()}..."); - this.sentinePubSub = new RedisPubSubServer(sentinelManager) + this.sentinelPubSub = new RedisPubSubServer(sentinelManager) { HeartbeatInterval = null, IsSentinelSubscription = true, From c9f27d1cd443626570f19a982ed6a49a7f93a3a2 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 21 Apr 2021 02:36:17 +0800 Subject: [PATCH 063/107] bump to v5.11 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index abe92e64..9b478479 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.10.5 + 5.11.0 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 5372a1ee..9c5df356 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.10.5 + 5.11.0 latest false From 1e1b80567455f4dda8d68ed6b76f5693678f6848 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 4 May 2021 02:14:19 +0800 Subject: [PATCH 064/107] Add callback to capture events in RedisPubServer --- src/ServiceStack.Redis/RedisPubSubServer.cs | 67 +++++++++++++++------ 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/src/ServiceStack.Redis/RedisPubSubServer.cs b/src/ServiceStack.Redis/RedisPubSubServer.cs index 01208cc1..a3ceacfd 100644 --- a/src/ServiceStack.Redis/RedisPubSubServer.cs +++ b/src/ServiceStack.Redis/RedisPubSubServer.cs @@ -1,8 +1,6 @@ using System; using System.Diagnostics; -using System.Text; using System.Threading; -using System.Threading.Tasks; using ServiceStack.Logging; using ServiceStack.Text; @@ -34,6 +32,7 @@ public class RedisPubSubServer : IRedisPubSubServer public Action OnControlCommand { get; set; } public Action OnUnSubscribe { get; set; } + public Action OnEvent { get; set; } public Action OnError { get; set; } public Action OnFailover { get; set; } public bool IsSentinelSubscription { get; set; } @@ -97,6 +96,8 @@ public IRedisPubSubServer Start() //Only 1 thread allowed past if (Interlocked.CompareExchange(ref status, Status.Starting, Status.Stopped) == Status.Stopped) //Should only be 1 thread past this point { + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} Stopped] Start()> Stopped -> Starting"); + var initErrors = 0; bool hasInit = false; while (!hasInit) @@ -108,6 +109,7 @@ public IRedisPubSubServer Start() } catch (Exception ex) { + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {GetStatus()}] Start().Init()> Exception: {ex.Message}"); OnError?.Invoke(ex); SleepBackOffMultiplier(initErrors++); } @@ -188,6 +190,8 @@ void SendHeartbeat(object state) if (DateTime.UtcNow - new DateTime(lastHeartbeatTicks) > HeartbeatTimeout) { currentStatus = Interlocked.CompareExchange(ref status, 0, 0); + + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {Status.GetStatus(currentStatus)}] SendHeartbeat()> Exceeded HeartbeatTimeout"); if (currentStatus == Status.Started) { Restart(); @@ -210,7 +214,7 @@ private void DisposeHeartbeatTimer() try { if (Log.IsDebugEnabled) - Log.DebugFormat("RedisPubServer.DisposeHeartbeatTimer()"); + Log.Debug("RedisPubServer.DisposeHeartbeatTimer()"); heartbeatTimer.Dispose(); } @@ -227,6 +231,8 @@ private void RunLoop() if (Interlocked.CompareExchange(ref status, Status.Started, Status.Starting) != Status.Starting) return; Interlocked.Increment(ref timesStarted); + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} Started] RunLoop().Stop> Starting -> Started, timesStarted: {timesStarted}"); + try { //RESET @@ -280,22 +286,31 @@ bool IsCtrlMessage(byte[] msg) if (Log.IsDebugEnabled) Log.Debug("Stop Command Issued"); + var holdStatus = GetStatus(); + Interlocked.CompareExchange(ref status, Status.Stopping, Status.Started); + + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {holdStatus}] RunLoop().Stop> Started -> Stopping"); try { if (Log.IsDebugEnabled) Log.Debug("UnSubscribe From All Channels..."); + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {GetStatus()}] RunLoop().Stop> subscription.UnSubscribeFromAllChannels()"); + // ReSharper disable once AccessToDisposedClosure subscription.UnSubscribeFromAllChannels(); //Un block thread. } finally { + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {GetStatus()}] RunLoop().Stop> Stopping -> Stopped"); Interlocked.CompareExchange(ref status, Status.Stopped, Status.Stopping); } return; case Operation.Reset: + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {GetStatus()}] RunLoop().Reset> subscription.UnSubscribeFromAllChannels()"); + // ReSharper disable once AccessToDisposedClosure subscription.UnSubscribeFromAllChannels(); //Un block thread. return; @@ -330,10 +345,14 @@ bool IsCtrlMessage(byte[] msg) lastExMsg = ex.Message; Interlocked.Increment(ref noOfErrors); Interlocked.Increment(ref noOfContinuousErrors); + + var holdStatus = GetStatus(); if (Interlocked.CompareExchange(ref status, Status.Stopped, Status.Started) != Status.Started) Interlocked.CompareExchange(ref status, Status.Stopped, Status.Stopping); + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {holdStatus}] RunLoop().Stop> Started|Stopping -> Stopped"); + OnStop?.Invoke(); OnError?.Invoke(ex); @@ -343,6 +362,8 @@ bool IsCtrlMessage(byte[] msg) { if (WaitBeforeNextRestart != null) TaskUtils.Sleep(WaitBeforeNextRestart.Value); + + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {GetStatus()}] RunLoop().AutoRestart> Start()"); Start(); } } @@ -361,6 +382,8 @@ private void Stop(bool shouldRestart) if (Interlocked.CompareExchange(ref status, Status.Stopping, Status.Started) == Status.Started) { + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {GetStatus()}] Stop()> Started -> Stopping"); + if (Log.IsDebugEnabled) Log.Debug("Stopping RedisPubSubServer..."); @@ -392,7 +415,7 @@ private void NotifyAllSubscribers(string commandType=null) catch (Exception ex) { OnError?.Invoke(ex); - Log.Warn("Could not send '{0}' message to bg thread: {1}".Fmt(msg, ex.Message)); + Log.WarnFormat("Could not send '{0}' message to bg thread: {1}", msg, ex.Message); } } @@ -447,10 +470,12 @@ private void KillBgThreadIfExists() { #if !NETSTANDARD2_0 //Ideally we shouldn't get here, but lets try our hardest to clean it up + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {GetStatus()}] KillBgThreadIfExists()> bgThread.Interrupt()"); Log.Warn("Interrupting previous Background Thread: " + bgThread.Name); bgThread.Interrupt(); if (!bgThread.Join(TimeSpan.FromSeconds(3))) { + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {GetStatus()}] KillBgThreadIfExists()> bgThread.Abort()"); Log.Warn(bgThread.Name + " just wont die, so we're now aborting it..."); bgThread.Abort(); } @@ -471,7 +496,7 @@ private void SleepBackOffMultiplier(int continuousErrorsCount) maxSleepMs); if (Log.IsDebugEnabled) - Log.Debug("Sleeping for {0}ms after {1} continuous errors".Fmt(nextTry, continuousErrorsCount)); + Log.DebugFormat("Sleeping for {0}ms after {1} continuous errors", nextTry, continuousErrorsCount); TaskUtils.Sleep(nextTry); } @@ -514,26 +539,22 @@ class Status //dep-free copy of WorkerStatus public const int Stopping = 1; public const int Starting = 2; public const int Started = 3; - } - public string GetStatus() - { - switch (Interlocked.CompareExchange(ref status, 0, 0)) + public static string GetStatus(int status) { - case Status.Disposed: - return "Disposed"; - case Status.Stopped: - return "Stopped"; - case Status.Stopping: - return "Stopping"; - case Status.Starting: - return "Starting"; - case Status.Started: - return "Started"; + return status switch { + Disposed => nameof(Disposed), + Stopped => nameof(Stopped), + Stopping => nameof(Stopping), + Starting => nameof(Starting), + Started => nameof(Started), + _ => throw new NotSupportedException("Unknown status: " + status) + }; } - return null; } + public string GetStatus() => Status.GetStatus(Interlocked.CompareExchange(ref status, 0, 0)); + public string GetStatsDescription() { var sb = StringBuilderCache.Allocate(); @@ -555,11 +576,17 @@ public virtual void Dispose() if (Log.IsDebugEnabled) Log.Debug("RedisPubServer.Dispose()..."); + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {GetStatus()}] Dispose()>"); + Stop(); + var holdStatus = GetStatus(); + if (Interlocked.CompareExchange(ref status, Status.Disposed, Status.Stopped) != Status.Stopped) Interlocked.CompareExchange(ref status, Status.Disposed, Status.Stopping); + OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {holdStatus}] Dispose()> -> Disposed"); + try { OnDispose?.Invoke(); From 321e944c8a1257c0fd771273368d519e3a0d9c91 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 4 May 2021 04:37:50 +0800 Subject: [PATCH 065/107] bump to v5.11.1 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 9b478479..9cae0c4b 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.11.0 + 5.11.1 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 9c5df356..3f70834f 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.11.0 + 5.11.1 latest false From b9a09632e0c5a9c46223854e8df9631595d77d6c Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 4 May 2021 14:17:03 +0800 Subject: [PATCH 066/107] typo --- src/ServiceStack.Redis/RedisNativeClient.Async.cs | 2 +- src/ServiceStack.Redis/RedisNativeClient.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ServiceStack.Redis/RedisNativeClient.Async.cs b/src/ServiceStack.Redis/RedisNativeClient.Async.cs index c7ff1e89..d265c0ae 100644 --- a/src/ServiceStack.Redis/RedisNativeClient.Async.cs +++ b/src/ServiceStack.Redis/RedisNativeClient.Async.cs @@ -709,7 +709,7 @@ ValueTask IRedisNativeClientAsync.ClientKillAsync(string clientAddr, Cancellatio => SendExpectSuccessAsync(token, Commands.Client, Commands.Kill, clientAddr.ToUtf8Bytes()); ValueTask IRedisNativeClientAsync.ClientKillAsync(string addr, string id, string type, string skipMe, CancellationToken token) - => SendExpectLongAsync(token, ClientKillPerpareArgs(addr, id, type, skipMe)); + => SendExpectLongAsync(token, ClientKillPrepareArgs(addr, id, type, skipMe)); ValueTask IRedisNativeClientAsync.ClientListAsync(CancellationToken token) => SendExpectDataAsync(token, Commands.Client, Commands.List); diff --git a/src/ServiceStack.Redis/RedisNativeClient.cs b/src/ServiceStack.Redis/RedisNativeClient.cs index 4493cc6c..220f0405 100644 --- a/src/ServiceStack.Redis/RedisNativeClient.cs +++ b/src/ServiceStack.Redis/RedisNativeClient.cs @@ -897,10 +897,10 @@ public void ClientKill(string clientAddr) public long ClientKill(string addr = null, string id = null, string type = null, string skipMe = null) { - return SendExpectLong(ClientKillPerpareArgs(addr, id, type, skipMe)); + return SendExpectLong(ClientKillPrepareArgs(addr, id, type, skipMe)); } - static byte[][] ClientKillPerpareArgs(string addr, string id, string type, string skipMe) + static byte[][] ClientKillPrepareArgs(string addr, string id, string type, string skipMe) { var cmdWithArgs = new List { From b96308fd8906d1a6d86dd853ef239bc68c347c5b Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 25 May 2021 22:01:15 +0800 Subject: [PATCH 067/107] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a956bb59..94ee2e01 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ With each client providing different layers of abstraction: ### API Overview -[![Redis Client API](https://servicestack.net/img/redis-annotated-preview.png)](https://servicestack.net/img/redis-annotated.png) +[![Redis Client API](https://servicestack.net/images/redis-annotated-preview.png)](https://servicestack.net/images/redis-annotated.png) ## Redis Connection Strings From db8e14446c71c185e8bd115491a10c46c2329950 Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 9 Jun 2021 12:43:41 +1000 Subject: [PATCH 068/107] Change `DeleteAll` to use `SSCAN` to page through data instead of pulling all keys into memory all at once. This will reduce memory, but make the command chatty for very large typed sets. --- .../Generic/RedisTypedClient.Async.cs | 15 +++++++++++++-- .../Generic/RedisTypedClient.cs | 17 +++++++++++++---- src/ServiceStack.Redis/RedisClient.Async.cs | 19 +++++++++++++++---- src/ServiceStack.Redis/RedisClient.cs | 18 ++++++++++++++---- 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs index 8fad6092..6e6725ff 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs @@ -131,13 +131,24 @@ async Task IEntityStoreAsync.DeleteByIdsAsync(IEnumerable ids, CancellationTo async Task IEntityStoreAsync.DeleteAllAsync(CancellationToken token) { - var ids = await AsyncClient.GetAllItemsFromSetAsync(this.TypeIdsSetKey, token).ConfigureAwait(false); + await DeleteAllAsync(0,1000, token).ConfigureAwait(false); + } + + private async Task DeleteAllAsync(ulong cursor, int pageSize, CancellationToken token) + { + var scanResult = await AsyncNative.SScanAsync(this.TypeIdsSetKey, cursor, pageSize, token: token).ConfigureAwait(false); + var lastCursor = scanResult.Cursor; + var ids = scanResult.Results.Select(x => Encoding.UTF8.GetString(x)).ToList(); var urnKeys = ids.Map(t => client.UrnKey(t)); if (urnKeys.Count > 0) { await AsyncClient.RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); - await AsyncClient.RemoveEntryAsync(new[] { this.TypeIdsSetKey }, token).ConfigureAwait(false); } + + if (lastCursor != 0) + await DeleteAllAsync(lastCursor, pageSize, token).ConfigureAwait(false); + else + await AsyncClient.RemoveEntryAsync(new[] { this.TypeIdsSetKey }, token).ConfigureAwait(false); } async ValueTask> IRedisTypedClientAsync.GetValuesAsync(List keys, CancellationToken token) diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.cs index 5e661160..e5a5d74c 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.cs @@ -466,16 +466,25 @@ public void DeleteByIds(IEnumerable ids) } } - public void DeleteAll() + private void DeleteAll(ulong cursor, int pageSize) { - var ids = client.GetAllItemsFromSet(this.TypeIdsSetKey); + var scanResult = client.SScan(this.TypeIdsSetKey, cursor, pageSize); + var resultCursor = scanResult.Cursor; + var ids = scanResult.Results.Select(x => Encoding.UTF8.GetString(x)).ToList(); var urnKeys = ids.Map(t => client.UrnKey(t)); if (urnKeys.Count > 0) { - this.RemoveEntry(urnKeys.ToArray()); - this.RemoveEntry(this.TypeIdsSetKey); } + if(resultCursor != 0) + DeleteAll(resultCursor,1000); + else + this.RemoveEntry(this.TypeIdsSetKey); + } + + public void DeleteAll() + { + DeleteAll(0,1000); } #endregion diff --git a/src/ServiceStack.Redis/RedisClient.Async.cs b/src/ServiceStack.Redis/RedisClient.Async.cs index 4c666eeb..d3c5582d 100644 --- a/src/ServiceStack.Redis/RedisClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClient.Async.cs @@ -658,15 +658,26 @@ async Task IEntityStoreAsync.DeleteByIdsAsync(ICollection ids, CancellationTo } async Task IEntityStoreAsync.DeleteAllAsync(CancellationToken token) + { + await DeleteAllAsync(0, 1000, token).ConfigureAwait(false); + } + + private async Task DeleteAllAsync(ulong cursor, int pageSize, CancellationToken token) { var typeIdsSetKey = this.GetTypeIdsSetKey(); - var ids = await AsAsync().GetAllItemsFromSetAsync(typeIdsSetKey, token).ConfigureAwait(false); - if (ids.Count > 0) + var scanResult = await NativeAsync.SScanAsync(typeIdsSetKey, cursor, pageSize, token: token).ConfigureAwait(false); + var lastCursor = scanResult.Cursor; + var ids = scanResult.Results.Select(x => x.FromUtf8Bytes()); + var urnKeys = ids.Map(t => AsAsync().UrnKey(t)); + if (urnKeys.Count > 0) { - var urnKeys = ids.ToList().ConvertAll(UrnKey); await AsAsync().RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); - await AsAsync().RemoveAsync(typeIdsSetKey, token).ConfigureAwait(false); } + + if (lastCursor != 0) + await DeleteAllAsync(lastCursor, pageSize, token).ConfigureAwait(false); + else + await AsAsync().RemoveEntryAsync(new[] { typeIdsSetKey }, token).ConfigureAwait(false); } ValueTask> IRedisClientAsync.SearchSortedSetAsync(string setId, string start, string end, int? skip, int? take, CancellationToken token) diff --git a/src/ServiceStack.Redis/RedisClient.cs b/src/ServiceStack.Redis/RedisClient.cs index 3f4bd92b..0d9d5994 100644 --- a/src/ServiceStack.Redis/RedisClient.cs +++ b/src/ServiceStack.Redis/RedisClient.cs @@ -830,15 +830,25 @@ public void DeleteByIds(ICollection ids) } public void DeleteAll() + { + DeleteAll(0,1000); + } + + private void DeleteAll(ulong cursor, int pageSize) { var typeIdsSetKey = this.GetTypeIdsSetKey(); - var ids = this.GetAllItemsFromSet(typeIdsSetKey); - if (ids.Count > 0) + var scanResult = this.SScan(typeIdsSetKey, cursor, pageSize); + var resultCursor = scanResult.Cursor; + var ids = scanResult.Results.Select(x => x.FromUtf8Bytes()); + var urnKeys = ids.Map(t => this.UrnKey(t)); + if (urnKeys.Count > 0) { - var urnKeys = ids.ToList().ConvertAll(UrnKey); this.RemoveEntry(urnKeys.ToArray()); - this.Remove(typeIdsSetKey); } + if(resultCursor != 0) + DeleteAll(resultCursor, 1000); + else + this.RemoveEntry(typeIdsSetKey); } public RedisClient CloneClient() From a7fb93e86bae6e879dd938a6bb9a063ec41f6675 Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 9 Jun 2021 12:50:27 +1000 Subject: [PATCH 069/107] Use pageSize consistently. --- src/ServiceStack.Redis/Generic/RedisTypedClient.cs | 2 +- src/ServiceStack.Redis/RedisClient.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.cs index e5a5d74c..62d17abc 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.cs @@ -477,7 +477,7 @@ private void DeleteAll(ulong cursor, int pageSize) this.RemoveEntry(urnKeys.ToArray()); } if(resultCursor != 0) - DeleteAll(resultCursor,1000); + DeleteAll(resultCursor,pageSize); else this.RemoveEntry(this.TypeIdsSetKey); } diff --git a/src/ServiceStack.Redis/RedisClient.cs b/src/ServiceStack.Redis/RedisClient.cs index 0d9d5994..64f0ee2a 100644 --- a/src/ServiceStack.Redis/RedisClient.cs +++ b/src/ServiceStack.Redis/RedisClient.cs @@ -846,7 +846,7 @@ private void DeleteAll(ulong cursor, int pageSize) this.RemoveEntry(urnKeys.ToArray()); } if(resultCursor != 0) - DeleteAll(resultCursor, 1000); + DeleteAll(resultCursor, pageSize); else this.RemoveEntry(typeIdsSetKey); } From 0e63be8c99083cb5f69ba279e6538202dcd27ca2 Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 9 Jun 2021 14:39:07 +1000 Subject: [PATCH 070/107] Use RedisConfig for new static config option. Add tests for deletes bigger than batch size. Change to use while loop rather than recursion. --- .../Generic/RedisTypedClient.Async.cs | 25 ++++++------ .../Generic/RedisTypedClient.cs | 25 +++++++----- src/ServiceStack.Redis/RedisClient.Async.cs | 25 ++++++------ src/ServiceStack.Redis/RedisClient.cs | 25 +++++++----- src/ServiceStack.Redis/RedisConfig.cs | 5 +++ .../Generic/RedisTypedClientTests.Async.cs | 40 +++++++++++++++++++ .../Generic/RedisTypedClientTests.cs | 38 ++++++++++++++++++ 7 files changed, 137 insertions(+), 46 deletions(-) diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs index 6e6725ff..8a6eac1c 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs @@ -131,24 +131,25 @@ async Task IEntityStoreAsync.DeleteByIdsAsync(IEnumerable ids, CancellationTo async Task IEntityStoreAsync.DeleteAllAsync(CancellationToken token) { - await DeleteAllAsync(0,1000, token).ConfigureAwait(false); + await DeleteAllAsync(0,RedisConfig.DeleteAllBatchSize, token).ConfigureAwait(false); } private async Task DeleteAllAsync(ulong cursor, int pageSize, CancellationToken token) { - var scanResult = await AsyncNative.SScanAsync(this.TypeIdsSetKey, cursor, pageSize, token: token).ConfigureAwait(false); - var lastCursor = scanResult.Cursor; - var ids = scanResult.Results.Select(x => Encoding.UTF8.GetString(x)).ToList(); - var urnKeys = ids.Map(t => client.UrnKey(t)); - if (urnKeys.Count > 0) + var callCount = 0; + while (cursor != 0 || callCount == 0) { - await AsyncClient.RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); + var scanResult = await AsyncNative.SScanAsync(this.TypeIdsSetKey, cursor, pageSize, token: token).ConfigureAwait(false); + callCount++; + cursor = scanResult.Cursor; + var ids = scanResult.Results.Select(x => Encoding.UTF8.GetString(x)).ToList(); + var urnKeys = ids.Map(t => client.UrnKey(t)); + if (urnKeys.Count > 0) + { + await AsyncClient.RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); + } } - - if (lastCursor != 0) - await DeleteAllAsync(lastCursor, pageSize, token).ConfigureAwait(false); - else - await AsyncClient.RemoveEntryAsync(new[] { this.TypeIdsSetKey }, token).ConfigureAwait(false); + await AsyncClient.RemoveEntryAsync(new[] { this.TypeIdsSetKey }, token).ConfigureAwait(false); } async ValueTask> IRedisTypedClientAsync.GetValuesAsync(List keys, CancellationToken token) diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.cs index 62d17abc..ececa68b 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.cs @@ -468,23 +468,26 @@ public void DeleteByIds(IEnumerable ids) private void DeleteAll(ulong cursor, int pageSize) { - var scanResult = client.SScan(this.TypeIdsSetKey, cursor, pageSize); - var resultCursor = scanResult.Cursor; - var ids = scanResult.Results.Select(x => Encoding.UTF8.GetString(x)).ToList(); - var urnKeys = ids.Map(t => client.UrnKey(t)); - if (urnKeys.Count > 0) + var callCount = 0; + while (cursor != 0 || callCount == 0) { - this.RemoveEntry(urnKeys.ToArray()); + var scanResult = client.SScan(this.TypeIdsSetKey, cursor, pageSize); + callCount++; + cursor = scanResult.Cursor; + var ids = scanResult.Results.Select(x => Encoding.UTF8.GetString(x)).ToList(); + var urnKeys = ids.Map(t => client.UrnKey(t)); + if (urnKeys.Count > 0) + { + this.RemoveEntry(urnKeys.ToArray()); + } } - if(resultCursor != 0) - DeleteAll(resultCursor,pageSize); - else - this.RemoveEntry(this.TypeIdsSetKey); + + this.RemoveEntry(this.TypeIdsSetKey); } public void DeleteAll() { - DeleteAll(0,1000); + DeleteAll(0,RedisConfig.DeleteAllBatchSize); } #endregion diff --git a/src/ServiceStack.Redis/RedisClient.Async.cs b/src/ServiceStack.Redis/RedisClient.Async.cs index d3c5582d..da5b51dc 100644 --- a/src/ServiceStack.Redis/RedisClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClient.Async.cs @@ -659,25 +659,26 @@ async Task IEntityStoreAsync.DeleteByIdsAsync(ICollection ids, CancellationTo async Task IEntityStoreAsync.DeleteAllAsync(CancellationToken token) { - await DeleteAllAsync(0, 1000, token).ConfigureAwait(false); + await DeleteAllAsync(0, RedisConfig.DeleteAllBatchSize, token).ConfigureAwait(false); } private async Task DeleteAllAsync(ulong cursor, int pageSize, CancellationToken token) { var typeIdsSetKey = this.GetTypeIdsSetKey(); - var scanResult = await NativeAsync.SScanAsync(typeIdsSetKey, cursor, pageSize, token: token).ConfigureAwait(false); - var lastCursor = scanResult.Cursor; - var ids = scanResult.Results.Select(x => x.FromUtf8Bytes()); - var urnKeys = ids.Map(t => AsAsync().UrnKey(t)); - if (urnKeys.Count > 0) + var callCount = 0; + while (cursor != 0 || callCount == 0) { - await AsAsync().RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); + var scanResult = await NativeAsync.SScanAsync(typeIdsSetKey, cursor, pageSize, token: token).ConfigureAwait(false); + callCount++; + cursor = scanResult.Cursor; + var ids = scanResult.Results.Select(x => x.FromUtf8Bytes()); + var urnKeys = ids.Map(t => AsAsync().UrnKey(t)); + if (urnKeys.Count > 0) + { + await AsAsync().RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); + } } - - if (lastCursor != 0) - await DeleteAllAsync(lastCursor, pageSize, token).ConfigureAwait(false); - else - await AsAsync().RemoveEntryAsync(new[] { typeIdsSetKey }, token).ConfigureAwait(false); + await AsAsync().RemoveEntryAsync(new[] { typeIdsSetKey }, token).ConfigureAwait(false); } ValueTask> IRedisClientAsync.SearchSortedSetAsync(string setId, string start, string end, int? skip, int? take, CancellationToken token) diff --git a/src/ServiceStack.Redis/RedisClient.cs b/src/ServiceStack.Redis/RedisClient.cs index 64f0ee2a..2127b5c2 100644 --- a/src/ServiceStack.Redis/RedisClient.cs +++ b/src/ServiceStack.Redis/RedisClient.cs @@ -831,24 +831,27 @@ public void DeleteByIds(ICollection ids) public void DeleteAll() { - DeleteAll(0,1000); + DeleteAll(0,RedisConfig.DeleteAllBatchSize); } private void DeleteAll(ulong cursor, int pageSize) { var typeIdsSetKey = this.GetTypeIdsSetKey(); - var scanResult = this.SScan(typeIdsSetKey, cursor, pageSize); - var resultCursor = scanResult.Cursor; - var ids = scanResult.Results.Select(x => x.FromUtf8Bytes()); - var urnKeys = ids.Map(t => this.UrnKey(t)); - if (urnKeys.Count > 0) + var callCount = 0; + while (cursor != 0 || callCount == 0) { - this.RemoveEntry(urnKeys.ToArray()); + var scanResult = this.SScan(typeIdsSetKey, cursor, pageSize); + callCount++; + cursor = scanResult.Cursor; + var ids = scanResult.Results.Select(x => x.FromUtf8Bytes()); + var urnKeys = ids.Map(t => this.UrnKey(t)); + if (urnKeys.Count > 0) + { + this.RemoveEntry(urnKeys.ToArray()); + } } - if(resultCursor != 0) - DeleteAll(resultCursor, pageSize); - else - this.RemoveEntry(typeIdsSetKey); + + this.RemoveEntry(typeIdsSetKey); } public RedisClient CloneClient() diff --git a/src/ServiceStack.Redis/RedisConfig.cs b/src/ServiceStack.Redis/RedisConfig.cs index cad76d9c..1ffde4dc 100644 --- a/src/ServiceStack.Redis/RedisConfig.cs +++ b/src/ServiceStack.Redis/RedisConfig.cs @@ -72,6 +72,11 @@ public class RedisConfig /// public static int BufferPoolMaxSize = 500000; + /// + /// The DeleteAll Batch Size is the number of keys returned each SSCAN when using DeleteAll on the RedisTypedClient. + /// + public static int DeleteAllBatchSize = 1000; + /// /// Whether Connections to Master hosts should be verified they're still master instances (default true) /// diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.Async.cs index 91c57d9d..f5fd43d5 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.Async.cs @@ -121,6 +121,46 @@ public async Task Can_Delete_All_Items() Assert.That(await RedisTyped.GetByIdAsync("key"), Is.Null); } + + [Test] + public async Task Can_Delete_All_Items_multiple_batches() + { + // Clear previous usage + await RedisAsync.DeleteAsync(RedisRaw.GetTypeIdsSetKey()); + var cachedRecord = new CacheRecord + { + Id = "key", + Children = { + new CacheRecordChild { Id = "childKey", Data = "data" } + } + }; + + var exists = RedisRaw.Exists(RedisRaw.GetTypeIdsSetKey(typeof(CacheRecord))); + Assert.That(exists, Is.EqualTo(0)); + + await RedisTyped.StoreAsync(cachedRecord); + + exists = RedisRaw.Exists(RedisRaw.GetTypeIdsSetKey(typeof(CacheRecord))); + Assert.That(exists, Is.EqualTo(1)); + + RedisConfig.DeleteAllBatchSize = 5; + + for (int i = 0; i < 50; i++) + { + cachedRecord.Id = "key" + i; + await RedisTyped.StoreAsync(cachedRecord); + } + + Assert.That(await RedisTyped.GetByIdAsync("key"), Is.Not.Null); + + await RedisTyped.DeleteAllAsync(); + + Assert.That(await RedisTyped.GetByIdAsync("key"), Is.Null); + + exists = RedisRaw.Exists(RedisRaw.GetTypeIdsSetKey(typeof(CacheRecord))); + Assert.That(exists, Is.EqualTo(0)); + + } } } \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.cs index a2c78b03..33375fd9 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.cs @@ -117,7 +117,45 @@ public void Can_Delete_All_Items() RedisTyped.DeleteAll(); Assert.That(RedisTyped.GetById("key"), Is.Null); + } + + [Test] + public void Can_Delete_All_Items_multiple_batches() + { + // Clear previous usage + Redis.Delete(Redis.GetTypeIdsSetKey(typeof(CacheRecord))); + var cachedRecord = new CacheRecord + { + Id = "key", + Children = { + new CacheRecordChild { Id = "childKey", Data = "data" } + } + }; + var exists = Redis.Exists(Redis.GetTypeIdsSetKey(typeof(CacheRecord))); + Assert.That(exists, Is.EqualTo(0)); + + RedisTyped.Store(cachedRecord); + + exists = Redis.Exists(Redis.GetTypeIdsSetKey(typeof(CacheRecord))); + + Assert.That(exists, Is.EqualTo(1)); + + RedisConfig.DeleteAllBatchSize = 5; + + for (int i = 0; i < 50; i++) + { + cachedRecord.Id = "key" + i; + RedisTyped.Store(cachedRecord); + } + + Assert.That(RedisTyped.GetById("key"), Is.Not.Null); + + RedisTyped.DeleteAll(); + + exists = Redis.Exists(Redis.GetTypeIdsSetKey(typeof(CacheRecord))); + Assert.That(exists, Is.EqualTo(0)); + Assert.That(RedisTyped.GetById("key"), Is.Null); } } From b66710974c9fe5197529618e6363bd35b947ca4a Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 9 Jun 2021 13:53:22 +0800 Subject: [PATCH 071/107] minor optimizations --- src/ServiceStack.Redis.sln.DotSettings | 1 + .../Generic/RedisTypedClient.Async.cs | 38 ++++++------ .../Generic/RedisTypedClient.cs | 23 ++++---- src/ServiceStack.Redis/RedisClient.Async.cs | 51 +++++++++------- src/ServiceStack.Redis/RedisClient.cs | 59 ++++++++++--------- src/ServiceStack.Redis/RedisConfig.cs | 5 +- .../Generic/RedisTypedClientTests.Async.cs | 3 +- .../Generic/RedisTypedClientTests.cs | 4 +- 8 files changed, 100 insertions(+), 84 deletions(-) diff --git a/src/ServiceStack.Redis.sln.DotSettings b/src/ServiceStack.Redis.sln.DotSettings index 11f2c267..28747c5c 100644 --- a/src/ServiceStack.Redis.sln.DotSettings +++ b/src/ServiceStack.Redis.sln.DotSettings @@ -1,3 +1,4 @@  + True <data><IncludeFilters /><ExcludeFilters /></data> <data /> \ No newline at end of file diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs index 8a6eac1c..01018721 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.Async.cs @@ -106,7 +106,7 @@ async Task IEntityStoreAsync.DeleteAsync(T entity, CancellationToken token) { var urnKey = client.UrnKey(entity); await AsyncClient.RemoveEntryAsync(new[] { urnKey }, token).ConfigureAwait(false); - await client.RemoveTypeIdsAsync(new[] { entity }, token).ConfigureAwait(false); + await client.RemoveTypeIdsByValueAsync(entity, token).ConfigureAwait(false); } async Task IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken token) @@ -114,41 +114,39 @@ async Task IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken tok var urnKey = client.UrnKey(id); await AsyncClient.RemoveEntryAsync(new[] { urnKey }, token).ConfigureAwait(false); - await client.RemoveTypeIdsAsync(new[] { id.ToString() }, token).ConfigureAwait(false); + await client.RemoveTypeIdsByIdAsync(id.ToString(), token).ConfigureAwait(false); } async Task IEntityStoreAsync.DeleteByIdsAsync(IEnumerable ids, CancellationToken token) { if (ids == null) return; - var urnKeys = ids.Map(t => client.UrnKey(t)); - if (urnKeys.Count > 0) + var idStrings = ids.Cast().Select(x => x.ToString()).ToArray(); + var urnKeys = idStrings.Select(t => client.UrnKey(t)).ToArray(); + if (urnKeys.Length > 0) { - await AsyncClient.RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); - await client.RemoveTypeIdsAsync(ids.Map(x => x.ToString()).ToArray(), token).ConfigureAwait(false); + await AsyncClient.RemoveEntryAsync(urnKeys, token).ConfigureAwait(false); + await client.RemoveTypeIdsByIdsAsync(idStrings, token).ConfigureAwait(false); } } async Task IEntityStoreAsync.DeleteAllAsync(CancellationToken token) { - await DeleteAllAsync(0,RedisConfig.DeleteAllBatchSize, token).ConfigureAwait(false); + await DeleteAllAsync(0,RedisConfig.CommandKeysBatchSize, token).ConfigureAwait(false); } - private async Task DeleteAllAsync(ulong cursor, int pageSize, CancellationToken token) + private async Task DeleteAllAsync(ulong cursor, int batchSize, CancellationToken token) { - var callCount = 0; - while (cursor != 0 || callCount == 0) + do { - var scanResult = await AsyncNative.SScanAsync(this.TypeIdsSetKey, cursor, pageSize, token: token).ConfigureAwait(false); - callCount++; + var scanResult = await AsyncNative.SScanAsync(this.TypeIdsSetKey, cursor, batchSize, token: token).ConfigureAwait(false); cursor = scanResult.Cursor; - var ids = scanResult.Results.Select(x => Encoding.UTF8.GetString(x)).ToList(); - var urnKeys = ids.Map(t => client.UrnKey(t)); - if (urnKeys.Count > 0) + var urnKeys = scanResult.Results.Select(x => client.UrnKey(Encoding.UTF8.GetString(x))).ToArray(); + if (urnKeys.Length > 0) { - await AsyncClient.RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); + await AsyncClient.RemoveEntryAsync(urnKeys, token).ConfigureAwait(false); } - } + } while (cursor != 0); await AsyncClient.RemoveEntryAsync(new[] { this.TypeIdsSetKey }, token).ConfigureAwait(false); } @@ -251,9 +249,9 @@ ValueTask IRedisTypedClientAsync.RemoveEntryAsync(string[] keys, Cancel async ValueTask IRedisTypedClientAsync.RemoveEntryAsync(IHasStringId[] entities, CancellationToken token) { - var ids = entities.Map(x => x.Id); - var success = await AsyncNative.DelAsync(ids.ToArray(), token).IsSuccessAsync().ConfigureAwait(false); - if (success) await client.RemoveTypeIdsAsync(ids.ToArray(), token).ConfigureAwait(false); + var ids = entities.Select(x => x.Id).ToArray(); + var success = await AsyncNative.DelAsync(ids, token).IsSuccessAsync().ConfigureAwait(false); + if (success) await client.RemoveTypeIdsByValuesAsync(ids, token).ConfigureAwait(false); return success; } diff --git a/src/ServiceStack.Redis/Generic/RedisTypedClient.cs b/src/ServiceStack.Redis/Generic/RedisTypedClient.cs index ececa68b..cf070975 100644 --- a/src/ServiceStack.Redis/Generic/RedisTypedClient.cs +++ b/src/ServiceStack.Redis/Generic/RedisTypedClient.cs @@ -220,7 +220,7 @@ public bool RemoveEntry(params IHasStringId[] entities) { var ids = entities.Map(x => x.Id); var success = client.Del(ids.ToArray()) == RedisNativeClient.Success; - if (success) client.RemoveTypeIds(ids.ToArray()); + if (success) client.RemoveTypeIdsByValues(ids.ToArray()); return success; } @@ -443,7 +443,7 @@ public void Delete(T entity) { var urnKey = client.UrnKey(entity); this.RemoveEntry(urnKey); - client.RemoveTypeIds(entity); + client.RemoveTypeIdsByValue(entity); } public void DeleteById(object id) @@ -451,28 +451,27 @@ public void DeleteById(object id) var urnKey = client.UrnKey(id); this.RemoveEntry(urnKey); - client.RemoveTypeIds(id.ToString()); + client.RemoveTypeIdsById(id.ToString()); } public void DeleteByIds(IEnumerable ids) { if (ids == null) return; - var urnKeys = ids.Map(t => client.UrnKey(t)); - if (urnKeys.Count > 0) + var idStrings = ids.Map(x => x.ToString()).ToArray(); + var urnKeys = idStrings.Select(t => client.UrnKey(t)).ToArray(); + if (urnKeys.Length > 0) { - this.RemoveEntry(urnKeys.ToArray()); - client.RemoveTypeIds(ids.Map(x => x.ToString()).ToArray()); + this.RemoveEntry(urnKeys); + client.RemoveTypeIdsByIds(idStrings); } } private void DeleteAll(ulong cursor, int pageSize) { - var callCount = 0; - while (cursor != 0 || callCount == 0) + do { var scanResult = client.SScan(this.TypeIdsSetKey, cursor, pageSize); - callCount++; cursor = scanResult.Cursor; var ids = scanResult.Results.Select(x => Encoding.UTF8.GetString(x)).ToList(); var urnKeys = ids.Map(t => client.UrnKey(t)); @@ -480,14 +479,14 @@ private void DeleteAll(ulong cursor, int pageSize) { this.RemoveEntry(urnKeys.ToArray()); } - } + } while (cursor != 0); this.RemoveEntry(this.TypeIdsSetKey); } public void DeleteAll() { - DeleteAll(0,RedisConfig.DeleteAllBatchSize); + DeleteAll(0,RedisConfig.CommandKeysBatchSize); } #endregion diff --git a/src/ServiceStack.Redis/RedisClient.Async.cs b/src/ServiceStack.Redis/RedisClient.Async.cs index da5b51dc..6648a224 100644 --- a/src/ServiceStack.Redis/RedisClient.Async.cs +++ b/src/ServiceStack.Redis/RedisClient.Async.cs @@ -599,7 +599,9 @@ internal ValueTask RegisterTypeIdsAsync(IEnumerable values, CancellationTo } } - internal async ValueTask RemoveTypeIdsAsync(T[] values, CancellationToken token) + internal ValueTask RemoveTypeIdsByValueAsync(T value, CancellationToken token) => + RemoveTypeIdsByIdAsync(value.GetId().ToString(), token); + internal async ValueTask RemoveTypeIdsByValuesAsync(T[] values, CancellationToken token) { var typeIdsSetKey = GetTypeIdsSetKey(); if (this.Pipeline != null) @@ -616,7 +618,18 @@ internal async ValueTask RemoveTypeIdsAsync(T[] values, CancellationToken tok } } - internal async ValueTask RemoveTypeIdsAsync(string[] ids, CancellationToken token) + internal async ValueTask RemoveTypeIdsByIdAsync(string id, CancellationToken token) + { + var typeIdsSetKey = GetTypeIdsSetKey(); + if (this.Pipeline != null) + GetRegisteredTypeIdsWithinPipeline(typeIdsSetKey).Remove(id); + else + { + await AsAsync().RemoveItemFromSetAsync(typeIdsSetKey, id, token).ConfigureAwait(false); + } + } + + internal async ValueTask RemoveTypeIdsByIdsAsync(string[] ids, CancellationToken token) { var typeIdsSetKey = GetTypeIdsSetKey(); if (this.Pipeline != null) @@ -637,48 +650,46 @@ async Task IEntityStoreAsync.DeleteAsync(T entity, CancellationToken token) { var urnKey = UrnKey(entity); await AsAsync().RemoveAsync(urnKey, token).ConfigureAwait(false); - await this.RemoveTypeIdsAsync(new[] { entity }, token).ConfigureAwait(false); + await this.RemoveTypeIdsByValueAsync(entity, token).ConfigureAwait(false); } async Task IEntityStoreAsync.DeleteByIdAsync(object id, CancellationToken token) { var urnKey = UrnKey(id); await AsAsync().RemoveAsync(urnKey, token).ConfigureAwait(false); - await this.RemoveTypeIdsAsync(new[] { id.ToString() }, token).ConfigureAwait(false); + await this.RemoveTypeIdsByIdAsync(id.ToString(), token).ConfigureAwait(false); } async Task IEntityStoreAsync.DeleteByIdsAsync(ICollection ids, CancellationToken token) { if (ids == null || ids.Count == 0) return; - var idsList = ids.Cast().ToList(); - var urnKeys = idsList.Map(UrnKey); - await AsAsync().RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); - await this.RemoveTypeIdsAsync(idsList.Map(x => x.ToString()).ToArray(), token).ConfigureAwait(false); + var idStrings = ids.Cast().Select(x => x.ToString()).ToArray(); + var urnKeys = idStrings.Select(UrnKey).ToArray(); + await AsAsync().RemoveEntryAsync(urnKeys, token).ConfigureAwait(false); + await this.RemoveTypeIdsByIdsAsync(idStrings, token).ConfigureAwait(false); } async Task IEntityStoreAsync.DeleteAllAsync(CancellationToken token) { - await DeleteAllAsync(0, RedisConfig.DeleteAllBatchSize, token).ConfigureAwait(false); + await DeleteAllAsync(0, RedisConfig.CommandKeysBatchSize, token).ConfigureAwait(false); } - private async Task DeleteAllAsync(ulong cursor, int pageSize, CancellationToken token) + private async Task DeleteAllAsync(ulong cursor, int batchSize, CancellationToken token) { var typeIdsSetKey = this.GetTypeIdsSetKey(); - var callCount = 0; - while (cursor != 0 || callCount == 0) + var asyncClient = AsAsync(); + do { - var scanResult = await NativeAsync.SScanAsync(typeIdsSetKey, cursor, pageSize, token: token).ConfigureAwait(false); - callCount++; + var scanResult = await NativeAsync.SScanAsync(typeIdsSetKey, cursor, batchSize, token: token).ConfigureAwait(false); cursor = scanResult.Cursor; - var ids = scanResult.Results.Select(x => x.FromUtf8Bytes()); - var urnKeys = ids.Map(t => AsAsync().UrnKey(t)); - if (urnKeys.Count > 0) + var urnKeys = scanResult.Results.Select(id => UrnKey(id.FromUtf8Bytes())).ToArray(); + if (urnKeys.Length > 0) { - await AsAsync().RemoveEntryAsync(urnKeys.ToArray(), token).ConfigureAwait(false); + await asyncClient.RemoveEntryAsync(urnKeys, token).ConfigureAwait(false); } - } - await AsAsync().RemoveEntryAsync(new[] { typeIdsSetKey }, token).ConfigureAwait(false); + } while (cursor != 0); + await asyncClient.RemoveEntryAsync(new[] { typeIdsSetKey }, token).ConfigureAwait(false); } ValueTask> IRedisClientAsync.SearchSortedSetAsync(string setId, string start, string end, int? skip, int? take, CancellationToken token) diff --git a/src/ServiceStack.Redis/RedisClient.cs b/src/ServiceStack.Redis/RedisClient.cs index 2127b5c2..1908f322 100644 --- a/src/ServiceStack.Redis/RedisClient.cs +++ b/src/ServiceStack.Redis/RedisClient.cs @@ -614,7 +614,16 @@ internal void RegisterTypeIds(IEnumerable values) } } - internal void RemoveTypeIds(params string[] ids) + internal void RemoveTypeIdsById(string id) + { + var typeIdsSetKey = GetTypeIdsSetKey(); + if (this.Pipeline != null) + GetRegisteredTypeIdsWithinPipeline(typeIdsSetKey).Remove(id); + else + this.RemoveItemFromSet(typeIdsSetKey, id); + } + + internal void RemoveTypeIdsByIds(IEnumerable ids) { var typeIdsSetKey = GetTypeIdsSetKey(); if (this.Pipeline != null) @@ -628,7 +637,9 @@ internal void RemoveTypeIds(params string[] ids) } } - internal void RemoveTypeIds(params T[] values) + internal void RemoveTypeIdsByValue(T value) => RemoveTypeIdsById(value.GetId().ToString()); + + internal void RemoveTypeIdsByValues(IEnumerable values) { var typeIdsSetKey = GetTypeIdsSetKey(); if (this.Pipeline != null) @@ -809,14 +820,14 @@ public void Delete(T entity) { var urnKey = UrnKey(entity); this.Remove(urnKey); - this.RemoveTypeIds(entity); + this.RemoveTypeIdsByValue(entity); } public void DeleteById(object id) { var urnKey = UrnKey(id); this.Remove(urnKey); - this.RemoveTypeIds(id.ToString()); + this.RemoveTypeIdsById(id.ToString()); } public void DeleteByIds(ICollection ids) @@ -824,44 +835,37 @@ public void DeleteByIds(ICollection ids) if (ids == null || ids.Count == 0) return; var idsList = ids.Cast(); - var urnKeys = idsList.Map(UrnKey); - this.RemoveEntry(urnKeys.ToArray()); - this.RemoveTypeIds(idsList.Map(x => x.ToString()).ToArray()); + var urnKeys = idsList.Select(UrnKey).ToArray(); + this.RemoveEntry(urnKeys); + this.RemoveTypeIdsByIds(ids.Map(x => x.ToString()).ToArray()); } public void DeleteAll() { - DeleteAll(0,RedisConfig.DeleteAllBatchSize); + DeleteAll(0,RedisConfig.CommandKeysBatchSize); } - private void DeleteAll(ulong cursor, int pageSize) + private void DeleteAll(ulong cursor, int batchSize) { var typeIdsSetKey = this.GetTypeIdsSetKey(); - var callCount = 0; - while (cursor != 0 || callCount == 0) + do { - var scanResult = this.SScan(typeIdsSetKey, cursor, pageSize); - callCount++; + var scanResult = this.SScan(typeIdsSetKey, cursor, batchSize); cursor = scanResult.Cursor; - var ids = scanResult.Results.Select(x => x.FromUtf8Bytes()); - var urnKeys = ids.Map(t => this.UrnKey(t)); - if (urnKeys.Count > 0) + var urnKeys = scanResult.Results.Select(id => UrnKey(id.FromUtf8Bytes())).ToArray(); + if (urnKeys.Length > 0) { - this.RemoveEntry(urnKeys.ToArray()); + this.RemoveEntry(urnKeys); } - } + } while (cursor != 0); this.RemoveEntry(typeIdsSetKey); } - public RedisClient CloneClient() - { - return new RedisClient(Host, Port, Password, Db) - { - SendTimeout = SendTimeout, - ReceiveTimeout = ReceiveTimeout - }; - } + public RedisClient CloneClient() => new(Host, Port, Password, Db) { + SendTimeout = SendTimeout, + ReceiveTimeout = ReceiveTimeout + }; /// /// Returns key with automatic object id detection in provided value with generic type. @@ -899,8 +903,7 @@ public string UrnKey(Type type, object id) #region LUA EVAL - static readonly ConcurrentDictionary CachedLuaSha1Map = - new ConcurrentDictionary(); + static readonly ConcurrentDictionary CachedLuaSha1Map = new(); public T ExecCachedLua(string scriptBody, Func scriptSha1) { diff --git a/src/ServiceStack.Redis/RedisConfig.cs b/src/ServiceStack.Redis/RedisConfig.cs index 1ffde4dc..39867dbd 100644 --- a/src/ServiceStack.Redis/RedisConfig.cs +++ b/src/ServiceStack.Redis/RedisConfig.cs @@ -73,9 +73,9 @@ public class RedisConfig public static int BufferPoolMaxSize = 500000; /// - /// The DeleteAll Batch Size is the number of keys returned each SSCAN when using DeleteAll on the RedisTypedClient. + /// Batch size of keys to include in a single Redis Command (e.g. DEL k1 k2...) /// - public static int DeleteAllBatchSize = 1000; + public static int CommandKeysBatchSize = 10000; /// /// Whether Connections to Master hosts should be verified they're still master instances (default true) @@ -141,6 +141,7 @@ public static void Reset() DefaultMaxPoolSize = null; BackOffMultiplier = 10; BufferPoolMaxSize = 500000; + CommandKeysBatchSize = 10000; VerifyMasterConnections = true; RetryReconnectOnFailedMasters = true; HostLookupTimeoutMs = 200; diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.Async.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.Async.cs index f5fd43d5..7224d6c5 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.Async.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.Async.cs @@ -143,7 +143,7 @@ public async Task Can_Delete_All_Items_multiple_batches() exists = RedisRaw.Exists(RedisRaw.GetTypeIdsSetKey(typeof(CacheRecord))); Assert.That(exists, Is.EqualTo(1)); - RedisConfig.DeleteAllBatchSize = 5; + RedisConfig.CommandKeysBatchSize = 5; for (int i = 0; i < 50; i++) { @@ -160,6 +160,7 @@ public async Task Can_Delete_All_Items_multiple_batches() exists = RedisRaw.Exists(RedisRaw.GetTypeIdsSetKey(typeof(CacheRecord))); Assert.That(exists, Is.EqualTo(0)); + RedisConfig.Reset(); } } diff --git a/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.cs b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.cs index 33375fd9..18236cf2 100644 --- a/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.cs +++ b/tests/ServiceStack.Redis.Tests/Generic/RedisTypedClientTests.cs @@ -141,7 +141,7 @@ public void Can_Delete_All_Items_multiple_batches() Assert.That(exists, Is.EqualTo(1)); - RedisConfig.DeleteAllBatchSize = 5; + RedisConfig.CommandKeysBatchSize = 5; for (int i = 0; i < 50; i++) { @@ -156,6 +156,8 @@ public void Can_Delete_All_Items_multiple_batches() exists = Redis.Exists(Redis.GetTypeIdsSetKey(typeof(CacheRecord))); Assert.That(exists, Is.EqualTo(0)); Assert.That(RedisTyped.GetById("key"), Is.Null); + + RedisConfig.Reset(); } } From 680962d68304f2652219ff65a172eae81c6710b4 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Thu, 1 Jul 2021 01:31:10 +0800 Subject: [PATCH 072/107] Update RedisConfig.cs --- src/ServiceStack.Redis/RedisConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ServiceStack.Redis/RedisConfig.cs b/src/ServiceStack.Redis/RedisConfig.cs index 39867dbd..da28ffb5 100644 --- a/src/ServiceStack.Redis/RedisConfig.cs +++ b/src/ServiceStack.Redis/RedisConfig.cs @@ -105,7 +105,7 @@ public class RedisConfig public static TimeSpan DeactivatedClientsExpiry = TimeSpan.Zero; /// - /// Whether Debug Logging should log detailed Redis operations (default true) + /// Whether Debug Logging should log detailed Redis operations (default false) /// public static bool EnableVerboseLogging = false; From 5b5e2dca0c65ca99bf049f460f144f793c7cd86a Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Sun, 15 Aug 2021 19:57:22 +0800 Subject: [PATCH 073/107] upgrade deps --- .vscode/tasks.json | 17 ++++++++++------- .../ServiceStack.Redis.Tests.Sentinel.csproj | 2 +- .../ServiceStack.Redis.Tests.csproj | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c549ce77..d5b6d366 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,17 +1,20 @@ { // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format - "version": "0.1.0", + "version": "2.0.0", "command": "dotnet", - "isShellCommand": true, "args": [], "tasks": [ { - "taskName": "build", - "args": [ "tests/ServiceStack.Redis.Tests"], - "isBuildCommand": true, - "showOutput": "silent", - "problemMatcher": "$msCompile" + "label": "build", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "tests/ServiceStack.Redis.Tests" + ], + "problemMatcher": "$msCompile", + "group": "build" } ] } \ No newline at end of file diff --git a/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj b/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj index ad5f7bdb..db18a646 100644 --- a/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj +++ b/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj @@ -46,7 +46,7 @@ - + diff --git a/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj b/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj index 29196776..e54b1520 100644 --- a/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj +++ b/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj @@ -50,6 +50,6 @@ - + \ No newline at end of file From 9a69480a32183a78722a0bae47530bfe0ffe5f4c Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Sun, 15 Aug 2021 21:16:45 +0800 Subject: [PATCH 074/107] Update ServiceStack.Redis.csproj --- src/ServiceStack.Redis/ServiceStack.Redis.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.csproj index 4808134c..77a5ad5b 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.csproj @@ -29,7 +29,7 @@ - + @@ -40,7 +40,7 @@ - + \ No newline at end of file From 09d3a084c6c1789dbbee00bc78b7076b4ef051cf Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 17 Aug 2021 19:50:50 +0800 Subject: [PATCH 075/107] bump to v5.12.0 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 9cae0c4b..436bc536 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.11.1 + 5.12.0 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 3f70834f..7ade3329 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.11.1 + 5.12.0 latest false From 8c60169d9a724b4d49f2f30666abf70bcfe22207 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 18 Aug 2021 17:43:08 +0800 Subject: [PATCH 076/107] bump to v5.12.1 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 436bc536..878aa666 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.12.0 + 5.12.1 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 7ade3329..f2302eba 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.12.0 + 5.12.1 latest false From b8d9fc5c70225b071db8d212e0e9c17983a26ddf Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 18 Aug 2021 21:16:48 +0800 Subject: [PATCH 077/107] rm ServiceStack.Redis.Benchmark\obj --- build/build.proj | 1 + 1 file changed, 1 insertion(+) diff --git a/build/build.proj b/build/build.proj index 300a4087..b81892c7 100644 --- a/build/build.proj +++ b/build/build.proj @@ -43,6 +43,7 @@ + From 752194ac138eec4e0151930043ce2cec5a7418b0 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 27 Aug 2021 18:43:03 +0800 Subject: [PATCH 078/107] Update README.md --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 94ee2e01..5598f78a 100644 --- a/README.md +++ b/README.md @@ -327,17 +327,19 @@ public class HomeController : ServiceStackController } ``` +## [Redis Vue Desktop](https://sharpscript.net/sharp-apps/redis#redis-vue) -## [Redis React Browser](https://servicestack.net/redis-react) - -Redis React is a simple user-friendly UI for browsing data in Redis servers which takes advantages of the complex +Redis Vue is a simple user-friendly [Vue Desktop App](https://www.vuedesktop.com) for browsing data in Redis servers which takes advantages of the complex type conventions built in the ServiceStack.Redis Client to provide a rich, human-friendly UI for navigating related datasets, enabling a fast and fluid browsing experience for your Redis servers. -#### [Live Demo](http://redisreact.servicestack.net/#/) +Install [.NET SDK](https://dotnet.microsoft.com/download) and `app` dotnet tool and run with: -[![](https://raw.githubusercontent.com/ServiceStack/Assets/master/img/livedemos/redis-react/home.png)](http://redisreact.servicestack.net/#/) +``` +dotnet tool install -g app +app open redis +``` -#### Downloads available from [Redis React Home Page](https://servicestack.net/redis-react) +[![](https://sharpscript.net/assets/img/screenshots/redis.png)](http://redis.web-app.io) ## [Redis Sentinel](https://github.com/ServiceStack/ServiceStack.Redis/wiki/Redis-Sentinel) From 36004dd6b6ce6703e40c80afbed3382fca0400b9 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 27 Aug 2021 18:50:12 +0800 Subject: [PATCH 079/107] Update README.md --- README.md | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 5598f78a..59a297ab 100644 --- a/README.md +++ b/README.md @@ -112,13 +112,6 @@ ServiceStack.Redis has great support AWS's ElastiCache Redis solution, follow th - [ElastiCache Redis](https://github.com/ServiceStackApps/AwsGettingStarted/blob/master/docs/redis-guide.md) -### Try out [ServiceStack.Redis Live](http://gistlyn.com/redis-todo) - -A great way to try out ServiceStack.Redis is on [gistlyn.com](http://gistlyn.com) which lets you immediately -run and explore Redis features from the comfort of your browser with zero software install: - -[![](https://raw.githubusercontent.com/ServiceStack/Assets/master/img/redis/gistlyn-redis.png)](http://gistlyn.com/redis-todo) - ## Redis Client Managers The recommended way to access `RedisClient` instances is to use one of the available Thread-Safe Client Managers below. Client Managers are connection factories which should be registered as a Singleton either in your IOC or static class. @@ -332,14 +325,19 @@ public class HomeController : ServiceStackController Redis Vue is a simple user-friendly [Vue Desktop App](https://www.vuedesktop.com) for browsing data in Redis servers which takes advantages of the complex type conventions built in the ServiceStack.Redis Client to provide a rich, human-friendly UI for navigating related datasets, enabling a fast and fluid browsing experience for your Redis servers. -Install [.NET SDK](https://dotnet.microsoft.com/download) and `app` dotnet tool and run with: +Install [.NET SDK](https://dotnet.microsoft.com/download) and run [install/app.ps1](https://servicestack.net/install/app.ps1) to install the `app` dotnet tool: -``` -dotnet tool install -g app -app open redis -``` + powershell iwr gist.cafe/install.ps1 -useb | iex + +Then run `redis` Vue Desktop App in a browser: + +### [app://redis](app://redis) + +Or from the command-line: + + app open redis -[![](https://sharpscript.net/assets/img/screenshots/redis.png)](http://redis.web-app.io) +[![](https://sharpscript.net/assets/img/screenshots/redis.png)](https://sharpscript.net/sharp-apps/redis#redis-vue) ## [Redis Sentinel](https://github.com/ServiceStack/ServiceStack.Redis/wiki/Redis-Sentinel) From faf81c4f27b49005d94b4f16b56f1fa76c17d9ff Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Sun, 10 Oct 2021 15:12:47 +0800 Subject: [PATCH 080/107] Update .Source csproj's --- src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj index ac2a4be5..23aedec2 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj @@ -10,6 +10,7 @@ Thread-Safe Basic and Pooled client managers included. Redis;NoSQL;Client;Distributed;Cache;PubSub;Messaging;Transactions + false @@ -29,7 +30,7 @@ - + @@ -38,9 +39,9 @@ - + - + \ No newline at end of file From 4371a0681ae4a6c1ae4d2b204ce59bbe63ba2b0f Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 26 Oct 2021 22:29:27 +0800 Subject: [PATCH 081/107] Add .NET 6 build support --- .../ServiceStack.Redis.Core.csproj | 18 +++++++++++++++++- .../ServiceStack.Redis.csproj | 14 ++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj index 2f611d71..6a420676 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj @@ -3,13 +3,19 @@ ServiceStack.Redis.Core ServiceStack.Redis ServiceStack.Redis - netstandard2.0 + netstandard2.0;net6.0 ServiceStack.Redis .NET Standard 2.0 .NET Standard 2.0 version of ServiceStack.Redis Redis;NoSQL;Client;Distributed;Cache;PubSub;Messaging;Transactions + + $(DefineConstants);ASYNC_MEMORY + + + $(DefineConstants);ASYNC_MEMORY;NET6_0 + @@ -20,6 +26,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.csproj index 77a5ad5b..6dd6dfde 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.csproj @@ -2,7 +2,7 @@ ServiceStack.Redis ServiceStack.Redis - net45;net472;netstandard2.0;netstandard2.1 + net45;net472;netstandard2.0;netstandard2.1;net6.0 C# Redis client for the Redis NoSQL DB C# Redis Client for the worlds fastest distributed NoSQL datastore. @@ -15,6 +15,9 @@ $(DefineConstants);ASYNC_MEMORY + + $(DefineConstants);ASYNC_MEMORY;NET6_0 + @@ -38,9 +41,16 @@ - + + + + + + + + \ No newline at end of file From 9f48c9eac62562fb25c505eada929fed455995ef Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 26 Oct 2021 22:35:11 +0800 Subject: [PATCH 082/107] fix build --- .../Support/Diagnostic/TrackingRedisClientProxy.cs | 2 +- .../Support/Diagnostic/TrackingRedisClientsManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientProxy.cs b/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientProxy.cs index e2c73350..7ee72602 100644 --- a/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientProxy.cs +++ b/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientProxy.cs @@ -1,4 +1,4 @@ -#if !(NETSTANDARD2_0 || NETSTANDARD2_1) +#if !(NETSTANDARD2_0 || NETSTANDARD2_1 || NET6_0) using System; using System.Reflection; using System.Runtime.Remoting.Messaging; diff --git a/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientsManager.cs b/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientsManager.cs index d2408905..ab8216bd 100644 --- a/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientsManager.cs +++ b/src/ServiceStack.Redis/Support/Diagnostic/TrackingRedisClientsManager.cs @@ -1,4 +1,4 @@ -#if !(NETSTANDARD2_0 || NETSTANDARD2_1) +#if !(NETSTANDARD2_0 || NETSTANDARD2_1 || NET6_0) using System; using System.Collections.Generic; using System.Diagnostics; From c5342f0835bafc7ce141f7c750079a1cea0fc5ea Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 27 Oct 2021 21:51:11 +0800 Subject: [PATCH 083/107] Replace symbold NETSTANDARD2_0 with NETCORE --- src/ServiceStack.Redis/BufferedStream.cs | 2 +- src/ServiceStack.Redis/RedisPubSubServer.cs | 2 +- src/ServiceStack.Redis/ServiceStack.Redis.csproj | 7 +++++-- src/ServiceStack.Redis/Support/ObjectSerializer.cs | 8 ++++---- .../Support/OptimizedObjectSerializer.cs | 4 ++-- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/ServiceStack.Redis/BufferedStream.cs b/src/ServiceStack.Redis/BufferedStream.cs index 322e5573..2d745c5f 100644 --- a/src/ServiceStack.Redis/BufferedStream.cs +++ b/src/ServiceStack.Redis/BufferedStream.cs @@ -1,4 +1,4 @@ -#if NETSTANDARD2_0 +#if NETCORE using System; using System.IO; using System.Net.Sockets; diff --git a/src/ServiceStack.Redis/RedisPubSubServer.cs b/src/ServiceStack.Redis/RedisPubSubServer.cs index a3ceacfd..a1d4fd17 100644 --- a/src/ServiceStack.Redis/RedisPubSubServer.cs +++ b/src/ServiceStack.Redis/RedisPubSubServer.cs @@ -468,7 +468,7 @@ private void KillBgThreadIfExists() //give it a small chance to die gracefully if (!bgThread.Join(500)) { -#if !NETSTANDARD2_0 +#if !NETCORE //Ideally we shouldn't get here, but lets try our hardest to clean it up OnEvent?.Invoke($"[{DateTime.UtcNow.TimeOfDay:g} {GetStatus()}] KillBgThreadIfExists()> bgThread.Interrupt()"); Log.Warn("Interrupting previous Background Thread: " + bgThread.Name); diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.csproj index 6dd6dfde..a9bcc74c 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.csproj @@ -12,11 +12,14 @@ Redis;NoSQL;Client;Distributed;Cache;PubSub;Messaging;Transactions + + $(DefineConstants);NETCORE + - $(DefineConstants);ASYNC_MEMORY + $(DefineConstants);ASYNC_MEMORY;NETCORE - $(DefineConstants);ASYNC_MEMORY;NET6_0 + $(DefineConstants);ASYNC_MEMORY;NETCORE;NET6_0 diff --git a/src/ServiceStack.Redis/Support/ObjectSerializer.cs b/src/ServiceStack.Redis/Support/ObjectSerializer.cs index 07947a62..5c756903 100644 --- a/src/ServiceStack.Redis/Support/ObjectSerializer.cs +++ b/src/ServiceStack.Redis/Support/ObjectSerializer.cs @@ -1,5 +1,5 @@ using System.IO; -#if !NETSTANDARD2_0 +#if !NETCORE using System.Runtime.Serialization.Formatters.Binary; #endif @@ -11,7 +11,7 @@ namespace ServiceStack.Redis.Support /// public class ObjectSerializer : ISerializer { -#if !NETSTANDARD2_0 +#if !NETCORE protected readonly BinaryFormatter bf = new BinaryFormatter(); #endif @@ -23,7 +23,7 @@ public class ObjectSerializer : ISerializer /// public virtual byte[] Serialize(object value) { -#if NETSTANDARD2_0 +#if NETCORE return null; #else if (value == null) @@ -42,7 +42,7 @@ public virtual byte[] Serialize(object value) /// public virtual object Deserialize(byte[] someBytes) { -#if NETSTANDARD2_0 +#if NETCORE return null; #else if (someBytes == null) diff --git a/src/ServiceStack.Redis/Support/OptimizedObjectSerializer.cs b/src/ServiceStack.Redis/Support/OptimizedObjectSerializer.cs index 63b99490..3d4dbf17 100644 --- a/src/ServiceStack.Redis/Support/OptimizedObjectSerializer.cs +++ b/src/ServiceStack.Redis/Support/OptimizedObjectSerializer.cs @@ -122,7 +122,7 @@ SerializedObjectWrapper SerializeToWrapper(object value) break; default: -#if NETSTANDARD2_0 +#if NETCORE data = new byte[0]; length = 0; #else @@ -231,7 +231,7 @@ object Unwrap(SerializedObjectWrapper item) case TypeCode.Object: using (var ms = new MemoryStream(data, offset, count)) { -#if NETSTANDARD2_0 +#if NETCORE return null; #else return bf.Deserialize(ms); From d09e71057d543851ebad9005c3269b493184272b Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 9 Nov 2021 19:08:56 +0800 Subject: [PATCH 084/107] Upgrade deps --- src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj | 2 +- src/ServiceStack.Redis/ServiceStack.Redis.csproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj index 6a420676..bcf5c309 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj @@ -27,7 +27,7 @@ - + diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.csproj index a9bcc74c..c523f959 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.csproj @@ -35,7 +35,7 @@ - + @@ -45,7 +45,7 @@ - + From 6b7a5582aa1fb575ff2619c0709f84482591769d Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 9 Nov 2021 22:48:54 +0800 Subject: [PATCH 085/107] bump to v5.13.0 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 878aa666..f37fa3f0 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.12.1 + 5.13.0 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index f2302eba..87526936 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.12.1 + 5.13.0 latest false From b0fefb50504522cc7a824b1134b2eca6098a4848 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 10 Nov 2021 13:18:49 +0800 Subject: [PATCH 086/107] upgrade .Source.csproj --- src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj index 23aedec2..78a8f664 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj @@ -30,7 +30,7 @@ - + @@ -41,7 +41,7 @@ - + \ No newline at end of file From cf46838307d75ede83b5e66e5278d2f5d4674271 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 10 Nov 2021 15:22:17 +0800 Subject: [PATCH 087/107] Update ServiceStack.Redis.Source.csproj --- src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj index 78a8f664..dbe7b981 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj @@ -13,8 +13,14 @@ false + + $(DefineConstants);NETCORE + - $(DefineConstants);ASYNC_MEMORY + $(DefineConstants);ASYNC_MEMORY;NETCORE + + + $(DefineConstants);ASYNC_MEMORY;NETCORE;NET6_0 From 60698df02075ed37eeac08550b91b8689e457fab Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 10 Nov 2021 15:41:41 +0800 Subject: [PATCH 088/107] Update ServiceStack.Redis.Source.csproj --- src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj index dbe7b981..4388bafa 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj @@ -2,7 +2,7 @@ ServiceStack.Redis ServiceStack.Redis - net45;net472;netstandard2.0;netstandard2.1 + net472;net6.0 C# Redis client for the Redis NoSQL DB C# Redis Client for the worlds fastest distributed NoSQL datastore. From 7769dbd4e6b3e448dc9bbf9f1f9a13603d6e2d33 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 10 Nov 2021 15:50:05 +0800 Subject: [PATCH 089/107] add build symbol to .csproj's --- src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj | 5 ++++- src/ServiceStack.Redis/ServiceStack.Redis.csproj | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj index 4388bafa..f4847b5f 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj @@ -13,6 +13,9 @@ false + + $(DefineConstants);ASYNC_MEMORY + $(DefineConstants);NETCORE @@ -34,7 +37,7 @@ - + diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.csproj index c523f959..8dd30dcb 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.csproj @@ -12,6 +12,9 @@ Redis;NoSQL;Client;Distributed;Cache;PubSub;Messaging;Transactions + + $(DefineConstants);ASYNC_MEMORY + $(DefineConstants);NETCORE From f1d9923e4d95fc99bf22542f1fb3d038a9384975 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 10 Nov 2021 16:05:21 +0800 Subject: [PATCH 090/107] Redis Source .csproj's --- src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj | 2 +- src/ServiceStack.Redis/ServiceStack.Redis.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj index f4847b5f..24d99642 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj @@ -14,7 +14,7 @@ - $(DefineConstants);ASYNC_MEMORY + $(DefineConstants);ASYNC_MEMORY;NET472 $(DefineConstants);NETCORE diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.csproj index 8dd30dcb..d7738fd3 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.csproj @@ -13,7 +13,7 @@ - $(DefineConstants);ASYNC_MEMORY + $(DefineConstants);ASYNC_MEMORY;NET472 $(DefineConstants);NETCORE From 42374e59c5c5e520e4faaf0e80b7fb16321ec725 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 10 Nov 2021 20:51:05 +0800 Subject: [PATCH 091/107] Upgrade Tests to net472;net6.0 --- src/ServiceStack.Redis/ServiceStack.Redis.csproj | 2 +- .../ServiceStack.Redis.Tests.csproj | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.csproj index d7738fd3..b0eb9aa9 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.csproj @@ -13,7 +13,7 @@ - $(DefineConstants);ASYNC_MEMORY;NET472 + $(DefineConstants);NET472 $(DefineConstants);NETCORE diff --git a/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj b/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj index e54b1520..3b441b49 100644 --- a/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj +++ b/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj @@ -2,12 +2,12 @@ - net46;net472;netcoreapp2.1;net5.0 + net472;net6.0 portable ServiceStack.Redis.Tests Library @@ -29,17 +29,17 @@ - + $(DefineConstants);NET45 - + - + $(DefineConstants);NETCORE - + @@ -49,7 +49,7 @@ - + \ No newline at end of file From 2c92bae9268684d36cef600a4862bd0ea5d94ae5 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Thu, 11 Nov 2021 01:10:10 +0800 Subject: [PATCH 092/107] update Source .csproj --- src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj index 24d99642..1e890094 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj @@ -14,7 +14,7 @@ - $(DefineConstants);ASYNC_MEMORY;NET472 + $(DefineConstants);NET472 $(DefineConstants);NETCORE From 4a06f3a1468bbf72b1fdcaf5fa00284ff1668362 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 12 Nov 2021 12:44:13 +0800 Subject: [PATCH 093/107] update test csproj's --- .../ServiceStack.Redis.Tests.Sentinel.csproj | 6 +++--- .../ServiceStack.Redis.Tests.csproj | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj b/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj index db18a646..3c9f8e06 100644 --- a/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj +++ b/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj @@ -21,9 +21,9 @@ - - - + + + diff --git a/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj b/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj index 3b441b49..0b7c7ea1 100644 --- a/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj +++ b/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj @@ -23,9 +23,9 @@ - - - + + + From 60bc9c035e735658e4c801be5458aac43f67a4c2 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 12 Nov 2021 12:44:20 +0800 Subject: [PATCH 094/107] bump v5.13.1 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index f37fa3f0..bbcfa094 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.13.0 + 5.13.1 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 87526936..621ac90d 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.13.0 + 5.13.1 latest false From dfe4233aa4ff15bda52f49915eb3e2046af5a8f9 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Sat, 20 Nov 2021 14:10:32 +0800 Subject: [PATCH 095/107] bump to v5.13.2 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index bbcfa094..a16d385b 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.13.1 + 5.13.2 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 621ac90d..3dd7240d 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.13.1 + 5.13.2 latest false From f898542b2a0fcb09140b69842c49aa3f6636c6fa Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Sat, 20 Nov 2021 16:06:56 +0800 Subject: [PATCH 096/107] bump to v5.13.3 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index a16d385b..15a4ef6e 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.13.2 + 5.13.3 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 3dd7240d..eceed39a 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.13.2 + 5.13.3 latest false From 2ed1d8907d8b0b8978cc889edc692ffed68d52c0 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Mon, 29 Nov 2021 18:25:29 +0800 Subject: [PATCH 097/107] Update .Source.csproj's to only netstandard/net6 --- .../ServiceStack.Redis.Source.csproj | 34 ++++++------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj index 1e890094..3efa57ed 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Source.csproj @@ -2,7 +2,7 @@ ServiceStack.Redis ServiceStack.Redis - net472;net6.0 + netstandard2.0;net6.0 C# Redis client for the Redis NoSQL DB C# Redis Client for the worlds fastest distributed NoSQL datastore. @@ -13,34 +13,15 @@ false - - $(DefineConstants);NET472 - - - $(DefineConstants);NETCORE - - $(DefineConstants);ASYNC_MEMORY;NETCORE + $(DefineConstants);ASYNC_MEMORY - $(DefineConstants);ASYNC_MEMORY;NETCORE;NET6_0 + $(DefineConstants);ASYNC_MEMORY;NET6_0 - - - - - - - - - - - - - @@ -48,9 +29,16 @@ - + + + + + + + + \ No newline at end of file From 2320169987cbb860b17c4e5fc8e416d34f1a649b Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Mon, 24 Jan 2022 01:47:00 +0800 Subject: [PATCH 098/107] update pkg refs --- .../DbSelectConnectionStringIssue.cs | 39 +++++++++++++++++++ tests/Console.Tests/Program.cs | 4 +- .../ServiceStack.Redis.Tests.Sentinel.csproj | 9 ++--- .../ServiceStack.Redis.Tests.csproj | 1 - 4 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 tests/Console.Tests/DbSelectConnectionStringIssue.cs diff --git a/tests/Console.Tests/DbSelectConnectionStringIssue.cs b/tests/Console.Tests/DbSelectConnectionStringIssue.cs new file mode 100644 index 00000000..49d453f5 --- /dev/null +++ b/tests/Console.Tests/DbSelectConnectionStringIssue.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading; +using ServiceStack; +using ServiceStack.Logging; +using ServiceStack.Redis; + +namespace ConsoleTests; + +class DbSelectConnectionStringIssue +{ + public void Execute() + { + LogManager.LogFactory = new ConsoleLogFactory(); + + Licensing.RegisterLicense(""); + + var redisManagerPool = new RedisManagerPool("redis://redisHost?db=7"); + + for (int i = 0; i < 5; i++) + { + try + { + using (IRedisClient client = redisManagerPool.GetClient()) + { + string value = client.GetValue("status"); + + Console.WriteLine($"Successfully retrieved value => '{value}'"); + } + } + catch (Exception ex) + { + Console.WriteLine($"Exception handled \n{ex}"); + } + + Console.WriteLine("Sleeping for 25 seconds to allow client to be garbage collected"); + Thread.Sleep(TimeSpan.FromSeconds(25)); + } + } +} \ No newline at end of file diff --git a/tests/Console.Tests/Program.cs b/tests/Console.Tests/Program.cs index 07ee217c..2db1835b 100644 --- a/tests/Console.Tests/Program.cs +++ b/tests/Console.Tests/Program.cs @@ -40,7 +40,9 @@ static void Main(string[] args) //new BlockingRemoveAfterReconnection().Execute(); - new MultiBlockingRemoveAfterReconnection().Execute(); + //new MultiBlockingRemoveAfterReconnection().Execute(); + + new DbSelectConnectionStringIssue().Execute(); } } } diff --git a/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj b/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj index 3c9f8e06..2d59d514 100644 --- a/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj +++ b/tests/ServiceStack.Redis.Tests.Sentinel/ServiceStack.Redis.Tests.Sentinel.csproj @@ -1,7 +1,7 @@  - net472;net5.0 + net472;net6.0 portable ServiceStack.Redis.Tests.Sentinel Library @@ -41,12 +41,11 @@ - - $(DefineConstants);NETCORE_SUPPORT;NETCORE + + $(DefineConstants);NETCORE;NET6_0;NET6_0_OR_GREATER - - + diff --git a/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj b/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj index 0b7c7ea1..374a13f9 100644 --- a/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj +++ b/tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj @@ -50,6 +50,5 @@ - \ No newline at end of file From 6e7234a0a05814dfa4199cbf268f2f2e613b2c7f Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Mon, 24 Jan 2022 03:07:15 +0800 Subject: [PATCH 099/107] Upgrade to v15.4.0 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 15a4ef6e..5f25ed5c 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.13.3 + 5.14.0 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index eceed39a..cb0a99ac 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.13.3 + 5.14.0 latest false From ada48890e70b7530318395d4775e7097db671d92 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Mon, 24 Jan 2022 21:56:44 +0800 Subject: [PATCH 100/107] Upgrade to v6 --- build/build-core.proj | 2 +- build/build.proj | 2 +- src/Directory.Build.props | 22 ++++++------------ .../Properties/AssemblyInfo.cs | 2 +- .../ServiceStack.Redis.csproj | 13 ++--------- tests/Console.Tests/Console.Tests.csproj | 2 +- tests/Directory.Build.props | 23 ++++++++----------- .../ServiceStack.Redis.Benchmark.csproj | 2 +- 8 files changed, 24 insertions(+), 44 deletions(-) diff --git a/build/build-core.proj b/build/build-core.proj index 82110455..3ac34b8b 100644 --- a/build/build-core.proj +++ b/build/build-core.proj @@ -4,7 +4,7 @@ - 5 + 6 0 $(BUILD_NUMBER) diff --git a/build/build.proj b/build/build.proj index b81892c7..7899a808 100644 --- a/build/build.proj +++ b/build/build.proj @@ -4,7 +4,7 @@ - 5 + 6 0 $(BUILD_NUMBER) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 5f25ed5c..e4b6d4e5 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 5.14.0 + 6.0.0 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc @@ -24,31 +24,23 @@ true - - $(DefineConstants);NETFX;NET45 + + $(DefineConstants);NETFX;NET45;NET472 True False ../servicestack.snk - - $(DefineConstants);NETFX;NET472 - - $(DefineConstants);NETSTANDARD;NETSTANDARD2_0 - - $(DefineConstants);NETSTANDARD;NETSTANDARD2_1 - - - - $(DefineConstants);NET50 + + $(DefineConstants);NET6_0;NET6_0_OR_GREATER - - $(DefineConstants);NETCORE_SUPPORT;NETCORE + + $(DefineConstants);NETCORE;NETCORE_SUPPORT diff --git a/src/ServiceStack.Redis/Properties/AssemblyInfo.cs b/src/ServiceStack.Redis/Properties/AssemblyInfo.cs index 07aaea2f..4ce9daba 100644 --- a/src/ServiceStack.Redis/Properties/AssemblyInfo.cs +++ b/src/ServiceStack.Redis/Properties/AssemblyInfo.cs @@ -2,4 +2,4 @@ [assembly: ComVisible(false)] [assembly: Guid("70a33fa7-9f81-418d-bb25-6a4be6648ae4")] -[assembly: System.Reflection.AssemblyVersion("5.0.0.0")] \ No newline at end of file +[assembly: System.Reflection.AssemblyVersion("6.0.0.0")] \ No newline at end of file diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.csproj index b0eb9aa9..5a6ed5c8 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.csproj @@ -2,7 +2,7 @@ ServiceStack.Redis ServiceStack.Redis - net45;net472;netstandard2.0;netstandard2.1;net6.0 + net472;netstandard2.0;netstandard2.1;net6.0 C# Redis client for the Redis NoSQL DB C# Redis Client for the worlds fastest distributed NoSQL datastore. @@ -24,19 +24,10 @@ $(DefineConstants);ASYNC_MEMORY;NETCORE;NET6_0 - - - - - - - - - - + diff --git a/tests/Console.Tests/Console.Tests.csproj b/tests/Console.Tests/Console.Tests.csproj index cfd8fff7..5b260ab0 100644 --- a/tests/Console.Tests/Console.Tests.csproj +++ b/tests/Console.Tests/Console.Tests.csproj @@ -2,7 +2,7 @@ Exe - net5.0 + net6.0 Console.Tests diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index cb0a99ac..187fbd04 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 5.14.0 + 6.0.0 latest false @@ -10,23 +10,20 @@ DEBUG - - $(DefineConstants);NETFX;NET45 + + $(DefineConstants);NETFX;NET472 - - $(DefineConstants);NETFX;NET46 + + $(DefineConstants);NETCORE;NETSTANDARD2_0 - - $(DefineConstants);NETCORE_SUPPORT;NETCORE + + $(DefineConstants);NET6_0;NET6_0_OR_GREATER - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + $(DefineConstants);NETCORE;NETCORE_SUPPORT + diff --git a/tests/ServiceStack.Redis.Benchmark/ServiceStack.Redis.Benchmark.csproj b/tests/ServiceStack.Redis.Benchmark/ServiceStack.Redis.Benchmark.csproj index 815d5617..a5bcef26 100644 --- a/tests/ServiceStack.Redis.Benchmark/ServiceStack.Redis.Benchmark.csproj +++ b/tests/ServiceStack.Redis.Benchmark/ServiceStack.Redis.Benchmark.csproj @@ -2,7 +2,7 @@ Exe - net5.0;net472 + net6.0;net472 8 From 346448c335de2e635d1685b19a7ca90fc9c334ea Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Mon, 24 Jan 2022 23:30:16 +0800 Subject: [PATCH 101/107] Remove unnecessary net6.0 deps --- src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj | 6 ------ src/ServiceStack.Redis/ServiceStack.Redis.csproj | 6 ------ 2 files changed, 12 deletions(-) diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj index bcf5c309..a2fe7181 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.Core.csproj @@ -30,12 +30,6 @@ - - - - - - \ No newline at end of file diff --git a/src/ServiceStack.Redis/ServiceStack.Redis.csproj b/src/ServiceStack.Redis/ServiceStack.Redis.csproj index 5a6ed5c8..4e6274a9 100644 --- a/src/ServiceStack.Redis/ServiceStack.Redis.csproj +++ b/src/ServiceStack.Redis/ServiceStack.Redis.csproj @@ -42,12 +42,6 @@ - - - - - - \ No newline at end of file From 0271bf4a8dde6912659b07e4b71df444f385e98a Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 26 Jan 2022 13:31:38 +1100 Subject: [PATCH 102/107] Update README.md Update Readme to point to new home of Redis docs. --- README.md | 1147 +---------------------------------------------------- 1 file changed, 1 insertion(+), 1146 deletions(-) diff --git a/README.md b/README.md index 59a297ab..4dd3d022 100644 --- a/README.md +++ b/README.md @@ -1,1151 +1,6 @@ Follow [@ServiceStack](https://twitter.com/servicestack), [view the docs](https://docs.servicestack.net), use [StackOverflow](https://stackoverflow.com/questions/ask?tags=servicestack,servicestack.redis) or [Customer Forums](https://forums.servicestack.net/) for support. -# C#/.NET Client for Redis - -[ServiceStack's C# Redis Client](https://github.com/ServiceStack/ServiceStack.Redis) is a simple, high-performance and feature-rich C# Client for Redis with native support and [high-level abstractions](https://github.com/ServiceStack/ServiceStack.Redis/wiki/DesigningNoSqlDatabase) for serializing POCOs and Complex Types. - -There are a number of different APIs available with the `RedisClient` implementing the following interfaces: - - * [Caching Provider](http://docs.servicestack.net/caching) - If you are using Redis solely as a cache, you should bind to the ServiceStack's common interface as there already are In-Memory an Memcached implementations available in ServiceStack, allowing you to easily switch providers - * [IRedisNativeClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisNativeClient.cs) / [Async](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisNativeClientAsync.cs) - For those wanting a low-level raw byte access (where you can control your own serialization/deserialization) that map 1:1 with Redis operations of the same name. - -For most cases if you require access to Redis specific functionality you would want to bind to the interface below: - - * [IRedisClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClient.cs) / [Async](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClientAsync.cs) - Provides a friendlier, more descriptive API that lets you store values as strings (UTF8 encoding). - * [Redis generic client APIs](https://github.com/ServiceStack/ServiceStack/tree/master/src/ServiceStack.Interfaces/Redis/Generic) - created with `redis.As()` - returns a 'strongly-typed client' that provides a typed-interface for all redis value operations that works against any C#/.NET POCO type. - -The interfaces works cleanly with any IOC and allows your app logic to bind to implementation-free interfaces which can easily be mocked and substituted. - -An overview of class hierarchy for the C# Redis clients looks like: - - RedisTypedClient (POCO) > RedisClient (string) > RedisNativeClient (raw byte[]) - -With each client providing different layers of abstraction: - - * The RedisNativeClient exposes raw `byte[]` apis and does no marshalling and passes all values directly to redis. - * The RedisClient assumes `string` values and simply converts strings to UTF8 bytes before sending to Redis - * The RedisTypedClient provides a generic interface allowing you to add POCO values. POCOs are serialized using [ServiceStack.Text](https://github.com/ServiceStack/ServiceStack.Text) which is then converted to UTF8 bytes and sent to Redis. - -### API Overview - -[![Redis Client API](https://servicestack.net/images/redis-annotated-preview.png)](https://servicestack.net/images/redis-annotated.png) - -## Redis Connection Strings - -Redis Connection strings have been expanded to support the more versatile URI format which is now able to capture most of Redis Client -settings in a single connection string (akin to DB Connection strings). - -Redis Connection Strings supports multiple URI-like formats, from a simple **hostname** or **IP Address and port** pair to a -fully-qualified **URI** with multiple options specified on the QueryString. - -Some examples of supported formats: - - localhost - 127.0.0.1:6379 - redis://localhost:6379 - password@localhost:6379 - clientid:password@localhost:6379 - redis://clientid:password@localhost:6380?ssl=true&db=1 - -> More examples can be seen in -[ConfigTests.cs](https://github.com/ServiceStack/ServiceStack.Redis/blob/master/tests/ServiceStack.Redis.Tests/ConfigTests.cs) - -Any additional configuration can be specified as QueryString parameters. The full list of options that can be specified include: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SslboolIf this is an SSL connection
DbintThe Redis DB this connection should be set to
ClientstringA text alias to specify for this connection for analytic purposes
PasswordstringUrlEncoded version of the Password for this connection
ConnectTimeoutintTimeout in ms for making a TCP Socket connection
SendTimeoutintTimeout in ms for making a synchronous TCP Socket Send
ReceiveTimeoutintTimeout in ms for waiting for a synchronous TCP Socket Receive
IdleTimeOutSecsintTimeout in Seconds for an Idle connection to be considered active
NamespacePrefixstringUse a custom prefix for ServiceStack.Redis internal index colletions
- -## Download - - PM> Install-Package ServiceStack.Redis - -_Latest v4+ on NuGet is a [commercial release](https://servicestack.net/redis) with [free quotas](https://servicestack.net/download#free-quotas)._ - -#### [Getting Started with AWS ElastiCache Redis and ServiceStack](https://github.com/ServiceStackApps/AwsGettingStarted) - -ServiceStack.Redis has great support AWS's ElastiCache Redis solution, follow this guide to help getting up and running quickly: - -- [ElastiCache Redis](https://github.com/ServiceStackApps/AwsGettingStarted/blob/master/docs/redis-guide.md) - -## Redis Client Managers - -The recommended way to access `RedisClient` instances is to use one of the available Thread-Safe Client Managers below. Client Managers are connection factories which should be registered as a Singleton either in your IOC or static class. - -### RedisManagerPool - -With the enhanced Redis URI Connection Strings we've been able to simplify and streamline the existing `PooledRedisClientManager` implementation and have extracted it out into a new clients manager called `RedisManagerPool`. - -In addition to removing all above options on the Client Manager itself, readonly connection strings have also been removed so the configuration ends up much simpler and more aligned with the common use-case: - -```csharp -container.Register(c => - new RedisManagerPool(redisConnectionString)); -``` - -**Pooling Behavior** - -Any connections required after the maximum Pool size has been reached will be created and disposed outside of the Pool. By not being restricted to a maximum pool size, the pooling behavior in `RedisManagerPool` can maintain a smaller connection pool size at the cost of potentially having a higher opened/closed connection count. - -### PooledRedisClientManager - -If you prefer to define options on the Client Manager itself or you want to provide separate Read/Write and ReadOnly -(i.e. Master and Replica) redis-servers, use the `PooledRedisClientManager` instead: - -```csharp -container.Register(c => - new PooledRedisClientManager(redisReadWriteHosts, redisReadOnlyHosts) { - ConnectTimeout = 100, - //... - }); -``` - -**Pooling Behavior** - -The `PooledRedisClientManager` imposes a maximum connection limit and when its maximum pool size has been reached will instead block on any new connection requests until the next `RedisClient` is released back into the pool. If no client became available within `PoolTimeout`, a Pool `TimeoutException` will be thrown. - -#### Read Only Clients - -By default resolving a RedisClient with `GetRedisClient()` or `GetRedisClientAsync()` will return a client connected to the configured primary (master) host, if you also have replica (slave) hosts configured, you can access it with the `GetReadOnlyClient()` or `GetReadOnlyClientAsync()` APIs, e.g: - -```csharp -using var redisReadOnly = clientsManager.GetReadOnlyClient(); -``` - -### BasicRedisClientManager - -If don't want to use connection pooling (i.e. you're accessing a local redis-server instance) you can use a basic (non-pooled) Clients Manager which creates a new `RedisClient` instance each time: - -```csharp -container.Register(c => - new BasicRedisClientManager(redisConnectionString)); -``` - -### Accessing the Redis Client - -Once registered, accessing the RedisClient is the same in all Client Managers, e.g: - -```csharp -var clientsManager = container.Resolve(); -using var redis = clientsManager.GetClient(); - -redis.IncrementValue("counter"); -List days = redis.GetAllItemsFromList("days"); - -//Access Typed API -var redisTodos = redis.As(); - -redisTodos.Store(new Todo { - Id = redisTodos.GetNextSequence(), - Content = "Learn Redis", -}); - -var todo = redisTodos.GetById(1); - -//Access Native Client -var redisNative = (IRedisNativeClient)redis; - -redisNative.Incr("counter"); -List days = redisNative.LRange("days", 0, -1); -``` - -A more detailed list of the available RedisClient APIs used in the example can be seen in the C# interfaces below: - - - [IRedisClientsManager](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClientsManager.cs) - - [IRedisClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClient.cs) - - [IRedisNativeClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisNativeClient.cs) - - [IRedisSubscription](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisSubscription.cs) - -#### Pipeline & Transaction APIs - - - [IRedisTransaction](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisTransaction.cs) - - [IRedisPipelineShared](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Pipeline/IRedisPipelineShared.cs) - - [IRedisQueueableOperation](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Pipeline/IRedisQueueableOperation.cs) - - [IRedisQueueCompletableOperation](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Pipeline/IRedisQueueCompletableOperation.cs) - -#### Generic Client APIs - - - [IRedisTypedClient](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisTypedClient.cs) - - [IRedisHash](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisHash.Generic.cs) - - [IRedisList](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisList.Generic.cs) - - [IRedisSet](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisSet.Generic.cs) - - [IRedisSortedSet](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisSortedSet.Generic.cs) - - [IRedisTypedQueueableOperation](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisTypedQueueableOperation.cs) - -#### Server Collection APIs - - - [IRedisHash](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisHash.cs) - - [IRedisList](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisList.cs) - - [IRedisSet](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisSet.cs) - - [IRedisSortedSet](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisSortedSet.cs) - -### Async Redis - -The async support in ServiceStack.Redis is designed for optimal efficiency and uses `ValueTask` & other modern Async APIs only available in **.NET Standard 2.0** and **.NET Framework v4.7.2+** projects where there's async API equivalents for most sync APIs as contained within the Async Redis interfaces below: - - - [IRedisClientsManagerAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClientsManagerAsync.cs) - - [IRedisClientAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisClientAsync.cs) - - [IRedisNativeClientAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisNativeClientAsync.cs) - - [IRedisSubscriptionAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisSubscriptionAsync.cs) - -#### Async Pipeline & Transaction APIs - - - [IRedisTransactionAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisTransactionAsync.cs) - - [IRedisPipelineSharedAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Pipeline/IRedisPipelineSharedAsync.cs) - - [IRedisQueueableOperationAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Pipeline/IRedisQueueableOperationAsync.cs) - - [IRedisQueueCompletableOperationAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Pipeline/IRedisQueueCompletableOperationAsync.cs) - -#### Async Generic Client APIs - - - [IRedisTypedClientAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisTypedClientAsync.cs) - - [IRedisHashAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisHash.Generic.Async.cs) - - [IRedisListAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisList.Generic.Async.cs) - - [IRedisSetAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisSet.Generic.Async.cs) - - [IRedisSortedSetAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisSortedSet.Generic.Async.cs) - - [IRedisTypedTransactionAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisTypedTransactionAsync.cs) - - [IRedisTypedQueueableOperationAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/Generic/IRedisTypedQueueableOperationAsync.cs) - -#### Async Server Collection APIs - - - [IRedisHashAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisHashAsync.cs) - - [IRedisListAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisListAsync.cs) - - [IRedisSetAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisSetAsync.cs) - - [IRedisSortedSetAsync](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisSortedSetAsync.cs) - - -### Redis Async Usage - -All Redis Client Managers implement both `IRedisClientsManager` and `IRedisClientsManagerAsync` so IOC registrations remain the same which can continue to register against the existing `IRedisClientsManager` interface, e.g: - -```csharp -container.Register(c => - new RedisManagerPool(redisConnectionString)); -``` - -Where it can be used to resolve both sync `IRedisClient` and async `IRedisClientAsync` clients, e.g: - -```csharp -using var syncRedis = container.Resolve().GetClient(); -await using var asyncRedis = await container.Resolve().GetClientAsync(); -``` - -If you want to force async-only API usage could choose to just register `IRedisClientsManagerAsync` where it only lets you resolve async only `IRedisClientAsync` and `ICacheClientAsync` clients, e.g: - -```csharp -public void ConfigureServices(IServiceCollection services) -{ - services.AddSingleton(c => new RedisManagerPool()); -} - -//... - -public class MyDep -{ - private IRedisClientsManagerAsync manager; - public MyDep(IRedisClientsManagerAsync manager) => this.manager = manager; - - public async Task Incr(string key, uint value) - { - await using var redis = await manager.GetClientAsync(); - return await redis.IncrementAsync(key, value); - } -} -``` - -### Usage in ServiceStack - -Inside ServiceStack Services & Controllers we recommend using `GetRedisAsync()` to resolve an `IRedisClientAsync`: - -```csharp -public class MyService : Service -{ - public async Task Any(MyRequest request) - { - await using var redis = await GetRedisAsync(); - await redis.IncrementAsync(nameof(MyRequest), 1); - } -} - -public class HomeController : ServiceStackController -{ - public async Task Index() - { - await using var redis = await GetRedisAsync(); - await redis.IncrementAsync(nameof(HomeController), 1); - } -} -``` - -## [Redis Vue Desktop](https://sharpscript.net/sharp-apps/redis#redis-vue) - -Redis Vue is a simple user-friendly [Vue Desktop App](https://www.vuedesktop.com) for browsing data in Redis servers which takes advantages of the complex -type conventions built in the ServiceStack.Redis Client to provide a rich, human-friendly UI for navigating related datasets, enabling a fast and fluid browsing experience for your Redis servers. - -Install [.NET SDK](https://dotnet.microsoft.com/download) and run [install/app.ps1](https://servicestack.net/install/app.ps1) to install the `app` dotnet tool: - - powershell iwr gist.cafe/install.ps1 -useb | iex - -Then run `redis` Vue Desktop App in a browser: - -### [app://redis](app://redis) - -Or from the command-line: - - app open redis - -[![](https://sharpscript.net/assets/img/screenshots/redis.png)](https://sharpscript.net/sharp-apps/redis#redis-vue) - -## [Redis Sentinel](https://github.com/ServiceStack/ServiceStack.Redis/wiki/Redis-Sentinel) - -To use the new Sentinel support, instead of populating the Redis Client Managers with the -connection string of the master and slave instances you would create a single RedisSentinel -instance configured with the connection string of the running Redis Sentinels: - -```csharp -var sentinelHosts = new[]{ "sentinel1", "sentinel2:6390", "sentinel3" }; -var sentinel = new RedisSentinel(sentinelHosts, masterName: "mymaster"); -``` - -This configures a `RedisSentinel` with 3 sentinel hosts looking at **mymaster** group. -As the default port for sentinels when unspecified is **26379** and how RedisSentinel is able to -auto-discover other sentinels, the minimum configuration required is with a single Sentinel host: - -```csharp -var sentinel = new RedisSentinel("sentinel1"); -``` - -### Custom Redis Connection String - -The host the RedisSentinel is configured with only applies to that Sentinel Host, to use the -flexibility of [Redis Connection Strings](#redis-connection-strings) to apply configuration on -individual Redis Clients you need to register a custom `HostFilter`: - -```csharp -sentinel.HostFilter = host => $"{host}?db=1&RetryTimeout=5000"; -``` - -An alternative to using connection strings for configuring clients is to modify -[default configuration on RedisConfig](https://github.com/ServiceStack/ServiceStack.Redis/wiki/Redis-Config). - -### Change to use RedisManagerPool - -By default RedisSentinel uses a `PooledRedisClientManager`, this can be changed to use the -newer `RedisManagerPool` with: - -```csharp -sentinel.RedisManagerFactory = (master,replicas) => new RedisManagerPool(master); -``` - -### Start monitoring Sentinels - -Once configured, you can start monitoring the Redis Sentinel servers and access the pre-configured -client manager with: - -```csharp -IRedisClientsManager redisManager = sentinel.Start(); -``` - -Which as before, can be registered in your preferred IOC as a **singleton** instance: - -```csharp -container.Register(c => sentinel.Start()); -``` - -## [Configure Redis Sentinel Servers](https://github.com/ServiceStack/redis-config) - -[![Instant Redis Setup](https://raw.githubusercontent.com/ServiceStack/Assets/master/img/redis/instant-sentinel-setup.png)](https://github.com/ServiceStack/redis-config) - -See the -[redis config project](https://github.com/ServiceStack/redis-config) for a quick way to setup up -the minimal -[highly available Redis Sentinel configuration](https://github.com/ServiceStack/redis-config/blob/master/README.md#3x-sentinels-monitoring-1x-master-and-2x-slaves) -including start/stop scripts for instantly running multiple redis instances on a single (or multiple) -Windows, OSX or Linux servers. - -### [Redis Stats](https://github.com/ServiceStack/ServiceStack.Redis/wiki/Redis-Stats) - -You can use the `RedisStats` class for visibility and introspection into your running instances. -The [Redis Stats wiki](https://github.com/ServiceStack/ServiceStack.Redis/wiki/Redis-Stats) lists the stats available. - -## [Automatic Retries](https://github.com/ServiceStack/ServiceStack.Redis/wiki/Automatic-Retries) - -To improve the resilience of client connections, `RedisClient` will transparently retry failed -Redis operations due to Socket and I/O Exceptions in an exponential backoff starting from -**10ms** up until the `RetryTimeout` of **10000ms**. These defaults can be tweaked with: - -```csharp -RedisConfig.DefaultRetryTimeout = 10000; -RedisConfig.BackOffMultiplier = 10; -``` - -## [ServiceStack.Redis SSL Support](http://docs.servicestack.net/ssl-redis-azure) - -ServiceStack.Redis supports **SSL connections** making it suitable for accessing remote Redis server instances over a -**secure SSL connection**. - -![Azure Redis Cache](https://github.com/ServiceStack/Assets/raw/master/img/wikis/redis/azure-redis-instance.png) - -#### Specify SSL Protocol - -Support for changing the Ssl Protocols used for encrypted SSL connections can be set on the connection string using the `sslprotocols` modifier, e.g: - -```csharp -var connString = $"redis://{Host}?ssl=true&sslprotocols=Tls12&password={Password.UrlEncode()}"; -var redisManager = new RedisManagerPool(connString); -using var client = redisManager.GetClient(); -//... -``` - -### [Connecting to Azure Redis](http://docs.servicestack.net/ssl-redis-azure) - -As connecting to [Azure Redis Cache](http://azure.microsoft.com/en-us/services/cache/) via SSL was the primary use-case for this feature, -we've added a new -[Getting connected to Azure Redis via SSL](http://docs.servicestack.net/ssl-redis-azure) to help you get started. - -## [Redis GEO](https://github.com/ServiceStackApps/redis-geo) - -The [release of Redis 3.2.0](http://antirez.com/news/104) brings it exciting new -[GEO capabilities](http://redis.io/commands/geoadd) which will let you store Lat/Long coordinates in Redis -and query locations within a specified radius. To demonstrate this functionality we've created a new -[Redis GEO Live Demo](https://github.com/ServiceStackApps/redis-geo) which lets you click on anywhere in -the U.S. to find the list of nearest cities within a given radius, Live Demo at: https://redis.netcore.io - - -## Generic APIs for calling Custom Redis commands - -Most of the time when waiting to use a new [Redis Command](http://redis.io/commands) you'll need to wait for an updated version of -**ServiceStack.Redis** to add support for the new commands likewise there are times when the Redis Client doesn't offer every permutation -that redis-server supports. - -With the new `Custom` and `RawCommand` APIs on `IRedisClient` and `IRedisNativeClient` you can now use the RedisClient to send your own -custom commands that can call adhoc Redis commands: - -```csharp -public interface IRedisClient -{ - ... - RedisText Custom(params object[] cmdWithArgs); -} - -public interface IRedisNativeClient -{ - ... - RedisData RawCommand(params object[] cmdWithArgs); - RedisData RawCommand(params byte[][] cmdWithBinaryArgs); -} -``` - -These Custom APIs take a flexible `object[]` arguments which accepts any serializable value e.g. -`byte[]`, `string`, `int` as well as any user-defined Complex Types which are transparently serialized -as JSON and send across the wire as UTF-8 bytes. - -```csharp -var ret = Redis.Custom("SET", "foo", 1); // ret.Text = "OK" - -byte[] cmdSet = Commands.Set; -ret = Redis.Custom(cmdSet, "bar", "b"); // ret.Text = "OK" - -ret = Redis.Custom("GET", "foo"); // ret.Text = "1" -``` - -There are also -[convenient extension methods](https://github.com/ServiceStack/ServiceStack.Redis/blob/master/src/ServiceStack.Redis/RedisDataExtensions.cs) -on `RedisData` and `RedisText` that make it easy to access structured data, e.g: - -```csharp -var ret = Redis.Custom(Commands.Keys, "*"); -var keys = ret.GetResults(); // keys = ["foo", "bar"] - -ret = Redis.Custom(Commands.MGet, "foo", "bar"); -var values = ret.GetResults(); // values = ["1", "b"] - -Enum.GetNames(typeof(DayOfWeek)).ToList() - .ForEach(x => Redis.Custom(Commands.RPush, "DaysOfWeek", x)); -ret = Redis.Custom(Commands.LRange, "DaysOfWeek", 1, -2); -var weekDays = ret.GetResults(); - -weekDays.PrintDump(); // ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] -``` - -and some more examples using Complex Types with the Custom APIs: - -```csharp -var ret = Redis.Custom(Commands.Set, "foo", new Poco { Name = "Bar" }); // ret.Text = "OK" - -ret = Redis.Custom(Commands.Get, "foo"); // ret.Text = {"Name":"Bar"} -Poco dto = ret.GetResult(); - -dto.Name.Print(); // Bar -``` - -This API is used in most of Redis React UI's -[redis.js](https://github.com/ServiceStackApps/RedisReact/blob/master/src/RedisReact/RedisReact/js/redis.js) -JavaScript client library where Redis server commands are made available via the -[single ServiceStack Service](https://github.com/ServiceStackApps/RedisReact/blob/a1b66603d52d2f18b96227fc455ecb5323e424c8/src/RedisReact/RedisReact.ServiceInterface/RedisServices.cs#L73): - -```csharp -public object Any(CallRedis request) -{ - var args = request.Args.ToArray(); - var response = new CallRedisResponse { Result = Redis.Custom(args) }; - return response; -} -``` - -## Managed Pub/Sub Server - -The Pub/Sub engine powering -[Redis ServerEvents](https://github.com/ServiceStack/ServiceStack/wiki/Redis-Server-Events) and -[Redis MQ](https://github.com/ServiceStack/ServiceStack/wiki/Messaging-and-Redis) has been extracted -and encapsulated it into a re-usable class that can be used independently for handling messages -published to specific [Redis Pub/Sub](http://redis.io/commands#pubsub) channels. - -`RedisPubSubServer` processes messages in a managed background thread that **automatically reconnects** -when the redis-server connection fails and works like an independent background Service that can be -stopped and started on command. - -The public API is captured in the -[IRedisPubSubServer](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Redis/IRedisPubSubServer.cs) interface: - -```csharp -public interface IRedisPubSubServer : IDisposable -{ - IRedisClientsManager ClientsManager { get; } - // What Channels it's subscribed to - string[] Channels { get; } - - // Run once on initial StartUp - Action OnInit { get; set; } - // Called each time a new Connection is Started - Action OnStart { get; set; } - // Invoked when Connection is broken or Stopped - Action OnStop { get; set; } - // Invoked after Dispose() - Action OnDispose { get; set; } - - // Fired when each message is received - Action OnMessage { get; set; } - // Fired after successfully subscribing to the specified channels - Action OnUnSubscribe { get; set; } - // Called when an exception occurs - Action OnError { get; set; } - // Called before attempting to Failover to a new redis master - Action OnFailover { get; set; } - - int? KeepAliveRetryAfterMs { get; set; } - // The Current Time for RedisServer - DateTime CurrentServerTime { get; } - - // Current Status: Starting, Started, Stopping, Stopped, Disposed - string GetStatus(); - // Different life-cycle stats - string GetStatsDescription(); - - // Subscribe to specified Channels and listening for new messages - IRedisPubSubServer Start(); - // Close active Connection and stop running background thread - void Stop(); - // Stop than Start - void Restart(); -} -``` -### Usage - -To use `RedisPubSubServer`, initialize it with the channels you want to subscribe to and assign handlers -for each of the events you want to handle. At a minimum you'll want to handle `OnMessage`: - -```csharp -var clientsManager = new PooledRedisClientManager(); -var redisPubSub = new RedisPubSubServer(clientsManager, "channel-1", "channel-2") { - OnMessage = (channel, msg) => "Received '{0}' from '{1}'".Print(msg, channel) - }.Start(); -``` - -Calling `Start()` after it's initialized will get it to start listening and processing any messages -published to the subscribed channels. - -### Lex Operations - -The new [ZRANGEBYLEX](http://redis.io/commands/zrangebylex) sorted set operations allowing you to query a sorted set lexically have been added. -A good showcase for this is available on [autocomplete.redis.io](http://autocomplete.redis.io/). - -These new operations are available as a 1:1 mapping with redis-server on `IRedisNativeClient`: - -```csharp -public interface IRedisNativeClient -{ - ... - byte[][] ZRangeByLex(string setId, string min, string max, int? skip, int? take); - long ZLexCount(string setId, string min, string max); - long ZRemRangeByLex(string setId, string min, string max); -} -``` - -And the more user-friendly APIs under `IRedisClient`: - -```csharp -public interface IRedisClient -{ - ... - List SearchSortedSet(string setId, string start=null, string end=null); - long SearchSortedSetCount(string setId, string start=null, string end=null); - long RemoveRangeFromSortedSetBySearch(string setId, string start=null, string end=null); -} -``` - -Just like NuGet version matchers, Redis uses `[` char to express inclusiveness and `(` char for exclusiveness. -Since the `IRedisClient` APIs defaults to inclusive searches, these two APIs are the same: - -```csharp -Redis.SearchSortedSetCount("zset", "a", "c") -Redis.SearchSortedSetCount("zset", "[a", "[c") -``` - -Alternatively you can specify one or both bounds to be exclusive by using the `(` prefix, e.g: - -```csharp -Redis.SearchSortedSetCount("zset", "a", "(c") -Redis.SearchSortedSetCount("zset", "(a", "(c") -``` - -More API examples are available in [LexTests.cs](https://github.com/ServiceStack/ServiceStack.Redis/blob/master/tests/ServiceStack.Redis.Tests/LexTests.cs). - -### HyperLog API - -The development branch of Redis server (available when v3.0 is released) includes an ingenious algorithm to approximate the unique elements in a set with maximum space and time efficiency. For details about how it works see Redis's creator Salvatore's blog who [explains it in great detail](http://antirez.com/news/75). Essentially it lets you maintain an efficient way to count and merge unique elements in a set without having to store its elements. -A Simple example of it in action: - -```csharp -redis.AddToHyperLog("set1", "a", "b", "c"); -redis.AddToHyperLog("set1", "c", "d"); -var count = redis.CountHyperLog("set1"); //4 - -redis.AddToHyperLog("set2", "c", "d", "e", "f"); - -redis.MergeHyperLogs("mergedset", "set1", "set2"); - -var mergeCount = redis.CountHyperLog("mergedset"); //6 -``` - -### Scan APIs - -Redis v2.8 introduced a beautiful new [SCAN](http://redis.io/commands/scan) operation that provides an optimal strategy for traversing a redis instance entire keyset in managable-size chunks utilizing only a client-side cursor and without introducing any server state. It's a higher performance alternative and should be used instead of [KEYS](http://redis.io/commands/keys) in application code. SCAN and its related operations for traversing members of Sets, Sorted Sets and Hashes are now available in the Redis Client in the following APIs: - -```csharp -public interface IRedisClient -{ - ... - IEnumerable ScanAllKeys(string pattern = null, int pageSize = 1000); - IEnumerable ScanAllSetItems(string setId, string pattern = null, int pageSize = 1000); - IEnumerable> ScanAllSortedSetItems(string setId, string pattern = null, int pageSize = 1000); - IEnumerable> ScanAllHashEntries(string hashId, string pattern = null, int pageSize = 1000); -} - -public interface IRedisClientAsync -{ - IAsyncEnumerable ScanAllKeysAsync(string pattern = null, int pageSize, CancellationToken ct); - IAsyncEnumerable ScanAllSetItemsAsync(string setId, string pattern = null, int pageSize, CancellationToken ct); - IAsyncEnumerable> ScanAllSortedSetItemsAsync(string setId, string pattern = null, int pageSize, ct); - IAsyncEnumerable> ScanAllHashEntriesAsync(string hashId, string pattern = null, int pageSize, ct); -} - -//Low-level API -public interface IRedisNativeClient -{ - ... - ScanResult Scan(ulong cursor, int count = 10, string match = null); - ScanResult SScan(string setId, ulong cursor, int count = 10, string match = null); - ScanResult ZScan(string setId, ulong cursor, int count = 10, string match = null); - ScanResult HScan(string hashId, ulong cursor, int count = 10, string match = null); -} - -public interface IRedisNativeClientAsync -{ - ValueTask ScanAsync(ulong cursor, int count = 10, string match = null, CancellationToken ct); - ValueTask SScanAsync(string setId, ulong cursor, int count = 10, string match = null, CancellationToken ct); - ValueTask ZScanAsync(string setId, ulong cursor, int count = 10, string match = null, CancellationToken ct); - ValueTask HScanAsync(string hashId, ulong cursor, int count = 10, string match = null, CancellationToken ct); -} -``` - -The `IRedisClient` provides a higher-level API that abstracts away the client cursor to expose a lazy Enumerable sequence to provide an optimal way to stream scanned results that integrates nicely with LINQ, e.g: - -```csharp -var scanUsers = Redis.ScanAllKeys("urn:User:*"); -var sampleUsers = scanUsers.Take(10000).ToList(); //Stop after retrieving 10000 user keys -``` - -### Efficient SCAN in LUA - -The C# API below returns the first 10 results matching the `key:*` pattern: - -```csharp -var keys = Redis.ScanAllKeys(pattern: "key:*", pageSize: 10) - .Take(10).ToList(); -``` - -However the C# Streaming API above requires an unknown number of Redis Operations (bounded to the number of keys in Redis) -to complete the request. The number of SCAN calls can be reduced by choosing a higher `pageSize` to tell Redis to scan more keys -each time the SCAN operation is called. - -As the number of API calls has the potential to result in a large number of Redis Operations, it can end up yielding an unacceptable -delay due to the latency of multiple dependent remote network calls. An easy solution is to instead have the multiple SCAN calls -performed in-process on the Redis Server, eliminating the network latency of multiple SCAN calls, e.g: - -```csharp -const string FastScanScript = @" -local limit = tonumber(ARGV[2]) -local pattern = ARGV[1] -local cursor = 0 -local len = 0 -local results = {} -repeat - local r = redis.call('scan', cursor, 'MATCH', pattern, 'COUNT', limit) - cursor = tonumber(r[1]) - for k,v in ipairs(r[2]) do - table.insert(results, v) - len = len + 1 - if len == limit then break end - end -until cursor == 0 or len == limit -return results"; - -RedisText r = redis.ExecLua(FastScanScript, "key:*", "10"); -r.Children.Count.Print() //= 10 -``` - -The `ExecLua` API returns this complex LUA table response in the `Children` collection of the `RedisText` Response. - -#### Alternative Complex API Response - -Another way to return complex data structures in a LUA operation is to serialize the result as JSON - -```lua -return cjson.encode(results) -``` - -Which you can access as raw JSON by parsing the response as a String with: - -```csharp -string json = redis.ExecLuaAsString(FastScanScript, "key:*", "10"); -``` - -> This is also the approach used in Redis React's -[RedisServices](https://github.com/ServiceStackApps/RedisReact/blob/a1b66603d52d2f18b96227fc455ecb5323e424c8/src/RedisReact/RedisReact.ServiceInterface/RedisServices.cs#L60). - -### ExecCachedLua - -ExecCachedLua is a convenient high-level API that eliminates the bookkeeping required for executing high-performance server LUA -Scripts which suffers from many of the problems that RDBMS stored procedures have which depends on pre-existing state in the RDBMS -that needs to be updated with the latest version of the Stored Procedure. - -With Redis LUA you either have the option to send, parse, load then execute the entire LUA script each time it's called or -alternatively you could pre-load the LUA Script into Redis once on StartUp and then execute it using the Script's SHA1 hash. -The issue with this is that if the Redis server is accidentally flushed you're left with a broken application relying on a -pre-existing script that's no longer there. The new `ExecCachedLua` API provides the best of both worlds where it will always -execute the compiled SHA1 script, saving bandwidth and CPU but will also re-create the LUA Script if it no longer exists. - -You can instead execute the compiled LUA script above by its SHA1 identifier, which continues to work regardless if it never existed -or was removed at runtime, e.g: - -```csharp -// #1: Loads LUA script and caches SHA1 hash in Redis Client -r = redis.ExecCachedLua(FastScanScript, sha1 => - redis.ExecLuaSha(sha1, "key:*", "10")); - -// #2: Executes using cached SHA1 hash -r = redis.ExecCachedLua(FastScanScript, sha1 => - redis.ExecLuaSha(sha1, "key:*", "10")); - -// Deletes all existing compiled LUA scripts -redis.ScriptFlush(); - -// #3: Executes using cached SHA1 hash, gets NOSCRIPT Error, -// re-creates then re-executes the LUA script using its SHA1 hash -r = redis.ExecCachedLua(FastScanScript, sha1 => - redis.ExecLuaSha(sha1, "key:*", "10")); -``` - -### IRedisClient LUA APIs - -The `IRedisClient` APIs for [redis server-side LUA support](http://redis.io/commands/eval) have been re-factored into the more user-friendly APIs below: - -```csharp -public interface IRedisClient -{ - //Eval/Lua operations - T ExecCachedLua(string scriptBody, Func scriptSha1); - - RedisText ExecLua(string body, params string[] args); - RedisText ExecLua(string luaBody, string[] keys, string[] args); - RedisText ExecLuaSha(string sha1, params string[] args); - RedisText ExecLuaSha(string sha1, string[] keys, string[] args); - - string ExecLuaAsString(string luaBody, params string[] args); - string ExecLuaAsString(string luaBody, string[] keys, string[] args); - string ExecLuaShaAsString(string sha1, params string[] args); - string ExecLuaShaAsString(string sha1, string[] keys, string[] args); - - int ExecLuaAsInt(string luaBody, params string[] args); - int ExecLuaAsInt(string luaBody, string[] keys, string[] args); - int ExecLuaShaAsInt(string sha1, params string[] args); - int ExecLuaShaAsInt(string sha1, string[] keys, string[] args); - - List ExecLuaAsList(string luaBody, params string[] args); - List ExecLuaAsList(string luaBody, string[] keys, string[] args); - List ExecLuaShaAsList(string sha1, params string[] args); - List ExecLuaShaAsList(string sha1, string[] keys, string[] args); - - string CalculateSha1(string luaBody); - - bool HasLuaScript(string sha1Ref); - Dictionary WhichLuaScriptsExists(params string[] sha1Refs); - void RemoveAllLuaScripts(); - void KillRunningLuaScript(); - string LoadLuaScript(string body); -} -``` - -### Usage Examples - -Here's how you can implement a **ZPOP** in Lua to remove the items with the lowest rank from a sorted set: - -```csharp -var luaBody = @" - local val = redis.call('zrange', KEYS[1], 0, ARGV[1]-1) - if val then redis.call('zremrangebyrank', KEYS[1], 0, ARGV[1]-1) end - return val"; - -var i = 0; -var alphabet = 26.Times(c => ((char)('A' + c)).ToString()); -alphabet.ForEach(x => Redis.AddItemToSortedSet("zalphabet", x, i++)); - -//Remove the letters with the lowest rank from the sorted set 'zalphabet' -var letters = Redis.ExecLuaAsList(luaBody, keys: new[] { "zalphabet" }, args: new[] { "3" }); -letters.PrintDump(); //[A, B, C] -``` - -And how to implement **ZREVPOP** to remove items with the highest rank from a sorted set: - -```csharp -var luaBody = @" - local val = redis.call('zrange', KEYS[1], -ARGV[1], -1) - if val then redis.call('zremrangebyrank', KEYS[1], -ARGV[1], -1) end - return val"; - -var i = 0; -var alphabet = 26.Times(c => ((char)('A' + c)).ToString()); -alphabet.ForEach(x => Redis.AddItemToSortedSet("zalphabet", x, i++)); - -//Remove the letters with the highest rank from the sorted set 'zalphabet' -List letters = Redis.ExecLuaAsList(luaBody, - keys: new[] { "zalphabet" }, args: new[] { "3" }); - -letters.PrintDump(); //[X, Y, Z] -``` - -### Other examples - -Returning an `int`: - -```csharp -int intVal = Redis.ExecLuaAsInt("return 123"); //123 -int intVal = Redis.ExecLuaAsInt("return ARGV[1] + ARGV[2]", "10", "20"); //30 -``` - -Returning an `string`: - -```csharp -//Hello, Redis Lua! -var strVal = Redis.ExecLuaAsString(@"return 'Hello, ' .. ARGV[1] .. '!'", "Redis Lua"); -``` - -Returning a `List` of strings: - -```csharp -Enum.GetNames(typeof(DayOfWeek)).ToList() - .ForEach(x => Redis.AddItemToList("DaysOfWeek", x)); - -var daysOfWeek = Redis.ExecLuaAsList("return redis.call('LRANGE', 'DaysOfWeek', 0, -1)"); -daysOfWeek.PrintDump(); //[Sunday, Monday, Tuesday, ...] -``` - -More examples can be found in the [Redis Eval Lua tests](https://github.com/ServiceStack/ServiceStack.Redis/blob/master/tests/ServiceStack.Redis.Tests/RedisClientEvalTests.cs) - -### Debugging Data Corruption Issues - -An issue that can be hard to debug is if the same `RedisClient` instance is shared across multiple threads which can result in returning corrupted data. -Typically this is a result of using `IRedisClient` field in a singleton instance or sharing it as a static instance. To prevent this, each Thread that -uses Redis should retrieve the redis client within a using statement, e.g: - -```csharp -using var redis = redisManager.GetClient(); -//... -``` - -Unfortunately the call-site which returns the corrupted response or runtime Exception doesn't identify where else the Redis client instance was being used. -To help identify where client instances are being used you can assert that the client is only used in the Thread that resolved it from the pool with: - -```csharp -RedisConfig.AssertAccessOnlyOnSameThread = true; -``` - -This captures the Thread's StackTrace each time the client is resolved from the pool which as it adds a lot of overhead, should only be enabled when debugging connection issues. - -If it does detect the client is being accessed from a different thread it will throw a `InvalidAccessException` with the message containing the different **Thread Ids** and the **original StackTrace** where the client was resolved from the pool. You can compare this with the StackTrace of the Exception to hopefully identify where the client is being improperly used. - -#### Avoiding Concurrent Usage issues - -What to look out for in your code-base to prevent against multiple concurrent usage of a `IRedisClient` instance: - - - Use `IRedisClient` redis instance client within a `using` statement - - Never use a client instance after it has been disposed - - Never use (or return) a "server collection or resource" (e.g. [Redis.Lists](#simple-example-using-redis-lists), lock) after the client has been disposed - - Never keep a Singleton or `static` instance to a redis client (just the `IRedisClientsManager` factory) - - Never use the same redis client in multiple threads, i.e. have each thread resolve their own client from the factory - -## Copying - -Since September 2013, ServiceStack source code is available under GNU Affero General Public License/FOSS License Exception, see license.txt in the source. Alternative commercial licensing is also available, see https://servicestack.net/pricing for details. - -### [Docs and Downloads for older v3 BSD releases](https://github.com/ServiceStackV3/ServiceStackV3) - -## Contributing - -Contributors need to approve the [Contributor License Agreement](https://docs.google.com/forms/d/16Op0fmKaqYtxGL4sg7w_g-cXXyCoWjzppgkuqzOeKyk/viewform) before any code will be reviewed, see the [Contributing wiki](https://github.com/ServiceStack/ServiceStack/wiki/Contributing) for more details. - -### Redis Server builds for Windows - - * [Redis on Windows](https://github.com/ServiceStack/redis-windows) - * [Redis 5.0.10 for Windows - maintained OSS port of MS Open Tech](https://github.com/tporadowski/redis/) - * [Memurai - Redis on Windows, derived from MS Open Tech (now defunct); commercial with free developer edition](https://www.memurai.com) - * [Downloads for Cygwin 32bit Redis Server Windows builds](http://code.google.com/p/servicestack/wiki/RedisWindowsDownload). - * [Project that lets you run Redis as a Windows Service](https://github.com/rgl/redis) - * [Another Redis as a Windows Service project, which allows you to run separate service for each Redis instance](https://github.com/kcherenkov/redis-windows-service) - * [Downloads for MinGW 32bit and 64bit Redis Server Windows builds](http://github.com/dmajkic/redis/downloads) - -### Redis Virtual Machines - - * [Run Redis in a Vagrant virtual machine](https://github.com/JasonPunyon/redishobo) - -# Getting Started with the C# Redis client - -###[C# Redis Client wiki](https://github.com/ServiceStack/ServiceStack.Redis/wiki) -Contains all the examples, tutorials and resources you need to get you up to speed with common operations and the latest features. - -[Useful Links on Redis server](https://github.com/ServiceStack/ServiceStack.Redis/wiki/Useful-Redis-Links) - -### Specific Examples - * [Using Transactions in Redis (i.e. MULTI/EXEC/DISCARD)](https://github.com/ServiceStack/ServiceStack.Redis/wiki/RedisTransactions) - * [Using Redis's built-in Publish/Subscribe pattern for high performance network notifications](https://github.com/ServiceStack/ServiceStack.Redis/wiki/RedisPubSub) - * [Using Redis to create high performance *distributed locks* spannable across multiple app servers](https://github.com/ServiceStack/ServiceStack.Redis/wiki/RedisLocks) - -# Simple example using Redis Lists - -Below is a simple example to give you a flavour of how easy it is to use some of Redis's advanced data structures - in this case Redis Lists: -_Full source code of this example is [viewable online](https://github.com/ServiceStack/ServiceStack.Redis/blob/master/tests/ServiceStack.Redis.Tests/ShippersExample.cs)_ - -```csharp -using var redisClient = new RedisClient(); -//Create a 'strongly-typed' API that makes all Redis Value operations to apply against Shippers -IRedisTypedClient redis = redisClient.As(); - -//Redis lists implement IList while Redis sets implement ICollection -var currentShippers = redis.Lists["urn:shippers:current"]; -var prospectiveShippers = redis.Lists["urn:shippers:prospective"]; - -currentShippers.Add( - new Shipper { - Id = redis.GetNextSequence(), - CompanyName = "Trains R Us", - DateCreated = DateTime.UtcNow, - ShipperType = ShipperType.Trains, - UniqueRef = Guid.NewGuid() - }); - -currentShippers.Add( - new Shipper { - Id = redis.GetNextSequence(), - CompanyName = "Planes R Us", - DateCreated = DateTime.UtcNow, - ShipperType = ShipperType.Planes, - UniqueRef = Guid.NewGuid() - }); - -var lameShipper = new Shipper { - Id = redis.GetNextSequence(), - CompanyName = "We do everything!", - DateCreated = DateTime.UtcNow, - ShipperType = ShipperType.All, - UniqueRef = Guid.NewGuid() -}; - -currentShippers.Add(lameShipper); - -Dump("ADDED 3 SHIPPERS:", currentShippers); - -currentShippers.Remove(lameShipper); - -Dump("REMOVED 1:", currentShippers); - -prospectiveShippers.Add( - new Shipper { - Id = redis.GetNextSequence(), - CompanyName = "Trucks R Us", - DateCreated = DateTime.UtcNow, - ShipperType = ShipperType.Automobiles, - UniqueRef = Guid.NewGuid() - }); - -Dump("ADDED A PROSPECTIVE SHIPPER:", prospectiveShippers); - -redis.PopAndPushBetweenLists(prospectiveShippers, currentShippers); - -Dump("CURRENT SHIPPERS AFTER POP n' PUSH:", currentShippers); -Dump("PROSPECTIVE SHIPPERS AFTER POP n' PUSH:", prospectiveShippers); - -var poppedShipper = redis.PopFromList(currentShippers); -Dump("POPPED a SHIPPER:", poppedShipper); -Dump("CURRENT SHIPPERS AFTER POP:", currentShippers); - -//reset sequence and delete all lists -redis.SetSequence(0); -redis.Remove(currentShippers, prospectiveShippers); -Dump("DELETING CURRENT AND PROSPECTIVE SHIPPERS:", currentShippers); -``` - - == EXAMPLE OUTPUT == - - ADDED 3 SHIPPERS: - Id:1,CompanyName:Trains R Us,ShipperType:Trains,DateCreated:2010-01-31T11:53:37.7169323Z,UniqueRef:d17c5db0415b44b2ac5da7b6ebd780f5 - Id:2,CompanyName:Planes R Us,ShipperType:Planes,DateCreated:2010-01-31T11:53:37.799937Z,UniqueRef:e02a73191f4b4e7a9c44eef5b5965d06 - Id:3,CompanyName:We do everything!,ShipperType:All,DateCreated:2010-01-31T11:53:37.8009371Z,UniqueRef:d0c249bbbaf84da39fc4afde1b34e332 - - REMOVED 1: - Id:1,CompanyName:Trains R Us,ShipperType:Trains,DateCreated:2010-01-31T11:53:37.7169323Z,UniqueRef:d17c5db0415b44b2ac5da7b6ebd780f5 - Id:2,CompanyName:Planes R Us,ShipperType:Planes,DateCreated:2010-01-31T11:53:37.799937Z,UniqueRef:e02a73191f4b4e7a9c44eef5b5965d06 - - ADDED A PROSPECTIVE SHIPPER: - Id:4,CompanyName:Trucks R Us,ShipperType:Automobiles,DateCreated:2010-01-31T11:53:37.8539401Z,UniqueRef:67d7d4947ebc4b0ba5c4d42f5d903bec - - CURRENT SHIPPERS AFTER POP n' PUSH: - Id:4,CompanyName:Trucks R Us,ShipperType:Automobiles,DateCreated:2010-01-31T11:53:37.8539401Z,UniqueRef:67d7d4947ebc4b0ba5c4d42f5d903bec - Id:1,CompanyName:Trains R Us,ShipperType:Trains,DateCreated:2010-01-31T11:53:37.7169323Z,UniqueRef:d17c5db0415b44b2ac5da7b6ebd780f5 - Id:2,CompanyName:Planes R Us,ShipperType:Planes,DateCreated:2010-01-31T11:53:37.799937Z,UniqueRef:e02a73191f4b4e7a9c44eef5b5965d06 - - PROSPECTIVE SHIPPERS AFTER POP n' PUSH: - - POPPED a SHIPPER: - Id:2,CompanyName:Planes R Us,ShipperType:Planes,DateCreated:2010-01-31T11:53:37.799937Z,UniqueRef:e02a73191f4b4e7a9c44eef5b5965d06 - - CURRENT SHIPPERS AFTER POP: - Id:4,CompanyName:Trucks R Us,ShipperType:Automobiles,DateCreated:2010-01-31T11:53:37.8539401Z,UniqueRef:67d7d4947ebc4b0ba5c4d42f5d903bec - Id:1,CompanyName:Trains R Us,ShipperType:Trains,DateCreated:2010-01-31T11:53:37.7169323Z,UniqueRef:d17c5db0415b44b2ac5da7b6ebd780f5 - - DELETING CURRENT AND PROSPECTIVE SHIPPERS: - -More examples are available in the [RedisExamples Redis examples page] and in the comprehensive -[test suite](https://github.com/ServiceStack/ServiceStack.Redis/tree/master/tests/ServiceStack.Redis.Tests) - - -## Speed -One of the best things about Redis is the speed - it is quick. - -[This example](https://github.com/ServiceStack/ServiceStack.Redis/blob/master/tests/ServiceStack.Redis.Tests/RedisClientTests.cs) -below stores and gets the entire [Northwind database](http://code.google.com/p/servicestack/source/browse/trunk/Common/Northwind.Benchmarks/Northwind.Common/DataModel/NorthwindData.cs) (3202 records) in less *1.2 secs* - we've never had it so quick! - -_(Running inside a VS.NET/R# unit test on a 3 year old iMac)_ - -```csharp -using var client = new RedisClient(); - -var before = DateTime.Now; -client.StoreAll(NorthwindData.Categories); -client.StoreAll(NorthwindData.Customers); -client.StoreAll(NorthwindData.Employees); -client.StoreAll(NorthwindData.Shippers); -client.StoreAll(NorthwindData.Orders); -client.StoreAll(NorthwindData.Products); -client.StoreAll(NorthwindData.OrderDetails); -client.StoreAll(NorthwindData.CustomerCustomerDemos); -client.StoreAll(NorthwindData.Regions); -client.StoreAll(NorthwindData.Territories); -client.StoreAll(NorthwindData.EmployeeTerritories); - -Console.WriteLine("Took {0}ms to store the entire Northwind database ({1} records)", - (DateTime.Now - before).TotalMilliseconds, totalRecords); - -before = DateTime.Now; -var categories = client.GetAll(); -var customers = client.GetAll(); -var employees = client.GetAll(); -var shippers = client.GetAll(); -var orders = client.GetAll(); -var products = client.GetAll(); -var orderDetails = client.GetAll(); -var customerCustomerDemos = client.GetAll(); -var regions = client.GetAll(); -var territories = client.GetAll(); -var employeeTerritories = client.GetAll(); - -Console.WriteLine("Took {0}ms to get the entire Northwind database ({1} records)", - (DateTime.Now - before).TotalMilliseconds, totalRecords); -/* -== EXAMPLE OUTPUT == - -Took 1020.0583ms to store the entire Northwind database (3202 records) -Took 132.0076ms to get the entire Northwind database (3202 records) -*/ -``` - - -Note: The total time taken includes an extra Redis operation for each record to store the id in a Redis set for each -type as well as serializing and de-serializing each record using Service Stack's TypeSerializer. - +# ServiceStack.Redis Docs have [moved to ServiceStack Docs](https://docs.servicestack.net/redis/overview) # Community Resources From a83c66ed6a1b1269f0eabfa947fd7bb733f36275 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 26 Jan 2022 16:26:51 +0800 Subject: [PATCH 103/107] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4dd3d022..11ee23da 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ Follow [@ServiceStack](https://twitter.com/servicestack), [view the docs](https://docs.servicestack.net), use [StackOverflow](https://stackoverflow.com/questions/ask?tags=servicestack,servicestack.redis) or [Customer Forums](https://forums.servicestack.net/) for support. -# ServiceStack.Redis Docs have [moved to ServiceStack Docs](https://docs.servicestack.net/redis/overview) +# Read ServiceStack.Redis Docs at [docs.servicestack.net/redis](https://docs.servicestack.net/redis/overview) -# Community Resources +## Community Resources - [Synchronizing Redis local caches for distributed multi-subscriber scenarios](http://toreaurstad.blogspot.no/2015/09/synchronizing-redis-local-caches-for.html) by [@Tore_Aurstad](https://twitter.com/Tore_Aurstad) - [Distributed Caching using Redis Server with .NET/C# Client](http://www.codeproject.com/Articles/636730/Distributed-Caching-using-Redis) by [Sem.Shekhovtsov](http://www.codeproject.com/script/Membership/View.aspx?mid=6495187) From 42ee5aa72a0cc8dd1cc5f1414f4440a576bf8a6a Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 26 Jan 2022 17:25:15 +0800 Subject: [PATCH 104/107] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 11ee23da..a605e8ef 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Follow [@ServiceStack](https://twitter.com/servicestack), [view the docs](https://docs.servicestack.net), use [StackOverflow](https://stackoverflow.com/questions/ask?tags=servicestack,servicestack.redis) or [Customer Forums](https://forums.servicestack.net/) for support. -# Read ServiceStack.Redis Docs at [docs.servicestack.net/redis](https://docs.servicestack.net/redis/overview) +# Read ServiceStack.Redis Docs at [docs.servicestack.net/redis](https://docs.servicestack.net/redis/) ## Community Resources From 9d8a1eb5a360cce1456e84d49a42e0c09728e46c Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Mon, 31 Jan 2022 02:24:17 +0800 Subject: [PATCH 105/107] bump to v6.0.2 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index e4b6d4e5..3ce66421 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 6.0.0 + 6.0.2 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 187fbd04..04628190 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 6.0.0 + 6.0.2 latest false From 3aca78a5e335cedf15dd3b09412710227884a7e3 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Tue, 1 Feb 2022 21:25:51 +0800 Subject: [PATCH 106/107] bump to v6.0.3 --- src/Directory.Build.props | 2 +- tests/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 3ce66421..2a6ba4e0 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,7 @@ - 6.0.2 + 6.0.3 ServiceStack ServiceStack, Inc. © 2008-2018 ServiceStack, Inc diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 04628190..7eb7717e 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - 6.0.2 + 6.0.3 latest false From 33faac719a248ca5ced5bee2e6e85b3fbbce4bf8 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Wed, 16 Feb 2022 12:46:00 +0800 Subject: [PATCH 107/107] Update README.md --- README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.md b/README.md index a605e8ef..cfbbe837 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,4 @@ Follow [@ServiceStack](https://twitter.com/servicestack), [view the docs](https: # Read ServiceStack.Redis Docs at [docs.servicestack.net/redis](https://docs.servicestack.net/redis/) -## Community Resources - - - [Synchronizing Redis local caches for distributed multi-subscriber scenarios](http://toreaurstad.blogspot.no/2015/09/synchronizing-redis-local-caches-for.html) by [@Tore_Aurstad](https://twitter.com/Tore_Aurstad) - - [Distributed Caching using Redis Server with .NET/C# Client](http://www.codeproject.com/Articles/636730/Distributed-Caching-using-Redis) by [Sem.Shekhovtsov](http://www.codeproject.com/script/Membership/View.aspx?mid=6495187) - - [Fan Messaging with ServiceStack.Redis](http://cornishdev.wordpress.com/2013/04/04/fan-messaging-with-servicestack-redis/) by [miket](http://stackoverflow.com/users/1804544/miket) - - [Getting started with Redis in ASP.NET under Windows](http://maxivak.com/getting-started-with-redis-and-asp-net-mvc-under-windows/) by [@maxivak](https://twitter.com/maxivak) - +### This repository [has moved](https://docs.servicestack.net/mono-repo) to [github.com/ServiceStack/ServiceStack/ServiceStack.Redis](https://github.com/ServiceStack/ServiceStack/tree/main/ServiceStack.Redis)