This commit is contained in:
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -50,7 +50,7 @@
|
|||||||
"AYANOVA_DB_CONNECTION": "Server=localhost;Username=postgres;Password=raven;Database=AyaNova;CommandTimeout=120;",
|
"AYANOVA_DB_CONNECTION": "Server=localhost;Username=postgres;Password=raven;Database=AyaNova;CommandTimeout=120;",
|
||||||
//"AYANOVA_DB_CONNECTION": "Server=localhost;Username=postgres;Password=abraxis;Database=AyaNova;CommandTimeout=120;",
|
//"AYANOVA_DB_CONNECTION": "Server=localhost;Username=postgres;Password=abraxis;Database=AyaNova;CommandTimeout=120;",
|
||||||
"AYANOVA_USE_URLS": "http://*:7575;",
|
"AYANOVA_USE_URLS": "http://*:7575;",
|
||||||
"AYANOVA_REPORT_RENDERING_TIMEOUT": "5000",
|
"AYANOVA_REPORT_RENDERING_TIMEOUT": "2000",
|
||||||
"AYANOVA_REPORT_RENDERING_MAX_INSTANCES": "2",
|
"AYANOVA_REPORT_RENDERING_MAX_INSTANCES": "2",
|
||||||
"AYANOVA_FOLDER_USER_FILES": "c:\\temp\\RavenTestData\\userfiles",
|
"AYANOVA_FOLDER_USER_FILES": "c:\\temp\\RavenTestData\\userfiles",
|
||||||
"AYANOVA_FOLDER_BACKUP_FILES": "c:\\temp\\RavenTestData\\backupfiles",
|
"AYANOVA_FOLDER_BACKUP_FILES": "c:\\temp\\RavenTestData\\backupfiles",
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ namespace AyaNova.Api.Controllers
|
|||||||
//Note that this *should* normally return a 503 however we're pretty tightly wired into that meaning the server is closed at the client end which
|
//Note that this *should* normally return a 503 however we're pretty tightly wired into that meaning the server is closed at the client end which
|
||||||
//handles it at a lower level
|
//handles it at a lower level
|
||||||
//returning an OK method here allows the client to handle it at the level of the report dialog rather than the api handler which will short circuit if it was a 503
|
//returning an OK method here allows the client to handle it at the level of the report dialog rather than the api handler which will short circuit if it was a 503
|
||||||
if (!Util.ReportRenderManager.RenderSlotAvailable())
|
if (!Util.ReportRenderManager.RenderSlotAvailable(log))
|
||||||
return Ok(ApiOkResponse.Response(new { busy = true, retryafter = DateTime.UtcNow.AddMilliseconds(ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT) }));
|
return Ok(ApiOkResponse.Response(new { busy = true, retryafter = DateTime.UtcNow.AddMilliseconds(ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT) }));
|
||||||
|
|
||||||
ReportBiz biz = ReportBiz.GetBiz(ct, HttpContext);
|
ReportBiz biz = ReportBiz.GetBiz(ct, HttpContext);
|
||||||
|
|||||||
@@ -434,7 +434,7 @@ namespace AyaNova.Biz
|
|||||||
log.LogDebug("Initializing report system");
|
log.LogDebug("Initializing report system");
|
||||||
var ReportJSFolderPath = Path.Combine(ServerBootConfig.AYANOVA_CONTENT_ROOT_PATH, "resource", "rpt");
|
var ReportJSFolderPath = Path.Combine(ServerBootConfig.AYANOVA_CONTENT_ROOT_PATH, "resource", "rpt");
|
||||||
|
|
||||||
var lo = new LaunchOptions { Headless = true };
|
var lo = new LaunchOptions { Headless = true };
|
||||||
bool isWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows);
|
bool isWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows);
|
||||||
if (!isWindows)
|
if (!isWindows)
|
||||||
{
|
{
|
||||||
@@ -489,7 +489,7 @@ namespace AyaNova.Biz
|
|||||||
using (var browser = await Puppeteer.LaunchAsync(lo))
|
using (var browser = await Puppeteer.LaunchAsync(lo))
|
||||||
using (var page = await browser.NewPageAsync())
|
using (var page = await browser.NewPageAsync())
|
||||||
{
|
{
|
||||||
//mark this process so it can be cancelled if it times out
|
//track this process so it can be cancelled if it times out
|
||||||
ReportRenderManager.AddProcess(browser.Process.Id);
|
ReportRenderManager.AddProcess(browser.Process.Id);
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -732,6 +732,7 @@ namespace AyaNova.Biz
|
|||||||
{
|
{
|
||||||
log.LogDebug($"Closing browser");
|
log.LogDebug($"Closing browser");
|
||||||
await browser.CloseAsync();
|
await browser.CloseAsync();
|
||||||
|
ReportRenderManager.RemoveProcess(browser.Process.Id, log);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,55 +44,77 @@ namespace AyaNova.Util
|
|||||||
public class ReportRenderInstanceInfo
|
public class ReportRenderInstanceInfo
|
||||||
{
|
{
|
||||||
public int ReporterProcessId { get; set; }
|
public int ReporterProcessId { get; set; }
|
||||||
public DateTime Started { get; set; }
|
public DateTime Expires { get; set; }
|
||||||
|
|
||||||
public ReportRenderInstanceInfo(int processId)
|
public ReportRenderInstanceInfo(int processId)
|
||||||
{
|
{
|
||||||
ReporterProcessId = processId;
|
ReporterProcessId = processId;
|
||||||
Started = DateTime.UtcNow;
|
Expires = DateTime.UtcNow.AddMilliseconds(ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static bool RenderSlotAvailable()
|
public static bool RenderSlotAvailable(ILogger log)
|
||||||
{
|
{
|
||||||
if (_baginstances.Count >= ServerBootConfig.AYANOVA_REPORT_RENDERING_MAX_INSTANCES)
|
log.LogTrace("RenderSlotAvailable check");
|
||||||
|
var count = _baginstances.Count;
|
||||||
|
#if (DEBUG)
|
||||||
|
log.LogInformation($"DBG: RenderSlotAvailable check, there are currently {count} instances in the bag");
|
||||||
|
#endif
|
||||||
|
if (count >= ServerBootConfig.AYANOVA_REPORT_RENDERING_MAX_INSTANCES)
|
||||||
{
|
{
|
||||||
|
log.LogTrace($"RenderSlotAvailable there are no free report rendering slots available, current count is {count}, checking for expired slots to force closed");
|
||||||
//check for expired and remove
|
//check for expired and remove
|
||||||
var Instances = _baginstances.ToArray();
|
var Instances = _baginstances.ToArray();
|
||||||
ReportRenderInstanceInfo oldest = null;
|
var dtNow = DateTime.UtcNow;
|
||||||
foreach (ReportRenderInstanceInfo i in Instances)
|
foreach (ReportRenderInstanceInfo i in Instances)
|
||||||
{
|
{
|
||||||
if (oldest == null)
|
if (i.Expires < dtNow)
|
||||||
{
|
{
|
||||||
oldest = i;
|
#if (DEBUG)
|
||||||
continue;
|
log.LogInformation($"DBG: RenderSlotAvailable attempting kill of expired process {i.ReporterProcessId}");
|
||||||
|
#endif
|
||||||
|
ForceCloseProcess(i, log);
|
||||||
}
|
}
|
||||||
if (i.Started < oldest.Started) oldest = i;
|
|
||||||
}
|
}
|
||||||
if (oldest != null)
|
}
|
||||||
{
|
//allow to continue if there are now fewer than max instances in the bag
|
||||||
try
|
return _baginstances.Count < ServerBootConfig.AYANOVA_REPORT_RENDERING_MAX_INSTANCES;
|
||||||
{
|
}
|
||||||
var p = Process.GetProcessById(oldest.ReporterProcessId);
|
|
||||||
if (p != null)
|
|
||||||
{
|
|
||||||
//try to kill it
|
|
||||||
p.Kill();
|
|
||||||
if (p?.HasExited == false) return false;//can't kill it so can't free up a slot
|
|
||||||
}
|
|
||||||
_baginstances.TryTake(out oldest);
|
|
||||||
return true;//process that was there is now not there so while not perfect system we will consider it free
|
|
||||||
|
|
||||||
}
|
private static bool ForceCloseProcess(ReportRenderInstanceInfo instance, ILogger log)
|
||||||
catch (ArgumentException)
|
{
|
||||||
|
log.LogTrace($"ForceCloseProcess on instance id {instance.ReporterProcessId} started {instance.Expires.ToString()} utc");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var p = Process.GetProcessById(instance.ReporterProcessId);
|
||||||
|
if (p != null)
|
||||||
|
{
|
||||||
|
//we have an existing process
|
||||||
|
//try to kill it
|
||||||
|
p.Kill();
|
||||||
|
if (p.HasExited == false)
|
||||||
{
|
{
|
||||||
return true;//no process available / not running
|
|
||||||
|
log.LogDebug($"RenderSlotAvailable oldest slot could not be stopped");
|
||||||
|
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 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
|
||||||
|
|
||||||
}
|
}
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -101,6 +123,18 @@ namespace AyaNova.Util
|
|||||||
_baginstances.Add(new ReportRenderInstanceInfo(processId));
|
_baginstances.Add(new ReportRenderInstanceInfo(processId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void RemoveProcess(int processId, ILogger log)
|
||||||
|
{
|
||||||
|
foreach (var i in _baginstances)
|
||||||
|
{
|
||||||
|
if (i.ReporterProcessId == processId)
|
||||||
|
{
|
||||||
|
ForceCloseProcess(i, log);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// internal async static Task EnsureReporterAvailableAsync(ILogger log)
|
// internal async static Task EnsureReporterAvailableAsync(ILogger log)
|
||||||
|
|||||||
Reference in New Issue
Block a user