Skip to content

Commit 8601490

Browse files
Add AB Testing to reverse dependencies feat (without view) (#8011)
Progress on #4718
1 parent 3e83e7f commit 8601490

19 files changed

Lines changed: 343 additions & 38 deletions

File tree

src/GitHubVulnerabilities2Db/Gallery/ThrowingTelemetryService.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ public void TraceException(Exception exception)
2121
throw new NotImplementedException();
2222
}
2323

24-
public void TrackABTestEnrollmentInitialized(int schemaVersion, int previewSearchBucket)
24+
public void TrackABTestEnrollmentInitialized(int schemaVersion, int previewSearchBucket, int packageDepentsBucket)
25+
{
26+
throw new NotImplementedException();
27+
}
28+
29+
public void TrackABTestEnrollmentUpgraded(int schemaVersion, int previewSearchBucket, int packageDepentsBucket)
2530
{
2631
throw new NotImplementedException();
2732
}

src/NuGetGallery.Services/Configuration/ABTestConfiguration.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,26 @@ public ABTestConfiguration()
1212
{
1313
PreviewSearchPercentage = 0;
1414
PreviewHijackPercentage = 0;
15+
DependentsPercentage = 0;
1516
}
1617

1718
[JsonConstructor]
18-
public ABTestConfiguration(int previewSearchPercentage, int previewHijackPercentage)
19+
public ABTestConfiguration(int previewSearchPercentage, int previewHijackPercentage, int dependentsPercentage)
1920
{
2021
GuardPercentage(previewSearchPercentage, nameof(previewSearchPercentage));
2122
GuardPercentage(previewHijackPercentage, nameof(previewHijackPercentage));
23+
GuardPercentage(dependentsPercentage, nameof(dependentsPercentage));
2224

2325
PreviewSearchPercentage = previewSearchPercentage;
2426
PreviewHijackPercentage = previewHijackPercentage;
27+
DependentsPercentage = dependentsPercentage;
2528
}
2629

2730
public int PreviewSearchPercentage { get; }
2831
public int PreviewHijackPercentage { get; }
2932

33+
public int DependentsPercentage { get; }
34+
3035
private static void GuardPercentage(int value, string paramName)
3136
{
3237
if (value < 0 || value > 100)

src/NuGetGallery.Services/Configuration/IABTestConfiguration.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,11 @@ public interface IABTestConfiguration
1616
/// hijack experience.
1717
/// </summary>
1818
int PreviewHijackPercentage { get; }
19+
20+
/// <summary>
21+
/// A value between 0 and 100 (inclusive) representing the desired percentage of users the should get the reverse dependencies details
22+
/// experience.
23+
/// </summary>
24+
int DependentsPercentage { get; }
1925
}
2026
}

src/NuGetGallery.Services/PackageManagement/PackageService.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,6 @@ orderby ng.Key.DownloadCount descending
179179
packageDependent.Id = pd.Id;
180180
packageDependentsList.Add(packageDependent);
181181
}
182-
183182
return packageDependentsList;
184183
}
185184

src/NuGetGallery.Services/Telemetry/ITelemetryService.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,9 +362,22 @@ void TrackSearchSideBySide(
362362
/// </summary>
363363
/// <param name="schemaVersion">The schema version.</param>
364364
/// <param name="previewSearchBucket">The bucket for the preview search test.</param>
365+
/// <param name="packageDependentBucket">The bucket for the package dependents test</param>
365366
void TrackABTestEnrollmentInitialized(
366367
int schemaVersion,
367-
int previewSearchBucket);
368+
int previewSearchBucket,
369+
int packageDependentBucket);
370+
371+
/// <summary>
372+
/// Track when an A/B test enrollment is upgraded
373+
/// </summary>
374+
/// <param name="schemaVersion">The schema version.</param>
375+
/// <param name="previewSearchBucket">The bucket for the preview search test.</param>
376+
/// <param name="packageDependentBucket">The bucket for the package dependents test</param>
377+
void TrackABTestEnrollmentUpgraded(
378+
int schemaVersion,
379+
int previewSearchBucket,
380+
int packageDependentBucket);
368381

369382
/// <summary>
370383
/// Track when an A/B test is evaluated.

src/NuGetGallery.Services/Telemetry/TelemetryService.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public class Events
8585
public const string SearchSideBySideFeedback = "SearchSideBySideFeedback";
8686
public const string SearchSideBySide = "SearchSideBySide";
8787
public const string ABTestEnrollmentInitialized = "ABTestEnrollmentInitialized";
88+
public const string ABTestEnrollmentUpgraded = "ABTestEnrollmentUpgraded";
8889
public const string ABTestEvaluated = "ABTestEvaluated";
8990
}
9091

@@ -213,8 +214,10 @@ public class Events
213214
public const string HasEmailAddress = "HasEmailAddress";
214215

