Nullify InstanceManager

This commit is contained in:
Jordan Dominion
2023-12-20 23:11:33 -05:00
parent 476e903bb0
commit 5bffb97cdb
5 changed files with 38 additions and 29 deletions
@@ -10,6 +10,6 @@
/// </summary>
/// <param name="instance">The <see cref="Instance"/> to get the <see cref="IInstanceCore"/> for.</param>
/// <returns>The <see cref="IInstanceCore"/> if it is online, <see langword="null"/> otherwise.</returns>
IInstanceCore GetInstance(Models.Instance instance);
IInstanceCore? GetInstance(Models.Instance instance);
}
}
@@ -19,6 +19,6 @@ namespace Tgstation.Server.Host.Components
/// </summary>
/// <param name="metadata">The <see cref="Models.Instance"/> of the desired <see cref="IInstance"/>.</param>
/// <returns>The <see cref="IInstance"/> associated with the given <paramref name="metadata"/> if it is online, <see langword="null"/> otherwise.</returns>
IInstanceReference GetInstanceReference(Api.Models.Instance metadata);
IInstanceReference? GetInstanceReference(Api.Models.Instance metadata);
}
}
@@ -21,13 +21,12 @@ using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
using Tgstation.Server.Host.Swarm;
using Tgstation.Server.Host.System;
using Tgstation.Server.Host.Utils;
#nullable disable
namespace Tgstation.Server.Host.Components
{
/// <inheritdoc cref="IInstanceManager" />
@@ -117,7 +116,7 @@ namespace Tgstation.Server.Host.Components
readonly Dictionary<string, IBridgeHandler> bridgeHandlers;
/// <summary>
/// <see cref="SemaphoreSlim"/> used to guard calls to <see cref="OnlineInstance(Models.Instance, CancellationToken)"/> and <see cref="OfflineInstance(Models.Instance, Models.User, CancellationToken)"/>.
/// <see cref="SemaphoreSlim"/> used to guard calls to <see cref="OnlineInstance(Models.Instance, CancellationToken)"/> and <see cref="OfflineInstance(Models.Instance, User, CancellationToken)"/>.
/// </summary>
readonly SemaphoreSlim instanceStateChangeSemaphore;
@@ -149,12 +148,12 @@ namespace Tgstation.Server.Host.Components
/// <summary>
/// The original <see cref="IConsole.Title"/> of <see cref="console"/>.
/// </summary>
readonly string originalConsoleTitle;
readonly string? originalConsoleTitle;
/// <summary>
/// The <see cref="Task"/> returned by <see cref="Initialize(CancellationToken)"/>.
/// </summary>
Task startupTask;
Task? startupTask;
/// <summary>
/// If the <see cref="InstanceManager"/> has been <see cref="DisposeAsync"/>'d.
@@ -243,13 +242,13 @@ namespace Tgstation.Server.Host.Components
}
/// <inheritdoc />
public IInstanceReference GetInstanceReference(Api.Models.Instance metadata)
public IInstanceReference? GetInstanceReference(Api.Models.Instance metadata)
{
ArgumentNullException.ThrowIfNull(metadata);
lock (instances)
{
if (!instances.TryGetValue(metadata.Id.Value, out var instance))
if (!instances.TryGetValue(metadata.Require(x => x.Id), out var instance))
return null;
return instance.AddReference();
@@ -265,7 +264,7 @@ namespace Tgstation.Server.Host.Components
using var instanceReferenceCheck = GetInstanceReference(instance);
if (instanceReferenceCheck != null)
throw new InvalidOperationException("Cannot move an online instance!");
var newPath = instance.Path;
var newPath = instance.Path!;
try
{
await ioManager.MoveDirectory(oldPath, newPath, cancellationToken);
@@ -328,22 +327,23 @@ namespace Tgstation.Server.Host.Components
}
/// <inheritdoc />
public async ValueTask OfflineInstance(Models.Instance metadata, Models.User user, CancellationToken cancellationToken)
public async ValueTask OfflineInstance(Models.Instance metadata, User user, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(metadata);
using (await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken))
{
ReferenceCountingContainer<IInstance, InstanceWrapper> container;
ReferenceCountingContainer<IInstance, InstanceWrapper>? container;
var instanceId = metadata.Require(x => x.Id);
lock (instances)
{
if (!instances.TryGetValue(metadata.Id.Value, out container))
if (!instances.TryGetValue(instanceId, out container))
{
logger.LogDebug("Not offlining removed instance {instanceId}", metadata.Id);
return;
}
instances.Remove(metadata.Id.Value);
instances.Remove(instanceId);
}
logger.LogInformation("Offlining instance ID {instanceId}", metadata.Id);
@@ -353,15 +353,15 @@ namespace Tgstation.Server.Host.Components
await container.OnZeroReferences.WaitAsync(cancellationToken);
// we are the one responsible for cancelling his jobs
ValueTask<Models.Job[]> groupedTask = default;
ValueTask<Job?[]> groupedTask = default;
await databaseContextFactory.UseContext(
async db =>
{
var jobs = await db
.Jobs
.AsQueryable()
.Where(x => x.Instance.Id == metadata.Id && !x.StoppedAt.HasValue)
.Select(x => new Models.Job(x.Id.Value))
.Where(x => x.Instance!.Id == metadata.Id && !x.StoppedAt.HasValue)
.Select(x => new Job(x.Id!.Value))
.ToListAsync(cancellationToken);
groupedTask = ValueTaskExtensions.WhenAll(
@@ -375,7 +375,7 @@ namespace Tgstation.Server.Host.Components
{
// not too late to change your mind
lock (instances)
instances.Add(metadata.Id.Value, container);
instances.Add(instanceId, container);
throw;
}
@@ -397,9 +397,10 @@ namespace Tgstation.Server.Host.Components
{
ArgumentNullException.ThrowIfNull(metadata);
var instanceId = metadata.Require(x => x.Id);
using var lockContext = await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken);
lock (instances)
if (instances.ContainsKey(metadata.Id.Value))
if (instances.ContainsKey(instanceId))
{
logger.LogDebug("Aborting instance creation due to it seemingly already being online");
return;
@@ -415,7 +416,7 @@ namespace Tgstation.Server.Host.Components
{
lock (instances)
instances.Add(
metadata.Id.Value,
instanceId,
new ReferenceCountingContainer<IInstance, InstanceWrapper>(instance));
}
catch (Exception ex)
@@ -457,6 +458,12 @@ namespace Tgstation.Server.Host.Components
using (cancellationToken.Register(shutdownCancellationTokenSource.Cancel))
try
{
if (startupTask == null)
{
logger.LogWarning("InstanceManager was never started!");
return;
}
logger.LogDebug("Stopping instance manager...");
if (!startupTask.IsCompleted)
@@ -499,11 +506,11 @@ namespace Tgstation.Server.Host.Components
}
/// <inheritdoc />
public async ValueTask<BridgeResponse> ProcessBridgeRequest(BridgeParameters parameters, CancellationToken cancellationToken)
public async ValueTask<BridgeResponse?> ProcessBridgeRequest(BridgeParameters parameters, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(parameters);
IBridgeHandler bridgeHandler = null;
IBridgeHandler? bridgeHandler = null;
for (var i = 0; bridgeHandler == null && i < 30; ++i)
{
// There's a miniscule time period where we could potentially receive a bridge request and not have the registration ready when we launch DD
@@ -550,11 +557,11 @@ namespace Tgstation.Server.Host.Components
}
/// <inheritdoc />
public IInstanceCore GetInstance(Models.Instance metadata)
public IInstanceCore? GetInstance(Models.Instance metadata)
{
lock (instances)
{
instances.TryGetValue(metadata.Id.Value, out var container);
instances.TryGetValue(metadata.Require(x => x.Id), out var container);
return container?.Instance;
}
}
@@ -578,13 +585,13 @@ namespace Tgstation.Server.Host.Components
await InitializeSwarm(cancellationToken);
List<Models.Instance> dbInstances = null;
List<Models.Instance>? dbInstances = null;
async ValueTask EnumerateInstances(IDatabaseContext databaseContext)
=> dbInstances = await databaseContext
.Instances
.AsQueryable()
.Where(x => x.Online.Value && x.SwarmIdentifer == swarmConfiguration.Identifier)
.Where(x => x.Online!.Value && x.SwarmIdentifer == swarmConfiguration.Identifier)
.Include(x => x.RepositorySettings)
.Include(x => x.ChatSettings)
.ThenInclude(x => x.Channels)
@@ -598,7 +605,7 @@ namespace Tgstation.Server.Host.Components
await Task.WhenAll(instanceEnumeration.AsTask(), factoryStartup, jobManagerStartup);
var instanceOnliningTasks = dbInstances.Select(
var instanceOnliningTasks = dbInstances!.Select(
async metadata =>
{
try
@@ -129,7 +129,7 @@ namespace Tgstation.Server.Host.Controllers
instance ??= Instance ?? throw new InvalidOperationException("ComponentInterfacingController has no Instance!");
using var instanceReference = instanceManager.GetInstanceReference(instance);
using (LogContext.PushProperty(SerilogContextHelper.InstanceReferenceContextProperty, instanceReference.Uid))
using (LogContext.PushProperty(SerilogContextHelper.InstanceReferenceContextProperty, instanceReference?.Uid))
{
if (instanceReference == null)
return Conflict(new ErrorMessageResponse(ErrorCode.InstanceOffline));
+3 -1
View File
@@ -460,8 +460,10 @@ namespace Tgstation.Server.Host.Jobs
QueueHubUpdate(job.ToApi(), false);
logger.LogTrace("Starting job...");
var instanceCore = instanceCoreProvider.GetInstance(job.Instance!)
?? throw new JobException("Could not retrieve instance core for job!");
await operation(
instanceCoreProvider.GetInstance(job.Instance!),
instanceCore,
databaseContextFactory,
job,
new JobProgressReporter(