164 lines
6.2 KiB
C#
164 lines
6.2 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.Threading.Tasks;
|
|
using Sockeye.Biz;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Sockeye.Util
|
|
{
|
|
|
|
//Track processes and kill any that go past their expiry date
|
|
internal static class ReportRenderManager
|
|
{
|
|
|
|
internal static ConcurrentBag<ReportRenderInstanceInfo> _baginstances;
|
|
|
|
static ReportRenderManager()
|
|
{
|
|
_baginstances = new ConcurrentBag<ReportRenderInstanceInfo>();
|
|
}
|
|
|
|
internal class ReportRenderInstanceInfo
|
|
{
|
|
internal int ReporterProcessId { get; set; }
|
|
internal DateTime Expires { get; set; }
|
|
internal Guid JobId { get; set; }
|
|
|
|
internal ReportRenderInstanceInfo(Guid jobId, DateTime expires)
|
|
{
|
|
JobId = jobId;
|
|
Expires = expires;
|
|
ReporterProcessId = -1;
|
|
}
|
|
}
|
|
|
|
|
|
internal static async Task KillExpiredRenders(ILogger log)
|
|
{
|
|
log.LogDebug("Clear potential expired render jobs check");
|
|
//check for expired and remove
|
|
var Instances = _baginstances.ToArray();
|
|
var dtNow = DateTime.UtcNow;
|
|
foreach (ReportRenderInstanceInfo i in Instances)
|
|
{
|
|
if (i.Expires < dtNow)
|
|
{
|
|
log.LogDebug($"attempting close of expired process {i.ReporterProcessId} for job {i.JobId}");
|
|
await CloseRenderProcess(i, log, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static async Task<bool> CloseRenderProcess(ReportRenderInstanceInfo instance, ILogger log, bool force)
|
|
{
|
|
if (force)
|
|
log.LogDebug($"Force CloseRenderProcess on report render instance id {instance.ReporterProcessId} expiry {instance.Expires.ToString()} utc");
|
|
else
|
|
log.LogDebug($"Normal CloseRenderProcess on report render instance id {instance.ReporterProcessId}");
|
|
|
|
try
|
|
{
|
|
//either way, clear the job so the client gets informed
|
|
if (force)
|
|
{
|
|
if (instance.Expires < DateTime.UtcNow)
|
|
{
|
|
var json = Newtonsoft.Json.JsonConvert.SerializeObject(new { rendererror = new { timeout = true, timeoutsetting = ServerBootConfig.SOCKEYE_REPORT_RENDERING_TIMEOUT } }, Newtonsoft.Json.Formatting.None);
|
|
await JobsBiz.LogJobAsync(instance.JobId, json);
|
|
await JobsBiz.UpdateJobStatusAsync(instance.JobId, JobStatus.Failed);
|
|
}
|
|
else
|
|
{
|
|
var json = Newtonsoft.Json.JsonConvert.SerializeObject(new { rendererror = new { cancelled = true} }, Newtonsoft.Json.Formatting.None);
|
|
await JobsBiz.LogJobAsync(instance.JobId, json);
|
|
await JobsBiz.UpdateJobStatusAsync(instance.JobId, JobStatus.Completed);
|
|
}
|
|
}
|
|
|
|
if (instance.ReporterProcessId != -1)//if a job doesn't have a process id yet it will be -1
|
|
{
|
|
var p = Process.GetProcessById(instance.ReporterProcessId);
|
|
if (p != null)
|
|
{
|
|
//we have an existing process
|
|
//try to kill it
|
|
p.Kill(true);
|
|
if (p.HasExited == false)
|
|
{
|
|
log.LogWarning($"Expired report render instance id {instance.ReporterProcessId} could not be force closed");
|
|
return false;//can't kill it so can't free up a slot
|
|
}
|
|
}
|
|
}
|
|
//remove it from the list, it's either gone or killed at this point
|
|
//this would not be unexpected since it will normally just close on it's own
|
|
//at the finally block in render report
|
|
_baginstances.TryTake(out instance);
|
|
return true;//process that was there is now not there so while not perfect system we will consider it free
|
|
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
//do nothing, this is normal, the process could not be found and this means it's already been removed:
|
|
//ArgumentException
|
|
//The process specified by the processId parameter is not running. The identifier might be expired.
|
|
_baginstances.TryTake(out instance);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
internal static void AddJob(Guid jobId, ILogger log)
|
|
{
|
|
log.LogDebug($"AddJob - {jobId} to the collection");
|
|
_baginstances.Add(new ReportRenderInstanceInfo(jobId, DateTime.UtcNow.AddMinutes(ServerBootConfig.SOCKEYE_REPORT_RENDERING_TIMEOUT)));
|
|
log.LogDebug($"AddJob - there are currently {_baginstances.Count} instances in the collection");
|
|
}
|
|
|
|
|
|
internal static void SetProcess(Guid jobId, int processId, ILogger log)
|
|
{
|
|
log.LogDebug($"SetProcess - setting {jobId} to render process id {processId}");
|
|
foreach (var i in _baginstances)
|
|
{
|
|
if (i.JobId == jobId)
|
|
{
|
|
i.ReporterProcessId = processId;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static async Task RemoveJob(Guid jobId, ILogger log, bool force)
|
|
{
|
|
foreach (var i in _baginstances)
|
|
{
|
|
if (i.JobId == jobId)
|
|
{
|
|
await CloseRenderProcess(i, log, force);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
internal static bool KeepGoing(Guid jobId)
|
|
{
|
|
|
|
//if job id is empty it means it was called from outside of a job (report designer get data for example)
|
|
if(jobId==Guid.Empty) return true;
|
|
foreach (var i in _baginstances)
|
|
{
|
|
if (i.JobId == jobId)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
}//eoc
|
|
}//eons |