From 3e61b5c49d3f1e6fd786a9fc238b1a3cd351380a Mon Sep 17 00:00:00 2001 From: Chris Eager Date: Wed, 26 May 2021 16:41:32 -0500 Subject: [PATCH] Add call chain and mismatch check for push token timestamp --- .../storage/AccountsManager.java | 25 ++++++++++++++++++- .../tests/storage/AccountsManagerTest.java | 15 ++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java index 7966f9827..2806b667b 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java @@ -21,12 +21,16 @@ import io.micrometer.core.instrument.Metrics; import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; +import java.util.stream.Collectors; +import net.logstash.logback.argument.StructuredArguments; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier; @@ -464,6 +468,10 @@ public class AccountsManager { if (!Objects.equals(databaseAccount.getMasterDevice().get().getSignedPreKey(), dynamoAccount.getMasterDevice().get().getSignedPreKey())) { return Optional.of("masterDeviceSignedPreKey"); } + + if (!Objects.equals(databaseAccount.getMasterDevice().get().getPushTimestamp(), dynamoAccount.getMasterDevice().get().getPushTimestamp())) { + return Optional.of("masterDevicePushTimestamp"); + } } if (!serializedEquals(databaseAccount.getDevices(), dynamoAccount.getDevices())) { @@ -520,11 +528,26 @@ public class AccountsManager { if (maybeUUid.isPresent() && dynamicConfigurationManager.getConfiguration().getAccountsDynamoDbMigrationConfiguration().isLogMismatches()) { - logger.info("Mismatched {} for {}", mismatchDescription, maybeUUid.get()); + + final String abbreviatedCallChain = getAbbreviatedCallChain(new RuntimeException().getStackTrace()); + + logger.info("Mismatched account data: {}", StructuredArguments.entries(Map.of( + "type", mismatchDescription, + "uuid", maybeUUid.get(), + "callChain", abbreviatedCallChain + ))); } }); } + private String getAbbreviatedCallChain(final StackTraceElement[] stackTrace) { + return Arrays.stream(stackTrace) + .filter(stackTraceElement -> stackTraceElement.getClassName().contains("org.whispersystems")) + .filter(stackTraceElement -> !(stackTraceElement.getClassName().endsWith("AccountsManager") && stackTraceElement.getMethodName().contains("compare"))) + .map(stackTraceElement -> StringUtils.substringAfterLast(stackTraceElement.getClassName(), ".") + ":" + stackTraceElement.getMethodName()) + .collect(Collectors.joining(" -> ")); + } + private static abstract class AccountComparisonMixin extends Account { @JsonIgnore diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsManagerTest.java index e795eac9c..e2aa4c3a2 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsManagerTest.java @@ -385,7 +385,7 @@ class AccountsManagerTest { } @Test - void testCompareAccounts() { + void testCompareAccounts() throws Exception { RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); Accounts accounts = mock(Accounts.class); @@ -434,11 +434,24 @@ class AccountsManagerTest { assertEquals(Optional.of("devices"), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2))); + device1.setName(null); + device1.setSignedPreKey(new SignedPreKey(1L, "123", "456")); device2.setSignedPreKey(new SignedPreKey(2L, "123", "456")); assertEquals(Optional.of("masterDeviceSignedPreKey"), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2))); + device1.setSignedPreKey(null); + device2.setSignedPreKey(null); + + assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2))); + + device1.setApnId("123"); + Thread.sleep(5); + device2.setApnId("123"); + + assertEquals(Optional.of("masterDevicePushTimestamp"), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2))); + a1.removeDevice(1L); a2.removeDevice(1L);