FusionCache

FusionCache is an easy to use, fast and robust hybrid cache with advanced resiliency features.

MIT License

Stars
1.6K
Committers
11

Bot releases are hidden (Show)

FusionCache - v1.2.0 Latest Release

Published by jodydonetti 5 months ago

πŸ”‘ Added DI Keyed Services support (docs)

Since .NET 8 we now have native support for multiple services of the same type, identified by different names, thanks to the addition of so called keyed services.

The idea is basically that we can now register services not only by type but also by specifying the name, like this:

services.AddKeyedSingleton<MyService>("foo");
services.AddKeyedSingleton<MyService>("bar");

and later is possible to resolve it by both the type and a name.

Another way is to simply mark a constructor parameter or web action with the [FromKeyedServices] attribute, like this:

app.MapGet("/foo", ([FromKeyedServices("foo")] MyService myService) => myService.Whatever(123));
app.MapGet("/bar", ([FromKeyedServices("bar")] MyService myService) => myService.Whatever(123));

From now on, when registering a named cache, we can simply add AsKeyedServiceByCacheName() like this:

services.AddFusionCache("MyCache")
  .AsKeyedServiceByCacheName();

and later we'll be able to have the named cache both as usual:

app.MapGet("/foo", (IFusionCacheProvider cacheProvider) => {
  var cache = cacheProvider.GetCache("MyCache");
  cache.Set("key", 123);
});

and as a keyed service, like this:

app.MapGet("/foo", ([FromKeyedServices("MyCache")] IFusionCache cache) => {
  cache.Set("key", 123);
});

We can even use AsKeyedService(object? serviceKey) and specify a custom service key like for any other keyed service in .NET.

On top of being able to register FusionCache as a keyed service, we can even consume keyed services as FusionCache components, like memory cache, distributed cache, serializer, backplane, etc.

For more read at the official docs.

See here for the original issue.

⚑ Add PreferSyncSerialization option

It has been observed that in some situations async serialization and deserialization can be slower than the sync counterpart: this has nothing to do with FusionCache itself, but how serialization works in general.

So I added a new option called PreferSyncSerialization (default: false, fully backward compatible), that can allow the sync version to be preferred.

See here for the original issue.

πŸ”­ Better OpenTelemetry traces for backplane notifications

Community user @imperugo noticed that when using the backplane with OpenTelemetry traces enabled, all the spans for the notifications incoming via the backplane were put under one single parent span, basically creating a single mega-span "containing" all the others.

image

Ideally, each span for each notification should be on their own, and now this is the case.

Also while I was at it I noticed another couple of things that, if added to the traces, could make the developer experience better.
In detail:

  • include a tag with the source id (the InstanceId of the remote FusionCache instance)
  • change the status of the trace in case of errors, like invalid notifications or similar
  • add an event in case of, well, some event occurring during the activity

So yeah, I took this opportunity to make the overall experience better.

Finally, since backplane notifications can create a lot of background noise inside observability tools, I changed the default so that, even when there's a backplane setup, traces for backplane notifications are not enabled: to change this simply enable it at setup time.

See here for the original issue.

🐡 Add ChaosMemoryCache

Among all the chaos-related components already available, one to work with IMemoryCache was missing: not anymore.

βœ… Better tests

Some more tests have been added, including better cross-platform snapshot tests.

πŸ“• Docs

Updated some docs with the latest new things.

FusionCache - v1.2.0-preview1

Published by jodydonetti 5 months ago

πŸ”‘ Added DI Keyed Services support

Since .NET 8 we now have native support for multiple services of the same type, identified by different names, thanks to the addition of so called keyed services.

The idea is basically that we can now register services not only by type but also by specifying the name, like this:

services.AddKeyedSingleton<MyService>("foo");
services.AddKeyedSingleton<MyService>("bar");

and later is possible to resolve it by both the type and a name.

Another way is to simply mark a constructor parameter or web action with the [FromKeyedServices] attribute, like this:

app.MapGet("/foo", ([FromKeyedServices("foo")] MyService myService) => myService.Whatever(123));
app.MapGet("/bar", ([FromKeyedServices("bar")] MyService myService) => myService.Whatever(123));

From now on, when registering a named cache, we can simply add AsKeyedService() like this:

services.AddFusionCache("MyCache")
  .AsKeyedService();

and later we'll be able to have the named cache with something like this:

app.MapGet("/foo", ([FromKeyedServices("foo")] IFusionCache cache) => {
  cache.Set("key", 123);
});

Of course the named cache provider way is still available, like this:

app.MapGet("/foo", (IFusionCacheProvider cacheProvider) => {
  var cache = cacheProvider.GetCache("foo");
  cache.Set("key", 123);
});

See here for the original issue.

⚑ Add PreferSyncSerialization option

It has been observed that in some situations async serialization and deserialization can be slower than the sync counterpart: this has nothing to do with FusionCache itself, but how serialization works in general.

So I added a new option called PreferSyncSerialization (default: false, fully backward compatible), that can allow the sync version to be preferred.

See here for the original issue.

🐡 Add ChaosMemoryCache

Among all the chaos-related components already available, one to work with IMemoryCache was missing: not anymore.

βœ… Better tests

Some more tests have been added.

πŸ“• Docs

Updated some docs with the latest new things.

FusionCache - v1.1.0

Published by jodydonetti 6 months ago

The theme for this release is some bug fixes, general quality of life improvements and some minor perf optimizations.

πŸ“ž Added a couple of missing OnMiss events

Community user @ConMur noticed that sometimes, in a couple of code paths related to distributed cache operations, FusionCache was missing some OnMiss events (no pun intended): now this has been fixed.

See here for the original issue.

πŸ’£ Better FailSafeMaxDuration handling

User @F2 and user @sabbadino both noticed that fail-safe max duration was not being respected all the times, particularly when multiple fail-safe activations actually occurred in sequence there was in fact the risk of extending the physical duration of the cache more than what should've been correct.
This has been fixed (while also introducing some nice memory and cpu savings!).

See here and here for the original issues.

πŸ’€ A rare case of deadlock

While doing some extensive testing community user @martindisch discovered a rare case of deadlock that was happening only when all of these conditions were met simultaneously:

  • Eager Refresh enabled
  • call GetOrSet[Async] while passing a CancellationToken
  • the call that actually triggered the eager refresh is cancelled, after the eager refresh kicked in but before it finished
  • not all the times, but only when the execution flow passed in a certain spot at a certain time

This issue kicked off an experimentation about a reworking of FusionCache internals regarding the general theme of cancellations of background factory executions in general (eager refresh, background factory completion with soft/hard timeouts, etc): I am happy to say that now the deadlock is gone for good.
To do that well I slightly changed the behaviour of FusionCache regarding background factory executions: now they cannot be cancelled anymore by cancelling the original request that generated them, since it doesn't make that much sense to begin with, since a cancellation is used to cancel the current operation, but a background execution (particularly with eager refresh) is basically a side effect, which does have a life of its own, so it doesn't make a lot of sense to cancel that, too.

All in all, there should be realistically no discernible externally observable difference in behaviour (and no more deadlocks!).

Finally, I've added some tests to detect these scenario to avoid future regressions.

See here for the original issue.

πŸ“’ Better AutoRecoveryDelay default value

The default value for AutoRecoveryDelay has been changed from 2s to 5s, to better align with the standard reconnect timing of StackExchange.Redis, which is the most commonly used implementation for the distributed cache and the backplane.
The idea is about "sensible defaults" and the overarching theme of "it just works": if the default distributed cache and backplane are Redis, let's just make sure that the defualt experience is better aligned with that (and also, when bad things happen in production, automatically recovering from it with a slightly longer delay is, pragmatically, really not a big deal).

🧽 Some code cleanup

Thanks to @SimonCropp the code has been cleaned up a little bit here, updated to the latest C# features there, plus some other minor tweaks. Thanks Simon!

πŸš€ Performance

In this release I've been able to squeeze in some minor but nice memory/cpu optimizations.

βœ… Better tests

I added some more tests to have a higher code coverage.

πŸ“• Docs

Updated some docs with the latest new things.

FusionCache - v1.0.0

Published by jodydonetti 8 months ago

FusionCache is now v1.0 πŸ₯³

Yes, it finally happened.

Let's see what this release includes.

πŸš€ Performance, performance everywhere

FusionCache always tried to be as optimized as possible, but sometimes useful new features took some precedence over micro-optimizing this or that.

Now that all the major features (and then some) are there, it was time to do a deep dive and optimize a cpu cycle here, remove an allocation there and tweak some hot path to achieve the best use of resources.

So here's a non-comprehensive list of nice performance improvements in this release:

  • zero allocations/minimal cpu usage in Get happy path
  • reduced allocations/cpu usage in Set happy path
  • less allocations/cpu usage when not using distributed components
  • less allocations/cpu usage (via closures) when using events
  • zero allocations at all when not using logging (no support structures init for operationId generation)
  • reduced overhead in some async code paths

Oh, and thanks to community member @neon-sunset for the issue highlighting some shortcomings, that now have been solved!

See here for the issue.

πŸ¦… Better Eager Refresh (docs)

When executing an Eager Refresh, the initial check for an updated cache entry on the distributed cache is now totally non-blocking, for even better performance.

πŸ†• Added IgnoreIncomingBackplaneNotifications option (docs)

FusionCache always allowed to optionally skip sending backplane notifications granularly, for each operation (or globally thanks to DefaultEntryOptions): it was not possible though to ignore receiving them.

Now we may be thinking "why would I want to use a backplane, but not receive its notifications?" and the answer to that can be found in the feature request made by community member @celluj34 .

See here for the issue.

⚠️ Better nullability annotations for generic types

This is linked to the evolution of nullable reference types, nullables with generics and the related static analysis with each new version of c# and its compiler.

Along the years I tried to adjust the annotations to better handle generic types + nullables with each new version, because what the compiler allowed me to do and was able to infer changed at every release (the first version had problems with generics without where T : class/struct constraints, for example).

I've now updated them to reflect the latest behaviour, so that it's now more strict in the generic signatures, mostly for GetOrSet<T> and GetOrSetAsync<T>: in previous versions the return type was always nullable, so when calling GetOrSet<Person> we would have a return value of Person? (nullable) even if the call was not GetOrSet<Person?>.

Now this is better.

Thanks for community member @angularsen for highlighting this.

See here for the issue.

⚠️ Changed FusionCacheEntryOptions.Size to be nullable (docs)

The type of the Size option in the FusionCacheEntryOptions type has been historically long (default: 1): the underlying Size option in the MemoryCacheEntryOption type is instead long? (default: null).

So, to better align them, now the types and default values are the same.

πŸͺž Reflection no more

Not technically a problem per se, but with the release of the new and improved auto-recovery in v0.24.0, I had to add a little bit of reflection usage to support complex scenario of recovering some of the transient distributed errors.

Now, the small amount of code that was using reflection is gone, and this in turn means:

  • overall better performance
  • be better positioned for, eventually, playing with AOT (where reflection is basically a no-go)

See here for the issue.

πŸ”­ Add eviction reason to Open Telemetry metrics

With v0.26.0 native support for Open Telemetry has been added to FusionCache.

Now community members @JoeShook noticed that the eviction reason was missing from the Eviction counter, which could be in fact useful.

Now it has been added, thanks Joe!

See here for the PR.

πŸ”­ Removed cache instance id from Open Telemetry metrics

Community member @rafalzabrowarny noticed that FusionCache was adding a tag to the metrics, specifically one with the cache instance id: now, since it's a random value generated for every FusionCache instance, it will have a high cardinality and that is usually problematic with APM platforms and tools.

Now it's gone, thanks RafaΕ‚!

See here for the issue.

πŸ‘·β€β™‚οΈ Better detection of incoherent CacheNames options

With the introduction of the builder in v0.20 FusionCache got a nice way to configure the various options and components, in a very flexible way.

In one particular scenario though, it was possible to specify something incoherent: a single instance with multiple CacheNames, specified in different ways by using both the high level AddFusionCache("MyCache") and the WithOptions(...) methods.

A couple of examples:

services.AddFusionCache("foo")
  .WithOptions(options => {
    options.CacheName = "bar";
  });

or, more subtly:

services.AddFusionCache()
  .WithOptions(options => {
    options.CacheName = "bar";
  });

Now FusionCache correctly detects this scenario and throws an exception as soon as possible, helping the developer by showing the golden path to follow and how to do to solve it.

Thanks @albx for spotting this!

See here for the issue.

πŸ‘·β€β™‚οΈ Better builder auto-setup

Again with the builder, when using the TryWithAutoSetup() method in the builder it now also try to check for registered memory lockers by calling TryWithMemoryLocker(), automatically.

βœ… Better tests

I added some more tests to have a higher code coverage, and made the snapshot tests better.

πŸ“œ Better logs

More detailed log messages in some areas where they could've been better (mostly related to the backplane).

πŸ“• Docs

Updated some docs with the latest new things.

FusionCache - v1.0.0-preview2

Published by jodydonetti 8 months ago

[!IMPORTANT]
Yep, it's almost v1.0 time!

Please try this preview2 release and let me know if you find any issue, so the v1.0 can be as good as possible: from now until v1.0 is out I will no longer consider requests for new features.

Thanks πŸ™

πŸš€ Performance, performance everywhere

FusionCache always tried to be as optimized as possible, but sometimes useful new features took some precedence over micro-optimizing this or that.

Now that all the major features (and then some) are there, it was time to do a deep dive and optimize a cpu cycle here, remove an allocation there and tweak some hot path to achieve the best use of resources.

So here's a non-comprehensive list of nice performance improvements in this release:

  • zero allocations/minimal cpu usage in Get happy path
  • minimal allocations/cpu usage in Set happy path
  • less allocations/cpu usage when not using distributed components
  • less allocations/cpu usage (via closures) when using events
  • zero allocations at all when not using logging (no support structures init for operationId generation)

Oh, and thanks to community member @neon-sunset for the issue highlighting some shortcomings, that now have been solved!

See here for the issue.

πŸ¦… Better Eager Refresh (docs)

When executing an Eager Refresh, the initial check for an updated cache entry on the distributed cache is now totally non-blocking, for even better performance.

πŸ†• Added IgnoreIncomingBackplaneNotifications option (docs)

FusionCache always allowed to optionally skip sending backplane notifications granularly, for each operation (or globally thanks to DefaultEntryOptions): it was not possible though to ignore receiving them.

Now we may be thinking "why would I want to use a backplane, but not receive its notifications?" and the answer to that can be found in the feature request made by community member @celluj34 .

See here for the issue.

⚠️ Better nullability annotations for generic types

This is linked to the evolution of nullable reference types, nullables with generics and the related static analysis with each new version of c# and its compiler.

Along the years I tried to adjust the annotations to better handle generic types + nullables with each new version, because what the compiler allowed me to do and was able to infer changed at every release (the first version had problems with generics without where T : class/struct constraints, for example).

I've now updated them to reflect the latest behaviour, so that it's now more strict in the generic signatures, mostly for GetOrSet<T> and GetOrSetAsync<T>: in previous versions the return type was always nullable, so when calling GetOrSet<Person> we would have a return value of Person? (nullable) even if the call was not GetOrSet<Person?>.

Now this is better.

Thanks for community member @angularsen for highlighting this.

See here for the issue.

⚠️ Changed FusionCacheEntryOptions.Size to be nullable (docs)

The type of the Size option in the FusionCacheEntryOptions type has been historically long (default: 1): the underlying Size option in the MemoryCacheEntryOption type is instead long? (default: null).

So, to better align them, now the types and default values are the same.

βœ… Better tests

I added some more tests to have a higher code coverage, and made the snapshot tests better.

(Follows recap from preview1)

⚑ Reflection no more

Not technically a problem per se, but with the release of the new and improved auto-recovery in v0.24.0, I had to add a little bit of reflection usage to support complex scenario of recovering some of the transient distributed errors.

Now, the small amount of code that was using reflection is gone, and this in turn means:

  • overall better performance
  • be better positioned for, eventually, playing with AOT (where reflection is basically a no-go)

See here for the issue.

πŸ”­ Add eviction reason to Open Telemetry metrics

With v0.26.0 native support for Open Telemetry has been added to FusionCache.

Now community members @JoeShook noticed that the eviction reason was missing from the Eviction counter, which could be in fact useful.

Now it has been added, thanks Joe!

See here for the PR.

πŸ”­ Removed cache instance id from Open Telemetry metrics

Community member @rafalzabrowarny noticed that FusionCache was adding a tag to the metrics, specifically one with the cache instance id: now, since it's a random value generated for every FusionCache instance, it will have a high cardinality and that is usually problematic with APM platforms and tools.

Now it's gone, thanks RafaΕ‚!

See here for the issue.

πŸ‘·β€β™‚οΈ Better detection of incoherent CacheNames options

With the introduction of the builder in v0.20 FusionCache got a nice way to configure the various options and components, in a very flexible way.

In one particular scenario though, it was possible to specify something incoherent: a single instance with multiple CacheNames, specified in different ways by using both the high level AddFusionCache("MyCache") and the WithOptions(...) methods.

A couple of examples:

services.AddFusionCache("foo")
  .WithOptions(options => {
    options.CacheName = "bar";
  });

or, more subtly:

services.AddFusionCache()
  .WithOptions(options => {
    options.CacheName = "bar";
  });

Now FusionCache correctly detects this scenario and throws an exception as soon as possible, helping the developer by showing the golden path to follow and how to do to solve it.

Thanks @albx for spotting this!

See here for the issue.

πŸ‘·β€β™‚οΈ Better builder auto-setup

Again with the builder, when using the TryWithAutoSetup() method in the builder it now also try to check for registered memory lockers by calling TryWithMemoryLocker(), automatically.

πŸ“œ Better logs

More detailed log messages in some areas where they could've been better (mostly related to the backplane).

πŸ“• Docs

Updated some docs with the latest new things.

FusionCache - v1.0.0-preview1

Published by jodydonetti 8 months ago

πŸš€ Yes, it's (almost) v1.0 time !

Please try this v1.0.0-preview1 release and let me know if you find any issue, so the v1.0 can be as good as possible.

From now until v1.0 is out I will no longer consider requests for new features.

Thanks πŸ™

⚑ Reflection no more

Not technically a problem per se, but with the release of the new and improved auto-recovery in v0.24.0, I had to add a little bit of reflection usage to support complex scenario of recovering some of the transient distributed errors.

Now, the small amount of code that was using reflection is gone, and this in turn means:

  • overall better performance
  • be better positioned for, eventually, playing with AOT (where reflection is basically a no-go)

See here for the issue.

πŸ”­ Add eviction reason to Open Telemetry metrics

With v0.26.0 native support for Open Telemetry has been added to FusionCache.

Now community members @JoeShook noticed that the eviction reason was missing from the Eviction counter, which could be in fact useful.

Now it has been added, thanks Joe!

See here for the PR.

πŸ”­ Removed cache instance id from Open Telemetry metrics

Community member @rafalzabrowarny noticed that FusionCache was adding a tag to the metrics, specifically one with the cache instance id: now, since it's a random value generated for every FusionCache instance, it will have a high cardinality and that is usually problematic with APM platforms and tools.

Now it's gone, thanks RafaΕ‚!

See here for the issue.

πŸ‘·β€β™‚οΈ Better detection of incoherent CacheNames options

With the introduction of the builder in v0.20 FusionCache got a nice way to configure the various options and components, in a very flexible way.

In one particular scenario though, it was possible to specify something incoherent: a single instance with multiple CacheNames, specified in different ways by using both the high level AddFusionCache("MyCache") and the WithOptions(...) methods.

A couple of examples:

services.AddFusionCache("foo")
  .WithOptions(options => {
    options.CacheName = "bar";
  });

or, more subtly:

services.AddFusionCache()
  .WithOptions(options => {
    options.CacheName = "bar";
  });

Now FusionCache correctly detects this scenario and throws an exception as soon as possible, helping the developer by showing the golden path to follow and how to do to solve it.

Thanks @albx for spotting this!

See here for the issue.

πŸ‘·β€β™‚οΈ Better builder auto-setup

Again with the builder, when using the TryWithAutoSetup() method in the builder it now also try to check for registered memory lockers by calling TryWithMemoryLocker(), automatically.

πŸ“• Docs

Updated some docs with the latest new things.

FusionCache - v0.26.0

Published by jodydonetti 8 months ago

[!IMPORTANT]
This version supersede v0.25.0 (now deprecated) because of a small breaking change: although it technically is a breaking change, it is about the new IFusionCacheMemoryLocker introduced just a week ago, so unless you are already implementing your own custom memory locker it will not be a problem.

Apart from this, v0.26.0 is basically the same as v0.25.0, so please update to v0.26.0 as soon as possible so you'll be ready for the big v1.0 that will be released very soon.

πŸ”­ OpenTelemetry support (docs)

FusionCache now natively supports full observability, thanks to the great native support of OpenTelemetry in .NET via the core Activity and Meter classes without any extra dependency to produce them.

In general, the 3 main signals in the observability world are traces, metrics and logs.

FusionCache always supported logging, and even in a pretty advanced and customizable way.

Now the two remaining pieces are finally here: traces and metrics.

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 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 is then possible to simply plug one of the existing OpenTelemetry-compatible collectors for systems like Jaeger, Prometheus or Honeycomb and voilΓ , there we have full observability of our FusionCache instances.

Here's an example of how to set it up without dependency injection:

// SETUP TRACES
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
  .AddFusionCacheInstrumentation()
  .AddConsoleExporter()
  .Build();

// SETUP METRICS
using var meterProvider = Sdk.CreateMeterProviderBuilder()
  .AddFusionCacheInstrumentation()
  .AddConsoleExporter()
  .Build();

or, via dependency injection:

