Releases: ZiggyCreatures/FusionCache
v0.14.0
↩️ Backplane auto-recovery (experimental)
FusionCache now has a backplane auto-recovery feature!
It has been added to automatically recover from transient errors in the backplane by keeping a local queue of the notifications that have not been sent successfully. These notifications will be automatically retried as soon as the backplane will become available again, without having to do anything.
It also handles common edge cases, like out of order notifications, and it can also be limited in size to avoid too consuming too much memory, with a sensible heuristic about which notifications to keep.
🧪 NOTE: for now the feature is experimental, and must be manually enabled via the new EnableBackplaneAutoRecovery
option.
More info here.
🐵 Chaos-related split
The chaos-related utilities and components, like ChaosDistributedCache
and ChaosBackplane
, have been moved to a separate project (ZiggyCreatures.FusionCache.Chaos).
This should also slightly reduce dependencies and code size of the main package.
📕 Xml Docs
Added links to the related online documentation in the xml docs for most options, and fixed some typos.
📕 Docs
Added a page about Logging.
Added a chapter about the lock timeout option in the Cache Stampede page.
Added a chapter about notifications behaviour in the Backplane page.
Added a chapter about auto-recovery in the Backplane page.
⚠ Removal of [Obsolete] members
Some members marked as [Obsolete]
from a very very long time have been finally removed to clean up the code.
v0.13.0
⏳ Better inferred local expiration
A member of the community (@yzhoholiev , thanks!) noticed that the local (memory cache) expiration, when coming from the distributed cache, was not good enough: with this fix the inferred memory cache expiration in those situations is more precise.
In some scenario a little bit more memory may be used with the distributed cache (eg: when not using fail-safe, otherwise it stays the same) but that should not be very frequent and the additional memory should reasonably not be a problem.
See here for more.
v0.12.0
🆕 Added DistributedCacheDuration
Added a new entry option DistributedCacheDuration
(as a nullable TimeSpan
).
This serves as an optional specific duration for the distributed cache (if you are using one) to allow custom overrides of the "main" duration: in this way it is now possible for example to specify a duration in the memory cache of 1 min
and a duration in the distributed cache of, say, 1 hour
.
In theory this is useless if you are using a backplane but, in case you prefer not to or cannot for some reason, this would mitigate the synchronization problems you may encounter in a multi-node scenario.
See here for more.
🔗 Added SourceLink support
FusionCache now supports SourceLink, which should make your debugging experience easier.
See here for more.
🛡 Added Deterministic Builds support
FusionCache now supports Deterministic Builds, which is a nice addition.
See here for more.
⚠ Removal of a Duplicate()
method override
The override removed has been marked [Obsolete]
for a very very long time and hopefully nobody should've been using it.
v0.11.1
⏱ Rollback of: use soft timeout if failSafeDefaultValue
provided
A decision has been made to temporarily rollback a change made in v0.11.0.
To see why take a look at this comment.
The main reason is to avoid unwanted surprises for existing users, who could have suddenly started to receive sub-optimal values during a first call.
Discussion are ongoing on how to better handle this scenario.
v0.11.0
This release includes a lot of small features, changes and fixes.
☎️ Better GetOrSet events
In a previous release there has been a small regression in the events emitted during a GetOrSet
call: now it has been fixed.
See #49
⏱ Use soft timeout if failSafeDefaultValue
provided
Historically the soft timeout, if specified, has been used only when there was a fallback value in the form of an already expired cache entry.
Sadly, after having introduced the failSafeDefaultValue
param, that has not been used to enable the soft timeout.
Now this is resolved thanks to @alexmaek , who also made their first contribution!
See #67
Note
This feature has been rolled back in v0.11.1 release because it was not the right move. See the pull request above for more!
💣 Optional re-throwing of distributed cache exceptions
Following a community request, it is now possible to enable re-throwing of distributed cache exceptions, if you feel like it.
This is done via the new ReThrowDistributedCacheExceptions
option on the FusionCacheEntryOptions
object.
See #34
📢 Better backplane timeouts handling
Now synthetic timeouts are handled in a more specific way, and there's a new BackplaneSyntheticTimeoutsLogLevel
option in the FusionCacheEntryOptions
object.
📢 Better backplane error handling
The internal flow on backplane exceptions is now better, to let the (optional) circuit breaker do its job more easily.
🎉 New Contributors
v0.10.0
🧙♂️ Adaptive Caching (more)
FusionCache now supports adaptive caching!
This means having new GetOrSet
overloads where it is possible to change some options inside the factory, to be able to adapt (hence the name of the feature) some of these options to the object being cached. A typical example is caching a fresh news article for a small Duration
, and an old one for a higher one.
More examples and explanations in the related documentation.
🚀 Better lock perf (in some scenarios)
There's a new optimization that kicks in automatically during a GetOrSet
call if:
- fail-safe is enabled (via
IsFailSafeEnabled
) - AND there's an expired entry available as a fallback
- AND a
FactorySoftTimeout
has been specified - AND
LockTimeout
has NOT been specified (pretty common)
In this case it may happen that a during multiple concurrent GetOrSet
calls for the same key, the first one acquired the lock and start executing the factory. During this time the other GetOrSet
calls for the same cache key are put on hold (to prevent a cache stampede, see here). But if the factory takes too much time it doesn't make sense to wait for it for the other callers, if they allow fail-safe and there's stale data to use as a fallback.
So with this optimization this is exactly what happens, maybe granting a perf boost in thee situations.
NOTE: these early-returning GetOrSet
calls btw will NOT temporarily re-save the stale data in the cache, because the currently running factory will do so nonetheless, and because doing so does not incur in extra factory calls, so we are still protected from the cache stampede problem.
📞 More stale hit events
Thanks to a suggestion by @JoeShook , FusionCache now emit events for cache hit with isStale
set to true
not only when a fail-safe is being activated, but in all subsequent cache hits until the entry is replaced by a successful factory execution, which frankly makes more sense.
🆕 Added HasDistributedCache
A new bool
property has been added to IFusionCache
to let you know if there is a distributed cache configured (like the already existing HasBackplane
).
📜 Logging (new warning in a specific case)
Added a warning log when setting up a backplane in this scenario:
- a backplane is configured
- AND a distributed cache is NOT configured
- AND the option
DefaultEntryOptions.EnableBackplaneNotifications
is set totrue
This is important to prevent shooting yourself in the foot without knowing it, and that may happen because without a distributed cache there is no shared state between nodes, and if by default the backplane notifications are enabled it means that every time something is put into the cache in a node, all the other nodes will wipe their copy, creating a never ending update-remove-update-remove-etc loop.
You can read more about a scenario like this in the backplane documentation.
v0.10.0-preview1
🧙♂️ Adaptive Caching (more)
FusionCache now supports adaptive caching!
This means having new GetOrSet
overloads where it is possible to change some options inside the factory, to be able to adapt (hence the name of the feature) some of these options to the object being cached. A typical example is caching a fresh news article for a small Duration
, and an old one for a higher one.
More examples and explanations in the related documentation.
🚀 Better lock perf (in some scenarios)
There's a new optimization that kicks in automatically during a GetOrSet
call if:
- fail-safe is enabled (via
IsFailSafeEnabled
) - AND there's an expired entry available as a fallback
- AND a
FactorySoftTimeout
has been specified - AND
LockTimeout
has NOT been specified (pretty common)
In this case it may happen that a during multiple concurrent GetOrSet
calls for the same key, the first one acquired the lock and start executing the factory. During this time the other GetOrSet
calls for the same cache key are put on hold (to prevent a cache stampede, see here). But if the factory takes too much time it doesn't make sense to wait for it for the other callers, if they allow fail-safe and there's stale data to use as a fallback.
So with this optimization this is exactly what happens, maybe granting a perf boost in thee situations.
NOTE: these early-returning GetOrSet
calls btw will NOT temporarily re-save the stale data in the cache, because the currently running factory will do so nonetheless, and because doing so does not incur in extra factory calls, so we are still protected from the cache stampede problem.
🆕 Added HasDistributedCache
A new bool
property has been added to IFusionCache
to let you know if there is a distributed cache configured (like the already existing HasBackplane
).
📜 Logging (new warning in a specific case)
Added a warning log when setting up a backplane in this scenario:
- a backplane is configured
- AND a distributed cache is NOT configured
- AND the option
DefaultEntryOptions.EnableBackplaneNotifications
is set totrue
This is important to prevent shooting yourself in the foot without knowing it, and that may happen because without a distributed cache there is no shared state between nodes, and if by default the backplane notifications are enabled it means that every time something is put into the cache in a node, all the other nodes will wipe their copy, creating a never ending update-remove-update-remove-etc loop.
You can read more about a scenario like this in the backplane documentation.
🙏 Tell me what you think
Please try it out and let me know so I can push the final version out.
v0.9.0
📢 Backplane (more)
FusionCache now has a fully functioning backplane, to ease synchronization between nodes in a multi-node scenario.
It's very easy to setup and requires no additional work: it just works.
There are currently 2 implementations: one memory-only (mainly for testing) and one for Redis.
The backplane, like the 2nd level cache, is fully featured including events and logging and, since it talks to a separate system (eg: a Redis or Memcached instance), it natively support a simple circuit-breaker to better handle transient errors, again like the 2nd level cache.
📕 Better docs
Since the v0.9.0 is a big release with a great new feature, I took the chance and tried to improve the docs.
Of course there's the new part about the backplane and how to use it in various situations (both with and without the distributed cache).
I also collected all the IDistributedCache
implementations available on Nuget, which is something I hope can be useful to the community as they can serve as the secondary cache layer.
Finally some typos have been corrected and a lot of small parts in general have been added where I felt they were missing.
📜 Logging (fail-safe activation)
Thanks to a tip by the community (see here #38), I changed a log level used when no fallback entry was available for a fail-safe activation, since it seemed more correct this way.
⚠ Removed CacheKeyPrefix
option
The FusionCacheOptions.CacheKeyPrefix
option is now fully obsolete: it has been marked with the [Obsolete]
attribute including the additional error
flag, and is hidden via the [EditorBrowsable]
attribute so its use will not compile anymore (see #33 for more).
⚠ Removed Evict
method
The Evict
method was just something theoretically used to create the backplane, nothing to be used in the normal FusionCache usage.
Since in the end the final backplane design did not need this method, it has been removed to keep a more streamlined API.
v0.1.10-beta3
This is the third and last BETA release containing the new Backplane #11 🎉.
It contains everything that was already in alpha1, alpha2, beta1 and beta2 plus:
👌 Final polishing
In general this is the final polishing pass of everything.
📕 Better docs
The new docs are finished, particularly the ones about the new backplane and how to use it in various situations (both with and without the distributed cache). I also collected all the IDistributedCache
implementations available on Nuget, which is something I hope can be useful to the community as they can serve as the secondary cache layer. Finally some typos have been corrected and more parts in general have been added where I felt they were missing.
🆕 Removed Publish[Async] method
The tentative new Publish[Async] method was experimentally introduced a little while ago: it served its purpose, and since it was just an experiment and realliy only used internally, I decided to remove it before the next official version, so to keep a clean a slim api.
🆕 Removed Evict method
The tentative new Evict method was experimentally introduced a little while ago: it served its purpose, and since it was just an experiment and realliy only used internally, I decided to remove it before the next official version, so to keep a clean a slim api.
🙏 Tell me what you think
Please try it and let me know so I can push the final version out!
v0.1.10-beta2
This is the second BETA release containing the new Backplane #11 🎉.
It contains everything that was already in alpha1, alpha2 and beta1 plus:
🤦♂️ I'm as dumb as a rock
Because of some local code explorations + git reverts, I haven't in fact pushed the code for the backplane auto-setup in DI: this means that in a DI scenario we were not, in fact, using the backplane, even if properly configured.
Yep, I'm that dumb 😑
⚡ backplane subscription
Better initial backplane subscription handling, with a design that is more open for future extensibility.
🙏 Tell me what you think
Please try it and let me know so I can push the final version out!