Wire NuCache and IdkMap

This commit is contained in:
Stephan
2018-03-30 14:00:44 +02:00
parent c75d4e5b5b
commit f89816111b
6 changed files with 49 additions and 44 deletions

View File

@@ -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)>();

View File

@@ -85,6 +85,7 @@ namespace Umbraco.Tests.Scoping
runtimeStateMock.Object,
ServiceContext,
contentTypeFactory,
null,
publishedSnapshotAccessor,
Logger,
ScopeProvider,

View File

@@ -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;

View File

@@ -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)

View File

@@ -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

View File

@@ -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()