Merge "Keep track of deleted PS to avoid using stale meta data in NoteDB"
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 8291d5e..8efff67 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1653,25 +1653,9 @@
   ]
 ----
 
-As result the watched projects of the user are returned as a list of
-link:#project-watch-info[ProjectWatchInfo] entities.
-The result is sorted by project name in ascending order.
-
 .Response
 ----
-  HTTP/1.1 200 OK
-  Content-Disposition: attachment
-  Content-Type: application/json; charset=UTF-8
-
-  )]}'
-  [
-    {
-      "project": "Test Project 2",
-      "notify_new_changes": true,
-      "notify_new_patch_sets": true,
-      "notify_all_comments": true,
-    }
-  ]
+  HTTP/1.1 204 No Content
 ----
 
 [[default-star-endpoints]]
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 7991afe2..6198fbc 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -88,6 +88,7 @@
 import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -121,6 +122,8 @@
     db.accountExternalIds().delete(getExternalIds(admin));
     db.accountExternalIds().delete(getExternalIds(user));
     db.accountExternalIds().insert(savedExternalIds);
+    accountCache.evict(admin.getId());
+    accountCache.evict(user.getId());
   }
 
   @After
@@ -135,9 +138,9 @@
     }
   }
 
-  private List<AccountExternalId> getExternalIds(TestAccount account)
+  private Collection<AccountExternalId> getExternalIds(TestAccount account)
       throws Exception {
-    return db.accountExternalIds().byAccount(account.getId()).toList();
+    return accountCache.get(account.getId()).getExternalIds();
   }
 
   @After
@@ -513,6 +516,7 @@
         user.getId(), new AccountExternalId.Key("foo:myId"));
 
     db.accountExternalIds().insert(Collections.singleton(extId));
+    accountCache.evict(user.getId());
 
     TestKey key = validKeyWithSecondUserId();
     addGpgKey(key.getPublicKeyArmored());
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
index 32ce7bb..db6cb7a 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
@@ -28,8 +28,11 @@
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.index.account.AccountIndexCollection;
+import com.google.gerrit.server.query.account.InternalAccountQuery;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -47,6 +50,7 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -63,6 +67,8 @@
   @Singleton
   public static class Factory {
     private final Provider<ReviewDb> db;
+    private final AccountIndexCollection accountIndexes;
+    private final Provider<InternalAccountQuery> accountQueryProvider;
     private final String webUrl;
     private final IdentifiedUser.GenericFactory userFactory;
     private final int maxTrustDepth;
@@ -71,9 +77,13 @@
     @Inject
     Factory(@GerritServerConfig Config cfg,
         Provider<ReviewDb> db,
+        AccountIndexCollection accountIndexes,
+        Provider<InternalAccountQuery> accountQueryProvider,
         IdentifiedUser.GenericFactory userFactory,
         @CanonicalWebUrl String webUrl) {
       this.db = db;
+      this.accountIndexes = accountIndexes;
+      this.accountQueryProvider = accountQueryProvider;
       this.webUrl = webUrl;
       this.userFactory = userFactory;
       this.maxTrustDepth = cfg.getInt("receive", null, "maxTrustDepth", 0);
@@ -107,6 +117,8 @@
   }
 
   private final Provider<ReviewDb> db;
+  private final AccountIndexCollection accountIndexes;
+  private final Provider<InternalAccountQuery> accountQueryProvider;
   private final String webUrl;
   private final IdentifiedUser.GenericFactory userFactory;
 
@@ -114,6 +126,8 @@
 
   private GerritPublicKeyChecker(Factory factory) {
     this.db = factory.db;
+    this.accountIndexes = factory.accountIndexes;
+    this.accountQueryProvider = factory.accountQueryProvider;
     this.webUrl = factory.webUrl;
     this.userFactory = factory.userFactory;
     if (factory.trusted != null) {
@@ -163,12 +177,26 @@
 
   private CheckResult checkIdsForArbitraryUser(PGPPublicKey key)
       throws PGPException, OrmException {
-    AccountExternalId extId = db.get().accountExternalIds().get(
-        toExtIdKey(key));
-    if (extId == null) {
-      return CheckResult.bad("Key is not associated with any users");
+    IdentifiedUser user;
+    if (accountIndexes.getSearchIndex() != null) {
+      List<AccountState> accountStates =
+          accountQueryProvider.get().byExternalId(toExtIdKey(key).get());
+      if (accountStates.isEmpty()) {
+        return CheckResult.bad("Key is not associated with any users");
+      }
+      if (accountStates.size() > 1) {
+        return CheckResult.bad("Key is associated with multiple users");
+      }
+      user = userFactory.create(accountStates.get(0));
+    } else {
+      AccountExternalId extId = db.get().accountExternalIds().get(
+          toExtIdKey(key));
+      if (extId == null) {
+        return CheckResult.bad("Key is not associated with any users");
+      }
+      user = userFactory.create(extId.getAccountId());
     }
-    IdentifiedUser user = userFactory.create(extId.getAccountId());
+
     Set<String> allowedUserIds = getAllowedUserIds(user);
     if (allowedUserIds.isEmpty()) {
       return CheckResult.bad("No identities found for user");
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
index a16351e..cac0e72 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.account.AccountCache;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -45,14 +46,17 @@
   private final Provider<PersonIdent> serverIdent;
   private final Provider<ReviewDb> db;
   private final Provider<PublicKeyStore> storeProvider;
+  private final AccountCache accountCache;
 
   @Inject
   DeleteGpgKey(@GerritPersonIdent Provider<PersonIdent> serverIdent,
       Provider<ReviewDb> db,
-      Provider<PublicKeyStore> storeProvider) {
+      Provider<PublicKeyStore> storeProvider,
+      AccountCache accountCache) {
     this.serverIdent = serverIdent;
     this.db = db;
     this.storeProvider = storeProvider;
+    this.accountCache = accountCache;
   }
 
   @Override
@@ -64,6 +68,7 @@
         AccountExternalId.SCHEME_GPGKEY,
         BaseEncoding.base16().encode(key.getFingerprint()));
     db.get().accountExternalIds().deleteKeys(Collections.singleton(extIdKey));
+    accountCache.evict(rsrc.getUser().getAccountId());
 
     try (PublicKeyStore store = storeProvider.get()) {
       store.remove(rsrc.getKeyRing().getPublicKey().getFingerprint());
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
index 7e55d45..c8694d4 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
@@ -44,6 +44,7 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.mail.AddKeySender;
 import com.google.gwtorm.server.OrmException;
@@ -85,6 +86,7 @@
   private final Provider<PublicKeyStore> storeProvider;
   private final GerritPublicKeyChecker.Factory checkerFactory;
   private final AddKeySender.Factory addKeyFactory;
+  private final AccountCache accountCache;
 
   @Inject
   PostGpgKeys(@GerritPersonIdent Provider<PersonIdent> serverIdent,
@@ -92,13 +94,15 @@
       Provider<CurrentUser> self,
       Provider<PublicKeyStore> storeProvider,
       GerritPublicKeyChecker.Factory checkerFactory,
-      AddKeySender.Factory addKeyFactory) {
+      AddKeySender.Factory addKeyFactory,
+      AccountCache accountCache) {
     this.serverIdent = serverIdent;
     this.db = db;
     this.self = self;
     this.storeProvider = storeProvider;
     this.checkerFactory = checkerFactory;
     this.addKeyFactory = addKeyFactory;
+    this.accountCache = accountCache;
   }
 
   @Override
@@ -141,6 +145,7 @@
               return toExtIdKey(fp.get());
             }
           }));
+      accountCache.evict(rsrc.getUser().getAccountId());
       return toJson(newKeys, toRemove, store, rsrc.getUser());
     }
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountPredicates.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountPredicates.java
index 7a9f5bd..085035c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountPredicates.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountPredicates.java
@@ -43,6 +43,10 @@
         AccountQueryBuilder.FIELD_NAME, name.toLowerCase());
   }
 
