Skip to content
This repository was archived by the owner on Jul 30, 2024. It is now read-only.

Commit c249e44

Browse files
authored
More appropriate handling of the optional validations in the Orchestrator (#450)
* work in progress * more work in progress * Builds and probably passes existing tests * Fixes and tests * `Enum.GetValues` instead of explicitly listing the values * PR issues addressed * Ignoring exceptions inside allwed to fail validations. * catch modification
1 parent f9f855d commit c249e44

10 files changed

Lines changed: 356 additions & 103 deletions

src/NuGet.Services.Validation.Orchestrator/IValidationOutcomeProcessor.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ public interface IValidationOutcomeProcessor
1919
/// </summary>
2020
/// <param name="validationSet">Current state of validation set</param>
2121
/// <param name="package">Package information from Gallery DB</param>
22+
/// <param name="currentCallStats">Contains information about what happened during current message processing in
23+
/// the validation set processor.</param>
2224
/// <returns>A task that completes when the outcome has been processed</returns>
23-
Task ProcessValidationOutcomeAsync(PackageValidationSet validationSet, Package package);
25+
Task ProcessValidationOutcomeAsync(PackageValidationSet validationSet, Package package, ValidationSetProcessorResult currentCallStats);
2426
}
2527
}

src/NuGet.Services.Validation.Orchestrator/IValidationSetProcessor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public interface IValidationSetProcessor
1717
/// </summary>
1818
/// <param name="validationSet">Validation set to work with. Any validation updates would be reflected in that object upon return.</param>
1919
/// <param name="package">Gallery DB package information</param>
20-
/// <returns>Task object representing async operation</returns>
21-
Task ProcessValidationsAsync(PackageValidationSet validationSet, Package package);
20+
/// <returns>Information about what happened during processing of the message.</returns>
21+
Task<ValidationSetProcessorResult> ProcessValidationsAsync(PackageValidationSet validationSet, Package package);
2222
}
2323
}

src/NuGet.Services.Validation.Orchestrator/NuGet.Services.Validation.Orchestrator.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
<Compile Include="Configuration\ValidationConfigurationItem.cs" />
8989
<Compile Include="ValidationFailureBehavior.cs" />
9090
<Compile Include="ValidationPackageFileService.cs" />
91+
<Compile Include="ValidationSetProcessorResult.cs" />
9192
<Compile Include="Vcs\IPackageCriteria.cs" />
9293
<Compile Include="Vcs\IPackageCriteriaEvaluator.cs" />
9394
<Compile Include="Vcs\PackageCriteriaEvaluator.cs" />

src/NuGet.Services.Validation.Orchestrator/ValidationMessageHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ public async Task<bool> HandleAsync(PackageValidationMessageData message)
123123
return true;
124124
}
125125

126-
await _validationSetProcessor.ProcessValidationsAsync(validationSet, package);
127-
await _validationOutcomeProcessor.ProcessValidationOutcomeAsync(validationSet, package);
126+
var processorStats = await _validationSetProcessor.ProcessValidationsAsync(validationSet, package);
127+
await _validationOutcomeProcessor.ProcessValidationOutcomeAsync(validationSet, package, processorStats);
128128
}
129129
return true;
130130
}

src/NuGet.Services.Validation.Orchestrator/ValidationOutcomeProcessor.cs

Lines changed: 105 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public ValidationOutcomeProcessor(
5252
_validationConfigurationsByName = _validationConfiguration.Validations.ToDictionary(v => v.Name);
5353
}
5454

55-
public async Task ProcessValidationOutcomeAsync(PackageValidationSet validationSet, Package package)
55+
public async Task ProcessValidationOutcomeAsync(PackageValidationSet validationSet, Package package, ValidationSetProcessorResult currentCallStats)
5656
{
5757
var failedValidations = GetFailedValidations(validationSet);
5858

@@ -102,9 +102,10 @@ public async Task ProcessValidationOutcomeAsync(PackageValidationSet validationS
102102
validationSet.ValidationTrackingId);
103103
}
104104

