New IPublishedSnapshotStatus, reduces IPublishedSnapshotService

This commit is contained in:
Shannon
2020-12-21 17:41:12 +11:00
parent e8f5aa8ebc
commit c761fa0506
10 changed files with 146 additions and 117 deletions

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Umbraco.Web.Cache;
namespace Umbraco.Web.PublishedCache
@@ -102,12 +103,9 @@ namespace Umbraco.Web.PublishedCache
/// <param name="payloads">The changes.</param>
void Notify(DomainCacheRefresher.JsonPayload[] payloads);
// TODO: This is weird, why is this is this a thing? Maybe IPublishedSnapshotStatus?
string GetStatus();
// TODO: This is weird, why is this is this a thing? Maybe IPublishedSnapshotStatus?
string StatusUrl { get; }
void Collect();
/// <summary>
/// Cleans up unused snapshots
/// </summary>
Task CollectAsync();
}
}

View File

@@ -0,0 +1,18 @@
namespace Umbraco.Web.PublishedCache
{
/// <summary>
/// Returns the currents status for nucache
/// </summary>
public interface IPublishedSnapshotStatus
{
/// <summary>
/// Gets the status report as a string
/// </summary>
string GetStatus();
/// <summary>
/// Gets the URL used to retreive the status
/// </summary>
string StatusUrl { get; }
}
}

View File

@@ -1352,7 +1352,9 @@ namespace Umbraco.Web.PublishedCache.NuCache
// reading _floorGen is safe if _collectTask is null
if (_collectTask == null && _collectAuto && _liveGen - _floorGen > CollectMinGenDelta)
{
CollectAsyncLocked();
}
return snapshot;
}
@@ -1374,8 +1376,17 @@ namespace Umbraco.Web.PublishedCache.NuCache
private Task CollectAsyncLocked()
{
// NOTE: What in the heck is going on here? Why is any of this running in async contexts?
// SD: From what I can tell this was designed to be a set and forget background task to do the
// collecting which is why it's called from non-async methods within this class. This is
// slightly dangerous because it's not taking into account app shutdown.
// TODO: There should be a different method or class responsible for executing the cleanup on a
// background (set and forget) thread.
if (_collectTask != null)
{
return _collectTask;
}
// ReSharper disable InconsistentlySynchronizedField
var task = _collectTask = Task.Run((Action)Collect);

View File

@@ -27,6 +27,10 @@ namespace Umbraco.Web.PublishedCache.NuCache
builder.Services.AddTransient(factory => new PublishedSnapshotServiceOptions());
builder.SetPublishedSnapshotService<PublishedSnapshotService>();
// Add as itself
builder.Services.AddSingleton<PublishedSnapshotService>();
builder.Services.AddSingleton<IPublishedSnapshotStatus, PublishedSnapshotStatus>();
// replace this service since we want to improve the content/media
// mapping lookups if we are using nucache.
// TODO: Gotta wonder how much this does actually improve perf? It's a lot of weird code to make this happen so hope it's worth it

View File

@@ -94,14 +94,26 @@ namespace Umbraco.Infrastructure.PublishedCache.Persistence
/// <inheritdoc/>
public bool VerifyContentDbCache()
=> _repository.VerifyContentDbCache();
{
using IScope scope = ScopeProvider.CreateScope(autoComplete: true);
scope.ReadLock(Constants.Locks.ContentTree);
return _repository.VerifyContentDbCache();
}
/// <inheritdoc/>
public bool VerifyMediaDbCache()
=> _repository.VerifyMediaDbCache();
{
using IScope scope = ScopeProvider.CreateScope(autoComplete: true);
scope.ReadLock(Constants.Locks.MediaTree);
return _repository.VerifyMediaDbCache();
}
/// <inheritdoc/>
public bool VerifyMemberDbCache()
=> _repository.VerifyMemberDbCache();
{
using IScope scope = ScopeProvider.CreateScope(autoComplete: true);
scope.ReadLock(Constants.Locks.MemberTree);
return _repository.VerifyMemberDbCache();
}
}
}

