All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gerrit.server.notedb.NotesMigration Maven / Gradle / Ivy

There is a newer version: 3.10.1
Show newest version
// Copyright (C) 2017 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.gerrit.server.notedb.NoteDbTable.CHANGES;

import com.google.auto.value.AutoValue;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.inject.AbstractModule;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jgit.lib.Config;

/**
 * Current low-level settings of the NoteDb migration for changes.
 *
 * 

This class only describes the migration state of the {@link * com.google.gerrit.reviewdb.client.Change Change} entity group, since it is possible for a given * site to be in different states of the Change NoteDb migration process while staying at the same * ReviewDb schema version. It does not describe the migration state of non-Change tables; * those are automatically migrated using the ReviewDb schema migration process, so the NoteDb * migration state at a given ReviewDb schema cannot vary. * *

In many places, core Gerrit code should not directly care about the NoteDb migration state, * and should prefer high-level APIs like {@link com.google.gerrit.server.ApprovalsUtil * ApprovalsUtil} that don't require callers to inspect the migration state. The * implementation of those utilities does care about the state, and should query the {@code * NotesMigration} for the properties of the migration, for example, {@link #changePrimaryStorage() * where new changes should be stored}. * *

Core Gerrit code is mostly interested in one facet of the migration at a time (reading or * writing, say), but not all combinations of return values are supported or even make sense. * *

This class controls the state of the migration according to options in {@code gerrit.config}. * In general, any changes to these options should only be made by adventurous administrators, who * know what they're doing, on non-production data, for the purposes of testing the NoteDb * implementation. Changing options quite likely requires re-running {@code MigrateToNoteDb}. For * these reasons, the options remain undocumented. * *

Note: Callers should not assume the values returned by {@code * NotesMigration}'s methods will not change in a running server. */ public abstract class NotesMigration { public static final String SECTION_NOTE_DB = "noteDb"; public static final String READ = "read"; public static final String WRITE = "write"; public static final String DISABLE_REVIEW_DB = "disableReviewDb"; private static final String PRIMARY_STORAGE = "primaryStorage"; private static final String SEQUENCE = "sequence"; public static class Module extends AbstractModule { @Override public void configure() { bind(MutableNotesMigration.class); bind(NotesMigration.class).to(MutableNotesMigration.class); } } @AutoValue abstract static class Snapshot { static Builder builder() { // Default values are defined as what we would read from an empty config. return create(new Config()).toBuilder(); } static Snapshot create(Config cfg) { return new AutoValue_NotesMigration_Snapshot.Builder() .setWriteChanges(cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), WRITE, false)) .setReadChanges(cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), READ, false)) .setReadChangeSequence(cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), SEQUENCE, false)) .setChangePrimaryStorage( cfg.getEnum( SECTION_NOTE_DB, CHANGES.key(), PRIMARY_STORAGE, PrimaryStorage.REVIEW_DB)) .setDisableChangeReviewDb( cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), DISABLE_REVIEW_DB, false)) .setFailOnLoadForTest(false) // Only set in tests, can't be set via config. .build(); } abstract boolean writeChanges(); abstract boolean readChanges(); abstract boolean readChangeSequence(); abstract PrimaryStorage changePrimaryStorage(); abstract boolean disableChangeReviewDb(); abstract boolean failOnLoadForTest(); abstract Builder toBuilder(); void setConfigValues(Config cfg) { cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), WRITE, writeChanges()); cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), READ, readChanges()); cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), SEQUENCE, readChangeSequence()); cfg.setEnum(SECTION_NOTE_DB, CHANGES.key(), PRIMARY_STORAGE, changePrimaryStorage()); cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), DISABLE_REVIEW_DB, disableChangeReviewDb()); } @AutoValue.Builder abstract static class Builder { abstract Builder setWriteChanges(boolean writeChanges); abstract Builder setReadChanges(boolean readChanges); abstract Builder setReadChangeSequence(boolean readChangeSequence); abstract Builder setChangePrimaryStorage(PrimaryStorage changePrimaryStorage); abstract Builder setDisableChangeReviewDb(boolean disableChangeReviewDb); abstract Builder setFailOnLoadForTest(boolean failOnLoadForTest); abstract Snapshot autoBuild(); Snapshot build() { Snapshot s = autoBuild(); checkArgument( !(s.disableChangeReviewDb() && s.changePrimaryStorage() != PrimaryStorage.NOTE_DB), "cannot disable ReviewDb for changes if default change primary storage is ReviewDb"); return s; } } } protected final AtomicReference snapshot; /** * Read changes from NoteDb. * *

Change data is read from NoteDb refs, but ReviewDb is still the source of truth. If the * loader determines NoteDb is out of date, the change data in NoteDb will be transparently * rebuilt. This means that some code paths that look read-only may in fact attempt to write. * *

If true and {@code writeChanges() = false}, changes can still be read from NoteDb, but any * attempts to write will generate an error. */ public final boolean readChanges() { return snapshot.get().readChanges(); } /** * Write changes to NoteDb. * *

This method is awkwardly named because you should be using either {@link * #commitChangeWrites()} or {@link #failChangeWrites()} instead. * *

Updates to change data are written to NoteDb refs, but ReviewDb is still the source of * truth. Change data will not be written unless the NoteDb refs are already up to date, and the * write path will attempt to rebuild the change if not. * *

If false, the behavior when attempting to write depends on {@code readChanges()}. If {@code * readChanges() = false}, writes to NoteDb are simply ignored; if {@code true}, any attempts to * write will generate an error. */ public final boolean rawWriteChangesSetting() { return snapshot.get().writeChanges(); } /** * Read sequential change ID numbers from NoteDb. * *

If true, change IDs are read from {@code refs/sequences/changes} in All-Projects. If false, * change IDs are read from ReviewDb's native sequences. */ public final boolean readChangeSequence() { return snapshot.get().readChangeSequence(); } /** @return default primary storage for new changes. */ public final PrimaryStorage changePrimaryStorage() { return snapshot.get().changePrimaryStorage(); } /** * Disable ReviewDb access for changes. * *

When set, ReviewDb operations involving the Changes table become no-ops. Lookups return no * results; updates do nothing, as does opening, committing, or rolling back a transaction on the * Changes table. */ public final boolean disableChangeReviewDb() { return snapshot.get().disableChangeReviewDb(); } /** * Whether to fail when reading any data from NoteDb. * *

Used in conjunction with {@link #readChanges()} for tests. */ public boolean failOnLoadForTest() { return snapshot.get().failOnLoadForTest(); } public final boolean commitChangeWrites() { // It may seem odd that readChanges() without writeChanges() means we should // attempt to commit writes. However, this method is used by callers to know // whether or not they should short-circuit and skip attempting to read or // write NoteDb refs. // // It is possible for commitChangeWrites() to return true and // failChangeWrites() to also return true, causing an error later in the // same codepath. This specific condition is used by the auto-rebuilding // path to rebuild a change and stage the results, but not commit them due // to failChangeWrites(). return rawWriteChangesSetting() || readChanges(); } public final boolean failChangeWrites() { return !rawWriteChangesSetting() && readChanges(); } public final void setConfigValues(Config cfg) { snapshot.get().setConfigValues(cfg); } @Override public final boolean equals(Object o) { return o instanceof NotesMigration && snapshot.get().equals(((NotesMigration) o).snapshot.get()); } @Override public final int hashCode() { return snapshot.get().hashCode(); } protected NotesMigration(Snapshot snapshot) { this.snapshot = new AtomicReference<>(snapshot); } final Snapshot snapshot() { return snapshot.get(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy