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 eaefac30b..966f1401d 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java @@ -571,6 +571,12 @@ public CompletableFuture reserveUsernameHash(final Account () -> accounts.getByAccountIdentifierAsync(account.getUuid()).thenApply(Optional::orElseThrow), AccountChangeValidator.USERNAME_CHANGE_VALIDATOR, MAX_UPDATE_ATTEMPTS)) + .whenComplete((updatedAccount, throwable) -> { + if (throwable == null) { + // Make a best effort to clear any stale data that may have been cached while this operation was in progress + redisDeleteAsync(updatedAccount); + } + }) .thenApply(updatedAccount -> new UsernameReservation(updatedAccount, reservedUsernameHash.get())); } @@ -623,7 +629,13 @@ public CompletableFuture confirmReservedUsernameHash(final Account acco () -> accounts.getByAccountIdentifierAsync(account.getUuid()).thenApply(Optional::orElseThrow), AccountChangeValidator.USERNAME_CHANGE_VALIDATOR, MAX_UPDATE_ATTEMPTS - )); + )) + .whenComplete((updatedAccount, throwable) -> { + if (throwable == null) { + // Make a best effort to clear any stale data that may have been cached while this operation was in progress + redisDeleteAsync(updatedAccount); + } + }); } public CompletableFuture clearUsernameHash(final Account account) { @@ -634,7 +646,13 @@ public CompletableFuture clearUsernameHash(final Account account) { accounts::clearUsernameHash, () -> accounts.getByAccountIdentifierAsync(account.getUuid()).thenApply(Optional::orElseThrow), AccountChangeValidator.USERNAME_CHANGE_VALIDATOR, - MAX_UPDATE_ATTEMPTS)); + MAX_UPDATE_ATTEMPTS)) + .whenComplete((updatedAccount, throwable) -> { + if (throwable == null) { + // Make a best effort to clear any stale data that may have been cached while this operation was in progress + redisDeleteAsync(updatedAccount); + } + }); } public Account update(Account account, Consumer updater) { diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java index 32704f9f3..dcc9c910c 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java @@ -1346,7 +1346,7 @@ void testPniPqUpdate_incompleteKeys() { } @Test - void testReserveUsernameHash() throws UsernameHashNotAvailableException { + void testReserveUsernameHash() { final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH]); when(accounts.getByAccountIdentifierAsync(account.getUuid())).thenReturn(CompletableFuture.completedFuture(Optional.of(account))); @@ -1359,7 +1359,7 @@ void testReserveUsernameHash() throws UsernameHashNotAvailableException { } @Test - void testReserveOwnUsernameHash() throws UsernameHashNotAvailableException { + void testReserveOwnUsernameHash() { final byte[] oldUsernameHash = TestRandomUtil.nextBytes(32); final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH]); account.setUsernameHash(oldUsernameHash); @@ -1373,7 +1373,7 @@ void testReserveOwnUsernameHash() throws UsernameHashNotAvailableException { } @Test - void testReserveUsernameOptimisticLockingFailure() throws UsernameHashNotAvailableException { + void testReserveUsernameOptimisticLockingFailure() { final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH]); when(accounts.getByAccountIdentifierAsync(account.getUuid())).thenReturn(CompletableFuture.completedFuture(Optional.of(account)));