diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1cd858b8..fc4118ab 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -45,7 +45,7 @@ jobs: run: make GRPC_WEB=${{ matrix.grpc-web }} build - name: Test - run: make test + run: make prod-test build_examples: runs-on: ubuntu-latest diff --git a/.github/workflows/on-push-to-main-branch.yaml b/.github/workflows/on-push-to-main-branch.yaml index 5f912c6d..41ab7404 100644 --- a/.github/workflows/on-push-to-main-branch.yaml +++ b/.github/workflows/on-push-to-main-branch.yaml @@ -32,7 +32,7 @@ jobs: run: make GRPC_WEB=${{ matrix.grpc-web }} build - name: Test - run: make test + run: make prod-test generate_readme: runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index 1fb3fa66..809b10b6 100644 --- a/Makefile +++ b/Makefile @@ -33,13 +33,11 @@ ifneq (,$(findstring NT,$(OS))) BUILD_TARGETS := build-dotnet6 build-dotnet-framework TEST_TARGETS := test-dotnet6 test-dotnet-framework TEST_TARGETS_AUTH_SERVICE := test-dotnet6-auth-service test-dotnet-framework-auth-service - TEST_TARGETS_CACHE_SERVICE := test-dotnet6-cache-service test-dotnet-framework-cache-service TEST_TARGETS_TOPICS_SERVICE := test-dotnet6-topics-service test-dotnet-framework-topics-service else BUILD_TARGETS := build-dotnet6 TEST_TARGETS := test-dotnet6 TEST_TARGETS_AUTH_SERVICE := test-dotnet6-auth-service - TEST_TARGETS_CACHE_SERVICE := test-dotnet6-cache-service TEST_TARGETS_TOPICS_SERVICE := test-dotnet6-topics-service endif @@ -98,6 +96,16 @@ restore: test: ${TEST_TARGETS} +## Run unit and integration tests with consistent reads (conditioned by OS) +prod-test: + @echo "running tests with consistent reads..." +ifeq (,$(findstring NT,$(OS))) + @CONSISTENT_READS=1 $(MAKE) ${TEST_TARGETS} +else + @set CONSISTENT_READS=1 && $(MAKE) ${TEST_TARGETS} +endif + + ## Run unit and integration tests on the .NET 6.0 runtime test-dotnet6: @echo "Running unit and integration tests on the .NET 6.0 runtime..." @@ -151,7 +159,12 @@ test-auth-service: ${TEST_TARGETS_AUTH_SERVICE} ## Run cache service tests -test-cache-service: ${TEST_TARGETS_CACHE_SERVICE} +test-cache-service: +ifeq (,$(findstring NT,$(OS))) + @CONSISTENT_READS=1 $(MAKE) test-dotnet6-cache-service +else + @set CONSISTENT_READS=1 && $(MAKE) test-dotnet6-cache-service test-dotnet-framework-cache-service +endif ## Run leaderboard service tests diff --git a/src/Momento.Sdk/Config/Configuration.cs b/src/Momento.Sdk/Config/Configuration.cs index 407e0a37..8f094495 100644 --- a/src/Momento.Sdk/Config/Configuration.cs +++ b/src/Momento.Sdk/Config/Configuration.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; using Momento.Sdk.Config.Middleware; using Momento.Sdk.Config.Retry; using Momento.Sdk.Config.Transport; @@ -14,18 +13,24 @@ public class Configuration : IConfiguration { /// public ILoggerFactory LoggerFactory { get; } + /// public IRetryStrategy RetryStrategy { get; } + /// public IList Middlewares { get; } + /// public ITransportStrategy TransportStrategy { get; } + /// + public ReadConcern ReadConcern { get; } + /// - public Configuration(ILoggerFactory loggerFactory, IRetryStrategy retryStrategy, ITransportStrategy transportStrategy) + public Configuration(ILoggerFactory loggerFactory, IRetryStrategy retryStrategy, + ITransportStrategy transportStrategy) : this(loggerFactory, retryStrategy, new List(), transportStrategy) { - } /// @@ -34,31 +39,34 @@ public Configuration(ILoggerFactory loggerFactory, IRetryStrategy retryStrategy, /// Defines a contract for how and when to retry a request /// The Middleware interface allows the Configuration to provide a higher-order function that wraps all requests. /// This is responsible for configuring network tunables. - /// This is responsible for configuraing logging. - public Configuration(ILoggerFactory loggerFactory, IRetryStrategy retryStrategy, IList middlewares, ITransportStrategy transportStrategy) + /// This is responsible for configuring logging. + /// The client-wide setting for read-after-write consistency. Defaults to Balanced. + public Configuration(ILoggerFactory loggerFactory, IRetryStrategy retryStrategy, IList middlewares, + ITransportStrategy transportStrategy, ReadConcern readConcern = ReadConcern.Balanced) { - this.LoggerFactory = loggerFactory; - this.RetryStrategy = retryStrategy; - this.Middlewares = middlewares; - this.TransportStrategy = transportStrategy; + LoggerFactory = loggerFactory; + RetryStrategy = retryStrategy; + Middlewares = middlewares; + TransportStrategy = transportStrategy; + ReadConcern = readConcern; } /// public IConfiguration WithRetryStrategy(IRetryStrategy retryStrategy) { - return new Configuration(LoggerFactory, retryStrategy, Middlewares, TransportStrategy); + return new Configuration(LoggerFactory, retryStrategy, Middlewares, TransportStrategy, ReadConcern); } /// public IConfiguration WithMiddlewares(IList middlewares) { - return new Configuration(LoggerFactory, RetryStrategy, middlewares, TransportStrategy); + return new Configuration(LoggerFactory, RetryStrategy, middlewares, TransportStrategy, ReadConcern); } /// public IConfiguration WithTransportStrategy(ITransportStrategy transportStrategy) { - return new Configuration(LoggerFactory, RetryStrategy, Middlewares, transportStrategy); + return new Configuration(LoggerFactory, RetryStrategy, Middlewares, transportStrategy, ReadConcern); } /// @@ -72,12 +80,13 @@ public Configuration WithAdditionalMiddlewares(IList additionalMidd retryStrategy: RetryStrategy, middlewares: Middlewares.Concat(additionalMiddlewares).ToList(), transportStrategy: TransportStrategy, - loggerFactory: LoggerFactory + loggerFactory: LoggerFactory, + readConcern: ReadConcern ); } /// - /// Add the specified client timeout to an existing instance of Configuration object as an addiion to the existing transport strategy. + /// Add the specified client timeout to an existing instance of Configuration object as an addition to the existing transport strategy. /// /// The amount of time to wait before cancelling the request. /// Configuration object with client timeout provided @@ -87,7 +96,20 @@ public Configuration WithClientTimeout(TimeSpan clientTimeout) retryStrategy: RetryStrategy, middlewares: Middlewares, transportStrategy: TransportStrategy.WithClientTimeout(clientTimeout), - loggerFactory: LoggerFactory + loggerFactory: LoggerFactory, + readConcern: ReadConcern + ); + } + + /// + public IConfiguration WithReadConcern(ReadConcern readConcern) + { + return new Configuration( + retryStrategy: RetryStrategy, + middlewares: Middlewares, + transportStrategy: TransportStrategy, + loggerFactory: LoggerFactory, + readConcern: readConcern ); } @@ -106,9 +128,10 @@ public override bool Equals(object obj) var other = (Configuration)obj; return RetryStrategy.Equals(other.RetryStrategy) && - Middlewares.SequenceEqual(other.Middlewares) && - TransportStrategy.Equals(other.TransportStrategy) && - LoggerFactory.Equals(other.LoggerFactory); + Middlewares.SequenceEqual(other.Middlewares) && + TransportStrategy.Equals(other.TransportStrategy) && + LoggerFactory.Equals(other.LoggerFactory) && + ReadConcern.Equals(other.ReadConcern); } /// @@ -116,4 +139,4 @@ public override int GetHashCode() { return base.GetHashCode(); } -} +} \ No newline at end of file diff --git a/src/Momento.Sdk/Config/IConfiguration.cs b/src/Momento.Sdk/Config/IConfiguration.cs index e1b7998e..2f8c07b0 100644 --- a/src/Momento.Sdk/Config/IConfiguration.cs +++ b/src/Momento.Sdk/Config/IConfiguration.cs @@ -21,6 +21,8 @@ public interface IConfiguration public IList Middlewares { get; } /// public ITransportStrategy TransportStrategy { get; } + /// + public ReadConcern ReadConcern { get; } /// /// Creates a new instance of the Configuration object, updated to use the specified retry strategy. @@ -49,4 +51,11 @@ public interface IConfiguration /// The amount of time to wait before cancelling the request. /// Configuration object with custom client timeout provided public IConfiguration WithClientTimeout(TimeSpan clientTimeout); + + /// + /// Creates a new instance of the Configuration object, updated to use the specified read concern. + /// + /// The read concern setting. + /// Configuration object with custom read concern provided + public IConfiguration WithReadConcern(ReadConcern readConcern); } diff --git a/src/Momento.Sdk/Config/ReadConcern.cs b/src/Momento.Sdk/Config/ReadConcern.cs new file mode 100644 index 00000000..5e9d2d0f --- /dev/null +++ b/src/Momento.Sdk/Config/ReadConcern.cs @@ -0,0 +1,41 @@ +using System; + +namespace Momento.Sdk.Config; + +/// +/// The read consistency setting for the cache client. Consistent guarantees read after write consistency, but applies a +/// 6x multiplier to your operation usage. +/// +public enum ReadConcern +{ + /// + /// Balanced is the default read concern for the cache client. + /// + Balanced, + /// + /// Consistent read concern guarantees read after write consistency. + /// + Consistent +} + +/// +/// Extension methods for the ReadConcern enum. +/// +public static class ReadConcernExtensions +{ + /// + /// Converts the read concern to a string value. + /// + /// to convert to a string + /// + /// if given an unknown read concern + public static string ToStringValue(this ReadConcern readConcern) + { + return readConcern switch + { + ReadConcern.Balanced => "balanced", + ReadConcern.Consistent => "consistent", + _ => throw new ArgumentOutOfRangeException() + }; + } +} \ No newline at end of file diff --git a/src/Momento.Sdk/Internal/DataGrpcManager.cs b/src/Momento.Sdk/Internal/DataGrpcManager.cs index eafc610a..1df390c0 100644 --- a/src/Momento.Sdk/Internal/DataGrpcManager.cs +++ b/src/Momento.Sdk/Internal/DataGrpcManager.cs @@ -4,11 +4,6 @@ using System.Linq; using System.Threading.Tasks; using Grpc.Core; -using Grpc.Net.Client; -using System.Net.Http; -#if USE_GRPC_WEB -using Grpc.Net.Client.Web; -#endif using Microsoft.Extensions.Logging; using Momento.Protos.CacheClient; using Momento.Protos.CachePing; @@ -17,7 +12,6 @@ using Momento.Sdk.Config.Retry; using Momento.Sdk.Exceptions; using Momento.Sdk.Internal.Middleware; -using static System.Reflection.Assembly; namespace Momento.Sdk.Internal; @@ -256,10 +250,17 @@ public class DataGrpcManager : GrpcManager internal DataGrpcManager(IConfiguration config, string authToken, string endpoint): base(config.TransportStrategy.GrpcConfig, config.LoggerFactory, authToken, endpoint, "DataGrpcManager") { + // Not sending a head concern header is treated the same as sending a balanced read concern header + if (config.ReadConcern != ReadConcern.Balanced) + { + var readConcernHeader = new Header(Header.ReadConcern, config.ReadConcern.ToStringValue()); + headers.Add(readConcernHeader); + } + var middlewares = config.Middlewares.Concat( new List { new RetryMiddleware(config.LoggerFactory, config.RetryStrategy), - new HeaderMiddleware(config.LoggerFactory, this.headers), + new HeaderMiddleware(config.LoggerFactory, headers), new MaxConcurrentRequestsMiddleware(config.LoggerFactory, config.TransportStrategy.MaxConcurrentRequests) } ).ToList(); @@ -280,7 +281,7 @@ await pingClient.PingAsync(new _PingRequest(), catch (RpcException ex) { MomentoErrorTransportDetails transportDetails = new MomentoErrorTransportDetails( - new MomentoGrpcErrorDetails(ex.StatusCode, ex.Message, null) + new MomentoGrpcErrorDetails(ex.StatusCode, ex.Message) ); throw new ConnectionException("Eager connection to server failed", transportDetails, ex); } diff --git a/src/Momento.Sdk/Internal/Middleware/HeaderMiddleware.cs b/src/Momento.Sdk/Internal/Middleware/HeaderMiddleware.cs index cb0aa8ee..03a074b0 100644 --- a/src/Momento.Sdk/Internal/Middleware/HeaderMiddleware.cs +++ b/src/Momento.Sdk/Internal/Middleware/HeaderMiddleware.cs @@ -5,6 +5,7 @@ using Grpc.Core; using Grpc.Core.Interceptors; using Microsoft.Extensions.Logging; +using Momento.Sdk.Config; using Momento.Sdk.Config.Middleware; namespace Momento.Sdk.Internal.Middleware @@ -14,6 +15,7 @@ internal class Header public const string AuthorizationKey = "authorization"; public const string AgentKey = "agent"; public const string RuntimeVersionKey = "runtime-version"; + public const string ReadConcern = "read-concern"; public readonly List onceOnlyHeaders = new List { Header.AgentKey, Header.RuntimeVersionKey }; public string Name; public string Value; diff --git a/tests/Integration/Momento.Sdk.Tests/Fixtures.cs b/tests/Integration/Momento.Sdk.Tests/Fixtures.cs index 4b93565e..beec2056 100644 --- a/tests/Integration/Momento.Sdk.Tests/Fixtures.cs +++ b/tests/Integration/Momento.Sdk.Tests/Fixtures.cs @@ -20,19 +20,29 @@ public class CacheClientFixture : IDisposable public CacheClientFixture() { AuthProvider = new EnvMomentoTokenProvider("MOMENTO_API_KEY"); + + // Enable consistent reads if the CONSISTENT_READS env var is set to anything + var consistentReads = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CONSISTENT_READS")); + + var config = Configurations.Laptop.Latest(LoggerFactory.Create(builder => + { + builder.AddSimpleConsole(options => + { + options.IncludeScopes = true; + options.SingleLine = true; + options.TimestampFormat = "hh:mm:ss "; + }); + builder.AddFilter("Grpc.Net.Client", LogLevel.Error); + builder.SetMinimumLevel(LogLevel.Information); + })); + + if (consistentReads) + { + config = config.WithReadConcern(ReadConcern.Consistent); + } + CacheName = $"dotnet-integration-{Utils.NewGuidString()}"; - Client = new TestCacheClient(Configurations.Laptop.Latest(LoggerFactory.Create(builder => - { - builder.AddSimpleConsole(options => - { - options.IncludeScopes = true; - options.SingleLine = true; - options.TimestampFormat = "hh:mm:ss "; - }); - builder.AddFilter("Grpc.Net.Client", LogLevel.Error); - builder.SetMinimumLevel(LogLevel.Information); - })), - AuthProvider, defaultTtl: DefaultTtl); + Client = new CacheClient(config, AuthProvider, defaultTtl: DefaultTtl); Utils.CreateCacheForTest(Client, CacheName); } diff --git a/tests/Integration/Momento.Sdk.Tests/TestCacheClient.cs b/tests/Integration/Momento.Sdk.Tests/TestCacheClient.cs deleted file mode 100644 index e1a38258..00000000 --- a/tests/Integration/Momento.Sdk.Tests/TestCacheClient.cs +++ /dev/null @@ -1,380 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Momento.Sdk.Auth; -using Momento.Sdk.Config; -using Momento.Sdk.Requests; - -namespace Momento.Sdk.Tests.Integration; - -/// -/// Client call wrapper to make test expectations easier to write. -/// -public class TestCacheClient : ICacheClient -{ - private readonly CacheClient client; - - public TestCacheClient(IConfiguration config, ICredentialProvider authProvider, TimeSpan defaultTtl) - { - client = new CacheClient(config, authProvider, defaultTtl); - } - - /// - /// Wait for the service to settle any slow replication or intermediate response cache. - /// Momento doesn't have strict read-after-write consistency guarantees, but a little - /// time should settle it in normal situations. This is mostly helpful when you're - /// running from a well-connected location, like inside of an AWS datacenter talking to - /// Momento in the same datacenter. From your laptop you'll pretty much always pay 10 - /// milliseconds to lightspeed tax, at least in 2023. - /// - private async Task Quiesce() - { - await Task.Delay(TimeSpan.FromMilliseconds(10)); - } - - public Task CreateCacheAsync(string cacheName) - { - return ((ICacheClient)client).CreateCacheAsync(cacheName); - } - - public Task DeleteAsync(string cacheName, byte[] key) - { - return ((ICacheClient)client).DeleteAsync(cacheName, key); - } - - public Task DeleteAsync(string cacheName, string key) - { - return ((ICacheClient)client).DeleteAsync(cacheName, key); - } - - public Task DeleteCacheAsync(string cacheName) - { - return ((ICacheClient)client).DeleteCacheAsync(cacheName); - } - - public Task DictionaryFetchAsync(string cacheName, string dictionaryName) - { - return ((ICacheClient)client).DictionaryFetchAsync(cacheName, dictionaryName); - } - - public Task DictionaryGetFieldAsync(string cacheName, string dictionaryName, byte[] field) - { - return ((ICacheClient)client).DictionaryGetFieldAsync(cacheName, dictionaryName, field); - } - - public Task DictionaryGetFieldAsync(string cacheName, string dictionaryName, string field) - { - return ((ICacheClient)client).DictionaryGetFieldAsync(cacheName, dictionaryName, field); - } - - public Task DictionaryGetFieldsAsync(string cacheName, string dictionaryName, IEnumerable fields) - { - return ((ICacheClient)client).DictionaryGetFieldsAsync(cacheName, dictionaryName, fields); - } - - public Task DictionaryGetFieldsAsync(string cacheName, string dictionaryName, IEnumerable fields) - { - return ((ICacheClient)client).DictionaryGetFieldsAsync(cacheName, dictionaryName, fields); - } - - public Task DictionaryIncrementAsync(string cacheName, string dictionaryName, string field, long amount = 1, CollectionTtl ttl = default) - { - return ((ICacheClient)client).DictionaryIncrementAsync(cacheName, dictionaryName, field, amount, ttl); - } - - public Task DictionaryLengthAsync(string cacheName, string dictionaryName) - { - return ((ICacheClient)client).DictionaryLengthAsync(cacheName, dictionaryName); - } - - public Task DictionaryRemoveFieldAsync(string cacheName, string dictionaryName, byte[] field) - { - return ((ICacheClient)client).DictionaryRemoveFieldAsync(cacheName, dictionaryName, field); - } - - public Task DictionaryRemoveFieldAsync(string cacheName, string dictionaryName, string field) - { - return ((ICacheClient)client).DictionaryRemoveFieldAsync(cacheName, dictionaryName, field); - } - - public Task DictionaryRemoveFieldsAsync(string cacheName, string dictionaryName, IEnumerable fields) - { - return ((ICacheClient)client).DictionaryRemoveFieldsAsync(cacheName, dictionaryName, fields); - } - - public Task DictionaryRemoveFieldsAsync(string cacheName, string dictionaryName, IEnumerable fields) - { - return ((ICacheClient)client).DictionaryRemoveFieldsAsync(cacheName, dictionaryName, fields); - } - - public Task DictionarySetFieldAsync(string cacheName, string dictionaryName, byte[] field, byte[] value, CollectionTtl ttl = default) - { - return ((ICacheClient)client).DictionarySetFieldAsync(cacheName, dictionaryName, field, value, ttl); - } - - public Task DictionarySetFieldAsync(string cacheName, string dictionaryName, string field, string value, CollectionTtl ttl = default) - { - return ((ICacheClient)client).DictionarySetFieldAsync(cacheName, dictionaryName, field, value, ttl); - } - - public Task DictionarySetFieldAsync(string cacheName, string dictionaryName, string field, byte[] value, CollectionTtl ttl = default) - { - return ((ICacheClient)client).DictionarySetFieldAsync(cacheName, dictionaryName, field, value, ttl); - } - - public Task DictionarySetFieldsAsync(string cacheName, string dictionaryName, IEnumerable> elements, CollectionTtl ttl = default) - { - return ((ICacheClient)client).DictionarySetFieldsAsync(cacheName, dictionaryName, elements, ttl); - } - - public Task DictionarySetFieldsAsync(string cacheName, string dictionaryName, IEnumerable> elements, CollectionTtl ttl = default) - { - return ((ICacheClient)client).DictionarySetFieldsAsync(cacheName, dictionaryName, elements, ttl); - } - - public Task DictionarySetFieldsAsync(string cacheName, string dictionaryName, IEnumerable> elements, CollectionTtl ttl = default) - { - return ((ICacheClient)client).DictionarySetFieldsAsync(cacheName, dictionaryName, elements, ttl); - } - - public void Dispose() - { - ((IDisposable)client).Dispose(); - } - - public Task FlushCacheAsync(string cacheName) - { - return ((ICacheClient)client).FlushCacheAsync(cacheName); - } - - public async Task GetAsync(string cacheName, byte[] key) - { - await Quiesce(); - return await ((ICacheClient)client).GetAsync(cacheName, key); - } - - public async Task GetAsync(string cacheName, string key) - { - await Quiesce(); - return await ((ICacheClient)client).GetAsync(cacheName, key); - } - - public Task IncrementAsync(string cacheName, string field, long amount = 1, TimeSpan? ttl = null) - { - return ((ICacheClient)client).IncrementAsync(cacheName, field, amount, ttl); - } - - public Task IncrementAsync(string cacheName, byte[] field, long amount = 1, TimeSpan? ttl = null) - { - return ((ICacheClient)client).IncrementAsync(cacheName, field, amount, ttl); - } - - public Task ItemGetTtlAsync(string cacheName, byte[] key) - { - return ((ICacheClient)client).ItemGetTtlAsync(cacheName, key); - } - - public Task ItemGetTtlAsync(string cacheName, string key) - { - return ((ICacheClient)client).ItemGetTtlAsync(cacheName, key); - } - - public Task KeyExistsAsync(string cacheName, byte[] key) - { - return ((ICacheClient)client).KeyExistsAsync(cacheName, key); - } - - public Task KeyExistsAsync(string cacheName, string key) - { - return ((ICacheClient)client).KeyExistsAsync(cacheName, key); - } - - public Task KeysExistAsync(string cacheName, IEnumerable keys) - { - return ((ICacheClient)client).KeysExistAsync(cacheName, keys); - } - - public Task KeysExistAsync(string cacheName, IEnumerable keys) - { - return ((ICacheClient)client).KeysExistAsync(cacheName, keys); - } - - public Task ListCachesAsync() - { - return ((ICacheClient)client).ListCachesAsync(); - } - - public Task ListConcatenateBackAsync(string cacheName, string listName, IEnumerable values, int? truncateFrontToSize = null, CollectionTtl ttl = default) - { - return ((ICacheClient)client).ListConcatenateBackAsync(cacheName, listName, values, truncateFrontToSize, ttl); - } - - public Task ListConcatenateBackAsync(string cacheName, string listName, IEnumerable values, int? truncateFrontToSize = null, CollectionTtl ttl = default) - { - return ((ICacheClient)client).ListConcatenateBackAsync(cacheName, listName, values, truncateFrontToSize, ttl); - } - - public Task ListConcatenateFrontAsync(string cacheName, string listName, IEnumerable values, int? truncateBackToSize = null, CollectionTtl ttl = default) - { - return ((ICacheClient)client).ListConcatenateFrontAsync(cacheName, listName, values, truncateBackToSize, ttl); - } - - public Task ListConcatenateFrontAsync(string cacheName, string listName, IEnumerable values, int? truncateBackToSize = null, CollectionTtl ttl = default) - { - return ((ICacheClient)client).ListConcatenateFrontAsync(cacheName, listName, values, truncateBackToSize, ttl); - } - - public Task ListFetchAsync(string cacheName, string listName, int? startIndex = null, int? endIndex = null) - { - return ((ICacheClient)client).ListFetchAsync(cacheName, listName, startIndex, endIndex); - } - - public Task ListLengthAsync(string cacheName, string listName) - { - return ((ICacheClient)client).ListLengthAsync(cacheName, listName); - } - - public Task ListPopBackAsync(string cacheName, string listName) - { - return ((ICacheClient)client).ListPopBackAsync(cacheName, listName); - } - - public Task ListPopFrontAsync(string cacheName, string listName) - { - return ((ICacheClient)client).ListPopFrontAsync(cacheName, listName); - } - - public Task ListPushBackAsync(string cacheName, string listName, byte[] value, int? truncateFrontToSize = null, CollectionTtl ttl = default) - { - return ((ICacheClient)client).ListPushBackAsync(cacheName, listName, value, truncateFrontToSize, ttl); - } - - public Task ListPushBackAsync(string cacheName, string listName, string value, int? truncateFrontToSize = null, CollectionTtl ttl = default) - { - return ((ICacheClient)client).ListPushBackAsync(cacheName, listName, value, truncateFrontToSize, ttl); - } - - public Task ListPushFrontAsync(string cacheName, string listName, byte[] value, int? truncateBackToSize = null, CollectionTtl ttl = default) - { - return ((ICacheClient)client).ListPushFrontAsync(cacheName, listName, value, truncateBackToSize, ttl); - } - - public Task ListPushFrontAsync(string cacheName, string listName, string value, int? truncateBackToSize = null, CollectionTtl ttl = default) - { - return ((ICacheClient)client).ListPushFrontAsync(cacheName, listName, value, truncateBackToSize, ttl); - } - - public Task ListRemoveValueAsync(string cacheName, string listName, byte[] value) - { - return ((ICacheClient)client).ListRemoveValueAsync(cacheName, listName, value); - } - - public Task ListRemoveValueAsync(string cacheName, string listName, string value) - { - return ((ICacheClient)client).ListRemoveValueAsync(cacheName, listName, value); - } - - public Task ListRetainAsync(string cacheName, string listName, int? startIndex = null, int? endIndex = null) - { - return ((ICacheClient)client).ListRetainAsync(cacheName, listName, startIndex, endIndex); - } - - public Task SetAddElementAsync(string cacheName, string setName, byte[] element, CollectionTtl ttl = default) - { - return ((ICacheClient)client).SetAddElementAsync(cacheName, setName, element, ttl); - } - - public Task SetAddElementAsync(string cacheName, string setName, string element, CollectionTtl ttl = default) - { - return ((ICacheClient)client).SetAddElementAsync(cacheName, setName, element, ttl); - } - - public Task SetAddElementsAsync(string cacheName, string setName, IEnumerable elements, CollectionTtl ttl = default) - { - return ((ICacheClient)client).SetAddElementsAsync(cacheName, setName, elements, ttl); - } - - public Task SetAddElementsAsync(string cacheName, string setName, IEnumerable elements, CollectionTtl ttl = default) - { - return ((ICacheClient)client).SetAddElementsAsync(cacheName, setName, elements, ttl); - } - - public Task SetAsync(string cacheName, byte[] key, byte[] value, TimeSpan? ttl = null) - { - return ((ICacheClient)client).SetAsync(cacheName, key, value, ttl); - } - - public Task SetAsync(string cacheName, string key, byte[] value, TimeSpan? ttl = null) - { - return ((ICacheClient)client).SetAsync(cacheName, key, value, ttl); - } - - public Task SetAsync(string cacheName, string key, string value, TimeSpan? ttl = null) - { - return ((ICacheClient)client).SetAsync(cacheName, key, value, ttl); - } - - public Task SetFetchAsync(string cacheName, string setName) - { - return ((ICacheClient)client).SetFetchAsync(cacheName, setName); - } - - public Task SetSampleAsync(string cacheName, string setName, int limit) - { - return ((ICacheClient)client).SetSampleAsync(cacheName, setName, limit); - } - - public Task SetIfNotExistsAsync(string cacheName, string key, string value, TimeSpan? ttl = null) - { - return ((ICacheClient)client).SetIfNotExistsAsync(cacheName, key, value, ttl); - } - - public Task SetIfNotExistsAsync(string cacheName, byte[] key, string value, TimeSpan? ttl = null) - { - return ((ICacheClient)client).SetIfNotExistsAsync(cacheName, key, value, ttl); - } - - public Task SetIfNotExistsAsync(string cacheName, string key, byte[] value, TimeSpan? ttl = null) - { - return ((ICacheClient)client).SetIfNotExistsAsync(cacheName, key, value, ttl); - } - - public Task SetIfNotExistsAsync(string cacheName, byte[] key, byte[] value, TimeSpan? ttl = null) - { - return ((ICacheClient)client).SetIfNotExistsAsync(cacheName, key, value, ttl); - } - - public Task SetLengthAsync(string cacheName, string setName) - { - return ((ICacheClient)client).SetLengthAsync(cacheName, setName); - } - - public Task SetRemoveElementAsync(string cacheName, string setName, byte[] element) - { - return ((ICacheClient)client).SetRemoveElementAsync(cacheName, setName, element); - } - - public Task SetRemoveElementAsync(string cacheName, string setName, string element) - { - return ((ICacheClient)client).SetRemoveElementAsync(cacheName, setName, element); - } - - public Task SetRemoveElementsAsync(string cacheName, string setName, IEnumerable elements) - { - return ((ICacheClient)client).SetRemoveElementsAsync(cacheName, setName, elements); - } - - public Task SetRemoveElementsAsync(string cacheName, string setName, IEnumerable elements) - { - return ((ICacheClient)client).SetRemoveElementsAsync(cacheName, setName, elements); - } - - public Task UpdateTtlAsync(string cacheName, byte[] key, TimeSpan ttl) - { - return ((ICacheClient)client).UpdateTtlAsync(cacheName, key, ttl); - } - - public Task UpdateTtlAsync(string cacheName, string key, TimeSpan ttl) - { - return ((ICacheClient)client).UpdateTtlAsync(cacheName, key, ttl); - } -}