Respawnsive/Apizr

Multiple CacheKey Attributes On Single Call Doesn't Work As Expected

Closed this issue · 3 comments

Caching was setup with multiple [CacheKey] as follows:

    [Get("/site/CRTeamraiserAPI?method=getParticipantProgress"), Cache(CacheMode.GetOrFetch, "00:05:00")]
    Task<getParticipantProgressResult> GetParticipantProgressAsync(
        [AliasAs("cons_id"), CacheKey] string constituentId,
        [AliasAs("fr_id"), CacheKey] string eventId);

So I anticipated that this would yield as result a cache key similar to:
GetParticipantProgressAsync(cons_id:{value}, eventId:{value})

instead it yielded a cache key that ignored the second CacheKey.
GetParticipantProgressAsync(cons_id:{value})

After panicking a bit, I figured out that the "fix" was to not use [CacheKey] at all.

I see from the code that it purposefully only gets the first CacheKey:

   // There's a specific cache key with a target field!
                            var cacheKeyField = extractedArgument
                                .Value
                                .GetType()
                                .GetRuntimeFields()
                                .FirstOrDefault(x => x.Name.Equals(cacheKeyAttribute.PropertyName)); 

Having said all this I can see a use case for specifying [CacheKey]'s for a subset of the arguments in a call, like for example:

TheCall(
        [CacheKey] string arg1,
        [CacheKey] string arg2,
        string  nonChangingArgWithLongNastyValueThatWeDontWantToInfluenceTheCacheKey
);

If I've misunderstood the usage of [CacheKey], please LKM, thx.

Well CacheKey attribute has been designed to be used for one and only parameter, so that you can tell Apizr which one of multiple parameters is the one to be used as cache key. But maybe I should reshape CacheKey more like you thought it was, it's a good point.
Anyway, for now, if you have multiple values to be included into the cache key, you should consider to use a custom type parameter like:

public class ParticipantProgressParameters
{
    [AliasAs("cons_id")]
    public string ConstituentId { get; set; }

    [AliasAs("fr_id")]
    public string EventId { get; set; }
}
...
[Get("/site/CRTeamraiserAPI?method=getParticipantProgress"), Cache(CacheMode.GetOrFetch, "00:05:00")]
Task<GetParticipantProgressResult> GetParticipantProgressAsync(ParticipantProgressParameters parameters);
...
var parameters = new ParticipantProgressParameters { ConstituentId = "Id1",  EventId = "Id2" };
var result = await _participantManager.ExecuteAsync(api => api.GetParticipantProgressAsync(parameters));

Then your cache key will be:
GetParticipantProgressAsync(parameters:{ConstituentId:Id1, EventId:Id2})

Note that:

  • AliasAs attribute is only here to adjust namings into Refit payload, not the CacheKey itself
  • CacheKey attribute is only here to discriminate multiple parameters (for now). It means that single parameter's methods doesn't really need it, as Apizr will take the only one it could find anyway.

Thanks for pointing that to me, I'll think about composite cachekey by multiple attribute using, in addition to the existing custom type solution. And I should update the documentation :)

Jeremy

Also note that if you don't provide any CacheKey attribute at all, Apizr will include all parameters while generating the cache key (doc updated but not yet pushed).

Anyway I finally implemented composite cache key generation with CacheKey attribute decoration today. It was quite effortless so I decided to adjust source code rigth the way to be part of the upcoming v6 (preview 4 maybe tomorrow). Doc will talk about that into the Cache configuration topic.

Thread reopened until preview 4 published.

Apizr-v6.0.0-preview.4 just released including composite cache key