Merge "Add config option setting default primary storage for new changes"

This commit is contained in:
Dave Borowitz
2017-01-19 21:09:03 +00:00
committed by Gerrit Code Review
14 changed files with 247 additions and 56 deletions

View File

@@ -25,6 +25,8 @@ import com.google.gerrit.pgm.Init;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.AsyncReceiveCommits;
import com.google.gerrit.server.ssh.NoSshModule;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.SocketUtil;
import com.google.gerrit.server.util.SystemLog;
import com.google.gerrit.testutil.FakeEmailSender;
@@ -62,6 +64,7 @@ public class GerritServer {
static Description forTestClass(org.junit.runner.Description testDesc,
String configName) {
return new AutoValue_GerritServer_Description(
testDesc,
configName,
true, // @UseLocalDisk is only valid on methods.
!has(NoHttpd.class, testDesc.getTestClass()),
@@ -75,6 +78,7 @@ public class GerritServer {
static Description forTestMethod(org.junit.runner.Description testDesc,
String configName) {
return new AutoValue_GerritServer_Description(
testDesc,
configName,
testDesc.getAnnotation(UseLocalDisk.class) == null,
testDesc.getAnnotation(NoHttpd.class) == null
@@ -97,6 +101,7 @@ public class GerritServer {
return false;
}
abstract org.junit.runner.Description testDescription();
@Nullable abstract String configName();
abstract boolean memory();
abstract boolean httpd();
@@ -297,10 +302,7 @@ public class GerritServer {
void stop() throws Exception {
try {
if (NoteDbMode.get().equals(NoteDbMode.CHECK)) {
testInjector.getInstance(NoteDbChecker.class)
.rebuildAndCheckAllChanges();
}
checkNoteDbState();
} finally {
daemon.getLifecycleManager().stop();
if (daemonService != null) {
@@ -312,6 +314,23 @@ public class GerritServer {
}
}
private void checkNoteDbState() throws Exception {
NoteDbMode mode = NoteDbMode.get();
if (mode != NoteDbMode.CHECK && mode != NoteDbMode.PRIMARY) {
return;
}
NoteDbChecker checker = testInjector.getInstance(NoteDbChecker.class);
OneOffRequestContext oneOffRequestContext =
testInjector.getInstance(OneOffRequestContext.class);
try (ManualRequestContext ctx = oneOffRequestContext.open()) {
if (mode == NoteDbMode.CHECK) {
checker.rebuildAndCheckAllChanges();
} else if (mode == NoteDbMode.PRIMARY) {
checker.assertNoReviewDbChanges(desc.testDescription());
}
}
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).addValue(desc).toString();

View File

@@ -99,6 +99,7 @@ import com.google.gerrit.server.git.BatchUpdate;
import com.google.gerrit.server.git.ChangeMessageModifier;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.Util;
import com.google.gerrit.testutil.FakeEmailSender.Message;
@@ -1011,6 +1012,8 @@ public class ChangeIT extends AbstractDaemonTest {
public void addReviewerWithNoteDbWhenDummyApprovalInReviewDbExists()
throws Exception {
assume().that(notesMigration.enabled()).isTrue();
assume().that(notesMigration.changePrimaryStorage())
.isEqualTo(PrimaryStorage.REVIEW_DB);
PushOneCommit.Result r = createChange();

View File

@@ -43,6 +43,7 @@ import com.google.gerrit.server.git.SearchingChangeCacheImpl;
import com.google.gerrit.server.git.TagCache;
import com.google.gerrit.server.git.VisibleRefFilter;
import com.google.gerrit.server.notedb.ChangeNoteUtil;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.Util;
import com.google.gerrit.server.query.change.ChangeData;
@@ -447,8 +448,6 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Test
public void receivePackOmitsMissingObject() throws Exception {
// Use the tactic from ConsistencyCheckerIT to insert a new patch set with a
// missing object.
String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
try (Repository repo = repoManager.openRepository(project)) {
TestRepository<?> tr = new TestRepository<>(repo);
@@ -457,9 +456,11 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
PatchSet.Id psId = new PatchSet.Id(c3.getId(), 2);
c.setCurrentPatchSet(psId, subject, c.getOriginalSubject());
if (notesMigration.changePrimaryStorage() == PrimaryStorage.REVIEW_DB) {
PatchSet ps = TestChanges.newPatchSet(psId, rev, admin.getId());
db.patchSets().insert(Collections.singleton(ps));
db.changes().update(Collections.singleton(c));
}
if (notesMigration.commitChangeWrites()) {
PersonIdent committer = serverIdent.get();

View File

@@ -50,6 +50,7 @@ import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
import com.google.gerrit.server.git.BatchUpdate.RepoContext;
import com.google.gerrit.server.git.validators.CommitValidators;
import com.google.gerrit.server.notedb.ChangeNoteUtil;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.testutil.InMemoryRepositoryManager;
import com.google.gerrit.testutil.TestChanges;
@@ -297,8 +298,10 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
PatchSet ps = newPatchSet(psId, rev, adminId);
if (notesMigration.changePrimaryStorage() == PrimaryStorage.REVIEW_DB) {
db.changes().insert(singleton(c));
db.patchSets().insert(singleton(ps));
}
addNoteDbCommit(
c.getId(),
"Create change\n"
@@ -824,10 +827,12 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
Change c = new Change(ctl.getChange());
PatchSet.Id psId = nextPatchSetId(ctl);
c.setCurrentPatchSet(psId, subject, c.getOriginalSubject());
PatchSet ps = newPatchSet(psId, rev, adminId);
if (PrimaryStorage.of(c) == PrimaryStorage.REVIEW_DB) {
db.patchSets().insert(singleton(ps));
db.changes().update(singleton(c));
}
addNoteDbCommit(
c.getId(),

View File

@@ -1049,18 +1049,45 @@ public class BatchUpdate implements AutoCloseable {
private ChangeContext newChangeContext(ReviewDb db, Repository repo,
RevWalk rw, Change.Id id) throws OrmException {
Change c = newChanges.get(id);
if (c == null) {
boolean isNew = c != null;
PrimaryStorage defaultStorage = notesMigration.changePrimaryStorage();
if (isNew) {
// New change: populate noteDbState.
checkState(c.getNoteDbState() == null,
"noteDbState should not be filled in by callers");
if (defaultStorage == PrimaryStorage.NOTE_DB) {
c.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
}
} else {
// Existing change.
c = ChangeNotes.readOneReviewDbChange(db, id);
if (c == null) {
if (defaultStorage == PrimaryStorage.REVIEW_DB) {
logDebug("Failed to get change {} from unwrapped db", id);
throw new NoSuchChangeException(id);
}
// Not in ReviewDb, but new changes are created with default primary
// storage as NOTE_DB, so we can assume that a missing change is
// NoteDb primary. Pass a synthetic change into ChangeNotes.Factory,
// which lets ChangeNotes take care of the existence check.
//
// TODO(dborowitz): This assumption is potentially risky, because
// it means once we turn this option on and start creating changes
// without writing anything to ReviewDb, we can't turn this option
// back off without making those changes inaccessible. The problem
// is we have no way of distinguishing a change that only exists in
// NoteDb because it only ever existed in NoteDb, from a change that
// only exists in NoteDb because it used to exist in ReviewDb and
// deleting from ReviewDb succeeded but deleting from NoteDb failed.
//
// TODO(dborowitz): We actually still have that problem anyway. Maybe
// we need a cutoff timestamp? Or maybe we need to start leaving
// tombstones in ReviewDb?
c = ChangeNotes.Factory.newNoteDbOnlyChange(project, id);
}
NoteDbChangeState.checkNotReadOnly(c, skewMs);
}
// Pass in preloaded change to controlFor, to avoid:
// - reading from a db that does not belong to this update
// - attempting to read a change that doesn't exist yet
ChangeNotes notes = changeNotesFactory.createForBatchUpdate(c);
ChangeNotes notes = changeNotesFactory.createForBatchUpdate(c, !isNew);
ChangeControl ctl = changeControlFactory.controlFor(notes, user);
return new ChangeContext(ctl, new BatchUpdateReviewDb(db), repo, rw);
}

View File

@@ -29,6 +29,7 @@ import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -174,7 +175,20 @@ public abstract class AbstractChangeNotes<T> {
return ref != null ? ref.getObjectId() : null;
}
protected LoadHandle openHandle(Repository repo) throws IOException {
/**
* Open a handle for reading this entity from a repository.
* <p>
* Implementations may override this method to provide auto-rebuilding
* behavior.
*
* @param repo open repository.
* @return handle for reading the entity.
*
* @throws NoSuchChangeException change does not exist.
* @throws IOException a repo-level error occurred.
*/
protected LoadHandle openHandle(Repository repo)
throws NoSuchChangeException, IOException {
return openHandle(repo, readRef(repo));
}
@@ -182,7 +196,7 @@ public abstract class AbstractChangeNotes<T> {
return LoadHandle.create(ChangeNotesCommit.newRevWalk(repo), id);
}
public T reload() throws OrmException {
public T reload() throws NoSuchChangeException, OrmException {
loaded = false;
return load();
}
@@ -215,7 +229,7 @@ public abstract class AbstractChangeNotes<T> {
/** Set up the metadata, parsing any state from the loaded revision. */
protected abstract void onLoad(LoadHandle handle)
throws IOException, ConfigInvalidException;
throws NoSuchChangeException, IOException, ConfigInvalidException;
@SuppressWarnings("unchecked")
protected final T self() {

View File

@@ -36,6 +36,7 @@ import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.metrics.Timer1;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.Comment;
@@ -124,7 +125,14 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
public ChangeNotes createChecked(ReviewDb db, Project.NameKey project,
Change.Id changeId) throws OrmException {
Change change = readOneReviewDbChange(db, changeId);
if (change == null || !change.getProject().equals(project)) {
if (change == null) {
if (!args.migration.readChanges()) {
throw new NoSuchChangeException(changeId);
}
// Change isn't in ReviewDb, but its primary storage might be in NoteDb.
// Prepopulate the change exists with proper noteDbState field.
change = newNoteDbOnlyChange(project, changeId);
} else if (!change.getProject().equals(project)) {
throw new NoSuchChangeException(changeId);
}
return new ChangeNotes(args, change).load();
@@ -144,16 +152,33 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return changes.get(0).notes();
}
public static Change newNoteDbOnlyChange(
Project.NameKey project, Change.Id changeId) {
Change change = new Change(
null, changeId, null,
new Branch.NameKey(project, "INVALID_NOTE_DB_ONLY"),
null);
change.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
return change;
}
private Change loadChangeFromDb(ReviewDb db, Project.NameKey project,
Change.Id changeId) throws OrmException {
Change change = readOneReviewDbChange(db, changeId);
checkArgument(project != null, "project is required");
checkNotNull(change,
"change %s not found in ReviewDb", changeId);
Change change = readOneReviewDbChange(db, changeId);
if (change == null && args.migration.readChanges()) {
// Change isn't in ReviewDb, but its primary storage might be in NoteDb.
// Prepopulate the change exists with proper noteDbState field.
change = newNoteDbOnlyChange(project, changeId);
} else {
checkNotNull(change, "change %s not found in ReviewDb", changeId);
checkArgument(change.getProject().equals(project),
"passed project %s when creating ChangeNotes for %s, but actual"
+ " project is %s",
project, changeId, change.getProject());
}
// TODO: Throw NoSuchChangeException when the change is not found in the
// database
return change;
@@ -168,7 +193,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
public ChangeNotes createWithAutoRebuildingDisabled(ReviewDb db,
Project.NameKey project, Change.Id changeId) throws OrmException {
return new ChangeNotes(
args, loadChangeFromDb(db, project, changeId), false, null).load();
args, loadChangeFromDb(db, project, changeId), true, false, null).load();
}
/**
@@ -183,13 +208,14 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return new ChangeNotes(args, change);
}
public ChangeNotes createForBatchUpdate(Change change) throws OrmException {
return new ChangeNotes(args, change, false, null).load();
public ChangeNotes createForBatchUpdate(Change change, boolean shouldExist)
throws OrmException {
return new ChangeNotes(args, change, shouldExist, false, null).load();
}
public ChangeNotes createWithAutoRebuildingDisabled(Change change,
RefCache refs) throws OrmException {
return new ChangeNotes(args, change, false, refs).load();
return new ChangeNotes(args, change, true, false, refs).load();
}
// TODO(ekempin): Remove when database backend is deleted
@@ -302,13 +328,18 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
Project.NameKey project) throws OrmException, IOException {
Set<Change.Id> ids = scan(repo);
List<ChangeNotes> changeNotes = new ArrayList<>(ids.size());
PrimaryStorage defaultStorage = args.migration.changePrimaryStorage();
for (Change.Id id : ids) {
Change change = readOneReviewDbChange(db, id);
if (change == null) {
if (defaultStorage == PrimaryStorage.REVIEW_DB) {
log.warn("skipping change {} found in project {} " +
"but not in ReviewDb",
id, project);
continue;
}
// TODO(dborowitz): See discussion in BatchUpdate#newChangeContext.
change = newNoteDbOnlyChange(project, id);
} else if (!change.getProject().equals(project)) {
log.error(
"skipping change {} found in project {} " +
@@ -337,6 +368,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
}
}
private final boolean shouldExist;
private final RefCache refs;
private Change change;
@@ -358,13 +390,14 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
@VisibleForTesting
public ChangeNotes(Args args, Change change) {
this(args, change, true, null);
this(args, change, true, true, null);
}
private ChangeNotes(Args args, Change change, boolean autoRebuild,
@Nullable RefCache refs) {
private ChangeNotes(Args args, Change change, boolean shouldExist,
boolean autoRebuild, @Nullable RefCache refs) {
super(args, change.getId(), PrimaryStorage.of(change), autoRebuild);
this.change = new Change(change);
this.shouldExist = shouldExist;
this.refs = refs;
}
@@ -555,9 +588,14 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
@Override
protected void onLoad(LoadHandle handle)
throws IOException, ConfigInvalidException {
throws NoSuchChangeException, IOException, ConfigInvalidException {
ObjectId rev = handle.id();
if (rev == null) {
if (args.migration.readChanges()
&& PrimaryStorage.of(change) == PrimaryStorage.NOTE_DB
&& shouldExist) {
throw new NoSuchChangeException(getChangeId());
}
loadDefaults();
return;
}
@@ -587,12 +625,17 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
}
@Override
protected LoadHandle openHandle(Repository repo) throws IOException {
protected LoadHandle openHandle(Repository repo)
throws NoSuchChangeException, IOException {
if (autoRebuild) {
NoteDbChangeState state = NoteDbChangeState.parse(change);
ObjectId id = readRef(repo);
if (state == null && id == null) {
if (id == null) {
if (state == null) {
return super.openHandle(repo, id);
} else if (shouldExist) {
throw new NoSuchChangeException(getChangeId());
}
}
RefCache refs = this.refs != null ? this.refs : new RepoRefCache(repo);
if (!NoteDbChangeState.isChangeUpToDate(state, refs, getChangeId())) {

View File

@@ -20,6 +20,7 @@ import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -49,9 +50,11 @@ public class ConfigNotesMigration extends NotesMigration {
}
private static final String NOTE_DB = "noteDb";
private static final String PRIMARY_STORAGE = "primaryStorage";
private static final String READ = "read";
private static final String WRITE = "write";
private static final String SEQUENCE = "sequence";
private static final String WRITE = "write";
private static void checkConfig(Config cfg) {
Set<String> keys = new HashSet<>();
@@ -81,6 +84,7 @@ public class ConfigNotesMigration extends NotesMigration {
private final boolean writeChanges;
private final boolean readChanges;
private final boolean readChangeSequence;
private final PrimaryStorage changePrimaryStorage;
private final boolean writeAccounts;
private final boolean readAccounts;
@@ -98,6 +102,9 @@ public class ConfigNotesMigration extends NotesMigration {
// NoteDb. This decision for the default may be reevaluated later.
readChangeSequence = cfg.getBoolean(NOTE_DB, CHANGES.key(), SEQUENCE, false);
changePrimaryStorage = cfg.getEnum(
NOTE_DB, CHANGES.key(), PRIMARY_STORAGE, PrimaryStorage.REVIEW_DB);
writeAccounts = cfg.getBoolean(NOTE_DB, ACCOUNTS.key(), WRITE, false);
readAccounts = cfg.getBoolean(NOTE_DB, ACCOUNTS.key(), READ, false);
}
@@ -117,6 +124,11 @@ public class ConfigNotesMigration extends NotesMigration {
return readChangeSequence;
}
@Override
public PrimaryStorage changePrimaryStorage() {
return changePrimaryStorage;
}
@Override
public boolean writeAccounts() {
return writeAccounts;

View File

@@ -188,7 +188,8 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
}
@Override
protected LoadHandle openHandle(Repository repo) throws IOException {
protected LoadHandle openHandle(Repository repo)
throws NoSuchChangeException, IOException {
if (rebuildResult != null) {
StagedResult sr = checkNotNull(rebuildResult.staged());
return LoadHandle.create(
@@ -216,7 +217,8 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
return null;
}
private LoadHandle rebuildAndOpen(Repository repo) throws IOException {
private LoadHandle rebuildAndOpen(Repository repo)
throws NoSuchChangeException, IOException {
Timer1.Context timer = args.metrics.autoRebuildLatency.start(CHANGES);
try {
Change.Id cid = getChangeId();

View File

@@ -14,6 +14,8 @@
package com.google.gerrit.server.notedb;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
/**
* Holds the current state of the NoteDb migration.
* <p>
@@ -71,6 +73,9 @@ public abstract class NotesMigration {
*/
public abstract boolean readChangeSequence();
/** @return default primary storage for new changes. */
public abstract PrimaryStorage changePrimaryStorage();
public abstract boolean readAccounts();
public abstract boolean writeAccounts();

View File

@@ -60,6 +60,7 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.account.AccountManager;
@@ -146,6 +147,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Inject protected InternalChangeQuery internalChangeQuery;
@Inject protected ChangeNotes.Factory notesFactory;
@Inject protected PatchSetInserter.Factory patchSetFactory;
@Inject protected PatchSetUtil psUtil;
@Inject protected ChangeControl.GenericFactory changeControlFactory;
@Inject protected ChangeQueryProcessor queryProcessor;
@Inject protected SchemaCreator schemaCreator;
@@ -1579,9 +1581,13 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
Account.Id user2 = createAccount("user2");
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo));
PatchSet ps1 = db.patchSets().get(change1.currentPatchSetId());
ChangeNotes notes1 =
notesFactory.create(db, change1.getProject(), change1.getId());
PatchSet ps1 = psUtil.get(db, notes1, change1.currentPatchSetId());
Change change2 = insert(repo, newChange(repo));
PatchSet ps2 = db.patchSets().get(change2.currentPatchSetId());
ChangeNotes notes2 =
notesFactory.create(db, change2.getProject(), change2.getId());
PatchSet ps2 = psUtil.get(db, notes2, change2.currentPatchSetId());
requestContext.setContext(newRequestContext(user1));
assertQuery("has:edit");
@@ -1692,7 +1698,9 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
Project.NameKey project = new Project.NameKey("repo");
TestRepository<Repo> repo = createProject(project.get());
Change change = insert(repo, newChange(repo));
PatchSet ps = db.patchSets().get(change.currentPatchSetId());
ChangeNotes notes =
notesFactory.create(db, change.getProject(), change.getId());
PatchSet ps = psUtil.get(db, notes, change.currentPatchSetId());
requestContext.setContext(newRequestContext(user));
assertThat(changeEditModifier.createEdit(change, ps))
@@ -1714,6 +1722,9 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void refStateFields() throws Exception {
// This test method manages primary storage manually.
assume().that(notesMigration.changePrimaryStorage())
.isEqualTo(PrimaryStorage.REVIEW_DB);
Account.Id user = createAccount("user");
Project.NameKey project = new Project.NameKey("repo");
TestRepository<Repo> repo = createProject(project.get());
@@ -1723,7 +1734,9 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
Change change = insert(repo, newChangeForCommit(repo, commit));
Change.Id id = change.getId();
int c = id.get();
PatchSet ps = db.patchSets().get(change.currentPatchSetId());
ChangeNotes notes =
notesFactory.create(db, change.getProject(), change.getId());
PatchSet ps = psUtil.get(db, notes, change.currentPatchSetId());
requestContext.setContext(newRequestContext(user));
// Ensure one of each type of supported ref is present for the change. If

View File

@@ -39,6 +39,7 @@ import com.google.inject.Singleton;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Repository;
import org.junit.runner.Description;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -124,6 +125,25 @@ public class NoteDbChecker {
}
}
public void assertNoReviewDbChanges(Description desc) throws Exception {
ReviewDb db = getUnwrappedDb();
assertThat(db.changes().all().toList())
.named("Changes in " + desc.getTestClass())
.isEmpty();
assertThat(db.changeMessages().all().toList())
.named("ChangeMessages in " + desc.getTestClass())
.isEmpty();
assertThat(db.patchSets().all().toList())
.named("PatchSets in " + desc.getTestClass())
.isEmpty();
assertThat(db.patchSetApprovals().all().toList())
.named("PatchSetApprovals in " + desc.getTestClass())
.isEmpty();
assertThat(db.patchComments().all().toList())
.named("PatchLineComments in " + desc.getTestClass())
.isEmpty();
}
private List<ChangeBundle> readExpected(Stream<Change.Id> changeIds)
throws Exception {
boolean old = notesMigration.readChanges();

View File

@@ -29,6 +29,9 @@ public enum NoteDbMode {
/** Reading and writing all data to NoteDb is enabled. */
READ_WRITE,
/** Changes are created with their primary storage as NoteDb. */
PRIMARY,
/**
* Run tests with NoteDb disabled, then convert ReviewDb to NoteDb and check
* that the results match.
@@ -59,6 +62,6 @@ public enum NoteDbMode {
}
public static boolean readWrite() {
return get() == READ_WRITE;
return get() == READ_WRITE || get() == PRIMARY;
}
}

View File

@@ -14,6 +14,9 @@
package com.google.gerrit.testutil;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.inject.Singleton;
@@ -22,6 +25,8 @@ import com.google.inject.Singleton;
public class TestNotesMigration extends NotesMigration {
private volatile boolean readChanges;
private volatile boolean writeChanges;
private volatile PrimaryStorage changePrimaryStorage =
PrimaryStorage.REVIEW_DB;
private volatile boolean failOnLoad;
@Override
@@ -36,6 +41,11 @@ public class TestNotesMigration extends NotesMigration {
return readChanges;
}
@Override
public PrimaryStorage changePrimaryStorage() {
return changePrimaryStorage;
}
// Increase visbility from superclass, as tests may want to check whether
// NoteDb data is written in specific migration scenarios.
@Override
@@ -68,6 +78,12 @@ public class TestNotesMigration extends NotesMigration {
return this;
}
public TestNotesMigration setChangePrimaryStorage(
PrimaryStorage changePrimaryStorage) {
this.changePrimaryStorage = checkNotNull(changePrimaryStorage);
return this;
}
public TestNotesMigration setFailOnLoad(boolean failOnLoad) {
this.failOnLoad = failOnLoad;
return this;
@@ -82,16 +98,24 @@ public class TestNotesMigration extends NotesMigration {
case READ_WRITE:
setWriteChanges(true);
setReadChanges(true);
setChangePrimaryStorage(PrimaryStorage.REVIEW_DB);
break;
case WRITE:
setWriteChanges(true);
setReadChanges(false);
setChangePrimaryStorage(PrimaryStorage.REVIEW_DB);
break;
case PRIMARY:
setWriteChanges(true);
setReadChanges(true);
setChangePrimaryStorage(PrimaryStorage.NOTE_DB);
break;
case CHECK:
case OFF:
default:
setWriteChanges(false);
setReadChanges(false);
setChangePrimaryStorage(PrimaryStorage.REVIEW_DB);
break;
}
return this;