New IPublishedSnapshotStatus, reduces IPublishedSnapshotService
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
18
src/Umbraco.Core/PublishedCache/IPublishedSnapshotStatus.cs
Normal file
18
src/Umbraco.Core/PublishedCache/IPublishedSnapshotStatus.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{ }
|
||||
|
||||
@@ -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" : "") + ".";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user