November Happy Hour will be moved to Thursday December 5th.

Caching resolved URLs across requests?

Vote:
 

I am profiling the performance of a website with a decent amount of blocks, images and links on the start page. In general it is fast, even under fair load. I use the Bombardier tool (2000 requests with 20 concurrent connections) for a simple load test on my development machine.

However, what sticks out a lot from the recorded profiler data is that URL resolving accounts for almost all of the call time and an excessive memory usage (around 2 GB during the said load test).

I can see that all links are always resolved from cached content on each request. E.g., calling Url.ContentUrl on a ContentReference to an image, seems to load that content item from cache (or DB on first load), as well as all its parent containers, in order to resolve the URL. Apparently, the resulting URL is not cached, because this process gets repeated for all request in my load test.

Is there a simple way of enabling caching of generated URLs acrosss requests? It could be short-lived caching, or it could be long-lived with content dependency keys.

Or would I have to sub-class DefaultUrlResolver and add such logic?

#201749
Mar 01, 2019 8:38
Vote:
 

As far as I know, this is not configurable. Instead of subclassing, I would probably intercept the default resolver:

Edit: Updated to override GetVirtualPath method instead of GetUrl.

[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class UrlResolverInitializer : IConfigurableModule
{
    public void ConfigureContainer(ServiceConfigurationContext context)
    {
        context.Services.Intercept<UrlResolver>(
            (locator, defaultResolover) => new CustomUrlResolver(defaultResolver));
    }

    public void Initialize(InitializationEngine context) { }
    public void Uninitialize(InitializationEngine context) { }
}

public class CustomUrlResolver : UrlResolver
{
    public CustomUrlResolver(UrlResolver defaultUrlResolver)
    {
        _defaultUrlResolver = defaultUrlResolver;
    }

    public override VirtualPathData GetVirtualPath(ContentReference contentLink, string language, VirtualPathArguments virtualPathArguments)
    {
        var virtualPathData = MyGetVirtualPathFromCacheMethod(contentLink, language, virtualPathArguments);

        if (virtualPathData == null)
        {
            virtualPathData = _defaultUrlResolver.GetVirtualPath(contentLink, language, virtualPathArguments);
            // Add data to cache here
        }

        return virtualPathData;
    }
}
#201757
Edited, Mar 01, 2019 11:28
Vote:
 

Thanks, Mattias.

Nice and simple solution, and somewhat like I had imagined it, myself. I will try to squeeze it in before the default Commerce and CMS resolvers.

I guess I just hoped for a built-in solution.

#201777
Mar 03, 2019 6:02
Vote:
 

Hello again!

I tested this a bit and it seems that the GetVirtualPath method is what should be overridden and cached. The GetUrl method doesn't seem to be called that often internally.

#201801
Mar 04, 2019 9:14
Vote:
 

Beware of that VirtualPathArguments parameter. I would probably let the default resolver take care of more advanced arguments with route values, other actions than Index etc. Maybe something like this:

private bool ShouldCacheVirtualPath(VirtualPathArguments virtualPathArguments)
{
    return virtualPathArguments == null
           || (virtualPathArguments.Action == null
           || virtualPathArguments.Action.Equals("index", StringComparison.OrdinalIgnoreCase))
           && virtualPathArguments.RouteValues == null
           && (virtualPathArguments.ContextMode == ContextMode.Default || virtualPathArguments.ContextMode == ContextMode.Undefined);
}
#201811
Mar 04, 2019 10:42
Vote:
 

I ended up overriding the abstract UrlResolver class, for now only intercepting the main method, GetVirtualPath.

In that method, I:

  1. Make up a cache key, including the VirtualPathArguments parameters (if not null).
  2. Look up the URL from cache, or call the default URL resolver through interception.

I also found that calling Url.ContentLink with a LinkItem does not invoke the stack of URL resolvers.

#201819
Mar 04, 2019 13:42
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.