Merge "AccountCacheImpl: Stop caching negative results"
diff --git a/java/com/google/gerrit/server/account/AccountCacheImpl.java b/java/com/google/gerrit/server/account/AccountCacheImpl.java
index af8d8b0..ad9d009 100644
--- a/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ b/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -59,7 +59,7 @@
     return new CacheModule() {
       @Override
       protected void configure() {
-        cache(BYID_NAME, Account.Id.class, new TypeLiteral<Optional<AccountState>>() {})
+        cache(BYID_NAME, Account.Id.class, new TypeLiteral<AccountState>() {})
             .loader(ByIdLoader.class);
 
         bind(AccountCacheImpl.class);
@@ -69,13 +69,13 @@
   }
 
   private final ExternalIds externalIds;
-  private final LoadingCache<Account.Id, Optional<AccountState>> byId;
+  private final LoadingCache<Account.Id, AccountState> byId;
   private final ExecutorService executor;
 
   @Inject
   AccountCacheImpl(
       ExternalIds externalIds,
-      @Named(BYID_NAME) LoadingCache<Account.Id, Optional<AccountState>> byId,
+      @Named(BYID_NAME) LoadingCache<Account.Id, AccountState> byId,
       @FanOutExecutor ExecutorService executor) {
     this.externalIds = externalIds;
     this.byId = byId;
@@ -85,9 +85,11 @@
   @Override
   public AccountState getEvenIfMissing(Account.Id accountId) {
     try {
-      return byId.get(accountId).orElse(missing(accountId));
+      return byId.get(accountId);
     } catch (ExecutionException e) {
-      logger.atWarning().withCause(e).log("Cannot load AccountState for %s", accountId);
+      if (!(e.getCause() instanceof AccountNotFoundException)) {
+        logger.atWarning().withCause(e).log("Cannot load AccountState for %s", accountId);
+      }
       return missing(accountId);
     }
   }
@@ -95,9 +97,11 @@
   @Override
   public Optional<AccountState> get(Account.Id accountId) {
     try {
-      return byId.get(accountId);
+      return Optional.ofNullable(byId.get(accountId));
     } catch (ExecutionException e) {
-      logger.atWarning().withCause(e).log("Cannot load AccountState for ID %s", accountId);
+      if (!(e.getCause() instanceof AccountNotFoundException)) {
+        logger.atWarning().withCause(e).log("Cannot load AccountState for %s", accountId);
+      }
       return Optional.empty();
     }
   }
@@ -107,10 +111,10 @@
     Map<Account.Id, AccountState> accountStates = new HashMap<>(accountIds.size());
     List<Callable<Optional<AccountState>>> callables = new ArrayList<>();
     for (Account.Id accountId : accountIds) {
-      Optional<AccountState> state = byId.getIfPresent(accountId);
+      AccountState state = byId.getIfPresent(accountId);
       if (state != null) {
         // The value is in-memory, so we just get the state
-        state.ifPresent(s -> accountStates.put(accountId, s));
+        accountStates.put(accountId, state);
       } else {
         // Queue up a callable so that we can load accounts in parallel
         callables.add(() -> get(accountId));
@@ -170,7 +174,7 @@
     return AccountState.forAccount(account.build());
   }
 
-  static class ByIdLoader extends CacheLoader<Account.Id, Optional<AccountState>> {
+  static class ByIdLoader extends CacheLoader<Account.Id, AccountState> {
     private final Accounts accounts;
 
     @Inject
@@ -179,12 +183,21 @@
     }
 
     @Override
-    public Optional<AccountState> load(Account.Id who) throws Exception {
+    public AccountState load(Account.Id who) throws Exception {
       try (TraceTimer timer =
           TraceContext.newTimer(
               "Loading account", Metadata.builder().accountId(who.get()).build())) {
-        return accounts.get(who);
+        return accounts
+            .get(who)
+            .orElseThrow(() -> new AccountNotFoundException(who + " not found"));
       }
     }
   }
+
+  /** Signals that the account was not found in the primary storage. */
+  private static class AccountNotFoundException extends Exception {
+    public AccountNotFoundException(String message) {
+      super(message);
+    }
+  }
 }
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index c1e83d4..48c9995 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -225,7 +225,7 @@
 
   @Inject
   @Named("accounts")
-  private LoadingCache<Account.Id, Optional<AccountState>> accountsCache;
+  private LoadingCache<Account.Id, AccountState> accountsCache;
 
   @Inject private AccountOperations accountOperations;