Replace IServiceProvider parameter in job execution with IDatabaseContext

This commit is contained in:
Cyberboss
2018-09-18 10:40:14 -04:00
parent ee2dc83195
commit 4f99568d41
9 changed files with 59 additions and 77 deletions
@@ -4,7 +4,6 @@ using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Host.Components.Byond;
using Tgstation.Server.Host.Components.Chat;
using Tgstation.Server.Host.Components.Compiler;
using Tgstation.Server.Host.Components.Repository;
using Tgstation.Server.Host.Components.StaticFiles;
using Tgstation.Server.Host.Components.Watchdog;
@@ -65,10 +64,10 @@ namespace Tgstation.Server.Host.Components
/// Run the compile job and insert it into the database. Meant to be called by a <see cref="Core.IJobManager"/>
/// </summary>
/// <param name="job">The running <see cref="Job"/></param>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> for the operation</param>
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> for the operation</param>
/// <param name="progressReporter">The <see cref="Action{T1}"/> to report compilation progress</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task CompileProcess(Job job, IServiceProvider serviceProvider, Action<int> progressReporter, CancellationToken cancellationToken);
Task CompileProcess(Job job, IDatabaseContext databaseContext, Action<int> progressReporter, CancellationToken cancellationToken);
}
}
@@ -1,5 +1,4 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
@@ -117,18 +116,16 @@ namespace Tgstation.Server.Host.Components
}
/// <inheritdoc />
public async Task CompileProcess(Job job, IServiceProvider serviceProvider, Action<int> progressReporter, CancellationToken cancellationToken)
public async Task CompileProcess(Job job, IDatabaseContext databaseContext, Action<int> progressReporter, CancellationToken cancellationToken)
{
//DO NOT FOLLOW THE SUGGESTION FOR A THROW EXPRESSION HERE
if (job == null)
throw new ArgumentNullException(nameof(job));
if (serviceProvider == null)
throw new ArgumentNullException(nameof(serviceProvider));
if (databaseContext == null)
throw new ArgumentNullException(nameof(databaseContext));
if (progressReporter == null)
throw new ArgumentNullException(nameof(progressReporter));
var databaseContext = serviceProvider.GetRequiredService<IDatabaseContext>();
var ddSettingsTask = databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == metadata.Id).Select(x => new DreamDaemonSettings
{
StartupTimeout = x.StartupTimeout,
@@ -205,10 +202,9 @@ namespace Tgstation.Server.Host.Components
};
var noRepo = false;
await jobManager.RegisterOperation(repositoryUpdateJob, async (paramJob, serviceProvider, progressReporter, jobCancellationToken) =>
await jobManager.RegisterOperation(repositoryUpdateJob, async (paramJob, databaseContext, progressReporter, jobCancellationToken) =>
{
var db = serviceProvider.GetRequiredService<IDatabaseContext>();
var repositorySettingsTask = db.RepositorySettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(jobCancellationToken);
var repositorySettingsTask = databaseContext.RepositorySettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(jobCancellationToken);
//assume 5 steps with synchronize
const int ProgressSections = 5;
@@ -836,7 +836,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
CancelRight = (ulong)DreamDaemonRights.Shutdown,
CancelRightsType = RightsType.DreamDaemon
};
await jobManager.RegisterOperation(job, (j, serviceProvider, progressFunction, ct) => Launch(ct), cancellationToken).ConfigureAwait(false);
await jobManager.RegisterOperation(job, (j, databaseContext, progressFunction, ct) => Launch(ct), cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc />
@@ -95,7 +95,7 @@ namespace Tgstation.Server.Host.Controllers
CancelRight = (ulong)ByondRights.CancelInstall,
Instance = Instance
};
await jobManager.RegisterOperation(job, (paramJob, serviceProvicer, progressHandler, ct) => byondManager.ChangeVersion(installingVersion, ct), cancellationToken).ConfigureAwait(false);
await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressHandler, ct) => byondManager.ChangeVersion(installingVersion, ct), cancellationToken).ConfigureAwait(false);
result.InstallJob = job.ToApi();
}
result.Version = byondManager.ActiveVersion;
@@ -67,7 +67,7 @@ namespace Tgstation.Server.Host.Controllers
StartedBy = AuthenticationContext.User
};
await jobManager.RegisterOperation(job,
async (paramJob, serviceProvider, progressHandler, innerCt) =>
async (paramJob, databaseContext, progressHandler, innerCt) =>
{
var result = await instance.Watchdog.Launch(innerCt).ConfigureAwait(false);
if (result == null)
@@ -231,7 +231,7 @@ namespace Tgstation.Server.Host.Controllers
var watchdog = instanceManager.GetInstance(Instance).Watchdog;
await jobManager.RegisterOperation(job, (paramJob, serviceProvider, progressReporter, ct) => watchdog.Restart(false, ct), cancellationToken).ConfigureAwait(false);
await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressReporter, ct) => watchdog.Restart(false, ct), cancellationToken).ConfigureAwait(false);
return Accepted(job.ToApi());
}
}
@@ -352,7 +352,7 @@ namespace Tgstation.Server.Host.Controllers
StartedBy = AuthenticationContext.User
};
await jobManager.RegisterOperation(job, (paramJob, serviceProvider, progressHandler, ct) => instanceManager.MoveInstance(originalModel, rawPath, ct), cancellationToken).ConfigureAwait(false);
await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressHandler, ct) => instanceManager.MoveInstance(originalModel, rawPath, ct), cancellationToken).ConfigureAwait(false);
api.MoveJob = job.ToApi();
}
@@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
@@ -186,20 +185,19 @@ namespace Tgstation.Server.Host.Controllers
Instance = Instance
};
var api = currentModel.ToApi();
await jobManager.RegisterOperation(job, async (paramJob, serviceProvider, progressReporter, ct) =>
await jobManager.RegisterOperation(job, async (paramJob, databaseContext, progressReporter, ct) =>
{
using (var repos = await repoManager.CloneRepository(new Uri(origin), cloneBranch, currentModel.AccessUser, currentModel.AccessToken, progressReporter, ct).ConfigureAwait(false))
{
if (repos == null)
throw new JobException("Filesystem conflict while cloning repository!");
var db = serviceProvider.GetRequiredService<IDatabaseContext>();
var instance = new Models.Instance
{
Id = Instance.Id
};
db.Instances.Attach(instance);
if (await PopulateApi(api, repos, db, instance, ct).ConfigureAwait(false))
await db.Save(ct).ConfigureAwait(false);
databaseContext.Instances.Attach(instance);
if (await PopulateApi(api, repos, databaseContext, instance, ct).ConfigureAwait(false))
await databaseContext.Save(ct).ConfigureAwait(false);
}
}, cancellationToken).ConfigureAwait(false);
@@ -238,7 +236,7 @@ namespace Tgstation.Server.Host.Controllers
Instance = Instance
};
var api = currentModel.ToApi();
await jobManager.RegisterOperation(job, (paramJob, serviceProvider, progressReporter, ct) => instanceManager.GetInstance(Instance).RepositoryManager.DeleteRepository(cancellationToken), cancellationToken).ConfigureAwait(false);
await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressReporter, ct) => instanceManager.GetInstance(Instance).RepositoryManager.DeleteRepository(cancellationToken), cancellationToken).ConfigureAwait(false);
api.ActiveJob = job.ToApi();
return Accepted(api);
}
@@ -419,7 +417,7 @@ namespace Tgstation.Server.Host.Controllers
CancelRight = (ulong)RepositoryRights.CancelPendingChanges,
};
await jobManager.RegisterOperation(job, async (paramJob, serviceProvider, progressReporter, ct) =>
await jobManager.RegisterOperation(job, async (paramJob, databaseContext, progressReporter, ct) =>
{
using (var repo = await repoManager.LoadRepository(ct).ConfigureAwait(false))
{
@@ -443,8 +441,7 @@ namespace Tgstation.Server.Host.Controllers
//get a base line for where we are
Models.RevisionInformation lastRevisionInfo = null;
var databaseContext = serviceProvider.GetRequiredService<IDatabaseContext>();
var attachedInstance = new Models.Instance
{
Id = Instance.Id
@@ -20,10 +20,10 @@ namespace Tgstation.Server.Host.Core
/// Registers a given <see cref="Job"/> and begins running it
/// </summary>
/// <param name="job">The <see cref="Job"/></param>
/// <param name="operation">The operation to run taking the started <see cref="Job"/>, a <see cref="IServiceProvider"/> progress reporter <see cref="Action{T1}"/> and a <see cref="CancellationToken"/></param>
/// <param name="operation">The operation to run taking the started <see cref="Job"/>, a <see cref="IDatabaseContext"/>, progress reporter <see cref="Action{T1}"/> and a <see cref="CancellationToken"/></param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing a running operation</returns>
Task RegisterOperation(Job job, Func<Job, IServiceProvider, Action<int>, CancellationToken, Task> operation, CancellationToken cancellationToken);
Task RegisterOperation(Job job, Func<Job, IDatabaseContext, Action<int>, CancellationToken, Task> operation, CancellationToken cancellationToken);
/// <summary>
/// Wait for a given <paramref name="job"/> to complete
+38 -48
View File
@@ -1,5 +1,4 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
@@ -16,7 +15,7 @@ namespace Tgstation.Server.Host.Core
/// <summary>
/// The <see cref="IServiceProvider"/> for the <see cref="JobManager"/>
/// </summary>
readonly IServiceProvider serviceProvider;
readonly IDatabaseContextFactory databaseContextFactory;
/// <summary>
/// The <see cref="ILogger"/> for the <see cref="JobManager"/>
@@ -31,11 +30,11 @@ namespace Tgstation.Server.Host.Core
/// <summary>
/// Construct a <see cref="JobManager"/>
/// </summary>
/// <param name="serviceProvider">The value of <see cref="serviceProvider"/></param>
/// <param name="databaseContextFactory">The value of <see cref="databaseContextFactory"/></param>
/// <param name="logger">The value of <see cref="logger"/></param>
public JobManager(IServiceProvider serviceProvider, ILogger<JobManager> logger)
public JobManager(IDatabaseContextFactory databaseContextFactory, ILogger<JobManager> logger)
{
this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
jobs = new Dictionary<long, JobHandler>();
}
@@ -69,11 +68,11 @@ namespace Tgstation.Server.Host.Core
/// <param name="operation">The operation for the <paramref name="job"/></param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
async Task RunJob(Job job, Func<Job, IServiceProvider, CancellationToken, Task> operation, CancellationToken cancellationToken)
async Task RunJob(Job job, Func<Job, IDatabaseContext, CancellationToken, Task> operation, CancellationToken cancellationToken)
{
try
{
using (var scope = serviceProvider.CreateScope())
await databaseContextFactory.UseContext(async databaseContext =>
{
async Task HandleExceptions(Task task)
{
@@ -97,15 +96,13 @@ namespace Tgstation.Server.Host.Core
}
}
IDatabaseContext databaseContext = null;
async Task RunJobInternal()
{
var oldJob = job;
job = new Job { Id = oldJob.Id };
databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
databaseContext.Jobs.Attach(job);
await operation(job, scope.ServiceProvider, cancellationToken).ConfigureAwait(false);
await operation(job, databaseContext, cancellationToken).ConfigureAwait(false);
logger.LogDebug("Job {0} completed!", job.Id);
};
@@ -123,7 +120,7 @@ namespace Tgstation.Server.Host.Core
if (JobErroredOrCancelled())
await databaseContext.Save(default).ConfigureAwait(false);
}
}
}).ConfigureAwait(false);
}
finally
{
@@ -137,50 +134,44 @@ namespace Tgstation.Server.Host.Core
}
/// <inheritdoc />
public async Task RegisterOperation(Job job, Func<Job, IServiceProvider, Action<int>, CancellationToken, Task> operation, CancellationToken cancellationToken)
public Task RegisterOperation(Job job, Func<Job, IDatabaseContext, Action<int>, CancellationToken, Task> operation, CancellationToken cancellationToken) => databaseContextFactory.UseContext(async databaseContext =>
{
using (var scope = serviceProvider.CreateScope())
job.StartedAt = DateTimeOffset.Now;
job.Cancelled = false;
job.Instance = new Instance
{
var databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
job.StartedAt = DateTimeOffset.Now;
job.Cancelled = false;
job.Instance = new Instance
Id = job.Instance.Id
};
databaseContext.Instances.Attach(job.Instance);
if (job.StartedBy != null)
{
job.StartedBy = new User
{
Id = job.Instance.Id
Id = job.StartedBy.Id
};
databaseContext.Instances.Attach(job.Instance);
if (job.StartedBy != null)
{
job.StartedBy = new User
{
Id = job.StartedBy.Id
};
databaseContext.Users.Attach(job.StartedBy);
}
databaseContext.Jobs.Add(job);
await databaseContext.Save(cancellationToken).ConfigureAwait(false);
logger.LogDebug("Starting job {0}: {1}...", job.Id, job.Description);
var jobHandler = JobHandler.Create(x => RunJob(job, (jobParam, serviceProvider, ct) =>
operation(jobParam, serviceProvider, y =>
{
lock (this)
if (jobs.TryGetValue(job.Id, out var handler))
handler.Progress = y;
}, ct),
x));
lock (this)
jobs.Add(job.Id, jobHandler);
databaseContext.Users.Attach(job.StartedBy);
}
}
databaseContext.Jobs.Add(job);
await databaseContext.Save(cancellationToken).ConfigureAwait(false);
logger.LogDebug("Starting job {0}: {1}...", job.Id, job.Description);
var jobHandler = JobHandler.Create(x => RunJob(job, (jobParam, serviceProvider, ct) =>
operation(jobParam, serviceProvider, y =>
{
lock (this)
if (jobs.TryGetValue(job.Id, out var handler))
handler.Progress = y;
}, ct),
x));
lock (this)
jobs.Add(job.Id, jobHandler);
});
/// <inheritdoc />
public async Task StartAsync(CancellationToken cancellationToken)
{
logger.LogTrace("Starting job manager...");
using (var scope = serviceProvider.CreateScope())
await databaseContextFactory.UseContext(async databaseContext =>
{
var databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
//mark all jobs as cancelled
var badJobs = await databaseContext.Jobs.Where(y => !y.StoppedAt.HasValue).Select(y => y.Id).ToListAsync(cancellationToken).ConfigureAwait(false);
if (badJobs.Count > 0)
@@ -195,7 +186,7 @@ namespace Tgstation.Server.Host.Core
}
await databaseContext.Save(cancellationToken).ConfigureAwait(false);
}
}
}).ConfigureAwait(false);
logger.LogDebug("Job manager started!");
}
@@ -228,9 +219,8 @@ namespace Tgstation.Server.Host.Core
return false;
}
handler.Cancel(); //this will ensure the db update is only done once
using (var scope = serviceProvider.CreateScope())
await databaseContextFactory.UseContext(async databaseContext =>
{
var databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
job = new Job { Id = job.Id };
databaseContext.Jobs.Attach(job);
user = new User { Id = user.Id };
@@ -238,7 +228,7 @@ namespace Tgstation.Server.Host.Core
job.CancelledBy = user;
//let either startup or cancellation set job.cancelled
await databaseContext.Save(cancellationToken).ConfigureAwait(false);
}
}).ConfigureAwait(false);
if (blocking)
await handler.Wait(cancellationToken).ConfigureAwait(false);
return true;