{"id":13414863,"url":"https://github.com/TurnerSoftware/CacheTower","last_synced_at":"2025-03-14T22:32:23.563Z","repository":{"id":37802583,"uuid":"201466305","full_name":"TurnerSoftware/CacheTower","owner":"TurnerSoftware","description":"An efficient multi-layered caching system for .NET","archived":false,"fork":false,"pushed_at":"2024-09-16T17:33:38.000Z","size":11253,"stargazers_count":611,"open_issues_count":35,"forks_count":36,"subscribers_count":16,"default_branch":"main","last_synced_at":"2024-09-16T20:19:33.337Z","etag":null,"topics":["cache","cachemanager","caching","caching-library","distributed","dotnet","json","memory-cache","protobuf","redis","redis-caching"],"latest_commit_sha":null,"homepage":"","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/TurnerSoftware.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"License.txt","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},"funding":{"github":"Turnerj"}},"created_at":"2019-08-09T12:48:26.000Z","updated_at":"2024-08-31T04:19:58.000Z","dependencies_parsed_at":"2023-01-30T23:31:17.123Z","dependency_job_id":"295f95ca-04af-445a-a53e-f41c22adde8f","html_url":"https://github.com/TurnerSoftware/CacheTower","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TurnerSoftware%2FCacheTower","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TurnerSoftware%2FCacheTower/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TurnerSoftware%2FCacheTower/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TurnerSoftware%2FCacheTower/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TurnerSoftware","download_url":"https://codeload.github.com/TurnerSoftware/CacheTower/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221513933,"owners_count":16835746,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["cache","cachemanager","caching","caching-library","distributed","dotnet","json","memory-cache","protobuf","redis","redis-caching"],"created_at":"2024-07-30T21:00:38.699Z","updated_at":"2024-10-26T08:30:40.514Z","avatar_url":"https://github.com/TurnerSoftware.png","language":"C#","readme":"﻿\u003cdiv align=\"center\"\u003e\n\n![Icon](images/icon.png)\n# Cache Tower\nAn efficient multi-layered caching system for .NET\n\n![Build](https://img.shields.io/github/actions/workflow/status/TurnerSoftware/CacheTower/build.yml?branch=main)\n[![Codecov](https://img.shields.io/codecov/c/github/turnersoftware/cachetower/main.svg)](https://codecov.io/gh/TurnerSoftware/CacheTower)\n[![NuGet](https://img.shields.io/nuget/v/CacheTower.svg)](https://www.nuget.org/packages/CacheTower/)\n\u003c/div\u003e\n\n## Overview\n\nComputers have multiple layers of caching from L1/L2/L3 CPU caches to RAM or even disk caches, each with a different purpose and performance profile.\n\n_Why don't we do this with our code?_\n\nCache Tower isn't a single type of cache, its a multi-layer solution to caching with each layer on top of another.\nA multi-layer cache provides the performance benefits of a fast cache like in-memory with the resilience of a file, database or Redis-backed cache.\n\nThis library was inspired by a blog post by Nick Craver about [how Stack Overflow do caching](https://nickcraver.com/blog/2019/08/06/stack-overflow-how-we-do-app-caching/).\nStack Overflow use a custom 2-layer caching solution with in-memory and Redis.\n\n## 📋 Features\n\n- High performance with low allocations ([see comparison to other caching solutions](/docs/Comparison.md)).\n- Local system caching with [in-memory](#MemoryCacheLayer) and [file-based caches](#JsonFileCacheLayer).\n- Distributed system caching with [MongoDB](#MongoDbCacheLayer) and [Redis](#RedisCacheLayer).\n- Supports one or more cache layers, [allowing a cache that has the best of all worlds](#Understanding-a-Multi-Layer-Caching-System).\n- [Background refreshes of non-expired but \"stale\" data](#Background-Refreshing-of-Stale-Data), helping avoid expired data cache misses.\n- Local refresh locking, guaranteeing only 1 factory call per key locally.\n- [Distributed refresh locking](#Distributed-Locking-via-Redis), guaranteeing only 1 factory call per key across multiple application instances.\n- [Distributed evictions](#Distributed-Eviction-via-Redis), helping to keep caches across multiple application instances the same.\n- All-async API, ready for high performance workloads.\n- [Targets minimum .NET Standard 2.0 for wide compatibility (.NET Framework 4.6.1+, .NET Core 2.0+, .NET 5.0+)](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support).\n\n## 🤝 Licensing and Support\n\nCache Tower is licensed under the MIT license. It is free to use in personal and commercial projects.\n\nThere are [support plans](https://turnersoftware.com.au/support-plans) available that cover all active [Turner Software OSS projects](https://github.com/TurnerSoftware).\nSupport plans provide private email support, expert usage advice for our projects, priority bug fixes and more.\nThese support plans help fund our OSS commitments to provide better software for everyone.\n\n## 📖 Table of Contents\n\n- [Installation](#installation)\n- [Understanding a Multi-Layer Caching System](#understanding-multi-layer-caching)\n- [The Cache Layers of Cache Tower](#official-cache-layers)\n- [Making Your Own Cache Layer](#custom-cache-layers)\n- [Cache Serializers](#cache-serializers)\n- [Getting Started](#getting-started)\n- [Background Refreshing of Stale Data](#background-refreshing)\n  - [Avoiding Disposed Contexts](#avoiding-disposed-contexts)\n- [Cache Tower Extensions](#extensions)\n  - [Automatic Cleanup](#automatic-cleanup)\n  - [Distributed Locking via Redis](#distributed-locking-via-redis)\n  - [Distributed Eviction via Redis](#distributed-eviction-via-redis)\n- [Performance and Comparisons](#performance)\n- [Advanced Usage](#advanced-usage)\n  - [Flushing the Cache](#flushing-the-cache)\n\n## \u003ca id=\"installation\" /\u003e 💿 Installation\n\nYou will need the `CacheTower` package on NuGet - it provides the core infrastructure for Cache Tower as well as an in-memory cache layer.\nTo add additional cache layers, you will need to install the appropriate packages as listed below.\n\n| Package | NuGet | Downloads |\n| ------- | ----- | --------- |\n| [CacheTower](https://www.nuget.org/packages/CacheTower/)\u003cbr\u003e\u003csmall\u003eThe core library with in-memory and file caching support.\u003c/small\u003e | ![NuGet](https://img.shields.io/nuget/v/CacheTower.svg) | ![NuGet](https://img.shields.io/nuget/dt/CacheTower.svg) |\n| [CacheTower.Extensions.Redis](https://www.nuget.org/packages/CacheTower.Extensions.Redis/)\u003cbr\u003e\u003csmall\u003eProvides distributed locking \u0026amp; eviction via Redis.\u003c/small\u003e | ![NuGet](https://img.shields.io/nuget/v/CacheTower.Extensions.Redis.svg) | ![NuGet](https://img.shields.io/nuget/dt/CacheTower.Extensions.Redis.svg) |\n| [CacheTower.Providers.Database.MongoDB](https://www.nuget.org/packages/CacheTower.Providers.Database.MongoDB/)\u003cbr\u003e\u003csmall\u003eProvides a cache layer for MongoDB.\u003c/small\u003e | ![NuGet](https://img.shields.io/nuget/v/CacheTower.Providers.Database.MongoDB.svg) | ![NuGet](https://img.shields.io/nuget/dt/CacheTower.Providers.Database.MongoDB.svg) |\n| [CacheTower.Providers.Redis](https://www.nuget.org/packages/CacheTower.Providers.Redis/)\u003cbr\u003e\u003csmall\u003eProvides a cache layer for Redis.\u003c/small\u003e | ![NuGet](https://img.shields.io/nuget/v/CacheTower.Providers.Redis.svg)| ![NuGet](https://img.shields.io/nuget/dt/CacheTower.Providers.Redis.svg) |\n| [CacheTower.Serializers.NewtonsoftJson](https://www.nuget.org/packages/CacheTower.Serializers.NewtonsoftJson/)\u003cbr\u003e\u003csmall\u003eProvides a JSON serializer using Newtonsoft.Json.\u003c/small\u003e | ![NuGet](https://img.shields.io/nuget/v/CacheTower.Serializers.NewtonsoftJson.svg) | ![NuGet](https://img.shields.io/nuget/dt/CacheTower.Serializers.NewtonsoftJson.svg) |\n| [CacheTower.Serializers.SystemTextJson](https://www.nuget.org/packages/CacheTower.Serializers.SystemTextJson/)\u003cbr\u003e\u003csmall\u003eProvides a JSON serializer using System.Text.Json.\u003c/small\u003e | ![NuGet](https://img.shields.io/nuget/v/CacheTower.Serializers.SystemTextJson.svg) | ![NuGet](https://img.shields.io/nuget/dt/CacheTower.Serializers.SystemTextJson.svg) |\n| [CacheTower.Serializers.Protobuf](https://www.nuget.org/packages/CacheTower.Serializers.Protobuf/)\u003cbr\u003e\u003csmall\u003eProvides a Protobuf serializer using protobuf-net.\u003c/small\u003e | ![NuGet](https://img.shields.io/nuget/v/CacheTower.Serializers.Protobuf.svg) | ![NuGet](https://img.shields.io/nuget/dt/CacheTower.Serializers.Protobuf.svg) |\n\n## \u003ca id=\"understanding-multi-layer-caching\" /\u003e 🎓 Understanding a Multi-Layer Caching System\n\nAt its most basic level, caching is designed to prevent reprocessing of data by storing the result _somewhere_.\nIn turn, preventing the reprocessing of data makes our code faster and more scaleable.\nDepending on the method of storage or transportation, the performance profile can vary drastically.\nNot only that, limitations of different types of caches can affect what you can do with your application.\n\n----\n\n### In-memory Caching\n\n✔ **Pro**: The fastest cache you can possible have!\n\n❌ **Con**: Only lasts the lifetime of the application.\n\n❌ **Con**: Memory capacity is more limited than other types of storage.\n\n### File-based Caching\n\n✔ **Pro**: Caching huge amounts of data is not just possible, it is usually cheap!\n\n✔ **Pro**: Resilient to application restarts!\n\n❌ **Con**: Even with fast SSDs, it can be _1500x slower_ than in-memory!\n\n### Database Caching\n\n✔ **Pro**: Database can run on the local machine _OR_ a remote machine!\n\n✔ **Pro**: Resilient to application restarts!\n\n✔ **Pro**: Can support multiple systems at the same time!\n\n❌ **Con**: Performance is only as good as the database provider itself. Don't forget network latency either!\n\n### Redis Caching\n\n✔ **Pro**: Redis can run on the local machine _OR_ a remote machine!\n\n✔ **Pro**: Resilient to application restarts!\n\n✔ **Pro**: Can support multiple systems at the same time!\n\n✔ **Pro**: High performance (faster than file-based, slower than in-memory).\n\n❌ **Con**: Linux only. *\n\n\u003csmall\u003e_* On Windows, [Memurai is your best Redis-compatible alternative](https://www.memurai.com/) - just need to list some sort of con for Redis and what it ran on was all I could think of at the time._\u003c/small\u003e\n\n----\n\nAn ideal caching solution should be fast, flexible, resilient and scale with your usage.\nIt is through combining these different cache types that this can be achieved.\n\nCache Tower supports n-layers of caching with flexibility to even make your own.\nYou \"stack\" the cache layers from the fastest to slowest for your particular usage.\n\nFor example, you might have:\n1. In-memory cache\n1. File-based cache\n\nWith this setup, you have:\n- A fast first-layer cache\n- A resilient second-layer cache\n\nIf your application restarts and your in-memory cache is empty, your second-layer cache will be checked.\nIf a valid cache entry is found, that will be returned.\n\nWhich combination of cache layers you use to build your cache stack is up to you and what is best for your application.\n\n|ℹ Don't need a multi-layer cache right now? |\n|:-|\n|Multi-layer caching is only one part of Cache Tower. If you only need one layer of caching, you can still leverage the different types of caches available and take advantage of background refreshing. If later on you need to add more layers, you only need to change the configuration of your cache stack!|\n\n\n## \u003ca id=\"official-cache-layers\" /\u003e 🏢 The Cache Layers of Cache Tower\n\nCache Tower has a number of officially supported cache layers that you can use.\n\n### MemoryCacheLayer\n\n\u003e Bundled with Cache Tower\n\n```csharp\nbuilder.AddMemoryCacheLayer();\n```\n\nAllows for fast, local memory caching.\nThe data is kept as a reference in memory and _not serialized_.\nIt is strongly recommended to treat the cached instance as immutable.\nModification of an in-memory cached value won't be updated to other cache layers.\n\n### FileCacheLayer\n\n\u003e Bundled with Cache Tower\n\n```csharp\nbuilder.AddFileCacheLayer(new FileCacheLayerOptions(\"~/\", NewtonsoftJsonCacheSerializer.Instance));\n```\n\nProvides a basic file-based caching solution using [your choice of serializer](#cache-serializers).\nIt stores each serialized cache item into its own file and uses a singular manifest file to track the status of the cache.\n\n### MongoDbCacheLayer\n\n```powershell\nPM\u003e Install-Package CacheTower.Providers.Database.MongoDB\n```\n\n```csharp\nbuilder.AddMongoDbCacheLayer(/* MongoDB Connection */);\n```\n\nAllows caching through a MongoDB server.\nCache entries are serialized to BSON using `MongoDB.Bson.Serialization.BsonSerializer`.\n\n### RedisCacheLayer\n\n```powershell\nPM\u003e Install-Package CacheTower.Providers.Redis\n```\n\n```csharp\nbuilder.AddRedisCacheLayer(/* Redis Connection */, new RedisCacheLayerOptions(ProtobufCacheSerializer.Instance));\n```\n\nAllows caching of data in Redis using [your choice of serializer](#cache-serializers).\n\n## \u003ca id=\"cache-serializers\" /\u003e ✍ Cache Serializers\n\nThe `FileCacheLayer` and `RedisCacheLayer` support custom serializers for caching data.\nDifferent serializers have different performance profiles as well as different tradeoffs for configuration.\n\n### NewtonsoftJsonCacheSerializer\n\n```powershell\nPM\u003e Install-Package CacheTower.Serializers.NewtonsoftJson\n```\n\nUses [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json/) to perform serialization.\n\n\n### SystemTextJsonCacheSerializer\n\n```powershell\nPM\u003e Install-Package CacheTower.Serializers.SystemTextJson\n```\n\nUses [System.Text.Json](https://www.nuget.org/packages/System.Text.Json) to perform serialization.\n\n### ProtobufCacheSerializer\n\n```powershell\nPM\u003e Install-Package CacheTower.Serializers.Protobuf\n```\n\nThe use of [protobuf-net requires decorating the class](https://github.com/protobuf-net/protobuf-net#1-first-decorate-your-classes) you want to cache with attributes `[ProtoContract]` and `[ProtoMember]`.\n\n**Example with Protobuf Attributes**\n```csharp\n[ProtoContract]\npublic class UserProfile\n{\n\t[ProtoMember(1)]\n\tpublic int UserId { get; set; }\n\t[ProtoMember(2)]\n\tpublic string UserName { get; set; }\n\n\t...\n}\n```\n\nAdditionally, as the Protobuf format doesn't have a way to represent an empty collection, these will be returned as `null`.\nWhile this can be inconvienent, using Protobuf ensures high performance and low allocations for serializing.\n\n## \u003ca id=\"custom-cache-layers\" /\u003e 🔨 Making Your Own Cache Layer\n\nYou can create your own cache layer by implementing [`ICacheLayer`](src/CacheTower/ICacheLayer.cs).\nWith it, you could implement caching layers that talk to SQL databases or cloud-based storage systems.\n\nWhen making your own cache layer, you will need to keep in mind that your implementation should be thread safe.\nCache Stack prevents multiple threads at once calling the value factory, not preventing multiple threads accessing the cache layer.\n\n## \u003ca id=\"getting-started\" /\u003e ⭐ Getting Started\n\n\u003e In this example, `UserContext` is a type added to the service collection.\nIt will be retrieved from the service provider every time a cache refresh is required.\n\nCreate and configure your `CacheStack`, this is the backbone for Cache Tower.\n\n```csharp\nservices.AddCacheStack\u003cUserContext\u003e((provider, builder) =\u003e builder\n\t.AddMemoryCacheLayer()\n\t.AddRedisCacheLayer(/* Your Redis Connection */, new RedisCacheLayerOptions(ProtobufCacheSerializer.Instance))\n\t.WithCleanupFrequency(TimeSpan.FromMinutes(5))\n);\n```\n\nThe cache stack will be injected into constructors that accept `ICacheStack\u003cUserContext\u003e`.\nOnce you have your cache stack, you can call `GetOrSetAsync` - this is the primary way to access the data in the cache.\n\n```csharp\nvar userId = 17;\n\nawait cacheStack.GetOrSetAsync\u003cUserProfile\u003e($\"user-{userId}\", async (old, context) =\u003e {\n\treturn await context.GetUserForIdAsync(userId);\n}, new CacheSettings(TimeSpan.FromDays(1), TimeSpan.FromMinutes(60));\n```\n\nThis call to `GetOrSetAsync` is configured with a cache expiry of `1 day` and an effective stale time after `60 minutes`.\nA good stale time is extremely useful for high performance scenarios where background refreshing is leveraged.\n\n## \u003ca id=\"background-refreshing\" /\u003e 🔁 Background Refreshing of Stale Data\n\nA high-performance cache needs to keep throughput high.\nHaving a cache miss because of expired data stalls the potential throughput.\n\nRather than only having a cache expiry, Cache Tower supports specifying a stale time for the cache entry.\nIf there is a cache hit on an item and the item is considered stale, it will perform a background refresh.\nBy doing this, it avoids blocking the request on a potential cache miss later.\n\n```csharp\nawait cacheStack.GetOrSetAsync\u003cMyCachedType\u003e(\"my-cache-key\", async (oldValue) =\u003e {\n\treturn await DoWorkThatNeedsToBeCachedAsync();\n}, new CacheSettings(timeToLive: TimeSpan.FromMinutes(60), staleAfter: TimeSpan.FromMinutes(30)));\n```\n\nIn the example above, the cache would expire in 60 minutes time (`timeToLive`).\nHowever, in 30 minutes, the cache will be considered stale (`staleAfter`).\n\n### Example Flow of Background Refreshing\n\n- You request an item from the cache\n  - No entry is found (cache miss)\n  - Your value factory is called\n  - The value is cached and returned\n- You request the item again later (after the `staleAfter` time but before `timeToLive`)\n  - The non-expired entry is found\n  - It is checked if it is stale (it is)\n  - A background refresh is started\n  - The non-expired (stale) entry is returned\n- You request the item again later (after the background refresh has finished)\n  - The non-expired entry is found\n  - It is checked if it is stale (it isn't)\n  - The non-expired non-stale entry is returned\n\n### Picking A Good Stale Time\n\nThere is no one-size-fits-all `staleAfter` value - it will depend on what you're caching and why.\nThat said, a reasonable rule of thumb would be to have a stale time no less than half of the `timeToLive`.\n\nThe shorter you make the `staleAfter` value, the more frequent background refreshing will happen.\n\n⚠ **Warning: Avoid setting a stale time that is too short!**\n\nThis is called _\"over refreshing\"_ whereby the background refreshing happens far more frequently than is useful.\nOver refreshing is at its worse with stale times shorter than a few minutes for cache entries that are frequently hit.\n\nThis has two effects:\n1. Frequent refreshes would increase load on the factory that provides the data to cache, potentially degrading its performance.\n2. Background refreshing, while efficient, has a non-zero cost when invoked thus putting additional pressure on the application where they are triggering.\n\nWith this in mind, it is not advised to set your `staleAfter` time to 0.\nThis effectively means the cache is always stale, performing a background refresh every hit of the cache.\n\n### Avoiding Disposed Contexts\n\nWith stale refreshes happening in the background, it is important to not reference potentially disposed objects and contexts.\nCache Tower can help with this by providing a context into the `GetOrSetAsync` method.\n\n```csharp\nawait cacheStack.GetOrSetAsync\u003cMyCachedType\u003e(\"my-cache-key\", async (oldValue, context) =\u003e {\n\treturn await DoWorkThatNeedsToBeCachedAsync(context);\n}, new CacheSettings(timeToLive: TimeSpan.FromMinutes(60), staleAfter: TimeSpan.FromMinutes(30)));\n```\n\nThe type of `context` is established at the time of configuring the cache stack.\n\n```csharp\nservices.AddCacheStack\u003cMyContext\u003e((provider, builder) =\u003e builder\n\t.AddMemoryCacheLayer()\n\t.WithCleanupFrequency(TimeSpan.FromMinutes(5))\n);\n```\n\nCache Tower will resolve the context from the same service collection the `AddCacheStack` call was added to.\nA scope will be created and context resolved every time there is a cache refresh.\n\nYou can use this context to hold any of the other objects or properties you need for safe access in a background thread, avoiding the possibility of accessing disposed objects like database connections.\n\n|ℹ Need a custom context resolving solution? |\n|:-|\n|You can specify your own context activator via `builder.CacheContextActivator` by implementing a custom `ICacheContextActivator`. To see a complete example, see [this integration for SimpleInjector](https://github.com/mgoodfellow/CacheTower.ContextActivators.SimpleInjector)|\n\n## \u003ca id=\"named-cache-stacks\" /\u003e 🏷 Named Cache Stacks\n\nYou might not always want a single large `CacheStack` shared between all your code - perhaps you want an in-memory cache with a Redis layer for one section and a file cache for another.\nCache Tower supports named `CacheStack` implementations via `ICacheStackAccessor`/`ICacheStackAccessor\u003cMyContext\u003e`.\n\nThis follows a similar pattern to how `IHttpClientFactory` works, allowing you to fetch the specific `CacheStack` implementation you want within your own class.\n\n```csharp\nservices.AddCacheStack\u003cMyContext\u003e(\"MyAwesomeCacheStack\", (provider, builder) =\u003e builder\n\t.AddMemoryCacheLayer()\n\t.WithCleanupFrequency(TimeSpan.FromMinutes(5))\n);\n\npublic class MyController\n{\n\tprivate readonly ICacheStack\u003cMyContext\u003e cacheStack;\n\t\n\tpublic MyController(ICacheStackAccessor\u003cMyContext\u003e cacheStackAccessor)\n\t{\n\t\tcacheStack = cacheStackAccessor.GetCacheStack(\"MyAwesomeCacheStack\");\n\t}\n}\n```\n\n## \u003ca id=\"extensions\" /\u003e 🏗 Cache Tower Extensions\n\nTo allow more flexibility, Cache Tower uses an extension system to enhance functionality.\nSome of these extensions rely on third party libraries and software to function correctly.\n\n### Automatic Cleanup\n\n\u003e Bundled with Cache Tower\n\n```csharp\nbuilder.WithCleanupFrequency(TimeSpan.FromMinutes(5));\n```\n\nThe cache layers themselves, for the most part, don't directly manage the co-ordination of when they need to delete expired data.\nWhile the `RedisCacheLayer` does handle cache expiration directly via Redis, none of the other official cache layers do.\nUnless you are only using the Redis cache layer, you will be wanting to include this extension in your cache stack.\n\n\n### Distributed Locking via Redis\n\n```powershell\nPM\u003e Install-Package CacheTower.Extensions.Redis\n```\n\n```csharp\nbuilder.WithRedisDistributedLocking(/* Your Redis connection */);\n```\n\nThe `RedisLockExtension` uses Redis as a shared lock between multiple instances of your application.\nUsing Redis in this way can avoid cache stampedes where multiple different web servers are refreshing values at the same instant.\n\nIf you are only running one web server/instance of your application, you won't need this extension.\n\n### Distributed Eviction via Redis\n\n```powershell\nPM\u003e Install-Package CacheTower.Extensions.Redis\n```\n\n```csharp\nbuilder.WithRedisRemoteEviction(/* Your Redis connection */);\n```\n\nThe `RedisRemoteEvictionExtension` extension uses the pub/sub feature of Redis to co-ordinate cache invalidation across multiple instances of your application.\nThis works in the situation where one web server has refreshed a key and wants to let the other web servers know their data is now old.\n\n## \u003ca id=\"performance\" /\u003e 🥇 Performance and Comparisons\n\nCache Tower has been built from the ground up for high performance and low memory consumption.\nAcross a number of benchmarks against other caching solutions, Cache Tower performs similarly or better than the competition.\n\nWhere Cache Tower makes up in speed, it may lack a variety of features common amongst other caching solutions.\nIt is important to weigh both the feature set and performance when deciding on a caching solution.\n\n[Performance Comparisons to Cache Tower Alternatives](/docs/Comparison.md)\n\n## \u003ca id=\"advanced-usage\" /\u003e 🧪 Advanced Usage\n\n### Flushing the Cache\n\nThere are times where you want to clear all cache layers - whether to help with debugging an issue or force fresh data on subsequent calls to the cache.\nThis type of action is available in Cache Tower however is obfuscated somewhat to prevent accidental use.\nPlease only flush the cache if you know what you're doing and what it would mean!\n\nIf you have injected `ICacheStack` or `ICacheStack\u003cUserContext\u003e` into your current method or class, you can cast to `IFlushableCacheStack`.\nThis interface exposes the method `FlushAsync`.\n\n```csharp\nawait (myCacheStack as IFlushableCacheStack).FlushAsync();\n```\n\nFor the `MemoryCacheLayer`, the backing store is cleared.\nFor file cache layers, all cache files are removed.\nFor MongoDB, all documents are deleted in the cache collection.\nFor Redis, a `FlushDB` command is sent.\n\nCombined with the `RedisRemoteEvictionExtension`, a call to `FlushAsync` will additionally be sent to all connected `CacheStack` instances.\n","funding_links":["https://github.com/sponsors/Turnerj"],"categories":["Caching"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTurnerSoftware%2FCacheTower","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTurnerSoftware%2FCacheTower","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTurnerSoftware%2FCacheTower/lists"}