Files
Umbraco-CMS/src/Umbraco.Core/Services/Implement/ServerRegistrationService.cs
2018-10-02 11:14:04 +02:00

163 lines
6.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.Repositories.Implement;
using Umbraco.Core.Scoping;
using Umbraco.Core.Sync;
namespace Umbraco.Core.Services.Implement
{
/// <summary>
/// Manages server registrations in the database.
/// </summary>
public sealed class ServerRegistrationService : ScopeRepositoryService, IServerRegistrationService
{
private readonly IServerRegistrationRepository _serverRegistrationRepository;
private static readonly string CurrentServerIdentityValue = NetworkHelper.MachineName // eg DOMAIN\SERVER
+ "/" + HttpRuntime.AppDomainAppId; // eg /LM/S3SVC/11/ROOT
private ServerRole _currentServerRole = ServerRole.Unknown;
/// <summary>
/// Initializes a new instance of the <see cref="ServerRegistrationService"/> class.
/// </summary>
/// <param name="scopeProvider">A UnitOfWork provider.</param>
/// <param name="logger">A logger.</param>
/// <param name="eventMessagesFactory"></param>
public ServerRegistrationService(IScopeProvider scopeProvider, ILogger logger, IEventMessagesFactory eventMessagesFactory,
IServerRegistrationRepository serverRegistrationRepository)
: base(scopeProvider, logger, eventMessagesFactory)
{
_serverRegistrationRepository = serverRegistrationRepository;
}
/// <summary>
/// Touches a server to mark it as active; deactivate stale servers.
/// </summary>
/// <param name="serverAddress">The server url.</param>
/// <param name="serverIdentity">The server unique identity.</param>
/// <param name="staleTimeout">The time after which a server is considered stale.</param>
public void TouchServer(string serverAddress, string serverIdentity, TimeSpan staleTimeout)
{
using (var scope = ScopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.Servers);
((ServerRegistrationRepository) _serverRegistrationRepository).ClearCache(); // ensure we have up-to-date cache
var regs = _serverRegistrationRepository.GetMany().ToArray();
var hasMaster = regs.Any(x => ((ServerRegistration) x).IsMaster);
var server = regs.FirstOrDefault(x => x.ServerIdentity.InvariantEquals(serverIdentity));
if (server == null)
{
server = new ServerRegistration(serverAddress, serverIdentity, DateTime.Now);
}
else
{
server.ServerAddress = serverAddress; // should not really change but it might!
server.UpdateDate = DateTime.Now;
}
server.IsActive = true;
if (hasMaster == false)
server.IsMaster = true;
_serverRegistrationRepository.Save(server);
_serverRegistrationRepository.DeactiveStaleServers(staleTimeout); // triggers a cache reload
// reload - cheap, cached
// reload - cheap, cached
// default role is single server, but if registrations contain more
// than one active server, then role is master or replica
regs = _serverRegistrationRepository.GetMany().ToArray();
// default role is single server, but if registrations contain more
// than one active server, then role is master or replica
_currentServerRole = regs.Count(x => x.IsActive) > 1
? (server.IsMaster ? ServerRole.Master : ServerRole.Replica)
: ServerRole.Single;
scope.Complete();
}
}
/// <summary>
/// Deactivates a server.
/// </summary>
/// <param name="serverIdentity">The server unique identity.</param>
public void DeactiveServer(string serverIdentity)
{
// because the repository caches "all" and has queries disabled...
using (var scope = ScopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.Servers);
((ServerRegistrationRepository) _serverRegistrationRepository).ClearCache(); // ensure we have up-to-date cache // ensure we have up-to-date cache
var server = _serverRegistrationRepository.GetMany().FirstOrDefault(x => x.ServerIdentity.InvariantEquals(serverIdentity));
if (server == null) return;
server.IsActive = server.IsMaster = false;
_serverRegistrationRepository.Save(server); // will trigger a cache reload // will trigger a cache reload
scope.Complete();
}
}
/// <summary>
/// Deactivates stale servers.
/// </summary>
/// <param name="staleTimeout">The time after which a server is considered stale.</param>
public void DeactiveStaleServers(TimeSpan staleTimeout)
{
using (var scope = ScopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.Servers);
_serverRegistrationRepository.DeactiveStaleServers(staleTimeout);
scope.Complete();
}
}
/// <summary>
/// Return all active servers.
/// </summary>
/// <param name="refresh">A value indicating whether to force-refresh the cache.</param>
/// <returns>All active servers.</returns>
/// <remarks>By default this method will rely on the repository's cache, which is updated each
/// time the current server is touched, and the period depends on the configuration. Use the
/// <paramref name="refresh"/> parameter to force a cache refresh and reload active servers
/// from the database.</remarks>
public IEnumerable<IServerRegistration> GetActiveServers(bool refresh = false)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
scope.ReadLock(Constants.Locks.Servers);
if (refresh) ((ServerRegistrationRepository) _serverRegistrationRepository).ClearCache();
return _serverRegistrationRepository.GetMany().Where(x => x.IsActive).ToArray(); // fast, cached // fast, cached
}
}
/// <summary>
/// Gets the local server identity.
/// </summary>
public string CurrentServerIdentity => CurrentServerIdentityValue;
/// <summary>
/// Gets the role of the current server.
/// </summary>
/// <returns>The role of the current server.</returns>
public ServerRole GetCurrentServerRole()
{
return _currentServerRole;
}
}
}