neon-sunset / fast-cache

The fastest cache library written in C# for items with set expiration time. Easy to use, thread-safe and light on memory.
https://www.nuget.org/packages/FastCache.Cached/
MIT License
88 stars 8 forks source link

Add `Cached<T>.Save` overload #38

Closed xPaw closed 1 year ago

xPaw commented 1 year ago

It seems that the only way to save without getting from cache first is to use FastCache.Extensions. I prefer using explicit methods rather than extension for something like this.

neon-sunset commented 1 year ago

Hello and thank you for the interest in the library!

When prototyping API surface, I ended up deciding against such method because it looked quite cumbersome to call e.g. Cached<UserData>.Save(id, tenant, userData, TimeSpan.FromMinutes(3)); vs userData.Cache(id, tenant, TimeSpan.FromMinutes(3));.

In addition, due to the interaction between the static cache nature of the library and C# overload resolution, placing method arguments Kn and V adjacent to each other increased the likelihood of user error.

Could you provide a more specific example of your preferred usage? I'll consider supporting that scenario. For cache seeding with multiple values, you can employ CachedRange<V>.Save, which offers better performance.

P.S.: In retrospect, I believe that implementing this as a static cache may not have been the best decision, despite its appeal for minimal API prototypes / fine-grained microservices. Although it has benefits for code generation, they do not sufficiently offset the disadvantages.

xPaw commented 1 year ago

My particular use case was caching strings in a received event, and retrieve it from the cache in a separate event. So in this instance, always writing new key/values to the cache.

neon-sunset commented 1 year ago

I think as an alternative option we can do the following API that would allow composing limits and multi-argument keys without making it too cumbersome:

namespace FastCache;

public static partial class Cached<V>
{
    public static Cached<K, V> FromKey<K>(K key) { }
    ...
    public static Cached<(K1, K2, K3), V> FromKey(K1 param1, K2 param2, K3 param3) { }
    ...
}

e.g.

Cached<EventPayload>
    .FromKey(eventId, queueId)
    .Save(payload, TimeSpan.FromSeconds(300));

Important: this is static cache so Cached<string> will behave like a global interning pool (except likely better performance, I haven't touched the actual interning pool since .NET framework days). If you are okay with this - good, if not - feel free to use something like readonly record struct EventString(string Value); to ensure isolation.

xPaw commented 1 year ago

Do I understand you correctly that it would be similar to TryGet?

Cached<SalesReport>.FromKey(companyId).Save(report, TimeSpan.FromMinutes(60)); if we use the sample from the readme.

I think it might be more confusing because it won't fetch the cached value if there is one.

neon-sunset commented 1 year ago

Hmm, I agree, and so does GPT-4, seems there aren't that many ways to make it more idiomatic. I will add Cached<V>.Save(K1...K7 key, V Value, TimeSpan expiration) + limit overloads, it is consistent with CachedRange<V> which helps. At least there won't be any performance difference between now three ways of doing it which other libraries seem to suffer from.

neon-sunset commented 1 year ago

@xPaw https://www.nuget.org/packages/FastCache.Cached/1.7.0