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

Commit 52fc47a

Browse files
committed
Add initial telemetry service with stub implementation (#317)
Use package key instead of ID/version and add unit tests for OtherRecentValidationSetForPackageExists Progress on NuGet/NuGetGallery#4652
1 parent b5caf0b commit 52fc47a

34 files changed

Lines changed: 841 additions & 116 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,5 @@ Results.*.xml
207207
AssemblyInfo.*.cs
208208
/tests/Validation.Helper.Tests/Validation.Helper.Tests.nuget.props
209209
/tests/Validation.Common.Tests/Validation.Common.Tests.nuget.props
210+
/tests/Validation.Common.Tests/Validation.Common.Tests.nuget.targets
211+
/tests/Validation.Helper.Tests/Validation.Helper.Tests.nuget.targets

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ public interface IValidationStorageService
2525
/// <returns>Validation set instance if found, null otherwise.</returns>
2626
Task<PackageValidationSet> GetValidationSetAsync(Guid validationTrackingId);
2727

28+
/// <summary>
29+
/// Gets the number of validation sets that the provided package key has.
30+
/// </summary>
31+
/// <param name="packageKey">The package key.</param>
32+
/// <returns>The count.</returns>
33+
Task<int> GetValidationSetCountAsync(int packageKey);
34+
2835
/// <summary>
2936
/// Updates the passed <see cref="PackageValidation"/> with the validation result's status,
3037
/// updates the <see cref="PackageValidation.ValidationStatusTimestamp"/> to current timestamp,
@@ -62,8 +69,7 @@ public interface IValidationStorageService
6269
/// <param name="currentValidationSetTrackingId">Validation set tracking for the currently processed request.</param>
6370
/// <returns>True if validation set exists, false otherwise.</returns>
6471
Task<bool> OtherRecentValidationSetForPackageExists(
65-
string packageId,
66-
string normalizedVersion,
72+
int packageKey,
6773
TimeSpan recentDuration,
6874
Guid currentValidationSetTrackingId);
6975
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
using NuGet.Services.Configuration;
2424
using NuGet.Services.KeyVault;
2525
using NuGet.Services.ServiceBus;
26+
using NuGet.Services.Validation.Orchestrator.Telemetry;
2627
using NuGet.Services.Validation.PackageCertificates;
2728
using NuGet.Services.Validation.PackageSigning;
2829
using NuGet.Services.Validation.Vcs;
@@ -207,6 +208,7 @@ private void ConfigureJobServices(IServiceCollection services, IConfigurationRoo
207208
services.AddTransient<ICoreMessageServiceConfiguration, CoreMessageServiceConfiguration>();
208209
services.AddTransient<ICoreMessageService, CoreMessageService>();
209210
services.AddTransient<IMessageService, MessageService>();
211+
services.AddTransient<ITelemetryService, TelemetryService>();
210212
}
211213

212214
private static IServiceProvider CreateProvider(IServiceCollection services)

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@
7272
<Compile Include="Properties\AssemblyInfo.cs" />
7373
<Compile Include="Properties\AssemblyInfo.*.cs" />
7474
<Compile Include="SmtpConfiguration.cs" />
75+
<Compile Include="Telemetry\ITelemetryService.cs" />
76+
<Compile Include="Telemetry\TelemetryService.cs" />
7577
<Compile Include="ValidationConfiguration.cs" />
7678
<Compile Include="ValidationConfigurationItem.cs" />
7779
<Compile Include="ValidationFailureBehavior.cs" />
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using NuGetGallery;
6+
7+
namespace NuGet.Services.Validation.Orchestrator.Telemetry
8+
{
9+
/// <summary>
10+
/// The interface used for emitting telemetry from the validation orchestrator.
11+
/// </summary>
12+
public interface ITelemetryService
13+
{
14+
/// <summary>
15+
/// The duration from when the package was created to when the first validation set was created. This metric
16+
/// is not emitted for revalidation requests.
17+
/// </summary>
18+
void TrackDurationToValidationSetCreation(TimeSpan duration);
19+
20+
/// <summary>
21+
/// A counter metric emitted when a package changes package status. This metric is not emitted if package status
22+
/// does not change. This metric is emitted for revalidation if the terminal state changes.
23+
/// </summary>
24+
/// <param name="fromStatus">The status that the package moved from.</param>
25+
/// <param name="toStatus">The status that the package moved tp.</param>
26+
void TrackPackageStatusChange(PackageStatus fromStatus, PackageStatus toStatus);
27+
28+
/// <summary>
29+
/// The total duration of all validators. This is the time that the validation set is first created until all of
30+
/// the validators have completed. This metric is also emitted for revalidations.
31+
/// </summary>
32+
/// <param name="duration">The duration.</param>
33+
/// <param name="isSuccess">Whether or not all of the validations succeeded.</param>
34+
void TrackTotalValidationDuration(TimeSpan duration, bool isSuccess);
35+
36+
/// <summary>
37+
/// A counter metric emitted when a validator fails due to the <see cref="ValidationConfigurationItem.FailAfter"/>
38+
/// configuration.
39+
/// </summary>
40+
/// <param name="validatorType">The validator type (name).</param>
41+
void TrackValidatorTimeout(string validatorType);
42+
43+
/// <summary>
44+
/// The total duration of a single validator. This is the time from when the validation is first started until
45+
/// when the validation either completes or times out.
46+
/// </summary>
47+
/// <param name="duration">The duration.</param>
48+
/// <param name="validatorType">The validator type (name).</param>
49+
/// <param name="isSuccess">Whether or not the validation succeeded.</param>
50+
void TrackValidatorDuration(TimeSpan duration, string validatorType, bool isSuccess);
51+
52+
/// <summary>
53+
/// A counter metric emitted when a validator is started.
54+
/// </summary>
55+
/// <param name="validatorType">The validator type (name).</param>
56+
void TrackValidatorStarted(string validatorType);
57+
58+
/// <summary>
59+
/// A counter metric emmitted when a validator reaches a terminal state and potentially persists validation
60+
/// issues. A count of zero is emitted if the validator does not produce any issues. This metric is not emitted
61+
/// if the validation is still at a non-terminal state.
62+
/// </summary>
63+
/// <param name="count">The number of issues.</param>
64+
/// <param name="validatorType">The validator type (name) that returned the issue list.</param>
65+
/// <param name="isSuccess">Whether or not the validation succeeded.</param>
66+
void TrackValidationIssueCount(int count, string validatorType, bool isSuccess);
67+
68+
/// <summary>
69+
/// A counter metric emitted when a validation issue is created.
70+
/// </summary>
71+
/// <param name="validatorType">The validator type (name) the produced the issue.</param>
72+
/// <param name="code">The issue code.</param>
73+
void TrackValidationIssue(string validatorType, ValidationIssueCode code);
74+
75+
/// <summary>
76+
/// A counter metric emitted when a client-mastered validation issue is emitted.
77+
/// </summary>
78+
/// <param name="validatorType">The validator type (name) the produced the issue.</param>
79+
/// <param name="clientCode">The client code.</param>
80+
void TrackClientValidationIssue(string validatorType, string clientCode);
81+
}
82+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using NuGetGallery;
6+
7+
namespace NuGet.Services.Validation.Orchestrator.Telemetry
8+
{
9+
public class TelemetryService : ITelemetryService
10+
{
11+
public void TrackDurationToValidationSetCreation(TimeSpan duration)
12+
{
13+
}
14+
15+
public void TrackPackageStatusChange(PackageStatus fromStatus, PackageStatus toStatus)
16+
{
17+
}
18+
19+
public void TrackTotalValidationDuration(TimeSpan duration, bool isSuccess)
20+
{
21+
}
22+
23+
public void TrackValidationIssue(string validatorType, ValidationIssueCode code)
24+
{
25+
}
26+
27+
public void TrackValidationIssueCount(int count, string validatorType, bool isSuccess)
28+
{
29+
}
30+
31+
public void TrackValidatorTimeout(string validatorType)
32+
{
33+
}
34+
35+
public void TrackValidatorDuration(TimeSpan duration, string validatorType, bool isSuccess)
36+
{
37+
}
38+
39+
public void TrackValidatorStarted(string validatorType)
40+
{
41+
}
42+
43+
public void TrackClientValidationIssue(string validatorType, string clientCode)
44+
{
45+
}
46+
}
47+
}

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

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
using System.Threading.Tasks;
88
using Microsoft.Extensions.Logging;
99
using Microsoft.Extensions.Options;
10+
using NuGet.Services.Validation.Orchestrator.Telemetry;
1011
using NuGetGallery;
11-
using NuGetGallery.Services;
1212

1313
namespace NuGet.Services.Validation.Orchestrator
1414
{
@@ -19,6 +19,7 @@ public class ValidationOutcomeProcessor : IValidationOutcomeProcessor
1919
private readonly IPackageValidationEnqueuer _validationEnqueuer;
2020
private readonly ValidationConfiguration _validationConfiguration;
2121
private readonly IMessageService _messageService;
22+
private readonly ITelemetryService _telemetryService;
2223
private readonly ILogger<ValidationOutcomeProcessor> _logger;
2324

2425
public ValidationOutcomeProcessor(
@@ -27,6 +28,7 @@ public ValidationOutcomeProcessor(
2728
IPackageValidationEnqueuer validationEnqueuer,
2829
IOptionsSnapshot<ValidationConfiguration> validationConfigurationAccessor,
2930
IMessageService messageService,
31+
ITelemetryService telemetryService,
3032
ILogger<ValidationOutcomeProcessor> logger)
3133
{
3234
_galleryPackageService = galleryPackageService ?? throw new ArgumentNullException(nameof(galleryPackageService));
@@ -40,6 +42,7 @@ public ValidationOutcomeProcessor(
4042
?? throw new ArgumentException($"The {nameof(validationConfigurationAccessor)}.Value property cannot be null",
4143
nameof(validationConfigurationAccessor));
4244
_messageService = messageService ?? throw new ArgumentNullException(nameof(messageService));
45+
_telemetryService = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
4346
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
4447
}
4548

@@ -63,9 +66,14 @@ ValidationConfigurationItem GetValidationConfigurationItem(string validationName
6366
validationSet.ValidationTrackingId,
6467
GetFailedValidations(validationSet, GetValidationConfigurationItem));
6568

66-
if (package.PackageStatusKey != PackageStatus.Available)
69+
// The only way we can move to the failed validation state is if the package is currently in the
70+
// validating state. This has a beneficial side effect of only sending a failed validation email to the
71+
// customer when the package first moves to the failed validation state. If an admin comes along and
72+
// revalidates the package and the package fails validation again, we don't want another email going
73+
// out since that would be noisy for the customer.
74+
if (package.PackageStatusKey == PackageStatus.Validating)
6775
{
68-
await _galleryPackageService.UpdatePackageStatusAsync(package, PackageStatus.FailedValidation);
76+
await UpdatePackageStatusAsync(package, PackageStatus.FailedValidation);
6977

7078
var issuesExistAndAllPackageSigned = validationSet
7179
.PackageValidations
@@ -85,15 +93,18 @@ ValidationConfigurationItem GetValidationConfigurationItem(string validationName
8593
}
8694
else
8795
{
88-
// The case when validation fails while PackageStatus is Available is the case of
96+
// The case when validation fails while PackageStatus not validating is the case of
8997
// manual revalidation. In this case we don't want to take package down automatically
90-
// and let the person who requested revalidation to decide how to proceed. User will be
98+
// and let the person who requested revalidation to decide how to proceed. Ops will be
9199
// alerted by failed validation monitoring.
92-
_logger.LogInformation("Package {PackageId} {PackageVersion} was available when validation set {ValidationSetId} failed. Will not mark it as failed",
100+
_logger.LogInformation("Package {PackageId} {PackageVersion} was {PackageStatus} when validation set {ValidationSetId} failed. Will not mark it as failed.",
93101
package.PackageRegistration.Id,
94102
package.NormalizedVersion,
103+
package.PackageStatusKey,
95104
validationSet.ValidationTrackingId);
96105
}
106+
107+
TrackTotalValidationDuration(validationSet, isSuccess: false);
97108
}
98109
else if (AllValidationsSucceeded(validationSet, GetValidationConfigurationItem))
99110
{
@@ -116,6 +127,8 @@ ValidationConfigurationItem GetValidationConfigurationItem(string validationName
116127
package.PackageRegistration.Id,
117128
package.NormalizedVersion,
118129
validationSet.ValidationTrackingId);
130+
131+
TrackTotalValidationDuration(validationSet, isSuccess: true);
119132
}
120133
else
121134
{
@@ -126,6 +139,13 @@ ValidationConfigurationItem GetValidationConfigurationItem(string validationName
126139
}
127140
}
128141

142+
private void TrackTotalValidationDuration(PackageValidationSet validationSet, bool isSuccess)
143+
{
144+
_telemetryService.TrackTotalValidationDuration(
145+
DateTime.UtcNow - validationSet.Created,
146+
isSuccess);
147+
}
148+
129149
private async Task MoveFileToPublicStorageAndMarkPackageAsAvailable(PackageValidationSet validationSet, Package package)
130150
{
131151
_logger.LogInformation("Copying .nupkg to public storage for package {PackageId} {PackageVersion}, validation set {ValidationSetId}",
@@ -142,7 +162,9 @@ private async Task MoveFileToPublicStorageAndMarkPackageAsAvailable(PackageValid
142162
package.NormalizedVersion,
143163
validationSet.ValidationTrackingId,
144164
PackageStatus.Available);
145-
await _galleryPackageService.UpdatePackageStatusAsync(package, PackageStatus.Available, commitChanges: true);
165+
166+
await UpdatePackageStatusAsync(package, PackageStatus.Available);
167+
146168
_messageService.SendPackagePublishedMessage(package);
147169
}
148170
catch (Exception e)
@@ -193,5 +215,17 @@ private bool AnyValidationFailed(
193215
{
194216
return GetFailedValidations(packageValidationSet, getValidationConfigurationItem).Any();
195217
}
218+
219+
private async Task UpdatePackageStatusAsync(Package package, PackageStatus toStatus)
220+
{
221+
var fromStatus = package.PackageStatusKey;
222+
223+
await _galleryPackageService.UpdatePackageStatusAsync(package, toStatus, commitChanges: true);
224+
225+
if (fromStatus != toStatus)
226+
{
227+
_telemetryService.TrackPackageStatusChange(fromStatus, toStatus);
228+
}
229+
}
196230
}
197231
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Threading.Tasks;
88
using Microsoft.Extensions.Logging;
99
using Microsoft.Extensions.Options;
10+
using NuGet.Services.Validation.Orchestrator.Telemetry;
1011
using NuGetGallery;
1112

1213
namespace NuGet.Services.Validation.Orchestrator
@@ -18,13 +19,15 @@ public class ValidationSetProcessor : IValidationSetProcessor
1819
private readonly IValidationStorageService _validationStorageService;
1920
private readonly ValidationConfiguration _validationConfiguration;
2021
private readonly ICorePackageFileService _packageFileService;
22+
private readonly ITelemetryService _telemetryService;
2123
private readonly ILogger<ValidationSetProcessor> _logger;
2224

2325
public ValidationSetProcessor(
2426
IValidatorProvider validatorProvider,
2527
IValidationStorageService validationStorageService,
2628
IOptionsSnapshot<ValidationConfiguration> validationConfigurationAccessor,
2729
ICorePackageFileService packageFileService,
30+
ITelemetryService telemetryService,
2831
ILogger<ValidationSetProcessor> logger)
2932
{
3033
_validatorProvider = validatorProvider ?? throw new ArgumentNullException(nameof(validatorProvider));
@@ -35,6 +38,7 @@ public ValidationSetProcessor(
3538
}
3639
_validationConfiguration = validationConfigurationAccessor.Value ?? throw new ArgumentException($"The Value property cannot be null", nameof(validationConfigurationAccessor));
3740
_packageFileService = packageFileService ?? throw new ArgumentNullException(nameof(packageFileService));
41+
_telemetryService = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
3842
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
3943
}
4044

@@ -195,6 +199,8 @@ private async Task<bool> ProcessNotStartedValidations(PackageValidationSet valid
195199
else
196200
{
197201
await _validationStorageService.MarkValidationStartedAsync(packageValidation, validationResult);
202+
203+
_telemetryService.TrackValidatorStarted(packageValidation.Type);
198204
}
199205

200206
tryMoreValidations = tryMoreValidations || validationResult.Status == ValidationStatus.Succeeded;
@@ -232,6 +238,9 @@ private async Task ProcessIncompleteValidation(PackageValidation packageValidati
232238
packageValidation.PackageValidationSet.PackageNormalizedVersion,
233239
validationConfiguration.FailAfter);
234240
await _validationStorageService.UpdateValidationStatusAsync(packageValidation, ValidationResult.Failed);
241+
242+
_telemetryService.TrackValidatorTimeout(packageValidation.Type);
243+
235244
return;
236245
}
237246
}

0 commit comments

Comments
 (0)