Further changes requested during review of #12049 (#12053)

This commit is contained in:
Paul Johnson
2022-02-25 08:22:37 +00:00
committed by GitHub
parent de4b3af28f
commit 4351ce6ee4
5 changed files with 45 additions and 55 deletions

View File

@@ -29,6 +29,7 @@ namespace Umbraco.Cms.Core.Configuration.Models
internal const string StaticNoNodesViewPath = "~/umbraco/UmbracoWebsite/NoNodes.cshtml";
internal const string StaticSqlWriteLockTimeOut = "00:00:05";
internal const bool StaticSanitizeTinyMce = false;
internal const int StaticMainDomReleaseSignalPollingInterval = 2000;
/// <summary>
/// Gets or sets a value for the reserved URLs (must end with a comma).
@@ -145,6 +146,18 @@ namespace Umbraco.Cms.Core.Configuration.Models
/// </summary>
public string MainDomKeyDiscriminator { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the duration (in milliseconds) for which the MainDomLock release signal polling task should sleep.
/// </summary>
/// <remarks>
/// Doesn't apply to MainDomSemaphoreLock.
/// <para>
/// The default value is 2000ms.
/// </para>
/// </remarks>
[DefaultValue(StaticMainDomReleaseSignalPollingInterval)]
public int MainDomReleaseSignalPollingInterval { get; set; } = StaticMainDomReleaseSignalPollingInterval;
/// <summary>
/// Gets or sets the telemetry ID.
/// </summary>

View File

@@ -234,7 +234,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
if (globalSettings.Value.MainDomLock == "FileSystemMainDomLock")
{
return new FileSystemMainDomLock(loggerFactory.CreateLogger<FileSystemMainDomLock>(), mainDomKeyGenerator, hostingEnvironment);
return new FileSystemMainDomLock(loggerFactory.CreateLogger<FileSystemMainDomLock>(), mainDomKeyGenerator, hostingEnvironment, factory.GetRequiredService<IOptionsMonitor<GlobalSettings>>());
}
return globalSettings.Value.MainDomLock.Equals("SqlMainDomLock") || isWindows == false

View File

@@ -1,10 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Runtime;
@@ -13,6 +14,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime
internal class FileSystemMainDomLock : IMainDomLock
{
private readonly ILogger<FileSystemMainDomLock> _logger;
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
private readonly CancellationTokenSource _cancellationTokenSource = new();
private readonly string _lockFilePath;
private readonly string _releaseSignalFilePath;
@@ -20,14 +22,14 @@ namespace Umbraco.Cms.Infrastructure.Runtime
private FileStream _lockFileStream;
private Task _listenForReleaseSignalFileTask;
private const int s_maxTriesRemovingLockReleaseSignalFile = 3;
public FileSystemMainDomLock(
ILogger<FileSystemMainDomLock> logger,
IMainDomKeyGenerator mainDomKeyGenerator,
IHostingEnvironment hostingEnvironment)
IHostingEnvironment hostingEnvironment,
IOptionsMonitor<GlobalSettings> globalSettings)
{
_logger = logger;
_globalSettings = globalSettings;
var lockFileName = $"MainDom_{mainDomKeyGenerator.GenerateKey()}.lock";
_lockFilePath = Path.Combine(hostingEnvironment.LocalTempPath, lockFileName);
@@ -45,18 +47,18 @@ namespace Umbraco.Cms.Infrastructure.Runtime
{
_logger.LogDebug("Attempting to obtain MainDom lock file handle {lockFilePath}", _lockFilePath);
_lockFileStream = File.Open(_lockFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
DeleteLockReleaseFile();
DeleteLockReleaseSignalFile();
return Task.FromResult(true);
}
catch (IOException)
{
_logger.LogDebug("Couldn't obtain MainDom lock file handle, signalling for release of {lockFilePath}", _lockFilePath);
CreateLockReleaseFile();
Thread.Sleep(500);
CreateLockReleaseSignalFile();
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected exception attempting to obtain MainDom lock file handle {lockFilePath}, giving up", _lockFilePath);
_lockFileStream?.Close();
return Task.FromResult(false);
}
}
@@ -65,6 +67,12 @@ namespace Umbraco.Cms.Infrastructure.Runtime
return Task.FromResult(false);
}
public void CreateLockReleaseSignalFile() =>
_ = File.Open(_releaseSignalFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete);
public void DeleteLockReleaseSignalFile() =>
File.Delete(_releaseSignalFilePath);
// Create a long running task to poll to check if anyone has created a lock release file.
public Task ListenAsync()
{
@@ -82,46 +90,6 @@ namespace Umbraco.Cms.Infrastructure.Runtime
return _listenForReleaseSignalFileTask;
}
public void Dispose()
{
_lockFileStream?.Close();
_lockFileStream = null;
}
private void CreateLockReleaseFile()
{
try
{
// Dispose immediately to release the file handle so it's easier to cleanup in any process.
using FileStream releaseFileStream = File.Open(_releaseSignalFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected exception attempting to create lock release signal file {file}", _releaseSignalFilePath);
}
}
private void DeleteLockReleaseFile()
{
List<Exception> encounteredExceptions = new();
for (var i = 0; i < s_maxTriesRemovingLockReleaseSignalFile; i++)
{
try
{
File.Delete(_releaseSignalFilePath);
return;
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected exception attempting to delete release signal file {file}", _releaseSignalFilePath);
encounteredExceptions.Add(ex);
Thread.Sleep(500 * (i + 1));
}
}
throw new ApplicationException($"Failed to remove lock release signal file {_releaseSignalFilePath}", new AggregateException(encounteredExceptions));
}
private void ListeningLoop()
{
while (true)
@@ -140,8 +108,14 @@ namespace Umbraco.Cms.Infrastructure.Runtime
break;
}
Thread.Sleep(2000);
Thread.Sleep(_globalSettings.CurrentValue.MainDomReleaseSignalPollingInterval);
}
}
public void Dispose()
{
_lockFileStream?.Close();
_lockFileStream = null;
}
}
}

View File

@@ -236,7 +236,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime
{
// poll every couple of seconds
// local testing shows the actual query to be executed from client/server is approx 300ms but would change depending on environment/IO
Thread.Sleep(2000);
Thread.Sleep(_globalSettings.Value.MainDomReleaseSignalPollingInterval);
if (!_dbFactory.Configured)
{

View File

@@ -1,7 +1,10 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using NUnit.Framework;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Runtime;
using Umbraco.Cms.Infrastructure.Runtime;
@@ -31,8 +34,11 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Runtime
LockFilePath = Path.Combine(HostingEnvironment.LocalTempPath, lockFileName);
LockReleaseFilePath = LockFilePath + "_release";
var globalSettings = Mock.Of<IOptionsMonitor<GlobalSettings>>();
Mock.Get(globalSettings).Setup(x => x.CurrentValue).Returns(new GlobalSettings());
var log = GetRequiredService<ILogger<FileSystemMainDomLock>>();
FileSystemMainDomLock = new FileSystemMainDomLock(log, MainDomKeyGenerator, HostingEnvironment);
FileSystemMainDomLock = new FileSystemMainDomLock(log, MainDomKeyGenerator, HostingEnvironment, globalSettings);
}
[TearDown]
@@ -79,10 +85,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Runtime
var before = await sut.AcquireLockAsync(1000);
await using (_ = File.Open(LockReleaseFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
{
}
sut.CreateLockReleaseSignalFile();
await sut.ListenAsync();
var after = await sut.AcquireLockAsync(1000);