Skip to content

Commit

Permalink
Merge pull request #104 from RWS/feature/CRQ-33855
Browse files Browse the repository at this point in the history
CRQ-33855 : Fix threading issue within localization resolver
  • Loading branch information
majiccode committed May 22, 2023
2 parents c902a50 + 568ee77 commit 23578bf
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 16 deletions.
1 change: 1 addition & 0 deletions Sdl.Web.Common/CacheRegions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public static class CacheRegions
public const string LinkResolving = "LinkResolving";
public const string BrokerQuery = "BrokerQuery";
public const string RenderedOutput = "RenderedOutput";
public const string LocalizationResolving = "LocalizationResolving";

/// <summary>
/// Returns true if view model caching is enabled in the web applications configuration.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using Sdl.Tridion.Api.Client.ContentModel;
using Sdl.Web.Common;
using Sdl.Web.Common.Configuration;
Expand All @@ -9,6 +10,8 @@ namespace Sdl.Web.Tridion
{
public class GraphQLLocalizationResolver : LocalizationResolver
{
private static readonly ConcurrentDictionary<string, object> KeyLocks = new ConcurrentDictionary<string, object>();

/// <summary>
/// Resolves a matching <see cref="ILocalization"/> for a given URL.
/// </summary>
Expand All @@ -19,6 +22,7 @@ public override Localization ResolveLocalization(Uri url)
{
using (new Tracer(url))
{
Log.Trace($"Resolving localization for url: '{url}'");
string urlLeftPart = url.GetLeftPart(UriPartial.Path);

// TODO PERF: to optimize caching, we could only take the first part of the URL path (e.g. one or two levels)
Expand All @@ -30,25 +34,38 @@ public override Localization ResolveLocalization(Uri url)
urlLeftPart = urlLeftPart.Substring(0, espaceIndex);
}

// NOTE: we're not using UrlToLocalizationMapping here, because we may match too eagerly on a base URL when there is a matching mapping with a more specific URL.
PublicationMapping mapping = null;
try
{
mapping = ApiClientFactory.Instance.CreateClient().GetPublicationMapping(ContentNamespace.Sites, urlLeftPart);
}
catch
{
Log.Error($"Failed to get publication mapping for url {urlLeftPart}");
}

if (mapping == null || mapping.Port != url.Port.ToString()) // See CRQ-1195
Localization result = GetCachedLocalization(urlLeftPart);
if (result != null)
{
throw new DxaUnknownLocalizationException($"No matching Localization found for URL '{urlLeftPart}'");
return result;
}

Localization result;
lock (KnownLocalizations)
lock (KeyLocks.GetOrAdd(urlLeftPart, new object()))
{
result = GetCachedLocalization(urlLeftPart);
if (result != null)
{
RemoveLock(urlLeftPart);
return result;
}

// NOTE: we're not using UrlToLocalizationMapping here, because we may match too eagerly on a base URL when there is a matching mapping with a more specific URL.
PublicationMapping mapping = null;
try
{
mapping = ApiClientFactory.Instance.CreateClient().GetPublicationMapping(ContentNamespace.Sites, urlLeftPart);
}
catch
{
Log.Error($"Failed to get publication mapping for url {urlLeftPart}");
}

if (mapping == null || mapping.Port != url.Port.ToString()) // See CRQ-1195
{
RemoveLock(urlLeftPart);
throw new DxaUnknownLocalizationException($"No matching Localization found for URL '{urlLeftPart}'");
}

string localizationId = mapping.PublicationId.ToString();
if (!KnownLocalizations.TryGetValue(localizationId, out result))
{
Expand All @@ -65,11 +82,40 @@ public override Localization ResolveLocalization(Uri url)
// a partially created localization.
result.Path = mapping.Path;
}


result.EnsureInitialized();

Log.Trace($"Localization for url '{url}' initialized and reports to be for Publication Id: {result.PublicationId()}, Path: {result.Path}");
CacheLocalization(urlLeftPart, result);
RemoveLock(urlLeftPart);
return result;
}
}
}

result.EnsureInitialized();
protected void CacheLocalization(String urlPart, Localization localization)
{
Log.Trace($"Attempting to cache localization for url part: '{urlPart}'");
SiteConfiguration.CacheProvider.Store($"{urlPart}", CacheRegions.LocalizationResolving, localization);
}

protected Localization GetCachedLocalization(String urlPart)
{
Localization result;
if (SiteConfiguration.CacheProvider.TryGet($"{urlPart}", CacheRegions.LocalizationResolving, out result))
{
Log.Trace($"Found cached localization for url part: '{urlPart}' with Publication Id: {result.PublicationId()}, Path: {result.Path}");
return result;
}
Log.Trace($"No cached localization found for url part: '{urlPart}'");
return null;
}

protected static void RemoveLock(String urlPart)
{
object tempKeyLock;
KeyLocks.TryRemove(urlPart, out tempKeyLock);
}

public override Localization GetLocalization(string localizationId)
Expand Down
1 change: 1 addition & 0 deletions Site/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
<add name="LinkResolving" cacheName="regularCache" />
<add name="PublicationMapping" cacheName="regularCache" />
<add name="BrokerQuery" cacheName="regularCache" />
<add name="LocalizationResolving" cacheName="regularCache" />
<!-- View Rendering output -->
<add name="RenderedOutput" cacheName="longLivedCache" />
</regions>
Expand Down

0 comments on commit 23578bf

Please sign in to comment.