215216
// A/B testing properties
216-
public const string SchemaVersion = "SchemaVersion";
217+
public const string OldSchemaVersion = "OldSchemaVersion";
218+
public const string NewSchemaVersion = "NewSchemaVersion";
217219
public const string PreviewSearchBucket = "PreviewSearchBucket";
220+
public const string PackageDependentBucket = "PackageDependentBucket";
218221
public const string TestName = "TestName";
219222
public const string IsActive = "IsActive";
220223
public const string TestBucket = "TestBucket";
@@ -1037,13 +1040,28 @@ public void TrackSearchSideBySide(
10371040
}
10381041

10391042
public void TrackABTestEnrollmentInitialized(
1040-
int schemaVersion,
1041-
int previewSearchBucket)
1043+
int newSchemaVersion,
1044+
int previewSearchBucket,
1045+
int packageDependentBucket)
10421046
{
10431047
TrackMetric(Events.ABTestEnrollmentInitialized, 1, properties =>
10441048
{
1045-
properties.Add(SchemaVersion, schemaVersion.ToString());
1049+
properties.Add(NewSchemaVersion, newSchemaVersion.ToString());
10461050
properties.Add(PreviewSearchBucket, previewSearchBucket.ToString());
1051+
properties.Add(PackageDependentBucket, packageDependentBucket.ToString());
1052+
});
1053+
}
1054+
1055+
public void TrackABTestEnrollmentUpgraded(
1056+
int newSchemaVersion,
1057+
int previewSearchBucket,
1058+
int packageDependentBucket)
1059+
{
1060+
TrackMetric(Events.ABTestEnrollmentUpgraded, 1, properties =>
1061+
{
1062+
properties.Add(NewSchemaVersion, newSchemaVersion.ToString());
1063+
properties.Add(PreviewSearchBucket, previewSearchBucket.ToString());
1064+
properties.Add(PackageDependentBucket, packageDependentBucket.ToString());
10471065
});
10481066
}
10491067

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2-
"PreviewSearchPercentage": 0,
3-
"PreviewHijackPercentage": 0
2+
"PreviewSearchPercentage": 0,
3+
"PreviewHijackPercentage": 0,
4+
"DependentsPercentage": 0
45
}

src/NuGetGallery/Controllers/PackagesController.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -856,8 +856,10 @@ public virtual async Task<ActionResult> DisplayPackage(string id, string version
856856
model.IsAtomFeedEnabled = _featureFlagService.IsPackagesAtomFeedEnabled();
857857
model.IsPackageDeprecationEnabled = _featureFlagService.IsManageDeprecationEnabled(currentUser, allVersions);
858858
model.IsPackageRenamesEnabled = _featureFlagService.IsPackageRenamesEnabled(currentUser);
859-
860-
if (model.IsPackageDependentsEnabled = _featureFlagService.IsPackageDependentsEnabled(currentUser))
859+
model.IsPackageDependentsEnabled = _featureFlagService.IsPackageDependentsEnabled(currentUser) &&
860+
_abTestService.IsPackageDependendentsABEnabled(currentUser);
861+
862+
if (model.IsPackageDependentsEnabled)
861863
{
862864
model.PackageDependents = GetPackageDependents(id);
863865
}
@@ -961,7 +963,6 @@ private PackageDependents GetPackageDependents(string id)
961963
DateTime.UtcNow.AddMinutes(5),
962964
Cache.NoSlidingExpiration,
963965
CacheItemPriority.Default, null);
964-
965966
}
966967

967968
// Cache contains PackageDependents

src/NuGetGallery/Infrastructure/ABTestEnrollment.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@ namespace NuGetGallery
2020
/// </summary>
2121
public class ABTestEnrollment
2222
{
23-
public ABTestEnrollment(ABTestEnrollmentState state, int schemaVersion, int previewSearchBucket)
23+
public ABTestEnrollment(ABTestEnrollmentState state, int schemaVersion, int previewSearchBucket, int packageDependentBucket)
2424
{
2525
State = state;
2626
SchemaVersion = schemaVersion;
2727
PreviewSearchBucket = previewSearchBucket;
28+
PackageDependentBucket = packageDependentBucket;
2829
}
29-
3030
public ABTestEnrollmentState State { get; }
3131
public int SchemaVersion { get; }
3232
public int PreviewSearchBucket { get; }
33+
public int PackageDependentBucket { get; }
3334
}
3435
}

