Add queue hardening by modifying DeploymentJobTargets, DeploymentJobSteps, and DeploymentJobs tables
- Drop existing indexes on DeploymentJobTargets and DeploymentJobSteps - Alter Status column to nvarchar(450) in DeploymentJobTargets, DeploymentJobSteps, and DeploymentJobs - Add new columns: Finished, OutputMetadataJson, Started to DeploymentJobTargets; ErrorMessage, Finished, OutputMetadataJson, Started to DeploymentJobSteps; CorrelationId, HeartbeatAt, Priority, RowVersion, ScheduledAt, WorkerName to DeploymentJobs - Create new indexes for improved query performance - Add constraints to ensure OutputMetadataJson is valid JSON - Record migration in __EFMigrationsHistory
This commit is contained in:
@@ -37,6 +37,7 @@ namespace Microsoft.SelfService.Portal.Core.API.Services
|
||||
{
|
||||
Type = QueueJobType.TemplateJsonChanged,
|
||||
Status = QueueJobStatus.Pending,
|
||||
CorrelationId = Guid.NewGuid(),
|
||||
PayloadJson = JsonSerializer.Serialize(payload),
|
||||
Targets = deployments.Select(deployment => new QueueJobTargetModel
|
||||
{
|
||||
@@ -66,6 +67,10 @@ namespace Microsoft.SelfService.Portal.Core.API.Services
|
||||
.Include(group => group.Template)
|
||||
.ThenInclude(template => template.DeploymentRule)
|
||||
.ThenInclude(rule => rule.Steps)
|
||||
.Include(group => group.TemplateSelections)
|
||||
.ThenInclude(selection => selection.TemplateVersion)
|
||||
.ThenInclude(version => version.Template)
|
||||
.Include(group => group.TargetAssignments)
|
||||
.Include(group => group.DeploymentRule)
|
||||
.ThenInclude(rule => rule!.Steps)
|
||||
.FirstOrDefault(group => group.Id == deploymentGroupId);
|
||||
@@ -75,11 +80,23 @@ namespace Microsoft.SelfService.Portal.Core.API.Services
|
||||
throw new InvalidOperationException("DeploymentGroup does not exist.");
|
||||
}
|
||||
|
||||
var templateId = deploymentGroup.TemplateId;
|
||||
var primaryTemplateSelection = deploymentGroup.TemplateSelections
|
||||
.OrderBy(selection => selection.SortOrder)
|
||||
.FirstOrDefault();
|
||||
var templateId = primaryTemplateSelection?.TemplateVersion.TemplateId ?? deploymentGroup.TemplateId;
|
||||
var resolvedTargetIds = targetIds
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
if (resolvedTargetIds.Count == 0)
|
||||
{
|
||||
resolvedTargetIds = deploymentGroup.TargetAssignments
|
||||
.OrderBy(assignment => assignment.SortOrder)
|
||||
.Select(assignment => assignment.TargetId)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
if (resolvedTargetIds.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("No target Targets provided.");
|
||||
@@ -133,6 +150,31 @@ namespace Microsoft.SelfService.Portal.Core.API.Services
|
||||
DeploymentGroupId = deploymentGroupId,
|
||||
TemplateId = templateId,
|
||||
DeploymentRuleId = resolvedRule?.Id,
|
||||
TemplateSelections = deploymentGroup.TemplateSelections
|
||||
.OrderBy(selection => selection.SortOrder)
|
||||
.Select(selection => new
|
||||
{
|
||||
selection.Id,
|
||||
selection.TemplateVersionId,
|
||||
TemplateId = selection.TemplateVersion.TemplateId,
|
||||
TemplateName = selection.TemplateVersion.Template.Name,
|
||||
selection.TemplateVersion.Version,
|
||||
selection.TemplateVersion.JsonHash,
|
||||
selection.TemplateRole,
|
||||
selection.SortOrder,
|
||||
selection.Alias
|
||||
}),
|
||||
TargetAssignments = deploymentGroup.TargetAssignments
|
||||
.OrderBy(assignment => assignment.SortOrder)
|
||||
.Where(assignment => resolvedTargetIds.Contains(assignment.TargetId))
|
||||
.Select(assignment => new
|
||||
{
|
||||
assignment.Id,
|
||||
assignment.TargetId,
|
||||
assignment.RoleKey,
|
||||
assignment.SortOrder,
|
||||
assignment.NodeDataJson
|
||||
}),
|
||||
TargetIds = resolvedTargetIds,
|
||||
JsonData = jsonData,
|
||||
TargetCount = resolvedTargetIds.Count,
|
||||
@@ -143,6 +185,7 @@ namespace Microsoft.SelfService.Portal.Core.API.Services
|
||||
{
|
||||
Type = QueueJobType.DeploymentRequested,
|
||||
Status = QueueJobStatus.Pending,
|
||||
CorrelationId = Guid.NewGuid(),
|
||||
PayloadJson = JsonSerializer.Serialize(payload),
|
||||
RuleSnapshotJson = resolvedRule != null
|
||||
? SerializeRuleSnapshot(resolvedRule)
|
||||
@@ -179,6 +222,8 @@ namespace Microsoft.SelfService.Portal.Core.API.Services
|
||||
queueJob.Finished = null;
|
||||
queueJob.LockedUntil = null;
|
||||
queueJob.LockedBy = null;
|
||||
queueJob.HeartbeatAt = null;
|
||||
queueJob.WorkerName = null;
|
||||
queueJob.Steps ??= new List<QueueJobStepModel>();
|
||||
|
||||
foreach (var target in queueJob.Targets)
|
||||
@@ -187,6 +232,9 @@ namespace Microsoft.SelfService.Portal.Core.API.Services
|
||||
{
|
||||
target.Status = QueueJobStatus.Pending;
|
||||
target.ErrorMessage = null;
|
||||
target.Started = null;
|
||||
target.Finished = null;
|
||||
target.OutputMetadataJson = null;
|
||||
}
|
||||
}
|
||||
foreach (var step in queueJob.Steps)
|
||||
@@ -200,6 +248,10 @@ namespace Microsoft.SelfService.Portal.Core.API.Services
|
||||
step.ApprovedAt = null;
|
||||
step.ApprovedBy = null;
|
||||
step.ApprovalComment = null;
|
||||
step.Started = null;
|
||||
step.Finished = null;
|
||||
step.ErrorMessage = null;
|
||||
step.OutputMetadataJson = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,6 +276,8 @@ namespace Microsoft.SelfService.Portal.Core.API.Services
|
||||
step.QueueJob.Status = QueueJobStatus.Pending;
|
||||
step.QueueJob.LockedUntil = null;
|
||||
step.QueueJob.LockedBy = null;
|
||||
step.QueueJob.HeartbeatAt = null;
|
||||
step.QueueJob.WorkerName = null;
|
||||
step.QueueJob.ErrorMessage = null;
|
||||
|
||||
return _context.SaveChanges() > 0;
|
||||
@@ -248,6 +302,8 @@ namespace Microsoft.SelfService.Portal.Core.API.Services
|
||||
step.QueueJob.Finished = DateTime.UtcNow;
|
||||
step.QueueJob.LockedUntil = null;
|
||||
step.QueueJob.LockedBy = null;
|
||||
step.QueueJob.HeartbeatAt = null;
|
||||
step.QueueJob.WorkerName = null;
|
||||
step.QueueJob.ErrorMessage = comment ?? "Deployment step rejected.";
|
||||
|
||||
return _context.SaveChanges() > 0;
|
||||
|
||||
Reference in New Issue
Block a user