This commit is contained in:
2022-09-27 23:06:28 +00:00
parent 1d706b4bb7
commit 153978785c
9 changed files with 142 additions and 7 deletions

View File

@@ -20,7 +20,7 @@ Done!
License agreement page for subscription vs perpetual License agreement page for subscription vs perpetual
("Quickly, quickly, there's no time" must use a new format as this is a subscription "SERVICE" not a software product so maybe look at quickbooks license or service agreement I guess it would be called) ("Quickly, quickly, there's no time" must use a new format as this is a subscription "SERVICE" not a software product so maybe look at quickbooks license or service agreement I guess it would be called)
Must be clear customer is responsible for their data and must remain within reasonable usage levels or we will cancel the service Must be clear customer is responsible for their data and must remain within reasonable usage levels or we will cancel the service
TODO: must change the "View license agreement" link in About form to "View service agreement" and use Service agreement text to describe it
- Launch a trialing server, can start small and work up but keep all users on it so to save money and not any of our own corporate infrastructure servers - Launch a trialing server, can start small and work up but keep all users on it so to save money and not any of our own corporate infrastructure servers
"All free trial subscriptions are located in New York data center. Paid subscriptions can be in any of the following data centers of your choice ...blah blah blah." "All free trial subscriptions are located in New York data center. Paid subscriptions can be in any of the following data centers of your choice ...blah blah blah."

View File

@@ -99,7 +99,20 @@ namespace AyaNova.Api.Controllers
return Ok(ApiOkResponse.Response(await JobsBiz.GetJobStatusAsync(gid))); return Ok(ApiOkResponse.Response(await JobsBiz.GetJobStatusAsync(gid)));
} }
/// <summary>
/// Get current job status and progress for a job
/// </summary>
/// <param name="gid"></param>
/// <returns>A single job's current status and progress</returns>
[HttpGet("progress/{gid}")]
public async Task<IActionResult> GetJobProgress([FromRoute] Guid gid)
{
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
return Ok(ApiOkResponse.Response(await JobsBiz.GetJobProgressAsync(gid)));
}
/// <summary> /// <summary>
@@ -240,6 +253,23 @@ namespace AyaNova.Api.Controllers
} }
/// <summary>
/// Request cancellation of Job. Not all jobs can be cancelled.
/// </summary>
/// <param name="gid"></param>
/// <returns>Accepted</returns>
[HttpPost("request-cancel/{gid}")]
public async Task<IActionResult> RequestCancelJob([FromRoute] Guid gid)
{
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
await JobsBiz.RequestCancelAsync(gid);
return Accepted();
}
//------------ //------------

View File

@@ -596,10 +596,29 @@ namespace AyaNova.Biz
else else
idList = await ct.Customer.AsNoTracking().Select(z => z.Id).ToListAsync(); idList = await ct.Customer.AsNoTracking().Select(z => z.Id).ToListAsync();
bool SaveIt = false; bool SaveIt = false;
//case 4192
TimeSpan ProgressAndCancelCheckSpan = new TimeSpan(0, 0, ServerBootConfig.JOB_PROGRESS_UPDATE_AND_CANCEL_CHECK_SECONDS);
DateTime LastProgressCheck = DateTime.UtcNow.Subtract(new TimeSpan(1, 1, 1, 1, 1));
var TotalRecords = idList.LongCount();
long CurrentRecord = -1;
foreach (long id in idList) foreach (long id in idList)
{ {
try try
{ {
//Update progress / cancel requested?
CurrentRecord++;
if (DateUtil.IsAfterDuration(LastProgressCheck, ProgressAndCancelCheckSpan))
{
await JobsBiz.UpdateJobProgressAsync(job.GId, $"{CurrentRecord}/{TotalRecords}");
if (await JobsBiz.GetJobStatusAsync(job.GId) == JobStatus.CancelRequested)
{
break;
}
}
SaveIt = false; SaveIt = false;
ClearErrors(); ClearErrors();
Customer o = null; Customer o = null;
@@ -635,7 +654,7 @@ namespace AyaNova.Biz
FailedObjectCount++; FailedObjectCount++;
} }
} }
//TEMPORARY MEASURE AWAITING BETTER SOLUTION AND CANCELLABLE EXTENSION JOBS //TEMPORARY MEASURE AWAITING BETTER SOLUTION AND CANCELLABLE EXTENSION JOBS
//delay so we're not tying up all the resources in a tight loop //delay so we're not tying up all the resources in a tight loop
await Task.Delay(AyaNova.Util.ServerBootConfig.JOB_OBJECT_HANDLE_BATCH_JOB_LOOP_DELAY); await Task.Delay(AyaNova.Util.ServerBootConfig.JOB_OBJECT_HANDLE_BATCH_JOB_LOOP_DELAY);
} }

View File

@@ -11,7 +11,8 @@ namespace AyaNova.Biz
Sleeping = 1, Sleeping = 1,
Running = 2, Running = 2,
Completed = 3, Completed = 3,
Failed = 4 Failed = 4,
CancelRequested = 5
} }

View File

