mirror of
https://github.com/ncblakely/GiantsTools
synced 2024-12-24 16:27:23 +01:00
Implement timed cleanup.
This commit is contained in:
parent
aa16e3507c
commit
75c2b9474e
@ -1,9 +1,5 @@
|
||||
namespace Giants.DataContract
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
public class PlayerInfo
|
||||
{
|
||||
public int Index { get; set; }
|
||||
|
@ -11,6 +11,9 @@
|
||||
services.AddSingleton<IServerRegistryService, ServerRegistryService>();
|
||||
services.AddSingleton<IServerRegistryStore, CosmosDbServerRegistryStore>();
|
||||
services.AddSingleton<IDateTimeProvider, DefaultDateTimeProvider>();
|
||||
|
||||
services.AddHostedService<InitializerHostedService>();
|
||||
services.AddHostedService<ServerRegistryCleanupService>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.12.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
13
Giants.Services/Services/IServerRegistryCleanupService.cs
Normal file
13
Giants.Services/Services/IServerRegistryCleanupService.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Giants.Services
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
public interface IServerRegistryCleanupService : IHostedService
|
||||
{
|
||||
}
|
||||
}
|
@ -1,13 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Giants.Services;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Giants.WebApi
|
||||
namespace Giants.Services
|
||||
{
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
public class InitializerHostedService : IHostedService
|
||||
{
|
||||
private readonly IServerRegistryStore serverRegistryStore;
|
77
Giants.Services/Services/ServerRegistryCleanupService.cs
Normal file
77
Giants.Services/Services/ServerRegistryCleanupService.cs
Normal file
@ -0,0 +1,77 @@
|
||||
namespace Giants.Services
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Giants.Services.Core;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
public class ServerRegistryCleanupService : IServerRegistryCleanupService, IDisposable
|
||||
{
|
||||
private readonly ILogger<ServerRegistryCleanupService> logger;
|
||||
private readonly IConfiguration configuration;
|
||||
private readonly IServerRegistryStore serverRegistryStore;
|
||||
private readonly IDateTimeProvider dateTimeProvider;
|
||||
private readonly TimeSpan timeoutPeriod;
|
||||
private readonly TimeSpan cleanupInterval;
|
||||
private Timer timer;
|
||||
|
||||
public ServerRegistryCleanupService(
|
||||
ILogger<ServerRegistryCleanupService> logger,
|
||||
IConfiguration configuration,
|
||||
IServerRegistryStore serverRegistryStore,
|
||||
IDateTimeProvider dateTimeProvider)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.configuration = configuration;
|
||||
this.serverRegistryStore = serverRegistryStore;
|
||||
this.dateTimeProvider = dateTimeProvider;
|
||||
this.timeoutPeriod = TimeSpan.FromMinutes(Convert.ToDouble(this.configuration["ServerTimeoutPeriodInMinutes"]));
|
||||
this.cleanupInterval = TimeSpan.FromMinutes(Convert.ToDouble(this.configuration["ServerCleanupIntervalInMinutes"]));
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
this.timer = new Timer(TimerCallback, null, TimeSpan.Zero, this.cleanupInterval);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
this.timer?.Change(Timeout.Infinite, 0);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.timer?.Dispose();
|
||||
}
|
||||
|
||||
private void TimerCallback(object state)
|
||||
{
|
||||
this.CleanupServers().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private async Task CleanupServers()
|
||||
{
|
||||
List<string> expiredServers = (await this
|
||||
.serverRegistryStore
|
||||
.GetServerInfos(whereExpression: s => s.LastHeartbeat < (this.dateTimeProvider.UtcNow - this.timeoutPeriod)))
|
||||
.Select(s => s.id)
|
||||
.ToList();
|
||||
|
||||
if (expiredServers.Any())
|
||||
{
|
||||
logger.LogInformation("Cleaning up {Count} servers.", expiredServers.Count);
|
||||
|
||||
await this.serverRegistryStore.DeleteServers(expiredServers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -46,7 +46,7 @@
|
||||
public async Task<IEnumerable<ServerInfo>> GetAllServers()
|
||||
{
|
||||
return (await this.registryStore
|
||||
.GetServerInfos(c => c.LastHeartbeat > this.dateTimeProvider.UtcNow - this.timeoutPeriod))
|
||||
.GetServerInfos(whereExpression: c => c.LastHeartbeat > this.dateTimeProvider.UtcNow - this.timeoutPeriod))
|
||||
.Take(this.maxServerCount);
|
||||
}
|
||||
}
|
||||
|
@ -32,10 +32,15 @@
|
||||
|
||||
public async Task<IEnumerable<TSelect>> GetItems<T, TSelect>(
|
||||
Expression<Func<T, TSelect>> selectExpression,
|
||||
string partitionKey,
|
||||
Expression<Func<T, bool>> whereExpression = null)
|
||||
Expression<Func<T, bool>> whereExpression = null,
|
||||
string partitionKey = null)
|
||||
where T : IIdentifiable
|
||||
{
|
||||
if (partitionKey == null)
|
||||
{
|
||||
partitionKey = typeof(T).Name;
|
||||
}
|
||||
|
||||
IQueryable<T> query = this.container
|
||||
.GetItemLinqQueryable<T>(requestOptions: new QueryRequestOptions
|
||||
{
|
||||
@ -131,5 +136,25 @@
|
||||
|
||||
this.container = containerResponse.Container;
|
||||
}
|
||||
|
||||
public async Task DeleteItem<T>(
|
||||
string id,
|
||||
string partitionKey = null,
|
||||
ItemRequestOptions requestOptions = null)
|
||||
{
|
||||
if (partitionKey == null)
|
||||
{
|
||||
partitionKey = typeof(T).Name;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await this.container.DeleteItemAsync<T>(id, new PartitionKey(partitionKey), requestOptions);
|
||||
}
|
||||
catch (CosmosException e) when (e.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,23 +6,40 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Cosmos;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
public class CosmosDbServerRegistryStore : IServerRegistryStore
|
||||
{
|
||||
private readonly ILogger<CosmosDbServerRegistryStore> logger;
|
||||
private readonly IConfiguration configuration;
|
||||
private CosmosDbClient client;
|
||||
|
||||
public CosmosDbServerRegistryStore(IConfiguration configuration)
|
||||
public CosmosDbServerRegistryStore(
|
||||
ILogger<CosmosDbServerRegistryStore> logger,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ServerInfo>> GetServerInfos(
|
||||
Expression<Func<ServerInfo, bool>> whereExpression = null)
|
||||
Expression<Func<ServerInfo, bool>> whereExpression = null,
|
||||
string partitionKey = null)
|
||||
{
|
||||
return await this.client.GetItems<ServerInfo>(whereExpression);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TSelect>> GetServerInfos<TSelect>(
|
||||
Expression<Func<ServerInfo, TSelect>> selectExpression,
|
||||
Expression<Func<ServerInfo, bool>> whereExpression = null,
|
||||
string partitionKey = null)
|
||||
{
|
||||
return await this.client.GetItems<ServerInfo, TSelect>(
|
||||
selectExpression: selectExpression,
|
||||
whereExpression: whereExpression,
|
||||
partitionKey: partitionKey);
|
||||
}
|
||||
|
||||
public async Task<ServerInfo> GetServerInfo(string ipAddress)
|
||||
{
|
||||
return await this.client.GetItemById<ServerInfo>(ipAddress);
|
||||
@ -30,7 +47,19 @@
|
||||
|
||||
public async Task UpsertServerInfo(ServerInfo serverInfo)
|
||||
{
|
||||
await this.client.UpsertItem(serverInfo, new PartitionKey(serverInfo.DocumentType));
|
||||
await this.client.UpsertItem(
|
||||
item: serverInfo,
|
||||
partitionKey: new PartitionKey(serverInfo.DocumentType));
|
||||
}
|
||||
|
||||
public async Task DeleteServers(IEnumerable<string> ids, string partitionKey = null)
|
||||
{
|
||||
foreach (string id in ids)
|
||||
{
|
||||
this.logger.LogInformation("Deleting server for host IP {IPAddress}", id);
|
||||
|
||||
await this.client.DeleteItem<ServerInfo>(id, partitionKey);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Initialize()
|
||||
@ -44,4 +73,4 @@
|
||||
await this.client.Initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,12 +7,19 @@
|
||||
|
||||
public interface IServerRegistryStore
|
||||
{
|
||||
public Task Initialize();
|
||||
Task Initialize();
|
||||
|
||||
public Task<IEnumerable<ServerInfo>> GetServerInfos(Expression<Func<ServerInfo, bool>> whereExpression = null);
|
||||
Task<IEnumerable<ServerInfo>> GetServerInfos(Expression<Func<ServerInfo, bool>> whereExpression = null, string partitionKey = null);
|
||||
|
||||
public Task<ServerInfo> GetServerInfo(string ipAddress);
|
||||
Task<IEnumerable<TSelect>> GetServerInfos<TSelect>(
|
||||
Expression<Func<ServerInfo, TSelect>> selectExpression,
|
||||
Expression<Func<ServerInfo, bool>> whereExpression = null,
|
||||
string partitionKey = null);
|
||||
|
||||
public Task UpsertServerInfo(ServerInfo serverInfo);
|
||||
Task<ServerInfo> GetServerInfo(string ipAddress);
|
||||
|
||||
Task UpsertServerInfo(ServerInfo serverInfo);
|
||||
|
||||
Task DeleteServers(IEnumerable<string> ids, string partitionKey = null);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
|
||||
public interface IUpdaterStore
|
||||
{
|
||||
}
|
||||
|
@ -12,13 +12,6 @@
|
||||
{
|
||||
private ConcurrentDictionary<string, ServerInfo> servers = new ConcurrentDictionary<string, ServerInfo>();
|
||||
|
||||
public Task<IEnumerable<ServerInfo>> GetServerInfos(
|
||||
Expression<Func<ServerInfo, bool>> whereExpression = null)
|
||||
{
|
||||
return Task.FromResult(
|
||||
this.servers.Values.AsEnumerable());
|
||||
}
|
||||
|
||||
public Task<ServerInfo> GetServerInfo(string ipAddress)
|
||||
{
|
||||
if (servers.ContainsKey(ipAddress))
|
||||
@ -42,5 +35,25 @@
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<IEnumerable<ServerInfo>> GetServerInfos(Expression<Func<ServerInfo, bool>> whereExpression = null, string partitionKey = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IEnumerable<TSelect>> GetServerInfos<TSelect>(Expression<Func<ServerInfo, TSelect>> selectExpression, Expression<Func<ServerInfo, bool>> whereExpression = null, string partitionKey = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task DeleteServers(IEnumerable<string> ids, string partitionKey = null)
|
||||
{
|
||||
foreach (string id in ids)
|
||||
{
|
||||
this.servers.TryRemove(id, out _);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
using AutoMapper;
|
||||
using Giants.Services;
|
||||
using Giants.WebApi;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
@ -27,7 +26,7 @@ namespace Giants.Web
|
||||
|
||||
services.AddHttpContextAccessor();
|
||||
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
|
||||
services.AddHostedService<InitializerHostedService>();
|
||||
|
||||
|
||||
ServicesModule.RegisterServices(services, Configuration);
|
||||
|
||||
|
@ -10,5 +10,6 @@
|
||||
"DatabaseId": "DefaultDatabase",
|
||||
"ContainerId": "DefaultContainer",
|
||||
"ServerTimeoutPeriodInMinutes": "7",
|
||||
"MaxServerCount": 1000
|
||||
"ServerCleanupIntervalInMinutes": "1",
|
||||
"MaxServerCount": 1000
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user