{"id":46238308,"url":"https://github.com/aliexpressru/memcached","last_synced_at":"2026-03-03T19:34:02.088Z","repository":{"id":59393988,"uuid":"536052810","full_name":"aliexpressru/memcached","owner":"aliexpressru","description":"A high-performance client library for managing multiple instances of Memcached, optimized for scalability and reliability. This library is designed to provide seamless integration and efficient caching solutions for large-scale applications, ensuring low latency and high throughput. Developed and maintained by AliExpress CIS.","archived":false,"fork":false,"pushed_at":"2025-12-17T08:55:08.000Z","size":613,"stargazers_count":29,"open_issues_count":8,"forks_count":2,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-12-18T14:34:15.829Z","etag":null,"topics":["aliexpress","c-sharp","cache","distributed-cache","dotnet-core","memcached","performance","scalability"],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/Aerx.Memcached.Client","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aliexpressru.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-09-13T09:30:33.000Z","updated_at":"2025-12-16T09:34:36.000Z","dependencies_parsed_at":"2023-02-16T23:00:55.703Z","dependency_job_id":"2713edb3-2839-49df-9417-625430ca1078","html_url":"https://github.com/aliexpressru/memcached","commit_stats":null,"previous_names":[],"tags_count":100,"template":false,"template_full_name":null,"purl":"pkg:github/aliexpressru/memcached","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aliexpressru%2Fmemcached","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aliexpressru%2Fmemcached/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aliexpressru%2Fmemcached/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aliexpressru%2Fmemcached/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aliexpressru","download_url":"https://codeload.github.com/aliexpressru/memcached/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aliexpressru%2Fmemcached/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30056070,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-03T18:21:05.932Z","status":"ssl_error","status_checked_at":"2026-03-03T18:20:59.341Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aliexpress","c-sharp","cache","distributed-cache","dotnet-core","memcached","performance","scalability"],"created_at":"2026-03-03T19:34:01.369Z","updated_at":"2026-03-03T19:34:02.050Z","avatar_url":"https://github.com/aliexpressru.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Aer Memcached Client\n\n[![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Faliexpressru%2Fmemcached%2Fbadge%3Fref%3Dmain\u0026style=flat)](https://actions-badge.atrox.dev/aliexpressru/memcached/goto?ref=main)\n[![NuGet Release][package-image]][package-nuget-url]\n\n## Features\n- **Distributed Caching:** Easily scale your cache across multiple servers.\n- **High Performance:** Efficiently handles large amounts of data with low latency.\n- **Scalability:** Seamlessly increase cache capacity as your application grows.\n- **Ease of Use:** Simple setup and configuration.\n- **Multi-Target Support:** Supports both .NET 8.0 and .NET 10.0.\n\n---\n\n## Configuration\n\nTo use the library add `MemcachedConfiguration` section to appsettings.json as follows:\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"HeadlessServiceAddress\": \"my-memchached-service-headless.namespace.svc.cluster.local\"\n  }\n}\n```\n\n`HeadlessServiceAddress` groups all memcached pods. Using dns lookup all the ip addresses of the pods can be obtained.\n\nDefault Memcached port is `11211`, but you can also specify it in config.\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"MemcachedPort\": 12345\n  }\n}\n```\n\n`MemcachedPort` is an optional setting that specifies which port should each memcached node use. If not set the default value of `11211` used.\n\nFor local run or if you have a static amount and setup of pods you can specify `Servers` manually instead of seting the `HeadlessServiceAddress`:\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"Servers\": [\n      {\n        \"IpAddress\": \"1.1.1.1\"\n      },\n      {\n        \"IpAddress\": \"2.2.2.2\"\n      },\n      {\n        \"IpAddress\": \"3.3.3.3\"\n      }\n    ]\n  }\n}\n```\n\nDefault Memcached port is `11211`, but you can also specify it in config\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"Servers\": [\n      {\n        \"IpAddress\": \"1.1.1.1\",\n        \"Port\": 12345\n      }\n    ]\n  }\n}\n```\n\nIn case you have only one instance deployed in k8s specify consistent dns name of a pod.\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"Servers\": [\n      {\n        \"IpAddress\": \"my-memchached.namespace.svc.cluster.local\"\n      }\n    ]\n  }\n}\n```\n\nYpu can have both `HeadlessServiceAddress` and `Servers` nodes in configuration. In this case the resulting nodes will be collected from both headless service and static configuration.\n\n## Usage\n\n```c#\npublic void ConfigureServices(IServiceCollection services)\n{\n    ...\n    \n    services.AddMemcached(Configuration);\n}\n```\n\nThen inject `IMemcachedClient` whenever you need it.\n\nThis client supports single-key `get`, `store`, `delete`, `inc`, `decr`, `flush` operaions. Multiple keys counterparts are available for `get`, `store` and `delete` operations.\n\n### Store\n\nStores specified key-value of multiple key-value pairs to the cache.\n\n```c#\nTask\u003cMemcachedClientResult\u003e StoreAsync\u003cT\u003e(\n   string key,\n   T value,\n   TimeSpan? expirationTime,\n   CancellationToken token,\n   StoreMode storeMode = StoreMode.Set);\n```\n\n- `key` : the entry key to store value for\n- `value` : the value to store under specified key\n- `expirationTime` : the absolute expiration time for the key-value entry. If not set or set to `TimeSpan.Zero` or `TimeSpan.MaxValue`, cached value never expires\n- `token` : the cancellation token\n- `storeMode` : the mode under which the store operation is performed\n\n```c#\nTask MultiStoreAsync\u003cT\u003e(\n   Dictionary\u003cstring, T\u003e keyValues,\n   TimeSpan? expirationTime,\n   CancellationToken token,\n   StoreMode storeMode = StoreMode.Set,\n   BatchingOptions batchingOptions = null,\n   IDictionary\u003cstring, TimeSpan?\u003e expirationMap = null);\n```\n\n- `keyValues` : the entry key-value pairs to store\n- `expirationTime` : the absolute expiration time for the key-value entry. If not set or set to `TimeSpan.Zero` or `TimeSpan.MaxValue`, cached value never expires\n- `token` : the cancellation token\n- `storeMode` : the mode under which the store operation is performed\n- `batchingOptions` : optional batching options. The batching will be covered in the later part of this documentation\n- `expirationMap` : individual key expirations that will be used instead expirationTime if provided\n\n### Get\n\nGets the value for the specified key or set of keys from cache. If value is not found by the key - `IsEmptyResult` is `true`\nIn MultiGet scenarion the key in dictionary is absent.\n\n```c#\nTask\u003cMemcachedClientGetResult\u003cT\u003e\u003e GetAsync\u003cT\u003e(\n    string key, \n    CancellationToken token);\n```\n\n- `key` : the key to get the value for\n- `token` : the cancellation token\n\n```c#\nTask\u003cIDictionary\u003cstring, T\u003e\u003e MultiGetAsync\u003cT\u003e(\n    IEnumerable\u003cstring\u003e keys, \n    CancellationToken token,\n    BatchingOptions batchingOptions = null);\n```\n\n- `keys` : the keys to get values for\n- `token` : the cancellation token\n- `batchingOptions` : optional batching options. The batching will be covered in the later part of this documentation\n\n### GetAndTouch\n\nGets the value for the specified key and updates the expiration time of the entry stored in the cache. If value is not found by the key - `IsEmptyResult` is `true`\n\n```c#\nTask\u003cMemcachedClientGetResult\u003cT\u003e\u003e GetAndTouchAsync\u003cT\u003e(\n    string key,\n    TimeSpan? expirationTime,\n    CancellationToken token);\n```\n\n- `key` : the key to get the value for\n- `expirationTime` : the absolute expiration time for the key-value entry. If not set or set to `TimeSpan.Zero` or `TimeSpan.MaxValue`, cached value never expires\n- `token` : the cancellation token\n\n### Delete\n\nDeletes the value for the specified key or set of keys from cache.\n\n```c#\nTask\u003cMemcachedClientResult\u003e DeleteAsync(string key, CancellationToken token);\n```\n\n- `key` : the key to delete the value for\n- `token` : the cancellation token\n\n```c#\nTask MultiDeleteAsync(\n    IEnumerable\u003cstring\u003e keys,\n    CancellationToken token,\n    BatchingOptions batchingOptions = null);\n```\n\n- `keys` : the keys to delete values for\n- `token` : the cancellation token\n- `batchingOptions` : optional batching options. The batching will be covered in the later part of this documentation\n\n### Incr/Decr\n\n```c#\nTask\u003cMemcachedClientValueResult\u003culong\u003e\u003e IncrAsync(\n    string key,\n    ulong amountToAdd,\n    ulong initialValue,\n    TimeSpan? expirationTime,\n    CancellationToken token);\n```\n\n- `key` : the key to increment value for\n- `amountToAdd` : amount to add to stored value\n- `initialValue` : initial value if value does not exist\n- `expirationTime` : the absolute expiration time for the key-value entry\n- `token` : the cancellation token\n\n```c#\nTask\u003cMemcachedClientValueResult\u003culong\u003e\u003e DecrAsync(\n    string key,\n    ulong amountToSubtract,\n    ulong initialValue,\n    TimeSpan? expirationTime,\n    CancellationToken token);\n```\n\n- `key` : the key to increment value for\n- `amountToSubtract` : amount to subtract from stored value\n- `initialValue` : initial value if value does not exist\n- `expirationTime` : the absolute expiration time for the key-value entry\n- `token` : the cancellation token\n\n### Flush\n\nClears the cache on all the memcached cluster nodes.\n\n```c#\nTask FlushAsync(CancellationToken token);\n```\n\n### Error logging\n\nThis library internally logs all exceptions that occur during memcached operations. Also both `MemcachedClientResult` and `MemcachedClientValueResult\u003cT\u003e` have a public property `Success` which is set to `false` if any error occured during command execution. You can inspect the `ErrorMessage` property to get error message.\nTo simplify the error logging, an extension method exist for each of the aforementioned return types.\n\n```csharp\npublic static void LogErrorIfAny(\n        this MemcachedClientResult target,\n        ILogger logger,\n        [CallerMemberName] string operationName = null)\n\npublic static void LogErrorIfAny\u003cT\u003e(\n    this MemcachedClientValueResult\u003cT\u003e target,\n    ILogger logger,\n    int? cacheKeysCount,\n    [CallerMemberName] string operationName = null)\n```\n\n#### Cancellation logging\n\nBy default cancellations and therefore `OperationCancelledException` occurrences are treated as any other exceptions and logged accordingly. But there are times when one just need to know that cancellation heppened and doesn't need a full stacktrace for it.\n\nIt's possible to switch on the _terse cancellaiton logging_ by setting `MemcachedConfiguration.IsTerseCancellationLogging` config key to `true`.\n\nIn that case only the operation name and a short message will be logged upon cancellation.\n\n### Batching\n\nFor `MultiStoreAsync` and `MultiGetAsync` methods there is an optional argument `batchingOptions`. If this argument is specified the store and get operations split input key or key-value collection into batches an processe every batch on every memcached node in parallel with specified maximum degree of parallelism (`Environment.ProcessorCount` by default).\nBatching is requered to lower the load on memcached nodes in high-load scenarios when keys are long and values are even longer than keys. There is an empyric default value of batch size : `20` which is optimal for the most cases.\nBy default batching is off for all the operations.\n\nTo use the dafault values simply pass the new instance :\n\n```c#\nawait _client.MultiStoreAsync(\n    keyValues,\n    TimeSpan.FromSeconds(10),\n    CancellationToken.None,\n    batchingOptions: new BatchingOptions());\n```\n\nTo configure the batch size for one physical memcached operation or change the maximum degree of parallelism - change the batchingOptions properties accordingly.\n\n```c#\nnew BatchingOptions(){\n    BatchSize = 100,\n    MaxDegreeOfParallelism = 4\n}\n```\n\nNote that the batch size lower than zero is invalid and `MaxDegreeOfParallelism` lower then zero is equivalent to `Environment.ProcessorCount`.\n\n### Replication\n\nFor `MultiStoreAsync`, `MultiGetAsync` and `MultiDeleteAsync` methods there is an optional argument `replicationFactor`. If `replicationFactor \u003e 0` a key will be stored on `replicationFactor` additional physical nodes clockwise from initial one chosen by hash. In case of `MultiGetAsync` if you need replica fallback you can specify `replicationFactor = 1` even if `replicationFactor` for `MultiStoreAsync` is more than 1. While physical node does not respond but still is on HashRing `MultiGetAsync` command will try to fetch data from both initial node and it's replica. When request to initial node is cancelled you still have data from it's replica. In case the broken node is already removed from HashRing you will have some probability to hit it's replica and with higher `replicationFactor` probability is higher as well.\n\nBe careful when using this parameter as it increases workload by `x replicationFactor`. You also should consider some tunings for memcached - see [Memcached tuning](#memcached-tuning) part of the README.\n\n**Please note that `replicationFactor` parameter only present on multi-key versions of store, get and delte operations.**\n\n### Serialization\n\nSince Memcached requires data to be binary serialized before storing it, we utilize various binary serializers to perform the serialization.\n\nWe use hand-written plain binary serializer to serialize primitive types, but with complex user-defined types the matters are a bit more complex.\n\nCurrently there is no universal binary serializer that can handle all the possible type heirarchies while not requiring annotating stored types with some kind of attributes (contractless).\n\nSome examples.\n\nThe BSON serializer is contractless but can't handle `DateTimeOffset` values or dictionaries with non-primitive keys without writnig custom converters.\n\nThe MessagePack serizlizer can be contractless, can handle all kinds of types but can't handle reference loops (when object has a property that is either a direct or an indirect reference to the object itself).\n\nProtobuf serializer can handle reference loops, all types of objects but can't be contractless and creating contracts at run time using reflection is slow.\n\nSo we've decided to give the end user the ability to choose the serializer and if she does not like neither of the provided options - add a custom one.\n\nThe type of the serializer is configured as follows.\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"BinarySerializerType\" : \"Bson\"\n  }\n}\n```\n\nThe `ObjectBinarySerializerType` can have the following values.\n\n- `Bson` - The default binary object serializer, it was used since v1 and is pretty good default, unless you need to store dictionaries with non-primitive keys.\n- `MessagePack` - Fast, can handle any type, but can't handle reference loops.\n- `Custom` - this option indicates that the serializer will be provided by the end user.\n\nIf the `Custom` serializer type is selected then the library will search the DI container for a type that implements `IObjectBinarySerializer` and use it as a serializer implementation.\n\nHere is the example of custom serializer that uses MessagePack as an underlying serializer.\n\n```csharp\ninternal class MyObjectBinarySerializer : IObjectBinarySerializer\n{\n    private int _serializationCount;\n    private int _deserializationCount;\n\n    public int SerializationsCount =\u003e _serializationCount;\n    public int DeserializationsCount =\u003e _deserializationCount;\n\n    public byte[] Serialize\u003cT\u003e(T value)\n    {\n        var data = MessagePackSerializer.Typeless.Serialize(value);\n\n        Interlocked.Increment(ref _serializationCount);\n\n        return data;\n    }\n\n    public T Deserialize\u003cT\u003e(byte[] serializedObject)\n    {\n        var deserializedObject = (T) MessagePackSerializer.Typeless.Deserialize(serializedObject);\n\n        Interlocked.Increment(ref _deserializationCount);\n\n        return deserializedObject;\n    }\n}\n```\n\nWe register this serializer in DI as singleton.\n\n```csharp\nsc.AddSingleton\u003cIObjectBinarySerializer, MyObjectBinarySerializer\u003e();\n```\n\nAnd set the `BinarySerializerType` to `Custom`.\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"BinarySerializerType\" : \"Custom\"\n  }\n}\n````\n\n#### Binary serializer change\n\nWhen changing binary serializer type, previously serialized values might fail to deserialize. There is a transition period solution : delete undeserializable keys from memcached to refresh data. This option is not enabled by default to not conseal errorneous deployment consequnces.\n\nTo enable this option set the following configuration key.\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"IsDeleteMemcachedKeyOnDeserializationFail\" : true\n  }\n}\n```\n\nDo not forget to set this option back to `false` or to delte it after the transition period is over.\n\n## Restrictions \u0026\u0026 Limitations\n\nKey must be less than 250 characters and value must be less than the value `-I` configured during memcached instance start.\n\nThe key length restriction can be lifted, see [Long keys support](#long-keys-support) section for the details.\n\nIf the value is greater than the configured `-I` value the memcached client won't throw an exception - it simply won't store a key without any notification.\nTherefore, when trying to read such key - nothing will be returned.\n\nAlso, please note that this library utilizes a memcached item `flags` to store item type information to simplify and make deserialization faster.\nThis implies that one is not advised to use this library to read the data from memcached that you didn't put there using this library - it might be deserialized incorrectly or not deserialized at all.\n\n## Additional configuration\n\n### SASL\n\nTo use SASL specify `MemcachedAuth` section in config:\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"HeadlessServiceAddress\": \"my-memchached-service-headless.namespace.svc.cluster.local\"\n  },\n  \"MemcachedAuth\": {\n      \"Username\": \"mmchdadmin\",\n      \"Password\": \"pass\"\n  }\n}\n```\n\n### Metrics \u0026\u0026 Disagnostics\n\nThis library utilizes diagnostic source to write out metrics and diagnostic data. But the diagnostcs data output is switched off by default.\n\nTo enable metrics and diagnostics use tho following DI extension method in `Startup` class in `Configure` method.\n\n```c#\n    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)\n    {\n        // some previous calls here\n        app.EnableMemcachedDiagnostics(configuration);\n    }\n\n```\n\n#### Metrics\n\n`MemcachedClient` writes RT and RPS metrics to either Prometheus or OpenTelemetry diagnostics. To disable metrics specify:\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"HeadlessServiceAddress\": \"my-memchached-service-headless.namespace.svc.cluster.local\",\n    \"Diagnostics\": {\n      \"DisableDiagnostics\": true\n    }\n  }\n}\n```\n\nYou can select which metrics provider will be used by setting `MetricsProviderName` to either `Prometheus` or `OpenTelemetry` as follows:\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"HeadlessServiceAddress\": \"my-memchached-service-headless.namespace.svc.cluster.local\",\n    \"Diagnostics\":{    \n      \"MetricsProviderName\" : \"Prometheus\"\n    }\n  }\n}\n```\n\n**`MetricsProviderName` values**\n\n- `Prometheus`: use Prometheus metrics\n- `OpenTelemetry`: use OpenTelemetry metrics\n\nThis library exposes the following memcached metrics\n\n- `memcached_command_duration_seconds` - memcached command duration in seconds per command\n- `memcached_socket_pool_used_sockets` - number of used socket pool sockets per endpoint\n- `memcached_commands_total` - total executed memcached commands number\n- `memcached_socket_pool_exhausted_state` - socket pool exhaustion state per endpoint (1 = exhausted, absent = ok). **Note:** This metric is only available for OpenTelemetry. When a socket pool reaches its maximum capacity and cannot serve new requests, this metric is set to 1. Once the pool recovers and can serve requests again, the metric is removed.\n- `memcached_socket_unread_data_detected_total` - counter of unread data detections on sockets per endpoint. Indicates sockets left in invalid state (e.g., after timeout or error). Available for both OpenTelemetry and Prometheus.\n\n#### Distributed Tracing\n\nOpenTelemetry distributed tracing provides visibility into memcached operations. Tracing is disabled by default.\n\n**Enable tracing in `appsettings.json`:**\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"Diagnostics\": {\n      \"EnableTracing\": true\n    }\n  }\n}\n```\n\n**Configure OpenTelemetry exporter:**\n\n```csharp\nbuilder.Services.AddMemcached(builder.Configuration);\n\nbuilder.Services.AddOpenTelemetry()\n    .ConfigureResource(resource =\u003e resource.AddService(\"your-service-name\"))\n    .WithTracing(tracing =\u003e\n    {\n        tracing\n            .AddAspNetCoreInstrumentation()\n            .AddOtlpExporter(options =\u003e\n            {\n                options.Endpoint = new Uri(\"http://localhost:4317\");\n            });\n    });\n```\n\n**Features:**\n- Follows OpenTelemetry semantic conventions for database operations\n- Traces all memcached commands with operation name, server address, and replica information\n- Works with Jaeger, Zipkin, Azure Application Insights, AWS X-Ray, and other OTLP-compatible backends\n\n#### TracingOptions - Per-Operation Tracing Control\n\nAll memcached operations support optional `TracingOptions` parameter for granular tracing control.\n\n**Example:**\n\n```csharp\n// Disable tracing for fire-and-forget operation\n_ = Task.Run(async () =\u003e \n{\n    await _memcachedClient.MultiStoreAsync(\n        keyValues: data,\n        expirationTime: TimeSpan.FromMinutes(30),\n        token: CancellationToken.None,\n        tracingOptions: TracingOptions.Disabled\n    );\n});\n```\n\n**Options:**\n- `TracingOptions.Enabled` - Force enable tracing\n- `TracingOptions.Disabled` - Force disable tracing\n- `null` (default) - Use global `EnableTracing` setting\n\n**Note:** Global `EnableTracing = false` disables tracing regardless of `TracingOptions`.\n\n#### Disagnostic information\n\n`MemcachedClient` writes memcached nodes rebuild process state to diagnostics. This state includes the nodes that are currently in use and socket pools statistics. To disable this data logging specify:\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"HeadlessServiceAddress\": \"my-memchached-service-headless.namespace.svc.cluster.local\",\n    \"Diagnostics\": {\n      \"DisableRebuildNodesStateLogging\": true\n    }\n  }\n}\n```\n\n`MemcachedClient` writes socket pool diagnostic data. This data includes events for the socket being created and destroyed. By default this data is written out as `LogLevel.Information` log events. To disable this diagnostics or configure event level use the following settings.\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"HeadlessServiceAddress\": \"my-memchached-service-headless.namespace.svc.cluster.local\",\n    \"Diagnostics\": {\n      \"DisableSocketPoolDiagnosticsLogging\": true, // this disables socket pool diagnostics entirely\n      \"SocketPoolDiagnosticsLoggingEventLevel\" : \"Debug\"\n    }\n  }\n}\n```\n\n### Socket Pooling\n\nSocket pool has a default configuration.\nIf you need to tune it, add the following sections in config:\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"SocketPool\": {\n      \"ConnectionTimeout\": \"00:00:01\",\n      \"ReceiveTimeout\": \"00:00:01\",\n      \"SocketPoolingTimeout\": \"00:00:00.150\",\n      \"MaxPoolSize\": 100\n    }\n  }\n}\n```\n\n**`SocketPool` settings**\n\n- `ConnectionTimeout`: Amount of time after which the connection attempt will fail\n- `ReceiveTimeout`: Amount of time after which receiving data from the socket will fail\n- `SocketPoolingTimeout`: Amount of time to acquire socket from pool\n- `MaxPoolSize`: Maximum amount of sockets per memcached instance in the socket pool\n- `MaximumSocketCreationAttempts`: Maximum number of attempts to create a socket before the associated SocketPool is considered poisoned and its underlying endpoint broken\n\n### Maintenance background service\n\nMaintainer service has a default configuration.\nIf you need to tune it, add the following sections in config:\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"MemcachedMaintainer\": {\n      \"NodesRebuildingPeriod\": \"00:00:15\",\n      \"NodesHealthCheckPeriod\": \"00:01:00\",\n      \"NodeHealthCheckEnabled\": true,\n      \"MaxDegreeOfParallelism\": -1,\n      \"UseSocketPoolForNodeHealthChecks\" : true,\n      \"MaintainerCyclesToCloseSocketAfter\" : 2,\n      \"NumberOfSocketsToClosePerPool\" : 2\n    }\n  }\n}\n```\n\n**`MemcachedMaintainer` settings**\n\n- `NodesRebuildingPeriod`: Period to rebuild nodes using dns lookup by Headless Service\n- `NodesHealthCheckPeriod`: Period to check if nodes are responsive. If node is not responded during `SocketPool.ConnectionTimeout` it is marked as dead and will be deleted from memcached nodes until it is responsive again. Default value is `00:01:00` (1 minute)\n- `NodeHealthCheckEnabled`: Enables health check of nodes to remove dead nodes. Default value is `true`\n- `MaxDegreeOfParallelism`: Maximum degree of parallelism for node health checks. Set to `-1` to use `Environment.ProcessorCount`, or specify a positive number to set a fixed thread count. Default value is `-1`\n- `UseSocketPoolForNodeHealthChecks`: If set to `true` node health checker mechanism should use socket pool to obtain sockets for nodes health checks. If set to `false`, new non-pooled socket will be created for each node health check\n- `MaintainerCyclesToCloseSocketAfter`: Number of memcached maintainer cycles to close `NumberOfSocketsToClosePerPool` (see later) socket connections after. The sockets are going to be destroyed on the next maintainer cycle after the specified number\n- `NumberOfSocketsToClosePerPool`: Number of sockets to close per pool on each `MaintainerCyclesToCloseSocketAfter`\n\n### Jitter\n\nInitial number of seconds is got by last digits of calculated hash. Number of digits depends on `SpreadFactor`, be default it is the remainder of the division by `SpreadFactor` = 2 digits. Then it is multiplied by this factor to get final expiration time. `MultiplicationFactor` = 1 makes jitter in a range of 0 to 99 seconds. `MultiplicationFactor` = 10 makes jitter in a range of 0 to 990 seconds (0, 10, 20, ..., 990) etc.\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"HeadlessServiceAddress\": \"my-memchached-service-headless.namespace.svc.cluster.local\",\n    \"ExpirationJitter\": {\n      \"MultiplicationFactor\": 1,\n      \"SpreadFactor\": 100\n    }\n  }\n}\n```\n\n### Cache sync\n\nIn case you need consistent cache across clusters or data centers you can use cache synchronization feature of this library.\n\n```json\n{\n    \"MemcachedConfiguration\": {\n        \"HeadlessServiceAddress\": \"my-memchached-service-headless.namespace.svc.cluster.local\",\n        \"SyncSettings\": {\n            \"SyncServers\": [\n                {\n                    \"Address\": \"http://my-service.cluster1.k8s.net\",\n                    \"ClusterName\": \"cluster1\"\n                },\n                {\n                    \"Address\": \"http://my-service.cluster2.k8s.net\",\n                    \"ClusterName\": \"cluster2\"\n                }\n            ],\n            \"ClusterNameEnvVariable\": \"MY_ENV_VAR_FOR_CLUSTER_NAME\",\n            \"RetryCount\": 3,\n            \"TimeToSync\": \"00:00:01\",\n            \"CacheSyncCircuitBreaker\": {\n                \"Interval\": \"00:01:00\",\n                \"MaxErrors\": 50,\n                \"SwitchOffTime\": \"00:02:00\"\n            },\n            \"SyncEndpointsAuthAllowAnonymous\": false\n        }\n    }\n}\n\n```\n\n`DefaultSyncServersProvider` is used as default and can be replaced with your own implementation.\nBy default sync servers are obtained from `SyncServers` array and filtered by name of a cluster that is specified in `ClusterNameEnvVariable` to prevent a service instnace in one cluster from syncing cache to itself.\n\n- `RetryCount` - number of retries to sync data to servers. Default value is `3`.\n- `TimeToSync` - time before sync attempt is cancelled. Default value is `00:00:01`.\n\n`CacheSyncCircuitBreaker` allows to switch off synchronization if there are too many errors\n\n- `Interval` time interval to count errors\n- `MaxErrors` maximum number of errors in `Interval` by instance\n- `SwitchOffTime` time of synchronization switch off\n\nAlso you must add memcached endpoints in `Configure` method of your `Startup` class.\n\n```c#\napp.UseEndpoints(endpoints =\u003e\n        {\n            endpoints.AddMemcachedEndpoints(this.Configuration);\n            endpoints.MapControllers();\n        });\n```\n\n`Configuration` argument here is a property on a `Startup` instance\n\nIf you have implicit authorization configured for your service you can allow anonymous access to sync endpoints by setting `MemcachedConfiguration.SyncSettings.SyncEndpointsAuthAllowAnonymous` to `true`.\n\nWhen using cache synchronization feature, the `MemcachedClientResult.SyncSuccess` property can be inspected to determine whether the sync operation succeeded. When cache synchronization is not used this property is set to `false`.\n\nTo check whether the cache synchronization is configured and enabled call the `IMemcachedClient.IsCacheSyncEnabled` method.\n\n#### BatchingOptions support in cache sync\n\nCache synchronization now supports `BatchingOptions` for `MultiStoreAsync` operations. When `BatchingOptions` is specified, it will be passed to the remote cluster where the actual batching will occur during the `MultiStoreAsync` execution on the target cluster.\n\nThis is particularly useful when syncing large numbers of key-value pairs across clusters, as it allows the target cluster to process the data in optimized batches.\n\n```c#\nawait _client.MultiStoreAsync(\n    keyValues,\n    TimeSpan.FromSeconds(10),\n    CancellationToken.None,\n    batchingOptions: new BatchingOptions\n    {\n        BatchSize = 100,\n        MaxDegreeOfParallelism = 4\n    });\n```\n\nThe `BatchingOptions` are automatically included in the `CacheSyncModel` and will be used by the receiving cluster for efficient batch processing.\n\n### Long keys support\n\nMemcached has a by-design restriction on the key length - 250 bytes (or in most cases 250 characters).\n\nThis library has a mechanism to circumvent this restriction. If the following option is set\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"IsAllowLongKeys\" : true\n  }\n}\n```\n\nthen on each operation the key byte size is calculated and, if it exceeds the 250-byte limit, the key is hashed using `xxHash128` algorithm to a fixed length string.\n\nPlease note that all hashing algorithms, icluding `xxHash128`, has a potential to get a collision - same hash for two different inputs. Enabling aforementioned option might lead to unforeseen consequences, if the long to short keys ratio is considerable. Therefore it is recommended to enable `IsAllowLongKeys` option only if the long keys count is low.\n\nNonetheless, it worth noting that every case for this option should be considered and tested individually.\n\n### Run time configuration\n\nYou can configure at run time which operations should be processed; others will be ignored. In such cases, the result models `MemcachedClientResult` and `MemcachedClientValueResult\u003cT\u003e` will have both the `Success` and `OperationIgnored` properties set to `true`.\n\n```json\n{\n  \"MemcachedConfiguration\": {\n    \"RuntimeConfig\": {\n      \"EnabledOperations\": \"All\"\n    }\n  }\n}\n```\n\nThe `EnabledOperations` can have the following values.\n\n- `All` - The default flag, it means that all operations will be processed.\n- `None` - No operations will be processed.\n- `Get` - Combination of `GetAsync`, `GetAndTouchAsync`, `MultiGetAsync` and `MultiGetSafeAsync` operations. \n- `Store` - Combination of `StoreAsync`, `MultiStoreAsync` and `MultiStoreSynchronizeDataAsync` operations.\n- `Delete` - Combination of `DeleteAsync` and `MultiDeleteAsync` operations.\n- `Counter` - Combination of `IncrAsync` and `DecrAsync` operations.\n- `FlushAsync` - For `FlushAsync` operation.\n\nOr any combination of available operations.\n\n## Monitoring\n\nOther than logs check Prometheus \\ OpenTelemetry metrics.\n\nTo check if there are any unsuccesful commands: problems with connection, pool is run out of sockets, etc. :\n`sum(rate(memcached_commands_total{app_kubernetes_io_instance=\"$instance\",kube_cluster=~\"$cluster\",is_successful=\"0\"}[1m])) by (command_name)`\n\nRPS:\n`sum(irate(memcached_commands_total{app_kubernetes_io_instance=\"$instance\",kube_cluster=~\"$cluster\"}[$__interval])) by (command_name)`\n\nRT:\n`histogram_quantile(0.9, sum by (le, command_name) (rate(memcached_command_duration_seconds_bucket{app_kubernetes_io_instance=~\"$instance\",kube_cluster=~\"$cluster\"}[$__interval])))`\n\n## Docker compose\n\n```yaml\nversion: \"3\"\n\nservices:\n  memcached:\n    image: memcached\n    restart: always\n    container_name: memcached\n    command: [\"-m\", \"128\"]  \n    ports:  \n      - 11211:11211\n```\n\n## Memcached tuning\n\nConsider setting memory limit to have all your data in cache and avoid unneccessary evictions.\nAlso make sure that number of connections is enough, it will spike in moment of redeployment.\n\n```plaintext\n# MaxMemoryLimit, this should be less than the resources.limits.memory, or memcached will crash. Default is 64MB\n- -m 8192 \n# Specify the maximum number of simultaneous connections to the memcached service. The default is 1024. \n- -c 20000\n```\n\nIn multi key client methods of the library there is a default `BatchSize = 15`, if you want to change it consider tuning the following parameter:\n\n```plaintext\n- -R 40\nThe command-line parameter -R is in charge of the maximum number of requests per \nnetwork IO event (default value is 20). The application should adopt its batch \nsize according to this parameter. Please note that the requests limit does not \naffect multi-key reads, or the number of keys per get request.\n```\n\nEach multi key client method requires addition noop operation so you need to set `-R` parameter as `BatchSize + 1`.\n\nOtherwise you can encounter the limit:\n\n```plaintext\nSTAT conn_yields 126672162\nNumber of times any connection yielded to another due to hitting the -R limit\n```\n\n[package-nuget-url]:https://www.nuget.org/packages/Aerx.Memcached.Client/\n[package-image]:\nhttps://img.shields.io/nuget/v/Aerx.Memcached.Client.svg\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faliexpressru%2Fmemcached","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faliexpressru%2Fmemcached","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faliexpressru%2Fmemcached/lists"}