105-
await CompleteValidationSetAsync(package, validationSet, isSuccess: false);
105+
await CleanupValidationStorageAsync(validationSet);
106+
TrackValidationSetCompletion(package, validationSet, isSuccess: false);
106107
}
107-
else if (AllValidationsSucceeded(validationSet))
108+
else if (AllRequiredValidationsSucceeded(validationSet))
108109
{
109110
_logger.LogInformation("All validations are complete for the package {PackageId} {PackageVersion}, validation set {ValidationSetId}",
110111
package.PackageRegistration.Id,
@@ -126,75 +127,28 @@ public async Task ProcessValidationOutcomeAsync(PackageValidationSet validationS
126127
_messageService.SendPackagePublishedMessage(package);
127128
}
128129

129-
await CompleteValidationSetAsync(package, validationSet, isSuccess: true);
130-
}
131-
else
132-
{
133-
// There are no failed validations and some validations are still in progress. Update
134-
// the validation set's Updated field and send a notice if the validation set is taking
135-
// too long to complete.
136-
var previousUpdateTime = validationSet.Updated;
137-
138-
await _validationStorageService.UpdateValidationSetAsync(validationSet);
139-
140-
var validationSetDuration = validationSet.Updated - validationSet.Created;
141-
var previousDuration = previousUpdateTime - validationSet.Created;
142-
143-
// Only send a "validating taking too long" notice once. This is ensured by verifying this is
144-
// the package's first validation set and that this is the first time the validation set duration
145-
// is greater than the configured threshold. Service Bus message duplication for a single validation
146-
// set will not cause multiple notices to be sent due to the row version on PackageValidationSet.
147-
if (validationSetDuration > _validationConfiguration.ValidationSetNotificationTimeout &&
148-
previousDuration <= _validationConfiguration.ValidationSetNotificationTimeout &&
149-
await _validationStorageService.GetValidationSetCountAsync(package.Key) == 1)
150-
{
151-
_logger.LogWarning("Sending message that validation set {ValidationTrackingId} for package {PackageId} {PackageVersion} is taking too long",
152-
validationSet.ValidationTrackingId,
153-
validationSet.PackageId,
154-
validationSet.PackageNormalizedVersion);
155-
156-
_messageService.SendPackageValidationTakingTooLongMessage(package);
157-
_telemetryService.TrackSentValidationTakingTooLongMessage(package.PackageRegistration.Id, package.NormalizedVersion, validationSet.ValidationTrackingId);
158-
}
159-
160-
// Track any validations that are past their expected thresholds.
161-
var timedOutValidations = GetIncompleteTimedOutValidations(validationSet);
162-
163-
if (timedOutValidations.Any())
130+
if (currentCallStats.AnyRequiredValidationSucceeded)
164131
{
165-
foreach (var validation in timedOutValidations)
166-
{
167-
var duration = DateTime.UtcNow - validation.Started;
168-
169-
_logger.LogWarning("Validation {Validation} for package {PackageId} {PackageVersion} is past its expected duration after {Duration}",
170-
validation.Type,
171-
validationSet.PackageId,
172-
validationSet.PackageNormalizedVersion,
173-
duration);
174-
175-
_telemetryService.TrackValidatorTimeout(validation.Type);
176-
}
132+
TrackValidationSetCompletion(package, validationSet, isSuccess: true);
177133
}
178134

179-
// Schedule another check if we haven't reached the validation set timeout yet.
180-
if (validationSetDuration <= _validationConfiguration.TimeoutValidationSetAfter)
135+
if (AreOptionalValidationsRunning(validationSet))
181136
{
182-
var messageData = new PackageValidationMessageData(package.PackageRegistration.Id, package.Version, validationSet.ValidationTrackingId);
183-
var postponeUntil = DateTimeOffset.UtcNow + _validationConfiguration.ValidationMessageRecheckPeriod;
184-
185-
await _validationEnqueuer.StartValidationAsync(messageData, postponeUntil);
137+
await ScheduleCheckIfNotTimedOut(validationSet, package, tooLongNotificationAllowed: false);
186138
}
187139
else
188140
{
189-
_telemetryService.TrackValidationSetTimeout(package.PackageRegistration.Id, package.NormalizedVersion, validationSet.ValidationTrackingId);
141+
await CleanupValidationStorageAsync(validationSet);
190142
}
191143
}
144+
else
145+
{
146+
await ScheduleCheckIfNotTimedOut(validationSet, package, tooLongNotificationAllowed: true);
147+
}
192148
}
193149