View File

@@ -4,6 +4,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CSharpTest.Net.Collections;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -47,7 +48,6 @@ namespace Umbraco.Web.PublishedCache.NuCache
private readonly IIOHelper _ioHelper;
private readonly NuCacheSettings _config;
// volatile because we read it with no lock
private bool _isReady;
private bool _isReadSet;
private object _isReadyLock;
@@ -264,7 +264,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
/// <summary>
/// Populates the stores
/// </summary>
private void EnsureCaches() => LazyInitializer.EnsureInitialized(
internal void EnsureCaches() => LazyInitializer.EnsureInitialized(
ref _isReady,
ref _isReadSet,
ref _isReadyLock,
@@ -914,7 +914,6 @@ namespace Umbraco.Web.PublishedCache.NuCache
}
// some may be missing - not checking here
return contentTypes.Select(x => _publishedContentTypeFactory.CreateContentType(x)).ToList();
}
@@ -950,7 +949,6 @@ namespace Umbraco.Web.PublishedCache.NuCache
// content (and content types) are read-locked while reading content
// contentStore is wlocked (so readable, only no new views)
// and it can be wlocked by 1 thread only at a time
using (var scope = _scopeProvider.CreateScope())
{
scope.ReadLock(Constants.Locks.ContentTypes);
@@ -989,7 +987,6 @@ namespace Umbraco.Web.PublishedCache.NuCache
// media (and content types) are read-locked while reading media
// mediaStore is wlocked (so readable, only no new views)
// and it can be wlocked by 1 thread only at a time
using (var scope = _scopeProvider.CreateScope())
{
scope.ReadLock(Constants.Locks.MediaTypes);
@@ -1047,7 +1044,6 @@ namespace Umbraco.Web.PublishedCache.NuCache
// nothing like that...
// for elements cache, DictionaryAppCache is a No-No, use something better.
// ie FastDictionaryAppCache (thread safe and all)
ContentStore.Snapshot contentSnap, mediaSnap;
SnapDictionary<int, Domain>.Snapshot domainSnap;
IAppCache elementsCache;
@@ -1059,7 +1055,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
lock (_elementsLock)
{
var scopeContext = _scopeProvider.Context;
IScopeContext scopeContext = _scopeProvider.Context;
if (scopeContext == null)
{
@@ -1089,7 +1085,6 @@ namespace Umbraco.Web.PublishedCache.NuCache
}, int.MaxValue);
}
// create a new snapshot cache if snapshots are different gens
if (contentSnap.Gen != _contentGen || mediaSnap.Gen != _mediaGen || domainSnap.Gen != _domainGen || _elementsCache == null)
{
@@ -1126,75 +1121,12 @@ namespace Umbraco.Web.PublishedCache.NuCache
IReadOnlyCollection<int> memberTypeIds = null)
=> _publishedContentService.Rebuild(groupSize, contentTypeIds, mediaTypeIds, memberTypeIds);
public bool VerifyContentDbCache()
{
// TODO: Shouldn't this entire logic just exist in the call to _publishedContentService?
using (var scope = _scopeProvider.CreateScope())
{
scope.ReadLock(Constants.Locks.ContentTree);
var ok = _publishedContentService.VerifyContentDbCache();
scope.Complete();
return ok;
}
}
public bool VerifyMediaDbCache()
{
// TODO: Shouldn't this entire logic just exist in the call to _publishedContentService?
using (var scope = _scopeProvider.CreateScope())
{
scope.ReadLock(Constants.Locks.MediaTree);
var ok = _publishedContentService.VerifyMediaDbCache();
scope.Complete();
return ok;
}
}
public bool VerifyMemberDbCache()
{
// TODO: Shouldn't this entire logic just exist in the call to _publishedContentService?
using (var scope = _scopeProvider.CreateScope())
{
scope.ReadLock(Constants.Locks.MemberTree);
var ok = _publishedContentService.VerifyMemberDbCache();
scope.Complete();
return ok;
}
}
public string GetStatus()
public async Task CollectAsync()
{
EnsureCaches();
var dbCacheIsOk = VerifyContentDbCache()
&& VerifyMediaDbCache()
&& VerifyMemberDbCache();
var cg = _contentStore.GenCount;
var mg = _mediaStore.GenCount;
var cs = _contentStore.SnapCount;
var ms = _mediaStore.SnapCount;
var ce = _contentStore.Count;
var me = _mediaStore.Count;
return
" Database cache is " + (dbCacheIsOk ? "ok" : "NOT ok (rebuild?)") + "." +
" ContentStore contains " + ce + " item" + (ce > 1 ? "s" : "") +
" and has " + cg + " generation" + (cg > 1 ? "s" : "") +
" and " + cs + " snapshot" + (cs > 1 ? "s" : "") + "." +
" MediaStore contains " + me + " item" + (ce > 1 ? "s" : "") +
" and has " + mg + " generation" + (mg > 1 ? "s" : "") +
" and " + ms + " snapshot" + (ms > 1 ? "s" : "") + ".";
}
// TODO: This should be async since it's calling into async
public void Collect()
{
EnsureCaches();
var contentCollect = _contentStore.CollectAsync();
var mediaCollect = _mediaStore.CollectAsync();
System.Threading.Tasks.Task.WaitAll(contentCollect, mediaCollect);
await _contentStore.CollectAsync();
await _mediaStore.CollectAsync();
}
internal ContentStore GetContentStore()
@@ -1209,9 +1141,6 @@ namespace Umbraco.Web.PublishedCache.NuCache
return _mediaStore;
}
/// <inheritdoc/>
public virtual string StatusUrl => "views/dashboard/settings/publishedsnapshotcache.html";
/// <inheritdoc/>
public void Dispose()
{ }

