Add config option setting default primary storage for new changes
Setting to NOTE_DB means new changes will never be written to ReviewDb. Update ChangeNotes.Factory to properly detect the case of a change not existing in ReviewDb but possibly still existing in NoteDb. This config option only controls the primary storage for new changes. Old changes (that have not been migrated, which is all of them since this change predates the migration tools) keep their primary storage as REVIEW_DB. This means that a single running server needs to be able to handle a mix of NOTE_DB/REVIEW_DB changes. Thus we need to continue using a live ReviewDb instance in the server, and just avoid writing any NoteDb-primary changes to that instance. The easiest way to implement this technically is to keep all the BatchUpdate code the same but only commit the change transaction if the change requires ReviewDb. This means we don't have to change any BatchUpdate.Op implementations, they can continue unconditionally writing. Rolling back the transaction is much simpler than creating some kind of ReviewDb wrapper that drops writes on the floor, even though it does technically create some database traffic even though no writes are committed. Add a new NoteDbMode to run all tests with this option enabled, double-checking after the tests that no changes were stored in ReviewDb. Tweak tests in various ways to work with this option enabled, avoiding direct use of ReviewDb when the primary storage is NoteDb. Change-Id: I9caf13192f955c4ec90409da32609d0a6f496d96
This commit is contained in:
@@ -25,6 +25,8 @@ import com.google.gerrit.pgm.Init;
|
|||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gerrit.server.git.AsyncReceiveCommits;
|
import com.google.gerrit.server.git.AsyncReceiveCommits;
|
||||||
import com.google.gerrit.server.ssh.NoSshModule;
|
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.SocketUtil;
|
||||||
import com.google.gerrit.server.util.SystemLog;
|
import com.google.gerrit.server.util.SystemLog;
|
||||||
import com.google.gerrit.testutil.FakeEmailSender;
|
import com.google.gerrit.testutil.FakeEmailSender;
|
||||||
@@ -62,6 +64,7 @@ public class GerritServer {
|
|||||||
static Description forTestClass(org.junit.runner.Description testDesc,
|
static Description forTestClass(org.junit.runner.Description testDesc,
|
||||||
String configName) {
|
String configName) {
|
||||||
return new AutoValue_GerritServer_Description(
|
return new AutoValue_GerritServer_Description(
|
||||||
|
testDesc,
|
||||||
configName,
|
configName,
|
||||||
true, // @UseLocalDisk is only valid on methods.
|
true, // @UseLocalDisk is only valid on methods.
|
||||||
!has(NoHttpd.class, testDesc.getTestClass()),
|
!has(NoHttpd.class, testDesc.getTestClass()),
|
||||||
@@ -75,6 +78,7 @@ public class GerritServer {
|
|||||||
static Description forTestMethod(org.junit.runner.Description testDesc,
|
static Description forTestMethod(org.junit.runner.Description testDesc,
|
||||||
String configName) {
|
String configName) {
|
||||||
return new AutoValue_GerritServer_Description(
|
return new AutoValue_GerritServer_Description(
|
||||||
|
testDesc,
|
||||||
configName,
|
configName,
|
||||||
testDesc.getAnnotation(UseLocalDisk.class) == null,
|
testDesc.getAnnotation(UseLocalDisk.class) == null,
|
||||||
testDesc.getAnnotation(NoHttpd.class) == null
|
testDesc.getAnnotation(NoHttpd.class) == null
|
||||||
@@ -97,6 +101,7 @@ public class GerritServer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract org.junit.runner.Description testDescription();
|
||||||
@Nullable abstract String configName();
|
@Nullable abstract String configName();
|
||||||
abstract boolean memory();
|
abstract boolean memory();
|
||||||
abstract boolean httpd();
|
abstract boolean httpd();
|
||||||
@@ -297,10 +302,7 @@ public class GerritServer {
|
|||||||
|
|
||||||
void stop() throws Exception {
|
void stop() throws Exception {
|
||||||
try {
|
try {
|
||||||
if (NoteDbMode.get().equals(NoteDbMode.CHECK)) {
|
checkNoteDbState();
|
||||||
testInjector.getInstance(NoteDbChecker.class)
|
|
||||||
.rebuildAndCheckAllChanges();
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
daemon.getLifecycleManager().stop();
|
daemon.getLifecycleManager().stop();
|
||||||
if (daemonService != null) {
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return MoreObjects.toStringHelper(this).addValue(desc).toString();
|
return MoreObjects.toStringHelper(this).addValue(desc).toString();
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ import com.google.gerrit.server.git.BatchUpdate;
|
|||||||
import com.google.gerrit.server.git.ChangeMessageModifier;
|
import com.google.gerrit.server.git.ChangeMessageModifier;
|
||||||
import com.google.gerrit.server.git.ProjectConfig;
|
import com.google.gerrit.server.git.ProjectConfig;
|
||||||
import com.google.gerrit.server.group.SystemGroupBackend;
|
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.ChangeControl;
|
||||||
import com.google.gerrit.server.project.Util;
|
import com.google.gerrit.server.project.Util;
|
||||||
import com.google.gerrit.testutil.FakeEmailSender.Message;
|
import com.google.gerrit.testutil.FakeEmailSender.Message;
|
||||||
@@ -1011,6 +1012,8 @@ public class ChangeIT extends AbstractDaemonTest {
|
|||||||
public void addReviewerWithNoteDbWhenDummyApprovalInReviewDbExists()
|
public void addReviewerWithNoteDbWhenDummyApprovalInReviewDbExists()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
assume().that(notesMigration.enabled()).isTrue();
|
assume().that(notesMigration.enabled()).isTrue();
|
||||||
|
assume().that(notesMigration.changePrimaryStorage())
|
||||||
|
.isEqualTo(PrimaryStorage.REVIEW_DB);
|
||||||
|
|
||||||
PushOneCommit.Result r = createChange();
|
PushOneCommit.Result r = createChange();
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import com.google.gerrit.server.git.SearchingChangeCacheImpl;
|
|||||||
import com.google.gerrit.server.git.TagCache;
|
import com.google.gerrit.server.git.TagCache;
|
||||||
import com.google.gerrit.server.git.VisibleRefFilter;
|
import com.google.gerrit.server.git.VisibleRefFilter;
|
||||||
import com.google.gerrit.server.notedb.ChangeNoteUtil;
|
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.ProjectControl;
|
||||||
import com.google.gerrit.server.project.Util;
|
import com.google.gerrit.server.project.Util;
|
||||||
import com.google.gerrit.server.query.change.ChangeData;
|
import com.google.gerrit.server.query.change.ChangeData;
|
||||||
@@ -447,8 +448,6 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void receivePackOmitsMissingObject() throws Exception {
|
public void receivePackOmitsMissingObject() throws Exception {
|
||||||
// Use the tactic from ConsistencyCheckerIT to insert a new patch set with a
|
|
||||||
// missing object.
|
|
||||||
String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
|
String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
|
||||||
try (Repository repo = repoManager.openRepository(project)) {
|
try (Repository repo = repoManager.openRepository(project)) {
|
||||||
TestRepository<?> tr = new TestRepository<>(repo);
|
TestRepository<?> tr = new TestRepository<>(repo);
|
||||||
@@ -457,9 +456,11 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
|
|||||||
PatchSet.Id psId = new PatchSet.Id(c3.getId(), 2);
|
PatchSet.Id psId = new PatchSet.Id(c3.getId(), 2);
|
||||||
c.setCurrentPatchSet(psId, subject, c.getOriginalSubject());
|
c.setCurrentPatchSet(psId, subject, c.getOriginalSubject());
|
||||||
|
|
||||||
|
if (notesMigration.changePrimaryStorage() == PrimaryStorage.REVIEW_DB) {
|
||||||
PatchSet ps = TestChanges.newPatchSet(psId, rev, admin.getId());
|
PatchSet ps = TestChanges.newPatchSet(psId, rev, admin.getId());
|
||||||
db.patchSets().insert(Collections.singleton(ps));
|
db.patchSets().insert(Collections.singleton(ps));
|
||||||
db.changes().update(Collections.singleton(c));
|
db.changes().update(Collections.singleton(c));
|
||||||
|
}
|
||||||
|
|
||||||
if (notesMigration.commitChangeWrites()) {
|
if (notesMigration.commitChangeWrites()) {
|
||||||
PersonIdent committer = serverIdent.get();
|
PersonIdent committer = serverIdent.get();
|
||||||
|
|||||||
@@ -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.BatchUpdate.RepoContext;
|
||||||
import com.google.gerrit.server.git.validators.CommitValidators;
|
import com.google.gerrit.server.git.validators.CommitValidators;
|
||||||
import com.google.gerrit.server.notedb.ChangeNoteUtil;
|
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.server.project.ChangeControl;
|
||||||
import com.google.gerrit.testutil.InMemoryRepositoryManager;
|
import com.google.gerrit.testutil.InMemoryRepositoryManager;
|
||||||
import com.google.gerrit.testutil.TestChanges;
|
import com.google.gerrit.testutil.TestChanges;
|
||||||
@@ -297,8 +298,10 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
|
|||||||
String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
|
String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
|
||||||
PatchSet ps = newPatchSet(psId, rev, adminId);
|
PatchSet ps = newPatchSet(psId, rev, adminId);
|
||||||
|
|
||||||
|
if (notesMigration.changePrimaryStorage() == PrimaryStorage.REVIEW_DB) {
|
||||||
db.changes().insert(singleton(c));
|
db.changes().insert(singleton(c));
|
||||||
db.patchSets().insert(singleton(ps));
|
db.patchSets().insert(singleton(ps));
|
||||||
|
}
|
||||||
addNoteDbCommit(
|
addNoteDbCommit(
|
||||||
c.getId(),
|
c.getId(),
|
||||||
"Create change\n"
|
"Create change\n"
|
||||||
@@ -824,10 +827,12 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
|
|||||||
Change c = new Change(ctl.getChange());
|
Change c = new Change(ctl.getChange());
|
||||||
PatchSet.Id psId = nextPatchSetId(ctl);
|
PatchSet.Id psId = nextPatchSetId(ctl);
|
||||||
c.setCurrentPatchSet(psId, subject, c.getOriginalSubject());
|
c.setCurrentPatchSet(psId, subject, c.getOriginalSubject());
|
||||||
|
|
||||||
PatchSet ps = newPatchSet(psId, rev, adminId);
|
PatchSet ps = newPatchSet(psId, rev, adminId);
|
||||||
|
|
||||||
|
if (PrimaryStorage.of(c) == PrimaryStorage.REVIEW_DB) {
|
||||||
db.patchSets().insert(singleton(ps));
|
db.patchSets().insert(singleton(ps));
|
||||||
db.changes().update(singleton(c));
|
db.changes().update(singleton(c));
|
||||||
|
}
|
||||||
|
|
||||||
addNoteDbCommit(
|
addNoteDbCommit(
|
||||||
c.getId(),
|
c.getId(),
|
||||||
|
|||||||
@@ -1049,18 +1049,45 @@ public class BatchUpdate implements AutoCloseable {
|
|||||||
private ChangeContext newChangeContext(ReviewDb db, Repository repo,
|
private ChangeContext newChangeContext(ReviewDb db, Repository repo,
|
||||||
RevWalk rw, Change.Id id) throws OrmException {
|
RevWalk rw, Change.Id id) throws OrmException {
|
||||||
Change c = newChanges.get(id);
|
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);
|
c = ChangeNotes.readOneReviewDbChange(db, id);
|
||||||
if (c == null) {
|
if (c == null) {
|
||||||
|
if (defaultStorage == PrimaryStorage.REVIEW_DB) {
|
||||||
logDebug("Failed to get change {} from unwrapped db", id);
|
logDebug("Failed to get change {} from unwrapped db", id);
|
||||||
throw new NoSuchChangeException(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);
|
NoteDbChangeState.checkNotReadOnly(c, skewMs);
|
||||||
}
|
}
|
||||||
// Pass in preloaded change to controlFor, to avoid:
|
ChangeNotes notes = changeNotesFactory.createForBatchUpdate(c, !isNew);
|
||||||
// - 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);
|
|
||||||
ChangeControl ctl = changeControlFactory.controlFor(notes, user);
|
ChangeControl ctl = changeControlFactory.controlFor(notes, user);
|
||||||
return new ChangeContext(ctl, new BatchUpdateReviewDb(db), repo, rw);
|
return new ChangeContext(ctl, new BatchUpdateReviewDb(db), repo, rw);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.ChangeNotesCommit.ChangeNotesRevWalk;
|
||||||
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
|
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
|
||||||
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
|
||||||
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
@@ -174,7 +175,20 @@ public abstract class AbstractChangeNotes<T> {
|
|||||||
return ref != null ? ref.getObjectId() : null;
|
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));
|
return openHandle(repo, readRef(repo));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +196,7 @@ public abstract class AbstractChangeNotes<T> {
|
|||||||
return LoadHandle.create(ChangeNotesCommit.newRevWalk(repo), id);
|
return LoadHandle.create(ChangeNotesCommit.newRevWalk(repo), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T reload() throws OrmException {
|
public T reload() throws NoSuchChangeException, OrmException {
|
||||||
loaded = false;
|
loaded = false;
|
||||||
return load();
|
return load();
|
||||||
}
|
}
|
||||||
@@ -215,7 +229,7 @@ public abstract class AbstractChangeNotes<T> {
|
|||||||
|
|
||||||
/** Set up the metadata, parsing any state from the loaded revision. */
|
/** Set up the metadata, parsing any state from the loaded revision. */
|
||||||
protected abstract void onLoad(LoadHandle handle)
|
protected abstract void onLoad(LoadHandle handle)
|
||||||
throws IOException, ConfigInvalidException;
|
throws NoSuchChangeException, IOException, ConfigInvalidException;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected final T self() {
|
protected final T self() {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import com.google.gerrit.common.Nullable;
|
|||||||
import com.google.gerrit.common.data.SubmitRecord;
|
import com.google.gerrit.common.data.SubmitRecord;
|
||||||
import com.google.gerrit.metrics.Timer1;
|
import com.google.gerrit.metrics.Timer1;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
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.Change;
|
||||||
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
||||||
import com.google.gerrit.reviewdb.client.Comment;
|
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,
|
public ChangeNotes createChecked(ReviewDb db, Project.NameKey project,
|
||||||
Change.Id changeId) throws OrmException {
|
Change.Id changeId) throws OrmException {
|
||||||
Change change = readOneReviewDbChange(db, changeId);
|
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);
|
throw new NoSuchChangeException(changeId);
|
||||||
}
|
}
|
||||||
return new ChangeNotes(args, change).load();
|
return new ChangeNotes(args, change).load();
|
||||||
@@ -144,16 +152,33 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
return changes.get(0).notes();
|
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,
|
private Change loadChangeFromDb(ReviewDb db, Project.NameKey project,
|
||||||
Change.Id changeId) throws OrmException {
|
Change.Id changeId) throws OrmException {
|
||||||
Change change = readOneReviewDbChange(db, changeId);
|
|
||||||
checkArgument(project != null, "project is required");
|
checkArgument(project != null, "project is required");
|
||||||
checkNotNull(change,
|
Change change = readOneReviewDbChange(db, changeId);
|
||||||
"change %s not found in ReviewDb", 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),
|
checkArgument(change.getProject().equals(project),
|
||||||
"passed project %s when creating ChangeNotes for %s, but actual"
|
"passed project %s when creating ChangeNotes for %s, but actual"
|
||||||
+ " project is %s",
|
+ " project is %s",
|
||||||
project, changeId, change.getProject());
|
project, changeId, change.getProject());
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Throw NoSuchChangeException when the change is not found in the
|
// TODO: Throw NoSuchChangeException when the change is not found in the
|
||||||
// database
|
// database
|
||||||
return change;
|
return change;
|
||||||
@@ -168,7 +193,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
public ChangeNotes createWithAutoRebuildingDisabled(ReviewDb db,
|
public ChangeNotes createWithAutoRebuildingDisabled(ReviewDb db,
|
||||||
Project.NameKey project, Change.Id changeId) throws OrmException {
|
Project.NameKey project, Change.Id changeId) throws OrmException {
|
||||||
return new ChangeNotes(
|
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);
|
return new ChangeNotes(args, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangeNotes createForBatchUpdate(Change change) throws OrmException {
|
public ChangeNotes createForBatchUpdate(Change change, boolean shouldExist)
|
||||||
return new ChangeNotes(args, change, false, null).load();
|
throws OrmException {
|
||||||
|
return new ChangeNotes(args, change, shouldExist, false, null).load();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangeNotes createWithAutoRebuildingDisabled(Change change,
|
public ChangeNotes createWithAutoRebuildingDisabled(Change change,
|
||||||
RefCache refs) throws OrmException {
|
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
|
// TODO(ekempin): Remove when database backend is deleted
|
||||||
@@ -302,13 +328,18 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
Project.NameKey project) throws OrmException, IOException {
|
Project.NameKey project) throws OrmException, IOException {
|
||||||
Set<Change.Id> ids = scan(repo);
|
Set<Change.Id> ids = scan(repo);
|
||||||
List<ChangeNotes> changeNotes = new ArrayList<>(ids.size());
|
List<ChangeNotes> changeNotes = new ArrayList<>(ids.size());
|
||||||
|
PrimaryStorage defaultStorage = args.migration.changePrimaryStorage();
|
||||||
for (Change.Id id : ids) {
|
for (Change.Id id : ids) {
|
||||||
Change change = readOneReviewDbChange(db, id);
|
Change change = readOneReviewDbChange(db, id);
|
||||||
if (change == null) {
|
if (change == null) {
|
||||||
|
if (defaultStorage == PrimaryStorage.REVIEW_DB) {
|
||||||
log.warn("skipping change {} found in project {} " +
|
log.warn("skipping change {} found in project {} " +
|
||||||
"but not in ReviewDb",
|
"but not in ReviewDb",
|
||||||
id, project);
|
id, project);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
// TODO(dborowitz): See discussion in BatchUpdate#newChangeContext.
|
||||||
|
change = newNoteDbOnlyChange(project, id);
|
||||||
} else if (!change.getProject().equals(project)) {
|
} else if (!change.getProject().equals(project)) {
|
||||||
log.error(
|
log.error(
|
||||||
"skipping change {} found in project {} " +
|
"skipping change {} found in project {} " +
|
||||||
@@ -337,6 +368,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final boolean shouldExist;
|
||||||
private final RefCache refs;
|
private final RefCache refs;
|
||||||
|
|
||||||
private Change change;
|
private Change change;
|
||||||
@@ -358,13 +390,14 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public ChangeNotes(Args args, Change change) {
|
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,
|
private ChangeNotes(Args args, Change change, boolean shouldExist,
|
||||||
@Nullable RefCache refs) {
|
boolean autoRebuild, @Nullable RefCache refs) {
|
||||||
super(args, change.getId(), PrimaryStorage.of(change), autoRebuild);
|
super(args, change.getId(), PrimaryStorage.of(change), autoRebuild);
|
||||||
this.change = new Change(change);
|
this.change = new Change(change);
|
||||||
|
this.shouldExist = shouldExist;
|
||||||
this.refs = refs;
|
this.refs = refs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,9 +588,14 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onLoad(LoadHandle handle)
|
protected void onLoad(LoadHandle handle)
|
||||||
throws IOException, ConfigInvalidException {
|
throws NoSuchChangeException, IOException, ConfigInvalidException {
|
||||||
ObjectId rev = handle.id();
|
ObjectId rev = handle.id();
|
||||||
if (rev == null) {
|
if (rev == null) {
|
||||||
|
if (args.migration.readChanges()
|
||||||
|
&& PrimaryStorage.of(change) == PrimaryStorage.NOTE_DB
|
||||||
|
&& shouldExist) {
|
||||||
|
throw new NoSuchChangeException(getChangeId());
|
||||||
|
}
|
||||||
loadDefaults();
|
loadDefaults();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -587,12 +625,17 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LoadHandle openHandle(Repository repo) throws IOException {
|
protected LoadHandle openHandle(Repository repo)
|
||||||
|
throws NoSuchChangeException, IOException {
|
||||||
if (autoRebuild) {
|
if (autoRebuild) {
|
||||||
NoteDbChangeState state = NoteDbChangeState.parse(change);
|
NoteDbChangeState state = NoteDbChangeState.parse(change);
|
||||||
ObjectId id = readRef(repo);
|
ObjectId id = readRef(repo);
|
||||||
if (state == null && id == null) {
|
if (id == null) {
|
||||||
|
if (state == null) {
|
||||||
return super.openHandle(repo, id);
|
return super.openHandle(repo, id);
|
||||||
|
} else if (shouldExist) {
|
||||||
|
throw new NoSuchChangeException(getChangeId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
RefCache refs = this.refs != null ? this.refs : new RepoRefCache(repo);
|
RefCache refs = this.refs != null ? this.refs : new RepoRefCache(repo);
|
||||||
if (!NoteDbChangeState.isChangeUpToDate(state, refs, getChangeId())) {
|
if (!NoteDbChangeState.isChangeUpToDate(state, refs, getChangeId())) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
|
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
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 NOTE_DB = "noteDb";
|
||||||
|
|
||||||
|
private static final String PRIMARY_STORAGE = "primaryStorage";
|
||||||
private static final String READ = "read";
|
private static final String READ = "read";
|
||||||
private static final String WRITE = "write";
|
|
||||||
private static final String SEQUENCE = "sequence";
|
private static final String SEQUENCE = "sequence";
|
||||||
|
private static final String WRITE = "write";
|
||||||
|
|
||||||
private static void checkConfig(Config cfg) {
|
private static void checkConfig(Config cfg) {
|
||||||
Set<String> keys = new HashSet<>();
|
Set<String> keys = new HashSet<>();
|
||||||
@@ -81,6 +84,7 @@ public class ConfigNotesMigration extends NotesMigration {
|
|||||||
private final boolean writeChanges;
|
private final boolean writeChanges;
|
||||||
private final boolean readChanges;
|
private final boolean readChanges;
|
||||||
private final boolean readChangeSequence;
|
private final boolean readChangeSequence;
|
||||||
|
private final PrimaryStorage changePrimaryStorage;
|
||||||
|
|
||||||
private final boolean writeAccounts;
|
private final boolean writeAccounts;
|
||||||
private final boolean readAccounts;
|
private final boolean readAccounts;
|
||||||
@@ -98,6 +102,9 @@ public class ConfigNotesMigration extends NotesMigration {
|
|||||||
// NoteDb. This decision for the default may be reevaluated later.
|
// NoteDb. This decision for the default may be reevaluated later.
|
||||||
readChangeSequence = cfg.getBoolean(NOTE_DB, CHANGES.key(), SEQUENCE, false);
|
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);
|
writeAccounts = cfg.getBoolean(NOTE_DB, ACCOUNTS.key(), WRITE, false);
|
||||||
readAccounts = cfg.getBoolean(NOTE_DB, ACCOUNTS.key(), READ, false);
|
readAccounts = cfg.getBoolean(NOTE_DB, ACCOUNTS.key(), READ, false);
|
||||||
}
|
}
|
||||||
@@ -117,6 +124,11 @@ public class ConfigNotesMigration extends NotesMigration {
|
|||||||
return readChangeSequence;
|
return readChangeSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrimaryStorage changePrimaryStorage() {
|
||||||
|
return changePrimaryStorage;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeAccounts() {
|
public boolean writeAccounts() {
|
||||||
return writeAccounts;
|
return writeAccounts;
|
||||||
|
|||||||
@@ -188,7 +188,8 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LoadHandle openHandle(Repository repo) throws IOException {
|
protected LoadHandle openHandle(Repository repo)
|
||||||
|
throws NoSuchChangeException, IOException {
|
||||||
if (rebuildResult != null) {
|
if (rebuildResult != null) {
|
||||||
StagedResult sr = checkNotNull(rebuildResult.staged());
|
StagedResult sr = checkNotNull(rebuildResult.staged());
|
||||||
return LoadHandle.create(
|
return LoadHandle.create(
|
||||||
@@ -216,7 +217,8 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
|
|||||||
return null;
|
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);
|
Timer1.Context timer = args.metrics.autoRebuildLatency.start(CHANGES);
|
||||||
try {
|
try {
|
||||||
Change.Id cid = getChangeId();
|
Change.Id cid = getChangeId();
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.notedb;
|
package com.google.gerrit.server.notedb;
|
||||||
|
|
||||||
|
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the current state of the NoteDb migration.
|
* Holds the current state of the NoteDb migration.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -71,6 +73,9 @@ public abstract class NotesMigration {
|
|||||||
*/
|
*/
|
||||||
public abstract boolean readChangeSequence();
|
public abstract boolean readChangeSequence();
|
||||||
|
|
||||||
|
/** @return default primary storage for new changes. */
|
||||||
|
public abstract PrimaryStorage changePrimaryStorage();
|
||||||
|
|
||||||
public abstract boolean readAccounts();
|
public abstract boolean readAccounts();
|
||||||
|
|
||||||
public abstract boolean writeAccounts();
|
public abstract boolean writeAccounts();
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
|||||||
import com.google.gerrit.reviewdb.server.ReviewDbUtil;
|
import com.google.gerrit.reviewdb.server.ReviewDbUtil;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
import com.google.gerrit.server.PatchSetUtil;
|
||||||
import com.google.gerrit.server.Sequences;
|
import com.google.gerrit.server.Sequences;
|
||||||
import com.google.gerrit.server.StarredChangesUtil;
|
import com.google.gerrit.server.StarredChangesUtil;
|
||||||
import com.google.gerrit.server.account.AccountManager;
|
import com.google.gerrit.server.account.AccountManager;
|
||||||
@@ -146,6 +147,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
|
|||||||
@Inject protected InternalChangeQuery internalChangeQuery;
|
@Inject protected InternalChangeQuery internalChangeQuery;
|
||||||
@Inject protected ChangeNotes.Factory notesFactory;
|
@Inject protected ChangeNotes.Factory notesFactory;
|
||||||
@Inject protected PatchSetInserter.Factory patchSetFactory;
|
@Inject protected PatchSetInserter.Factory patchSetFactory;
|
||||||
|
@Inject protected PatchSetUtil psUtil;
|
||||||
@Inject protected ChangeControl.GenericFactory changeControlFactory;
|
@Inject protected ChangeControl.GenericFactory changeControlFactory;
|
||||||
@Inject protected ChangeQueryProcessor queryProcessor;
|
@Inject protected ChangeQueryProcessor queryProcessor;
|
||||||
@Inject protected SchemaCreator schemaCreator;
|
@Inject protected SchemaCreator schemaCreator;
|
||||||
@@ -1579,9 +1581,13 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
|
|||||||
Account.Id user2 = createAccount("user2");
|
Account.Id user2 = createAccount("user2");
|
||||||
TestRepository<Repo> repo = createProject("repo");
|
TestRepository<Repo> repo = createProject("repo");
|
||||||
Change change1 = insert(repo, newChange(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));
|
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));
|
requestContext.setContext(newRequestContext(user1));
|
||||||
assertQuery("has:edit");
|
assertQuery("has:edit");
|
||||||
@@ -1692,7 +1698,9 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
|
|||||||
Project.NameKey project = new Project.NameKey("repo");
|
Project.NameKey project = new Project.NameKey("repo");
|
||||||
TestRepository<Repo> repo = createProject(project.get());
|
TestRepository<Repo> repo = createProject(project.get());
|
||||||
Change change = insert(repo, newChange(repo));
|
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));
|
requestContext.setContext(newRequestContext(user));
|
||||||
assertThat(changeEditModifier.createEdit(change, ps))
|
assertThat(changeEditModifier.createEdit(change, ps))
|
||||||
@@ -1714,6 +1722,9 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void refStateFields() throws Exception {
|
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");
|
Account.Id user = createAccount("user");
|
||||||
Project.NameKey project = new Project.NameKey("repo");
|
Project.NameKey project = new Project.NameKey("repo");
|
||||||
TestRepository<Repo> repo = createProject(project.get());
|
TestRepository<Repo> repo = createProject(project.get());
|
||||||
@@ -1723,7 +1734,9 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
|
|||||||
Change change = insert(repo, newChangeForCommit(repo, commit));
|
Change change = insert(repo, newChangeForCommit(repo, commit));
|
||||||
Change.Id id = change.getId();
|
Change.Id id = change.getId();
|
||||||
int c = id.get();
|
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));
|
requestContext.setContext(newRequestContext(user));
|
||||||
|
|
||||||
// Ensure one of each type of supported ref is present for the change. If
|
// Ensure one of each type of supported ref is present for the change. If
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import com.google.inject.Singleton;
|
|||||||
|
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.junit.runner.Description;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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)
|
private List<ChangeBundle> readExpected(Stream<Change.Id> changeIds)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
boolean old = notesMigration.readChanges();
|
boolean old = notesMigration.readChanges();
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ public enum NoteDbMode {
|
|||||||
/** Reading and writing all data to NoteDb is enabled. */
|
/** Reading and writing all data to NoteDb is enabled. */
|
||||||
READ_WRITE,
|
READ_WRITE,
|
||||||
|
|
||||||
|
/** Changes are created with their primary storage as NoteDb. */
|
||||||
|
PRIMARY,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run tests with NoteDb disabled, then convert ReviewDb to NoteDb and check
|
* Run tests with NoteDb disabled, then convert ReviewDb to NoteDb and check
|
||||||
* that the results match.
|
* that the results match.
|
||||||
@@ -59,6 +62,6 @@ public enum NoteDbMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean readWrite() {
|
public static boolean readWrite() {
|
||||||
return get() == READ_WRITE;
|
return get() == READ_WRITE || get() == PRIMARY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
|
|
||||||
package com.google.gerrit.testutil;
|
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.gerrit.server.notedb.NotesMigration;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
@@ -22,6 +25,8 @@ import com.google.inject.Singleton;
|
|||||||
public class TestNotesMigration extends NotesMigration {
|
public class TestNotesMigration extends NotesMigration {
|
||||||
private volatile boolean readChanges;
|
private volatile boolean readChanges;
|
||||||
private volatile boolean writeChanges;
|
private volatile boolean writeChanges;
|
||||||
|
private volatile PrimaryStorage changePrimaryStorage =
|
||||||
|
PrimaryStorage.REVIEW_DB;
|
||||||
private volatile boolean failOnLoad;
|
private volatile boolean failOnLoad;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -36,6 +41,11 @@ public class TestNotesMigration extends NotesMigration {
|
|||||||
return readChanges;
|
return readChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrimaryStorage changePrimaryStorage() {
|
||||||
|
return changePrimaryStorage;
|
||||||
|
}
|
||||||
|
|
||||||
// Increase visbility from superclass, as tests may want to check whether
|
// Increase visbility from superclass, as tests may want to check whether
|
||||||
// NoteDb data is written in specific migration scenarios.
|
// NoteDb data is written in specific migration scenarios.
|
||||||
@Override
|
@Override
|
||||||
@@ -68,6 +78,12 @@ public class TestNotesMigration extends NotesMigration {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TestNotesMigration setChangePrimaryStorage(
|
||||||
|
PrimaryStorage changePrimaryStorage) {
|
||||||
|
this.changePrimaryStorage = checkNotNull(changePrimaryStorage);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public TestNotesMigration setFailOnLoad(boolean failOnLoad) {
|
public TestNotesMigration setFailOnLoad(boolean failOnLoad) {
|
||||||
this.failOnLoad = failOnLoad;
|
this.failOnLoad = failOnLoad;
|
||||||
return this;
|
return this;
|
||||||
@@ -82,16 +98,24 @@ public class TestNotesMigration extends NotesMigration {
|
|||||||
case READ_WRITE:
|
case READ_WRITE:
|
||||||
setWriteChanges(true);
|
setWriteChanges(true);
|
||||||
setReadChanges(true);
|
setReadChanges(true);
|
||||||
|
setChangePrimaryStorage(PrimaryStorage.REVIEW_DB);
|
||||||
break;
|
break;
|
||||||
case WRITE:
|
case WRITE:
|
||||||
setWriteChanges(true);
|
setWriteChanges(true);
|
||||||
setReadChanges(false);
|
setReadChanges(false);
|
||||||
|
setChangePrimaryStorage(PrimaryStorage.REVIEW_DB);
|
||||||
|
break;
|
||||||
|
case PRIMARY:
|
||||||
|
setWriteChanges(true);
|
||||||
|
setReadChanges(true);
|
||||||
|
setChangePrimaryStorage(PrimaryStorage.NOTE_DB);
|
||||||
break;
|
break;
|
||||||
case CHECK:
|
case CHECK:
|
||||||
case OFF:
|
case OFF:
|
||||||
default:
|
default:
|
||||||
setWriteChanges(false);
|
setWriteChanges(false);
|
||||||
setReadChanges(false);
|
setReadChanges(false);
|
||||||
|
setChangePrimaryStorage(PrimaryStorage.REVIEW_DB);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
Reference in New Issue
Block a user