-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathCleanupPerishingDialogsHandler.cs
More file actions
156 lines (133 loc) · 6.62 KB
/
CleanupPerishingDialogsHandler.cs
File metadata and controls
156 lines (133 loc) · 6.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
using Altinn.Correspondence.Core.Models.Entities;
using Altinn.Correspondence.Core.Models.Enums;
using Altinn.Correspondence.Core.Repositories;
using Altinn.Correspondence.Core.Services;
using Microsoft.Extensions.Logging;
using OneOf;
using System.Security.Claims;
using Hangfire;
namespace Altinn.Correspondence.Application.CleanupPerishingDialogs;
public class CleanupPerishingDialogsHandler(
ICorrespondenceRepository correspondenceRepository,
IDialogportenService dialogportenService,
IBackgroundJobClient backgroundJobClient,
ILogger<CleanupPerishingDialogsHandler> logger) : IHandler<CleanupPerishingDialogsRequest, CleanupPerishingDialogsResponse>
{
public Task<OneOf<CleanupPerishingDialogsResponse, Error>> Process(CleanupPerishingDialogsRequest request, ClaimsPrincipal? user, CancellationToken cancellationToken)
{
logger.LogInformation("Starting cleanup of perishing dialogs (removing expiresAt) with window size {windowSize}", request.WindowSize);
var jobId = backgroundJobClient.Enqueue(() => ExecuteCleanupInBackground(request.WindowSize, CancellationToken.None));
logger.LogInformation("Cleanup job {jobId} has been enqueued", jobId);
return Task.FromResult<OneOf<CleanupPerishingDialogsResponse, Error>>(new CleanupPerishingDialogsResponse
{
JobId = jobId,
Message = "Cleanup job has been Enqueued"
});
}
[AutomaticRetry(Attempts = 0)]
[DisableConcurrentExecution(timeoutInSeconds: 43200)]
public async Task ExecuteCleanupInBackground(int windowSize, CancellationToken cancellationToken)
{
logger.LogInformation("Executing cleanup of perishing dialogs in background job");
var totalProcessed = 0;
var totalPatched = 0;
var totalAlreadyOk = 0;
var totalErrors = 0;
var allErrors = new List<string>();
try
{
DateTimeOffset? lastCreated = null;
Guid? lastId = null;
bool isMoreCorrespondences = true;
while (isMoreCorrespondences)
{
logger.LogInformation("Processing batch starting after cursor {lastCreated} / {lastId}", lastCreated, lastId);
var correspondencesWindow = await correspondenceRepository.GetCorrespondencesWindowAfter(
windowSize + 1,
lastCreated,
lastId,
true,
cancellationToken);
isMoreCorrespondences = correspondencesWindow.Count > windowSize;
if (isMoreCorrespondences)
{
correspondencesWindow = correspondencesWindow.Take(windowSize).ToList();
}
if (correspondencesWindow.Count > 0)
{
var last = correspondencesWindow[^1];
lastCreated = last.Created;
lastId = last.Id;
}
var windowIds = correspondencesWindow.Select(c => c.Id).ToList();
var candidates = await correspondenceRepository.GetCorrespondencesByIdsWithExternalReference(
windowIds,
ReferenceType.DialogportenDialogId,
cancellationToken);
logger.LogInformation(
"Scanned {scanned} correspondences, {candidates} to remove expiresAt for (IsMore: {isMore})",
correspondencesWindow.Count,
candidates.Count,
isMoreCorrespondences);
foreach (var correspondence in candidates)
{
try
{
var (patched, alreadyOk) = await ProcessSingleCorrespondence(correspondence);
if (patched) totalPatched++;
if (alreadyOk) totalAlreadyOk++;
}
catch (Exception ex)
{
totalErrors++;
var errorMessage = $"Failed to process correspondence {correspondence.Id}: {ex.Message}";
allErrors.Add(errorMessage);
logger.LogError(ex, "Failed to process correspondence {correspondenceId}", correspondence.Id);
}
}
totalProcessed += candidates.Count;
if (correspondencesWindow.Count == 0)
{
isMoreCorrespondences = false;
}
}
logger.LogInformation("Background cleanup completed. Total processed: {processedCount}, Total patched: {patchedCount}, Already ok: {alreadyOkCount}, Total errors: {errorCount}",
totalProcessed, totalPatched, totalAlreadyOk, totalErrors);
if (allErrors.Count > 0)
{
logger.LogWarning("Cleanup completed with {errorCount} errors: {errors}", totalErrors, string.Join("; ", allErrors));
}
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to execute background cleanup of perishing dialogs");
throw;
}
}
private async Task<(bool patched, bool alreadyOk)> ProcessSingleCorrespondence(CorrespondenceEntity correspondence)
{
var dialogId = correspondence.ExternalReferences
.FirstOrDefault(er => er.ReferenceType == ReferenceType.DialogportenDialogId)?.ReferenceValue;
if (dialogId == null)
{
if (correspondence.IsMigrating)
{
logger.LogWarning("Skipping correspondence {correspondenceId} as it is an Altinn2 correspondence without Dialogporten dialog",
correspondence.Id);
return (false, false);
}
logger.LogError("Correspondence {correspondenceId} has no dialog reference", correspondence.Id);
return (false, false);
}
logger.LogInformation("Attempting to remove expiresAt on dialog {dialogId} for correspondence {correspondenceId}",
dialogId, correspondence.Id);
var removed = await dialogportenService.TryRemoveDialogExpiresAt(dialogId);
if (removed)
{
logger.LogInformation("Successfully removed expiresAt on dialog {dialogId} for correspondence {correspondenceId}", dialogId, correspondence.Id);
return (true, false);
}
logger.LogInformation("Dialog {dialogId} already has no expiresAt in Dialogporten for correspondence {correspondenceId}", dialogId, correspondence.Id);
return (false, true);
}
}