Merge changes from topic 'sequence'
* changes: Read change ID sequence from notedb Notedb: Add a git repo based sequence implementation
This commit is contained in:
		@@ -30,6 +30,7 @@ import com.google.gerrit.reviewdb.client.Project;
 | 
			
		||||
import com.google.gerrit.reviewdb.client.RefNames;
 | 
			
		||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
			
		||||
import com.google.gerrit.server.IdentifiedUser;
 | 
			
		||||
import com.google.gerrit.server.Sequences;
 | 
			
		||||
import com.google.gerrit.server.account.GroupBackend;
 | 
			
		||||
import com.google.gerrit.server.change.ChangeInserter;
 | 
			
		||||
import com.google.gerrit.server.change.ChangeResource;
 | 
			
		||||
@@ -69,6 +70,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private final ReviewDb db;
 | 
			
		||||
  private final Sequences seq;
 | 
			
		||||
  private final IdentifiedUser user;
 | 
			
		||||
  private final Provider<PostReviewers> reviewersProvider;
 | 
			
		||||
  private final ProjectCache projectCache;
 | 
			
		||||
@@ -88,6 +90,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
 | 
			
		||||
      ChangeInserter.Factory changeInserterFactory,
 | 
			
		||||
      BatchUpdate.Factory updateFactory,
 | 
			
		||||
      Provider<SetParent> setParent,
 | 
			
		||||
      Sequences seq,
 | 
			
		||||
 | 
			
		||||
      @Assisted("projectName") Project.NameKey projectName,
 | 
			
		||||
      @Nullable @Assisted ObjectId base,
 | 
			
		||||
@@ -98,6 +101,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
 | 
			
		||||
        allProjects, setParent, projectName, base, sectionList,
 | 
			
		||||
        parentProjectName, message, false);
 | 
			
		||||
    this.db = db;
 | 
			
		||||
    this.seq = seq;
 | 
			
		||||
    this.user = user;
 | 
			
		||||
    this.reviewersProvider = reviewersProvider;
 | 
			
		||||
    this.projectCache = projectCache;
 | 
			
		||||
