Put message persisters behind feature flags.

This commit is contained in:
Jon Chambers 2020-08-27 13:15:08 -04:00 committed by Jon Chambers
parent cd4b85b0b5
commit 2b50367d7f
4 changed files with 63 additions and 43 deletions

View File

@ -332,8 +332,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(config.getTurnConfiguration()); TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(config.getTurnConfiguration());
RecaptchaClient recaptchaClient = new RecaptchaClient(config.getRecaptchaConfiguration().getSecret()); RecaptchaClient recaptchaClient = new RecaptchaClient(config.getRecaptchaConfiguration().getSecret());
MessagePersister messagePersister = new MessagePersister(messagesClient, messagesManager, pubSubManager, pushSender, accountsManager,config.getMessageCacheConfiguration().getPersistDelayMinutes(), TimeUnit.MINUTES); MessagePersister messagePersister = new MessagePersister(messagesClient, messagesManager, pubSubManager, pushSender, accountsManager, featureFlagsManager, config.getMessageCacheConfiguration().getPersistDelayMinutes(), TimeUnit.MINUTES);
RedisClusterMessagePersister clusterMessagePersister = new RedisClusterMessagePersister(clusterMessagesCache, messages, pubSubManager, pushSender, accountsManager, Duration.ofMinutes(config.getMessageCacheConfiguration().getPersistDelayMinutes())); RedisClusterMessagePersister clusterMessagePersister = new RedisClusterMessagePersister(clusterMessagesCache, messages, pubSubManager, pushSender, accountsManager, featureFlagsManager, Duration.ofMinutes(config.getMessageCacheConfiguration().getPersistDelayMinutes()));
DirectoryReconciliationClient directoryReconciliationClient = new DirectoryReconciliationClient(config.getDirectoryConfiguration().getDirectoryServerConfiguration()); DirectoryReconciliationClient directoryReconciliationClient = new DirectoryReconciliationClient(config.getDirectoryConfiguration().getDirectoryServerConfiguration());

View File