View File

@@ -0,0 +1,51 @@
using Umbraco.Infrastructure.PublishedCache.Persistence;
namespace Umbraco.Web.PublishedCache.NuCache
{
/// <summary>
/// Generates a status report for <see cref="PublishedSnapshotService"/>
/// </summary>
internal class PublishedSnapshotStatus : IPublishedSnapshotStatus
{
private readonly PublishedSnapshotService _service;
private readonly INuCacheContentService _publishedContentService;
public PublishedSnapshotStatus(PublishedSnapshotService service, INuCacheContentService publishedContentService)
{
_service = service;
_publishedContentService = publishedContentService;
}
/// <inheritdoc/>
public virtual string StatusUrl => "views/dashboard/settings/publishedsnapshotcache.html";
/// <inheritdoc/>
public string GetStatus()
{
_service.EnsureCaches();
var dbCacheIsOk = _publishedContentService.VerifyContentDbCache()
&& _publishedContentService.VerifyMediaDbCache()
&& _publishedContentService.VerifyMemberDbCache();
ContentStore contentStore = _service.GetContentStore();
ContentStore mediaStore = _service.GetMediaStore();
var cg = contentStore.GenCount;
var mg = mediaStore.GenCount;
var cs = contentStore.SnapCount;
var ms = mediaStore.SnapCount;
var ce = contentStore.Count;
var me = mediaStore.Count;
return
" Database cache is " + (dbCacheIsOk ? "ok" : "NOT ok (rebuild?)") + "." +
" ContentStore contains " + ce + " item" + (ce > 1 ? "s" : "") +
" and has " + cg + " generation" + (cg > 1 ? "s" : "") +
" and " + cs + " snapshot" + (cs > 1 ? "s" : "") + "." +
" MediaStore contains " + me + " item" + (ce > 1 ? "s" : "") +
" and has " + mg + " generation" + (mg > 1 ? "s" : "") +
" and " + ms + " snapshot" + (ms > 1 ? "s" : "") + ".";
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Umbraco.Core;
using Umbraco.Core.Cache;
@@ -177,8 +178,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
#endregion
public string StatusUrl => throw new System.NotImplementedException();
#region Xml specific
/// <summary>
@@ -258,13 +257,8 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
#endregion
public string GetStatus()
{
return "Test status";
}
public void Rebuild(int groupSize = 5000, IReadOnlyCollection<int> contentTypeIds = null, IReadOnlyCollection<int> mediaTypeIds = null, IReadOnlyCollection<int> memberTypeIds = null) { }
public void Collect() { }
public Task CollectAsync() => Task.CompletedTask;
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System;
using System.Reflection.Metadata;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Core;
using Umbraco.Web.Cache;
@@ -8,43 +9,54 @@ using Umbraco.Web.PublishedCache;
namespace Umbraco.Web.BackOffice.Controllers
{
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
public class PublishedSnapshotCacheStatusController : UmbracoAuthorizedApiController
{
private readonly IPublishedSnapshotService _publishedSnapshotService;
private readonly IPublishedSnapshotStatus _publishedSnapshotStatus;
private readonly DistributedCache _distributedCache;
public PublishedSnapshotCacheStatusController(IPublishedSnapshotService publishedSnapshotService, DistributedCache distributedCache)
/// <summary>
/// Initializes a new instance of the <see cref="PublishedSnapshotCacheStatusController"/> class.
/// </summary>
public PublishedSnapshotCacheStatusController(
IPublishedSnapshotService publishedSnapshotService,
IPublishedSnapshotStatus publishedSnapshotStatus,
DistributedCache distributedCache)
{
_publishedSnapshotService = publishedSnapshotService ?? throw new ArgumentNullException(nameof(publishedSnapshotService));
_publishedSnapshotStatus = publishedSnapshotStatus;
_distributedCache = distributedCache;
}
/// <summary>
/// Rebuilds the Database cache
/// </summary>
[HttpPost]
public string RebuildDbCache()
{
_publishedSnapshotService.Rebuild();
return _publishedSnapshotService.GetStatus();
return _publishedSnapshotStatus.GetStatus();
}
/// <summary>
/// Gets a status report
/// </summary>
[HttpGet]
public string GetStatus()
{
return _publishedSnapshotService.GetStatus();
}
public string GetStatus() => _publishedSnapshotStatus.GetStatus();
/// <summary>
/// Cleans up unused snapshots
/// </summary>
[HttpGet]
public string Collect()
public async Task<string> Collect()
{
GC.Collect();
_publishedSnapshotService.Collect();
return _publishedSnapshotService.GetStatus();
await _publishedSnapshotService.CollectAsync();
return _publishedSnapshotStatus.GetStatus();
}
[HttpPost]
public void ReloadCache()
{
_distributedCache.RefreshAllPublishedSnapshot();
}
public void ReloadCache() => _distributedCache.RefreshAllPublishedSnapshot();
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Web.PublishedCache;
@@ -6,22 +6,22 @@ namespace Umbraco.Web.BackOffice.Controllers
{
public class PublishedStatusController : UmbracoAuthorizedApiController
{
private readonly IPublishedSnapshotService _publishedSnapshotService;
private readonly IPublishedSnapshotStatus _publishedSnapshotStatus;
public PublishedStatusController(IPublishedSnapshotService publishedSnapshotService)
public PublishedStatusController(IPublishedSnapshotStatus publishedSnapshotStatus)
{
_publishedSnapshotService = publishedSnapshotService ?? throw new ArgumentNullException(nameof(publishedSnapshotService));
_publishedSnapshotStatus = publishedSnapshotStatus ?? throw new ArgumentNullException(nameof(publishedSnapshotStatus));
}
[HttpGet]
public string GetPublishedStatusUrl()
{
if (!string.IsNullOrWhiteSpace(_publishedSnapshotService.StatusUrl))
if (!string.IsNullOrWhiteSpace(_publishedSnapshotStatus.StatusUrl))
{
return _publishedSnapshotService.StatusUrl;
return _publishedSnapshotStatus.StatusUrl;
}
throw new NotSupportedException("Not supported: " + _publishedSnapshotService.GetType().FullName);
throw new NotSupportedException("Not supported: " + _publishedSnapshotStatus.GetType().FullName);
}
}
}