Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • seanrock 262 posts 483 karma points
    Dec 20, 2023 @ 17:21
    seanrock
    0

    Distributed cache and CacheRefreshers

    Hi

    Just reading up on this because I want to ensure my site is prepared for a very possible load balanced scenario.

    What I'm caching is custom data, nothing umbraco related. I started off using IMemoryCache but I want to ensure there will be no issues if/when this site moves to multiple servers.

    Its really unclear how to go about it and seem to be spinning my wheels. The documentation unfortunately, is lacking considerably. The docs say use an ICacheRefresher but doesn't go into enough detail about how to use it, or where to use it/call it etc.

    Any pointers are appreciated.

    Thanks.

  • Marc Goodson 2157 posts 14431 karma points MVP 9x c-trib
    Dec 20, 2023 @ 23:08
    Marc Goodson
    1

    Hi seanrock

    What I've been using is IDistributedCache, if it's used on a single instance, it falls back to an 'in memory' implementation - it forces all of your objects you put into memory to be serializable.

    If you need to clear the cache, on a single instance - when something is published, then you can handled the Content Services Published Notificaition, and remove the cache item.

    When you move to Load Balanced environment you can configure an external caching service, a Redis Cache or a Database Cache - you'll need to do something similar if your site relies on Session at all, and the load balanced hosting environment you are moving to doesn't implement sticky sessions.

    When you are using a DistributedCache - then you only have to remove things from it once. and this removes it across all instances consuming the distributed cache - so you can still use the Publishing Notification.

    But if you have something cached into memory, eg not using distributed cache that's when you need to implement a custom CacheRefresher, which can be triggered to notify the other instances to clear their 'memory cache'.

    The only downside of using IDistributedCache, is there isn't a 'bulk remove' by 'starts with' of multiple cache keys at once.

    regards

    marc

  • seanrock 262 posts 483 karma points
    Dec 21, 2023 @ 09:55
    seanrock
    0

    Marc

    Thanks for the reply. The data I'm caching isn't related to umbraco content so the usual content notifications are not relevant, it doesn't affect the cached data.

    I do like the ability to clear by key prefix so the CacheRefresher is the way i'm leaning. However, the documentation is pretty thin and there isn't much of an example of how to actually implement and use it.

    I've tried to look in the source but I cannot find any specific example.

  • Marc Goodson 2157 posts 14431 karma points MVP 9x c-trib
    Dec 21, 2023 @ 15:25
    Marc Goodson
    1

    Hi seanrock

    What's the trigger to clear the cache?

    (if you implement a Redis Cache, you can clear it by key prefix, but just not through the dot net core IDistributedCache interface)

    A good example of a Cache Refresher is the DictionaryCacheRefresher in the Umbraco Core:

    https://github.com/umbraco/Umbraco-CMS/blob/422218ea1599e961385eca4ec92a5cafb8fbe5f2/src/Umbraco.Core/Cache/Refreshers/Implement/DictionaryCacheRefresher.cs#L7

    The dictionary items are stored in a database and cached for a period when they are retrieved. They are not part of nucache or anything to do with the Umbraco Published Cache.

    So when a dictionary item is updated in the backoffice, it needs that cache to be cleared across distributed servers.

    So you can see in that link the basic implementation of the refresher, it's inheriting CacheRefresherBase, giving it a unique guid and name and then overriding the Refresh and the Remove method, there is also a RefreshAll and Refresh (by guid) in the base class that can be overridden and implemented.

    Here you can see that all the memory cache instances are cleared by entity type, so all Dictionary items are emptied from cache if one is saved.

    https://github.com/umbraco/Umbraco-CMS/blob/contrib/src/Umbraco.Core/Cache/Refreshers/CacheRefresherBase.cs#L94

    It gets triggered in the DictionaryItemSaveDistributedCacheNotification

    https://github.com/umbraco/Umbraco-CMS/blob/422218ea1599e961385eca4ec92a5cafb8fbe5f2/src/Umbraco.Core/Cache/NotificationHandlers/Implement/DictionaryItemSavedDistributedCacheNotificationHandler.cs#L20

    There is an extension method on the DistributedCache that is called from there:

    https://github.com/umbraco/Umbraco-CMS/blob/contrib/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs#L79

    and you can see this calls the 'Refresh' method and passes in teh uniqueId of the DictionaryCacheRefresher and the id of the updated dictionary item...

    Then the CacheRefresherBase will publish the notification out to other servers using the EventAggregator

    https://github.com/umbraco/Umbraco-CMS/blob/422218ea1599e961385eca4ec92a5cafb8fbe5f2/src/Umbraco.Core/Cache/Refreshers/CacheRefresherBase.cs#L101C79-L101C94

    Because the CacheRefresherBase implements ICacheReferesher this will be autodiscovered at start up.

    So does that help demystify things?

    Create a CacheRefresher that inherits CacheRefresherBase, give it unique id and name, and override the refresh methods you are going to want to call.

    Then whatever triggers the cache to be broken across environments, needs to call the appropriate refresh/remove method on your Cache Refresher, and the base should do the rest!

    regards

    Marc

  • seanrock 262 posts 483 karma points
    Dec 22, 2023 @ 09:45
    seanrock
    0

    Marc

    Thats great thanks for the info!

    An example of something we're caching is the customer's shopping cart (we're caching it so we don't have to read it from the db often). The cached item will need to be refreshed when, for example, the cart lines are changed (add/edit/delete) so will be triggered from either a surface controller or an apicontroller.

    I played around with inject the refresher but I don't think that is the correct way (?). I also tried sending refresher notification (e.g. MyCacheRefresherNotification) using the eventaggregator but the refresher never executes. So obviously i'm doing something wrong but tying it all together is what i'm struggling with.

    Thanks.

  • Marc Goodson 2157 posts 14431 karma points MVP 9x c-trib
    Dec 22, 2023 @ 22:30
    Marc Goodson
    0

    Hi seanrock

    For a shopping cart have you considered using session state?

    https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-8.0#session-state

    It would be 'per user' and you could use a Redis backed implementation of session state to avoid database latency...

    You can clear the session state when the user checks out the basket.

    Regards

    Marc

  • seanrock 262 posts 483 karma points
    Dec 24, 2023 @ 07:43
    seanrock
    0

    Thanks Marc, I was also looking at that as a more simple option.

Please Sign in or register to post replies

Write your reply to:

Draft