(
+ key,
+ async () =>
+ {
+ await Task.Delay(FactoryDurationMs).ConfigureAwait(false);
+ return new SamplePayload();
+ }
+ );
+ tasks.Add(t);
+ });
+ });
- // CLEANUP
- cache.Compact(1);
+ await Task.WhenAll(tasks).ConfigureAwait(false);
}
+
+ // CLEANUP
+ cache.Compact(1);
}
}
}
diff --git a/benchmarks/ZiggyCreatures.FusionCache.Benchmarks/ZiggyCreatures.FusionCache.Benchmarks.csproj b/benchmarks/ZiggyCreatures.FusionCache.Benchmarks/ZiggyCreatures.FusionCache.Benchmarks.csproj
index 5353d315..e8eaebfd 100644
--- a/benchmarks/ZiggyCreatures.FusionCache.Benchmarks/ZiggyCreatures.FusionCache.Benchmarks.csproj
+++ b/benchmarks/ZiggyCreatures.FusionCache.Benchmarks/ZiggyCreatures.FusionCache.Benchmarks.csproj
@@ -2,17 +2,19 @@
Exe
- net8.0
+
+ net6.0;net8.0
latest
enable
ZiggyCreatures.Caching.Fusion.Benchmarks
-
+
+
diff --git a/docs/AGentleIntroduction.md b/docs/AGentleIntroduction.md
index 382b926f..74802f8d 100644
--- a/docs/AGentleIntroduction.md
+++ b/docs/AGentleIntroduction.md
@@ -7,7 +7,7 @@
# π¦ A Gentle Introduction
-FusionCache is an easy to use, fast and robust cache with advanced resiliency features and an optional distributed 2nd layer.
+FusionCache is an easy to use, fast and robust cache with advanced resiliency features and an optional distributed 2nd level.
It uses a memory cache (any impl of the standard `IMemoryCache` interface) as the **primary** backing store and, optionally, a distributed cache (any impl of the standard `IDistributedCache` interface) as a **secondary** backing store for better resilience and higher performance, for example in a multi-node scenario or to avoid the typical effects of a cold start (initial empty cache, maybe after a restart).
@@ -52,7 +52,7 @@ Read more [**here**](CacheLevels.md), or enjoy the complete [**step by step**](S
## π’ Backplane ([more](Backplane.md))
-If we are in a scenario with multiple nodes, each with their own local memory cache, we typically also use a distributed cache as a secondary layer (see above).
+If we are in a scenario with multiple nodes, each with their own local memory cache, we typically also use a distributed cache as a secondary level (see above).
Even using that, we may find that each memory cache may not be necessarily in-sync with the others, because when a value is cached locally it will stay the same until the `Duration` passes and expiration occurs.
@@ -144,7 +144,7 @@ At a high level there are 6 core methods:
- `GetOrSet[Async]`
- `Expire[Async]`
-All of them work **on both the memory cache and the distributed cache** (if any) in a transparent way: we don't have to do anything extra for it to coordinate the 2 layers.
+All of them work **on both the memory cache and the distributed cache** (if any) in a transparent way: we don't have to do anything extra for it to coordinate the 2 levels.
All of them are available in both a **sync** and an **async** version.
@@ -179,7 +179,7 @@ Read more [**here**](NamedCaches.md).
## π Events ([more](Events.md))
-There's a comprehensive set of events to subscribe to regarding core events inside of a FusionCache instance, both at a high level and at lower levels (memory/distributed layers).
+There's a comprehensive set of events to subscribe to regarding core events inside of a FusionCache instance, both at a high level and at lower levels (memory/distributed levels).
Read more [**here**](Events.md).
diff --git a/docs/Backplane.md b/docs/Backplane.md
index 20539081..4ddc53a5 100644
--- a/docs/Backplane.md
+++ b/docs/Backplane.md
@@ -7,7 +7,7 @@
# π’ Backplane
-If we are in a scenario with multiple nodes, each with their own local memory cache, we typically also use a distributed cache as a secondary layer (see [here](CacheLevels.md)).
+If we are in a scenario with multiple nodes, each with their own local memory cache, we typically also use a distributed cache as a secondary level (see [here](CacheLevels.md)).
But even when using that, we may find that each memory cache on each node may not be in-sync with the others, because when a value is cached locally it will stay the same until the `Duration` passes and expiration occurs.
@@ -115,7 +115,7 @@ var redis = new RedisCache(new RedisCacheOptions() {
// INSTANTIATE THE FUSION CACHE SERIALIZER
var serializer = new FusionCacheNewtonsoftJsonSerializer();
-// SETUP THE DISTRIBUTED 2ND LAYER
+// SETUP THE DISTRIBUTED 2ND LEVEL
cache.SetupDistributedCache(redis, serializer);
// CREATE THE BACKPLANE
diff --git a/docs/CacheLevels.md b/docs/CacheLevels.md
index 2e9717df..ca54e6cd 100644
--- a/docs/CacheLevels.md
+++ b/docs/CacheLevels.md
@@ -19,9 +19,9 @@ On top of this you also need to specify a *serializer* to use, by providing an i
Basically it boils down to 2 possible ways:
-- **1οΈβ£ MEMORY ONLY:** if you don't setup a 2nd layer, FusionCache will act as a **normal memory cache** (`IMemoryCache`)
+- **1οΈβ£ MEMORY ONLY:** if you don't setup a 2nd level, FusionCache will act as a **normal memory cache** (`IMemoryCache`)
-- **2οΈβ£ MEMORY + DISTRIBUTED:** if you also setup a 2nd layer, FusionCache will automatically coordinate the 2 layers (`IMemoryCache` + `IDistributedCache`) gracefully handling all edge cases to get a smooth experience
+- **2οΈβ£ MEMORY + DISTRIBUTED:** if you also setup a 2nd level, FusionCache will automatically coordinate the 2 levels (`IMemoryCache` + `IDistributedCache`) gracefully handling all edge cases to get a smooth experience
Of course in both cases you will also have at your disposal the added ability to enable extra features, like [fail-safe](FailSafe.md), [advanced timeouts](Timeouts.md) and so on.
@@ -113,7 +113,7 @@ var serializer = new FusionCacheNewtonsoftJsonSerializer();
// INSTANTIATE FUSION CACHE
var cache = new FusionCache(new FusionCacheOptions());
-// SETUP THE DISTRIBUTED 2ND LAYER
+// SETUP THE DISTRIBUTED 2ND LEVEL
cache.SetupDistributedCache(redis, serializer);
```
diff --git a/docs/Comparison.md b/docs/Comparison.md
index 3d63826a..5152d90f 100644
--- a/docs/Comparison.md
+++ b/docs/Comparison.md
@@ -77,5 +77,5 @@ This is how they compare:
| **License** | `MIT` | `Apache 2.0` | `MIT` | `MIT` | `MIT` |
βΉ **NOTES**
-- (1): **EasyCaching** supports an `HybridCachingProvider` to handle 2 layers transparently, but it's implemented in a way that checks the distributed cache before the in-memory one, kind of invalidating the benefits of the latter, which is important to know.
+- (1): **EasyCaching** supports an `HybridCachingProvider` to handle 2 levels transparently, but it's implemented in a way that checks the distributed cache before the in-memory one, kind of invalidating the benefits of the latter, which is important to know.
- (2): **LazyCache** does have both sync and async support, but not for all the available methods (eg. `Remove`). This may be perfectly fine for you or not, but it's good to know.
diff --git a/docs/CoreMethods.md b/docs/CoreMethods.md
index 8da26b82..0e84f3a2 100644
--- a/docs/CoreMethods.md
+++ b/docs/CoreMethods.md
@@ -15,7 +15,7 @@ At a high level there are 6 core methods:
- `GetOrSet[Async]`
- `Expire[Async]`
-All of them work **on both the memory cache and the distributed cache** (if any) in a transparent way: you don't have to do anything extra for it to coordinate the 2 layers.
+All of them work **on both the memory cache and the distributed cache** (if any) in a transparent way: you don't have to do anything extra for it to coordinate the 2 levels.
All of them are available in both a **sync** and an **async** version.
diff --git a/docs/Events.md b/docs/Events.md
index b412f826..500fa638 100644
--- a/docs/Events.md
+++ b/docs/Events.md
@@ -8,15 +8,15 @@
FusionCache has a comprehensive set of events you can subscribe to, so you can be notified of core events when they happen.
-They cover both high-level things related to the FusionCache instance as a whole such as cache hits/misses, fail-safe activations or factory timeouts but also more lower level things related to each specific layer (memory/distributed) such as evictions in the memory cache, serialization/deserialization errors or cache hits/misses, but specific for each specific layer.
+They cover both high-level things related to the FusionCache instance as a whole such as cache hits/misses, fail-safe activations or factory timeouts but also more lower level things related to each specific level (memory/distributed) such as evictions in the memory cache, serialization/deserialization errors or cache hits/misses, but specific for each specific level.
Each event is modeled with native .NET `event`s so if you know how to use them you'll feel at home subscribing and unsubscribing to them.
They are grouped into "hubs":
- **general**: useful for high level events related to the FusionCache instance as a whole. It is accessible via the `cache.Events` object
-- **memory layer**: useful for the lower level memory layer. It is accessible via the `cache.Events.Memory` object
-- **distributed layer**: useful for the lower level distributed layer (if any). It is accessible via the `cache.Events.Distributed` object
+- **memory level**: useful for the lower level memory level. It is accessible via the `cache.Events.Memory` object
+- **distributed level**: useful for the lower level distributed level (if any). It is accessible via the `cache.Events.Distributed` object
An event handler is a simple .NET function you can express in the usual ways (lambda, etc...).
@@ -56,7 +56,7 @@ Here's a non comprehensive list of the available events:
- **Hit**: when a value was in the cache (there's also a flag to indicate if the data was stale or not)
- **Miss**: when a value was not in the cache
- **Remove**: when an entry has been removed
-- **Eviction**: when an eviction occurred, along with the reason (only for the memory layer)
+- **Eviction**: when an eviction occurred, along with the reason (only for the memory level)
- **FailSafeActivation**: when the fail-safe mechanism kicked in
There are more, and you easily discover them with code completion by just typing `cache.Events.` or `cache.Events.Memory` / `cache.Events.Distributed` in your code editor.
@@ -73,7 +73,7 @@ A single high level `GetOrSet` method call may result in one of these two set of
- **π’ 1 HIT**: if found
- **π΄ 1 MISS + π΅ 1 SET**: if not found, the factory is called and the returned value is then set into the cache
-But if you subscribe to the lower **memory layer** events you may fall into one of these situations:
+But if you subscribe to the lower **memory level** events you may fall into one of these situations:
- **π’ 1 HIT**: if immediately found
- **π΄ 1 MISS + π’ 1 HIT**: if not immediately found, a lock is acquired to call the factory in an optimized way, then another cache read is done and this time the entry is found (because another thread set it while acquiring the lock)
diff --git a/docs/OpenTelemetry.md b/docs/OpenTelemetry.md
new file mode 100644
index 00000000..91998181
--- /dev/null
+++ b/docs/OpenTelemetry.md
@@ -0,0 +1,93 @@
+
+
+![FusionCache logo](logo-128x128.png)
+
+
+
+
+# π OpenTelemetry
+
+Observability is a key feature of modern software systems that allows us to clearly see what is going on at any given time.
+
+It is composed from 3 main _signals_:
+- Logs
+- Traces
+- Metrics
+
+FusionCache has rich [logging](Logging.md) support via the standard [ILogger](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging) interface, with various options available to granularly configure it.
+
+For metrics and traces though, no unified abstraction has ever been available for app and library authors to produce signals, and 3rd party services to consume them, in a consistent and cross-platform way.
+
+That changed when [OpenTelemetry](https://opentelemetry.io/) has been introduced.
+
+From some time now OpenTelemetry has become a fundamental staple in modern software development to bring complete observability to our applications: after the official standard, the semantic conventions and all the needed pieces finally become `v1.0`, FusionCache happily adopted it as *the* observability mechanism.
+
+This is an example of the visibility we can achieve, in this case in [Honeycomb.io](Honeycomb):
+
+![An example of the visibility obtainable by using OpenTelemetry, in this case thanks to the Honeycomb SAAS](images/opentelemetry-example.png)
+
+βΉοΈ Please note that I don't have any affiliation, partnership or else with Honeycomb: I just created a free trial to test the OpenTelemetry integration.
+
+Other than that I've used a couple of their free [Observability Office Hours](https://www.honeycomb.io/devrel/observability-office-hours) to chat with the awesome [Martin Thwaites](https://twitter.com/MartinDotNet) to better understand the right way to name activity sources, which tags to use in the metrics and so on: thanks Martin!
+
+## OpenTelemetry in .NET
+
+Normally in other languages there's an OpenTelemetry SDK being made available but the kind folks working on it, so that library authors can use that SDK to integrate into the OpenTelemetry world and communicate with processors, exporters and so no.
+
+This is also true for .NET, but with a nice catch: in .NET there's always been some form of observability support via core primitives like `Activity`, `ActivitySource`, `Meter` and similar classes already part of the BCL, so instead of creating new primitives the OpenTelemetry team decided to use the existing abstractions and "talk" to them: in this way it's possible for library authors **not** to take a hard dependency on the OpenTelemetry packages and simply use the existing primitives and have the consuming side of the whole OpenTelemetry pipeline interact with them.
+
+Nice, very nice, and this is what FusionCache did.
+
+## How to use it
+
+It is possible to opt-in to generate traces and metrics for both:
+- **high-level operations**: things like `GetOrSet`/`Set`/`Remove` operations, `Hit`/`Miss` events, etc
+- **low-level operations**: things like memory/distributed level operations, backplane events, etc
+
+There's also a new [package](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.OpenTelemetry/) specific for OpenTelemetry that adds native support to instrument our applications, with FusionCache specific options ready to use, like including low-level signals or not.
+
+It's then possible to simply plug one of the existing OpenTelemetry-compatible exporters for systems like [Jaeger](https://www.jaegertracing.io/), [Prometheus](https://prometheus.io/) or [Honeycomb](https://www.honeycomb.io/) and voilΓ , there we have full observability of our FusionCache instances.
+
+### π©βπ» Example
+
+Add the [package](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.OpenTelemetry/):
+
+```PowerShell
+PM> Install-Package ZiggyCreatures.FusionCache.OpenTelemetry
+```
+
+and enable either traces, metrics or both for FusionCache.
+
+Without dependency injection we can do this:
+
+```csharp
+// SETUP TRACES
+using var tracerProvider = Sdk.CreateTracerProviderBuilder()
+ .AddFusionCacheInstrumentation()
+ .AddConsoleExporter()
+ .Build();
+
+// SETUP METRICS
+using var meterProvider = Sdk.CreateMeterProviderBuilder()
+ .AddFusionCacheInstrumentation()
+ .AddConsoleExporter()
+ .Build();
+```
+
+Or via dependency injection, like this:
+
+```csharp
+services.AddOpenTelemetry()
+ // SETUP TRACES
+ .WithTracing(tracing => tracing
+ .AddFusionCacheInstrumentation()
+ .AddConsoleExporter()
+ )
+ // SETUP METRICS
+ .WithMetrics(metrics => metrics
+ .AddFusionCacheInstrumentation()
+ .AddConsoleExporter()
+ );
+```
+
+Easy peasy.
\ No newline at end of file
diff --git a/docs/Options.md b/docs/Options.md
index 9e3c8211..ac4e7eaa 100644
--- a/docs/Options.md
+++ b/docs/Options.md
@@ -105,8 +105,8 @@ For a better **developer experience** and to **consume less memory** (higher per
| `FactorySoftTimeout` | `TimeSpan` | `none` | The maximum execution time allowed for the factory, applied only if fail-safe is enabled and there is a fallback value to return. |
| `FactoryHardTimeout` | `TimeSpan` | `none` | The maximum execution time allowed for the factory in any case, even if there is not a stale value to fallback to. |
| `AllowTimedOutFactoryBackgroundCompletion` | `bool` | `true` | It enables a factory that has hit a synthetic timeout (both soft/hard) to complete in the background and update the cache with the new value. |
-| π§ββοΈ `DistributedCacheDuration` | `TimeSpan?` | `null` | The custom duration to use for the distributed cache: this allows to have different duration between the 1st and 2nd layers. If `null`, the normal `Duration` will be used. |
-| π§ββοΈ `DistributedCacheFailSafeMaxDuration` | `TimeSpan?` | `null` | The custom fail-safe max duration to use for the distributed cache: this allows to have different duration between the 1st and 2nd layers. If `null`, the normal `FailSafeMaxDuration` will be used. |
+| π§ββοΈ `DistributedCacheDuration` | `TimeSpan?` | `null` | The custom duration to use for the distributed cache: this allows to have different duration between the 1st and 2nd levels. If `null`, the normal `Duration` will be used. |
+| π§ββοΈ `DistributedCacheFailSafeMaxDuration` | `TimeSpan?` | `null` | The custom fail-safe max duration to use for the distributed cache: this allows to have different duration between the 1st and 2nd levels. If `null`, the normal `FailSafeMaxDuration` will be used. |
| π§ββοΈ `DistributedCacheSoftTimeout` | `TimeSpan` | `none` | The maximum execution time allowed for each operation on the distributed cache when is not problematic to simply timeout. |
| π§ββοΈ `DistributedCacheHardTimeout` | `TimeSpan` | `none` | The maximum execution time allowed for each operation on the distributed cache in any case, even if there is not a stale value to fallback to. |
| π§ββοΈ `AllowBackgroundDistributedCacheOperations` | `bool` | `false` | Normally operations on the distributed cache are executed in a blocking fashion: setting this flag to true let them run in the background in a kind of fire-and-forget way. This will give a perf boost, but watch out for rare side effects. |
@@ -117,5 +117,5 @@ For a better **developer experience** and to **consume less memory** (higher per
| π§ββοΈ `ReThrowBackplaneExceptions` | `bool` | `false` | Set this to true to allow the bubble up of backplane exceptions (default is `false`). Please note that, even if set to true, in some cases you would also need `AllowBackgroundBackplaneOperations` set to false. |
| π§ββοΈ `EagerRefreshThreshold` | `float?` | `null` | The threshold to apply when deciding whether to refresh the cache entry eagerly (that is, before the actual expiration). |
| π§ββοΈ `SkipDistributedCache` | `bool` | `false` | Skip the usage of the distributed cache, if any. |
-| π§ββοΈ `SkipDistributedCacheReadWhenStale` | `bool` | `false` | When a 2nd layer (distributed cache) is used and a cache entry in the 1st layer (memory cache) is found but is stale, a read is done on the distributed cache: the reason is that in a multi-node environment another node may have updated the cache entry, so we may found a newer version of it. |
+| π§ββοΈ `SkipDistributedCacheReadWhenStale` | `bool` | `false` | When a 2nd level (distributed cache) is used and a cache entry in the 1st level (memory cache) is found but is stale, a read is done on the distributed cache: the reason is that in a multi-node environment another node may have updated the cache entry, so we may found a newer version of it. |
| π§ββοΈ `SkipMemoryCache` | `bool` | `false` | Skip the usage of the memory cache. |
diff --git a/docs/README.md b/docs/README.md
index 80c47c2d..2df2b319 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -37,7 +37,8 @@ A deeper description of the main features:
- [**π¦
Eager Refresh**](EagerRefresh.md): start a non-blocking background refresh before the expiration occurs
- [**π Dependency Injection**](DependencyInjection.md): native support for Dependency Injection, with a nice fluent interface including a Builder support
- [**π Named Caches**](NamedCaches.md): easily work with multiple named caches, even if differently configured
+- [**π OpenTelemetry**](OpenTelemetry.md): native observability support via OpenTelemetry
+- [**π Logging**](Logging.md): comprehensive, structured and customizable, via the standard `ILogger` interface
- [**π« Natively sync/async**](CoreMethods.md): native support for both the synchronous and asynchronous programming model
- [**π Events**](Events.md): a comprehensive set of events, both at a high level and at lower levels (memory/distributed)
- [**𧩠Plugins**](Plugins.md): extend FusionCache with additional behavior like adding support for metrics, statistics, etc...
-- [**π Logging**](Logging.md): comprehensive, structured and customizable, via the standard `ILogger` interface
diff --git a/docs/Timeouts.md b/docs/Timeouts.md
index c8f595f3..fdfc04a9 100644
--- a/docs/Timeouts.md
+++ b/docs/Timeouts.md
@@ -95,7 +95,7 @@ var serializer = new FusionCacheNewtonsoftJsonSerializer();
// INSTANTIATE FUSION CACHE
var cache = new FusionCache(new FusionCacheOptions());
-// SETUP THE DISTRIBUTED 2ND LAYER
+// SETUP THE DISTRIBUTED 2ND LEVEL
cache.SetupDistributedCache(redis, serializer);
// SET A VALUE IN THE CACHE VIA FUSION CACHE, WITH BACKGROUND DISTRIBUTED OPERATIONS
diff --git a/docs/images/opentelemetry-example.png b/docs/images/opentelemetry-example.png
new file mode 100644
index 00000000..3215f585
Binary files /dev/null and b/docs/images/opentelemetry-example.png differ
diff --git a/src/ZiggyCreatures.FusionCache.Backplane.Memory/GlobalSuppressions.cs b/src/ZiggyCreatures.FusionCache.Backplane.Memory/GlobalSuppressions.cs
index b961566c..3389b8d5 100644
--- a/src/ZiggyCreatures.FusionCache.Backplane.Memory/GlobalSuppressions.cs
+++ b/src/ZiggyCreatures.FusionCache.Backplane.Memory/GlobalSuppressions.cs
@@ -5,4 +5,4 @@
using System.Diagnostics.CodeAnalysis;
-[assembly: SuppressMessage("Simplification", "RCS1049:Simplify boolean comparison.", Justification = "")]
+[assembly: SuppressMessage("Simplification", "RCS1049:Simplify boolean comparison.")]
diff --git a/src/ZiggyCreatures.FusionCache.Backplane.Memory/MemoryBackplaneExtensions.cs b/src/ZiggyCreatures.FusionCache.Backplane.Memory/MemoryBackplaneExtensions.cs
index 4ec8f41d..ebd2d3cb 100644
--- a/src/ZiggyCreatures.FusionCache.Backplane.Memory/MemoryBackplaneExtensions.cs
+++ b/src/ZiggyCreatures.FusionCache.Backplane.Memory/MemoryBackplaneExtensions.cs
@@ -49,7 +49,11 @@ public static IFusionCacheBuilder WithMemoryBackplane(this IFusionCacheBuilder b
return builder
.WithBackplane(sp =>
{
- var options = sp.GetService>().Get(builder.CacheName);
+ var options = sp.GetService>()?.Get(builder.CacheName);
+
+ if (options is null)
+ throw new NullReferenceException($"Unable to find an instance of {nameof(MemoryBackplaneOptions)} for the cache named '{builder.CacheName}'.");
+
if (setupOptionsAction is not null)
setupOptionsAction?.Invoke(options);
var logger = sp.GetService>();
diff --git a/src/ZiggyCreatures.FusionCache.Backplane.Memory/ZiggyCreatures.FusionCache.Backplane.Memory.csproj b/src/ZiggyCreatures.FusionCache.Backplane.Memory/ZiggyCreatures.FusionCache.Backplane.Memory.csproj
index b4717310..c34ec142 100644
--- a/src/ZiggyCreatures.FusionCache.Backplane.Memory/ZiggyCreatures.FusionCache.Backplane.Memory.csproj
+++ b/src/ZiggyCreatures.FusionCache.Backplane.Memory/ZiggyCreatures.FusionCache.Backplane.Memory.csproj
@@ -4,16 +4,16 @@
netstandard2.0
latest
enable
- 0.24.0
+ 0.25.0
ZiggyCreatures.FusionCache.Backplane.Memory
logo-128x128.png
FusionCache in memory backplane, used for testing
- backplane;memory;stackexchange;caching;cache;multi-level;multilevel;fusion;fusioncache;fusion-cache;performance;async;ziggy
+ backplane;memory;caching;cache;multi-level;multilevel;fusion;fusioncache;fusion-cache;performance;async;ziggy
ZiggyCreatures.Caching.Fusion.Backplane.Memory
ZiggyCreatures.FusionCache.Backplane.Memory.xml
README.md
- - Update: dependencies
+ - Update: package dependencies
diff --git a/src/ZiggyCreatures.FusionCache.Backplane.Memory/docs/README.md b/src/ZiggyCreatures.FusionCache.Backplane.Memory/docs/README.md
index 0377f29c..fe5e2358 100644
--- a/src/ZiggyCreatures.FusionCache.Backplane.Memory/docs/README.md
+++ b/src/ZiggyCreatures.FusionCache.Backplane.Memory/docs/README.md
@@ -2,7 +2,7 @@
![FusionCache logo](https://raw.githubusercontent.com/ZiggyCreatures/FusionCache/main/docs/logo-256x256.png)
-### FusionCache is an easy to use, fast and robust cache with advanced resiliency features and an optional distributed 2nd layer.
+### FusionCache is an easy to use, fast and robust cache with advanced resiliency features and an optional distributed 2nd level.
It was born after years of dealing with all sorts of different types of caches: memory caching, distributed caching, http caching, CDNs, browser cache, offline cache, you name it. So I've tried to put together these experiences and came up with FusionCache.
diff --git a/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/RedisBackplaneOptions.cs b/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/RedisBackplaneOptions.cs
index 15d1f368..3ff8b64c 100644
--- a/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/RedisBackplaneOptions.cs
+++ b/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/RedisBackplaneOptions.cs
@@ -32,7 +32,7 @@ public class RedisBackplaneOptions
/// DEPRECATED: verify that at least one clients received the notifications after each publish.
///
[EditorBrowsable(EditorBrowsableState.Never)]
- [Obsolete]
+ [Obsolete("Please stop using this, it is now obsolete.")]
public bool VerifyReceivedClientsCountAfterPublish { get; set; } = false;
RedisBackplaneOptions IOptions.Value
diff --git a/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/StackExchangeRedisBackplaneExtensions.cs b/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/StackExchangeRedisBackplaneExtensions.cs
index dc783b6b..06617de9 100644
--- a/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/StackExchangeRedisBackplaneExtensions.cs
+++ b/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/StackExchangeRedisBackplaneExtensions.cs
@@ -49,7 +49,10 @@ public static IFusionCacheBuilder WithStackExchangeRedisBackplane(this IFusionCa
return builder
.WithBackplane(sp =>
{
- var options = sp.GetService>().Get(builder.CacheName);
+ var options = sp.GetService>()?.Get(builder.CacheName);
+ if (options is null)
+ throw new InvalidOperationException($"Unable to find a valid {nameof(RedisBackplaneOptions)} instance for the current cache name '{builder.CacheName}'.");
+
if (setupOptionsAction is not null)
setupOptionsAction?.Invoke(options);
var logger = sp.GetService>();
diff --git a/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis.csproj b/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis.csproj
index bc52432a..79ccc563 100644
--- a/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis.csproj
+++ b/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis.csproj
@@ -4,7 +4,7 @@
netstandard2.0
latest
enable
- 0.24.0
+ 0.25.0
ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis
logo-128x128.png
FusionCache backplane for Redis based on the StackExchange.Redis library
@@ -13,8 +13,7 @@
ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis.xml
README.md
- - Added: support for MultiplexerFactory in Redis Backplane
- - Update: dependencies
+ - Update: package dependencies
@@ -28,7 +27,7 @@