Wire NuCache and IdkMap
This commit is contained in:
@@ -3,8 +3,6 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
@@ -30,38 +28,22 @@ namespace Umbraco.Core.Services
|
||||
// to each other, then the id will never map to another guid, and the guid will never map
|
||||
// to another id
|
||||
//
|
||||
// - LeeK's solution in 7.7 was to look for the id/guid in the content cache "on demand" via
|
||||
// XPath, which is probably fast enough but cannot deal with media ids + it maintains a
|
||||
// separate, duplicate cache
|
||||
// see https://github.com/umbraco/Umbraco-CMS/pull/2398
|
||||
//
|
||||
// - Andy's solution in a package was to prefetch all by sql; it cannot prefecth reserved ids
|
||||
// as we don't know the corresponding object type, but that's not a big issue - but then we
|
||||
// have a full database query on startup
|
||||
// see https://github.com/AndyButland/UmbracoUdiToIdCache
|
||||
//
|
||||
// - the original IdkMap implementation that was used by services, did a database lookup on
|
||||
// each cache miss, which is fine enough for services, but would be really slow at content
|
||||
// cache level
|
||||
//
|
||||
// - cache is cleared by MediaCacheRefresher, UnpublishedPageCacheRefresher, and other
|
||||
// refreshers - because id/guid map is unique, we only clear to avoid leaking memory, 'cos
|
||||
// we don't risk caching obsolete values - and only when actually deleting
|
||||
//
|
||||
// so...
|
||||
//
|
||||
// - there's a single caching point, and it's idkMap
|
||||
// - there are no "helper methods" - the published content cache itself knows about Guids
|
||||
// - when the published content cache is instanciated, it populates the idkMap with what it knows
|
||||
// and it registers a way for the idkMap to look for id/keys in the published content cache
|
||||
// - we do NOT prefetch anything from database
|
||||
//
|
||||
// - NuCache maintains its own id/guid map for content & media items
|
||||
// it does *not* populate the idk map, because it directly uses its own map
|
||||
// still, it provides mappers so that the idk map can benefit from them
|
||||
// which means there will be some double-caching at some point ??
|
||||
//
|
||||
// - when a request comes in:
|
||||
// the published content cache uses the idkMap to map id/key
|
||||
// if the idkMap already knows about the map, it returns the value
|
||||
// else it tries the published cache via XPath
|
||||
// else it tries the published cache via mappers
|
||||
// else it hits the database
|
||||
|
||||
|
||||
private readonly ConcurrentDictionary<UmbracoObjectTypes, (Func<int, Guid> id2key, Func<Guid, int> key2id)> _dictionary
|
||||
= new ConcurrentDictionary<UmbracoObjectTypes, (Func<int, Guid> id2key, Func<Guid, int> key2id)>();
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ namespace Umbraco.Tests.Scoping
|
||||
runtimeStateMock.Object,
|
||||
ServiceContext,
|
||||
contentTypeFactory,
|
||||
null,
|
||||
publishedSnapshotAccessor,
|
||||
Logger,
|
||||
ScopeProvider,
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Xml;
|
||||
using Umbraco.Core.Xml.XPath;
|
||||
using Umbraco.Web.PublishedCache.NuCache.Navigable;
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
private readonly ConcurrentDictionary<int, LinkedNode<object>> _contentRootNodes;
|
||||
private readonly ConcurrentDictionary<int, LinkedNode<PublishedContentType>> _contentTypesById;
|
||||
private readonly ConcurrentDictionary<string, LinkedNode<PublishedContentType>> _contentTypesByAlias;
|
||||
private readonly ConcurrentDictionary<Guid, int> _xmap;
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private BPlusTree<int, ContentNodeKit> _localDb;
|
||||
@@ -52,6 +53,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
_contentRootNodes = new ConcurrentDictionary<int, LinkedNode<object>>();
|
||||
_contentTypesById = new ConcurrentDictionary<int, LinkedNode<PublishedContentType>>();
|
||||
_contentTypesByAlias = new ConcurrentDictionary<string, LinkedNode<PublishedContentType>>(StringComparer.InvariantCultureIgnoreCase);
|
||||
_xmap = new ConcurrentDictionary<Guid, int>();
|
||||
|
||||
_genRefRefs = new ConcurrentQueue<GenRefRef>();
|
||||
_genRefRef = null; // no initial gen exists
|
||||
@@ -477,6 +479,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
RemoveFromParentLocked(existing);
|
||||
AddToParentLocked(kit.Node);
|
||||
}
|
||||
|
||||
_xmap[kit.Node.Uid] = kit.Node.Id;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -504,6 +508,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
SetValueLocked(_contentNodes, kit.Node.Id, kit.Node);
|
||||
if (_localDb != null) RegisterChange(kit.Node.Id, kit);
|
||||
AddToParentLocked(kit.Node);
|
||||
|
||||
_xmap[kit.Node.Uid] = kit.Node.Id;
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -537,6 +543,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
SetValueLocked(_contentNodes, kit.Node.Id, kit.Node);
|
||||
if (_localDb != null) RegisterChange(kit.Node.Id, kit);
|
||||
AddToParentLocked(kit.Node);
|
||||
|
||||
_xmap[kit.Node.Uid] = kit.Node.Id;
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -576,8 +584,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
|
||||
private void ClearBranchLocked(int id)
|
||||
{
|
||||
LinkedNode<ContentNode> link;
|
||||
_contentNodes.TryGetValue(id, out link);
|
||||
_contentNodes.TryGetValue(id, out var link);
|
||||
if (link?.Value == null)
|
||||
return;
|
||||
ClearBranchLocked(link.Value);
|
||||
@@ -588,6 +595,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
SetValueLocked(_contentNodes, content.Id, null);
|
||||
if (_localDb != null) RegisterChange(content.Id, ContentNodeKit.Null);
|
||||
|
||||
_xmap.TryRemove(content.Uid, out _);
|
||||
|
||||
foreach (var childId in content.ChildContentIds)
|
||||
{
|
||||
if (_contentNodes.TryGetValue(childId, out LinkedNode<ContentNode> link) == false || link.Value == null) continue;
|
||||
@@ -597,8 +606,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
|
||||
private LinkedNode<ContentNode> GetParentLink(ContentNode content)
|
||||
{
|
||||
LinkedNode<ContentNode> link;
|
||||
_contentNodes.TryGetValue(content.ParentContentId, out link); // else null
|
||||
_contentNodes.TryGetValue(content.ParentContentId, out var link); // else null
|
||||
//if (link == null || link.Value == null)
|
||||
// throw new Exception("Panic: parent not found.");
|
||||
return link;
|
||||
@@ -709,6 +717,13 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
return GetValue(_contentNodes, id, gen);
|
||||
}
|
||||
|
||||
public ContentNode Get(Guid uid, long gen)
|
||||
{
|
||||
return _xmap.TryGetValue(uid, out var id)
|
||||
? GetValue(_contentNodes, id, gen)
|
||||
: null;
|
||||
}
|
||||
|
||||
public IEnumerable<ContentNode> GetAtRoot(long gen)
|
||||
{
|
||||
// look ma, no lock!
|
||||
@@ -1099,8 +1114,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
{
|
||||
if (_gen < 0)
|
||||
throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/);
|
||||
// fixme - optimize with an index - getAll/iterating is expensive
|
||||
return _store.GetAll(_gen).FirstOrDefault(x => x.Uid == id);
|
||||
return _store.Get(id, _gen);
|
||||
}
|
||||
|
||||
public IEnumerable<ContentNode> GetAtRoot()
|
||||
@@ -1110,6 +1124,13 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
return _store.GetAtRoot(_gen);
|
||||
}
|
||||
|
||||
public IEnumerable<ContentNode> GetAll()
|
||||
{
|
||||
if (_gen < 0)
|
||||
throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/);
|
||||
return _store.GetAll(_gen);
|
||||
}
|
||||
|
||||
public PublishedContentType GetContentType(int id)
|
||||
{
|
||||
if (_gen < 0)
|
||||
|
||||
@@ -16,18 +16,9 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
base.Compose(composition);
|
||||
|
||||
// register the NuCache published snapshot service
|
||||
composition.SetPublishedSnapshotService(factory => new PublishedSnapshotService(
|
||||
new PublishedSnapshotService.Options(),
|
||||
factory.GetInstance<MainDom>(),
|
||||
factory.GetInstance<IRuntimeState>(),
|
||||
factory.GetInstance<ServiceContext>(),
|
||||
factory.GetInstance<IPublishedContentTypeFactory>(),
|
||||
factory.GetInstance<IPublishedSnapshotAccessor>(),
|
||||
factory.GetInstance<ILogger>(),
|
||||
factory.GetInstance<IScopeProvider>(),
|
||||
factory.GetInstance<IDocumentRepository>(),
|
||||
factory.GetInstance<IMediaRepository>(),
|
||||
factory.GetInstance<IMemberRepository>()));
|
||||
// must register default options, required in the service ctor
|
||||
composition.Container.Register(factory => new PublishedSnapshotService.Options());
|
||||
composition.SetPublishedSnapshotService<PublishedSnapshotService>();
|
||||
|
||||
// add the NuCache health check (hidden from type finder)
|
||||
// todo - no NuCache health check yet
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
//private static int _singletonCheck;
|
||||
|
||||
public PublishedSnapshotService(Options options, MainDom mainDom, IRuntimeState runtime,
|
||||
ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory,
|
||||
ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IdkMap idkMap,
|
||||
IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, IScopeProvider scopeProvider,
|
||||
IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository)
|
||||
: base(publishedSnapshotAccessor)
|
||||
@@ -148,6 +148,12 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
_domainStore = new SnapDictionary<int, Domain>();
|
||||
|
||||
LoadCaches();
|
||||
|
||||
if (idkMap != null)
|
||||
{
|
||||
idkMap.SetMapper(UmbracoObjectTypes.Document, id => _contentStore.LiveSnapshot.Get(id).Uid, uid => _contentStore.LiveSnapshot.Get(uid).Id);
|
||||
idkMap.SetMapper(UmbracoObjectTypes.Media, id => _mediaStore.LiveSnapshot.Get(id).Uid, uid => _mediaStore.LiveSnapshot.Get(uid).Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadCaches()
|
||||
|
||||
Reference in New Issue
Block a user