public CompletableFuture<Void> clearUsernameHash(final Account account) {
    if (account.getUsernameHash().isEmpty()) {
      // no username to clear
      return CompletableFuture.completedFuture(null);
    }
    final byte[] usernameHash = account.getUsernameHash().get();
    final Timer.Sample sample = Timer.start();

    final Account updatedAccount = AccountUtil.cloneAccountAsNotStale(account);
    updatedAccount.setUsernameHash(null);
    updatedAccount.setUsernameLinkDetails(null, null);

    final Instant now = clock.instant();
    final Optional<byte[]> holdToRemove = addToHolds(updatedAccount, usernameHash, now);

    final List<TransactWriteItem> items = new ArrayList<>();

    // 0: remove the username from the account object, conditioned on account version
    items.add(UpdateAccountSpec.forAccount(accountsTableName, updatedAccount).transactItem());

    // 1: Un-confirm our username, adding a temporary hold for the old username to stop others from claiming it
    items.add(holdUsernameTransactItem(updatedAccount.getUuid(), usernameHash, now));

    // 2?: Adding that hold may have caused our account to exceed our maximum holds. Release an old hold
    holdToRemove.ifPresent(oldHold -> items.add(releaseHoldIfAllowedTransactItem(updatedAccount.getUuid(), oldHold, now)));

    return asyncClient.transactWriteItems(TransactWriteItemsRequest.builder().transactItems(items).build())
        .thenAccept(ignored -> {
          account.setUsernameHash(null);
          account.setUsernameLinkDetails(null, null);
          account.setVersion(account.getVersion() + 1);
          account.setUsernameHolds(updatedAccount.getUsernameHolds());
        })
        .exceptionally(ExceptionUtils.exceptionallyHandler(TransactionCanceledException.class, e -> {
          if (conditionalCheckFailed(e.cancellationReasons().get(0)) // Account version conflict
              // When we looked at the holds on our account, we thought we still held the corresponding username
              // reservation. But it turned out that someone else has taken the reservation since. This means that the
              // TTL on the hold must have just expired, so if we retry we should see that our hold is expired, and we
              // won't try to remove it again.
              || (e.cancellationReasons().size() > 2 && conditionalCheckFailed(e.cancellationReasons().get(2)))
              // concurrent update on any table
              || e.cancellationReasons().stream().anyMatch(Accounts::isTransactionConflict)) {
            throw new ContestedOptimisticLockException();
          } else {
            throw ExceptionUtils.wrap(e);
          }
        }))
        .whenComplete((ignored, throwable) -> sample.stop(CLEAR_USERNAME_HASH_TIMER));
  }
@Test
  void testClearUsernameNoUsername() {
    final Account account = generateAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID());
    createAccount(account);

    assertThatNoException().isThrownBy(() -> accounts.clearUsernameHash(account).join());
  }