@@ -111,7 +115,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
 | 
			
		||||
      ProjectConfig config, MetaDataUpdate md, boolean parentProjectUpdate)
 | 
			
		||||
      throws IOException, OrmException {
 | 
			
		||||
    md.setInsertChangeId(true);
 | 
			
		||||
    Change.Id changeId = new Change.Id(db.nextChangeId());
 | 
			
		||||
    Change.Id changeId = new Change.Id(seq.nextChangeId());
 | 
			
		||||
    RevCommit commit =
 | 
			
		||||
        config.commitToNewRef(md, new PatchSet.Id(changeId,
 | 
			
		||||
            Change.INITIAL_PATCH_SET_ID).toRefName());
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,9 @@ public class RefNames {
 | 
			
		||||
  /** A change starred by a user */
 | 
			
		||||
  public static final String REFS_STARRED_CHANGES = "refs/starred-changes/";
 | 
			
		||||
 | 
			
		||||
  /** Sequence counters in notedb. */
 | 
			
		||||
  public static final String REFS_SEQUENCES = "refs/sequences/";
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Prefix applied to merge commit base nodes.
 | 
			
		||||
   * <p>
 | 
			
		||||
 
 | 
			
		||||
@@ -113,8 +113,15 @@ public interface ReviewDb extends Schema {
 | 
			
		||||
  @Sequence
 | 
			
		||||
  int nextAccountGroupId() throws OrmException;
 | 
			
		||||
 | 
			
		||||
  /** Next unique id for a {@link Change}. */
 | 
			
		||||
  @Sequence
 | 
			
		||||
  int FIRST_CHANGE_ID = 1;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Next unique id for a {@link Change}.
 | 
			
		||||
   *
 | 
			
		||||
   * @deprecated use {@link com.google.gerrit.server.Sequences#nextChangeId()}.
 | 
			
		||||
   */
 | 
			
		||||
  @Sequence(startWith = FIRST_CHANGE_ID)
 | 
			
		||||
  @Deprecated
 | 
			
		||||
  int nextChangeId() throws OrmException;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,7 @@ java_library(
 | 
			
		||||
    '//lib:grappa',
 | 
			
		||||
    '//lib:gson',
 | 
			
		||||
    '//lib:guava',
 | 
			
		||||
    '//lib:guava-retrying',
 | 
			
		||||
    '//lib:gwtjsonrpc',
 | 
			
		||||
    '//lib:gwtorm',
 | 
			
		||||
    '//lib:jsch',
 | 
			
		||||
@@ -195,6 +196,7 @@ java_test(
 | 
			
		||||
    '//lib:args4j',
 | 
			
		||||
    '//lib:grappa',
 | 
			
		||||
    '//lib:guava',
 | 
			
		||||
    '//lib:guava-retrying',
 | 
			
		||||
    '//lib/dropwizard:dropwizard-core',
 | 
			
		||||
    '//lib/guice:guice-assistedinject',
 | 
			
		||||
    '//lib/prolog:runtime',
 | 
			
		||||
 
 | 
			
		||||
@@ -174,6 +174,7 @@ public class ChangeUtil {
 | 
			
		||||
 | 
			
		||||
  private final Provider<CurrentUser> user;
 | 
			
		||||
  private final Provider<ReviewDb> db;
 | 
			
		||||
  private final Sequences seq;
 | 
			
		||||
  private final Provider<InternalChangeQuery> queryProvider;
 | 
			
		||||
  private final ChangeControl.GenericFactory changeControlFactory;
 | 
			
		||||
  private final RevertedSender.Factory revertedSenderFactory;
 | 
			
		||||
@@ -186,6 +187,7 @@ public class ChangeUtil {
 | 
			
		||||
  @Inject
 | 
			
		||||
  ChangeUtil(Provider<CurrentUser> user,
 | 
			
		||||
      Provider<ReviewDb> db,
 | 
			
		||||
      Sequences seq,
 | 
			
		||||
      Provider<InternalChangeQuery> queryProvider,
 | 
			
		||||
      ChangeControl.GenericFactory changeControlFactory,
 | 
			
		||||
      RevertedSender.Factory revertedSenderFactory,
 | 
			
		||||
@@ -196,6 +198,7 @@ public class ChangeUtil {
 | 
			
		||||
      ChangeUpdate.Factory changeUpdateFactory) {
 | 
			
		||||
    this.user = user;
 | 
			
		||||
    this.db = db;
 | 
			
		||||
    this.seq = seq;
 | 
			
		||||
    this.queryProvider = queryProvider;
 | 
			
		||||
    this.changeControlFactory = changeControlFactory;
 | 
			
		||||
    this.revertedSenderFactory = revertedSenderFactory;
 | 
			
		||||
@@ -262,7 +265,7 @@ public class ChangeUtil {
 | 
			
		||||
        RefControl refControl = ctl.getRefControl();
 | 
			
		||||
        Change change = new Change(
 | 
			
		||||
            new Change.Key("I" + computedChangeId.name()),
 | 
			
		||||
            new Change.Id(db.get().nextChangeId()),
 | 
			
		||||
            new Change.Id(seq.nextChangeId()),
 | 
			
		||||
            user.get().getAccountId(),
 | 
			
		||||
            changeToRevert.getDest(),
 | 
			
		||||
            TimeUtil.nowTs());
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,56 @@
 | 
			
		||||
// Copyright (C) 2016 The Android Open Source Project
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package com.google.gerrit.server;
 | 
			
		||||
 | 
			
		||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
			
		||||
import com.google.gerrit.server.config.AllProjectsName;
 | 
			
		||||
import com.google.gerrit.server.git.GitRepositoryManager;
 | 
			
		||||
import com.google.gerrit.server.notedb.NotesMigration;
 | 
			
		||||
import com.google.gerrit.server.notedb.RepoSequence;
 | 
			
		||||
import com.google.gwtorm.server.OrmException;
 | 
			
		||||
import com.google.inject.Inject;
 | 
			
		||||
import com.google.inject.Provider;
 | 
			
		||||
import com.google.inject.Singleton;
 | 
			
		||||
 | 
			
		||||
@Singleton
 | 
			
		||||
public class Sequences {
 | 
			
		||||
  private final Provider<ReviewDb> db;
 | 
			
		||||
  private final NotesMigration migration;
 | 
			
		||||
  private final RepoSequence changeSeq;
 | 
			
		||||
 | 
			
		||||
  @Inject
 | 
			
		||||
  Sequences(Provider<ReviewDb> db,
 | 
			
		||||
      NotesMigration migration,
 | 
			
		||||
      GitRepositoryManager repoManager,
 | 
			
		||||
      AllProjectsName allProjects) {
 | 
			
		||||
    this.db = db;
 | 
			
		||||
    this.migration = migration;
 | 
			
		||||
    if (migration.readChanges()) {
 | 
			
		||||
      changeSeq = new RepoSequence(
 | 
			
		||||
          repoManager, allProjects, "changes", ReviewDb.FIRST_CHANGE_ID, 100);
 | 
			
		||||
    } else {
 | 
			
		||||
      changeSeq = null;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @SuppressWarnings("deprecation")
 | 
			
		||||
  public int nextChangeId() throws OrmException {
 | 
			
		||||
    if (migration.readChanges()) {
 | 
			
		||||
      return changeSeq.next();
 | 
			
		||||
    } else {
 | 
			
		||||
      return db.get().nextChangeId();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -30,6 +30,7 @@ import com.google.gerrit.server.ChangeMessagesUtil;
 | 
			
		||||
import com.google.gerrit.server.ChangeUtil;
 | 
			
		||||
import com.google.gerrit.server.GerritPersonIdent;
 | 
			
		||||
import com.google.gerrit.server.IdentifiedUser;
 | 
			
		||||
import com.google.gerrit.server.Sequences;
 | 
			
		||||
import com.google.gerrit.server.git.BatchUpdate;
 | 
			
		||||
import com.google.gerrit.server.git.CodeReviewCommit;
 | 
			
		||||
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
 | 
			
		||||
@@ -71,6 +72,7 @@ import java.util.TimeZone;
 | 
			
		||||
public class CherryPickChange {
 | 
			
		||||
 | 
			
		||||
  private final Provider<ReviewDb> db;
 | 
			
		||||
  private final Sequences seq;
 | 
			
		||||
  private final Provider<InternalChangeQuery> queryProvider;
 | 
			
		||||
  private final GitRepositoryManager gitManager;
 | 
			
		||||
  private final TimeZone serverTimeZone;
 | 
			
		||||
@@ -84,6 +86,7 @@ public class CherryPickChange {
 | 
			
		||||
 | 
			
		||||
  @Inject
 | 
			
		||||
  CherryPickChange(Provider<ReviewDb> db,
 | 
			
		||||
      Sequences seq,
 | 
			
		||||
      Provider<InternalChangeQuery> queryProvider,
 | 
			
		||||
      @GerritPersonIdent PersonIdent myIdent,
 | 
			
		||||
      GitRepositoryManager gitManager,
 | 
			
		||||
@@ -95,6 +98,7 @@ public class CherryPickChange {
 | 
			
		||||
      ChangeUpdate.Factory updateFactory,
 | 
			
		||||
      BatchUpdate.Factory batchUpdateFactory) {
 | 
			
		||||
    this.db = db;
 | 
			
		||||
    this.seq = seq;
 | 
			
		||||
    this.queryProvider = queryProvider;
 | 
			
		||||
    this.gitManager = gitManager;
 | 
			
		||||
    this.serverTimeZone = myIdent.getTimeZone();
 | 
			
		||||
@@ -233,7 +237,7 @@ public class CherryPickChange {
 | 
			
		||||
      IdentifiedUser identifiedUser, String topic, Branch.NameKey sourceBranch)
 | 
			
		||||
      throws RestApiException, UpdateException, OrmException {
 | 
			
		||||
    Change change =
 | 
			
		||||
        new Change(changeKey, new Change.Id(db.get().nextChangeId()),
 | 
			
		||||
        new Change(changeKey, new Change.Id(seq.nextChangeId()),
 | 
			
		||||
            identifiedUser.getAccountId(), new Branch.NameKey(project,
 | 
			
		||||
                destRef.getName()), TimeUtil.nowTs());
 | 
			
		||||
    change.setTopic(topic);
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,7 @@ import com.google.gerrit.server.ChangeUtil;
 | 
			
		||||
import com.google.gerrit.server.CurrentUser;
 | 
			
		||||
import com.google.gerrit.server.GerritPersonIdent;
 | 
			
		||||
import com.google.gerrit.server.IdentifiedUser;
 | 
			
		||||
import com.google.gerrit.server.Sequences;
 | 
			
		||||
import com.google.gerrit.server.config.GerritServerConfig;
 | 
			
		||||
import com.google.gerrit.server.git.BatchUpdate;
 | 
			
		||||
import com.google.gerrit.server.git.GitRepositoryManager;
 | 
			
		||||
@@ -77,6 +78,7 @@ public class CreateChange implements
 | 
			
		||||
 | 
			
		||||
  private final Provider<ReviewDb> db;
 | 
			
		||||
  private final GitRepositoryManager gitManager;
 | 
			
		||||
  private final Sequences seq;
 | 
			
		||||
  private final TimeZone serverTimeZone;
 | 
			
		||||
  private final Provider<CurrentUser> user;
 | 
			
		||||
  private final ProjectsCollection projectsCollection;
 | 
			
		||||
@@ -89,6 +91,7 @@ public class CreateChange implements
 | 
			
		||||
  @Inject
 | 
			
		||||
  CreateChange(Provider<ReviewDb> db,
 | 
			
		||||
      GitRepositoryManager gitManager,
 | 
			
		||||
      Sequences seq,
 | 
			
		||||
      @GerritPersonIdent PersonIdent myIdent,
 | 
			
		||||
      Provider<CurrentUser> user,
 | 
			
		||||
      ProjectsCollection projectsCollection,
 | 
			
		||||
@@ -99,6 +102,7 @@ public class CreateChange implements
 | 
			
		||||
      @GerritServerConfig Config config) {
 | 
			
		||||
    this.db = db;
 | 
			
		||||
    this.gitManager = gitManager;
 | 
			
		||||
    this.seq = seq;
 | 
			
		||||
    this.serverTimeZone = myIdent.getTimeZone();
 | 
			
		||||
    this.user = user;
 | 
			
		||||
    this.projectsCollection = projectsCollection;
 | 
			
		||||
@@ -194,7 +198,7 @@ public class CreateChange implements
 | 
			
		||||
 | 
			
		||||
        Change change = new Change(
 | 
			
		||||
            getChangeId(id, c),
 | 
			
		||||
            new Change.Id(db.get().nextChangeId()),
 | 
			
		||||
            new Change.Id(seq.nextChangeId()),
 | 
			
		||||
            me.getAccountId(),
 | 
			
		||||
            new Branch.NameKey(project, refName),
 | 
			
		||||
            now);
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,7 @@ import com.google.gerrit.server.ApprovalsUtil;
 | 
			
		||||
import com.google.gerrit.server.CmdLineParserModule;
 | 
			
		||||
import com.google.gerrit.server.IdentifiedUser;
 | 
			
		||||
import com.google.gerrit.server.PluginUser;
 | 
			
		||||
import com.google.gerrit.server.Sequences;
 | 
			
		||||
import com.google.gerrit.server.account.AccountByEmailCacheImpl;
 | 
			
		||||
import com.google.gerrit.server.account.AccountCacheImpl;
 | 
			
		||||
import com.google.gerrit.server.account.AccountControl;
 | 
			
		||||
@@ -165,6 +166,7 @@ public class GerritGlobalModule extends FactoryModule {
 | 
			
		||||
 | 
			
		||||
    bind(IdGenerator.class);
 | 
			
		||||
    bind(RulesCache.class);
 | 
			
		||||
    bind(Sequences.class);
 | 
			
		||||
    install(authModule);
 | 
			
		||||
    install(AccountByEmailCacheImpl.module());
 | 
			
		||||
    install(AccountCacheImpl.module());
 | 
			
		||||
 
 | 
			
		||||
@@ -84,6 +84,7 @@ import com.google.gerrit.server.ApprovalsUtil;
 | 
			
		||||
import com.google.gerrit.server.ChangeMessagesUtil;
 | 
			
		||||
import com.google.gerrit.server.ChangeUtil;
 | 
			
		||||
import com.google.gerrit.server.IdentifiedUser;
 | 
			
		||||
import com.google.gerrit.server.Sequences;
 | 
			
		||||
import com.google.gerrit.server.account.AccountCache;
 | 
			
		||||
import com.google.gerrit.server.account.AccountResolver;
 | 
			
		||||
import com.google.gerrit.server.change.ChangeInserter;
 | 
			
		||||
@@ -284,6 +285,7 @@ public class ReceiveCommits {
 | 
			
		||||
 | 
			
		||||
  private final IdentifiedUser user;
 | 
			
		||||
  private final ReviewDb db;
 | 
			
		||||
  private final Sequences seq;
 | 
			
		||||
  private final Provider<InternalChangeQuery> queryProvider;
 | 
			
		||||
  private final ChangeData.Factory changeDataFactory;
 | 
			
		||||
  private final ChangeUpdate.Factory updateFactory;
 | 
			
		||||
@@ -355,6 +357,7 @@ public class ReceiveCommits {
 | 
			
		||||
 | 
			
		||||
  @Inject
 | 
			
		||||
  ReceiveCommits(final ReviewDb db,
 | 
			
		||||
      final Sequences seq,
 | 
			
		||||
      final Provider<InternalChangeQuery> queryProvider,
 | 
			
		||||
      final SchemaFactory<ReviewDb> schemaFactory,
 | 
			
		||||
      final ChangeData.Factory changeDataFactory,
 | 
			
		||||
@@ -401,6 +404,7 @@ public class ReceiveCommits {
 | 
			
		||||
      final SetHashtagsOp.Factory hashtagsFactory) throws IOException {
 | 
			
		||||
    this.user = projectControl.getUser().asIdentifiedUser();
 | 
			
		||||
    this.db = db;
 | 
			
		||||
    this.seq = seq;
 | 
			
		||||
    this.queryProvider = queryProvider;
 | 
			
		||||
    this.changeDataFactory = changeDataFactory;
 | 
			
		||||
    this.updateFactory = updateFactory;
 | 
			
		||||
@@ -1733,7 +1737,7 @@ public class ReceiveCommits {
 | 
			
		||||
        throws OrmException {
 | 
			
		||||
      commit = c;
 | 
			
		||||
      change = new Change(changeKey,
 | 
			
		||||
          new Change.Id(db.nextChangeId()),
 | 
			
		||||
          new Change.Id(seq.nextChangeId()),
 | 
			
		||||
          user.getAccountId(),
 | 
			
		||||
          magicBranch.dest,
 | 
			
		||||
          TimeUtil.nowTs());
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,206 @@
 | 
			
		||||
// Copyright (C) 2016 The Android Open Source Project
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package com.google.gerrit.server.notedb;
 | 
			
		||||
 | 
			
		||||
import static com.google.common.base.Preconditions.checkArgument;
 | 
			
		||||
import static com.google.common.base.Preconditions.checkNotNull;
 | 
			
		||||
import static java.nio.charset.StandardCharsets.UTF_8;
 | 
			
		||||
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 | 
			
		||||
 | 
			
		||||
import com.google.common.annotations.VisibleForTesting;
 | 
			
		||||
import com.google.common.base.CharMatcher;
 | 
			
		||||
import com.google.common.base.Predicates;
 | 
			
		||||
import com.google.common.base.Throwables;
 | 
			
		||||
import com.google.common.primitives.Ints;
 | 
			
		||||
import com.google.common.util.concurrent.Runnables;
 | 
			
		||||
import com.google.gerrit.reviewdb.client.Project;
 | 
			
		||||
import com.google.gerrit.reviewdb.client.RefNames;
 | 
			
		||||
import com.google.gerrit.server.git.GitRepositoryManager;
 | 
			
		||||
import com.google.gwtorm.server.OrmException;
 | 
			
		||||
 | 
			
		||||
import com.github.rholder.retry.RetryException;
 | 
			
		||||
import com.github.rholder.retry.Retryer;
 | 
			
		||||
import com.github.rholder.retry.RetryerBuilder;
 | 
			
		||||
import com.github.rholder.retry.StopStrategies;
 | 
			
		||||
import com.github.rholder.retry.WaitStrategies;
 | 
			
		||||
 | 
			
		||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 | 
			
		||||
import org.eclipse.jgit.lib.ObjectId;
 | 
			
		||||
import org.eclipse.jgit.lib.ObjectInserter;
 | 
			
		||||
import org.eclipse.jgit.lib.ObjectLoader;
 | 
			
		||||
import org.eclipse.jgit.lib.Ref;
 | 
			
		||||
import org.eclipse.jgit.lib.RefUpdate;
 | 
			
		||||
import org.eclipse.jgit.lib.Repository;
 | 
			
		||||
import org.eclipse.jgit.revwalk.RevWalk;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.concurrent.Callable;
 | 
			
		||||
import java.util.concurrent.ExecutionException;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.concurrent.locks.Lock;
 | 
			
		||||
import java.util.concurrent.locks.ReentrantLock;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class for managing an incrementing sequence backed by a git repository.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * The current sequence number is stored as UTF-8 text in a blob pointed to
 | 
			
		||||
 * by a ref in the {@code refs/sequences/*} namespace. Multiple processes can
 | 
			
		||||
 * share the same sequence by incrementing the counter using normal git ref
 | 
			
		||||
 * updates. To amortize the cost of these ref updates, processes can increment
 | 
			
		||||
 * the counter by a larger number and hand out numbers from that range in memory
 | 
			
		||||
 * until they run out. This means concurrent processes will hand out somewhat
 | 
			
		||||
 * non-monotonic numbers.
 | 
			
		||||
 */
 | 
			
		||||
public class RepoSequence {
 | 
			
		||||
  @VisibleForTesting
 | 
			
		||||
  static RetryerBuilder<RefUpdate.Result> retryerBuilder() {
 | 
			
		||||
    return RetryerBuilder.<RefUpdate.Result> newBuilder()
 | 
			
		||||
        .retryIfResult(Predicates.equalTo(RefUpdate.Result.LOCK_FAILURE))
 | 
			
		||||
        .withWaitStrategy(
 | 
			
		||||
            WaitStrategies.join(
 | 
			
		||||
              WaitStrategies.exponentialWait(5, TimeUnit.SECONDS),
 | 
			
		||||
              WaitStrategies.randomWait(50, TimeUnit.MILLISECONDS)))
 | 
			
		||||
        .withStopStrategy(StopStrategies.stopAfterDelay(30, TimeUnit.SECONDS));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static Retryer<RefUpdate.Result> RETRYER = retryerBuilder().build();
 | 
			
		||||
 | 
			
		||||
  private final GitRepositoryManager repoManager;
 | 
			
		||||
  private final Project.NameKey projectName;
 | 
			
		||||
  private final String refName;
 | 
			
		||||
  private final int start;
 | 
			
		||||
  private final int batchSize;
 | 
			
		||||
  private final Runnable afterReadRef;
 | 
			
		||||
  private final Retryer<RefUpdate.Result> retryer;
 | 
			
		||||
 | 
			
		||||
  // Protects all non-final fields.
 | 
			
		||||
  private final Lock counterLock;
 | 
			
		||||
 | 
			
		||||
  private int limit;
 | 
			
		||||
  private int counter;
 | 
			
		||||
 | 
			
		||||
  @VisibleForTesting
 | 
			
		||||
  int acquireCount;
 | 
			
		||||
 | 
			
		||||
  public RepoSequence(GitRepositoryManager repoManager,
 | 
			
		||||
      Project.NameKey projectName, String name, int start, int batchSize) {
 | 
			
		||||
    this(repoManager, projectName, name, start, batchSize,
 | 
			
		||||
        Runnables.doNothing(), RETRYER);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @VisibleForTesting
 | 
			
		||||
  RepoSequence(GitRepositoryManager repoManager, Project.NameKey projectName,
 | 
			
		||||
      String name, int start, int batchSize, Runnable afterReadRef,
 | 
			
		||||
      Retryer<RefUpdate.Result> retryer) {
 | 
			
		||||
    this.repoManager = checkNotNull(repoManager, "repoManager");
 | 
			
		||||
    this.projectName = checkNotNull(projectName, "projectName");
 | 
			
		||||
    this.refName = RefNames.REFS_SEQUENCES + checkNotNull(name, "name");
 | 
			
		||||
    this.start = start;
 | 
			
		||||
    checkArgument(batchSize > 0, "expected batchSize > 0, got: %s", batchSize);
 | 
			
		||||
    this.batchSize = batchSize;
 | 
			
		||||
    this.afterReadRef = checkNotNull(afterReadRef, "afterReadRef");
 | 
			
		||||
    this.retryer = checkNotNull(retryer, "retryer");
 | 
			
		||||
 | 
			
		||||
    counterLock = new ReentrantLock(true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public int next() throws OrmException {
 | 
			
		||||
    counterLock.lock();
 | 
			
		||||
    try {
 | 
			
		||||
      if (counter >= limit) {
 | 
			
		||||
        acquire();
 | 
			
		||||
      }
 | 
			
		||||
      return counter++;
 | 
			
		||||
    } finally {
 | 
			
		||||
      counterLock.unlock();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void acquire() throws OrmException {
 | 
			
		||||
    try (Repository repo = repoManager.openRepository(projectName);
 | 
			
		||||
        RevWalk rw = new RevWalk(repo)) {
 | 
			
		||||
      TryAcquire attempt = new TryAcquire(repo, rw);
 | 
			
		||||
      RefUpdate.Result result = retryer.call(attempt);
 | 
			
		||||
      if (result != RefUpdate.Result.NEW && result != RefUpdate.Result.FORCED) {
 | 
			
		||||
        throw new OrmException("failed to update " + refName + ": " + result);
 | 
			
		||||
      }
 | 
			
		||||
      counter = attempt.next;
 | 
			
		||||
      limit = counter + batchSize;
 | 
			
		||||
      acquireCount++;
 | 
			
		||||
    } catch (ExecutionException | RetryException e) {
 | 
			
		||||
      Throwables.propagateIfInstanceOf(e.getCause(), OrmException.class);
 | 
			
		||||
      throw new OrmException(e);
 | 
			
		||||
    } catch (IOException e) {
 | 
			
		||||
      throw new OrmException(e);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private class TryAcquire implements Callable<RefUpdate.Result> {
 | 
			
		||||
    private final Repository repo;
 | 
			
		||||
    private final RevWalk rw;
 | 
			
		||||
 | 
			
		||||
    private int next;
 | 
			
		||||
 | 
			
		||||
    private TryAcquire(Repository repo, RevWalk rw) {
 | 
			
		||||
      this.repo = repo;
 | 
			
		||||
      this.rw = rw;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public RefUpdate.Result call() throws Exception {
 | 
			
		||||
      Ref ref = repo.exactRef(refName);
 | 
			
		||||
      afterReadRef.run();
 | 
			
		||||
      ObjectId oldId;
 | 
			
		||||
      if (ref == null) {
 | 
			
		||||
        oldId = ObjectId.zeroId();
 | 
			
		||||
        next = start;
 | 
			
		||||
      } else {
 | 
			
		||||
        oldId = ref.getObjectId();
 | 
			
		||||
        next = parse(oldId);
 | 
			
		||||
      }
 | 
			
		||||
      return store(oldId, next + batchSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private int parse(ObjectId id) throws IOException, OrmException {
 | 
			
		||||
      ObjectLoader ol = rw.getObjectReader().open(id, OBJ_BLOB);
 | 
			
		||||
      if (ol.getType() != OBJ_BLOB) {
 | 
			
		||||
        // In theory this should be thrown by open but not all implementations
 | 
			
		||||
        // may do it properly (certainly InMemoryRepository doesn't).
 | 
			
		||||
        throw new IncorrectObjectTypeException(id, OBJ_BLOB);
 | 
			
		||||
      }
 | 
			
		||||
      String str = CharMatcher.WHITESPACE.trimFrom(
 | 
			
		||||
          new String(ol.getCachedBytes(), UTF_8));
 | 
			
		||||
      Integer val = Ints.tryParse(str);
 | 
			
		||||
      if (val == null) {
 | 
			
		||||
        throw new OrmException(
 | 
			
		||||
            "invalid value in " + refName + " blob at " + id.name());
 | 
			
		||||
      }
 | 
			
		||||
      return val;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private RefUpdate.Result store(ObjectId oldId, int val) throws IOException {
 | 
			
		||||
      ObjectId newId;
 | 
			
		||||
      try (ObjectInserter ins = repo.newObjectInserter()) {
 | 
			
		||||
        newId = ins.insert(OBJ_BLOB, Integer.toString(val).getBytes(UTF_8));
 | 
			
		||||
        ins.flush();
 | 
			
		||||
      }
 | 
			
		||||
      RefUpdate ru = repo.updateRef(refName);
 | 
			
		||||
      ru.setExpectedOldObjectId(oldId);
 | 
			
		||||
      ru.setNewObjectId(newId);
 | 
			
		||||
      ru.setForceUpdate(true); // Required for non-commitish updates.
 | 
			
		||||
      return ru.update(rw);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,235 @@
 | 
			
		||||
// Copyright (C) 2016 The Android Open Source Project
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package com.google.gerrit.server.notedb;
 | 
			
		||||
 | 
			
		||||
import static com.google.common.truth.Truth.assertThat;
 | 
			
		||||
import static java.nio.charset.StandardCharsets.UTF_8;
 | 
			
		||||
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 | 
			
		||||
import static org.junit.Assert.fail;
 | 
			
		||||
 | 
			
		||||
import com.google.common.util.concurrent.Runnables;
 | 
			
		||||
import com.google.gerrit.reviewdb.client.Project;
 | 
			
		||||
import com.google.gerrit.reviewdb.client.RefNames;
 | 
			
		||||
import com.google.gerrit.testutil.InMemoryRepositoryManager;
 | 
			
		||||
import com.google.gwtorm.server.OrmException;
 | 
			
		||||
 | 
			
		||||
import com.github.rholder.retry.BlockStrategy;
 | 
			
		||||
import com.github.rholder.retry.Retryer;
 | 
			
		||||
import com.github.rholder.retry.RetryerBuilder;
 | 
			
		||||
import com.github.rholder.retry.StopStrategies;
 | 
			
		||||
 | 
			
		||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 | 
			
		||||
import org.eclipse.jgit.junit.TestRepository;
 | 
			
		||||
import org.eclipse.jgit.lib.ObjectId;
 | 
			
		||||
import org.eclipse.jgit.lib.ObjectInserter;
 | 
			
		||||
import org.eclipse.jgit.lib.RefUpdate;
 | 
			
		||||
import org.eclipse.jgit.lib.Repository;
 | 
			
		||||
import org.eclipse.jgit.revwalk.RevWalk;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.Rule;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.junit.rules.ExpectedException;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.concurrent.ExecutionException;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicBoolean;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger;
 | 
			
		||||
 | 
			
		||||
public class RepoSequenceTest {
 | 
			
		||||
  private static final Retryer<RefUpdate.Result> RETRYER =
 | 
			
		||||
      RepoSequence.retryerBuilder().withBlockStrategy(new BlockStrategy() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void block(long sleepTime) {
 | 
			
		||||
          // Don't sleep in tests.
 | 
			
		||||
        }
 | 
			
		||||
      }).build();
 | 
			
		||||
 | 
			
		||||
  @Rule
 | 
			
		||||
  public ExpectedException exception = ExpectedException.none();
 | 
			
		||||
 | 
			
		||||
  private InMemoryRepositoryManager repoManager;
 | 
			
		||||
  private Project.NameKey project;
 | 
			
		||||
 | 
			
		||||
  @Before
 | 
			
		||||
  public void setUp() throws Exception {
 | 
			
		||||
    repoManager = new InMemoryRepositoryManager();
 | 
			
		||||
    project = new Project.NameKey("project");
 | 
			
		||||
    repoManager.createRepository(project);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void oneCaller() throws Exception {
 | 
			
		||||
    int max = 20;
 | 
			
		||||
    for (int batchSize = 1; batchSize <= 10; batchSize++) {
 | 
			
		||||
      String name = "batch-size-" + batchSize;
 | 
			
		||||
      RepoSequence s = newSequence(name, 1, batchSize);
 | 
			
		||||
      for (int i = 1; i <= max; i++) {
 | 
			
		||||
        try {
 | 
			
		||||
          assertThat(s.next()).named("next for " + name).isEqualTo(i);
 | 
			
		||||
        } catch (OrmException e) {
 | 
			
		||||
          throw new AssertionError(
 | 
			
		||||
              "failed batchSize=" + batchSize + ", i=" + i, e);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      assertThat(s.acquireCount)
 | 
			
		||||
          .named("acquireCount for " + name)
 | 
			
		||||
          .isEqualTo(divCeil(max, batchSize));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void twoCallers() throws Exception {
 | 
			
		||||
    RepoSequence s1 = newSequence("id", 1, 3);
 | 
			
		||||
    RepoSequence s2 = newSequence("id", 1, 3);
 | 
			
		||||
 | 
			
		||||
    // s1 acquires 1-3; s2 acquires 4-6.
 | 
			
		||||
    assertThat(s1.next()).isEqualTo(1);
 | 
			
		||||
    assertThat(s2.next()).isEqualTo(4);
 | 
			
		||||
    assertThat(s1.next()).isEqualTo(2);
 | 
			
		||||
    assertThat(s2.next()).isEqualTo(5);
 | 
			
		||||
    assertThat(s1.next()).isEqualTo(3);
 | 
			
		||||
    assertThat(s2.next()).isEqualTo(6);
 | 
			
		||||
 | 
			
		||||
    // s2 acquires 7-9; s1 acquires 10-12.
 | 
			
		||||
    assertThat(s2.next()).isEqualTo(7);
 | 
			
		||||
    assertThat(s1.next()).isEqualTo(10);
 | 
			
		||||
    assertThat(s2.next()).isEqualTo(8);
 | 
			
		||||
    assertThat(s1.next()).isEqualTo(11);
 | 
			
		||||
    assertThat(s2.next()).isEqualTo(9);
 | 
			
		||||
    assertThat(s1.next()).isEqualTo(12);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void populateEmptyRefWithStartValue() throws Exception {
 | 
			
		||||
    RepoSequence s = newSequence("id", 1234, 10);
 | 
			
		||||
    assertThat(s.next()).isEqualTo(1234);
 | 
			
		||||
    assertThat(readBlob("id")).isEqualTo("1244");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void startIsIgnoredIfRefIsPresent() throws Exception {
 | 
			
		||||
    writeBlob("id", "1234");
 | 
			
		||||
    RepoSequence s = newSequence("id", 3456, 10);
 | 
			
		||||
    assertThat(s.next()).isEqualTo(1234);
 | 
			
		||||
    assertThat(readBlob("id")).isEqualTo("1244");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void retryOnLockFailure() throws Exception {
 | 
			
		||||
    // Seed existing ref value.
 | 
			
		||||
    writeBlob("id", "1");
 | 
			
		||||
 | 
			
		||||
    final AtomicBoolean doneBgUpdate = new AtomicBoolean(false);
 | 
			
		||||
    Runnable bgUpdate = new Runnable() {
 | 
			
		||||
      @Override
 | 
			
		||||
      public void run() {
 | 
			
		||||
        if (!doneBgUpdate.getAndSet(true)) {
 | 
			
		||||
          writeBlob("id", "1234");
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    RepoSequence s = newSequence("id", 1, 10, bgUpdate, RETRYER);
 | 
			
		||||
    assertThat(doneBgUpdate.get()).isFalse();
 | 
			
		||||
    assertThat(s.next()).isEqualTo(1234);
 | 
			
		||||
    // Single acquire call that results in 2 ref reads.
 | 
			
		||||
    assertThat(s.acquireCount).isEqualTo(1);
 | 
			
		||||
    assertThat(doneBgUpdate.get()).isTrue();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void failOnInvalidValue() throws Exception {
 | 
			
		||||
    ObjectId id = writeBlob("id", "not a number");
 | 
			
		||||
    exception.expect(OrmException.class);
 | 
			
		||||
    exception.expectMessage(
 | 
			
		||||
        "invalid value in refs/sequences/id blob at " + id.name());
 | 
			
		||||
    newSequence("id", 1, 3).next();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void failOnWrongType() throws Exception {
 | 
			
		||||
    try (Repository repo = repoManager.openRepository(project)) {
 | 
			
		||||
      TestRepository<Repository> tr = new TestRepository<>(repo);
 | 
			
		||||
      tr.branch(RefNames.REFS_SEQUENCES + "id").commit().create();
 | 
			
		||||
      try {
 | 
			
		||||
        newSequence("id", 1, 3).next();
 | 
			
		||||
        fail();
 | 
			
		||||
      } catch (OrmException e) {
 | 
			
		||||
        assertThat(e.getCause()).isInstanceOf(ExecutionException.class);
 | 
			
		||||
        assertThat(e.getCause().getCause())
 | 
			
		||||
            .isInstanceOf(IncorrectObjectTypeException.class);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void failAfterRetryerGivesUp() throws Exception {
 | 
			
		||||
    final AtomicInteger bgCounter = new AtomicInteger(1234);
 | 
			
		||||
    Runnable bgUpdate = new Runnable() {
 | 
			
		||||
      @Override
 | 
			
		||||
      public void run() {
 | 
			
		||||
        writeBlob("id", Integer.toString(bgCounter.getAndAdd(1000)));
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    RepoSequence s = newSequence(
 | 
			
		||||
        "id", 1, 10, bgUpdate,
 | 
			
		||||
        RetryerBuilder.<RefUpdate.Result> newBuilder()
 | 
			
		||||
          .withStopStrategy(StopStrategies.stopAfterAttempt(3))
 | 
			
		||||
          .build());
 | 
			
		||||
    exception.expect(OrmException.class);
 | 
			
		||||
    exception.expectMessage("failed to update refs/sequences/id: LOCK_FAILURE");
 | 
			
		||||
    s.next();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private RepoSequence newSequence(String name, int start, int batchSize) {
 | 
			
		||||
    return newSequence(
 | 
			
		||||
        name, start, batchSize, Runnables.doNothing(), RETRYER);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private RepoSequence newSequence(String name, int start, int batchSize,
 | 
			
		||||
      Runnable afterReadRef, Retryer<RefUpdate.Result> retryer) {
 | 
			
		||||
    return new RepoSequence(
 | 
			
		||||
        repoManager, project, name, start, batchSize, afterReadRef, retryer);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private ObjectId writeBlob(String sequenceName, String value) {
 | 
			
		||||
    String refName = RefNames.REFS_SEQUENCES + sequenceName;
 | 
			
		||||
    try (Repository repo = repoManager.openRepository(project);
 | 
			
		||||
        ObjectInserter ins = repo.newObjectInserter()) {
 | 
			
		||||
      ObjectId newId = ins.insert(OBJ_BLOB, value.getBytes(UTF_8));
 | 
			
		||||
      ins.flush();
 | 
			
		||||
      RefUpdate ru = repo.updateRef(refName);
 | 
			
		||||
      ru.setNewObjectId(newId);
 | 
			
		||||
      assertThat(ru.forceUpdate())
 | 
			
		||||
          .isAnyOf(RefUpdate.Result.NEW, RefUpdate.Result.FORCED);
 | 
			
		||||
      return newId;
 | 
			
		||||
    } catch (IOException e) {
 | 
			
		||||
      throw new RuntimeException(e);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private String readBlob(String sequenceName) throws Exception {
 | 
			
		||||
    String refName = RefNames.REFS_SEQUENCES + sequenceName;
 | 
			
		||||
    try (Repository repo = repoManager.openRepository(project);
 | 
			
		||||
        RevWalk rw = new RevWalk(repo)) {
 | 
			
		||||
      ObjectId id = repo.exactRef(refName).getObjectId();
 | 
			
		||||
      return new String(rw.getObjectReader().open(id).getCachedBytes(), UTF_8);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static long divCeil(float a, float b) {
 | 
			
		||||
    return Math.round(Math.ceil(a / b));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -51,6 +51,7 @@ import com.google.gerrit.reviewdb.client.Project;
 | 
			
		||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
			
		||||
import com.google.gerrit.server.CurrentUser;
 | 
			
		||||
import com.google.gerrit.server.IdentifiedUser;
 | 
			
		||||
import com.google.gerrit.server.Sequences;
 | 
			
		||||
import com.google.gerrit.server.account.AccountManager;
 | 
			
		||||
import com.google.gerrit.server.account.AuthRequest;
 | 
			
		||||
import com.google.gerrit.server.account.GroupCache;
 | 
			
		||||
@@ -104,24 +105,25 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Inject protected AccountManager accountManager;
 | 
			
		||||
  @Inject protected AddMembers addMembers;
 | 
			
		||||
  @Inject protected BatchUpdate.Factory updateFactory;
 | 
			
		||||
  @Inject protected ChangeInserter.Factory changeFactory;
 | 
			
		||||
  @Inject protected PatchSetInserter.Factory patchSetFactory;
 | 
			
		||||
  @Inject protected ChangeControl.GenericFactory changeControlFactory;
 | 
			
		||||
  @Inject protected ChangeInserter.Factory changeFactory;
 | 
			
		||||
  @Inject protected ChangeQueryBuilder queryBuilder;
 | 
			
		||||
  @Inject protected GerritApi gApi;
 | 
			
		||||
  @Inject protected GroupCache groupCache;
 | 
			
		||||
  @Inject protected IdentifiedUser.GenericFactory userFactory;
 | 
			
		||||
  @Inject protected IndexCollection indexes;
 | 
			
		||||
  @Inject protected InMemoryDatabase schemaFactory;
 | 
			
		||||
  @Inject protected InMemoryRepositoryManager repoManager;
 | 
			
		||||
  @Inject protected InternalChangeQuery internalChangeQuery;
 | 
			
		||||
  @Inject protected NotesMigration notesMigration;
 | 
			
		||||
  @Inject protected PatchSetInserter.Factory patchSetFactory;
 | 
			
		||||
  @Inject protected ProjectControl.GenericFactory projectControlFactory;
 | 
			
		||||
  @Inject protected ChangeQueryBuilder queryBuilder;
 | 
			
		||||
  @Inject protected QueryProcessor queryProcessor;
 | 
			
		||||
  @Inject protected SchemaCreator schemaCreator;
 | 
			
		||||
  @Inject protected Sequences seq;
 | 
			
		||||
  @Inject protected ThreadLocalRequestContext requestContext;
 | 
			
		||||
  @Inject protected GroupCache groupCache;
 | 
			
		||||
  @Inject protected AddMembers addMembers;
 | 
			
		||||
 | 
			
		||||
  protected LifecycleManager lifecycle;
 | 
			
		||||
  protected ReviewDb db;
 | 
			
		||||
@@ -1396,7 +1398,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
 | 
			
		||||
    Project.NameKey project = new Project.NameKey(
 | 
			
		||||
        repo.getRepository().getDescription().getRepositoryName());
 | 
			
		||||
 | 
			
		||||
    Change.Id id = new Change.Id(db.nextChangeId());
 | 
			
		||||
    Change.Id id = new Change.Id(seq.nextChangeId());
 | 
			
		||||
    if (key == null) {
 | 
			
		||||
      key = "I" + Hashing.sha1().newHasher()
 | 
			
		||||
          .putInt(id.get())
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								lib/BUCK
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								lib/BUCK
									
									
									
									
									
								
							@@ -68,6 +68,26 @@ maven_jar(
 | 
			
		||||
  license = 'Apache2.0',
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
maven_jar(
 | 
			
		||||
  name = 'guava-retrying',
 | 
			
		||||
  id = 'com.github.rholder:guava-retrying:2.0.0',
 | 
			
		||||
  sha1 = '974bc0a04a11cc4806f7c20a34703bd23c34e7f4',
 | 
			
		||||
  license = 'Apache2.0',
 | 
			
		||||
  deps = [':jsr305'],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
maven_jar(
 | 
			
		||||
  name = 'jsr305',
 | 
			
		||||
  id = 'com.google.code.findbugs:jsr305:2.0.2',
 | 
			
		||||
  sha1 = '516c03b21d50a644d538de0f0369c620989cd8f0',
 | 
			
		||||
  license = 'Apache2.0',
 | 
			
		||||
  attach_source = False,
 | 
			
		||||
  # Whitelist lib targets that have jsr305 as a dependency. Generally speaking
 | 
			
		||||
  # Gerrit core should not depend on these annotations, and instead use
 | 
			
		||||
  # equivalent annotations in com.google.gerrit.common.
 | 
			
		||||
  visibility = ['//lib:guava-retrying'],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
maven_jar(
 | 
			
		||||
  name = 'velocity',
 | 
			
		||||
  id = 'org.apache.velocity:velocity:1.7',
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user