Read exclusively from the cache cluster.

This commit is contained in:
Jon Chambers 2020-06-16 10:56:17 -04:00 committed by Jon Chambers
parent 2b109db1b1
commit fc1d88f5bb
14 changed files with 315 additions and 386 deletions

View File

@ -77,7 +77,6 @@ import org.whispersystems.textsecuregcm.controllers.SecureBackupController;
import org.whispersystems.textsecuregcm.controllers.SecureStorageController; import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
import org.whispersystems.textsecuregcm.controllers.StickerController; import org.whispersystems.textsecuregcm.controllers.StickerController;
import org.whispersystems.textsecuregcm.controllers.VoiceVerificationController; import org.whispersystems.textsecuregcm.controllers.VoiceVerificationController;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.filters.TimestampResponseFilter; import org.whispersystems.textsecuregcm.filters.TimestampResponseFilter;
import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.liquibase.NameableMigrationsBundle; import org.whispersystems.textsecuregcm.liquibase.NameableMigrationsBundle;
@ -291,9 +290,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
DirectoryQueue directoryQueue = new DirectoryQueue(config.getDirectoryConfiguration().getSqsConfiguration()); DirectoryQueue directoryQueue = new DirectoryQueue(config.getDirectoryConfiguration().getSqsConfiguration());
PendingAccountsManager pendingAccountsManager = new PendingAccountsManager(pendingAccounts, cacheClient, cacheCluster); PendingAccountsManager pendingAccountsManager = new PendingAccountsManager(pendingAccounts, cacheClient, cacheCluster);
PendingDevicesManager pendingDevicesManager = new PendingDevicesManager(pendingDevices, cacheClient, cacheCluster); PendingDevicesManager pendingDevicesManager = new PendingDevicesManager(pendingDevices, cacheClient, cacheCluster);
AccountsManager accountsManager = new AccountsManager(accounts, directory, cacheClient, cacheCluster, new Experiment("RedisCluster", "AccountsManager")); AccountsManager accountsManager = new AccountsManager(accounts, directory, cacheClient, cacheCluster);
UsernamesManager usernamesManager = new UsernamesManager(usernames, reservedUsernames, cacheClient, cacheCluster, new Experiment("RedisCluster", "UsernamesManager")); UsernamesManager usernamesManager = new UsernamesManager(usernames, reservedUsernames, cacheClient, cacheCluster);
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheClient, cacheCluster, new Experiment("RedisCluster", "ProfilesManager")); ProfilesManager profilesManager = new ProfilesManager(profiles, cacheClient, cacheCluster);
MessagesCache messagesCache = new MessagesCache(messagesClient, messages, accountsManager, config.getMessageCacheConfiguration().getPersistDelayMinutes()); MessagesCache messagesCache = new MessagesCache(messagesClient, messages, accountsManager, config.getMessageCacheConfiguration().getPersistDelayMinutes());
MessagesManager messagesManager = new MessagesManager(messages, messagesCache); MessagesManager messagesManager = new MessagesManager(messages, messagesCache);
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs); RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
@ -326,7 +325,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
DirectoryReconciliationClient directoryReconciliationClient = new DirectoryReconciliationClient(config.getDirectoryConfiguration().getDirectoryServerConfiguration()); DirectoryReconciliationClient directoryReconciliationClient = new DirectoryReconciliationClient(config.getDirectoryConfiguration().getDirectoryServerConfiguration());
ActiveUserCounter activeUserCounter = new ActiveUserCounter(config.getMetricsFactory(), cacheClient, cacheCluster, new Experiment("RedisCluster", "ActiveUserCounter")); ActiveUserCounter activeUserCounter = new ActiveUserCounter(config.getMetricsFactory(), cacheClient, cacheCluster);
DirectoryReconciler directoryReconciler = new DirectoryReconciler(directoryReconciliationClient, directory); DirectoryReconciler directoryReconciler = new DirectoryReconciler(directoryReconciliationClient, directory);
AccountCleaner accountCleaner = new AccountCleaner(accountsManager, directoryQueue); AccountCleaner accountCleaner = new AccountCleaner(accountsManager, directoryQueue);
PushFeedbackProcessor pushFeedbackProcessor = new PushFeedbackProcessor(accountsManager, directoryQueue); PushFeedbackProcessor pushFeedbackProcessor = new PushFeedbackProcessor(accountsManager, directoryQueue);

View File

