mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-22 11:38:06 +01:00
Build Dynamo DB backed Message Store (#358)
* Work in progress... * Finish first pass draft of MessagesDynamoDb * Use begins_with everywhere for destination device id * Remove now unused methods * First basic test built * Add another test case * Remove comment * Verify more of the message contents * Ensure all methods are tested * Integrate MessagesDynamoDb into the MessagesManager This change plugs the MessagesDynamoDb class into the live serving flow in MessagesManager. Tests are not yet as comprehensive for this big a change as they should be, but they now compile and pass so checkpointing here with a commit. * Put DynamoDB before RDBS when deleting specific messages * Extract method * Make aws sdk version into a property * Rename clientBuilder * Discard messages with no GUID * Unify batching logic into one function * Comment on the source of the value in this constant * Inline method * Variable name swizzle * Add timers to all public methods * Add missing return statements * Reject messages that are too large with response code 413 * Add configuration to control dynamo DB timeouts * Set server timestamp from the ReceiptSender * Change to shorter key names to optimize IOPS * Fix tests broken by changing column names * Fix broken copyright template output * Remove copyright template error text * Add experiments to control use of dynamo and rds in message storage * Specify instance profile credentials for the dynamic configuration manager * Use property for aws sdk version * Switch dynamo to instance profile credentials * Add metrics to the batch write loop * Use placeholders in logging
This commit is contained in:
@@ -20,6 +20,7 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.entities.MessageProtos;
|
||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||
import org.whispersystems.textsecuregcm.metrics.PushLatencyManager;
|
||||
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
||||
import org.whispersystems.textsecuregcm.redis.AbstractRedisClusterTest;
|
||||
@@ -28,11 +29,14 @@ import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.storage.FaultTolerantDatabase;
|
||||
import org.whispersystems.textsecuregcm.storage.Messages;
|
||||
import org.whispersystems.textsecuregcm.storage.MessagesCache;
|
||||
import org.whispersystems.textsecuregcm.storage.MessagesDynamoDb;
|
||||
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
||||
import org.whispersystems.textsecuregcm.tests.util.MessagesDynamoDbRule;
|
||||
import org.whispersystems.websocket.WebSocketClient;
|
||||
import org.whispersystems.websocket.messages.WebSocketResponseMessage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -47,6 +51,7 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atMost;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -60,13 +65,18 @@ public class WebSocketConnectionIntegrationTest extends AbstractRedisClusterTest
|
||||
@Rule
|
||||
public PreparedDbRule db = EmbeddedPostgresRules.preparedDatabase(LiquibasePreparer.forClasspathLocation("messagedb.xml"));
|
||||
|
||||
@Rule
|
||||
public MessagesDynamoDbRule messagesDynamoDbRule = new MessagesDynamoDbRule();
|
||||
|
||||
private ExecutorService executorService;
|
||||
private Messages messages;
|
||||
private MessagesDynamoDb messagesDynamoDb;
|
||||
private MessagesCache messagesCache;
|
||||
private Account account;
|
||||
private Device device;
|
||||
private WebSocketClient webSocketClient;
|
||||
private WebSocketConnection webSocketConnection;
|
||||
private ExperimentEnrollmentManager experimentEnrollmentManager;
|
||||
|
||||
private long serialTimestamp = System.currentTimeMillis();
|
||||
|
||||
@@ -82,17 +92,20 @@ public class WebSocketConnectionIntegrationTest extends AbstractRedisClusterTest
|
||||
executorService = Executors.newSingleThreadExecutor();
|
||||
messages = new Messages(new FaultTolerantDatabase("messages-test", Jdbi.create(db.getTestDatabase()), new CircuitBreakerConfiguration()));
|
||||
messagesCache = new MessagesCache(getRedisCluster(), getRedisCluster(), executorService);
|
||||
messagesDynamoDb = new MessagesDynamoDb(messagesDynamoDbRule.getDynamoDB(), MessagesDynamoDbRule.TABLE_NAME, Duration.ofDays(7));
|
||||
account = mock(Account.class);
|
||||
device = mock(Device.class);
|
||||
webSocketClient = mock(WebSocketClient.class);
|
||||
experimentEnrollmentManager = mock(ExperimentEnrollmentManager.class);
|
||||
|
||||
when(account.getNumber()).thenReturn("+18005551234");
|
||||
when(account.getUuid()).thenReturn(UUID.randomUUID());
|
||||
when(device.getId()).thenReturn(1L);
|
||||
when(experimentEnrollmentManager.isEnrolled(any(UUID.class), anyString())).thenReturn(Boolean.FALSE);
|
||||
|
||||
webSocketConnection = new WebSocketConnection(
|
||||
mock(ReceiptSender.class),
|
||||
new MessagesManager(messages, messagesCache, mock(PushLatencyManager.class)),
|
||||
new MessagesManager(messages, messagesDynamoDb, messagesCache, mock(PushLatencyManager.class), experimentEnrollmentManager),
|
||||
account,
|
||||
device,
|
||||
webSocketClient);
|
||||
|
||||
@@ -195,7 +195,7 @@ public class WebSocketConnectionTest {
|
||||
futures.get(0).completeExceptionally(new IOException());
|
||||
futures.get(2).completeExceptionally(new IOException());
|
||||
|
||||
verify(storedMessages, times(1)).delete(eq(account.getNumber()), eq(accountUuid), eq(2L), eq(2L), eq(false));
|
||||
verify(storedMessages, times(1)).delete(eq(account.getNumber()), eq(accountUuid), eq(2L), eq(outgoingMessages.get(1).getGuid()));
|
||||
verify(receiptSender, times(1)).sendReceipt(eq(account), eq("sender1"), eq(2222L));
|
||||
|
||||
connection.stop();
|
||||
@@ -712,7 +712,7 @@ public class WebSocketConnectionTest {
|
||||
|
||||
// We should delete all three messages even though we only sent two; one got discarded because it was too big for
|
||||
// desktop clients.
|
||||
verify(storedMessages, times(3)).delete(eq(account.getNumber()), eq(accountUuid), eq(2L), anyLong(), anyBoolean());
|
||||
verify(storedMessages, times(3)).delete(eq(account.getNumber()), eq(accountUuid), eq(2L), any(UUID.class));
|
||||
|
||||
connection.stop();
|
||||
verify(client).close(anyInt(), anyString());
|
||||
@@ -785,7 +785,7 @@ public class WebSocketConnectionTest {
|
||||
futures.get(1).complete(response);
|
||||
futures.get(2).complete(response);
|
||||
|
||||
verify(storedMessages, times(3)).delete(eq(account.getNumber()), eq(accountUuid), eq(2L), anyLong(), anyBoolean());
|
||||
verify(storedMessages, times(3)).delete(eq(account.getNumber()), eq(accountUuid), eq(2L), any(UUID.class));
|
||||
|
||||
connection.stop();
|
||||
verify(client).close(anyInt(), anyString());
|
||||
|
||||
Reference in New Issue
Block a user