mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 03:58:05 +01:00
Add an experiment enrollment manager.
This commit is contained in:
committed by
Jon Chambers
parent
92f6a79e1f
commit
35fc98a188
@@ -1,5 +1,19 @@
|
||||
package org.whispersystems.textsecuregcm.configuration.dynamic;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class DynamicConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@Valid
|
||||
private Map<String, DynamicExperimentEnrollmentConfiguration> experiments = Collections.emptyMap();
|
||||
|
||||
public Optional<DynamicExperimentEnrollmentConfiguration> getExperimentEnrollmentConfiguration(final String experimentName) {
|
||||
return Optional.ofNullable(experiments.get(experimentName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.dynamic;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DynamicExperimentEnrollmentConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@Valid
|
||||
private Set<UUID> enrolledUuids = Collections.emptySet();
|
||||
|
||||
@JsonProperty
|
||||
@Valid
|
||||
@Min(0)
|
||||
@Max(100)
|
||||
private int enrollmentPercentage = 0;
|
||||
|
||||
public Set<UUID> getEnrolledUuids() {
|
||||
return enrolledUuids;
|
||||
}
|
||||
|
||||
public int getEnrollmentPercentage() {
|
||||
return enrollmentPercentage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.experiment;
|
||||
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicExperimentEnrollmentConfiguration;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ExperimentEnrollmentManager {
|
||||
|
||||
private final DynamicConfigurationManager dynamicConfigurationManager;
|
||||
|
||||
public ExperimentEnrollmentManager(final DynamicConfigurationManager dynamicConfigurationManager) {
|
||||
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
||||
}
|
||||
|
||||
public boolean isEnrolled(final Account account, final String experimentName) {
|
||||
final Optional<DynamicExperimentEnrollmentConfiguration> maybeConfiguration = dynamicConfigurationManager.getConfiguration().getExperimentEnrollmentConfiguration(experimentName);
|
||||
|
||||
final Set<UUID> enrolledUuids = maybeConfiguration.map(DynamicExperimentEnrollmentConfiguration::getEnrolledUuids)
|
||||
.orElse(Collections.emptySet());
|
||||
|
||||
final boolean enrolled;
|
||||
|
||||
if (enrolledUuids.contains(account.getUuid())) {
|
||||
enrolled = true;
|
||||
} else {
|
||||
final int threshold = maybeConfiguration.map(DynamicExperimentEnrollmentConfiguration::getEnrollmentPercentage).orElse(0);
|
||||
final int enrollmentHash = ((account.getUuid().hashCode() ^ experimentName.hashCode()) & Integer.MAX_VALUE) % 100;
|
||||
|
||||
enrolled = enrollmentHash < threshold;
|
||||
}
|
||||
|
||||
return enrolled;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@ public class DynamicConfigurationManager implements Managed {
|
||||
private final String clientId;
|
||||
private final AmazonAppConfig appConfigClient;
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
private final AtomicReference<DynamicConfiguration> configuration = new AtomicReference<>();
|
||||
private final AtomicBoolean running = new AtomicBoolean(true);
|
||||
private final Logger logger = LoggerFactory.getLogger(DynamicConfigurationManager.class);
|
||||
@@ -40,6 +39,8 @@ public class DynamicConfigurationManager implements Managed {
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(new YAMLFactory()).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
|
||||
public DynamicConfigurationManager(String application, String environment, String configurationName) {
|
||||
this(AmazonAppConfigClient.builder()
|
||||
.withClientConfiguration(new ClientConfiguration().withClientExecutionTimeout(10000).withRequestTimeout(10000))
|
||||
@@ -104,7 +105,7 @@ public class DynamicConfigurationManager implements Managed {
|
||||
|
||||
if (!StringUtils.equals(lastConfigResult.getConfigurationVersion(), previousVersion)) {
|
||||
logger.info("Received new config version: {}", lastConfigResult.getConfigurationVersion());
|
||||
maybeDynamicConfiguration = Optional.of(mapper.readValue(StandardCharsets.UTF_8.decode(lastConfigResult.getContent().asReadOnlyBuffer()).toString(), DynamicConfiguration.class));
|
||||
maybeDynamicConfiguration = Optional.of(OBJECT_MAPPER.readValue(StandardCharsets.UTF_8.decode(lastConfigResult.getContent().asReadOnlyBuffer()).toString(), DynamicConfiguration.class));
|
||||
} else {
|
||||
// No change since last version
|
||||
maybeDynamicConfiguration = Optional.empty();
|
||||
|
||||
Reference in New Issue
Block a user