src/NuGetGallery/Infrastructure/ABTestEnrollmentFactory.cs

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace NuGetGallery
1212
public class ABTestEnrollmentFactory : IABTestEnrollmentFactory
1313
{
1414
private const int SchemaVersion1 = 1;
15+
private const int SchemaVersion2 = 2;
1516

1617
private static readonly RNGCryptoServiceProvider _secureRng = new RNGCryptoServiceProvider();
1718
private static readonly ThreadLocal<byte[]> _bytes = new ThreadLocal<byte[]>(() => new byte[sizeof(ulong)]);
@@ -31,12 +32,14 @@ public ABTestEnrollment Initialize()
3132
{
3233
var enrollment = new ABTestEnrollment(
3334
ABTestEnrollmentState.FirstHit,
34-
SchemaVersion1,
35-
previewSearchBucket: GetRandomWholePercentage());
35+
SchemaVersion2,
36+
previewSearchBucket: GetRandomWholePercentage(),
37+
packageDependentBucket: GetRandomWholePercentage());
3638

3739
_telemetryService.TrackABTestEnrollmentInitialized(
3840
enrollment.SchemaVersion,
39-
enrollment.PreviewSearchBucket);
41+
enrollment.PreviewSearchBucket,
42+
enrollment.PackageDependentBucket);
4043

4144
return enrollment;
4245
}
@@ -59,18 +62,19 @@ private static int GetRandomWholePercentage()
5962

6063
public string Serialize(ABTestEnrollment enrollment)
6164
{
62-
if (enrollment.SchemaVersion != SchemaVersion1)
65+
if (enrollment.SchemaVersion != SchemaVersion2)
6366
{
6467
throw new NotImplementedException($"Serializing schema version {enrollment.SchemaVersion} is not implemented.");
6568
}
6669

67-
var deserialized = new StateVersion1
70+
var deserialized2 = new StateVersion2
6871
{
69-
SchemaVersion = SchemaVersion1,
72+
SchemaVersion = SchemaVersion2,
7073
PreviewSearchBucket = enrollment.PreviewSearchBucket,
74+
PackageDependentBucket = enrollment.PackageDependentBucket,
7175
};
7276

73-
return JsonConvert.SerializeObject(deserialized);
77+
return JsonConvert.SerializeObject(deserialized2);
7478
}
7579

7680
public bool TryDeserialize(string serialized, out ABTestEnrollment enrollment)
@@ -81,6 +85,12 @@ public bool TryDeserialize(string serialized, out ABTestEnrollment enrollment)
8185
return false;
8286
}
8387

88+
return TryDeserializeStateVer2(serialized, out enrollment) || TryDeserializeStateVer1(serialized, out enrollment);
89+
}
90+
91+
private bool TryDeserializeStateVer1(string serialized, out ABTestEnrollment enrollment)
92+
{
93+
enrollment = null;
8494
try
8595
{
8696
var v1 = JsonConvert.DeserializeObject<StateVersion1>(serialized);
@@ -91,10 +101,44 @@ public bool TryDeserialize(string serialized, out ABTestEnrollment enrollment)
91101
return false;
92102
}
93103

104+
enrollment = new ABTestEnrollment(
105+
ABTestEnrollmentState.Upgraded,
106+
SchemaVersion2,
107+
v1.PreviewSearchBucket,
108+
packageDependentBucket: GetRandomWholePercentage());
109+
110+
_telemetryService.TrackABTestEnrollmentUpgraded(
111+
enrollment.SchemaVersion,
112+
enrollment.PreviewSearchBucket,
113+
enrollment.PackageDependentBucket);
114+
115+
return true;
116+
}
117+
catch (JsonException)
118+
{
119+
return false;
120+
}
121+
}
122+
123+
private bool TryDeserializeStateVer2(string serialized, out ABTestEnrollment enrollment)
124+
{
125+
enrollment = null;
126+
try
127+
{
128+
var v2 = JsonConvert.DeserializeObject<StateVersion2>(serialized);
129+
if (v2 == null
130+
|| v2.SchemaVersion != SchemaVersion2
131+
|| IsNotPercentage(v2.PreviewSearchBucket)
132+
|| IsNotPercentage(v2.PackageDependentBucket))
133+
{
134+
return false;
135+
}
136+
94137
enrollment = new ABTestEnrollment(
95138
ABTestEnrollmentState.Active,
96-
v1.SchemaVersion,
97-
v1.PreviewSearchBucket);
139+
v2.SchemaVersion,
140+
v2.PreviewSearchBucket,
141+
v2.PackageDependentBucket);
98142

99143
return true;
100144
}
@@ -117,5 +161,17 @@ private class StateVersion1
117161
[JsonProperty("ps", Required = Required.Always)]
118162
public int PreviewSearchBucket { get; set; }
119163
}
164+
165+
private class StateVersion2
166+
{
167+
[JsonProperty("v", Required = Required.Always)]
168+
public int SchemaVersion { get; set; }
169+
170+
[JsonProperty("ps", Required = Required.Always)]
171+
public int PreviewSearchBucket { get; set; }
172+
173+
[JsonProperty("pd", Required = Required.Always)]
174+
public int PackageDependentBucket { get; set; }
175+
}
120176
}
121177
}

0 commit comments

Comments
 (0)