Blog

Weekly Tips #6: Improving GrandNode performance

Tuesday, April 3, 2018 Comments (0)

What's the problem?

Imagine that you want to have a huge number of cateogries and subcategories in top menu. It's kind of uncommon scenario, because in the most cases we don't have such enormous number of categories, but in thix example we will have 10 main categories included in top menu and 3 thousand of subcategories. 

Simple trick <cache>

There are a few ways how you can achieve that. In our example if we want to cache the top menu elements, to increase their loading speed we should wrap the Top menu part in the <cache> tag. In GrandNode, top menu is generated in two places - _Root.cshtml and Header.cshtml files.

So, for example, we want to cache the top menu elements for 15 minutes. How we should set it? It's very simple process:

You should wrap the following line in the cache tag, like that:
<cache expires-after="@TimeSpan.FromMinutes(10)">
    @await Component.InvokeAsync("TopMenu")
</cache>

The cache tag is just a server side tag, you won't see it in generated HTML. You will see only the generated top menu. We were testing it on a store with 2500 categories, the loading time descreased from ~2 seconds to ~500ms. 

Types of <cache>

In the previous chapter, you've learned how to use the cache tag and implement it in GrandNode. Now, I will show you a few types of cache tag. Previous example was using the expires-after attribute. You were able to specify after how many minutes, the cache should expire.

Cache expiry

If you don't specify the cache expiration, the content will be cached as long as application life. 

expires-after

It's our example. Use the expires-after attribute to specify after what amount of time the cache should expire. This attribute is specified by TimeSpan value. If you want to expire item after 10 minutes, you should do it like that:

<cache expires-after="@TimeSpan.FromMinutes(10)">
@await Component.InvokeAsync("TopMenu")
</cache>

expires-on

You can use the expires-on attribute, if you want to expire cache at a specific time. You should use the DateTime value. It can be useful if you have a backend processes which will be updated by the end of the day. In this case you should specify it in this way:

<cache expires-on="@DateTime.Today.AddDays(1).AddTicks(-1)">
@await Component.InvokeAsync("TopMenu")
</cache>

expires-sliding

It can be used when you want to expire cache after it has not been accessed for a specified amount of time. It should be TimeSpan value, like in the first example.

<cache expires-sliding="@TimeSpan.FromMinutes(10)">
@await Component.InvokeAsync("TopMenu")
</cache>
 

Vary-by / Complex Cache Keys

Described tag helpers builds the cache keys by generating an id that is unique to the context of the cache tag. What does it mean for you? This ensures that you can have multiple cache tags on a single page and the contents will not override each other in the cache. How you can do it? One of the example can be a different content for each user by adding the vary-by-user attribute. 

vary-by-user

<cache vary-by-user="true">
content
</cache> 

This attribute is used to cache different contents for each logged in user. The username for the logged in user will be added to the cache key. This attribute expects a true or false value.

vary-by-route

You can use this attribute to cache different content based on a set of route data parameters. In this attribute you should use a comma-separated list of route data parameter names.

For example, the following cache tag would cache different contents based on the id route parameter:

<cache vary-by-route="id">
content
</cache>

vary-by-query

The vary-by-query attribute allows you to cache different contents based on the query parameters for the current request. This attribute expects a comma-separated list of query string parameter names. The value of those query string parameters will be added to the cache key.

vary-by-cookie

The vary-by-cookie attributes allows you to cache different contents based on values stored in a cookie. This attribute expects a comma-separated list of cookie names. The values of the specified cookie names will be added to the cache key.

<cache vary-by-cookie="MyCookie">
content
</cache>
 

vary-by-header

The vary-by-header attribute allows you to cache different contents based on the value of a specific request header. This attribute expects a single header name. For example, the following cache tag would cache different results based on the User-Agent header:

<cache vary-by-header="User-Agent">
content
</cache>
 

vary-by

Finally, the vary-by attribute allows you to cache different contents based on any arbitrary string value. This attribute can be used as a fall-back in case any of the other vary-by attributes do not meet your needs.

Complex keys

As mentioned earlier, you can specify any number of vary-by parameters and the cache tag helper will build a composite key. Here is an example of a cache tag that will cache different results for each user and id route parameter, used in the examples below:

<cache vary-by-user="true" vary-by-route="id">
content
</cache>

Limitations

The CacheTagHelper uses an instance of an IMemoryCache which stores cache entries in memory in the local process. Anything that causes the host process to shutdown / restart will cause a full loss of all content in the cache. For example, restarting an IIS App Pool or scaling down an Azure instance would cause the memory cache to reset. In this case, the CacheTagHelper would rebuild the contents on the next request. In a cloud service like Azure you never know when your website might get moved to a new server so this could happen at any time. The MemoryCache is not a durable storage mechanism so it is important not to treat it as one.

Conclusion

It can be used in many ways. For sure it can improve the work of your store, increase it speed, but the complete guide is dependent on industry and the type of an online store. I'm not able to create you a complete, working scenario. Because in each store, something different will work better or worse. 

Source: Dave Paquette blog.

Leave your comment