@@ -73,6 +73,16 @@ namespace AyaNova.Biz
} }
} }
/// <summary>
/// Request the cancellation of a job, not all jobs honour this
/// </summary>
/// <param name="jobId"></param>
internal static async Task RequestCancelAsync(Guid jobId)
{
await UpdateJobStatusAsync(jobId, JobStatus.CancelRequested);
await LogJobAsync(jobId, "LT:Cancel");
}
/// <summary> /// <summary>
/// Remove the job and it's logs /// Remove the job and it's logs
@@ -152,6 +162,37 @@ namespace AyaNova.Biz
return o.JobStatus; return o.JobStatus;
} }
} }
/// <summary>
/// Update the progress of a job
/// </summary>
/// <param name="jobId"></param>
/// <param name="progress"></param>
internal static async Task UpdateJobProgressAsync(Guid jobId, string progress)
{
using (AyContext ct = ServiceProviderProvider.DBContext)
{
var oFromDb = await ct.OpsJob.SingleOrDefaultAsync(z => z.GId == jobId);
if (oFromDb == null) return;
oFromDb.Progress = progress;
await ct.SaveChangesAsync();
}
}
/// <summary>
/// Get the progress and status of a job
/// </summary>
/// <param name="jobId"></param>
internal static async Task<JobProgress> GetJobProgressAsync(Guid jobId)
{
using (AyContext ct = ServiceProviderProvider.DBContext)
{
var o = await ct.OpsJob.AsNoTracking().SingleOrDefaultAsync(z => z.GId == jobId);
if (o == null) return new JobProgress() { JobStatus = JobStatus.Absent, Progress = string.Empty };
return new JobProgress() { JobStatus = o.JobStatus, Progress = o.Progress };
}
}
#endregion Job ops #endregion Job ops
#region PROCESSOR #region PROCESSOR

View File

@@ -39,6 +39,7 @@ namespace AyaNova.Models
/// </summary> /// </summary>
public string JobInfo { get; set; }//json as string of any required extra info for job public string JobInfo { get; set; }//json as string of any required extra info for job
public string Progress {get;set;}//any type of text digestible by client showing progress of job, typically just a string i.e. "133/344"
public OpsJob() public OpsJob()
{ {
@@ -53,6 +54,7 @@ namespace AyaNova.Models
AType = AyaType.NoType; AType = AyaType.NoType;
JobStatus = JobStatus.Sleeping; JobStatus = JobStatus.Sleeping;
JobInfo = null; JobInfo = null;
Progress=string.Empty;
} }

View File

@@ -0,0 +1,26 @@
using AyaNova.Biz;
using System;
namespace AyaNova.Models
{
/// <summary>
/// Job Progress
/// </summary>
public class JobProgress
{
/// <summary>
/// Progress string
/// </summary>
/// <returns>string</returns>
public string Progress { get; set; }
/// <summary>
/// Status of the job
/// </summary>
/// <returns>Job status</returns>
public JobStatus JobStatus { get; set; }
}
}

View File

@@ -20,9 +20,9 @@ namespace AyaNova.Util
/////////// CHANGE THIS ON NEW SCHEMA UPDATE //////////////////// /////////// CHANGE THIS ON NEW SCHEMA UPDATE ////////////////////
//!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!! //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!!
private const int DESIRED_SCHEMA_LEVEL = 7; private const int DESIRED_SCHEMA_LEVEL = 8;
internal const long EXPECTED_COLUMN_COUNT = 1377; internal const long EXPECTED_COLUMN_COUNT = 1378;
internal const long EXPECTED_INDEX_COUNT = 161; internal const long EXPECTED_INDEX_COUNT = 161;
internal const long EXPECTED_CHECK_CONSTRAINTS = 561; internal const long EXPECTED_CHECK_CONSTRAINTS = 561;
internal const long EXPECTED_FOREIGN_KEY_CONSTRAINTS = 204; internal const long EXPECTED_FOREIGN_KEY_CONSTRAINTS = 204;
@@ -1571,6 +1571,21 @@ $BODY$ LANGUAGE PLPGSQL STABLE");
} }
//////////////////////////////////////////////////
//
// 8.0.13 additions for job feedback
//
if (currentSchema < 8)
{
LogUpdateMessage(log);
await ExecQueryAsync("ALTER TABLE aopsjob ADD column progress TEXT");
await SetSchemaLevelAsync(++currentSchema);
}
//######################################### //#########################################
//!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!! //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!!

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@@ -15,7 +16,7 @@ namespace AyaNova.Util
//STATIC HARD CODED COMPILE TIME DEFAULTS NOT SET THROUGH CONFIG //STATIC HARD CODED COMPILE TIME DEFAULTS NOT SET THROUGH CONFIG
internal const int FAILED_AUTH_DELAY = 3000;//ms internal const int FAILED_AUTH_DELAY = 3000;//ms
internal const int JOB_OBJECT_HANDLE_BATCH_JOB_LOOP_DELAY = 200;//ms this delay is a temporary measure to ensure super big time consuming batch jobs don't use all server CPU resources internal const int JOB_OBJECT_HANDLE_BATCH_JOB_LOOP_DELAY = 200;//ms this delay is a temporary measure to ensure super big time consuming batch jobs don't use all server CPU resources
internal const int JOB_PROGRESS_UPDATE_AND_CANCEL_CHECK_SECONDS = 15;//seconds between progress updates and checks for cancellation of long running jobs
//UPLOAD LIMITS 1048576 = 1MiB for testing 10737420000 10737418240 10,737,418,240 //UPLOAD LIMITS 1048576 = 1MiB for testing 10737420000 10737418240 10,737,418,240
internal const long MAX_ATTACHMENT_UPLOAD_BYTES = 10737420000;//slight bit of overage as 10737418241=10GiB internal const long MAX_ATTACHMENT_UPLOAD_BYTES = 10737420000;//slight bit of overage as 10737418241=10GiB