@ -25,7 +25,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException; import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.util.Constants; import org.whispersystems.textsecuregcm.util.Constants;
@ -49,7 +48,6 @@ public class RateLimiter {
private final int bucketSize; private final int bucketSize;
private final double leakRatePerMillis; private final double leakRatePerMillis;
private final boolean reportLimits; private final boolean reportLimits;
private final Experiment redisClusterExperiment;
public RateLimiter(ReplicatedJedisPool cacheClient, FaultTolerantRedisCluster cacheCluster, String name, public RateLimiter(ReplicatedJedisPool cacheClient, FaultTolerantRedisCluster cacheCluster, String name,
int bucketSize, double leakRatePerMinute) int bucketSize, double leakRatePerMinute)
@ -71,7 +69,6 @@ public class RateLimiter {
this.bucketSize = bucketSize; this.bucketSize = bucketSize;
this.leakRatePerMillis = leakRatePerMinute / (60.0 * 1000.0); this.leakRatePerMillis = leakRatePerMinute / (60.0 * 1000.0);
this.reportLimits = reportLimits; this.reportLimits = reportLimits;
this.redisClusterExperiment = new Experiment("RedisCluster", "RateLimiter", name);
} }
public void validate(String key, int amount) throws RateLimitExceededException { public void validate(String key, int amount) throws RateLimitExceededException {
@ -114,11 +111,8 @@ public class RateLimiter {
} }
private LeakyBucket getBucket(String key) { private LeakyBucket getBucket(String key) {
try (Jedis jedis = cacheClient.getReadResource()) { try {
final String bucketName = getBucketName(key); final String serialized = cacheCluster.withReadCluster(connection -> connection.sync().get(getBucketName(key)));
String serialized = jedis.get(bucketName);
redisClusterExperiment.compareSupplierResult(serialized, () -> cacheCluster.withReadCluster(connection -> connection.sync().get(bucketName)));
if (serialized != null) { if (serialized != null) {
return LeakyBucket.fromSerialized(mapper, serialized); return LeakyBucket.fromSerialized(mapper, serialized);

View File

@ -20,11 +20,11 @@ import io.lettuce.core.ScriptOutputType;
import io.lettuce.core.SetArgs; import io.lettuce.core.SetArgs;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.redis.ClusterLuaScript; import org.whispersystems.textsecuregcm.redis.ClusterLuaScript;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.LuaScript; import org.whispersystems.textsecuregcm.redis.LuaScript;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import redis.clients.jedis.Jedis;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -32,8 +32,6 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import redis.clients.jedis.Jedis;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class AccountDatabaseCrawlerCache { public class AccountDatabaseCrawlerCache {
@ -49,8 +47,6 @@ public class AccountDatabaseCrawlerCache {
private final FaultTolerantRedisCluster cacheCluster; private final FaultTolerantRedisCluster cacheCluster;
private final LuaScript unlockScript; private final LuaScript unlockScript;
private final ClusterLuaScript unlockClusterScript; private final ClusterLuaScript unlockClusterScript;
private final Experiment isAcceleratedExperiment = new Experiment("RedisCluster", "AccountDatabaseCrawlerCache", "isAccelerated");
private final Experiment getLastUuidExperiment = new Experiment("RedisCluster", "AccountDatabaseCrawlerCache", "getLastUuid");
public AccountDatabaseCrawlerCache(ReplicatedJedisPool jedisPool, FaultTolerantRedisCluster cacheCluster) throws IOException { public AccountDatabaseCrawlerCache(ReplicatedJedisPool jedisPool, FaultTolerantRedisCluster cacheCluster) throws IOException {
this.jedisPool = jedisPool; this.jedisPool = jedisPool;
@ -67,12 +63,7 @@ public class AccountDatabaseCrawlerCache {
} }
public boolean isAccelerated() { public boolean isAccelerated() {
try (Jedis jedis = jedisPool.getWriteResource()) { return "1".equals(cacheCluster.withReadCluster(connection -> connection.sync().get(ACCELERATE_KEY)));
final String accelerated = jedis.get(ACCELERATE_KEY);
isAcceleratedExperiment.compareSupplierResult(accelerated, () -> cacheCluster.withReadCluster(connection -> connection.sync().get(ACCELERATE_KEY)));
return "1".equals(accelerated);
}
} }
public boolean claimActiveWork(String workerId, long ttlMs) { public boolean claimActiveWork(String workerId, long ttlMs) {
@ -101,14 +92,11 @@ public class AccountDatabaseCrawlerCache {
} }
public Optional<UUID> getLastUuid() { public Optional<UUID> getLastUuid() {
try (Jedis jedis = jedisPool.getWriteResource()) { final String lastUuidString = cacheCluster.withWriteCluster(connection -> connection.sync().get(LAST_UUID_KEY));
String lastUuidString = jedis.get(LAST_UUID_KEY);
getLastUuidExperiment.compareSupplierResult(lastUuidString, () -> cacheCluster.withWriteCluster(connection -> connection.sync().get(LAST_UUID_KEY)));
if (lastUuidString == null) return Optional.empty(); if (lastUuidString == null) return Optional.empty();
else return Optional.of(UUID.fromString(lastUuidString)); else return Optional.of(UUID.fromString(lastUuidString));
} }
}
public void setLastUuid(Optional<UUID> lastUuid) { public void setLastUuid(Optional<UUID> lastUuid) {
try (Jedis jedis = jedisPool.getWriteResource()) { try (Jedis jedis = jedisPool.getWriteResource()) {

View File

@ -22,13 +22,13 @@ import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.Timer; import com.codahale.metrics.Timer;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import io.lettuce.core.RedisException;
import io.lettuce.core.cluster.api.async.RedisAdvancedClusterAsyncCommands; import io.lettuce.core.cluster.api.async.RedisAdvancedClusterAsyncCommands;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands; import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier; import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
import org.whispersystems.textsecuregcm.entities.ClientContact; import org.whispersystems.textsecuregcm.entities.ClientContact;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.util.Constants; import org.whispersystems.textsecuregcm.util.Constants;
@ -42,7 +42,6 @@ import java.util.UUID;
import static com.codahale.metrics.MetricRegistry.name; import static com.codahale.metrics.MetricRegistry.name;
import redis.clients.jedis.Jedis; import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisException;
public class AccountsManager { public class AccountsManager {
@ -63,15 +62,13 @@ public class AccountsManager {
private final FaultTolerantRedisCluster cacheCluster; private final FaultTolerantRedisCluster cacheCluster;
private final DirectoryManager directory; private final DirectoryManager directory;
private final ObjectMapper mapper; private final ObjectMapper mapper;
private final Experiment redisClusterExperiment;
public AccountsManager(Accounts accounts, DirectoryManager directory, ReplicatedJedisPool cacheClient, FaultTolerantRedisCluster cacheCluster, Experiment redisClusterExperiment) { public AccountsManager(Accounts accounts, DirectoryManager directory, ReplicatedJedisPool cacheClient, FaultTolerantRedisCluster cacheCluster) {
this.accounts = accounts; this.accounts = accounts;
this.directory = directory; this.directory = directory;
this.cacheClient = cacheClient; this.cacheClient = cacheClient;
this.cacheCluster = cacheCluster; this.cacheCluster = cacheCluster;
this.mapper = SystemMapper.getMapper(); this.mapper = SystemMapper.getMapper();
this.redisClusterExperiment = redisClusterExperiment;
} }
public boolean create(Account account) { public boolean create(Account account) {
@ -174,37 +171,23 @@ public class AccountsManager {
} }
private Optional<Account> redisGet(String number) { private Optional<Account> redisGet(String number) {
try (Jedis jedis = cacheClient.getReadResource(); try (Timer.Context ignored = redisNumberGetTimer.time()) {
Timer.Context ignored = redisNumberGetTimer.time()) final String uuid = cacheCluster.withReadCluster(connection -> connection.sync().get(getAccountMapKey(number)));
{
final String key = getAccountMapKey(number);
String uuid = jedis.get(key); if (uuid != null) return redisGet(UUID.fromString(uuid));
redisClusterExperiment.compareSupplierResult(uuid, () -> cacheCluster.withReadCluster(connection -> connection.sync().get(key)));
if (uuid != null) return redisGet(jedis, UUID.fromString(uuid));
else return Optional.empty(); else return Optional.empty();
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
logger.warn("Deserialization error", e); logger.warn("Deserialization error", e);
return Optional.empty(); return Optional.empty();
} catch (JedisException e) { } catch (RedisException e) {
logger.warn("Redis failure", e); logger.warn("Redis failure", e);
return Optional.empty(); return Optional.empty();
} }
} }
private Optional<Account> redisGet(UUID uuid) { private Optional<Account> redisGet(UUID uuid) {
try (Jedis jedis = cacheClient.getReadResource()) {
return redisGet(jedis, uuid);
}
}
private Optional<Account> redisGet(Jedis jedis, UUID uuid) {
try (Timer.Context ignored = redisUuidGetTimer.time()) { try (Timer.Context ignored = redisUuidGetTimer.time()) {
final String key = getAccountEntityKey(uuid); final String json = cacheCluster.withReadCluster(connection -> connection.sync().get(getAccountEntityKey(uuid)));
String json = jedis.get(key);
redisClusterExperiment.compareSupplierResult(json, () -> cacheCluster.withReadCluster(connection -> connection.sync().get(key)));
if (json != null) { if (json != null) {
Account account = mapper.readValue(json, Account.class); Account account = mapper.readValue(json, Account.class);
@ -217,7 +200,7 @@ public class AccountsManager {
} catch (IOException e) { } catch (IOException e) {
logger.warn("Deserialization error", e); logger.warn("Deserialization error", e);
return Optional.empty(); return Optional.empty();
} catch (JedisException e) { } catch (RedisException e) {
logger.warn("Redis failure", e); logger.warn("Redis failure", e);
return Optional.empty(); return Optional.empty();
} }

View File

@ -21,7 +21,6 @@ import com.codahale.metrics.MetricRegistry;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.whispersystems.textsecuregcm.entities.ActiveUserTally; import org.whispersystems.textsecuregcm.entities.ActiveUserTally;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.util.SystemMapper; import org.whispersystems.textsecuregcm.util.SystemMapper;
@ -52,14 +51,12 @@ public class ActiveUserCounter extends AccountDatabaseCrawlerListener {
private final ReplicatedJedisPool jedisPool; private final ReplicatedJedisPool jedisPool;
private final FaultTolerantRedisCluster cacheCluster; private final FaultTolerantRedisCluster cacheCluster;
private final ObjectMapper mapper; private final ObjectMapper mapper;
private final Experiment redisClusterExperiment;
public ActiveUserCounter(MetricsFactory metricsFactory, ReplicatedJedisPool jedisPool, FaultTolerantRedisCluster cacheCluster, Experiment redisClusterExperiment) { public ActiveUserCounter(MetricsFactory metricsFactory, ReplicatedJedisPool jedisPool, FaultTolerantRedisCluster cacheCluster) {
this.metricsFactory = metricsFactory; this.metricsFactory = metricsFactory;
this.jedisPool = jedisPool; this.jedisPool = jedisPool;
this.cacheCluster = cacheCluster; this.cacheCluster = cacheCluster;
this.mapper = SystemMapper.getMapper(); this.mapper = SystemMapper.getMapper();
this.redisClusterExperiment = redisClusterExperiment;
} }
@Override @Override
@ -164,8 +161,7 @@ public class ActiveUserCounter extends AccountDatabaseCrawlerListener {
private void incrementTallies(UUID fromUuid, Map<String, long[]> platformIncrements, Map<String, long[]> countryIncrements) { private void incrementTallies(UUID fromUuid, Map<String, long[]> platformIncrements, Map<String, long[]> countryIncrements) {
try (Jedis jedis = jedisPool.getWriteResource()) { try (Jedis jedis = jedisPool.getWriteResource()) {
String tallyValue = jedis.get(TALLY_KEY); final String tallyValue = cacheCluster.withReadCluster(connection -> connection.sync().get(TALLY_KEY));
redisClusterExperiment.compareSupplierResult(tallyValue, () -> cacheCluster.withReadCluster(connection -> connection.sync().get(TALLY_KEY)));
ActiveUserTally activeUserTally; ActiveUserTally activeUserTally;
@ -208,9 +204,8 @@ public class ActiveUserCounter extends AccountDatabaseCrawlerListener {
} }
private ActiveUserTally getFinalTallies() { private ActiveUserTally getFinalTallies() {
try (Jedis jedis = jedisPool.getReadResource()) { try {
final String tallyJson = jedis.get(TALLY_KEY); final String tallyJson = cacheCluster.withReadCluster(connection -> connection.sync().get(TALLY_KEY));
redisClusterExperiment.compareSupplierResult(tallyJson, () -> cacheCluster.withReadCluster(connection -> connection.sync().get(TALLY_KEY)));
return mapper.readValue(tallyJson, ActiveUserTally.class); return mapper.readValue(tallyJson, ActiveUserTally.class);
} catch (IOException e) { } catch (IOException e) {

View File

@ -21,7 +21,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode; import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.util.SystemMapper; import org.whispersystems.textsecuregcm.util.SystemMapper;
@ -41,7 +40,6 @@ public class PendingAccountsManager {
private final ReplicatedJedisPool cacheClient; private final ReplicatedJedisPool cacheClient;
private final FaultTolerantRedisCluster cacheCluster; private final FaultTolerantRedisCluster cacheCluster;
private final ObjectMapper mapper; private final ObjectMapper mapper;
private final Experiment redisClusterExperiment = new Experiment("RedisCluster", "PendingAccountsManager");
public PendingAccountsManager(PendingAccounts pendingAccounts, ReplicatedJedisPool cacheClient, FaultTolerantRedisCluster cacheCluster) public PendingAccountsManager(PendingAccounts pendingAccounts, ReplicatedJedisPool cacheClient, FaultTolerantRedisCluster cacheCluster)
{ {
@ -85,11 +83,8 @@ public class PendingAccountsManager {
} }
private Optional<StoredVerificationCode> memcacheGet(String number) { private Optional<StoredVerificationCode> memcacheGet(String number) {
try (Jedis jedis = cacheClient.getReadResource()) { try {
final String key = CACHE_PREFIX + number; final String json = cacheCluster.withReadCluster(connection -> connection.sync().get(CACHE_PREFIX + number));
String json = jedis.get(key);
redisClusterExperiment.compareSupplierResult(json, () -> cacheCluster.withReadCluster(connection -> connection.sync().get(key)));
if (json == null) return Optional.empty(); if (json == null) return Optional.empty();
else return Optional.of(mapper.readValue(json, StoredVerificationCode.class)); else return Optional.of(mapper.readValue(json, StoredVerificationCode.class));

View File

@ -21,7 +21,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode; import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.util.SystemMapper; import org.whispersystems.textsecuregcm.util.SystemMapper;
@ -41,7 +40,6 @@ public class PendingDevicesManager {
private final ReplicatedJedisPool cacheClient; private final ReplicatedJedisPool cacheClient;
private final FaultTolerantRedisCluster cacheCluster; private final FaultTolerantRedisCluster cacheCluster;
private final ObjectMapper mapper; private final ObjectMapper mapper;
private final Experiment redisClusterExperiment = new Experiment("RedisCluster", "PendingDevicesManager");
public PendingDevicesManager(PendingDevices pendingDevices, ReplicatedJedisPool cacheClient, FaultTolerantRedisCluster cacheCluster) { public PendingDevicesManager(PendingDevices pendingDevices, ReplicatedJedisPool cacheClient, FaultTolerantRedisCluster cacheCluster) {
this.pendingDevices = pendingDevices; this.pendingDevices = pendingDevices;
@ -84,11 +82,8 @@ public class PendingDevicesManager {
} }
private Optional<StoredVerificationCode> memcacheGet(String number) { private Optional<StoredVerificationCode> memcacheGet(String number) {
try (Jedis jedis = cacheClient.getReadResource()) { try {
final String key = CACHE_PREFIX + number; final String json = cacheCluster.withReadCluster(connection -> connection.sync().get(CACHE_PREFIX + number));
String json = jedis.get(key);
redisClusterExperiment.compareSupplierResult(json, () -> cacheCluster.withReadCluster(connection -> connection.sync().get(key)));
if (json == null) return Optional.empty(); if (json == null) return Optional.empty();
else return Optional.of(mapper.readValue(json, StoredVerificationCode.class)); else return Optional.of(mapper.readValue(json, StoredVerificationCode.class));

View File

@ -2,9 +2,9 @@ package org.whispersystems.textsecuregcm.storage;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import io.lettuce.core.RedisException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.util.SystemMapper; import org.whispersystems.textsecuregcm.util.SystemMapper;
@ -14,7 +14,6 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import redis.clients.jedis.Jedis; import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisException;
public class ProfilesManager { public class ProfilesManager {
@ -26,14 +25,12 @@ public class ProfilesManager {
private final ReplicatedJedisPool cacheClient; private final ReplicatedJedisPool cacheClient;
private final FaultTolerantRedisCluster cacheCluster; private final FaultTolerantRedisCluster cacheCluster;
private final ObjectMapper mapper; private final ObjectMapper mapper;
private final Experiment redisClusterExperiment;
public ProfilesManager(Profiles profiles, ReplicatedJedisPool cacheClient, FaultTolerantRedisCluster cacheCluster, Experiment redisClusterExperiment) { public ProfilesManager(Profiles profiles, ReplicatedJedisPool cacheClient, FaultTolerantRedisCluster cacheCluster) {
this.profiles = profiles;
this.cacheClient = cacheClient; this.cacheClient = cacheClient;
this.profiles = profiles;
this.cacheCluster = cacheCluster; this.cacheCluster = cacheCluster;
this.mapper = SystemMapper.getMapper(); this.mapper = SystemMapper.getMapper();
this.redisClusterExperiment = redisClusterExperiment;
} }
public void set(UUID uuid, VersionedProfile versionedProfile) { public void set(UUID uuid, VersionedProfile versionedProfile) {
@ -70,18 +67,15 @@ public class ProfilesManager {
} }
private Optional<VersionedProfile> memcacheGet(UUID uuid, String version) { private Optional<VersionedProfile> memcacheGet(UUID uuid, String version) {
try (Jedis jedis = cacheClient.getReadResource()) { try {
final String key = CACHE_PREFIX + uuid.toString(); final String json = cacheCluster.withReadCluster(connection -> connection.sync().hget(CACHE_PREFIX + uuid.toString(), version));
String json = jedis.hget(key, version);
redisClusterExperiment.compareSupplierResult(json, () -> cacheCluster.withReadCluster(connection -> connection.sync().hget(key, version)));
if (json == null) return Optional.empty(); if (json == null) return Optional.empty();
else return Optional.of(mapper.readValue(json, VersionedProfile.class)); else return Optional.of(mapper.readValue(json, VersionedProfile.class));
} catch (IOException e) { } catch (IOException e) {
logger.warn("Error deserializing value...", e); logger.warn("Error deserializing value...", e);
return Optional.empty(); return Optional.empty();
} catch (JedisException e) { } catch (RedisException e) {
logger.warn("Redis exception", e); logger.warn("Redis exception", e);
return Optional.empty(); return Optional.empty();
} }

View File

@ -3,10 +3,10 @@ package org.whispersystems.textsecuregcm.storage;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries; import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.Timer; import com.codahale.metrics.Timer;
import io.lettuce.core.RedisException;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands; import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.util.Constants; import org.whispersystems.textsecuregcm.util.Constants;
@ -36,14 +36,12 @@ public class UsernamesManager {
private final ReservedUsernames reservedUsernames; private final ReservedUsernames reservedUsernames;
private final ReplicatedJedisPool cacheClient; private final ReplicatedJedisPool cacheClient;
private final FaultTolerantRedisCluster cacheCluster; private final FaultTolerantRedisCluster cacheCluster;
private final Experiment redisClusterExperiment;
public UsernamesManager(Usernames usernames, ReservedUsernames reservedUsernames, ReplicatedJedisPool cacheClient, FaultTolerantRedisCluster cacheCluster, Experiment redisClusterExperiment) { public UsernamesManager(Usernames usernames, ReservedUsernames reservedUsernames, ReplicatedJedisPool cacheClient, FaultTolerantRedisCluster cacheCluster) {
this.usernames = usernames; this.usernames = usernames;
this.reservedUsernames = reservedUsernames; this.reservedUsernames = reservedUsernames;
this.cacheClient = cacheClient; this.cacheClient = cacheClient;
this.cacheCluster = cacheCluster; this.cacheCluster = cacheCluster;
this.redisClusterExperiment = redisClusterExperiment;
} }
public boolean put(UUID uuid, String username) { public boolean put(UUID uuid, String username) {
@ -116,59 +114,50 @@ public class UsernamesManager {
} }
private void redisSet(UUID uuid, String username, boolean required) { private void redisSet(UUID uuid, String username, boolean required) {
try (Jedis jedis = cacheClient.getWriteResource();
Timer.Context ignored = redisSetTimer.time())
{
final String uuidMapKey = getUuidMapKey(uuid); final String uuidMapKey = getUuidMapKey(uuid);
final String usernameMapKey = getUsernameMapKey(username); final String usernameMapKey = getUsernameMapKey(username);
final Optional<String> maybeOldUsername = Optional.ofNullable(jedis.get(uuidMapKey)); try (Timer.Context ignored = redisSetTimer.time()) {
maybeOldUsername.ifPresent(oldUsername -> jedis.del(getUsernameMapKey(oldUsername)));
jedis.set(uuidMapKey, username);
jedis.set(usernameMapKey, uuid.toString());
cacheCluster.useWriteCluster(connection -> { cacheCluster.useWriteCluster(connection -> {
final RedisAdvancedClusterCommands<String, String> commands = connection.sync(); final RedisAdvancedClusterCommands<String, String> commands = connection.sync();
final Optional<String> maybeOldUsername = Optional.ofNullable(commands.get(uuidMapKey));
maybeOldUsername.ifPresent(oldUsername -> commands.del(getUsernameMapKey(oldUsername))); maybeOldUsername.ifPresent(oldUsername -> commands.del(getUsernameMapKey(oldUsername)));
commands.set(uuidMapKey, username); commands.set(uuidMapKey, username);
commands.set(usernameMapKey, uuid.toString()); commands.set(usernameMapKey, uuid.toString());
try (final Jedis jedis = cacheClient.getWriteResource()) {
maybeOldUsername.ifPresent(oldUsername -> jedis.del(getUsernameMapKey(oldUsername)));
jedis.set(uuidMapKey, username);
jedis.set(usernameMapKey, uuid.toString());
}
}); });
} catch (JedisException e) { } catch (JedisException | RedisException e) {
if (required) throw e; if (required) throw e;
else logger.warn("Ignoring jedis failure", e); else logger.warn("Ignoring Redis failure", e);
} }
} }
private Optional<UUID> redisGet(String username) { private Optional<UUID> redisGet(String username) {
try (Jedis jedis = cacheClient.getReadResource(); try (Timer.Context ignored = redisUsernameGetTimer.time()) {
Timer.Context ignored = redisUsernameGetTimer.time()) final String result = cacheCluster.withReadCluster(connection -> connection.sync().get(getUsernameMapKey(username)));
{
final String key = getUsernameMapKey(username);
String result = jedis.get(key);
redisClusterExperiment.compareSupplierResult(result, () -> cacheCluster.withReadCluster(connection -> connection.sync().get(key)));
if (result == null) return Optional.empty(); if (result == null) return Optional.empty();
else return Optional.of(UUID.fromString(result)); else return Optional.of(UUID.fromString(result));
} catch (JedisException e) { } catch (RedisException e) {
logger.warn("Redis get failure", e); logger.warn("Redis get failure", e);
return Optional.empty(); return Optional.empty();
} }
} }
private Optional<String> redisGet(UUID uuid) { private Optional<String> redisGet(UUID uuid) {
try (Jedis jedis = cacheClient.getReadResource(); try (Timer.Context ignored = redisUuidGetTimer.time()) {
Timer.Context ignored = redisUuidGetTimer.time()) final String result = cacheCluster.withReadCluster(connection -> connection.sync().get(getUuidMapKey(uuid)));
{
final String key = getUuidMapKey(uuid);
final String result = jedis.get(key);
redisClusterExperiment.compareSupplierResult(result, () -> cacheCluster.withReadCluster(connection -> connection.sync().get(key)));
return Optional.ofNullable(result); return Optional.ofNullable(result);
} catch (JedisException e) { } catch (RedisException e) {
logger.warn("Redis get failure", e); logger.warn("Redis get failure", e);
return Optional.empty(); return Optional.empty();
} }

View File

@ -1,8 +1,10 @@
package org.whispersystems.textsecuregcm.workers; package org.whispersystems.textsecuregcm.workers;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import io.lettuce.core.RedisURI; import io.dropwizard.Application;
import io.lettuce.core.cluster.RedisClusterClient; import io.dropwizard.cli.EnvironmentCommand;
import io.dropwizard.jdbi3.JdbiFactory;
import io.dropwizard.setup.Environment;
import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser; import net.sourceforge.argparse4j.inf.Subparser;
import org.jdbi.v3.core.Jdbi; import org.jdbi.v3.core.Jdbi;
@ -10,7 +12,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.WhisperServerConfiguration; import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials; import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.providers.RedisClientFactory; import org.whispersystems.textsecuregcm.providers.RedisClientFactory;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
@ -25,12 +26,6 @@ import org.whispersystems.textsecuregcm.util.Base64;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import io.dropwizard.Application;
import io.dropwizard.cli.EnvironmentCommand;
import io.dropwizard.jdbi3.JdbiFactory;
import io.dropwizard.setup.Environment;
public class DeleteUserCommand extends EnvironmentCommand<WhisperServerConfiguration> { public class DeleteUserCommand extends EnvironmentCommand<WhisperServerConfiguration> {
@ -78,7 +73,7 @@ public class DeleteUserCommand extends EnvironmentCommand<WhisperServerConfigura
ReplicatedJedisPool redisClient = new RedisClientFactory("directory_cache_delete_command", configuration.getDirectoryConfiguration().getRedisConfiguration().getUrl(), configuration.getDirectoryConfiguration().getRedisConfiguration().getReplicaUrls(), configuration.getDirectoryConfiguration().getRedisConfiguration().getCircuitBreakerConfiguration()).getRedisClientPool(); ReplicatedJedisPool redisClient = new RedisClientFactory("directory_cache_delete_command", configuration.getDirectoryConfiguration().getRedisConfiguration().getUrl(), configuration.getDirectoryConfiguration().getRedisConfiguration().getReplicaUrls(), configuration.getDirectoryConfiguration().getRedisConfiguration().getCircuitBreakerConfiguration()).getRedisClientPool();
DirectoryQueue directoryQueue = new DirectoryQueue (configuration.getDirectoryConfiguration().getSqsConfiguration()); DirectoryQueue directoryQueue = new DirectoryQueue (configuration.getDirectoryConfiguration().getSqsConfiguration());
DirectoryManager directory = new DirectoryManager(redisClient ); DirectoryManager directory = new DirectoryManager(redisClient );
AccountsManager accountsManager = new AccountsManager(accounts, directory, cacheClient, cacheCluster, new Experiment("RedisCluster", "AccountsManager")); AccountsManager accountsManager = new AccountsManager(accounts, directory, cacheClient, cacheCluster);
for (String user: users) { for (String user: users) {
Optional<Account> account = accountsManager.get(user); Optional<Account> account = accountsManager.get(user);

View File

@ -1,13 +1,16 @@
package org.whispersystems.textsecuregcm.tests.storage; package org.whispersystems.textsecuregcm.tests.storage;
import io.lettuce.core.RedisException;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import org.junit.Test; import org.junit.Test;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Accounts; import org.whispersystems.textsecuregcm.storage.Accounts;
import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.DirectoryManager; import org.whispersystems.textsecuregcm.storage.DirectoryManager;
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
import redis.clients.jedis.Jedis;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
@ -17,54 +20,58 @@ import static junit.framework.TestCase.assertSame;
import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.anyString;
import redis.clients.jedis.Jedis; import static org.mockito.Mockito.mock;
import redis.clients.jedis.exceptions.JedisException; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class AccountsManagerTest { public class AccountsManagerTest {
@Test @Test
public void testGetAccountByNumberInCache() { public void testGetAccountByNumberInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class ); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class ); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
Accounts accounts = mock(Accounts.class); Accounts accounts = mock(Accounts.class);
DirectoryManager directoryManager = mock(DirectoryManager.class); DirectoryManager directoryManager = mock(DirectoryManager.class);
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis); when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(uuid.toString());
when(jedis.get(eq("AccountMap::+14152222222"))).thenReturn(uuid.toString()); when(commands.get(eq("Account3::" + uuid.toString()))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}");
when(jedis.get(eq("Account3::" + uuid.toString()))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}");
AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient, cacheCluster, mock(Experiment.class)); AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient, cacheCluster);
Optional<Account> account = accountsManager.get("+14152222222"); Optional<Account> account = accountsManager.get("+14152222222");
assertTrue(account.isPresent()); assertTrue(account.isPresent());
assertEquals(account.get().getNumber(), "+14152222222"); assertEquals(account.get().getNumber(), "+14152222222");
assertEquals(account.get().getProfileName(), "test"); assertEquals(account.get().getProfileName(), "test");
verify(jedis, times(1)).get(eq("AccountMap::+14152222222")); verify(commands, times(1)).get(eq("AccountMap::+14152222222"));
verify(jedis, times(1)).get(eq("Account3::" + uuid.toString())); verify(commands, times(1)).get(eq("Account3::" + uuid.toString()));
verify(jedis, times(1)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verifyNoMoreInteractions(accounts); verifyNoMoreInteractions(accounts);
} }
@Test @Test
public void testGetAccountByUuidInCache() { public void testGetAccountByUuidInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class ); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class ); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
Accounts accounts = mock(Accounts.class); Accounts accounts = mock(Accounts.class);
DirectoryManager directoryManager = mock(DirectoryManager.class); DirectoryManager directoryManager = mock(DirectoryManager.class);
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis); when(commands.get(eq("Account3::" + uuid.toString()))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}");
when(jedis.get(eq("Account3::" + uuid.toString()))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}");
AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient, cacheCluster, mock(Experiment.class)); AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient, cacheCluster);
Optional<Account> account = accountsManager.get(uuid); Optional<Account> account = accountsManager.get(uuid);
assertTrue(account.isPresent()); assertTrue(account.isPresent());
@ -72,39 +79,37 @@ public class AccountsManagerTest {
assertEquals(account.get().getUuid(), uuid); assertEquals(account.get().getUuid(), uuid);
assertEquals(account.get().getProfileName(), "test"); assertEquals(account.get().getProfileName(), "test");
verify(jedis, times(1)).get(eq("Account3::" + uuid.toString())); verify(commands, times(1)).get(eq("Account3::" + uuid.toString()));
verify(jedis, times(1)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verifyNoMoreInteractions(accounts); verifyNoMoreInteractions(accounts);
} }
@Test @Test
public void testGetAccountByNumberNotInCache() { public void testGetAccountByNumberNotInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class ); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class ); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
Accounts accounts = mock(Accounts.class); Accounts accounts = mock(Accounts.class);
DirectoryManager directoryManager = mock(DirectoryManager.class); DirectoryManager directoryManager = mock(DirectoryManager.class);
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]); Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
when(cacheClient.getReadResource()).thenReturn(jedis); ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(jedis); when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
when(jedis.get(eq("AccountMap::+14152222222"))).thenReturn(null);
when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(null);
when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account)); when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account));
AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient, cacheCluster, mock(Experiment.class)); AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient, cacheCluster);
Optional<Account> retrieved = accountsManager.get("+14152222222"); Optional<Account> retrieved = accountsManager.get("+14152222222");
assertTrue(retrieved.isPresent()); assertTrue(retrieved.isPresent());
assertSame(retrieved.get(), account); assertSame(retrieved.get(), account);
verify(jedis, times(1)).get(eq("AccountMap::+14152222222")); verify(commands, times(1)).get(eq("AccountMap::+14152222222"));
verify(jedis, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString())); verify(commands, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
verify(jedis, times(1)).set(eq("Account3::" + uuid.toString()), anyString()); verify(commands, times(1)).set(eq("Account3::" + uuid.toString()), anyString());
verify(jedis, times(2)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verify(accounts, times(1)).get(eq("+14152222222")); verify(accounts, times(1)).get(eq("+14152222222"));
verifyNoMoreInteractions(accounts); verifyNoMoreInteractions(accounts);
@ -112,30 +117,29 @@ public class AccountsManagerTest {
@Test @Test
public void testGetAccountByUuidNotInCache() { public void testGetAccountByUuidNotInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class ); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class ); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
Accounts accounts = mock(Accounts.class); Accounts accounts = mock(Accounts.class);
DirectoryManager directoryManager = mock(DirectoryManager.class); DirectoryManager directoryManager = mock(DirectoryManager.class);
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]); Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
when(cacheClient.getReadResource()).thenReturn(jedis); ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(jedis); when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
when(jedis.get(eq("Account3::" + uuid))).thenReturn(null);
when(commands.get(eq("Account3::" + uuid))).thenReturn(null);
when(accounts.get(eq(uuid))).thenReturn(Optional.of(account)); when(accounts.get(eq(uuid))).thenReturn(Optional.of(account));
AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient, cacheCluster, mock(Experiment.class)); AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient, cacheCluster);
Optional<Account> retrieved = accountsManager.get(uuid); Optional<Account> retrieved = accountsManager.get(uuid);
assertTrue(retrieved.isPresent()); assertTrue(retrieved.isPresent());
assertSame(retrieved.get(), account); assertSame(retrieved.get(), account);
verify(jedis, times(1)).get(eq("Account3::" + uuid)); verify(commands, times(1)).get(eq("Account3::" + uuid));
verify(jedis, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString())); verify(commands, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
verify(jedis, times(1)).set(eq("Account3::" + uuid.toString()), anyString()); verify(commands, times(1)).set(eq("Account3::" + uuid.toString()), anyString());
verify(jedis, times(2)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verify(accounts, times(1)).get(eq(uuid)); verify(accounts, times(1)).get(eq(uuid));
verifyNoMoreInteractions(accounts); verifyNoMoreInteractions(accounts);
@ -143,30 +147,29 @@ public class AccountsManagerTest {
@Test @Test
public void testGetAccountByNumberBrokenCache() { public void testGetAccountByNumberBrokenCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class ); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class ); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
Accounts accounts = mock(Accounts.class); Accounts accounts = mock(Accounts.class);
DirectoryManager directoryManager = mock(DirectoryManager.class); DirectoryManager directoryManager = mock(DirectoryManager.class);
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]); Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
when(cacheClient.getReadResource()).thenReturn(jedis); ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(jedis); when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
when(jedis.get(eq("AccountMap::+14152222222"))).thenThrow(new JedisException("Connection lost!"));
when(commands.get(eq("AccountMap::+14152222222"))).thenThrow(new RedisException("Connection lost!"));
when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account)); when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account));
AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient, cacheCluster, mock(Experiment.class)); AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient, cacheCluster);
Optional<Account> retrieved = accountsManager.get("+14152222222"); Optional<Account> retrieved = accountsManager.get("+14152222222");
assertTrue(retrieved.isPresent()); assertTrue(retrieved.isPresent());
assertSame(retrieved.get(), account); assertSame(retrieved.get(), account);
verify(jedis, times(1)).get(eq("AccountMap::+14152222222")); verify(commands, times(1)).get(eq("AccountMap::+14152222222"));
verify(jedis, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString())); verify(commands, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
verify(jedis, times(1)).set(eq("Account3::" + uuid.toString()), anyString()); verify(commands, times(1)).set(eq("Account3::" + uuid.toString()), anyString());
verify(jedis, times(2)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verify(accounts, times(1)).get(eq("+14152222222")); verify(accounts, times(1)).get(eq("+14152222222"));
verifyNoMoreInteractions(accounts); verifyNoMoreInteractions(accounts);
@ -174,30 +177,29 @@ public class AccountsManagerTest {
@Test @Test
public void testGetAccountByUuidBrokenCache() { public void testGetAccountByUuidBrokenCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class ); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class ); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
Accounts accounts = mock(Accounts.class); Accounts accounts = mock(Accounts.class);
DirectoryManager directoryManager = mock(DirectoryManager.class); DirectoryManager directoryManager = mock(DirectoryManager.class);
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]); Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
when(cacheClient.getReadResource()).thenReturn(jedis); ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(jedis); when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
when(jedis.get(eq("Account3::" + uuid))).thenThrow(new JedisException("Connection lost!"));
when(commands.get(eq("Account3::" + uuid))).thenThrow(new RedisException("Connection lost!"));
when(accounts.get(eq(uuid))).thenReturn(Optional.of(account)); when(accounts.get(eq(uuid))).thenReturn(Optional.of(account));
AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient, cacheCluster, mock(Experiment.class)); AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient, cacheCluster);
Optional<Account> retrieved = accountsManager.get(uuid); Optional<Account> retrieved = accountsManager.get(uuid);
assertTrue(retrieved.isPresent()); assertTrue(retrieved.isPresent());
assertSame(retrieved.get(), account); assertSame(retrieved.get(), account);
verify(jedis, times(1)).get(eq("Account3::" + uuid)); verify(commands, times(1)).get(eq("Account3::" + uuid));
verify(jedis, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString())); verify(commands, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
verify(jedis, times(1)).set(eq("Account3::" + uuid.toString()), anyString()); verify(commands, times(1)).set(eq("Account3::" + uuid.toString()), anyString());
verify(jedis, times(2)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verify(accounts, times(1)).get(eq(uuid)); verify(accounts, times(1)).get(eq(uuid));
verifyNoMoreInteractions(accounts); verifyNoMoreInteractions(accounts);

View File

@ -17,14 +17,14 @@
package org.whispersystems.textsecuregcm.tests.storage; package org.whispersystems.textsecuregcm.tests.storage;
import org.whispersystems.textsecuregcm.experiment.Experiment; import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.ActiveUserCounter; import org.whispersystems.textsecuregcm.storage.ActiveUserCounter;
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerRestartException; import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerRestartException;
import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.Util; import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import io.dropwizard.metrics.MetricsFactory; import io.dropwizard.metrics.MetricsFactory;
@ -38,7 +38,6 @@ import java.util.concurrent.TimeUnit;
import java.util.Optional; import java.util.Optional;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.eq; import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
@ -66,12 +65,13 @@ public class ActiveUserCounterTest {
private final Account iosAccount = mock(Account.class); private final Account iosAccount = mock(Account.class);
private final Account noDeviceAccount = mock(Account.class); private final Account noDeviceAccount = mock(Account.class);
private final Jedis jedis = mock(Jedis.class); private final RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
private final ReplicatedJedisPool jedisPool = mock(ReplicatedJedisPool.class); private final FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
private final FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
private final MetricsFactory metricsFactory = mock(MetricsFactory.class); private final MetricsFactory metricsFactory = mock(MetricsFactory.class);
private final ActiveUserCounter activeUserCounter = new ActiveUserCounter(metricsFactory, jedisPool, cacheCluster, mock(Experiment.class)); private final ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
private final ActiveUserCounter activeUserCounter = new ActiveUserCounter(metricsFactory, cacheClient, cacheCluster);
@Before @Before
public void setup() { public void setup() {
@ -99,20 +99,18 @@ public class ActiveUserCounterTest {
when(noDeviceAccount.getMasterDevice()).thenReturn(Optional.ofNullable(null)); when(noDeviceAccount.getMasterDevice()).thenReturn(Optional.ofNullable(null));
when(noDeviceAccount.getNumber()).thenReturn(ACCOUNT_NUMBER_NODEVICE); when(noDeviceAccount.getNumber()).thenReturn(ACCOUNT_NUMBER_NODEVICE);
when(jedis.get(any(String.class))).thenReturn("{\"fromNumber\":\"+\",\"platforms\":{},\"countries\":{}}"); when(commands.get(any(String.class))).thenReturn("{\"fromNumber\":\"+\",\"platforms\":{},\"countries\":{}}");
when(jedisPool.getWriteResource()).thenReturn(jedis);
when(jedisPool.getReadResource()).thenReturn(jedis);
when(metricsFactory.getReporters()).thenReturn(ImmutableList.of()); when(metricsFactory.getReporters()).thenReturn(ImmutableList.of());
when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
} }
@Test @Test
public void testCrawlStart() { public void testCrawlStart() {
activeUserCounter.onCrawlStart(); activeUserCounter.onCrawlStart();
verify(jedisPool, times(1)).getWriteResource(); verify(cacheCluster, times(1)).useWriteCluster(any());
verify(jedis, times(1)).del(any(String.class)); verify(commands, times(1)).del(any(String.class));
verify(jedis, times(1)).close();
verifyZeroInteractions(iosDevice); verifyZeroInteractions(iosDevice);
verifyZeroInteractions(iosAccount); verifyZeroInteractions(iosAccount);
@ -120,17 +118,16 @@ public class ActiveUserCounterTest {
verifyZeroInteractions(androidAccount); verifyZeroInteractions(androidAccount);
verifyZeroInteractions(noDeviceAccount); verifyZeroInteractions(noDeviceAccount);
verifyZeroInteractions(metricsFactory); verifyZeroInteractions(metricsFactory);
verifyNoMoreInteractions(jedis); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedisPool); verifyNoMoreInteractions(cacheCluster);
} }
@Test @Test
public void testCrawlEnd() { public void testCrawlEnd() {
activeUserCounter.onCrawlEnd(Optional.empty()); activeUserCounter.onCrawlEnd(Optional.empty());
verify(jedisPool, times(1)).getReadResource(); verify(cacheCluster, times(1)).withReadCluster(any());
verify(jedis, times(1)).get(any(String.class)); verify(commands, times(1)).get(any(String.class));
verify(jedis, times(1)).close();
verify(metricsFactory, times(1)).getReporters(); verify(metricsFactory, times(1)).getReporters();
@ -141,8 +138,8 @@ public class ActiveUserCounterTest {
verifyZeroInteractions(noDeviceAccount); verifyZeroInteractions(noDeviceAccount);
verifyNoMoreInteractions(metricsFactory); verifyNoMoreInteractions(metricsFactory);
verifyNoMoreInteractions(jedis); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedisPool); verifyNoMoreInteractions(cacheCluster);
} }
@ -157,10 +154,10 @@ public class ActiveUserCounterTest {
verify(iosDevice, times(1)).getApnId(); verify(iosDevice, times(1)).getApnId();
verify(iosDevice, times(0)).getGcmId(); verify(iosDevice, times(0)).getGcmId();
verify(jedisPool, times(1)).getWriteResource(); verify(cacheCluster, times(1)).withReadCluster(any());
verify(jedis, times(1)).get(any(String.class)); verify(cacheCluster, times(1)).useWriteCluster(any());
verify(jedis, times(1)).set(any(String.class), eq("{\"fromUuid\":\""+UUID_IOS.toString()+"\",\"platforms\":{\"ios\":[1,1,1,1,1]},\"countries\":{\"1\":[1,1,1,1,1]}}")); verify(commands, times(1)).get(any(String.class));
verify(jedis, times(1)).close(); verify(commands, times(1)).set(any(String.class), eq("{\"fromUuid\":\""+UUID_IOS.toString()+"\",\"platforms\":{\"ios\":[1,1,1,1,1]},\"countries\":{\"1\":[1,1,1,1,1]}}"));
verify(metricsFactory, times(0)).getReporters(); verify(metricsFactory, times(0)).getReporters();
@ -171,8 +168,8 @@ public class ActiveUserCounterTest {
verifyNoMoreInteractions(iosDevice); verifyNoMoreInteractions(iosDevice);
verifyNoMoreInteractions(iosAccount); verifyNoMoreInteractions(iosAccount);
verifyNoMoreInteractions(jedis); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedisPool); verifyNoMoreInteractions(cacheCluster);
} }
@Test @Test
@ -181,10 +178,10 @@ public class ActiveUserCounterTest {
verify(noDeviceAccount, times(1)).getMasterDevice(); verify(noDeviceAccount, times(1)).getMasterDevice();
verify(jedisPool, times(1)).getWriteResource(); verify(cacheCluster, times(1)).withReadCluster(any());
verify(jedis, times(1)).get(eq(TALLY_KEY)); verify(cacheCluster, times(1)).useWriteCluster(any());
verify(jedis, times(1)).set(any(String.class), eq("{\"fromUuid\":\""+UUID_NODEVICE+"\",\"platforms\":{},\"countries\":{}}")); verify(commands, times(1)).get(eq(TALLY_KEY));
verify(jedis, times(1)).close(); verify(commands, times(1)).set(any(String.class), eq("{\"fromUuid\":\""+UUID_NODEVICE+"\",\"platforms\":{},\"countries\":{}}"));
verify(metricsFactory, times(0)).getReporters(); verify(metricsFactory, times(0)).getReporters();
@ -195,8 +192,8 @@ public class ActiveUserCounterTest {
verifyZeroInteractions(noDeviceAccount); verifyZeroInteractions(noDeviceAccount);
verifyZeroInteractions(metricsFactory); verifyZeroInteractions(metricsFactory);
verifyNoMoreInteractions(jedis); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedisPool); verifyNoMoreInteractions(cacheCluster);
} }
@Test @Test
@ -217,10 +214,10 @@ public class ActiveUserCounterTest {
verify(androidDevice, times(1)).getApnId(); verify(androidDevice, times(1)).getApnId();
verify(androidDevice, times(1)).getGcmId(); verify(androidDevice, times(1)).getGcmId();
verify(jedisPool, times(1)).getWriteResource(); verify(cacheCluster, times(1)).withReadCluster(any());
verify(jedis, times(1)).get(eq(TALLY_KEY)); verify(cacheCluster, times(1)).useWriteCluster(any());
verify(jedis, times(1)).set(any(String.class), eq("{\"fromUuid\":\""+UUID_IOS+"\",\"platforms\":{\"android\":[0,0,0,1,1],\"ios\":[1,1,1,1,1]},\"countries\":{\"55\":[0,0,0,1,1],\"1\":[1,1,1,1,1]}}")); verify(commands, times(1)).get(eq(TALLY_KEY));
verify(jedis, times(1)).close(); verify(commands, times(1)).set(any(String.class), eq("{\"fromUuid\":\""+UUID_IOS+"\",\"platforms\":{\"android\":[0,0,0,1,1],\"ios\":[1,1,1,1,1]},\"countries\":{\"55\":[0,0,0,1,1],\"1\":[1,1,1,1,1]}}"));
verify(metricsFactory, times(0)).getReporters(); verify(metricsFactory, times(0)).getReporters();
@ -231,8 +228,8 @@ public class ActiveUserCounterTest {
verifyNoMoreInteractions(androidDevice); verifyNoMoreInteractions(androidDevice);
verifyNoMoreInteractions(androidAccount); verifyNoMoreInteractions(androidAccount);
verifyNoMoreInteractions(noDeviceAccount); verifyNoMoreInteractions(noDeviceAccount);
verifyNoMoreInteractions(jedis); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedisPool); verifyNoMoreInteractions(cacheCluster);
} }
} }

View File

@ -1,13 +1,16 @@
package org.whispersystems.textsecuregcm.tests.storage; package org.whispersystems.textsecuregcm.tests.storage;
import io.lettuce.core.RedisException;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import org.junit.Test; import org.junit.Test;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.storage.Profiles; import org.whispersystems.textsecuregcm.storage.Profiles;
import org.whispersystems.textsecuregcm.storage.ProfilesManager; import org.whispersystems.textsecuregcm.storage.ProfilesManager;
import org.whispersystems.textsecuregcm.storage.VersionedProfile; import org.whispersystems.textsecuregcm.storage.VersionedProfile;
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
import org.whispersystems.textsecuregcm.util.Base64; import org.whispersystems.textsecuregcm.util.Base64;
import redis.clients.jedis.Jedis;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@ -18,25 +21,28 @@ import static org.assertj.core.api.Java6Assertions.assertThat;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.mock;
import redis.clients.jedis.Jedis; import static org.mockito.Mockito.times;
import redis.clients.jedis.exceptions.JedisException; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class ProfilesManagerTest { public class ProfilesManagerTest {
@Test @Test
public void testGetProfileInCache() { public void testGetProfileInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class ); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
Profiles profiles = mock(Profiles.class); Profiles profiles = mock(Profiles.class);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis); when(commands.hget(eq("profiles::" + uuid.toString()), eq("someversion"))).thenReturn("{\"version\": \"someversion\", \"name\": \"somename\", \"avatar\": \"someavatar\", \"commitment\":\"" + Base64.encodeBytes("somecommitment".getBytes()) + "\"}");
when(jedis.hget(eq("profiles::" + uuid.toString()), eq("someversion"))).thenReturn("{\"version\": \"someversion\", \"name\": \"somename\", \"avatar\": \"someavatar\", \"commitment\":\"" + Base64.encodeBytes("somecommitment".getBytes()) + "\"}");
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheClient, cacheCluster, mock(Experiment.class)); ProfilesManager profilesManager = new ProfilesManager(profiles, cacheClient, cacheCluster);
Optional<VersionedProfile> profile = profilesManager.get(uuid, "someversion"); Optional<VersionedProfile> profile = profilesManager.get(uuid, "someversion");
assertTrue(profile.isPresent()); assertTrue(profile.isPresent());
@ -44,37 +50,35 @@ public class ProfilesManagerTest {
assertEquals(profile.get().getAvatar(), "someavatar"); assertEquals(profile.get().getAvatar(), "someavatar");
assertThat(profile.get().getCommitment()).isEqualTo("somecommitment".getBytes()); assertThat(profile.get().getCommitment()).isEqualTo("somecommitment".getBytes());
verify(jedis, times(1)).hget(eq("profiles::" + uuid.toString()), eq("someversion")); verify(commands, times(1)).hget(eq("profiles::" + uuid.toString()), eq("someversion"));
verify(jedis, times(1)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verifyNoMoreInteractions(profiles); verifyNoMoreInteractions(profiles);
} }
@Test @Test
public void testGetProfileNotInCache() { public void testGetProfileNotInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class ); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
Profiles profiles = mock(Profiles.class); Profiles profiles = mock(Profiles.class);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
VersionedProfile profile = new VersionedProfile("someversion", "somename", "someavatar", "somecommitment".getBytes()); VersionedProfile profile = new VersionedProfile("someversion", "somename", "someavatar", "somecommitment".getBytes());
when(cacheClient.getReadResource()).thenReturn(jedis); when(commands.hget(eq("profiles::" + uuid.toString()), eq("someversion"))).thenReturn(null);
when(cacheClient.getWriteResource()).thenReturn(jedis);
when(jedis.hget(eq("profiles::" + uuid.toString()), eq("someversion"))).thenReturn(null);
when(profiles.get(eq(uuid), eq("someversion"))).thenReturn(Optional.of(profile)); when(profiles.get(eq(uuid), eq("someversion"))).thenReturn(Optional.of(profile));
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheClient, cacheCluster, mock(Experiment.class)); ProfilesManager profilesManager = new ProfilesManager(profiles, cacheClient, cacheCluster);
Optional<VersionedProfile> retrieved = profilesManager.get(uuid, "someversion"); Optional<VersionedProfile> retrieved = profilesManager.get(uuid, "someversion");
assertTrue(retrieved.isPresent()); assertTrue(retrieved.isPresent());
assertSame(retrieved.get(), profile); assertSame(retrieved.get(), profile);
verify(jedis, times(1)).hget(eq("profiles::" + uuid.toString()), eq("someversion")); verify(commands, times(1)).hget(eq("profiles::" + uuid.toString()), eq("someversion"));
verify(jedis, times(1)).hset(eq("profiles::" + uuid.toString()), eq("someversion"), anyString()); verify(commands, times(1)).hset(eq("profiles::" + uuid.toString()), eq("someversion"), anyString());
verify(jedis, times(2)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verify(profiles, times(1)).get(eq(uuid), eq("someversion")); verify(profiles, times(1)).get(eq(uuid), eq("someversion"));
verifyNoMoreInteractions(profiles); verifyNoMoreInteractions(profiles);
@ -82,29 +86,28 @@ public class ProfilesManagerTest {
@Test @Test
public void testGetProfileBrokenCache() { public void testGetProfileBrokenCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class ); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
Profiles profiles = mock(Profiles.class); Profiles profiles = mock(Profiles.class);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
VersionedProfile profile = new VersionedProfile("someversion", "somename", "someavatar", "somecommitment".getBytes()); VersionedProfile profile = new VersionedProfile("someversion", "somename", "someavatar", "somecommitment".getBytes());
when(cacheClient.getReadResource()).thenReturn(jedis); when(commands.hget(eq("profiles::" + uuid.toString()), eq("someversion"))).thenThrow(new RedisException("Connection lost"));
when(cacheClient.getWriteResource()).thenReturn(jedis);
when(jedis.hget(eq("profiles::" + uuid.toString()), eq("someversion"))).thenThrow(new JedisException("Connection lost"));
when(profiles.get(eq(uuid), eq("someversion"))).thenReturn(Optional.of(profile)); when(profiles.get(eq(uuid), eq("someversion"))).thenReturn(Optional.of(profile));
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheClient, cacheCluster, mock(Experiment.class)); ProfilesManager profilesManager = new ProfilesManager(profiles, cacheClient, cacheCluster);
Optional<VersionedProfile> retrieved = profilesManager.get(uuid, "someversion"); Optional<VersionedProfile> retrieved = profilesManager.get(uuid, "someversion");
assertTrue(retrieved.isPresent()); assertTrue(retrieved.isPresent());
assertSame(retrieved.get(), profile); assertSame(retrieved.get(), profile);
verify(jedis, times(1)).hget(eq("profiles::" + uuid.toString()), eq("someversion")); verify(commands, times(1)).hget(eq("profiles::" + uuid.toString()), eq("someversion"));
verify(jedis, times(1)).hset(eq("profiles::" + uuid.toString()), eq("someversion"), anyString()); verify(commands, times(1)).hset(eq("profiles::" + uuid.toString()), eq("someversion"), anyString());
verify(jedis, times(2)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verify(profiles, times(1)).get(eq(uuid), eq("someversion")); verify(profiles, times(1)).get(eq(uuid), eq("someversion"));
verifyNoMoreInteractions(profiles); verifyNoMoreInteractions(profiles);

View File

@ -1,12 +1,15 @@
package org.whispersystems.textsecuregcm.tests.storage; package org.whispersystems.textsecuregcm.tests.storage;
import io.lettuce.core.RedisException;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import org.junit.Test; import org.junit.Test;
import org.whispersystems.textsecuregcm.experiment.Experiment;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.storage.ReservedUsernames; import org.whispersystems.textsecuregcm.storage.ReservedUsernames;
import org.whispersystems.textsecuregcm.storage.Usernames; import org.whispersystems.textsecuregcm.storage.Usernames;
import org.whispersystems.textsecuregcm.storage.UsernamesManager; import org.whispersystems.textsecuregcm.storage.UsernamesManager;
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
import redis.clients.jedis.Jedis;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@ -15,91 +18,91 @@ import static junit.framework.TestCase.assertSame;
import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.mock;
import redis.clients.jedis.Jedis; import static org.mockito.Mockito.times;
import redis.clients.jedis.exceptions.JedisException; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class UsernamesManagerTest { public class UsernamesManagerTest {
@Test @Test
public void testGetByUsernameInCache() { public void testGetByUsernameInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
Usernames usernames = mock(Usernames.class); Usernames usernames = mock(Usernames.class);
ReservedUsernames reserved = mock(ReservedUsernames.class); ReservedUsernames reserved = mock(ReservedUsernames.class);
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis); when(commands.get(eq("UsernameByUsername::n00bkiller"))).thenReturn(uuid.toString());
when(jedis.get(eq("UsernameByUsername::n00bkiller"))).thenReturn(uuid.toString());
UsernamesManager usernamesManager = new UsernamesManager(usernames, reserved, cacheClient, cacheCluster, mock(Experiment.class)); UsernamesManager usernamesManager = new UsernamesManager(usernames, reserved, cacheClient, cacheCluster);
Optional<UUID> retrieved = usernamesManager.get("n00bkiller"); Optional<UUID> retrieved = usernamesManager.get("n00bkiller");
assertTrue(retrieved.isPresent()); assertTrue(retrieved.isPresent());
assertEquals(retrieved.get(), uuid); assertEquals(retrieved.get(), uuid);
verify(jedis, times(1)).get(eq("UsernameByUsername::n00bkiller")); verify(commands, times(1)).get(eq("UsernameByUsername::n00bkiller"));
verify(jedis, times(1)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verifyNoMoreInteractions(usernames); verifyNoMoreInteractions(usernames);
} }
@Test @Test
public void testGetByUuidInCache() { public void testGetByUuidInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
Usernames usernames = mock(Usernames.class); Usernames usernames = mock(Usernames.class);
ReservedUsernames reserved = mock(ReservedUsernames.class); ReservedUsernames reserved = mock(ReservedUsernames.class);
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis); when(commands.get(eq("UsernameByUuid::" + uuid.toString()))).thenReturn("n00bkiller");
when(jedis.get(eq("UsernameByUuid::" + uuid.toString()))).thenReturn("n00bkiller");
UsernamesManager usernamesManager = new UsernamesManager(usernames, reserved, cacheClient, cacheCluster, mock(Experiment.class)); UsernamesManager usernamesManager = new UsernamesManager(usernames, reserved, cacheClient, cacheCluster);
Optional<String> retrieved = usernamesManager.get(uuid); Optional<String> retrieved = usernamesManager.get(uuid);
assertTrue(retrieved.isPresent()); assertTrue(retrieved.isPresent());
assertEquals(retrieved.get(), "n00bkiller"); assertEquals(retrieved.get(), "n00bkiller");
verify(jedis, times(1)).get(eq("UsernameByUuid::" + uuid.toString())); verify(commands, times(1)).get(eq("UsernameByUuid::" + uuid.toString()));
verify(jedis, times(1)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verifyNoMoreInteractions(usernames); verifyNoMoreInteractions(usernames);
} }
@Test @Test
public void testGetByUsernameNotInCache() { public void testGetByUsernameNotInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
Usernames usernames = mock(Usernames.class); Usernames usernames = mock(Usernames.class);
ReservedUsernames reserved = mock(ReservedUsernames.class); ReservedUsernames reserved = mock(ReservedUsernames.class);
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
when(commands.get(eq("UsernameByUsername::n00bkiller"))).thenReturn(null);
when(cacheClient.getReadResource()).thenReturn(jedis);
when(cacheClient.getWriteResource()).thenReturn(jedis);
when(jedis.get(eq("UsernameByUsername::n00bkiller"))).thenReturn(null);
when(usernames.get(eq("n00bkiller"))).thenReturn(Optional.of(uuid)); when(usernames.get(eq("n00bkiller"))).thenReturn(Optional.of(uuid));
UsernamesManager usernamesManager = new UsernamesManager(usernames, reserved, cacheClient, cacheCluster, mock(Experiment.class)); UsernamesManager usernamesManager = new UsernamesManager(usernames, reserved, cacheClient, cacheCluster);
Optional<UUID> retrieved = usernamesManager.get("n00bkiller"); Optional<UUID> retrieved = usernamesManager.get("n00bkiller");
assertTrue(retrieved.isPresent()); assertTrue(retrieved.isPresent());
assertSame(retrieved.get(), uuid); assertSame(retrieved.get(), uuid);
verify(jedis, times(1)).get(eq("UsernameByUsername::n00bkiller")); verify(commands, times(1)).get(eq("UsernameByUsername::n00bkiller"));
verify(jedis, times(1)).set(eq("UsernameByUsername::n00bkiller"), eq(uuid.toString())); verify(commands, times(1)).set(eq("UsernameByUsername::n00bkiller"), eq(uuid.toString()));
verify(jedis, times(1)).set(eq("UsernameByUuid::" + uuid.toString()), eq("n00bkiller")); verify(commands, times(1)).set(eq("UsernameByUuid::" + uuid.toString()), eq("n00bkiller"));
verify(jedis, times(1)).get(eq("UsernameByUuid::" + uuid.toString())); verify(commands, times(1)).get(eq("UsernameByUuid::" + uuid.toString()));
verify(jedis, times(2)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verify(usernames, times(1)).get(eq("n00bkiller")); verify(usernames, times(1)).get(eq("n00bkiller"));
verifyNoMoreInteractions(usernames); verifyNoMoreInteractions(usernames);
@ -107,30 +110,29 @@ public class UsernamesManagerTest {
@Test @Test
public void testGetByUuidNotInCache() { public void testGetByUuidNotInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
Usernames usernames = mock(Usernames.class); Usernames usernames = mock(Usernames.class);
ReservedUsernames reserved = mock(ReservedUsernames.class); ReservedUsernames reserved = mock(ReservedUsernames.class);
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis); when(commands.get(eq("UsernameByUuid::" + uuid.toString()))).thenReturn(null);
when(cacheClient.getWriteResource()).thenReturn(jedis);
when(jedis.get(eq("UsernameByUuid::" + uuid.toString()))).thenReturn(null);
when(usernames.get(eq(uuid))).thenReturn(Optional.of("n00bkiller")); when(usernames.get(eq(uuid))).thenReturn(Optional.of("n00bkiller"));
UsernamesManager usernamesManager = new UsernamesManager(usernames, reserved, cacheClient, cacheCluster, mock(Experiment.class)); UsernamesManager usernamesManager = new UsernamesManager(usernames, reserved, cacheClient, cacheCluster);
Optional<String> retrieved = usernamesManager.get(uuid); Optional<String> retrieved = usernamesManager.get(uuid);
assertTrue(retrieved.isPresent()); assertTrue(retrieved.isPresent());
assertEquals(retrieved.get(), "n00bkiller"); assertEquals(retrieved.get(), "n00bkiller");
verify(jedis, times(2)).get(eq("UsernameByUuid::" + uuid)); verify(commands, times(2)).get(eq("UsernameByUuid::" + uuid));
verify(jedis, times(1)).set(eq("UsernameByUuid::" + uuid), eq("n00bkiller")); verify(commands, times(1)).set(eq("UsernameByUuid::" + uuid), eq("n00bkiller"));
verify(jedis, times(1)).set(eq("UsernameByUsername::n00bkiller"), eq(uuid.toString())); verify(commands, times(1)).set(eq("UsernameByUsername::n00bkiller"), eq(uuid.toString()));
verify(jedis, times(2)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verify(usernames, times(1)).get(eq(uuid)); verify(usernames, times(1)).get(eq(uuid));
verifyNoMoreInteractions(usernames); verifyNoMoreInteractions(usernames);
@ -138,31 +140,30 @@ public class UsernamesManagerTest {
@Test @Test
public void testGetByUsernameBrokenCache() { public void testGetByUsernameBrokenCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
Usernames usernames = mock(Usernames.class); Usernames usernames = mock(Usernames.class);
ReservedUsernames reserved = mock(ReservedUsernames.class); ReservedUsernames reserved = mock(ReservedUsernames.class);
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis); when(commands.get(eq("UsernameByUsername::n00bkiller"))).thenThrow(new RedisException("Connection lost!"));
when(cacheClient.getWriteResource()).thenReturn(jedis);
when(jedis.get(eq("UsernameByUsername::n00bkiller"))).thenThrow(new JedisException("Connection lost!"));
when(usernames.get(eq("n00bkiller"))).thenReturn(Optional.of(uuid)); when(usernames.get(eq("n00bkiller"))).thenReturn(Optional.of(uuid));
UsernamesManager usernamesManager = new UsernamesManager(usernames, reserved, cacheClient, cacheCluster, mock(Experiment.class)); UsernamesManager usernamesManager = new UsernamesManager(usernames, reserved, cacheClient, cacheCluster);
Optional<UUID> retrieved = usernamesManager.get("n00bkiller"); Optional<UUID> retrieved = usernamesManager.get("n00bkiller");
assertTrue(retrieved.isPresent()); assertTrue(retrieved.isPresent());
assertEquals(retrieved.get(), uuid); assertEquals(retrieved.get(), uuid);
verify(jedis, times(1)).get(eq("UsernameByUsername::n00bkiller")); verify(commands, times(1)).get(eq("UsernameByUsername::n00bkiller"));
verify(jedis, times(1)).set(eq("UsernameByUsername::n00bkiller"), eq(uuid.toString())); verify(commands, times(1)).set(eq("UsernameByUsername::n00bkiller"), eq(uuid.toString()));
verify(jedis, times(1)).set(eq("UsernameByUuid::" + uuid.toString()), eq("n00bkiller")); verify(commands, times(1)).set(eq("UsernameByUuid::" + uuid.toString()), eq("n00bkiller"));
verify(jedis, times(1)).get(eq("UsernameByUuid::" + uuid.toString())); verify(commands, times(1)).get(eq("UsernameByUuid::" + uuid.toString()));
verify(jedis, times(2)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verify(usernames, times(1)).get(eq("n00bkiller")); verify(usernames, times(1)).get(eq("n00bkiller"));
verifyNoMoreInteractions(usernames); verifyNoMoreInteractions(usernames);
@ -170,28 +171,27 @@ public class UsernamesManagerTest {
@Test @Test
public void testGetAccountByUuidBrokenCache() { public void testGetAccountByUuidBrokenCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class); RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
Jedis jedis = mock(Jedis.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
FaultTolerantRedisCluster cacheCluster = mock(FaultTolerantRedisCluster.class);
Usernames usernames = mock(Usernames.class); Usernames usernames = mock(Usernames.class);
ReservedUsernames reserved = mock(ReservedUsernames.class); ReservedUsernames reserved = mock(ReservedUsernames.class);
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
when(cacheClient.getWriteResource()).thenReturn(mock(Jedis.class));
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis); when(commands.get(eq("UsernameByUuid::" + uuid))).thenThrow(new RedisException("Connection lost!"));
when(cacheClient.getWriteResource()).thenReturn(jedis);
when(jedis.get(eq("UsernameByUuid::" + uuid))).thenThrow(new JedisException("Connection lost!"));
when(usernames.get(eq(uuid))).thenReturn(Optional.of("n00bkiller")); when(usernames.get(eq(uuid))).thenReturn(Optional.of("n00bkiller"));
UsernamesManager usernamesManager = new UsernamesManager(usernames, reserved, cacheClient, cacheCluster, mock(Experiment.class)); UsernamesManager usernamesManager = new UsernamesManager(usernames, reserved, cacheClient, cacheCluster);
Optional<String> retrieved = usernamesManager.get(uuid); Optional<String> retrieved = usernamesManager.get(uuid);
assertTrue(retrieved.isPresent()); assertTrue(retrieved.isPresent());
assertEquals(retrieved.get(), "n00bkiller"); assertEquals(retrieved.get(), "n00bkiller");
verify(jedis, times(2)).get(eq("UsernameByUuid::" + uuid)); verify(commands, times(2)).get(eq("UsernameByUuid::" + uuid));
verify(jedis, times(2)).close(); verifyNoMoreInteractions(commands);
verifyNoMoreInteractions(jedis);
verify(usernames, times(1)).get(eq(uuid)); verify(usernames, times(1)).get(eq(uuid));
verifyNoMoreInteractions(usernames); verifyNoMoreInteractions(usernames);