Reindex account whenever account is evicted from cache

Change-Id: I025cabc9be98628777066cda7aa97186f5a0da15
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
index 749360c..e39c8ae 100644
--- a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
+++ b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
@@ -66,6 +66,7 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -154,7 +155,7 @@
     return userFactory.create(id);
   }
 
-  private IdentifiedUser reloadUser() {
+  private IdentifiedUser reloadUser() throws IOException {
     accountCache.evict(userId);
     user = userFactory.create(userId);
     return user;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
index 56c5cbd..8ede324 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -219,7 +219,7 @@
     }
   }
 
-  private AuthResult create() {
+  private AuthResult create() throws IOException {
     String fakeId = AccountExternalId.SCHEME_UUID + UUID.randomUUID();
     try {
       return accountManager.authenticate(new AuthRequest(fakeId));
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
index bfbf1ff..40e0f60 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
@@ -153,7 +153,7 @@
   }
 
   private void updateRemoteExternalId(AuthResult arsp, String remoteAuthToken)
-      throws AccountException, OrmException {
+      throws AccountException, OrmException, IOException {
     AccountExternalId remoteAuthExtId =
         new AccountExternalId(arsp.getAccountId(), new AccountExternalId.Key(
             SCHEME_EXTERNAL, remoteAuthToken));
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/BaseServiceImplementation.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/BaseServiceImplementation.java
index 98fabd1..9cf6504 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/BaseServiceImplementation.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/BaseServiceImplementation.java
@@ -27,6 +27,8 @@
 import com.google.gwtorm.server.OrmRuntimeException;
 import com.google.inject.Provider;
 
+import java.io.IOException;
+
 /** Support for services which require a {@link ReviewDb} instance. */
 public class BaseServiceImplementation {
   private final Provider<ReviewDb> schema;
@@ -86,6 +88,8 @@
       handleOrmException(callback, ex);
     } catch (OrmException e) {
       handleOrmException(callback, e);
+    } catch (IOException e) {
+      callback.onFailure(e);
     } catch (Failure e) {
       if (e.getCause() instanceof NoSuchProjectException
           || e.getCause() instanceof NoSuchChangeException) {
@@ -132,6 +136,6 @@
      * @throws InvalidQueryException
      */
     T run(ReviewDb db) throws OrmException, Failure, NoSuchProjectException,
-        NoSuchGroupException, InvalidQueryException;
+        NoSuchGroupException, InvalidQueryException, IOException;
   }
 }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
index 3c1c12d..8daa9d8 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
@@ -40,6 +40,7 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
+import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -98,7 +99,8 @@
       final AsyncCallback<Account> callback) {
     run(callback, new Action<Account>() {
       @Override
-      public Account run(ReviewDb db) throws OrmException, Failure {
+      public Account run(ReviewDb db)
+          throws OrmException, Failure, IOException {
         IdentifiedUser self = user.get();
         final Account me = db.accounts().get(self.getAccountId());
         final String oldEmail = me.getPreferredEmail();
@@ -133,7 +135,8 @@
       final AsyncCallback<VoidResult> callback) {
     run(callback, new Action<VoidResult>() {
       @Override
-      public VoidResult run(final ReviewDb db) throws OrmException, Failure {
+      public VoidResult run(final ReviewDb db)
+          throws OrmException, Failure, IOException {
         ContributorAgreement ca = projectCache.getAllProjects().getConfig()
             .getContributorAgreement(agreementName);
         if (ca == null) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/DeleteExternalIds.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/DeleteExternalIds.java
index 1d45c7d..34b7a4b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/DeleteExternalIds.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/DeleteExternalIds.java
@@ -24,6 +24,7 @@
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -60,7 +61,7 @@
   }
 
   @Override
-  public Set<AccountExternalId.Key> call() throws OrmException {
+  public Set<AccountExternalId.Key> call() throws OrmException, IOException {
     final Map<AccountExternalId.Key, AccountExternalId> have = have();
 
     List<AccountExternalId> toDelete = new ArrayList<>();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCache.java
index d7d418d..2e97005 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCache.java
@@ -16,6 +16,8 @@
 
 import com.google.gerrit.reviewdb.client.Account;
 
+import java.io.IOException;
+
 /** Caches important (but small) account state to avoid database hits. */
 public interface AccountCache {
   AccountState get(Account.Id accountId);
@@ -24,7 +26,7 @@
 
   AccountState getByUsername(String username);
 
-  void evict(Account.Id accountId);
+  void evict(Account.Id accountId) throws IOException;
 
   void evictByUsername(String username);
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
index 1e06faa..7bf0642 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -26,6 +26,8 @@
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.index.Index;
+import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Inject;
@@ -74,12 +76,15 @@
 
   private final LoadingCache<Account.Id, AccountState> byId;
   private final LoadingCache<String, Optional<Account.Id>> byName;
+  private final AccountIndexCollection indexes;
 
   @Inject
   AccountCacheImpl(@Named(BYID_NAME) LoadingCache<Account.Id, AccountState> byId,
-      @Named(BYUSER_NAME) LoadingCache<String, Optional<Account.Id>> byUsername) {
+      @Named(BYUSER_NAME) LoadingCache<String, Optional<Account.Id>> byUsername,
+      AccountIndexCollection indexes) {
     this.byId = byId;
     this.byName = byUsername;
+    this.indexes = indexes;
   }
 
   @Override
@@ -109,9 +114,16 @@
   }
 
   @Override
-  public void evict(Account.Id accountId) {
+  public void evict(Account.Id accountId) throws IOException {
     if (accountId != null) {
       byId.invalidate(accountId);
+      index(accountId);
+    }
+  }
+
+  private void index(Account.Id id) throws IOException {
+    for (Index<?, AccountState> i : indexes.getWriteIndexes()) {
+      i.replace(get(id));
     }
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
index 330f44a..ac4d2ba 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
@@ -37,6 +37,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -101,7 +102,8 @@
    * @throws AccountException the account does not exist, and cannot be created,
    *         or exists, but cannot be located, or is inactive.
    */
-  public AuthResult authenticate(AuthRequest who) throws AccountException {
+  public AuthResult authenticate(AuthRequest who)
+      throws AccountException, IOException {
     who = realm.authenticate(who);
     try {
       try (ReviewDb db = schema.open()) {
@@ -152,7 +154,8 @@
   }
 
   private void update(ReviewDb db, AuthRequest who, AccountExternalId extId)
-      throws OrmException, NameAlreadyUsedException, InvalidUserNameException {
+      throws OrmException, NameAlreadyUsedException, InvalidUserNameException,
+      IOException {
     IdentifiedUser user = userFactory.create(extId.getAccountId());
     Account toUpdate = null;
 
@@ -214,7 +217,7 @@
   }
 
   private AuthResult create(ReviewDb db, AuthRequest who)
-      throws OrmException, AccountException {
+      throws OrmException, AccountException, IOException {
     Account.Id newId = new Account.Id(db.nextAccountId());
     Account account = new Account(newId, TimeUtil.nowTs());
     AccountExternalId extId = createId(newId, who);
@@ -340,7 +343,7 @@
    *         cannot be linked at this time.
    */
   public AuthResult link(Account.Id to, AuthRequest who)
-      throws AccountException, OrmException {
+      throws AccountException, OrmException, IOException {
     try (ReviewDb db = schema.open()) {
       AccountExternalId.Key key = id(who);
       AccountExternalId extId = getAccountExternalId(db, key);
@@ -392,7 +395,7 @@
    *         cannot be linked at this time.
    */
   public AuthResult updateLink(Account.Id to, AuthRequest who) throws OrmException,
-      AccountException {
+      AccountException, IOException {
     try (ReviewDb db = schema.open()) {
       AccountExternalId.Key key = id(who);
       List<AccountExternalId.Key> filteredKeysByScheme =
@@ -429,7 +432,7 @@
    *         cannot be unlinked at this time.
    */
   public AuthResult unlink(Account.Id from, AuthRequest who)
-      throws AccountException, OrmException {
+      throws AccountException, OrmException, IOException {
     try (ReviewDb db = schema.open()) {
       AccountExternalId.Key key = id(who);
       AccountExternalId extId = getAccountExternalId(db, key);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/ChangeUserName.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/ChangeUserName.java
index f466fa3..c1ecafd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/ChangeUserName.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/ChangeUserName.java
@@ -29,6 +29,7 @@
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -71,7 +72,7 @@
 
   @Override
   public VoidResult call() throws OrmException, NameAlreadyUsedException,
-      InvalidUserNameException {
+      InvalidUserNameException, IOException {
     final Collection<AccountExternalId> old = old();
     if (!old.isEmpty()) {
       throw new IllegalStateException(USERNAME_CANNOT_BE_CHANGED);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java
index 45dd971..1110acd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java
@@ -39,6 +39,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+
 public class CreateEmail implements RestModifyView<AccountResource, EmailInput> {
   private static final Logger log = LoggerFactory.getLogger(CreateEmail.class);
 
@@ -75,7 +77,7 @@
   public Response<EmailInfo> apply(AccountResource rsrc, EmailInput input)
       throws AuthException, BadRequestException, ResourceConflictException,
       ResourceNotFoundException, OrmException, EmailException,
-      MethodNotAllowedException {
+      MethodNotAllowedException, IOException {
     if (self.get() != rsrc.getUser()
         && !self.get().getCapabilities().canModifyAccount()) {
       throw new AuthException("not allowed to add email address");
@@ -104,7 +106,7 @@
   public Response<EmailInfo> apply(IdentifiedUser user, EmailInput input)
       throws AuthException, BadRequestException, ResourceConflictException,
       ResourceNotFoundException, OrmException, EmailException,
-      MethodNotAllowedException {
+      MethodNotAllowedException, IOException {
     if (input.email != null && !email.equals(input.email)) {
       throw new BadRequestException("email address must match URL");
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
index abdaf23..f6c48af 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
@@ -27,6 +27,7 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
+import java.io.IOException;
 import java.util.Collections;
 
 @RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
@@ -46,7 +47,7 @@
 
   @Override
   public Response<?> apply(AccountResource rsrc, Input input)
-      throws ResourceNotFoundException, OrmException {
+      throws ResourceNotFoundException, OrmException, IOException {
     Account a = dbProvider.get().accounts().get(rsrc.getUser().getAccountId());
     if (a == null) {
       throw new ResourceNotFoundException("account not found");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java
index f1e02bd..76f63b7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java
@@ -31,6 +31,8 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
+import java.io.IOException;
+
 @Singleton
 public class DeleteEmail implements RestModifyView<AccountResource.Email, Input> {
   public static class Input {
@@ -53,7 +55,8 @@
   @Override
   public Response<?> apply(AccountResource.Email rsrc, Input input)
       throws AuthException, ResourceNotFoundException,
-      ResourceConflictException, MethodNotAllowedException, OrmException {
+      ResourceConflictException, MethodNotAllowedException, OrmException,
+      IOException {
     if (self.get() != rsrc.getUser()
         && !self.get().getCapabilities().canModifyAccount()) {
       throw new AuthException("not allowed to delete email address");
@@ -63,7 +66,7 @@
 
   public Response<?> apply(IdentifiedUser user, String email)
       throws ResourceNotFoundException, ResourceConflictException,
-      MethodNotAllowedException, OrmException {
+      MethodNotAllowedException, OrmException, IOException {
     if (!realm.allowsEdit(FieldName.REGISTER_NEW_EMAIL)) {
       throw new MethodNotAllowedException("realm does not allow deleting emails");
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
index c7a63e5..8cc134f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
@@ -27,6 +27,7 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
+import java.io.IOException;
 import java.util.Collections;
 
 @RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
@@ -46,7 +47,7 @@
 
   @Override
   public Response<String> apply(AccountResource rsrc, Input input)
-      throws ResourceNotFoundException, OrmException {
+      throws ResourceNotFoundException, OrmException, IOException {
     Account a = dbProvider.get().accounts().get(rsrc.getUser().getAccountId());
     if (a == null) {
       throw new ResourceNotFoundException("account not found");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java
index 64fd11a..0cd93f1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java
@@ -34,6 +34,7 @@
 
 import org.apache.commons.codec.binary.Base64;
 
+import java.io.IOException;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.util.Collections;
@@ -69,8 +70,9 @@
   }
 
   @Override
-  public Response<String> apply(AccountResource rsrc, Input input) throws AuthException,
-      ResourceNotFoundException, ResourceConflictException, OrmException {
+  public Response<String> apply(AccountResource rsrc, Input input)
+      throws AuthException, ResourceNotFoundException,
+      ResourceConflictException, OrmException, IOException {
     if (input == null) {
       input = new Input();
     }
@@ -101,7 +103,8 @@
   }
 
   public Response<String> apply(IdentifiedUser user, String newPassword)
-      throws ResourceNotFoundException, ResourceConflictException, OrmException {
+      throws ResourceNotFoundException, ResourceConflictException, OrmException,
+      IOException {
     if (user.getUserName() == null) {
       throw new ResourceConflictException("username must be set");
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java
index 601ee76..6338b15 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java
@@ -36,6 +36,7 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
+import java.io.IOException;
 import java.util.Collections;
 
 @Singleton
@@ -62,7 +63,7 @@
   @Override
   public Response<String> apply(AccountResource rsrc, Input input)
       throws AuthException, MethodNotAllowedException,
-      ResourceNotFoundException, OrmException {
+      ResourceNotFoundException, OrmException, IOException {
     if (self.get() != rsrc.getUser()
         && !self.get().getCapabilities().canModifyAccount()) {
       throw new AuthException("not allowed to change name");
@@ -71,7 +72,8 @@
   }
 
   public Response<String> apply(IdentifiedUser user, Input input)
-      throws MethodNotAllowedException, ResourceNotFoundException, OrmException {
+      throws MethodNotAllowedException, ResourceNotFoundException, OrmException,
+      IOException {
     if (input == null) {
       input = new Input();
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java
index c49e3be..92357b5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java
@@ -28,6 +28,7 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
+import java.io.IOException;
 import java.util.Collections;
 
 @Singleton
@@ -50,7 +51,8 @@
 
   @Override
   public Response<String> apply(AccountResource.Email rsrc, Input input)
-      throws AuthException, ResourceNotFoundException, OrmException {
+      throws AuthException, ResourceNotFoundException, OrmException,
+      IOException {
     if (self.get() != rsrc.getUser()
         && !self.get().getCapabilities().canModifyAccount()) {
       throw new AuthException("not allowed to set preferred email address");
@@ -59,7 +61,7 @@
   }
 
   public Response<String> apply(IdentifiedUser user, String email)
-      throws ResourceNotFoundException, OrmException {
+      throws ResourceNotFoundException, OrmException, IOException {
     Account a = dbProvider.get().accounts().get(user.getAccountId());
     if (a == null) {
       throw new ResourceNotFoundException("account not found");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutUsername.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutUsername.java
index fdd4381..e9dc393 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutUsername.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutUsername.java
@@ -30,6 +30,8 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
+import java.io.IOException;
+
 @Singleton
 public class PutUsername implements RestModifyView<AccountResource, Input> {
   public static class Input {
@@ -56,7 +58,7 @@
   @Override
   public String apply(AccountResource rsrc, Input input) throws AuthException,
       MethodNotAllowedException, UnprocessableEntityException,
-      ResourceConflictException, OrmException {
+      ResourceConflictException, OrmException, IOException {
     if (self.get() != rsrc.getUser()
         && !self.get().getCapabilities().canAdministrateServer()) {
       throw new AuthException("not allowed to set username");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
index 98fc7c2..2038276 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
@@ -311,7 +311,7 @@
         new AccountResource.Email(account.getUser(), input.email);
     try {
       createEmailFactory.create(input.email).apply(rsrc, input);
-    } catch (EmailException | OrmException e) {
+    } catch (EmailException | OrmException | IOException e) {
       throw new RestApiException("Cannot add email", e);
     }
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
index 6f0c504..5660176 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
@@ -44,6 +44,7 @@
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
 
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
 
@@ -217,7 +218,7 @@
     try {
       addMembers.apply(
           rsrc, AddMembers.Input.fromMembers(Arrays.asList(members)));
-    } catch (OrmException e) {
+    } catch (OrmException | IOException e) {
       throw new RestApiException("Cannot add group members", e);
     }
   }
@@ -227,7 +228,7 @@
     try {
       deleteMembers.apply(
           rsrc, AddMembers.Input.fromMembers(Arrays.asList(members)));
-    } catch (OrmException e) {
+    } catch (OrmException | IOException e) {
       throw new RestApiException("Cannot remove group members", e);
     }
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java
index 3d2c960..b509c55 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java
@@ -90,7 +90,7 @@
       GroupInfo info = createGroup.create(in.name)
           .apply(TopLevelResource.INSTANCE, in);
       return id(info.id);
-    } catch (OrmException e) {
+    } catch (OrmException | IOException e) {
       throw new RestApiException("Cannot create group " + in.name, e);
     }
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountIdHandler.java b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountIdHandler.java
index 0314ef2..51f85fe 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountIdHandler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountIdHandler.java
@@ -32,6 +32,8 @@
 import org.kohsuke.args4j.spi.Parameters;
 import org.kohsuke.args4j.spi.Setter;
 
+import java.io.IOException;
+
 public class AccountIdHandler extends OptionHandler<Account.Id> {
   private final AccountResolver accountResolver;
   private final AccountManager accountManager;
@@ -76,7 +78,7 @@
             throw new CmdLineException(owner, "user \"" + token + "\" not found");
         }
       }
-    } catch (OrmException e) {
+    } catch (OrmException | IOException e) {
       throw new CmdLineException(owner, "database is down");
     }
     setter.addValue(accountId);
@@ -84,7 +86,7 @@
   }
 
   private Account.Id createAccountByLdap(String user)
-      throws CmdLineException {
+      throws CmdLineException, IOException {
     if (!user.matches(Account.USER_NAME_PATTERN)) {
       throw new CmdLineException(owner, "user \"" + user + "\" not found");
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfirmEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfirmEmail.java
index 9c05df0..87d0777 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfirmEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfirmEmail.java
@@ -30,6 +30,8 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
+import java.io.IOException;
+
 @Singleton
 public class ConfirmEmail implements RestModifyView<ConfigResource, Input> {
   public static class Input {
@@ -53,7 +55,7 @@
   @Override
   public Response<?> apply(ConfigResource rsrc, Input input)
       throws AuthException, UnprocessableEntityException, AccountException,
-      OrmException {
+      OrmException, IOException {
     CurrentUser user = self.get();
     if (!user.isIdentifiedUser()) {
       throw new AuthException("Authentication required");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
index 0039c3c..7dd2d81 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
@@ -45,6 +45,7 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
+import java.io.IOException;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -114,7 +115,7 @@
   @Override
   public List<AccountInfo> apply(GroupResource resource, Input input)
       throws AuthException, MethodNotAllowedException,
-      UnprocessableEntityException, OrmException {
+      UnprocessableEntityException, OrmException, IOException {
     AccountGroup internalGroup = resource.toAccountGroup();
     if (internalGroup == null) {
       throw new MethodNotAllowedException();
@@ -142,7 +143,7 @@
   }
 
   private Account findAccount(String nameOrEmail) throws AuthException,
-      UnprocessableEntityException, OrmException {
+      UnprocessableEntityException, OrmException, IOException {
     try {
       return accounts.parse(nameOrEmail).getAccount();
     } catch (UnprocessableEntityException e) {
@@ -174,7 +175,8 @@
   }
 
   public void addMembers(AccountGroup.Id groupId,
-      Collection<? extends Account.Id> newMemberIds) throws OrmException {
+      Collection<? extends Account.Id> newMemberIds)
+          throws OrmException, IOException {
     Map<Account.Id, AccountGroupMember> newAccountGroupMembers = new HashMap<>();
     for (Account.Id accId : newMemberIds) {
       if (!newAccountGroupMembers.containsKey(accId)) {
@@ -197,7 +199,7 @@
     }
   }
 
-  private Account createAccountByLdap(String user) {
+  private Account createAccountByLdap(String user) throws IOException {
     if (!user.matches(Account.USER_NAME_PATTERN)) {
       return null;
     }
@@ -238,7 +240,7 @@
     @Override
     public AccountInfo apply(GroupResource resource, PutMember.Input input)
         throws AuthException, MethodNotAllowedException,
-        ResourceNotFoundException, OrmException {
+        ResourceNotFoundException, OrmException, IOException {
       AddMembers.Input in = new AddMembers.Input();
       in._oneMember = id;
       try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java
index dbcad99..9976d4c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java
@@ -50,6 +50,7 @@
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.PersonIdent;
 
+import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -98,7 +99,7 @@
   @Override
   public GroupInfo apply(TopLevelResource resource, GroupInput input)
       throws BadRequestException, UnprocessableEntityException,
-      ResourceConflictException, OrmException {
+      ResourceConflictException, OrmException, IOException {
     if (input == null) {
       input = new GroupInput();
     }
@@ -138,7 +139,7 @@
   }
 
   private AccountGroup createGroup(CreateGroupArgs createGroupArgs)
-      throws OrmException, ResourceConflictException {
+      throws OrmException, ResourceConflictException, IOException {
 
     // Do not allow creating groups with the same name as system groups
     List<String> sysGroupNames = SystemGroupBackend.getNames();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
index 5a759bc..e1a6921 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
@@ -34,6 +34,7 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -62,7 +63,7 @@
   @Override
   public Response<?> apply(GroupResource resource, Input input)
       throws AuthException, MethodNotAllowedException,
-      UnprocessableEntityException, OrmException {
+      UnprocessableEntityException, OrmException, IOException {
     AccountGroup internalGroup = resource.toAccountGroup();
     if (internalGroup == null) {
       throw new MethodNotAllowedException();
@@ -125,7 +126,7 @@
     @Override
     public Response<?> apply(MemberResource resource, Input input)
         throws AuthException, MethodNotAllowedException,
-        UnprocessableEntityException, OrmException {
+        UnprocessableEntityException, OrmException, IOException {
       AddMembers.Input in = new AddMembers.Input();
       in._oneMember = resource.getMember().getAccountId().toString();
       return delete.get().apply(resource, in);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
index 11e65a3..cbeb38c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.WorkQueue;
+import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.index.account.AccountIndexDefinition;
 import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
@@ -87,6 +88,10 @@
   @Override
   protected void configure() {
     bind(IndexRewriter.class);
+
+    bind(AccountIndexCollection.class);
+    listener().to(AccountIndexCollection.class);
+
     bind(ChangeIndexCollection.class);
     listener().to(ChangeIndexCollection.class);
     factory(ChangeIndexer.Factory.class);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
index 155c2eb..22f9683 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
@@ -38,6 +38,7 @@
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
 
+import java.io.IOException;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -88,7 +89,7 @@
   private AddIncludedGroups addIncludedGroups;
 
   @Override
-  protected void run() throws Failure, OrmException {
+  protected void run() throws Failure, OrmException, IOException {
     try {
       GroupResource rsrc = createGroup();
 
@@ -104,7 +105,8 @@
     }
   }
 
-  private GroupResource createGroup() throws RestApiException, OrmException {
+  private GroupResource createGroup()
+      throws RestApiException, OrmException, IOException {
     GroupInput input = new GroupInput();
     input.description = groupDescription;
     input.visibleToAll = visibleToAll;
@@ -120,7 +122,7 @@
   }
 
   private void addMembers(GroupResource rsrc) throws RestApiException,
-      OrmException {
+      OrmException, IOException {
     AddMembers.Input input =
         AddMembers.Input.fromMembers(FluentIterable
             .from(initialMembers)
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index 535f79a..082395c 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -256,8 +256,8 @@
         new AccountResource.SshKey(user, sshKey), null);
   }
 
-  private void addEmail(String email) throws UnloggedFailure, RestApiException,
-      OrmException {
+  private void addEmail(String email)
+      throws UnloggedFailure, RestApiException, OrmException, IOException {
     EmailInput in = new EmailInput();
     in.email = email;
     in.noConfirmation = true;
@@ -268,7 +268,8 @@
     }
   }
 
-  private void deleteEmail(String email) throws RestApiException, OrmException {
+  private void deleteEmail(String email)
+      throws RestApiException, OrmException, IOException {
     if (email.equals("ALL")) {
       List<EmailInfo> emails = getEmails.apply(rsrc);
       for (EmailInfo e : emails) {
@@ -281,8 +282,8 @@
     }
   }
 
-  private void putPreferred(String email) throws RestApiException,
-      OrmException {
+  private void putPreferred(String email)
+      throws RestApiException, OrmException, IOException {
     for (EmailInfo e : getEmails.apply(rsrc)) {
       if (e.email.equals(email)) {
         putPreferred.apply(new AccountResource.Email(user, email), null);