alastairtree / LazyCache

An easy to use thread safe in-memory caching service with a simple developer friendly API for c#
https://nuget.org/packages/LazyCache
MIT License
1.72k stars 159 forks source link

Public API for Alternative Providers #138

Open svengeance opened 4 years ago

svengeance commented 4 years ago

[Use the Thumbs Up reaction to vote for this feature, and please avoid adding comments like "+1" as they create noise for others watching the issue.]

I think in the longer term, LazyCache would benefit greatly from having additional cache providers. There's two immediate ones that come to mind automatically -

FileCacheProvider - by storing metadata (expiration, weight, etc) in addition to the serialized content itself, this provider is capable of caching data between app executions.

FastMemoryCacheProvider - By discarding all the fripperies of dotnet's MemoryCache, it should be possible to build a provider dedicated to lowest possible allocations and fastest performance. This cache provider would be useful for those who are using cache in a hotpath, specifically where calculations that takes milliseconds are worth caching (and the cost of the cache is low nanoseconds).

@alastairtree I'm wondering if you had any ideas on an API to support additional providers. Ideally, users should be able to seamlessly switch between the providers.

Three possible approaches


1: Generic IAppCache

2: CacheFactory (see IHttpClientFactory)

3: Direct Provider Injection

To be honest I like the second option the best. It's a step away from the current implementation, but adding one layer of abstraction gives the user a whole lot of flexibility. We can also then provide an implementation if IMockCacheFactory, which always returns mocks.

TLDR

I may be off-base here entirely if this has already been thought out, but here are 3 implementation ideas for how we can support multiple providers in the future.

svengeance commented 4 years ago

I've forgotten, it seems, that CachingService can take in any provider as a ctor. Something like that should be retained to let the user spin up their own provider as necessary, but I do like the idea of providing first-class support for LazyCache's providers

alastairtree commented 4 years ago

Yes these make sense to me. There is already a provider interface, might not be perfect but should allow alternative implementations with minimal changes. Agree on a non default low feature faster in memory version, and local file version with serialisation version also useful. You would then need a hybrid version to combine in memory and some storage. There is also already an issue for the MS distributed cache interface implementation, which is probably more important I reckon. Might be better to have separate issues for each, so if someone starts, then split off and add an issue for it.

svengeance commented 4 years ago

@alastairtree What we currently have leans towards implementation 3, where we will do something like services.AddService<IFastCache>(services => new CachingService(new FastCacheProvider(..)));, since not everything can be IAppCache. This will work, but is a little rigid, since users will have to come up with ways to configure more than one implementation, and that can bleed logic from a service into the startup.

The idea with doing something like the second implementation is that users can configure the implementation of their cache per-service as needed when they invoke the factory, or go with a provider-agnostic IAppCache. It would be a direct emulation of the HttpClientFactory deal from MSFT, allowing for things like named caches, and configuration at the point of creation.

alastairtree commented 4 years ago

I see your point about option 2 - a cache factory allowing for multiple caches per app with different configs. However, it does add some complexity, and personally I have not needed it myself. A single cache and entry options has always been sufficient for my apps. Also, if someone (you?!) does need custom cache construction logic they can still build it on top of LazyCache using custom constructors logic inside startup/DI or by writing their own factory class. I prefer to focus any spare time efforts of mine on things that cannot be added on top by users like IDistributedCache which needs support/changes inside LazyCache. So maybe a seperate plugin?

svengeance commented 4 years ago

Hehe, not me necessarily. I'm just thinking about the future possibilities of having several different kinds of cache in an app. Crazy things like resource files (images) or so, where you'll want that flexibility.

I wonder if there's a good way to facilitate ease-of-use while offering some of that flexibility.

How do you feel about having first-class support at least for alternative providers, ala Option 3? This would allow users an extremely easy way to inject different providers per-class, and would only be restrictive in that you can't have the same provider with two different configurations.