mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-20 07:18:05 +01:00
Add hashKey to RemoteConfig
This allows the percentages for different entries in remote config to be aligned so one remote config can be a subset of another.
This commit is contained in:
@@ -23,6 +23,7 @@ import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
@@ -50,7 +51,8 @@ public class RemoteConfigController {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA1");
|
||||
|
||||
return new UserRemoteConfigList(remoteConfigsManager.getAll().stream().map(config -> {
|
||||
boolean inBucket = isInBucket(digest, account.getUuid(), config.getName().getBytes(), config.getPercentage(), config.getUuids());
|
||||
final byte[] hashKey = config.getHashKey() != null ? config.getHashKey().getBytes(StandardCharsets.UTF_8) : config.getName().getBytes(StandardCharsets.UTF_8);
|
||||
boolean inBucket = isInBucket(digest, account.getUuid(), hashKey, config.getPercentage(), config.getUuids());
|
||||
return new UserRemoteConfig(config.getName(), inBucket, inBucket ? config.getValue() : config.getDefaultValue());
|
||||
}).collect(Collectors.toList()));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
@@ -82,7 +84,7 @@ public class RemoteConfigController {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static boolean isInBucket(MessageDigest digest, UUID uid, byte[] configName, int configPercentage, Set<UUID> uuidsInBucket) {
|
||||
public static boolean isInBucket(MessageDigest digest, UUID uid, byte[] hashKey, int configPercentage, Set<UUID> uuidsInBucket) {
|
||||
if (uuidsInBucket.contains(uid)) return true;
|
||||
|
||||
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
|
||||
@@ -91,7 +93,7 @@ public class RemoteConfigController {
|
||||
|
||||
digest.update(bb.array());
|
||||
|
||||
byte[] hash = digest.digest(configName);
|
||||
byte[] hash = digest.digest(hashKey);
|
||||
int bucket = (int)(Math.abs(Conversions.byteArrayToLong(hash)) % 100);
|
||||
|
||||
return bucket < configPercentage;
|
||||
|
||||
@@ -32,14 +32,18 @@ public class RemoteConfig {
|
||||
@JsonProperty
|
||||
private String value;
|
||||
|
||||
@JsonProperty
|
||||
private String hashKey;
|
||||
|
||||
public RemoteConfig() {}
|
||||
|
||||
public RemoteConfig(String name, int percentage, Set<UUID> uuids, String defaultValue, String value) {
|
||||
public RemoteConfig(String name, int percentage, Set<UUID> uuids, String defaultValue, String value, String hashKey) {
|
||||
this.name = name;
|
||||
this.percentage = percentage;
|
||||
this.uuids = uuids;
|
||||
this.defaultValue = defaultValue;
|
||||
this.value = value;
|
||||
this.hashKey = hashKey;
|
||||
}
|
||||
|
||||
public int getPercentage() {
|
||||
@@ -61,4 +65,8 @@ public class RemoteConfig {
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getHashKey() {
|
||||
return hashKey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public class RemoteConfigs {
|
||||
public static final String UUIDS = "uuids";
|
||||
public static final String DEFAULT_VALUE = "default_value";
|
||||
public static final String VALUE = "value";
|
||||
public static final String HASH_KEY = "hash_key";
|
||||
|
||||
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
||||
private final Timer setTimer = metricRegistry.timer(name(Accounts.class, "set" ));
|
||||
@@ -36,12 +37,13 @@ public class RemoteConfigs {
|
||||
public void set(RemoteConfig remoteConfig) {
|
||||
database.use(jdbi -> jdbi.useHandle(handle -> {
|
||||
try (Timer.Context ignored = setTimer.time()) {
|
||||
handle.createUpdate("INSERT INTO remote_config (" + NAME + ", " + PERCENTAGE + ", " + UUIDS + ", " + DEFAULT_VALUE + ", " + VALUE + ") VALUES (:name, :percentage, :uuids, :default_value, :value) ON CONFLICT(" + NAME + ") DO UPDATE SET " + PERCENTAGE + " = EXCLUDED." + PERCENTAGE + ", " + UUIDS + " = EXCLUDED." + UUIDS + ", " + DEFAULT_VALUE + " = EXCLUDED." + DEFAULT_VALUE + ", " + VALUE + " = EXCLUDED." + VALUE)
|
||||
handle.createUpdate("INSERT INTO remote_config (" + NAME + ", " + PERCENTAGE + ", " + UUIDS + ", " + DEFAULT_VALUE + ", " + VALUE + ", " + HASH_KEY + ") VALUES (:name, :percentage, :uuids, :default_value, :value, :hash_key) ON CONFLICT(" + NAME + ") DO UPDATE SET " + PERCENTAGE + " = EXCLUDED." + PERCENTAGE + ", " + UUIDS + " = EXCLUDED." + UUIDS + ", " + DEFAULT_VALUE + " = EXCLUDED." + DEFAULT_VALUE + ", " + VALUE + " = EXCLUDED." + VALUE + ", " + HASH_KEY + " = EXCLUDED." + HASH_KEY)
|
||||
.bind("name", remoteConfig.getName())
|
||||
.bind("percentage", remoteConfig.getPercentage())
|
||||
.bind("uuids", remoteConfig.getUuids().toArray(new UUID[0]))
|
||||
.bind("default_value", remoteConfig.getDefaultValue())
|
||||
.bind("value", remoteConfig.getValue())
|
||||
.bind("hash_key", remoteConfig.getHashKey())
|
||||
.execute();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -20,6 +20,7 @@ public class RemoteConfigRowMapper implements RowMapper<RemoteConfig> {
|
||||
rs.getInt(RemoteConfigs.PERCENTAGE),
|
||||
new HashSet<>(Arrays.asList((UUID[])rs.getArray(RemoteConfigs.UUIDS).getArray())),
|
||||
rs.getString(RemoteConfigs.DEFAULT_VALUE),
|
||||
rs.getString(RemoteConfigs.VALUE));
|
||||
rs.getString(RemoteConfigs.VALUE),
|
||||
rs.getString(RemoteConfigs.HASH_KEY));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user