Merge pull request #5814 from OrchardCMS/task/machinenameprovider

Refactor renamed IMachineNameProvider.
This commit is contained in:
Sipke Schoorstra
2015-09-17 21:52:15 +01:00
16 changed files with 94 additions and 100 deletions

View File

@@ -15,7 +15,7 @@
<component instance-scope="single-instance" type="Orchard.Azure.Services.Environment.Configuration.AzureBlobShellSettingsManager, Orchard.Azure" service="Orchard.Environment.Configuration.IShellSettingsManager"></component>
<!-- Configure Orchard to use role instance ID instead of Windows machine name for task lease records. -->
<component instance-scope="single-instance" type="Orchard.Azure.Services.TaskLease.AzureMachineNameProvider, Orchard.Azure" service="Orchard.Environment.IMachineNameProvider, Orchard.Framework"></component>
<component instance-scope="single-instance" type="Orchard.Azure.Services.TaskLease.ApplicationEnvironment, Orchard.Azure" service="Orchard.Environment.IApplicationEnvironment, Orchard.Framework"></component>
</components>
</autofac>

View File

@@ -7,7 +7,7 @@
<autofac defaultAssembly="Orchard.Framework">
<components>
<component instance-scope="single-instance" type="Orchard.Environment.MachineNameProvider, Orchard.Framework" service="Orchard.Environment.IMachineNameProvider, Orchard.Framework"></component>
<component instance-scope="single-instance" type="Orchard.Environment.ApplicationEnvironment, Orchard.Framework" service="Orchard.Environment.IApplicationEnvironment, Orchard.Framework"></component>
<!--<component instance-scope="single-instance"
type="Orchard.Environment.Configuration.AzureBlobTenantManager"
service="Orchard.Environment.Configuration.IShellSettingsManager">

View File

@@ -272,7 +272,7 @@
<Compile Include="Localization\DateTimePartsTests.cs" />
<Compile Include="Localization\DefaultDateLocalizationServicesTests.cs" />
<Compile Include="Localization\DefaultDateFormatterTests.cs" />
<Compile Include="Stubs\StubMachineNameProvider.cs" />
<Compile Include="Stubs\StubApplicationEnvironment.cs" />
<Compile Include="Stubs\StubCultureSelector.cs" />
<Compile Include="Localization\TestHelpers.cs" />
<Compile Include="Logging\OrchardFileAppenderTests.cs" />

View File

