diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicExperimentEnrollmentConfiguration.java b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicExperimentEnrollmentConfiguration.java
index 04885c33f..9d9f019a6 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicExperimentEnrollmentConfiguration.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicExperimentEnrollmentConfiguration.java
@@ -26,8 +26,8 @@ public class DynamicExperimentEnrollmentConfiguration {
/**
* What percentage of enrolled UUIDs should the experiment be enabled for.
*
- * Unlike {@link this#enrollmentPercentage}, this is not stable by UUID. The same UUID may be
- * enrolled/unenrolled across calls.
+ * Unlike {@link this#enrollmentPercentage}, this is not stable by UUID. The same UUID may be enrolled/unenrolled
+ * across calls.
*/
@JsonProperty
@Valid
@@ -49,6 +49,14 @@ public class DynamicExperimentEnrollmentConfiguration {
@NotNull
private final UuidSelector uuidSelector = new UuidSelector();
+
+ /**
+ * UUIDs that the experiment should always be disabled for. This takes precedence over uuidSelector.
+ */
+ @Valid
+ @NotNull
+ private final Set excludedUuids = Collections.emptySet();
+
/**
* If the UUID is not enrolled via {@link UuidSelector#uuids}, what is the percentage chance it should be enrolled.
*
@@ -67,4 +75,9 @@ public class DynamicExperimentEnrollmentConfiguration {
public UuidSelector getUuidSelector() {
return uuidSelector;
}
+
+ public Set getExcludedUuids() {
+ return excludedUuids;
+ }
}
+
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/experiment/ExperimentEnrollmentManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/experiment/ExperimentEnrollmentManager.java
index cf95dead8..cfecdc8e8 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/experiment/ExperimentEnrollmentManager.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/experiment/ExperimentEnrollmentManager.java
@@ -5,11 +5,11 @@
package org.whispersystems.textsecuregcm.experiment;
+import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
-import com.google.common.annotations.VisibleForTesting;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicExperimentEnrollmentConfiguration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicPreRegistrationExperimentEnrollmentConfiguration;
@@ -47,6 +47,9 @@ public class ExperimentEnrollmentManager {
}
Optional isAccountEnrolled(final UUID accountUuid, DynamicExperimentEnrollmentConfiguration config) {
+ if (config.getExcludedUuids().contains(accountUuid)) {
+ return Optional.of(false);
+ }
if (config.getUuidSelector().getUuids().contains(accountUuid)) {
final int r = random.nextInt(100);
return Optional.of(r < config.getUuidSelector().getUuidEnrollmentPercentage());
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/experiment/ExperimentEnrollmentManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/experiment/ExperimentEnrollmentManagerTest.java
index 45a7fea6c..c3352a166 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/experiment/ExperimentEnrollmentManagerTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/experiment/ExperimentEnrollmentManagerTest.java
@@ -45,6 +45,7 @@ class ExperimentEnrollmentManagerTest {
private Random random;
private static final UUID ACCOUNT_UUID = UUID.randomUUID();
+ private static final UUID EXCLUDED_UUID = UUID.randomUUID();
private static final String UUID_EXPERIMENT_NAME = "uuid_test";
private static final String E164_AND_UUID_EXPERIMENT_NAME = "e164_uuid_test";
@@ -98,6 +99,12 @@ class ExperimentEnrollmentManagerTest {
when(experimentEnrollmentConfiguration.getEnrollmentPercentage()).thenReturn(100);
assertTrue(experimentEnrollmentManager.isEnrolled(account.getUuid(), UUID_EXPERIMENT_NAME));
+
+ when(experimentEnrollmentConfiguration.getExcludedUuids()).thenReturn(Set.of(EXCLUDED_UUID));
+ when(experimentEnrollmentConfiguration.getEnrollmentPercentage()).thenReturn(100);
+ when(uuidSelector.getUuidEnrollmentPercentage()).thenReturn(100);
+ when(uuidSelector.getUuids()).thenReturn(Set.of(EXCLUDED_UUID));
+ assertFalse(experimentEnrollmentManager.isEnrolled(EXCLUDED_UUID, UUID_EXPERIMENT_NAME));
}
@Test