EntityFramework-Plus icon indicating copy to clipboard operation
EntityFramework-Plus copied to clipboard

ExpireTags is not expiring the cache

Open binaryn3xus opened this issue 7 years ago • 5 comments

I have added the cache feature to my project and I am having an issue where it does not seem to be expiring the cache.

This is a .Net Core 2 application.

My startup file has some QueryCacheManager settings:

QueryCacheManager.DefaultMemoryCacheEntryOptions = new MemoryCacheEntryOptions
{
     AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
};

Then I have the Repository pattern set up in my project. When I allow someone to add a new record to the table, it is done without cache. When the table is just read back then it is cached. So this means that when they do a SaveChangesAsync, then it has to expire all the tables related to that module of the applications in order to not bring back old stale data. So here is a read from the applications....

public async Task<IQueryable<HuddleAttachment>> GetAttachmentsFromHuddleArticle(Guid huddleId)
        {
            var taskResults = await Task.Run(() =>
                {
                    return _ctx.HuddleAttachments.Where(o => o.HuddleArticleId == huddleId).FromCacheAsync("HuddlePlanner", "HuddleAttachments");
                });
            return taskResults.AsQueryable();
        }

Then when they do something to the database to the same record (which is directly to the _ctx.HuddleAttachment.Add(...)), they have to call the SaveChangesAsync() which looks like this...

public async Task<int> SaveChangesAsync()
 {
            var huddleRelatedKeys = QueryCacheManager.CacheTags.Keys.Where(o => o.Contains("Huddle"));
            QueryCacheManager.ExpireTag(huddleRelatedKeys.ToArray());
            return await _ctx.SaveChangesAsync();
}

image

Now, this may be my misunderstanding of the library but I decided to double check the source code of this library before I made a post here instead of on StackOverflow. Based on the code in QueryCacheManager.cs, I felt that it was github worthy issue. The way I read the library source code, it should be removing all items from the CacheTags property that match the key, which is not happening.

I am hoping you are going to tell me that I am just doing X wrong and its an easy fix. However, right now when I reload my page I am still seeing the cached content even after calling this ExpireTag method on a SaveChangeAsync (mine, not EFs).

Thanks in advance!

binaryn3xus avatar May 09 '18 20:05 binaryn3xus

Hello @JoshuaGarrison27 ,

I'm not sure if that's a mistake on your side or a bad coding behavior on our side (probably this one hehe).

Let take in the example the Huddle cache key.

When you cache your query, on our side, we cache the following string Z.EntityFramework.Plus.QueryCacheManager;Huddle

I believe this is the string you part in the ExpireTag method, however, the method is expecting the value Huddle and not Z.EntityFramework.Plus.QueryCacheManager;Huddle

The method ExpireTag try to expire the tag Z.EntityFramework.Plus.QueryCacheManager;Z.EntityFramework.Plus.QueryCacheManager;Huddle and nothing is found.

We will think more about this issue how we should handle it, but meanwhile you can remove the cache tag prefix with the Replace method.

Let me know if that explain correctly what's hapenning.

Best Regards,

Jonathan


Performance Libraries context.BulkInsert(list, options => options.BatchSize = 1000); Entity Framework ExtensionsBulk OperationsDapper PlusLinqToSql Plus

Runtime Evaluation Eval.Execute("x + y", new {x = 1, y = 2}); // return 3 C# Eval FunctionSQL Eval Function

JonathanMagnan avatar May 10 '18 23:05 JonathanMagnan

Makes complete sense. That is my fault for not paying closer attention to Line 227 (QueryCacheManager.cs)

public async Task<int> SaveChangesAsync()
        {
            var huddleRelatedKeys = QueryCacheManager.CacheTags.Keys.Where(o => o.Contains("Huddle"))
                .Select(o=>o.Replace(QueryCacheManager.CachePrefix, ""));
            QueryCacheManager.ExpireTag(huddleRelatedKeys.ToArray());
            return await _ctx.SaveChangesAsync();
        }

This new code works perfectly so far. Though, it might be better to just create a tag for all my modules and add it to each FromCacheAsync to reduce having to iterate thought the list and match based on a string. (ie. FromCacheAsync("{ModuleName}", "AllHuddleArticles") and then just expire ModuleName)

Question/Suggestion If you dont think it will cause a performance delay, what about doing a check for the prefix first before it is just prepended to the provided tag?

binaryn3xus avatar May 11 '18 14:05 binaryn3xus

Hello @JoshuaGarrison27 ,

Yes, that's our plan to probably have a Formalize method as you suggested. So it will work by default with your first code ;)

Best Regards,

Jonathan

JonathanMagnan avatar May 11 '18 14:05 JonathanMagnan

Fantastic! Do you want to leave this issue open as a work item or close it?

binaryn3xus avatar May 11 '18 14:05 binaryn3xus

Let it open,

That has been assigned to one of my developer for this week.

Best Regards,

Jonathan

JonathanMagnan avatar May 13 '18 13:05 JonathanMagnan