@ -49,30 +49,35 @@ public class MessagePersister implements Managed, Runnable {
private final long delayTime; private final long delayTime;
private final TimeUnit delayTimeUnit; private final TimeUnit delayTimeUnit;
private final MessagesManager messagesManager; private final MessagesManager messagesManager;
private final PubSubManager pubSubManager; private final PubSubManager pubSubManager;
private final PushSender pushSender; private final PushSender pushSender;
private final AccountsManager accountsManager; private final AccountsManager accountsManager;
private final FeatureFlagsManager featureFlagsManager;
private final LuaScript getQueuesScript; private final LuaScript getQueuesScript;
private boolean finished = false; private boolean finished = false;
private static final String DISABLE_PERSISTENCE_FLAG = "disable-singleton-persister";
public MessagePersister(final ReplicatedJedisPool jedisPool, public MessagePersister(final ReplicatedJedisPool jedisPool,
final MessagesManager messagesManager, final MessagesManager messagesManager,
final PubSubManager pubSubManager, final PubSubManager pubSubManager,
final PushSender pushSender, final PushSender pushSender,
final AccountsManager accountsManager, final AccountsManager accountsManager,
final long delayTime, final FeatureFlagsManager featureFlagsManager,
final TimeUnit delayTimeUnit) final long delayTime,
final TimeUnit delayTimeUnit)
throws IOException throws IOException
{ {
this.jedisPool = jedisPool; this.jedisPool = jedisPool;
this.messagesManager = messagesManager; this.messagesManager = messagesManager;
this.pubSubManager = pubSubManager; this.pubSubManager = pubSubManager;
this.pushSender = pushSender; this.pushSender = pushSender;
this.accountsManager = accountsManager; this.accountsManager = accountsManager;
this.featureFlagsManager = featureFlagsManager;
this.delayTime = delayTime; this.delayTime = delayTime;
this.delayTimeUnit = delayTimeUnit; this.delayTimeUnit = delayTimeUnit;
@ -87,23 +92,30 @@ public class MessagePersister implements Managed, Runnable {
@Override @Override
public void run() { public void run() {
while (running.get()) { while (running.get()) {
try { if (!featureFlagsManager.isFeatureFlagActive(DISABLE_PERSISTENCE_FLAG)) {
List<byte[]> queuesToPersist = getQueuesToPersist(); try {
queueCountHistogram.update(queuesToPersist.size()); List<byte[]> queuesToPersist = getQueuesToPersist();
queueCountHistogram.update(queuesToPersist.size());
for (byte[] queue : queuesToPersist) { for (byte[] queue : queuesToPersist) {
Key key = Key.fromUserMessageQueue(queue); Key key = Key.fromUserMessageQueue(queue);
persistQueue(jedisPool, key); persistQueue(jedisPool, key);
notifyClients(accountsManager, pubSubManager, pushSender, key); notifyClients(accountsManager, pubSubManager, pushSender, key);
}
if (queuesToPersist.isEmpty()) {
//noinspection BusyWait
Thread.sleep(10_000);
}
} catch (Throwable t) {
logger.error("Exception while persisting: ", t);
} }
} else {
if (queuesToPersist.isEmpty()) { try {
//noinspection BusyWait
Thread.sleep(10_000); Thread.sleep(10_000);
} catch (final InterruptedException ignored) {
} }
} catch (Throwable t) {
logger.error("Exception while persisting: ", t);
} }
} }

View File

@ -30,6 +30,7 @@ public class RedisClusterMessagePersister implements Managed {
private final PubSubManager pubSubManager; private final PubSubManager pubSubManager;
private final PushSender pushSender; private final PushSender pushSender;
private final AccountsManager accountsManager; private final AccountsManager accountsManager;
private final FeatureFlagsManager featureFlagsManager;
private final Duration persistDelay; private final Duration persistDelay;
@ -46,16 +47,18 @@ public class RedisClusterMessagePersister implements Managed {
static final int QUEUE_BATCH_LIMIT = 100; static final int QUEUE_BATCH_LIMIT = 100;
static final int MESSAGE_BATCH_LIMIT = 100; static final int MESSAGE_BATCH_LIMIT = 100;
static final String ENABLE_PERSISTENCE_FLAG = "enable-cluster-persister";
private static final Logger logger = LoggerFactory.getLogger(RedisClusterMessagePersister.class); private static final Logger logger = LoggerFactory.getLogger(RedisClusterMessagePersister.class);
public RedisClusterMessagePersister(final RedisClusterMessagesCache messagesCache, final Messages messagesDatabase, final PubSubManager pubSubManager, final PushSender pushSender, final AccountsManager accountsManager, final Duration persistDelay) { public RedisClusterMessagePersister(final RedisClusterMessagesCache messagesCache, final Messages messagesDatabase, final PubSubManager pubSubManager, final PushSender pushSender, final AccountsManager accountsManager, final FeatureFlagsManager featureFlagsManager, final Duration persistDelay) {
this.messagesCache = messagesCache; this.messagesCache = messagesCache;
this.messagesDatabase = messagesDatabase; this.messagesDatabase = messagesDatabase;
this.pubSubManager = pubSubManager; this.pubSubManager = pubSubManager;
this.pushSender = pushSender; this.pushSender = pushSender;
this.accountsManager = accountsManager; this.accountsManager = accountsManager;
this.featureFlagsManager = featureFlagsManager;
this.persistDelay = persistDelay; this.persistDelay = persistDelay;
} }
@VisibleForTesting @VisibleForTesting
@ -69,7 +72,10 @@ public class RedisClusterMessagePersister implements Managed {
workerThread = new Thread(() -> { workerThread = new Thread(() -> {
while (running) { while (running) {
persistNextQueues(Instant.now()); if (featureFlagsManager.isFeatureFlagActive(ENABLE_PERSISTENCE_FLAG)) {
persistNextQueues(Instant.now());
}
Util.sleep(100); Util.sleep(100);
} }
}); });
@ -130,7 +136,7 @@ public class RedisClusterMessagePersister implements Managed {
messagesCache.lockQueueForPersistence(queue); messagesCache.lockQueueForPersistence(queue);
try { try {
/* int messageCount = 0; int messageCount = 0;
List<MessageProtos.Envelope> messages; List<MessageProtos.Envelope> messages;
do { do {
@ -146,7 +152,7 @@ public class RedisClusterMessagePersister implements Managed {
} }
} while (!messages.isEmpty()); } while (!messages.isEmpty());
queueSizeHistogram.update(messageCount); */ queueSizeHistogram.update(messageCount);
} finally { } finally {
messagesCache.unlockQueueForPersistence(queue); messagesCache.unlockQueueForPersistence(queue);
} }

View File

@ -4,7 +4,6 @@ import com.google.protobuf.ByteString;
import io.lettuce.core.cluster.SlotHash; import io.lettuce.core.cluster.SlotHash;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.whispersystems.textsecuregcm.entities.MessageProtos; import org.whispersystems.textsecuregcm.entities.MessageProtos;
import org.whispersystems.textsecuregcm.push.PushSender; import org.whispersystems.textsecuregcm.push.PushSender;
@ -29,7 +28,6 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@Ignore
public class RedisClusterMessagePersisterTest extends AbstractRedisClusterTest { public class RedisClusterMessagePersisterTest extends AbstractRedisClusterTest {
private ExecutorService notificationExecutorService; private ExecutorService notificationExecutorService;
@ -50,6 +48,9 @@ public class RedisClusterMessagePersisterTest extends AbstractRedisClusterTest {
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
final FeatureFlagsManager featureFlagsManager = mock(FeatureFlagsManager.class);
when(featureFlagsManager.isFeatureFlagActive(RedisClusterMessagePersister.ENABLE_PERSISTENCE_FLAG)).thenReturn(true);
messagesDatabase = mock(Messages.class); messagesDatabase = mock(Messages.class);
accountsManager = mock(AccountsManager.class); accountsManager = mock(AccountsManager.class);
pubSubManager = mock(PubSubManager.class); pubSubManager = mock(PubSubManager.class);
@ -61,7 +62,7 @@ public class RedisClusterMessagePersisterTest extends AbstractRedisClusterTest {
notificationExecutorService = Executors.newSingleThreadExecutor(); notificationExecutorService = Executors.newSingleThreadExecutor();
messagesCache = new RedisClusterMessagesCache(getRedisCluster(), notificationExecutorService); messagesCache = new RedisClusterMessagesCache(getRedisCluster(), notificationExecutorService);
messagePersister = new RedisClusterMessagePersister(messagesCache, messagesDatabase, pubSubManager, mock(PushSender.class), accountsManager, PERSIST_DELAY); messagePersister = new RedisClusterMessagePersister(messagesCache, messagesDatabase, pubSubManager, mock(PushSender.class), accountsManager, featureFlagsManager, PERSIST_DELAY);
} }
@Override @Override
@ -136,6 +137,7 @@ public class RedisClusterMessagePersisterTest extends AbstractRedisClusterTest {
verify(messagesDatabase, times(queueCount * messagesPerQueue)).store(any(UUID.class), any(MessageProtos.Envelope.class), anyString(), anyLong()); verify(messagesDatabase, times(queueCount * messagesPerQueue)).store(any(UUID.class), any(MessageProtos.Envelope.class), anyString(), anyLong());
} }
@SuppressWarnings("SameParameterValue")
private static String generateRandomQueueNameForSlot(final int slot) { private static String generateRandomQueueNameForSlot(final int slot) {
final UUID uuid = UUID.randomUUID(); final UUID uuid = UUID.randomUUID();