services.AddOpenTelemetry()
  // SETUP TRACES
  .WithTracing(tracing => tracing
    .AddFusionCacheInstrumentation()
    .AddConsoleExporter()
  )
  // SETUP METRICS
  .WithMetrics(metrics => metrics
    .AddFusionCacheInstrumentation()
    .AddConsoleExporter()
  );

The end result of all of this, when viewed for example in Honeycomb, is something like this:

Example of having full observability

Quite nice, uh 😏 ?

Oh, and I can't thank @martinjt enough for the support!

See here for the issue.

πŸ”’ Extensible Memory Locker

FusionCache always protected us from the Cache Stampede problem, but the internals of the memory locking mechanism have always been private.

Now no more, and thanks to the new IFusionCacheMemoryLocker interface the community may come up with different designs and implementations.

The builder has also been extended with support for configuring different memory lockers thanks to new methods like WithRegisteredMemoryLocker, WithMemoryLocker(locker), WithStandardMemoryLocker() and more.

See here for the issue.

NOTE: this version contains a small breaking change regarding the params in the methods of IFusionCacheMemoryLocker (swapped the position of key and operationId, to be 100% consistent with the rest of the codebase. As explained in the note at the top it is something that realistically will not change anything, but it's good to know.

🐞 Fixed a minor SkipDistributedCacheReadWhenStale bug

Thanks to community member @angularsen I fixed a small bug related to SkipDistributedCacheReadWhenStale not working as expected during get-only operations (TryGet, GetOrDefault).

See here for the issue.

↩️ Better items cleanup for auto-recovery

Minor, but still: FusionCache now better cleans up processed auto-recovery items.

πŸ“• Docs

Updated some docs and added some new ones (Observability, etc).

FusionCache - v0.25.0

Published by jodydonetti 9 months ago

πŸ”­ OpenTelemetry support (docs)

FusionCache now natively supports full observability, thanks to the great native support of OpenTelemetry in .NET via the core Activity and Meter classes without any extra dependency to produce them.

In general, the 3 main signals in the observability world are traces, metrics and logs.

FusionCache always supported logging, and even in a pretty advanced and customizable way.

Now the two remaining pieces are finally here: traces and metrics.

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 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 is then possible to simply plug one of the existing OpenTelemetry-compatible collectors for systems like Jaeger, Prometheus or Honeycomb and voilΓ , there we have full observability of our FusionCache instances.

Here's an example of how to set it up without dependency injection:

// SETUP TRACES
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
  .AddFusionCacheInstrumentation()
  .AddConsoleExporter()
  .Build();

// SETUP METRICS
using var meterProvider = Sdk.CreateMeterProviderBuilder()
  .AddFusionCacheInstrumentation()
  .AddConsoleExporter()
  .Build();

or, via dependency injection:

services.AddOpenTelemetry()
  // SETUP TRACES
  .WithTracing(tracing => tracing
    .AddFusionCacheInstrumentation()
    .AddConsoleExporter()
  )
  // SETUP METRICS
  .WithMetrics(metrics => metrics
    .AddFusionCacheInstrumentation()
    .AddConsoleExporter()
  );

The end result of all of this, when viewed for example in Honeycomb, is something like this:

Example of having full observability

Quite nice, uh 😏 ?

Oh, and I can't thank @martinjt enough for the support!

See here for the issue.

πŸ”’ Extensible Memory Locker

FusionCache always protected us from the Cache Stampede problem, but the internals of the memory locking mechanism have always been private.

Now no more, and thanks to the new IFusionCacheMemoryLocker interface the community may come up with different designs and implementations.

The builder has also been extended with support for configuring different memory lockers thanks to new methods like WithRegisteredMemoryLocker, WithMemoryLocker(locker), WithStandardMemoryLocker() and more.

See here for the issue.

🐞 Fixed a minor SkipDistributedCacheReadWhenStale bug

Thanks to community member @angularsen I fixed a small bug related to SkipDistributedCacheReadWhenStale not working as expected during get-only operations (TryGet, GetOrDefault).

See here for the issue.

↩️ Better items cleanup for auto-recovery

Minor, but still: FusionCache now better cleans up processed auto-recovery items.

πŸ“• Docs

Updated some docs and added some new ones (Observability, etc).

FusionCache - v0.25.0-preview1

Published by jodydonetti 9 months ago

πŸ”­ OpenTelemetry support

FusionCache now natively support full observability, thanks to the amazing native support of OpenTelemetry in .NET via the core Activity and Meter classes.

The 3 main signals in the observability world are traces, metrics and logs.

FusionCache always supported logging, and even in a pretty advanced and customizable way.

Now the two remaining pieces are finally here: traces and metrics.

It is possible to opt-in to generate traces and meters 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 specific for OpenTelemetry that adds native support to instrument our applications, with FusionCache specific options ready to use.

It is then possible to simply plug on of the existing OpenTelemetry-compatible collectors like the ones for Jaeger, Prometheus, Honeycomb, etc and voilΓ , there we have full observability of our FusionCache

Here's an example of how to set it up:

// 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:

services.AddOpenTelemetry()
  // SETUP TRACES
  .WithTracing(tracing => tracing
    .AddFusionCacheInstrumentation()
    .AddConsoleExporter()
  )
  // SETUP METRICS
  .WithMetrics(metrics => metrics
    .AddFusionCacheInstrumentation()
    .AddConsoleExporter()
  );

The end result of all of this, when viewed via Honeycomb.io in this case, issomething like this:

image

Quite nice, uh 😏 ?

🐞 Fixed a minor SkipDistributedCacheReadWhenStale bug

Thanks to community member @angularsen I fixed a small bug related to SkipDistributedCacheReadWhenStale not working as expected during get-only operations (TryGet, GetOrDefault).

See here for the issue.

↩️ Better items cleanup for auto-recovery

Minor, but still: FusionCache now better cleans up processed auto-recovery items.

FusionCache - v0.24.0

Published by jodydonetti 11 months ago

⚠️ Update Notes

If you are updating from a previous version, please read here.

↩️ Brand new Auto-Recovery (docs)

The old Backplane Auto-Recovery is now, simply, Auto-Recovery!

It shines above the other features as a way to automatically handle and resolve transient errors with timed retries and other techniques, to do the best it can to avoid leaving your cache in a dire situation, no matter what.

Lately I proceeded with an almost complete rewrite of the internals of the entire distributed part, meaning both the distributed cache and the backplane, and in particular the auto-recovery feature to make it all even better..

This is the culmination of many months of research, experimentation, development, testing, benchmarking and in general a lot of work.

The new Auto-Recovery now supports both the distributed cache and the backplane, either individually or together.

Now it is also active, meaning it handles retries automatically instead of waiting for an external signal to "wake up".

Some of these improvements include:

  • continuous background processing (more robust)
  • queue automatic cleanup
  • active detection and processing of re-connection
  • the distributed cache and the backplane now work together even more (and better) than before, with granular control of re-sync operations needed
  • better handling of connection/subscription errors
  • better logging

Every edge case I thought of and all the others reported to me by the community are handled, all automatically.

You can read some of them at the dedicated docs page.

Please note that all the existing options named BackplaneAutoRecoveryXyz are now named simply AutoRecoveryXyz: this is not a breaking change though, since the old ones are still present but marked with the useful [Obsolete] attribute. Simply update to the latest version, recompile, look for some warnings and do the appropriate changes, if any.

πŸ–₯️ Simulator (docs)

In general it is quite complicated to understand what is going on in a distributed system.

When using FusionCache with the distributed cache and the backplane, a lot of stuff is going on at any given time: add to that intermittent transient errors and, even if we can be confident auto-recovery will handle it all automatically, clearly seeing the whole picture can become a daunting task.

It would be very useful to have something that let us clearly see it all in action, something that would let us configure different components, tweak some options, enable this, disable that and let us simulate a realistic workload to see the results.

Luckily there is, and is called Simulator.

FusionCache Simulator

πŸ“’ Better Backplane (docs)

This version introduces a better way to handle incoming backplane messages: long story short, it will now take less time for a change to be propagated to all the other nodes, and to be reflected into their local memory cache (only if necessary, to save resources).

πŸ“ž Eviction event now pass the cache value (docs)

Thanks to a request by community member @giulianob the Eviction event in the memory cache has been made more powerful, and is now passing the cache value being evicted.

This can be useful for example to dispose a value being evicted from the cache, or more.

See here for more.

πŸ’₯ Custom Exceptions

FusionCache now has 3 new custom exception types that are used to generalize errors of 3 main types: distributed cache errors, serialization errors and backplane errors.

In this way it will be easier to catch exception "groups" in our code instead of special-case it to work with Redis, CosmosDB, etc.

In case the old behaviour is needed for some reason, we can simply set the ReThrowOriginalExceptions options to true.

πŸ•‘ Better timestamping

Timestamps are now more precise, and are used more inside the various heuristics used to handle conflicts and more.

βš™οΈ Added ReThrowBackplaneExceptions option

It is now possible to more granularly specify if we want to re-throw exceptions happened in the backplane code.

To allow this though we need to disable background execution of the backplane (via the AllowBackgroundBackplaneOperations option), otherwise it would be physically impossible to re-throw them.

πŸ“œ Better Logging

Logging messages are now even better, with more useful information.

One that shines in particular and that is fundamental in debugging distributed issues is the inclusion of the InstanceId on top of the CacheName: with this we can better identify who sent messages to who without any doubt.

πŸ”΄ Add support for ConnectionMultiplexerFactory in RedisBackplane

It is now possible to directly pass an instance of IConnectionMultiplexer to waste less resources, thanks to a PR by the community user @JeffreyM .

Thanks Jeffrey!

🐞 NullFusionCache now correctly handles CreateEntryOptions

Thanks to a bug report by community member @kelko , a small issue has been fixed with the standard NullFusionCache implementation.

See here for more.

🐡 New base AbstractChaosComponent

Historically all the chaos-related components (eg: ChaosBackplane, ChaosDistributedCache, etc) all re-implement the same core properties and methods to control throws probability, random delays and so on.

Now that these components exist for some time and they are working well, they needed a little refactoring.

So a new AbstractChaosComponent has been created acting as a base class for all chaos-related components, so that they now all inherit from it to result in less code and better evolvability.

See here for more.

🐡 Added support for cancellation in chaos components

Chaos components now supports cancellation via the standard use of CancellationTokens.

βœ… Better Tests

The tests have been reorganized, better splitted into different files, they now have a unique abstract base class with some common useful stuff and most of them now have logging enabled, just in case.

πŸ“• Docs

Finally I've updated a lot of the existing docs and added some new ones, like for the brand new AutoRecovery feature, for the Simulator and more.

FusionCache - v0.24.0-preview1

Published by jodydonetti about 1 year ago

↩️ Better Auto-Recovery (docs)

The Backplane is a powerful component in FusionCache and is already a pretty good feature that works really well.

On top of the standard backplane features, auto-recovery shines above the others as a way to automatically handle transient errors with timed retries and other techniques, to do the best it can to avoid leaving your cache in a dire situation, no matter what.

In the last couple of months I proceeded with an almost complete rewrite of the internals of the backplane and in particular the auto-recovery feature, to make it work even better.

Some of these improvements include:

  • auto-recovery continuous background processing (more robust)
  • the distributed cache and the backplane now work together even more (and better) than before, with granular control of re-sync operations needed
  • auto-recovery queue automatic cleanup
  • active detection and processing of re-connection
  • better handling of connection/subscription errors
  • unified backplane auto-recovery delay via BackplaneAutoRecoveryDelay option (BackplaneAutoRecoveryReconnectDelay is now marked as obsolete)
  • added ReThrowBackplaneExceptions entry option, to have even more control of backplane errors
  • marked EnableDistributedExpireOnBackplaneAutoRecovery as obsolete, since now everything is fully automatic
  • better log messages

Now every edge case I thought of and all the others reported to me by the community are handled, all automatically.

Some examples:

  • when setting a value in the cache, the distributed cache was available but the backplane was (temporarily) not
  • when setting a value in the cache, the backplane was available but the distributed cache was (temporarily) not
  • when setting a value in the cache, both the distributed cache and the backplane were not available
  • in all the above cases, when both the distributed cache and/or the backplane turned out to be available again, but the other were not
  • a mix of all of these situations
  • and more, much more...

And what is needed to handle all of this? Nothing: as always, FusionCache tries to be helpful without any extra care on your side.

πŸ“ž Eviction event now pass the cache value (docs)

Thanks to a request by community member @giulianob the Eviction event in the memory cache has been made more powerful, and is now passing the cache value being evicted.

This can be useful for example to dispose a value being evicted from the cache, or more.

See here for more.

🐞 NullFusionCache now correctly handles CreateEntryOptions

Thaks to a bug report by community member @kelko , a small issue has been fixed with the standard NullFusionCache implementation.

See here for more.

🐡 new AbstractChaosComponent

Historically all the chaos-related components (eg: ChaosBackplane, ChaosDistributedCache, etc) all re-implement the same core properties and methods to control throws probability, random delays and so on.

Now that these components exist for some time and they are working well, they needed a little refactoring.

So a new AbstractChaosComponent has been created acting as a base class for all chaos-related components, so that they now all inherit from it to result in less code and better evolvability.

See here for more.

FusionCache - v0.23.0

Published by jodydonetti about 1 year ago

πŸ“’ Better Backplane (docs)

The Backplane is a powerful component in FusionCache and is already a pretty good feature that works really well.

Now some more scenarios and edge cases are handled even better:

  • active detection and processing of re-connection
  • better handling of connection/subscription errors
  • changed the defualt BackplaneAutoRecoveryMaxItems to unlimited
  • added support for fake connection strings in MemoryBackplane for better concurrency
  • more control on circuit breaker manual closing (eg: when receiving a message)

See here for more.

↩️ Better Auto-Recovery (docs)

The auto-recovery feature of the Backplane is already working pretty well, but sometimes there are situations where it could do a little more, a little better.

Now these scenarios are covered way better:

  • better backpressure handling, with automatic re-sync of the distributed cache state (EnableDistributedExpireOnBackplaneAutoRecovery option)
  • automatically delayed processing on reconnect (BackplaneAutoRecoveryReconnectDelay option)
  • better background queue processing
  • remove duplicate items on publish

See here for more.

πŸ“œ Better log messages (docs)

Log messages have been fine tuned even more, with greater details where needed, some extra log messages removed when they were not needed, and finally better wording in certain circumstances, which should help clarify the timing of some events eve more.

See here for more.

😢 More Null Object Pattern support

In FusionCache v0.22.0 the NullFusionCache has been introduced, which is an implementation of the Null Object Pattern.

Now these implementations are also available:

  • NullSerializer (impl of IFusionCacheSerializer)
  • NullDistributedCache (impl of IDistributedCache)
  • NullBackplane (impl of IFusionCacheBackplane)
  • NullPlugin (impl of IFusionCachePlugin)

There's also a new, specific namespace (ZiggyCreatures.Caching.Fusion.NullObjects) for these classes, to avoid polluting the root namespace.

See here for more.

[!WARNING]
The existing NullFusionCache class has been moved in the new namespace, and this is a (small) breaking change.

🐡 Added ChaosPlugin

FusionCache already has a set of chaos-engineering related classes like ChaosBackplane, ChaosDistributedCache and more, to ease testing how the system reacts to malfunctioning components, but in a controlled way.

A ChaosPlugin was missing: now not anymore.

See here for more.

FusionCache - v0.22.0

Published by jodydonetti over 1 year ago

πŸ—“οΈ Added Expire() method (docs)

FusionCache now supports expiring an entry explicitly.
It can behave in 2 different ways, based on the fact that fail-safe is enabled or not:

  • if fail-safe is enabled: the entry will be marked as logically expired, but will still be available as a fallback value in case of future problems
  • if fail-safe is disabled: the entry will be effectively removed

In this way the intent is clear, while still having space for some additional optimizations and support for problematic scenarios.

Here's an example:

await cache.ExpireAsync("foo");

Thanks to the community member @th3nu11 for getting started with the idea.

See here for the original issue.

πŸ”€ Added SkipMemoryCache option (docs)

Although very rare, sometimes it may be useful to skip the memory cache: now it is easily possible thanks to the new SkipMemoryCache option.

See here for the original issue.

πŸ“œ Better log messages (docs)

FusionCache now includes the CacheName in each log message, which is now important since the introduction of Named Caches in v0.20.0.

On top of that, when logging about backplane messages, it now also include the CacheInstanceId as an extra help when going detective mode.

See here for the original issue.

😢 Added NullFusionCache

Thanks to some input from community member @SorenZ , FusionCache now provides a native implementation of the Null Object Pattern for when it's needed.

See here for the original issue.

⏲ Better Timestamping

FusionCache now keeps better track of the timestamp of when a cached value has been originally created.

Long story short: this will better track times, and allow for more sophisticated decisions regarding which cache entry version to use in some circumstances.

See here for the original issue.

β›” Handle Zero

Thanks to community member @CM2Walki 's request, durations equals to (or less than) TimeSpan.Zero are now natively supported, with optimized code paths.

It's not as simple as doing nothing, and it's possible to read more in the original issue's description.

Anyway, long story short: it just works 😊

See here for the original issue.

😴 Lazy instantiations of Named Caches (docs)

When working with Named Caches FusionCache internally works with the DI container to have all the registered FusionCache instances to be provided via the new IFusionCacheProvider interface.

The way it worked until today is that it simply instantiated all the registered ones, immediately.

From now on a lazy handling has been introduced, and cpu and memory will be saved, automatically.

See here for the original issue.

🧽 Better cleanup

While disposing a FusionCache instance, distributed caches and backplanes are now automatically removed and/or unsubscribed from.
Not that it was a huge problem, but still: better cleanup is better, amirite?

See here for the original issue.

πŸ“’ Better backplane notifications (docs)

Backplane notifications handling is now even better: the order of some internal operations is more streamlined, some hot paths have been optimized and less memory is being allocated.

πŸ”’ More resilient lock release strategies (docs)

There were a couple of very rare edge cases where, at least theoretically, it could've been possible to not release one of the internal locking primitives used to protect us from Cache Stampede.

Even if they never actually happened, now they cannot happen even theoretically.

πŸ§™β€β™‚οΈ Better Adaptive Caching (docs)

More cases and more options are now supported while using adaptive caching: nothing specific to do, they now just work.

To see which options are supported with Adaptive Caching, just take a look at the Options docs and look for the πŸ§™β€β™‚οΈ icon.

πŸ¦… Better Eager Refresh (docs)

When using Eager Refresh the distributed cache will now also be checked, before executing the factory: this is useful in case some fresh version of the data is arrived there in the meantime.

Again, nothing extra to do here.

βœ… Automated snapshot testing for binary payload backward compatibility

There are now tests that automatically verify that old binary payloads, generated by old versions of the available serializers, can be deserialized correctly by the current version of the serializers.

This is important to guarantee that when updating a live system that uses a distributed cache that is already filled with some data, it will be possible to do so safely without having to do any extra migration activity or data loss.

See here for the original issue.

⚑ Performance boost

Some general performance optimizations regarding cpu usage, memory allocations and more.

⚠ More [Obsolete] usage

Some members that have been marked with the [Obsolete] attribute from some time, are now also marked with the error flag: there will still be helpful error messages on how to update existing code, but now that that code update must be completed.

This is important for the evolution of FusionCache, and for the upcoming v1.0.

FusionCache - v0.21.0

Published by jodydonetti over 1 year ago

πŸ”‚ Conditional Refresh (docs)

FusionCache now fully supports conditional refresh, which is conceptually similar to conditional requests in HTTP.

Basically it is a way to cache and re-use either an ETag, a LastModified date or both, so that it is possible to save resources by not fully getting a remote resource (think: a remote http service response) in case it was not changed since the last time we got it.

Thanks to the community member @aKzenT for the invaluable help with the design discussion.

See here for the original issue.

πŸ¦… Eager Refresh (docs)

It is now possible to tell FusionCache to eagerly start refreshing a value in the cache even before it's expired, all automatically and in the background (without blocking).

For example we can tell FusionCache to cache something for 10 min but eagerly start refreshing it after 90% of that (9 min) in the background, as soon as a GetOrSet<T>(...) call is made after the specified threshold: in this way fresh data will be ready earlier, and without having to wait for the refresh to complete when it would've expired.

Both eager refresh and the usual normal refresh + factory timeouts can be combined, without any problems.

See here for the original issue.

πŸ“ž Added EagerRefresh event

A new event has been added, for when eager refresh actually occurs.

πŸ“œ Added WithoutLogger() builder method (docs)

With the introduction of the Builder in the v0.20.0 release, something was missing: a way to tell FusionCache to not use a logger, at all: now this is fixed.

Thanks to community member @sherman89 for pointing that out.

See here for the original issue.

⚠ Breaking Change

The type for the factory execution context has changed, from FusionCacheFactoryExecutionContext to FusionCacheFactoryExecutionContext<TValue>: this has been done to support conditional refresh and have access to the stale value already cached, which requires knowing the type upfront.

Most existing code should be fine: the only noticeable difference is that in some situations the C# compiler is, for some reasons, not able to do type inference correctly.

Because of this, in some instances a code like this:

var product = cache.GetOrSet(...);

must be updated to explicitly add the TValue type, like this:

var product = cache.GetOrSet<Product>(...);
FusionCache - v0.21.0-preview2

Published by jodydonetti over 1 year ago

⚠ PRE-RELEASE

This is a pre-release of FusionCache, so please be aware that although it contains some shiny new stuff (see below), it is also subject to change before the final release.

Having said that, you should also know that this version is already very polished, the api design surface area well modeled, performance-tuned, with full xml-docs and it has all the bells and whistles of a normal release.

πŸ”‚ Conditional Refresh

FusionCache now fully supports conditional refresh, which is conceptually similar to conditional requests in HTTP.

Basically it is a way to cache and re-use either an ETag, a LastModified date or both, so that it is possible to save resources by not fully getting a remote resource (think: a remote http service response) in case it was not changed since the last time we got it.

Thanks to the community member @aKzenT for the invaluable help with the design discussion.

See here for the issue.

πŸ¦… Eager Refresh

It is now possible to tell FusionCache to eagerly start refreshing a value in the cache even before it's expired, all automatically and in the background (without blocking).

For example we can tell FusionCache to cache something for 10 min but eagerly start refreshing it after 90% of that (9 min) in the background, as soon as a GetOrSet(...) call is made after the specified threshold: in this way the fresh data will be ready earlier, and without having to wait for the refresh to complete when it would've expired.

Both eager refresh and the usual normal refresh + factory timeouts can be combined, without any problems.

See here for the issue.

πŸ“ž Added EagerRefresh event

A new event has been added, for when eager refresh actually occurs.

πŸ“œ Added WithoutLogger() builder method

With the introduction of the Builder in the v0.20.0 release, something was missing: a way to tell FusionCache to not use a logger, at all: now this is fixed.

Thanks to community member @sherman89 for pointing that out.

See here for the issue.

FusionCache - v0.21.0-preview1

Published by jodydonetti over 1 year ago

⚠ PRE-RELEASE

This is a pre-release of FusionCache, so please be aware that although it contains some shiny new stuff (see below), it is also subject to change before the final release.

Having said that, you should also know that this version is already very polished, the api design surface area well modeled, performance-tuned, with full xml-docs and it has all the bells and whistles of a normal release.

πŸ”‚ Conditional Refresh

FusionCache now fully supports conditional refresh, which is conceptually similar to conditional requests in HTTP.

Basically it is a way to cache and re-use either an ETag, a LastModified date or both, so that it is possible to save resources by not fully getting a remote resource (think: a remote http service response) in case it was not changed since the last time we got it.

Thanks to the community member @aKzenT for the invaluable help with the design discussion.

See here for the issue.

πŸ¦… Eager Refresh

It is now possible to tell FusionCache to eagerly start refreshing a value in the cache even before it is expired.

For example we can tell FusionCache to cache something for 10min but eagerly start refreshing it after 9min in the background, so that it will be ready earlier without waiting for the refresh to end.

Although it's basically the same as caching something directly for 9min + enabling fail-safe + setting a very low factory soft timeout like 10ms, some community members requested it because it felt more intuitive this way, while others felt the other way around: therefore the addition, to support both approaches and be more welcoming.

Also, both approaches can be combined without problems.

See here for the issue.

πŸ“œ Added WithoutLogger() builder method

With the introduction of the Builder in the v0.20.0 release, something was missing: a way to tell FusionCache to not use a logger, at all: now this is fixed.

Thanks to community member @sherman89 for pointing that out.

See here for the issue.

FusionCache - v0.20.0

Published by jodydonetti over 1 year ago

β„Ή Update Notes

If you are updating from a previous version, please read here.

πŸ‘·β€β™‚οΈ Added Builder support (docs)

There's now a really easy to use and flexible fluent API that supports the builder pattern.
This allows a nice modular setup of FusionCache when using dependency injection, with the ability to add components and configure them with a couple of keystrokes, like:

services.AddFusionCache()
  .WithSerializer(
    new FusionCacheSystemTextJsonSerializer()
  )
  .WithDistributedCache(
    new RedisCache(new RedisCacheOptions()
    {
      Configuration = "..."
    })
  )
  .WithBackplane(
    new RedisBackplane(new RedisBackplaneOptions()
    {
      Configuration = "..."
    })
  )
;

Thanks to the community member @aKzenT for the invaluable help with the design discussion.

See here for the original issue.

πŸ“› Added Named Caches support (docs)

FusionCache now natively support multiple named caches via dependency injection, thanks to the new IFusionCacheProvider interface/service.
This makes it easy to register and retrieve different caches, potentially with different underlying caching storage (but not necessarily) and different configurations.
This is even easier thanks to the aforementioned Builder Pattern support, like this:

// USERS CACHE
services.AddFusionCache("UsersCache")
  .WithDefaultEntryOptions(opt => opt
    .SetDuration(TimeSpan.FromSeconds(10))
  )
;

// PRODUCTS CACHE
services.AddFusionCache("ProductsCache")
  .WithDefaultEntryOptions(opt => opt
    .SetDuration(TimeSpan.FromSeconds(30))
    .SetFailSafe(true, TimeSpan.FromMinutes(10))
  )
;

Then you can depend on an IFusionCacheProvider service and easily ask to it for a certain cache with a GetCache("MyCache") call.
For example in an mvc controller you can go like this:

public class HomeController : Controller
{
  IFusionCache _usersCache;
  IFusionCache _productsCache;

  public HomeController(IFusionCacheProvider cacheProvider)
  {
    _productsCache = cacheProvider.GetCache("ProductsCache");
    _usersCache = cacheProvider.GetCache("UsersCache");
  }

  public IActionResult Product(int id)
  {
    _productsCache.GetOrDefault<Product>($"product:{id}");
    // ...
  }
}

And again, thanks to the community member @aKzenT for the invaluable help with the design discussion here, too.

See here for the original issue.

πŸ†• Added CacheKeyPrefix option (docs)

Since there's now native support for multiple named caches, I'm currently playing with the idea of adding support for a CacheKeyPrefix option, that would be added in front of any cache key you specify with a certain FusionCache instance. This can be helpful to avoid cache key collisions when working with multiple named caches all sharing the same underlying caching storage mechanism (eg: memory, Redis, MongoDB, etc).

β™Ύ Handle Infinity

There's now native support for infinite expirations, with specific optimizations in the code to avoid exceptions and for better perf.

See here for more.

πŸ“œ Custom Plugins Log Levels (docs)

Two new options have been added to control the level for plugins info/error log entries.

See here for more.

FusionCache - v0.20.0-preview2

Published by jodydonetti over 1 year ago

⚠ PRE-RELEASE

This is a pre-release of FusionCache, so please be aware that although it contains some shiny new stuff (see below), it is also subject to change before the final release.

Having said that, you should also know that this version is already very polished, the api design surface area well modeled, performance-tuned, with full xml-docs and it has all the bells and whistles of a normal release.

β„Ή NOTE: if you are updating from a previous version, please read here.

Ok, enough with the warnings: let's talk about the features πŸŽ‰

πŸ‘·β€β™‚οΈ Added Builder Pattern support

There's now a really easy to use and flexible fluent API that supports the builder pattern.
This allows a nice modular setup of FusionCache when using dependency injection, with the ability to add components and configure them with a couple of keystrokes, like:

services.AddFusionCache()
  .WithSerializer(
    new FusionCacheSystemTextJsonSerializer()
  )
  .WithDistributedCache(
    new RedisCache(new RedisCacheOptions()
    {
      Configuration = "..."
    })
  )
  .WithBackplane(
    new RedisBackplane(new RedisBackplaneOptions()
    {
      Configuration = "..."
    })
  )
;

NOTE: The api surface has changed a little bit from v0.20.0-preview1.

Thanks to the community member @aKzenT for the invaluable help with the design discussion.

See here for the issue.
See here for the docs.

πŸ“› Added Named Caches support

FusionCache now natively support multiple named caches via dependency injection, thanks to the new IFusionCacheProvider interface/service.
This makes it easy to register and retrieve different caches, potentially with different underlying caching storage (but not necessarily) and different configurations.
This is even easier thanks to the aforementioned Builder Pattern support, like this:

// USERS CACHE
services.AddFusionCache("UsersCache")
  .WithDefaultEntryOptions(opt => opt
    .SetDuration(TimeSpan.FromSeconds(10))
  )
;

// PRODUCTS CACHE
services.AddFusionCache("ProductsCache")
  .WithDefaultEntryOptions(opt => opt
    .SetDuration(TimeSpan.FromSeconds(30))
    .SetFailSafe(true, TimeSpan.FromMinutes(10))
  )
;

Then you can depend on an IFusionCacheProvider service and easily ask to it for a certain cache with a GetCache("MyCache") call.
For example in an mvc controller you can go like this:

public class HomeController : Controller
{
  IFusionCache _usersCache;
  IFusionCache _productsCache;

  public HomeController(IFusionCacheProvider cacheProvider)
  {
    _productsCache = cacheProvider.GetCache("ProductsCache");
    _usersCache = cacheProvider.GetCache("UsersCache");
  }

  public IActionResult Product(int id)
  {
    _productsCache.GetOrDefault<Product>($"product:{id}");
    // ...
  }
}

And again, thanks to the community member @aKzenT for the invaluable help with the design discussion here, too.

See here for the issue.
See here for the docs.

πŸ†• Added CacheKeyPrefix option

Since there's now native support for multiple named caches, I'm currently playing with the idea of adding support for a CacheKeyPrefix option, that would be added in front of any cache key you specify with a certain FusionCache instance. This can be helpful to avoid cache key collisions when working with multiple named caches all sharing the same underlying caching storage mechanism (eg: memory, Redis, MongoDB, etc).

β™Ύ Handle Infinity

There's now native support for infinite expirations, with specific optimizations in the code to avoid exceptions and for better perf.

See here for more.

πŸ“œ Custom Plugins Log Levels

Two new options have been added to control the level for plugins info/error log entries.

See here for more.

FusionCache - v0.20.0-preview1

Published by jodydonetti over 1 year ago

⚠ PRE-RELEASE

This is a pre-release of FusionCache, so please be aware that although it contains some shiny new stuff (see below), it is also subject to change before the final release.

Having said that, you should also know that this version is already very polished, the api design surface area well modeled, performance-tuned, with full xml-docs and it has all the bells and whistles of a normal release.

Ok, enough with the warnings: let's talk about the features πŸŽ‰

πŸ‘·β€β™‚οΈ Added Builder Pattern support

There's now a really easy to use and flexible fluent API that supports the builder pattern.
This allows a nice modular setup of FusionCache when using dependency injection, with the ability to add components and configure them with a couple of keystrokes, like:

services.AddFusionCache(b => b
  .WithSerializer(
    new FusionCacheSystemTextJsonSerializer()
  )
  .WithDistributedCache(
    new RedisCache(new RedisCacheOptions()
    {
      Configuration = "..."
    })
  )
  .WithBackplane(
    new RedisBackplane(new RedisBackplaneOptions()
    {
      Configuration = "..."
    })
  )
);

Thanks to the community member @aKzenT for the invaluable help with the design discussion.

See here for more.

πŸ“› Added Named Caches support

FusionCache now natively support multiple named caches via dependency injection, thanks to the new IFusionCacheProvider interface/service.
This makes it easy to register and retrieve different caches, potentially with different underlying caching storage (but not necessarily) and different configurations.
This is even easier thanks to the aforementioned Builder Pattern support, like this:

// USERS CACHE
services.AddFusionCache("UsersCache", b => b
  .WithDefaultEntryOptions(opt => opt
    .SetDuration(TimeSpan.FromSeconds(10))
  )
);

// PRODUCTS CACHE
services.AddFusionCache("ProductsCache", b => b
  .WithDefaultEntryOptions(opt => opt
    .SetDuration(TimeSpan.FromSeconds(30))
    .SetFailSafe(true, TimeSpan.FromMinutes(10))
  )
);

Then you can depend on an IFusionCacheProvider service and easily ask to it for a certain cache with a GetCache("MyCache") call.
For example in an mvc controller you can go like this:

public class HomeController : Controller
{
  IFusionCache _usersCache;
  IFusionCache _productsCache;

  public HomeController(IFusionCacheProvider cacheProvider)
  {
    _productsCache = cacheProvider.GetCache("ProductsCache");
    _usersCache = cacheProvider.GetCache("UsersCache");
  }

  public IActionResult Product(int id)
  {
    _productsCache.GetOrDefault<Product>($"product:{id}");
    // ...
  }
}

And again, thanks to the community member @aKzenT for the invaluable help with the design discussion here, too.

See here for more.

πŸ†• Added CacheKeyPrefix option

Since there's now native support for multiple named caches, I'm currently playing with the idea of adding support for a CacheKeyPrefix option, that would be added in front of any cache key you specify with a certain FusionCache instance. This can be helpful to avoid cache key collisions when working with multiple named caches all sharing the same underlying caching storage mechanism (eg: memory, Redis, MongoDB, etc).

This decision is not final, and I'm interested in getting the opinions from the community.

FusionCache - v0.19.0

Published by jodydonetti over 1 year ago

πŸ†• Added DistributedCacheFailSafeMaxDuration option (docs)

After a request by the community member @RMcD a new entry option has been added to be able to specify a fail-safe max duration specific for the distributed cache level.

See here for more.

⚠ Behaviour change with stale data

After talking with community member @fkuhnert , an old thought re-emerged: for readonly methods like TryGet[Async] and GetOrDefault[Async] no stale data throttling should occur. This means that, even with fail-safe enabled, if there's some usable stale data it will be returned, BUT not saved. This makes sense, as a TryGet or a GetOrDefault does not suggest a "save" operation (see: Principle of Least Astonishment).

This should not change any apparent behaviour from the outside: the only thing potentially noticeable is that, if you have a large number of TryGet/GetOrDefault calls done on stale data, you may see some more of them going to the distributed cache, that's all.

πŸ†• Added ServiceStack JSON support

A new serializer is now available in its own package, to support the ServiceStack JSON serializer.

Thanks @mythz for yout support!

β›” Added entry options normalization

Thanks to the community member @suhrab for spotting an edge case that could have been already handled.

When a user enable fail-safe and specifies a FailSafeMaxDuration lower than the Duration, the situation can feel messy since the thing being asked is not really in line with how FusionCache operates.

To avoid this, a new normalization phase has been added, to make all the values coherent with the situation.
In case a normalization occurs, FusionCache will also log it with a (configurable) Warning level.

See here for more.

πŸ“ž Added FactorySuccess event (docs)

A new event has been added that will trigger when a non-background factory completes successfully, after a request from community member @bartlomiejgawel (thanks for the tip!).
For the background one, keep using the BackgroundFactorySuccess event that already exists.

See here for more.

⚑ Performance Boost

When logging is enabled, FusionCache automatically generates a unique operation id per each operation (eg: each GetOrSet/GetOrDefault/TryGet/etc call) to make detective work easier while looking at the logs.

The generation of these operation ids now consumes 83% less cpu time and 45% less memory.

πŸ“• Better docs

The docs has been expanded and refined: one of these changes is that in the Options now there's a clear indication about which entry options are compatible with adaptive caching, thanks to a chat with @celluj34.

Thanks Joe for the inspiration!