GiantsTools/Giants.Services/Services/ServerRegistryService.cs

101 lines
4.2 KiB
C#
Raw Normal View History

namespace Giants.Services
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
2020-08-09 01:31:16 +02:00
using System.Linq;
using System.Threading.Tasks;
2020-08-09 01:31:16 +02:00
using Giants.Services.Core;
using Microsoft.Extensions.Caching.Memory;
2020-08-09 01:31:16 +02:00
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
public class ServerRegistryService : IServerRegistryService
{
private static readonly string[] SupportedGameNames = new[] { "Giants" };
private readonly ILogger<ServerRegistryService> logger;
private readonly IServerRegistryStore registryStore;
2020-08-09 01:31:16 +02:00
private readonly IConfiguration configuration;
private readonly IDateTimeProvider dateTimeProvider;
private readonly IMemoryCache memoryCache;
2020-08-09 01:31:16 +02:00
private readonly TimeSpan timeoutPeriod;
private readonly int maxServerCount;
2020-08-09 01:31:16 +02:00
public ServerRegistryService(
ILogger<ServerRegistryService> logger,
2020-08-09 01:31:16 +02:00
IServerRegistryStore registryStore,
IConfiguration configuration,
IDateTimeProvider dateTimeProvider,
IMemoryCache memoryCache)
{
this.logger = logger;
this.registryStore = registryStore;
2020-08-09 01:31:16 +02:00
this.configuration = configuration;
this.dateTimeProvider = dateTimeProvider;
this.memoryCache = memoryCache;
2020-08-09 01:31:16 +02:00
this.timeoutPeriod = TimeSpan.FromMinutes(Convert.ToDouble(this.configuration["ServerTimeoutPeriodInMinutes"]));
this.maxServerCount = Convert.ToInt32(this.configuration["MaxServerCount"]);
}
public async Task AddServer(
2020-08-09 02:52:26 +02:00
ServerInfo serverInfo)
{
2020-08-09 02:52:26 +02:00
ArgumentUtility.CheckForNull(serverInfo, nameof(serverInfo));
ArgumentUtility.CheckStringForNullOrEmpty(serverInfo.HostIpAddress, nameof(serverInfo.HostIpAddress));
if (!SupportedGameNames.Contains(serverInfo.GameName, StringComparer.OrdinalIgnoreCase))
{
throw new ArgumentException($"Unsupported game name {serverInfo.GameName}", nameof(serverInfo));
}
// Check cache before we write to store
ConcurrentDictionary<string, ServerInfo> allServerInfo = await this.memoryCache.GetOrCreateAsync(CacheKeys.ServerInfo, PopulateCache);
if (allServerInfo.ContainsKey(serverInfo.HostIpAddress))
{
if (allServerInfo[serverInfo.HostIpAddress].Equals(serverInfo))
{
this.logger.LogInformation("State for {IPAddress} is unchanged. Skipping write to store.", serverInfo.HostIpAddress);
return;
}
else
{
this.logger.LogInformation("State for {IPAddress} has changed. Committing update to store.", serverInfo.HostIpAddress);
}
}
2020-08-09 02:52:26 +02:00
await this.registryStore.UpsertServerInfo(serverInfo);
this.logger.LogInformation("Updating cache for request from {IPAddress}.", serverInfo.HostIpAddress);
if (allServerInfo.ContainsKey(serverInfo.HostIpAddress))
{
allServerInfo[serverInfo.HostIpAddress] = serverInfo;
}
else
{
allServerInfo.TryAdd(serverInfo.HostIpAddress, serverInfo);
}
}
public async Task<IEnumerable<ServerInfo>> GetAllServers()
{
ConcurrentDictionary<string, ServerInfo> serverInfo = await this.memoryCache.GetOrCreateAsync(CacheKeys.ServerInfo, PopulateCache);
return serverInfo.Values;
}
private async Task<ConcurrentDictionary<string, ServerInfo>> PopulateCache(ICacheEntry entry)
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
IDictionary<string, ServerInfo> serverInfo = (await this.registryStore
2020-08-09 02:26:41 +02:00
.GetServerInfos(whereExpression: c => c.LastHeartbeat > this.dateTimeProvider.UtcNow - this.timeoutPeriod))
.Take(this.maxServerCount)
.ToDictionary(x => x.HostIpAddress, y => y);
return new ConcurrentDictionary<string, ServerInfo>(serverInfo);
}
}
}