194-
private async Task CompleteValidationSetAsync(Package package, PackageValidationSet validationSet, bool isSuccess)
150+
private void TrackValidationSetCompletion(Package package, PackageValidationSet validationSet, bool isSuccess)
195151
{
196-
await _packageFileService.DeletePackageForValidationSetAsync(validationSet);
197-
198152
_logger.LogInformation("Done processing {PackageId} {PackageVersion} {ValidationSetId} with IsSuccess = {IsSuccess}.",
199153
package.PackageRegistration.Id,
200154
package.NormalizedVersion,
@@ -204,6 +158,11 @@ private async Task CompleteValidationSetAsync(Package package, PackageValidation
204158
TrackTotalValidationDuration(validationSet, isSuccess);
205159
}
206160

161+
private async Task CleanupValidationStorageAsync(PackageValidationSet validationSet)
162+
{
163+
await _packageFileService.DeletePackageForValidationSetAsync(validationSet);
164+
}
165+
207166
private ValidationConfigurationItem GetValidationConfigurationItemByName(string name)
208167
{
209168
_validationConfigurationsByName.TryGetValue(name, out var item);
@@ -218,14 +177,22 @@ private void TrackTotalValidationDuration(PackageValidationSet validationSet, bo
218177
isSuccess);
219178
}
220179

221-
private bool AllValidationsSucceeded(PackageValidationSet packageValidationSet)
180+
private bool AllRequiredValidationsSucceeded(PackageValidationSet packageValidationSet)
222181
{
223182
return packageValidationSet
224183
.PackageValidations
225184
.All(pv => pv.ValidationStatus == ValidationStatus.Succeeded
226185
|| GetValidationConfigurationItemByName(pv.Type)?.FailureBehavior == ValidationFailureBehavior.AllowedToFail);
227186
}
228187

188+
private bool AreOptionalValidationsRunning(PackageValidationSet packageValidationSet)
189+
{
190+
return packageValidationSet
191+
.PackageValidations
192+
.Any(pv => pv.ValidationStatus == ValidationStatus.Incomplete
193+
&& GetValidationConfigurationItemByName(pv.Type)?.FailureBehavior == ValidationFailureBehavior.AllowedToFail);
194+
}
195+
229196
private List<PackageValidation> GetFailedValidations(PackageValidationSet packageValidationSet)
230197
{
231198
return packageValidationSet
@@ -251,5 +218,82 @@ bool IsPackageValidationTimedOut(PackageValidation validation)
251218
.Where(IsPackageValidationTimedOut)
252219
.ToList();
253220
}
221+
222+
private async Task<TimeSpan> UpdateValidationDurationAsync(PackageValidationSet validationSet, Package package, bool tooLongNotificationAllowed)
223+
{
224+
// There are no failed validations and some validations are still in progress. Update
225+
// the validation set's Updated field and send a notice if the validation set is taking
226+
// too long to complete.
227+
var previousUpdateTime = validationSet.Updated;
228+
229+
await _validationStorageService.UpdateValidationSetAsync(validationSet);
230+
231+
var validationSetDuration = validationSet.Updated - validationSet.Created;
232+
var previousDuration = previousUpdateTime - validationSet.Created;
233+
234+
// Only send a "validating taking too long" notice once. This is ensured by verifying this is
235+
// the package's first validation set and that this is the first time the validation set duration
236+
// is greater than the configured threshold. Service Bus message duplication for a single validation
237+
// set will not cause multiple notices to be sent due to the row version on PackageValidationSet.
238+
if (tooLongNotificationAllowed &&
239+
validationSetDuration > _validationConfiguration.ValidationSetNotificationTimeout &&
240+
previousDuration <= _validationConfiguration.ValidationSetNotificationTimeout &&
241+
await _validationStorageService.GetValidationSetCountAsync(package.Key) == 1)
242+
{
243+
_logger.LogWarning("Sending message that validation set {ValidationTrackingId} for package {PackageId} {PackageVersion} is taking too long",
244+
validationSet.ValidationTrackingId,
245+
validationSet.PackageId,
246+
validationSet.PackageNormalizedVersion);
247+
248+
_messageService.SendPackageValidationTakingTooLongMessage(package);
249+
_telemetryService.TrackSentValidationTakingTooLongMessage(package.PackageRegistration.Id, package.NormalizedVersion, validationSet.ValidationTrackingId);
250+
}
251+
252+
// Track any validations that are past their expected thresholds.
253+
var timedOutValidations = GetIncompleteTimedOutValidations(validationSet);
254+
255+
if (timedOutValidations.Any())
256+
{
257+
foreach (var validation in timedOutValidations)
258+
{
259+
var duration = DateTime.UtcNow - validation.Started;
260+
261+
_logger.LogWarning("Validation {Validation} for package {PackageId} {PackageVersion} is past its expected duration after {Duration}",
262+
validation.Type,
263+
validationSet.PackageId,
264+
validationSet.PackageNormalizedVersion,
265+
duration);
266+
267+
_telemetryService.TrackValidatorTimeout(validation.Type);
268+
}
269+
}
270+
271+
return validationSetDuration;
272+
}
273+
274+
private async Task ScheduleCheckIfNotTimedOut(PackageValidationSet validationSet, Package package, bool tooLongNotificationAllowed)
275+
{
276+
var validationSetDuration = await UpdateValidationDurationAsync(validationSet, package, tooLongNotificationAllowed);
277+
278+
// Schedule another check if we haven't reached the validation set timeout yet.
279+
if (validationSetDuration <= _validationConfiguration.TimeoutValidationSetAfter)
280+
{
281+
var messageData = new PackageValidationMessageData(package.PackageRegistration.Id, package.Version, validationSet.ValidationTrackingId);
282+
var postponeUntil = DateTimeOffset.UtcNow + _validationConfiguration.ValidationMessageRecheckPeriod;
283+
284+
await _validationEnqueuer.StartValidationAsync(messageData, postponeUntil);
285+
}
286+
else
287+
{
288+
_logger.LogWarning("Abandoning checking status of validation set {ValidationTrackingId} for " +
289+
"package {PackageId} {PackageVersion} because it took too long (Duration: {Duration}, CutOffDuration: {CutOffDuration})",
290+
validationSet.ValidationTrackingId,
291+
validationSet.PackageId,
292+
validationSet.PackageNormalizedVersion,
293+
validationSetDuration,
294+
_validationConfiguration.TimeoutValidationSetAfter);
295+
_telemetryService.TrackValidationSetTimeout(package.PackageRegistration.Id, package.NormalizedVersion, validationSet.ValidationTrackingId);
296+
}
297+
}
254298
}
255299
}

0 commit comments

Comments
 (0)