Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[deps]: Update fusioncache monorepo to 2.4.0#6631

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
renovate wants to merge1 commit intomain
base:main
Choose a base branch
Loading
fromrenovate/fusioncache-monorepo

Conversation

@renovate
Copy link
Contributor

@renovaterenovatebot commentedNov 24, 2025
edited
Loading

This PR contains the following updates:

PackageChangeAgeConfidence
ZiggyCreatures.FusionCache2.0.2 ->2.4.0ageconfidence
ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis2.0.2 ->2.4.0ageconfidence
ZiggyCreatures.FusionCache.Serialization.SystemTextJson2.0.2 ->2.4.0ageconfidence

Release Notes

ZiggyCreatures/FusionCache (ZiggyCreatures.FusionCache)

v2.4.0

🏷️ AddStaleTags to factory execution context

Community user@​ted-mundy noticed a tricky behavior when using Tagging with stale entries (see next point).

To solve it, I added a newStaleTags property to the factory execution context, so that now it's possible to access both the tags that are being passed to theGetOrSet() call and the existing tags of the stale entry in the cache (if any), like this:

cache.GetOrSet<string>("foo",(ctx,token)=>{// THE TAGS PASSED BELOW ("tag1", "tag2" and "tag3")ctx.Tags;// THE TAGS OF THE STALE ENTRY ALREADY IN THE CACHE, IF ANYctx.StaleTags;return"Combo";},tags:["tag1","tag2","tag3"]);

This can be useful even in other scenarios, like applying some custom logic about what to do based on the tags already in the cache.

Nice.

🐞 Fix for tags with stale entries

As mentioned above, community user@​ted-mundy noticed a tricky behavior when using Tagging with stale entries.

Thanks to the addition of the newStaleTags property, this is now solved for good.

Thanks@​ted-mundy !

Seehere for the original issue.

Ⓜ️ Better entry options mapping with HybridCache adapter

Community user@​TheSpookyElectric noticed that, when working with theHybridCache adapter, theLocalCacheExpiration was not being handled correctly in all cases.

The mapping logic has been updated to account for that, and it now works as expected.

Thanks@​TheSpookyElectric !

Seehere for the original issue.

🐞 Fix forWithRegisteredSerializer()

Community user@​Inok noticed something... strange.
Thebuilder, when callingWithRegisteredSerializer() on it, was doing something else: acting on the distributed cache.

That was, well... dumb, that's just what it was: my bad.

Now this has been solved.

Thanks@​Inok !

Seehere for the original issue.

🐞 Fix forInvalidOperationException when usingAlwaysOffSampler with OTEL

Community user@​DFSko noticed anInvalidOperationException being thrown when working with OTEL and using theAlwaysOffSampler: this had to do with setting the currentActivity after certain operations, in conjunction with activities with a state not compatible for being set as the current one.

This has now been solved.

Thanks@​DFSko !

Seehere for the original issue.

📕 Fix for an inline doc issue

Community user@​marcominerva discovered a wrong description for theWithDistributedCache() method on the builder: fixed.

Thanks@​marcominerva !

Seehere for the original issue.

✅ 1500 tests, huzza!

FusionCache reached 1500 tests:

image

That's it, that's the tweet 🙂

v2.3.0

🔑 Access the cache key in the factory context

Community user@​posledam and others asked for the ability to access the cache key in the factory context, to be able to work with it while going to the database or similar, without creating a closure with the lambda.

So that's what I added, but there's a catch here: FusionCache provides automatic cache key manipulation with things likeCacheKeyPrefix usually in conjunction with things like Named Caches, so it would be nice to access both theoriginal one and theprocessed one.

Therefore I added both of them in the factory context, and it can be used like this:

cache.GetOrSet<string>("foo",(ctx,token)=>{ctx.Key;// THE (PROCESSED) CACHE KEYctx.OriginalKey;// THE (ORIGINAL) CACHE KEY});

Seehere for the original issue, andhere for the design issue.

⚙️ NewInternalStrings options

FusionCache automatically handles a lot of things for us, and to do that it may need to manipulate some strings used internally like the cache key or the backplane channel name.

For example to use theCacheName to automatically separate data in a shared cache when used in conjunction with other FusionCache instances, or the set of special cache keys used withTagging or the waywire format versioning is handled to automatically avoid errors when evolving the internal data structures.

In these cases some special characters are used as separators.

This is all good and well, but recently community user@​stebet started working on a NATS version of the backplane (and that's awesome!) and he noticed that some of these special characters create issues on NATS, which has somereserved characters that have special meaning or cannot be used anyway.

Because of this I'm adding a new set of options specifically for changing the set of internal strings used by FusionCache, so that it's possible to work with systems like NATS and avoid issues.

So now we have a newInternalStrings option inside ofFusionCacheOptions where we can set strings like:

  • TagCacheKeyPrefix
  • ClearRemoveTag
  • DistributedCacheWireFormatSeparator
  • BackplaneWireFormatSeparator
  • and more

It can be used like this:

varoptions=newFusionCacheOptions(){// ...InternalStrings={TagCacheKeyPrefix="tag__",ClearRemoveTag="clear_remove"// ...}};

But there's an issue here that I can already foresee: in the future I may need to add new internal strings, and anyone who had carefully set them to something "safe" for them will start having issues after updating to the new version with the new internal strings.

Therefore I also added a new method on the internal strings class, a method that I will keep updating in case new strings will be needed of course, and that simply set them to values that uses a commonly accepted safe set of characters, meaning only alphanumeric characters + some common separators.

It can be used like this:

options.InternalStrings.SetToSafeStrings();

or, if we want to customize the couple of special chars, like this:

options.InternalStrings.SetToSafeStrings(separator:'-',specialChar:'_');

In this way it will be possible to keep every little detail under control and always be future proof.

Seehere for the original issue.

⚙️ NewFusionCacheEntryOptionsProvider

Community user@​gleb-osokin was looking for a way to haveDefaultEntryOptions specific for cache keys, to avoid having only one global entry options and have a way to automatically use the right one based on some custom logic instead of having to specify them at every call site.

The design took some time and some back and forth since the default entry options existed since the beginning, and to get things in the right shape has been a particularly delicate effort.

But now we have a newFusionCacheEntryOptionsProvider abstract class which anyone can implement with their own custom logic, and that we can set in theFusionCacheOptions object that we pass to create a FusionCache instance.

Nothing else needs to change, and the per-key provider, if any, is now automatically considered and used.

Particular care has been put into allowing users to have their custom logic without having a ton of new allocations.

Here's the thing:

/// <summary>/// A provider to get <see cref="FusionCacheEntryOptions"/> based on a key./// <br/><br/>/// ⚠️ <strong>IMPORTANT:</strong> in your GetEntryOptions() implementation carefully set the canMutate out param to indicate if the returned object can be mutated or not./// </summary>publicabstractclassFusionCacheEntryOptionsProvider{/// <summary>/// Provide entry options based on a key, by either returning a new instance or a reference to an existing one (for improved performance)./// <br/><br/>/// ⚠️ <strong>IMPORTANT:</strong> carefully set the <paramref name="canMutate"/> out param to indicate if the returned object can be mutated or not./// </summary>/// <param name="ctx">The context, containing supporting features.</param>/// <param name="key">The cache key.</param>/// <param name="canMutate">An out parameter that indicate if the returned object can be mutated.</param>/// <returns>The entry options.</returns>publicabstractFusionCacheEntryOptions?GetEntryOptions(FusionCacheEntryOptionsProviderContextctx,stringkey,outboolcanMutate);}

As we can see extra care has been put into the xml comments, to warn implementers about the fact that they have to pay attention to thecanMutate param, which is fundamental to signal that the returned entry options can bu mutated or not (and FusionCache then will take care of the rest, eventually duplicating it if needed).

A new method has been also added toIFusionCache: historically we had theCreateEntryOptions(...), and now we also have theCreateEntryOptions(key, ...) variant to include the key in the logic.

Here is theoriginal PR, andhere the design issue.

🐞 Fix for skipped check in read-only methods

Community user@​permagne noticed that read-only methods (eg:TryGet andGetOrDefault) were not considering theSkipDistributedCacheRead entry option: this, in case of a cache miss, means that every call would go to the L2, slowing things down.

This has now been solved.

Seehere for the original issue.

✅ Update to xUnit v3

I finally took some time to update all the tests to xUnit v3, which has been out for some time now.

On top of this I also added some more tests to cover some missing scenarios, getting the size of the test suite to:

image

Almost 1500, not bad.

📕 Docs

And finally the docs, which I care a lot about.

I have updated some, like:

  • Clear, mostly about the difference betweenClear(true) andClear(false)
  • Core Methods, mostly updating howExpire() works
  • Cache Levels, specifically adding a part about the envelope used with L2 and specify better the wire format versioning logic
  • Tagging, mostly related to some common questions about other massive operations, like searching

Some of these came from always welcome questions by community members like@​GeddesJ ,@​bebo-dot-dev ,@​martinkoslof and@​jundayin : thanks!

v2.2.0

🎯 Changes in multi-targeting

Some time ago I started enabling multi-targeting on FusionCache: I didn't do that because I needed to special case some parts of the code, but just because I wanted to reduce the amount of explicit dependencies for certain TFMs after a request from the community.

But recently community user@​nick-randal noticed in#​416 some potential issues: long story short, from now on FusionCache will have explicit targeting only for currently supported TFMs (which today means no more .NET 3.1, .NET 6 or .NET 7) and for them it will have the minimum set of explicit dependencies.

But wait: does this mean that those older versions of .NET wil not be able to use FusionCache anymore?

Absolutely not: since FusionCache targets .NET Standard 2.0, this means that ANY version of .NET compatible with .NET Standard 2.0 (meaning: all versions) will still be able to use FusionCache, just without an explicit "support statement", since those versions are anyway not supported anymore, not even by Microsoft itself.

Seehere andhere for the original issues.

🚀 Make the AOT support official

FusionCache has been AOT compatible for a long time, which is already good.

I just need to make that more "official" by declaring it in the csproj, enabling analyzers, create a test console app and, in general, do everything that's needed. And that's what I did.

Thanks to community user@​digital88 for pointing that out.

Seehere andhere for the original issues.

🔀 Expose the current distributed cache, if any

Currently, given aFusionCache instance, it's only possible to know if there is a distributed cache level (L2) via thebool HasDistributedCache { get; } property, not which one it is.

Community user@​angularsen asked to expose it in#​443 .

Historically I've been hesitant to expose internals, but at this point I think I can let this one go.

So now there's a newIDistributedCache? DistributedCache { get; } property that expose theIDistributedCache instance being used, if any.

Seehere andhere for the original issues.

[!WARNING]
This istechnically a breaking change, but since nobody has customIFusionCache implementations and I updated both (FusionCache andNullFusionCache), I think this is fine.

📢 Expose the current backplane, if any

Same as above, but for the backplane.

So now there's a new a newIFusionCacheBackplane? Backplane { get; } property that expose theIFusionCacheBackplane instance being used, if any.

Seehere andhere for the original issues.

[!WARNING]
This istechnically a breaking change, but since nobody has customIFusionCache implementations and I updated both (FusionCache andNullFusionCache), I think this is fine.

😶 Add DI support forNullFusionCache

Community user@​eskild-th noted it was not possible to specify to use aNullFusionCache implementation via DI.
And now it is.

Seehere andhere for the original issues.

🧼 Better perf forClear(true)

Since I addedClear() support in v2, it has become one of the favourite features by the community (including, of course, Tagging).

Recently community user@​ctyar noted something seeminglystrange, which I then clarified, so all was good.

But this sparked an optimization idea so, even though the implementation forClear() has been highly optimized since day 1, now it is even more in cases whenRaw Clear is possible (eg: L1 only, no shared).

This, in reality, translates to better Clear checks,which means better perf forany method call in the mentioned scenario (eg:GetOrSet,TryGet, etc), not just when callingClear() itself: yeah 🥳

Seehere for the issue that sparked the idea for the optimization.

📢 More async backplane

Community user@​pil0t suggested to look into some backplane code, and proposed some changes, which I merged and then added some others on top of them.
The result is that now the backplane should work better, in a more async way, anytime possible: this allows for even less thread blocking than before.

Seehere for the original issue.

📜 Better logging

Community user@​gmij pointed out that there was not much use of the INFO logging level, and that most log entries were at the DEBUG level: that is a good thing to point out, so now all the "entry/exit points" are marked at INFO level.
This means there's more differentiation between the different levels used, which is better to get more visibility into FusionCache internals but without immediately gettingtoo much visibility.

On a different note, but still about logging, distributed cache errors related to internal FusionCache operations now log with a WARNING level instead of an ERROR level: this should avoid triggering some alarms in observability scenarios where that is configured.

Seehere andhere for the original issues.

🔓 New memory locker based on the 3rd party AsyncKeyedLock library

Community user@​MarkCiliaVincenti asked in#​134 (yup, quite some time ago 😅) to switch FusionCache internal locking mechanism to his own library,AsyncKeyedLock.

Now, I don't want to have adirect dependency to something external for something so core, but sincev0.26.0 there's a newIFusionCacheMemoryLocker abstraction which allows the creation of 3rd party implementations, so I decided to give it a try and added support for it.

How good is it? How does it compare to the standard one included in FusionCache? It depends on your own scenario, so the best thing is to try it out, measure everything, and see for yourself.

Seehere for the original issue.

🐞Fix for Eager Refresh in high concurrency scenarios

Community user@​HannaHromenko noted that sometimes, in highly concurrent scenarios, a null reference exception was being thrown, who knows why.

Well damn, I knew why, and that is now fixed.

Seehere for the original issue.

🐞 Small fix when jittering + fail-safe

It has been noted by@​fabianmenges that, when using both jittering and fail-safe, something unexpected could happen: now this has been fixed.

Seehere for the original issue.

🐞 Small fix when setting a value with AutoClone

Community user@​nlconor noted that when using AutoClone the serialization was being done lazily, and this fact may create problems when putting something in the the cache, keeping a reference to it, then change it.
Now this has been fixed.

Seehere for the original issue.

v2.1.0

🙋‍♂️ Updating tov2 ? Pleaseread here.

🔌 Integrate All The Things!

Now that v2 isfinally here and with fullTagging support, it's time to integrate all the things 🥳

The first 2 can be found below:

  • OutputCache
  • EF 2nd Level Cache

🚀 Output Cache, FusionCache style

The first one on my list isOutput Cache, and the nice thing about the way it has been designed in ASP.NET is that the only thing that is needed to make a custom version is an implementation ofIOutputCacheStore.

And so I did, and thanks to native Tagging in FusionCache the whole implementation is a thing of beauty with just 1 line per method:behold.

Btw while I was working on this, community user@​Fabman08asked for the same thing, talk about good timing!

Anyway, why is all of this useful?

Because now, when using OutputCache, we'll not be limited by a simple memory cache anymore, and can instead have the power of all the features of FusionCache likefail-safe,L1+L2,backplane support and more: imagine having the performance of a memory cache (L1) but with the availability and database savings of a distributed cache (L2) including instant synchronization of the backplane.

If you ask me, it's awesome.

Ok so, how can we set it up?

Easy:

// FUSION CACHEservices.AddFusionCache();// FUSION OUTPUT CACHEservices.AddFusionOutputCache();// OUTPUT CACHE (STANDARD SETUP)services.AddOutputCache(options=>{options.AddPolicy("Expire2", builder=>builder.Expire(TimeSpan.FromSeconds(2)));options.AddPolicy("Expire5", builder=>builder.Expire(TimeSpan.FromSeconds(5)));});

When using the normal OutputCache (with a memory-only cache store) we need to:

  • setup OutputCache (settings, profiles, etc)

With the FusionCache-based version we just need 2 extra steps, before the common one:

  • 🆕 setup a FusionCache instance
  • 🆕 setup the FusionCache-based OutputCache
  • setup OutputCache normally (settings, profiles, etc)

One thing to note is that, even though it's possible to use thedefault FusionCache instance like in the example above, it's usually better to have a separatenamed cache with a specific configuration for OutputCache: this can be useful both to avoid cache key collisions (even though it is already quite hard to have them because of the standard key structure in OutputCache itself) and to have different L1/L2/backplane configurations.

How? Easy:

// FUSION CACHE (WITH CUSTOM NAME, L2, BACKPLANE, DEFAULT ENTRY OPTIONS)services.AddFusionCache("MyOutputCache").WithDefaultEntryOptions(options=>{options.IsFailSafeEnabled=true;}).WithSerializer(newFusionCacheProtoBufNetSerializer()).WithDistributedCache(newRedisCache(newRedisCacheOptions{Configuration="..."})).WithBackplane(newRedisBackplane(newRedisBackplaneOptions{Configuration="..."}));// FUSION OUTPUT CACHEservices.AddFusionOutputCache(options=>{// WHICH NAMED CACHE TO USEoptions.CacheName="MyOutputCache";});// OUTPUT CACHE (STANDARD SETUP)services.AddOutputCache(options=>{options.AddPolicy("Expire2", builder=>builder.Expire(TimeSpan.FromSeconds(2)));options.AddPolicy("Expire5", builder=>builder.Expire(TimeSpan.FromSeconds(5)));});

Another important aspect is to be able to use adifferent serializers.

Wait, but why a different serializer?

Frequently it's common to use text-based serialziers (eg: JSON-based) for our entities and objects in the cache, and that is totally fine.

But OutputCache deals withbyte[] (containing the entire http response with headers, body, etc) and by using a text-based serializer we are not getting the best performance for our bucks.

So, my suggestion is to pick anatively binary serializer likeprotobuf-net,MessagePack orMemoryPack (the available ones can be foundhere): in this way the payload in L2 will be as small as possible, and performance will be top notch.

Awesome.

🚀 EF 2nd Level Cache

The other one is an interesting project by@​VahidN calledEFCoreSecondLevelCacheInterceptor, which proposes itself as a transparent 2nd level cache for EFCore.

The nice thing for this is that I did... nothing at all 😅

One community user@​kooshan asked for it in their repo some time ago, more recently another user@​bbehrens let me know about it and, before I was able to do anything, the maintainer worked on it, released the new v5 version with pluggable multi-provider support and...tada 🎉

If interested, I suggest using at leastv5.1 since it's the first that depends on the final FusionCache v2 bits.

📢 More async-y Backplane (docs)

The backplane has always been async at its core, meaning the messages sent and received.

A couple of smaller parts though were not as async-aware as ideally wanted, and now this is fixed: both the initial subscription and the ending unsubscription are now available in a fully async variant.

🐞 Fix edge case bug with parallel init (Protobuf-net)

Comunity user@​ilioan noticed (thanks!) a small regression in FusionCache v2 related to a particular edge case: highly parallel initializations.
In that case there was a missing check in a classic double-checked-lock, such that in some cases it resulted in a missing model registration.
Now this has been fixed.

Seehere for the original issue.

📜 Better Logging

Community user@​sebbarg noticed (thanks!) that when filtering log messages for theDebug level, the output was not totally consistent: the general method was being logged (eg:calling RemoveAsync), and the L1 operation was being logged too, but the L2 operation was nowhere to be found.
The issue? L2 operations were using theTrace log level 🤦.

Anyway now this has been fixed, and log messages are more consistent.

Seehere for the original issue.

🧬 Diagrams (docs)

Sometimes it's nice to be able to visualize the internal flow of a system, even more so for such a complex beast as an hybrid cache like FusionCache.

So, diagrams!

FusionCache Diagrams

✅ Better Tests

As with any new release I made the tests better, this time adding a couple of additional scenarios covered.

I also reorganized all the tests in a better way, by splitting the sync and async ones in separated files thanks to partial classes, so that they are now nicer to work with and easier to keep aligned.

📕 Better Docs (and diagrams!)

I've added some more docs for the latest stuff, and fixed some typos, no big deal.


Configuration

📅Schedule: Branch creation - "every 2nd week starting on the 2 week of the year before 4am on Monday" (UTC), Automerge - At any time (no schedule defined).

🚦Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated byMend Renovate. View therepository job log.

@bitwarden-botbitwarden-bot changed the title[deps]: Update fusioncache monorepo to 2.4.0[PM-28720] [deps]: Update fusioncache monorepo to 2.4.0Nov 24, 2025
@bitwarden-bot
Copy link

Internal tracking:

@renovaterenovatebot changed the title[PM-28720] [deps]: Update fusioncache monorepo to 2.4.0[deps]: Update fusioncache monorepo to 2.4.0Nov 24, 2025
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

2 participants

@bitwarden-bot

[8]ページ先頭

©2009-2025 Movatter.jp