This commit is contained in:
@@ -72,20 +72,6 @@ namespace AyaNova.Api.Controllers
|
||||
|
||||
|
||||
|
||||
// /// <summary>
|
||||
// /// Get Trial status of license
|
||||
// /// </summary>
|
||||
// /// <returns>True if a trial license</returns>
|
||||
// [AllowAnonymous]
|
||||
// [HttpGet("trial")]
|
||||
// public ActionResult GetTrialFlag()
|
||||
// {
|
||||
// //note: this route is called by the client as the first action so it also acts like a ping to see if the server is up as well
|
||||
// if (serverState.IsClosed)
|
||||
// return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
||||
// return Ok(ApiOkResponse.Response(AyaNova.Core.License.ActiveKey.TrialLicense));
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -121,9 +107,12 @@ namespace AyaNova.Api.Controllers
|
||||
//main thing is to not log anything but OK response
|
||||
if (ret == "ok")
|
||||
{
|
||||
//Log
|
||||
//Eventlog
|
||||
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.License, AyaEvent.LicenseFetch), ct);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
log.LogError($"LicenseController::FetchLicense - failed: {ret}");
|
||||
}
|
||||
|
||||
//any response here is OK, there's not necessarily an error, just no key or some mundane problem not technical but biz so let
|
||||
|
||||
@@ -570,7 +570,7 @@ namespace AyaNova
|
||||
if (ServerBootConfig.AYANOVA_SERVER_TEST_MODE)
|
||||
{
|
||||
_newLog.LogInformation($"Server test mode seeding, level is {ServerBootConfig.AYANOVA_SERVER_TEST_MODE_SEEDLEVEL}, tz offset is {ServerBootConfig.AYANOVA_SERVER_TEST_MODE_TZ_OFFSET}");
|
||||
AyaNova.Core.License.FetchKeyAsync(apiServerState, dbContext, _newLog).Wait();
|
||||
AyaNova.Core.License.FetchKeyAsync(apiServerState, dbContext, _newLog, true).Wait();
|
||||
//NOTE: For unit testing make sure the time zone is same as tester to ensure list filter by date tests will work because server is on same page as user in terms of time
|
||||
var seed = new Util.Seeder();
|
||||
seed.SeedDatabaseAsync(Seeder.Level.StringToSeedLevel(ServerBootConfig.AYANOVA_SERVER_TEST_MODE_SEEDLEVEL), ServerBootConfig.AYANOVA_SERVER_TEST_MODE_TZ_OFFSET).Wait();
|
||||
|
||||
@@ -155,9 +155,9 @@ namespace AyaNova.Biz
|
||||
ActivelyProcessing = true;
|
||||
try
|
||||
{
|
||||
//Sweep jobs table
|
||||
//System.Diagnostics.Debug.WriteLine($"JobsBiz processing sweeper");
|
||||
await CoreJobSweeper.DoSweepAsync();//run exclusively
|
||||
//## Critical internal jobs
|
||||
await CoreJobSweeper.DoSweepAsync();
|
||||
await CoreJobLicense.DoWorkAsync();
|
||||
|
||||
//BIZOBJECT DYNAMIC JOBS
|
||||
//get a list of exclusive jobs that are due to happen
|
||||
|
||||
@@ -4,6 +4,8 @@ using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using AyaNova.Models;
|
||||
using AyaNova.Core;
|
||||
using AyaNova.Util;
|
||||
|
||||
namespace AyaNova.Biz
|
||||
{
|
||||
@@ -16,141 +18,74 @@ namespace AyaNova.Biz
|
||||
internal static class CoreJobLicense
|
||||
{
|
||||
private static ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger("CoreJobLicense");
|
||||
private static DateTime lastCheck = DateTime.MinValue;
|
||||
private static DateTime _lastCheck = DateTime.MinValue;
|
||||
|
||||
//define timespans for various existing license scenarios
|
||||
//120 days after a new license purchase it should check for revoke from chargebacks
|
||||
//Trial should check regularly for purchase
|
||||
//revoked should do what?
|
||||
//rental licenses need to check more often than perpetual
|
||||
//slow and fast scenarios maybe? Just two timespans, fast for likely purchaser and slow for ongoing rental maint etc
|
||||
|
||||
|
||||
private static TimeSpan SWEEP_EVERY_INTERVAL = new TimeSpan(0, 30, 0);
|
||||
private static TimeSpan SUCCEEDED_JOBS_DELETE_AFTER_THIS_TIMESPAN = new TimeSpan(14, 0, 0, 0);//14 days
|
||||
private static TimeSpan FAILED_JOBS_DELETE_AFTER_THIS_TIMESPAN = new TimeSpan(14, 0, 0, 0);//14 days (gives people time to notice and look into it)
|
||||
private static TimeSpan INTERNAL_JOBS_LOGS_DELETE_AFTER_THIS_TIMESPAN = new TimeSpan(14, 0, 0, 0);//14 days
|
||||
|
||||
private static TimeSpan RUNNING_JOBS_BECOME_FAILED_AFTER_THIS_TIMESPAN = new TimeSpan(24, 0, 0);//24 hours (time running jobs are allowed to sit in "running" state before considered failed)
|
||||
private static TimeSpan FAST_TRACK = new TimeSpan(0, 0, 30, 0);
|
||||
private static TimeSpan SLOW_TRACK = new TimeSpan(24, 0, 0);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// DoSweep
|
||||
// DoWork
|
||||
//
|
||||
public static async Task DoSweepAsync()
|
||||
public static async Task DoWorkAsync()
|
||||
{
|
||||
//This will get triggered roughly every minute, but we don't want to sweep that frequently
|
||||
if (DateTime.UtcNow - lastCheck < SWEEP_EVERY_INTERVAL)
|
||||
log.LogTrace("Job starting");
|
||||
var tsSinceLastCheck = DateTime.UtcNow - _lastCheck;
|
||||
//which track are we on?
|
||||
/*
|
||||
NONE = 0,//fast track
|
||||
ActiveTrial = 1,//slow track
|
||||
ExpiredTrial = 2,//fast track
|
||||
ActivePurchased = 3,//slow track
|
||||
ExpiredPurchased = 4,//fast track
|
||||
Revoked = 5//slow track
|
||||
*/
|
||||
TimeSpan tsCheckFrequency;
|
||||
switch (AyaNova.Core.License.ActiveKey.Status)
|
||||
{
|
||||
case AyaNova.Core.License.AyaNovaLicenseKey.LicenseStatus.NONE:
|
||||
case AyaNova.Core.License.AyaNovaLicenseKey.LicenseStatus.ExpiredTrial:
|
||||
case AyaNova.Core.License.AyaNovaLicenseKey.LicenseStatus.ExpiredPurchased:
|
||||
tsCheckFrequency = FAST_TRACK;
|
||||
break;
|
||||
default:
|
||||
tsCheckFrequency = SLOW_TRACK;
|
||||
break;
|
||||
}
|
||||
log.LogTrace($"Check frequency{tsCheckFrequency}");
|
||||
if (tsSinceLastCheck < tsCheckFrequency)
|
||||
{
|
||||
log.LogTrace($"Not yet");
|
||||
return;
|
||||
}
|
||||
|
||||
log.LogTrace("Sweep starting");
|
||||
using (AyContext ct = AyaNova.Util.ServiceProviderProvider.DBContext)
|
||||
{
|
||||
//SWEEP SUCCESSFUL JOBS
|
||||
//calculate cutoff to delete
|
||||
DateTime dtDeleteCutoff = DateTime.UtcNow - SUCCEEDED_JOBS_DELETE_AFTER_THIS_TIMESPAN;
|
||||
await sweepAsync(ct, dtDeleteCutoff, JobStatus.Completed);
|
||||
|
||||
//SWEEP FAILED JOBS
|
||||
//calculate cutoff to delete
|
||||
dtDeleteCutoff = DateTime.UtcNow - FAILED_JOBS_DELETE_AFTER_THIS_TIMESPAN;
|
||||
await sweepAsync(ct, dtDeleteCutoff, JobStatus.Failed);
|
||||
|
||||
//KILL STUCK JOBS
|
||||
//calculate cutoff to delete
|
||||
DateTime dtRunningDeadline = DateTime.UtcNow - RUNNING_JOBS_BECOME_FAILED_AFTER_THIS_TIMESPAN;
|
||||
await killStuckJobsAsync(ct, dtRunningDeadline);
|
||||
|
||||
//SWEEP INTERNAL JOB LOG
|
||||
//calculate cutoff to delete
|
||||
dtDeleteCutoff = DateTime.UtcNow - INTERNAL_JOBS_LOGS_DELETE_AFTER_THIS_TIMESPAN;
|
||||
await SweepInternalJobsLogsAsync(ct, dtDeleteCutoff);
|
||||
|
||||
}
|
||||
lastCheck = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
|
||||
private static async Task sweepAsync(AyContext ct, DateTime dtDeleteCutoff, JobStatus jobStatus)
|
||||
try//allow exception to bubble up but using try block to ensure the lastcheck is set properly regardless so this doesn't spin out of control
|
||||
{
|
||||
//Get the deleteable succeeded jobs list
|
||||
var jobs = await ct.OpsJob
|
||||
.AsNoTracking()
|
||||
.Where(z => z.Created < dtDeleteCutoff && z.JobStatus == jobStatus)
|
||||
.OrderBy(z => z.Created)
|
||||
.ToListAsync();
|
||||
var ServerState = (AyaNova.Api.ControllerHelpers.ApiServerState)ServiceProviderProvider.Provider.GetService(typeof(AyaNova.Api.ControllerHelpers.ApiServerState));
|
||||
|
||||
log.LogTrace($"SweepAsync processing: cutoff={dtDeleteCutoff.ToString()}, for {jobs.Count.ToString()} jobs of status {jobStatus.ToString()}");
|
||||
|
||||
foreach (OpsJob j in jobs)
|
||||
var ret = await AyaNova.Core.License.FetchKeyAsync(ServerState, ct, log, true);
|
||||
//most often the result will be "notfound" but in future might be other results
|
||||
if (ret == "ok")
|
||||
{
|
||||
try
|
||||
//Eventlog
|
||||
await EventLogProcessor.LogEventToDatabaseAsync(new Event(1, 0, AyaType.License, AyaEvent.LicenseFetch, "FromCoreJob"), ct);
|
||||
}
|
||||
if(ret!="notfound")
|
||||
{
|
||||
|
||||
await JobsBiz.RemoveJobAndLogsAsync(j.GId);
|
||||
//I'm thinking do not log internally failed except as trace event as this would fill up log file
|
||||
//instead if they have a license issue they can do manual fetch and then that will log so they can see error
|
||||
//(also it will return an error to the client)
|
||||
log.LogTrace($"FetchLicense - failed: {ret}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
}
|
||||
finally
|
||||
{
|
||||
log.LogError(ex, "sweepAsync exception calling JobsBiz.RemoveJobAndLogsAsync");
|
||||
//for now just throw it but this needs to be removed when logging added and better handling
|
||||
throw (ex);
|
||||
_lastCheck = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Kill jobs that have been stuck in "running" state for too long
|
||||
/// </summary>
|
||||
private static async Task killStuckJobsAsync(AyContext ct, DateTime dtRunningDeadline)
|
||||
{
|
||||
//Get the deleteable succeeded jobs list
|
||||
var jobs = await ct.OpsJob
|
||||
.AsNoTracking()
|
||||
.Where(z => z.Created < dtRunningDeadline && z.JobStatus == JobStatus.Running)
|
||||
.OrderBy(z => z.Created)
|
||||
.ToListAsync();
|
||||
|
||||
log.LogTrace($"killStuckJobsAsync processing: cutoff={dtRunningDeadline.ToString()}, for {jobs.Count.ToString()} jobs of status {JobStatus.Running.ToString()}");
|
||||
|
||||
foreach (OpsJob j in jobs)
|
||||
{
|
||||
//OPSMETRIC
|
||||
await JobsBiz.LogJobAsync(j.GId, "Job took too long to run - setting to failed");
|
||||
log.LogError($"Job found job stuck in running status and set to failed: deadline={dtRunningDeadline.ToString()}, jobId={j.GId.ToString()}, jobname={j.Name}, jobtype={j.JobType.ToString()}, jobObjectType={j.ObjectType.ToString()}, jobObjectId={j.ObjectId.ToString()}");
|
||||
await JobsBiz.UpdateJobStatusAsync(j.GId, JobStatus.Failed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static async Task SweepInternalJobsLogsAsync(AyContext ct, DateTime dtDeleteCutoff)
|
||||
{
|
||||
//Get the deleteable list (this is for reporting, could easily just do it in one go)
|
||||
var logs = await ct.OpsJobLog
|
||||
.AsNoTracking()
|
||||
.Where(z => z.Created < dtDeleteCutoff)
|
||||
.OrderBy(z => z.Created)
|
||||
.ToListAsync();
|
||||
|
||||
log.LogTrace($"SweepInternalJobsLogsAsync processing: cutoff={dtDeleteCutoff.ToString()}, for {logs.Count.ToString()} log entries");
|
||||
|
||||
foreach (OpsJobLog l in logs)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ct.Database.ExecuteSqlInterpolatedAsync($"delete from aopsjoblog where gid = {l.GId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.LogError(ex, "SweepInternalJobsLogsAsync exception removed old log entries");
|
||||
throw (ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
}//eoc
|
||||
|
||||
|
||||
}//eons
|
||||
|
||||
|
||||
@@ -86,12 +86,12 @@ namespace AyaNova.Core
|
||||
|
||||
public enum LicenseStatus
|
||||
{
|
||||
NONE = 0,
|
||||
ActiveTrial = 1,
|
||||
ExpiredTrial = 2,
|
||||
ActivePurchased = 3,
|
||||
ExpiredPurchased = 4,
|
||||
Revoked = 5
|
||||
NONE = 0,//fast track
|
||||
ActiveTrial = 1,//slow track
|
||||
ExpiredTrial = 2,//fast track
|
||||
ActivePurchased = 3,//slow track
|
||||
ExpiredPurchased = 4,//fast track
|
||||
Revoked = 5//slow track
|
||||
}
|
||||
|
||||
public AyaNovaLicenseKey()
|
||||
@@ -174,7 +174,6 @@ namespace AyaNova.Core
|
||||
return LicenseStatus.ActivePurchased;
|
||||
if (!TrialLicense && LicenseExpired)
|
||||
return LicenseStatus.ExpiredPurchased;
|
||||
|
||||
throw new System.Exception("License::Status - unable to determine license status");
|
||||
}
|
||||
}
|
||||
@@ -484,9 +483,13 @@ namespace AyaNova.Core
|
||||
/// Fetch a key, validate it and install it in the db then initialize with it
|
||||
/// </summary>
|
||||
/// <returns>Result string</returns>
|
||||
internal static async Task<string> FetchKeyAsync(AyaNova.Api.ControllerHelpers.ApiServerState apiServerState, AyContext ct, ILogger log, bool calledFromInternalJob = true)
|
||||
internal static async Task<string> FetchKeyAsync(AyaNova.Api.ControllerHelpers.ApiServerState apiServerState, AyContext ct, ILogger log, bool calledFromInternalJob)
|
||||
{
|
||||
if (calledFromInternalJob)
|
||||
log.LogTrace($"Fetching license for DBID {LicenseDbId.ToString()} (called by job)");
|
||||
else
|
||||
log.LogInformation($"Fetching license for DBID {LicenseDbId.ToString()}");
|
||||
|
||||
string sUrl = $"{LICENSE_SERVER_URL}rvf/{LicenseDbId.ToString()}";
|
||||
try
|
||||
{
|
||||
@@ -504,11 +507,8 @@ namespace AyaNova.Core
|
||||
if (ParsedKey != null)
|
||||
{
|
||||
await InstallAsync(keyText, ParsedKey, apiServerState, ct, log);
|
||||
log.LogInformation($"Installed new license key {ParsedKey.Id}");
|
||||
//Log the license info so it's on the record
|
||||
log.LogInformation($"License - new key installed [{AyaNova.Core.License.LicenseInfoLogFormat}]");
|
||||
|
||||
|
||||
return "ok";
|
||||
}
|
||||
return $"E1020 - Error fetching license key: No key was returned";
|
||||
|
||||
Reference in New Issue
Block a user