@@ -1,12 +1,12 @@
using Orchard.Environment;
namespace Orchard.Tests.Stubs {
public class StubMachineNameProvider : IMachineNameProvider {
public StubMachineNameProvider() {
public class StubApplicationEnvironment : IApplicationEnvironment {
public StubApplicationEnvironment() {
MachineName = "Orchard Machine";
}
public string MachineName { get; set; }
public string GetMachineName() {
public string GetEnvironmentIdentifier() {
return MachineName;
}
}

View File

@@ -17,7 +17,7 @@ namespace Orchard.Tests.Tasks {
public class DistributedLockServiceTests : DatabaseEnabledTestsBase {
private const string LockName = "Orchard Test Lock";
private DistributedLockService _distributedLockService;
private StubMachineNameProvider _machineNameProvider;
private StubApplicationEnvironment _applicationEnvironment;
private IRepository<DistributedLockRecord> _distributedLockRepository;
private ITransactionManager _transactionManager;
@@ -27,14 +27,14 @@ namespace Orchard.Tests.Tasks {
public override void Register(ContainerBuilder builder) {
builder.RegisterType<StubClock>().As<IClock>();
builder.RegisterType<StubMachineNameProvider>().As<IMachineNameProvider>().SingleInstance();
builder.RegisterType<StubApplicationEnvironment>().As<IApplicationEnvironment>().SingleInstance();
builder.RegisterType<DistributedLockService>().AsSelf();
}
public override void Init() {
base.Init();
_distributedLockService = _container.Resolve<DistributedLockService>();
_machineNameProvider = (StubMachineNameProvider)_container.Resolve<IMachineNameProvider>();
_applicationEnvironment = (StubApplicationEnvironment)_container.Resolve<IApplicationEnvironment>();
_distributedLockRepository = _container.Resolve<IRepository<DistributedLockRecord>>();
_transactionManager = _container.Resolve<ITransactionManager>();
}
@@ -88,9 +88,9 @@ namespace Orchard.Tests.Tasks {
[Test]
public void TryAcquiringLockTwiceFails() {
IDistributedLock @lock;
_machineNameProvider.MachineName = "Orchard Test Machine 1";
_applicationEnvironment.MachineName = "Orchard Test Machine 1";
var attempt1 = _distributedLockService.TryAcquireLock(LockName, TimeSpan.FromSeconds(60), out @lock);
_machineNameProvider.MachineName = "Orchard Test Machine 2";
_applicationEnvironment.MachineName = "Orchard Test Machine 2";
var attempt2 = _distributedLockService.TryAcquireLock(LockName, TimeSpan.FromSeconds(60), out @lock);
Assert.That(attempt1, Is.True);
@@ -151,9 +151,9 @@ namespace Orchard.Tests.Tasks {
[Test]
public void MultipleAcquisitionsFromDifferentMachinesShouldFail() {
IDistributedLock @lock;
_machineNameProvider.MachineName = "Orchard Test Machine 1";
_applicationEnvironment.MachineName = "Orchard Test Machine 1";
var attempt1 = _distributedLockService.TryAcquireLock(LockName, TimeSpan.FromMinutes(60), out @lock);
_machineNameProvider.MachineName = "Orchard Test Machine 2";
_applicationEnvironment.MachineName = "Orchard Test Machine 2";
var attempt2 = _distributedLockService.TryAcquireLock(LockName, TimeSpan.FromMinutes(60), out @lock);
Assert.That(attempt1, Is.True);
@@ -163,9 +163,9 @@ namespace Orchard.Tests.Tasks {
[Test]
public void MultipleAcquisitionsFromDifferentMachinesOnDifferentTenantShouldSucceed() {
IDistributedLock @lock;
_machineNameProvider.MachineName = "Orchard Test Machine 1";
_applicationEnvironment.MachineName = "Orchard Test Machine 1";
var attempt1 = _distributedLockService.TryAcquireLock(LockName, TimeSpan.FromSeconds(60), out @lock);
_machineNameProvider.MachineName = "Orchard Test Machine 2";
_applicationEnvironment.MachineName = "Orchard Test Machine 2";
_shellSettings.Name = "Foo";
var attempt2 = _distributedLockService.TryAcquireLock(LockName, TimeSpan.FromSeconds(60), out @lock);
@@ -215,14 +215,14 @@ namespace Orchard.Tests.Tasks {
IDistributedLock @lock;
// Create a never expiring lock.
_machineNameProvider.MachineName = "Orchard Test Machine 1";
_applicationEnvironment.MachineName = "Orchard Test Machine 1";
var attempt1 = _distributedLockService.TryAcquireLock(LockName, maxValidFor: null, timeout: null, dLock: out @lock);
// Release the lock.
@lock.Dispose();
// Acquire the lock from another machine.
_machineNameProvider.MachineName = "Orchard Test Machine 2";
_applicationEnvironment.MachineName = "Orchard Test Machine 2";
var attempt2 = _distributedLockService.TryAcquireLock(LockName, maxValidFor: null, timeout: null, dLock: out @lock);
// Validate the results.
@@ -259,7 +259,7 @@ namespace Orchard.Tests.Tasks {
}
private string GetMachineName() {
return _machineNameProvider.GetMachineName();
return _applicationEnvironment.GetEnvironmentIdentifier();
}
}
}

View File

@@ -8,7 +8,7 @@
<autofac defaultAssembly="Orchard.Framework">
<components>
<component instance-scope="single-instance" type="Orchard.Localization.Services.CultureDateTimeFormatProvider, Orchard.Framework" service="Orchard.Localization.Services.IDateTimeFormatProvider"></component>
<component instance-scope="single-instance" type="Orchard.Environment.MachineNameProvider, Orchard.Framework" service="Orchard.Environment.IMachineNameProvider, Orchard.Framework"></component>
<component instance-scope="single-instance" type="Orchard.Environment.ApplicationEnvironment, Orchard.Framework" service="Orchard.Environment.IApplicationEnvironment, Orchard.Framework"></component>
</components>
</autofac>

View File

@@ -107,7 +107,7 @@
<ItemGroup>
<Compile Include="Services\Environment\Configuration\DefaultPlatformConfigurationAccessor.cs" />
<Compile Include="Services\Environment\Configuration\IPlatformConfigurationAccessor.cs" />
<Compile Include="Services\TaskLease\AzureMachineNameProvider.cs" />
<Compile Include="Services\Environment\AzureApplicationEnvironment.cs" />
<Content Include="Web.config" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Content Include="Module.txt" />

View File

@@ -0,0 +1,10 @@
using Microsoft.WindowsAzure.ServiceRuntime;
using Orchard.Environment;
namespace Orchard.Azure.Services.Environment {
public class AzureApplicationEnvironment : IApplicationEnvironment {
public string GetEnvironmentIdentifier() {
return RoleEnvironment.CurrentRoleInstance.Id;
}
}
}

View File

@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.WindowsAzure.ServiceRuntime;
using Orchard.Environment;
namespace Orchard.Azure.Services.TaskLease {
public class AzureMachineNameProvider : IMachineNameProvider {
public string GetMachineName() {
return RoleEnvironment.CurrentRoleInstance.Id;
}
}
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.Data.Conventions;
namespace Orchard.TaskLease.Models

View File

@@ -14,20 +14,20 @@ namespace Orchard.TaskLease.Services {
private readonly IRepository<TaskLeaseRecord> _repository;
private readonly IClock _clock;
private readonly IMachineNameProvider _machineNameProvider;
private readonly IApplicationEnvironment _applicationEnvironment;
public TaskLeaseService(
IRepository<TaskLeaseRecord> repository,
IClock clock,
IMachineNameProvider machineNameProvider) {
IApplicationEnvironment applicationEnvironment) {
_repository = repository;
_clock = clock;
_machineNameProvider = machineNameProvider;
_applicationEnvironment = applicationEnvironment;
}
public string Acquire(string taskName, DateTime expiredUtc) {
var machineName = _machineNameProvider.GetMachineName();
var environmentIdentifier = _applicationEnvironment.GetEnvironmentIdentifier();
// retrieve current lease for the specified task
var taskLease = _repository.Get(x => x.TaskName == taskName);
@@ -36,7 +36,7 @@ namespace Orchard.TaskLease.Services {
if (taskLease == null) {
taskLease = new TaskLeaseRecord {
TaskName = taskName,
MachineName = machineName,
MachineName = environmentIdentifier,
State = String.Empty,
UpdatedUtc = _clock.UtcNow,
ExpiredUtc = expiredUtc
@@ -49,12 +49,12 @@ namespace Orchard.TaskLease.Services {
}
// lease can't be aquired only if for a different machine and it has not expired
if (taskLease.MachineName != machineName && taskLease.ExpiredUtc >= _clock.UtcNow) {
if (taskLease.MachineName != environmentIdentifier && taskLease.ExpiredUtc >= _clock.UtcNow) {
return null;
}
// otherwise update information
taskLease.MachineName = machineName;
taskLease.MachineName = environmentIdentifier;
taskLease.UpdatedUtc = _clock.UtcNow;
taskLease.ExpiredUtc = expiredUtc;
@@ -64,10 +64,10 @@ namespace Orchard.TaskLease.Services {
}
public void Update(string taskName, string state) {
var machineName = _machineNameProvider.GetMachineName();
var environmentIdentifier = _applicationEnvironment.GetEnvironmentIdentifier();
// retrieve current lease for the specified task
var taskLease = _repository.Get(x => x.TaskName == taskName && x.MachineName == machineName);
var taskLease = _repository.Get(x => x.TaskName == taskName && x.MachineName == environmentIdentifier);
if (taskLease == null) {
return;
@@ -78,10 +78,10 @@ namespace Orchard.TaskLease.Services {
}
public void Update(string taskName, string state, DateTime expiredUtc) {
var machineName = _machineNameProvider.GetMachineName();
var environmentIdentifier = _applicationEnvironment.GetEnvironmentIdentifier();
// retrieve current lease for the specified task
var taskLease = _repository.Get(x => x.TaskName == taskName && x.MachineName == machineName);
var taskLease = _repository.Get(x => x.TaskName == taskName && x.MachineName == environmentIdentifier);
if (taskLease == null) {
return;

View File

@@ -2,8 +2,8 @@
using System.Diagnostics;
namespace Orchard.Environment {
public class MachineNameProvider : IMachineNameProvider {
public string GetMachineName() {
public class ApplicationEnvironment : IApplicationEnvironment {
public string GetEnvironmentIdentifier() {
// Add process ID to machine name because multiple web servers can
// be running on the same physical machine.
return String.Format("{0}:{1}", System.Environment.MachineName, Process.GetCurrentProcess().Id);

View File

@@ -0,0 +1,13 @@
namespace Orchard.Environment {
/// <summary>
/// Describes a service which returns the a machine identifier running the application.
/// </summary>
public interface IApplicationEnvironment {
/// <summary>
/// Returns the machine identifier running the application.
/// </summary>
string GetEnvironmentIdentifier();
}
}

View File

@@ -1,13 +0,0 @@
namespace Orchard.Environment {
/// <summary>
/// Describes a service which returns the name of the machine running the application.
/// </summary>
public interface IMachineNameProvider {
/// <summary>
/// Returns the name of the machine running the application.
/// </summary>
string GetMachineName();
}
}

View File

@@ -298,10 +298,10 @@
<Compile Include="Environment\HostComponentsConfigModule.cs" />
<Compile Include="Environment\Extensions\ICriticalErrorProvider.cs" />
<Compile Include="Environment\IFeatureEventHandler.cs" />
<Compile Include="Environment\IMachineNameProvider.cs" />
<Compile Include="Environment\IApplicationEnvironment.cs" />
<Compile Include="Environment\IOrchardFrameworkAssemblies.cs" />
<Compile Include="Environment\IWorkContextEvents.cs" />
<Compile Include="Environment\MachineNameProvider.cs" />
<Compile Include="Environment\ApplicationEnvironment.cs" />
<Compile Include="Environment\State\ContextState.cs" />
<Compile Include="Environment\ViewsBackgroundCompilation.cs" />
<Compile Include="Environment\Warmup\WarmupUtility.cs" />

View File

@@ -17,7 +17,7 @@ namespace Orchard.Tasks.Locking.Services {
public class DistributedLockService : Component, IDistributedLockService {
private readonly IMachineNameProvider _machineNameProvider;
private readonly IApplicationEnvironment _applicationEnvironment;
private readonly ILifetimeScope _lifetimeScope;
private readonly IClock _clock;
private readonly ShellSettings _shellSettings;
@@ -25,14 +25,14 @@ namespace Orchard.Tasks.Locking.Services {
private readonly TimeSpan _defaultRepeatInterval;
public DistributedLockService(
IMachineNameProvider machineNameProvider,
IApplicationEnvironment applicationEnvironment,
ILifetimeScope lifetimeScope,
IClock clock,
ShellSettings shellSettings) {
_clock = clock;
_lifetimeScope = lifetimeScope;
_shellSettings = shellSettings;
_machineNameProvider = machineNameProvider;
_applicationEnvironment = applicationEnvironment;
_locks = new Dictionary<string, DistributedLock>();
_defaultRepeatInterval = TimeSpan.FromMilliseconds(500);
}
@@ -44,44 +44,44 @@ namespace Orchard.Tasks.Locking.Services {
if (dLock != null) {
Logger.Debug("Successfully acquired lock '{0}'.", name);
return true;
}
}
Logger.Warning("Failed to acquire lock '{0}' within the specified timeout ({1}).", name, timeout);
}
catch (Exception ex) {
Logger.Error(ex, "Error while trying to acquire lock '{0}'.", name);
// TODO: Is it correct to not throw here? Should we instead ONLY swallow TimeoutException?
}
}
dLock = null;
return false;
}
public IDistributedLock AcquireLock(string name, TimeSpan? maxValidFor, TimeSpan? timeout) {
try {
DistributedLock result = AcquireLockInternal(name, maxValidFor, timeout, throwOnTimeout: true);
Logger.Debug("Successfully acquired lock '{0}'.", name);
return result;
}
}
catch (Exception ex) {
Logger.Error(ex, "Error while trying to acquire lock '{0}'.", name);
throw;
}
}
}
private DistributedLock AcquireLockInternal(string name, TimeSpan? maxValidFor, TimeSpan? timeout, bool throwOnTimeout) {
var internalName = GetInternalLockName(name);
var monitorTimeout = timeout.HasValue ? timeout.Value : TimeSpan.FromMilliseconds(-1); // -1 ms is .NET magic number for "infinite".
var monitorObj = String.Intern(String.Format("{0}:{1}", _machineNameProvider.GetMachineName(), internalName));
var monitorObj = String.Intern(String.Format("{0}:{1}", _applicationEnvironment.GetEnvironmentIdentifier(), internalName));
if (!Monitor.TryEnter(monitorObj, monitorTimeout)) {
Logger.Debug("Could not enter local monitor for lock '{0}' within the specified timeout ({1}).", internalName, timeout);
if (throwOnTimeout)
throw new TimeoutException(String.Format("Failed to acquire lock '{0}' within the specified timeout ({1}).", internalName, timeout));
return null;
}
}
Logger.Debug("Successfully entered local monitor for lock '{0}'.", internalName);
@@ -106,37 +106,37 @@ namespace Orchard.Tasks.Locking.Services {
dLock = new DistributedLock(name, internalName, releaseLockAction: () => {
Monitor.Exit(monitorObj);
DeleteDistributedLockRecord(internalName);
});
});
_locks.Add(monitorObj, dLock);
return true;
}
}
return false;
});
});
if (!success) {
Logger.Debug("Record for lock '{0}' could not be created for current machine within the specified timeout ({1}).", internalName, timeout);
if (throwOnTimeout)
throw new TimeoutException(String.Format("Failed to acquire lock '{0}' within the specified timeout ({1}).", internalName, timeout));
return null;
}
return null;
}
}
return dLock;
}
return dLock;
}
catch (Exception ex) {
Monitor.Exit(monitorObj);
Logger.Error(ex, "An error occurred while trying to acquire lock '{0}'.", internalName);
throw;
}
}
}
}
private bool EnsureDistributedLockRecord(string internalName, TimeSpan? maxValidFor) {
var localMachineName = _machineNameProvider.GetMachineName();
var environmentIdentifier = _applicationEnvironment.GetEnvironmentIdentifier();
var hasLockRecord = false;
ExecuteOnSeparateTransaction(repository => {
@@ -145,25 +145,25 @@ namespace Orchard.Tasks.Locking.Services {
if (record == null) {
// No record existed, so we're good to create a new one.
Logger.Debug("No valid record was found for lock '{0}'; creating a new record.", internalName);
repository.Create(new DistributedLockRecord {
Name = internalName,
MachineName = localMachineName,
MachineName = environmentIdentifier,
CreatedUtc = _clock.UtcNow,
ValidUntilUtc = maxValidFor.HasValue ? _clock.UtcNow + maxValidFor.Value : default(DateTime?)
});
hasLockRecord = true;
}
else if (record.MachineName == localMachineName) {
else if (record.MachineName == environmentIdentifier) {
// Existing lock was for correct machine name => lock record exists.
Logger.Debug("Found a valid record for lock '{0}' and current local machine name '{1}'.", internalName, localMachineName);
Logger.Debug("Found a valid record for lock '{0}' and current local machine name '{1}'.", internalName, environmentIdentifier);
hasLockRecord = true;
}
});
return hasLockRecord;
}
}
private void DeleteDistributedLockRecord(string internalName) {
try {
@@ -177,9 +177,9 @@ namespace Orchard.Tasks.Locking.Services {
}
catch (Exception ex) {
if (ex.IsFatal())
throw;
throw;
Logger.Warning(ex, "An error occurred while deleting record for lock '{0}'.", internalName);
}
}
}
private bool RepeatUntilTimeout(TimeSpan? timeout, TimeSpan repeatInterval, Func<bool> action) {
@@ -198,14 +198,14 @@ namespace Orchard.Tasks.Locking.Services {
if (action == null)
throw new ArgumentNullException();
using (var childLifetimeScope = _lifetimeScope.BeginLifetimeScope()) {
var repository = childLifetimeScope.Resolve<IRepository<DistributedLockRecord>>();
var transactionManager = childLifetimeScope.Resolve<ITransactionManager>();
transactionManager.RequireNew(IsolationLevel.ReadCommitted);
action(repository);
}
using (var childLifetimeScope = _lifetimeScope.BeginLifetimeScope()) {
var repository = childLifetimeScope.Resolve<IRepository<DistributedLockRecord>>();
var transactionManager = childLifetimeScope.Resolve<ITransactionManager>();
transactionManager.RequireNew(IsolationLevel.ReadCommitted);
action(repository);
}
}
private string GetInternalLockName(string name) {
// Prefix the requested lock name by a constant and the tenant name.
return String.Format("DistributedLock:{0}:{1}", _shellSettings.Name, name);