From c8052def7a57a4c2a5e247573dc0355db19aaa1c Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 17 Jul 2017 10:48:48 +0200 Subject: [PATCH] NuCache+Scope add scope lock to ContentStore --- .../PublishedCache/NuCache/ContentNodeKit.cs | 4 + .../PublishedCache/NuCache/ContentStore.cs | 452 +++++++++++------- .../PublishedCache/NuCache/FacadeService.cs | 45 +- 3 files changed, 295 insertions(+), 206 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs index 83d2023632..7d0c0bbbef 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs @@ -13,6 +13,10 @@ namespace Umbraco.Web.PublishedCache.NuCache public bool IsEmpty => Node == null; + public bool IsNull => ContentTypeId < 0; + + public static ContentNodeKit Null { get; } = new ContentNodeKit { ContentTypeId = -1 }; + public void Build(PublishedContentType contentType, IFacadeAccessor facadeAccessor) { Node.SetContentTypeAndData(contentType, DraftData, PublishedData, facadeAccessor); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 16f0b0307f..0cb673f3e9 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using CSharpTest.Net.Collections; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Scoping; namespace Umbraco.Web.PublishedCache.NuCache { @@ -22,7 +23,6 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly ConcurrentDictionary> _contentRootNodes; private readonly ConcurrentDictionary> _contentTypesById; private readonly ConcurrentDictionary> _contentTypesByAlias; - private readonly Dictionary> _contentTypeNodes; private readonly ILogger _logger; private BPlusTree _localDb; @@ -34,6 +34,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private bool _nextGen, _collectAuto; private Task _collectTask; private volatile int _wlocked; + private List> _wchanges; // fixme - collection trigger (ok for now) // see SnapDictionary notes @@ -51,7 +52,6 @@ namespace Umbraco.Web.PublishedCache.NuCache _contentRootNodes = new ConcurrentDictionary>(); _contentTypesById = new ConcurrentDictionary>(); _contentTypesByAlias = new ConcurrentDictionary>(StringComparer.InvariantCultureIgnoreCase); - _contentTypeNodes = new Dictionary>(); _genRefRefs = new ConcurrentQueue(); _genRefRef = null; // no initial gen exists @@ -64,102 +64,70 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Locking - public void WriteLocked(Action action) + // see notes on SnapDictionary + + private readonly string _instanceId = Guid.NewGuid().ToString("N"); + + private class ReadLockInfo { - var wtaken = false; - var wcount = false; - try + public bool Taken; + } + + private class WriteLockInfo + { + public bool Taken; + public bool Count; + } + + // a scope contextual that represents a locked writer to the dictionary + private class ContentStoreWriter : ScopeContextualBase + { + private readonly WriteLockInfo _lockinfo = new WriteLockInfo(); + private ContentStore _store; + + public ContentStoreWriter(ContentStore store, bool scoped) { - Monitor.Enter(_wlocko, ref wtaken); - - var rtaken = false; - try - { - Monitor.Enter(_rlocko, ref rtaken); - - // see SnapDictionary - try - { } - finally - { - _wlocked++; - wcount = true; - if (_nextGen == false) - { - // because we are changing things, a new generation - // is created, which will trigger a new snapshot - _nextGen = true; - _liveGen += 1; - } - } - } - finally - { - if (rtaken) Monitor.Exit(_rlocko); - } - - action(); + _store = store; + store.Lock(_lockinfo, scoped); } - finally + + public override void Release(bool completed) { - if (wcount) _wlocked--; - if (wtaken) Monitor.Exit(_wlocko); + if (_store== null) return; + _store.Release(_lockinfo, completed); + _store = null; } } - public T WriteLocked(Func func) + // gets a scope contextual representing a locked writer to the dictionary + // fixme GetScopedWriter? should the dict have a ref onto the scope provider? + public IDisposable GetWriter(IScopeProvider scopeProvider) { - var wtaken = false; - var wcount = false; - try - { - Monitor.Enter(_wlocko, ref wtaken); - - var rtaken = false; - try - { - Monitor.Enter(_rlocko, ref rtaken); - - try - { } - finally - { - _wlocked++; - wcount = true; - if (_nextGen == false) - { - // because we are changing things, a new generation - // is created, which will trigger a new snapshot - _nextGen = true; - _liveGen += 1; - } - } - } - finally - { - if (rtaken) Monitor.Exit(_rlocko); - } - - return func(); - } - finally - { - if (wcount) _wlocked--; - if (wtaken) Monitor.Exit(_wlocko); - } + return ScopeContextualBase.Get(scopeProvider, _instanceId, scoped => new ContentStoreWriter(this, scoped)); } - private T ReadLocked(Func func) + private void Lock(WriteLockInfo lockInfo, bool forceGen = false) { + Monitor.Enter(_wlocko, ref lockInfo.Taken); + var rtaken = false; try { Monitor.Enter(_rlocko, ref rtaken); - // we have rlock, so it cannot ++ - // it could -- though, so... volatile - var wlocked = _wlocked > 0; - return func(wlocked); + // see SnapDictionary + try { } finally + { + _wlocked++; + lockInfo.Count = true; + if (_nextGen == false || (forceGen && _wlocked == 1)) // if true already... ok to have "holes" in generation objects + { + // because we are changing things, a new generation + // is created, which will trigger a new snapshot + _nextGen = true; + _liveGen += 1; + } + } } finally { @@ -167,18 +135,100 @@ namespace Umbraco.Web.PublishedCache.NuCache } } + private void Lock(ReadLockInfo lockInfo) + { + Monitor.Enter(_rlocko, ref lockInfo.Taken); + } + + private void Release(WriteLockInfo lockInfo, bool commit = true) + { + if (commit == false) + { + var rtaken = false; + try + { + Monitor.Enter(_rlocko, ref rtaken); + try { } + finally + { + _nextGen = false; + _liveGen -= 1; + } + } + finally + { + if (rtaken) Monitor.Exit(_rlocko); + } + + Rollback(_contentNodes); + Rollback(_contentRootNodes); + Rollback(_contentTypesById); + Rollback(_contentTypesByAlias); + } + else if (_localDb != null && _wchanges != null) + { + foreach (var change in _wchanges) + { + if (change.Value.IsNull) + _localDb.TryRemove(change.Key, out ContentNodeKit unused); + else + _localDb[change.Key] = change.Value; + } + _wchanges = null; + _localDb.Commit(); + } + + if (lockInfo.Count) _wlocked--; + if (lockInfo.Taken) Monitor.Exit(_wlocko); + } + + private void Release(ReadLockInfo lockInfo) + { + if (lockInfo.Taken) Monitor.Exit(_rlocko); + } + + private void Rollback(ConcurrentDictionary> dictionary) + where TValue : class + { + foreach (var item in dictionary) + { + var link = item.Value; + if (link.Gen <= _liveGen) continue; + + var key = item.Key; + if (link.Next == null) + dictionary.TryRemove(key, out link); + else + dictionary.TryUpdate(key, link.Next, link); + } + + } + #endregion #region LocalDb public void ReleaseLocalDb() { - WriteLocked(() => + var lockInfo = new WriteLockInfo(); + try { + Lock(lockInfo); + if (_localDb == null) return; _localDb.Dispose(); _localDb = null; - }); + } + finally + { + Release(lockInfo); + } + } + + private void RegisterChange(int id, ContentNodeKit kit) + { + if (_wchanges == null) _wchanges = new List>(); + _wchanges.Add(new KeyValuePair(id, kit)); } #endregion @@ -187,97 +237,119 @@ namespace Umbraco.Web.PublishedCache.NuCache public void UpdateContentTypes(IEnumerable removedIds, IEnumerable refreshedTypes, IEnumerable kits) { - removedIds = removedIds ?? Enumerable.Empty(); - refreshedTypes = refreshedTypes ?? Enumerable.Empty(); - kits = kits ?? new ContentNodeKit[0]; + var removedIdsA = removedIds?.ToArray() ?? Array.Empty(); + var refreshedTypesA = refreshedTypes?.ToArray() ?? Array.Empty(); + var refreshedIdsA = refreshedTypesA.Select(x => x.Id).ToArray(); + kits = kits ?? Array.Empty(); - WriteLocked(() => + var lockInfo = new WriteLockInfo(); + try { - foreach (var id in removedIds) - { - // all content should have been deleted - but - if (_contentTypeNodes.ContainsKey(id)) - { - foreach (var node in _contentTypeNodes[id]) - ClearBranchLocked(node); - _contentTypeNodes.Remove(id); - } + Lock(lockInfo); + var removedContentTypeNodes = new List(); + var refreshedContentTypeNodes = new List(); + + foreach (var link in _contentNodes.Values) + { + var node = link.Value; + if (node == null) continue; + var contentTypeId = node.ContentType.Id; + if (removedIdsA.Contains(contentTypeId)) removedContentTypeNodes.Add(node.Id); + if (refreshedIdsA.Contains(contentTypeId)) refreshedContentTypeNodes.Add(node.Id); + } + + // all content should have been deleted - but + foreach (var node in removedContentTypeNodes) + ClearBranchLocked(node); + + foreach (var id in removedIdsA) + { if (_contentTypesById.TryGetValue(id, out LinkedNode link) == false || link.Value == null) continue; SetValueLocked(_contentTypesById, id, null); SetValueLocked(_contentTypesByAlias, link.Value.Alias, null); } - var temp = new Dictionary>(); - - foreach (var type in refreshedTypes) + foreach (var type in refreshedTypesA) { - if (_contentTypeNodes.ContainsKey(type.Id) == false) - _contentTypeNodes[type.Id] = new HashSet(); - SetValueLocked(_contentTypesById, type.Id, type); SetValueLocked(_contentTypesByAlias, type.Alias, type); - - temp.Add(type.Id, new HashSet(_contentTypeNodes[type.Id])); } // skip missing type // skip missing parents & unbuildable kits - what else could we do? + var visited = new List(); foreach (var kit in kits.Where(x => - temp.ContainsKey(x.ContentTypeId) && + refreshedIdsA.Contains(x.ContentTypeId) && ParentExistsLocked(x) && BuildKit(x))) { SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); - if (_localDb != null) - _localDb[kit.Node.Id] = kit; - temp[kit.ContentTypeId].Remove(kit.Node.Id); + visited.Add(kit.Node.Id); + if (_localDb != null) RegisterChange(kit.Node.Id, kit); } // all content should have been refreshed - but... - foreach (var id in temp.Values.SelectMany(x => x)) - ClearBranchLocked(id); - _localDb?.Commit(); - }); + var orphans = refreshedContentTypeNodes.Except(visited); + foreach (var id in orphans) + ClearBranchLocked(id); + } + finally + { + Release(lockInfo); + } } public void UpdateDataTypes(IEnumerable dataTypeIds, Func getContentType) { - WriteLocked(() => + var lockInfo = new WriteLockInfo(); + try { + Lock(lockInfo); + var contentTypes = _contentTypesById .Where(kvp => kvp.Value.Value != null && kvp.Value.Value.PropertyTypes.Any(p => dataTypeIds.Contains(p.DataTypeId))) .Select(kvp => kvp.Value.Value) - .Select(x => getContentType(x.Id)); + .Select(x => getContentType(x.Id)) + .Where(x => x != null) // poof, gone, very unlikely and probably an anomaly + .ToArray(); + + var contentTypeIdsA = contentTypes.Select(x => x.Id).ToArray(); + var contentTypeNodes = new Dictionary>(); + foreach (var id in contentTypeIdsA) + contentTypeNodes[id] = new List(); + foreach (var link in _contentNodes.Values) + { + var node = link.Value; + if (node != null && contentTypeIdsA.Contains(node.ContentType.Id)) + contentTypeNodes[node.ContentType.Id].Add(node.Id); + } foreach (var contentType in contentTypes) { - // poof, gone, very unlikely and probably an anomaly - if (contentType == null) - continue; - // again, weird situation - if (_contentTypeNodes.ContainsKey(contentType.Id) == false) + if (contentTypeNodes.ContainsKey(contentType.Id) == false) continue; - foreach (var id in _contentTypeNodes[contentType.Id]) + foreach (var id in contentTypeNodes[contentType.Id]) { _contentNodes.TryGetValue(id, out LinkedNode link); if (link?.Value == null) continue; var node = new ContentNode(link.Value, contentType, _facadeAccessor); SetValueLocked(_contentNodes, id, node); - if (_localDb != null) - _localDb[id] = node.ToKit(); + if (_localDb != null) RegisterChange(id, node.ToKit()); } } - - _localDb?.Commit(); - }); + } + finally + { + Release(lockInfo); + } } private bool BuildKit(ContentNodeKit kit) @@ -286,31 +358,16 @@ namespace Umbraco.Web.PublishedCache.NuCache if (kit.DraftData == null && kit.PublishedData == null) return false; - // unknown = bad if (_contentTypesById.TryGetValue(kit.ContentTypeId, out LinkedNode link) == false || link.Value == null) return false; - // not checking ByAlias, assuming we don't have internal errors - - // register - if (_contentTypeNodes.ContainsKey(kit.ContentTypeId) == false) - _contentTypeNodes[kit.ContentTypeId] = new HashSet(); - _contentTypeNodes[kit.ContentTypeId].Add(kit.Node.Id); - // and use kit.Build(link.Value, _facadeAccessor); return true; } - private void ReleaseContentTypeLocked(ContentNode content) - { - if (_contentTypeNodes.ContainsKey(content.ContentType.Id) == false) - return; // though, ?! - _contentTypeNodes[content.ContentType.Id].Remove(content.Id); - } - #endregion #region Set, Clear, Get @@ -335,8 +392,11 @@ namespace Umbraco.Web.PublishedCache.NuCache _logger.Debug("Set content ID:" + kit.Node.Id); - WriteLocked(() => + var lockInfo = new WriteLockInfo(); + try { + Lock(lockInfo); + // get existing _contentNodes.TryGetValue(kit.Node.Id, out LinkedNode link); var existing = link?.Value; @@ -354,8 +414,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // set SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); - if (_localDb != null) - _localDb[kit.Node.Id] = kit; + if (_localDb != null) RegisterChange(kit.Node.Id, kit); // manage the tree if (existing == null) @@ -369,15 +428,20 @@ namespace Umbraco.Web.PublishedCache.NuCache RemoveFromParentLocked(existing); AddToParentLocked(kit.Node); } - - _localDb?.Commit(); - }); + } + finally + { + Release(lockInfo); + } } public void SetAll(IEnumerable kits) { - WriteLocked(() => + var lockInfo = new WriteLockInfo(); + try { + Lock(lockInfo); + ClearLocked(_contentNodes); ClearLocked(_contentRootNodes); @@ -389,19 +453,23 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var kit in kits.Where(x => ParentExistsLocked(x) && BuildKit(x))) { SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); - if (_localDb != null) - _localDb[kit.Node.Id] = kit; + if (_localDb != null) RegisterChange(kit.Node.Id, kit); AddToParentLocked(kit.Node); } - - _localDb?.Commit(); - }); + } + finally + { + Release(lockInfo); + } } public void SetBranch(int rootContentId, IEnumerable kits) { - WriteLocked(() => + var lockInfo = new WriteLockInfo(); + try { + Lock(lockInfo); + // get existing _contentNodes.TryGetValue(rootContentId, out LinkedNode link); var existing = link?.Value; @@ -418,19 +486,23 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var kit in kits.Where(x => ParentExistsLocked(x) && BuildKit(x))) { SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); - if (_localDb != null) - _localDb[kit.Node.Id] = kit; + if (_localDb != null) RegisterChange(kit.Node.Id, kit); AddToParentLocked(kit.Node); } - - _localDb?.Commit(); - }); + } + finally + { + Release(lockInfo); + } } public bool Clear(int id) { - return WriteLocked(() => + var lockInfo = new WriteLockInfo(); + try { + Lock(lockInfo); + // try to find the content // if it is not there, nothing to do _contentNodes.TryGetValue(id, out LinkedNode link); // else null @@ -446,7 +518,11 @@ namespace Umbraco.Web.PublishedCache.NuCache RemoveFromParentLocked(content); return true; - }); + } + finally + { + Release(lockInfo); + } } private void ClearBranchLocked(int id) @@ -461,8 +537,8 @@ namespace Umbraco.Web.PublishedCache.NuCache private void ClearBranchLocked(ContentNode content) { SetValueLocked(_contentNodes, content.Id, null); - _localDb?.TryRemove(content.Id, out ContentNodeKit unused); - ReleaseContentTypeLocked(content); + if (_localDb != null) RegisterChange(content.Id, ContentNodeKit.Null); + foreach (var childId in content.ChildContentIds) { if (_contentNodes.TryGetValue(childId, out LinkedNode link) == false || link.Value == null) continue; @@ -564,22 +640,19 @@ namespace Umbraco.Web.PublishedCache.NuCache private void ClearLocked(ConcurrentDictionary> dict) where TValue : class { - WriteLocked(() => + // this is safe only because we're write-locked + foreach (var kvp in dict.Where(x => x.Value != null)) { - // this is safe only because we're write-locked - foreach (var kvp in dict.Where(x => x.Value != null)) + if (kvp.Value.Gen < _liveGen) { - if (kvp.Value.Gen < _liveGen) - { - var link = new LinkedNode(null, _liveGen, kvp.Value); - dict.TryUpdate(kvp.Key, link, kvp.Value); - } - else - { - kvp.Value.Value = null; - } + var link = new LinkedNode(null, _liveGen, kvp.Value); + dict.TryUpdate(kvp.Key, link, kvp.Value); } - }); + else + { + kvp.Value.Value = null; + } + } } public ContentNode Get(int id, long gen) @@ -671,8 +744,11 @@ namespace Umbraco.Web.PublishedCache.NuCache public Snapshot CreateSnapshot() { - return ReadLocked(wlocked => + var lockInfo = new ReadLockInfo(); + try { + Lock(lockInfo); + // if no next generation is required, and we already have one, // use it and create a new snapshot if (_nextGen == false && _genRefRef != null) @@ -685,7 +761,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // else we need to try to create a new gen ref // whether we are wlocked or not, noone can rlock while we do, // so _liveGen and _nextGen are safe - if (wlocked) + if (_wlocked > 0) // volatile, cannot ++ but could -- { // write-locked, cannot use latest gen (at least 1) so use previous var snapGen = _nextGen ? _liveGen - 1 : _liveGen; @@ -723,7 +799,11 @@ namespace Umbraco.Web.PublishedCache.NuCache CollectAsyncLocked(); return snapshot; - }); + } + finally + { + Release(lockInfo); + } } public Task CollectAsync() @@ -759,8 +839,7 @@ namespace Umbraco.Web.PublishedCache.NuCache #if DEBUG _logger.Debug("Collect."); #endif - GenRefRef genRefRef; - while (_genRefRefs.TryPeek(out genRefRef) && (genRefRef.Count == 0 || genRefRef.WGenRef.IsAlive == false)) + while (_genRefRefs.TryPeek(out GenRefRef genRefRef) && (genRefRef.Count == 0 || genRefRef.WGenRef.IsAlive == false)) { _genRefRefs.TryDequeue(out genRefRef); // cannot fail since TryPeek has succeeded _floorGen = genRefRef.Gen; @@ -868,6 +947,8 @@ namespace Umbraco.Web.PublishedCache.NuCache set => _store._collectAuto = value; } + public Snapshot LiveSnapshot => new Snapshot(_store, _store._liveGen); + public Tuple[] GetValues(int id) { _store._contentNodes.TryGetValue(id, out LinkedNode link); // else null @@ -939,6 +1020,12 @@ namespace Umbraco.Web.PublishedCache.NuCache #endif } + internal Snapshot(ContentStore store, long gen) + { + _store = store; + _gen = gen; + } + public ContentNode Get(int id) { if (_gen < 0) @@ -1013,7 +1100,8 @@ namespace Umbraco.Web.PublishedCache.NuCache _logger.Debug("Dispose snapshot (" + _genRef.GenRefRef.Count + ")."); #endif _gen = -1; - Interlocked.Decrement(ref _genRef.GenRefRef.Count); + if (_genRef != null) + Interlocked.Decrement(ref _genRef.GenRefRef.Count); GC.SuppressFinalize(this); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/FacadeService.cs b/src/Umbraco.Web/PublishedCache/NuCache/FacadeService.cs index 8020cf5098..9bb35bcbd4 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/FacadeService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/FacadeService.cs @@ -228,7 +228,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private void LockAndLoadContent(Action action) { - _contentStore.WriteLocked(() => + using (_contentStore.GetWriter(_scopeProvider)) { using (var uow = _uowProvider.CreateUnitOfWork()) { @@ -236,7 +236,7 @@ namespace Umbraco.Web.PublishedCache.NuCache action(uow); uow.Complete(); } - }); + } } private void LoadContentFromDatabaseLocked(IScopeUnitOfWork uow) @@ -298,7 +298,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private void LockAndLoadMedia(Action action) { - _mediaStore.WriteLocked(() => + using (_mediaStore.GetWriter(_scopeProvider)) { using (var uow = _uowProvider.CreateUnitOfWork()) { @@ -306,7 +306,7 @@ namespace Umbraco.Web.PublishedCache.NuCache action(uow); uow.Complete(); } - }); + } } private void LoadMediaFromDatabaseLocked(IScopeUnitOfWork uow) @@ -481,14 +481,12 @@ namespace Umbraco.Web.PublishedCache.NuCache return; } - var draftChanged2 = false; - var publishedChanged2 = false; - _contentStore.WriteLocked(() => + using (_contentStore.GetWriter(_scopeProvider)) { - NotifyLocked(payloads, out draftChanged2, out publishedChanged2); - }); - draftChanged = draftChanged2; - publishedChanged = publishedChanged2; + NotifyLocked(payloads, out bool draftChanged2, out bool publishedChanged2); + draftChanged = draftChanged2; + publishedChanged = publishedChanged2; + } if (draftChanged || publishedChanged) ((Facade)CurrentFacade).Resync(); @@ -581,12 +579,11 @@ namespace Umbraco.Web.PublishedCache.NuCache return; } - var anythingChanged2 = false; - _mediaStore.WriteLocked(() => + using (_mediaStore.GetWriter(_scopeProvider)) { - NotifyLocked(payloads, out anythingChanged2); - }); - anythingChanged = anythingChanged2; + NotifyLocked(payloads, out bool anythingChanged2); + anythingChanged = anythingChanged2; + } if (anythingChanged) ((Facade)CurrentFacade).Resync(); @@ -686,12 +683,12 @@ namespace Umbraco.Web.PublishedCache.NuCache .ToArray(); if (removedIds.Length > 0 || refreshedIds.Length > 0) - _contentStore.WriteLocked(() => + using (_contentStore.GetWriter(_scopeProvider)) { // ReSharper disable AccessToModifiedClosure RefreshContentTypesLocked(removedIds, refreshedIds); // ReSharper restore AccessToModifiedClosure - }); + } // same for media cache @@ -706,10 +703,10 @@ namespace Umbraco.Web.PublishedCache.NuCache .ToArray(); if (removedIds.Length > 0 || refreshedIds.Length > 0) - _mediaStore.WriteLocked(() => + using (_mediaStore.GetWriter(_scopeProvider)) { RefreshMediaTypesLocked(removedIds, refreshedIds); - }); + } ((Facade)CurrentFacade).Resync(); } @@ -725,9 +722,9 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var payload in payloads) _logger.Debug($"Notified {(payload.Removed ? "Removed" : "Refreshed")} for data type {payload.Id}"); - _contentStore.WriteLocked(() => - _mediaStore.WriteLocked(() => - { + using (_contentStore.GetWriter(_scopeProvider)) + using (_mediaStore.GetWriter(_scopeProvider)) + { var contentService = _serviceContext.ContentService as ContentService; if (contentService == null) throw new Exception("oops"); @@ -747,7 +744,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _mediaStore.UpdateDataTypes(idsA, id => CreateContentType(PublishedItemType.Media, id)); uow.Complete(); } - })); + } ((Facade) CurrentFacade).Resync(); }