+  static Predicate<AccountState> externalId(String externalId) {
+    return new AccountPredicate(AccountField.EXTERNAL_ID, externalId);
+  }
+
   public static Predicate<AccountState> isActive() {
     return new AccountPredicate(AccountField.ACTIVE, "1");
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
index aab76ab..d620fb8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
@@ -124,6 +124,7 @@
 
   @Override
   public Predicate<AccountState> defaultField(String query) {
+    // Adapt the capacity of this list when adding more default predicates.
     List<Predicate<AccountState>> preds = Lists.newArrayListWithCapacity(4);
     if ("self".equalsIgnoreCase(query)) {
       try {
@@ -138,6 +139,8 @@
     }
     preds.add(name(query));
     preds.add(username(query));
+    // Adapt the capacity of the "predicates" list when adding more default
+    // predicates.
     return Predicate.or(preds);
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/InternalAccountQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
index 8225c4c..43a23b0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
@@ -18,8 +18,10 @@
 import com.google.gerrit.server.index.IndexConfig;
 import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.query.InternalQuery;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
+import java.util.List;
 import java.util.Set;
 
 public class InternalAccountQuery extends InternalQuery<AccountState> {
@@ -53,4 +55,9 @@
     super.noFields();
     return this;
   }
+
+  public List<AccountState> byExternalId(String externalId)
+      throws OrmException {
+    return query(AccountPredicates.externalId(externalId));
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index a150c93..fb0ec51 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -964,7 +964,8 @@
       }
     }
 
-    List<Predicate<ChangeData>> predicates = Lists.newArrayListWithCapacity(9);
+    // Adapt the capacity of this list when adding more default predicates.
+    List<Predicate<ChangeData>> predicates = Lists.newArrayListWithCapacity(11);
     try {
       predicates.add(commit(query));
     } catch (IllegalArgumentException e) {
@@ -992,6 +993,8 @@
     predicates.add(ref(query));
     predicates.add(branch(query));
     predicates.add(topic(query));
+    // Adapt the capacity of the "predicates" list when adding more default
+    // predicates.
     return Predicate.or(predicates);
   }