Start adding basic metrics for NoteDb operations

Change-Id: Id903aa01aa3201b002ac09756f4096934fcaccec
This commit is contained in:
Dave Borowitz
2016-03-24 14:29:35 -04:00
parent c11eae02a7
commit ffab8f767d
10 changed files with 143 additions and 28 deletions

View File

@@ -14,7 +14,10 @@
package com.google.gerrit.server.notedb; package com.google.gerrit.server.notedb;
import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.gerrit.metrics.Timer1;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllUsersName; import com.google.gerrit.server.config.AllUsersName;
@@ -40,17 +43,20 @@ public abstract class AbstractChangeNotes<T> {
final NotesMigration migration; final NotesMigration migration;
final AllUsersName allUsers; final AllUsersName allUsers;
final ChangeNoteUtil noteUtil; final ChangeNoteUtil noteUtil;
final NoteDbMetrics metrics;
@Inject @Inject
Args( Args(
GitRepositoryManager repoManager, GitRepositoryManager repoManager,
NotesMigration migration, NotesMigration migration,
AllUsersName allUsers, AllUsersName allUsers,
ChangeNoteUtil noteUtil) { ChangeNoteUtil noteUtil,
NoteDbMetrics metrics) {
this.repoManager = repoManager; this.repoManager = repoManager;
this.migration = migration; this.migration = migration;
this.allUsers = allUsers; this.allUsers = allUsers;
this.noteUtil = noteUtil; this.noteUtil = noteUtil;
this.metrics = metrics;
} }
} }
@@ -82,7 +88,8 @@ public abstract class AbstractChangeNotes<T> {
loadDefaults(); loadDefaults();
return self(); return self();
} }
try (Repository repo = try (Timer1.Context timer = args.metrics.readLatency.start(CHANGES);
Repository repo =
args.repoManager.openMetadataRepository(getProjectName()); args.repoManager.openMetadataRepository(getProjectName());
RevWalk walk = new RevWalk(repo)) { RevWalk walk = new RevWalk(repo)) {
Ref ref = repo.getRefDatabase().exactRef(getRefName()); Ref ref = repo.getRefDatabase().exactRef(getRefName());

View File

@@ -526,7 +526,8 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return; return;
} }
try (ChangeNotesParser parser = new ChangeNotesParser( try (ChangeNotesParser parser = new ChangeNotesParser(
project, change.getId(), rev, walk, args.repoManager, args.noteUtil)) { project, change.getId(), rev, walk, args.repoManager, args.noteUtil,
args.metrics)) {
parser.parseAll(); parser.parseAll();
if (parser.status != null) { if (parser.status != null) {

View File

@@ -26,6 +26,7 @@ import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_SUBJECT;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_SUBMISSION_ID; import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_SUBMISSION_ID;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_SUBMITTED_WITH; import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_SUBMITTED_WITH;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_TOPIC; import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_TOPIC;
import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
import com.google.common.base.Enums; import com.google.common.base.Enums;
import com.google.common.base.Function; import com.google.common.base.Function;
@@ -47,6 +48,7 @@ import com.google.common.collect.Tables;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import com.google.gerrit.common.data.LabelType; import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.SubmitRecord; 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.Account;
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;
@@ -115,6 +117,7 @@ class ChangeNotesParser implements AutoCloseable {
RevisionNoteMap revisionNoteMap; RevisionNoteMap revisionNoteMap;
private final ChangeNoteUtil noteUtil; private final ChangeNoteUtil noteUtil;
private final NoteDbMetrics metrics;
private final Change.Id id; private final Change.Id id;
private final ObjectId tip; private final ObjectId tip;
private final RevWalk walk; private final RevWalk walk;
@@ -126,13 +129,14 @@ class ChangeNotesParser implements AutoCloseable {
ChangeNotesParser(Project.NameKey project, Change.Id changeId, ObjectId tip, ChangeNotesParser(Project.NameKey project, Change.Id changeId, ObjectId tip,
RevWalk walk, GitRepositoryManager repoManager, RevWalk walk, GitRepositoryManager repoManager,
ChangeNoteUtil noteUtil) ChangeNoteUtil noteUtil, NoteDbMetrics metrics)
throws RepositoryNotFoundException, IOException { throws RepositoryNotFoundException, IOException {
this.id = changeId; this.id = changeId;
this.tip = tip; this.tip = tip;
this.walk = walk; this.walk = walk;
this.repo = repoManager.openMetadataRepository(project); this.repo = repoManager.openMetadataRepository(project);
this.noteUtil = noteUtil; this.noteUtil = noteUtil;
this.metrics = metrics;
approvals = Maps.newHashMap(); approvals = Maps.newHashMap();
reviewers = Maps.newLinkedHashMap(); reviewers = Maps.newLinkedHashMap();
allPastReviewers = Lists.newArrayList(); allPastReviewers = Lists.newArrayList();
@@ -150,15 +154,21 @@ class ChangeNotesParser implements AutoCloseable {
} }
void parseAll() throws ConfigInvalidException, IOException { void parseAll() throws ConfigInvalidException, IOException {
// Don't include initial parse in timer, as this might do more I/O to page
// in the block containing most commits. Later reads are not guaranteed to
// avoid I/O, but often should.
walk.markStart(walk.parseCommit(tip)); walk.markStart(walk.parseCommit(tip));
for (RevCommit commit : walk) {
parse(commit); try (Timer1.Context timer = metrics.parseLatency.start(CHANGES)) {
for (RevCommit commit : walk) {
parse(commit);
}
parseNotes();
allPastReviewers.addAll(reviewers.keySet());
pruneReviewers();
updatePatchSetStates();
checkMandatoryFooters();
} }
parseNotes();
allPastReviewers.addAll(reviewers.keySet());
pruneReviewers();
updatePatchSetStates();
checkMandatoryFooters();
} }
ImmutableListMultimap<PatchSet.Id, PatchSetApproval> ImmutableListMultimap<PatchSet.Id, PatchSetApproval>

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.server.notedb; package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
@@ -45,21 +46,13 @@ public class ConfigNotesMigration extends NotesMigration {
} }
} }
private static enum Table {
CHANGES;
private String key() {
return name().toLowerCase();
}
}
private static final String NOTE_DB = "noteDb"; private static final String NOTE_DB = "noteDb";
private static final String READ = "read"; private static final String READ = "read";
private static final String WRITE = "write"; 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<>();
for (Table t : Table.values()) { for (NoteDbTable t : NoteDbTable.values()) {
keys.add(t.key()); keys.add(t.key());
} }
for (String t : cfg.getSubsections(NOTE_DB)) { for (String t : cfg.getSubsections(NOTE_DB)) {
@@ -79,7 +72,7 @@ public class ConfigNotesMigration extends NotesMigration {
public static Config allEnabledConfig() { public static Config allEnabledConfig() {
Config cfg = new Config(); Config cfg = new Config();
for (Table t : Table.values()) { for (NoteDbTable t : NoteDbTable.values()) {
cfg.setBoolean(NOTE_DB, t.key(), WRITE, true); cfg.setBoolean(NOTE_DB, t.key(), WRITE, true);
cfg.setBoolean(NOTE_DB, t.key(), READ, true); cfg.setBoolean(NOTE_DB, t.key(), READ, true);
} }
@@ -92,8 +85,8 @@ public class ConfigNotesMigration extends NotesMigration {
@Inject @Inject
ConfigNotesMigration(@GerritServerConfig Config cfg) { ConfigNotesMigration(@GerritServerConfig Config cfg) {
checkConfig(cfg); checkConfig(cfg);
writeChanges = cfg.getBoolean(NOTE_DB, Table.CHANGES.key(), WRITE, false); writeChanges = cfg.getBoolean(NOTE_DB, CHANGES.key(), WRITE, false);
readChanges = cfg.getBoolean(NOTE_DB, Table.CHANGES.key(), READ, false); readChanges = cfg.getBoolean(NOTE_DB, CHANGES.key(), READ, false);
} }
@Override @Override

View File

@@ -0,0 +1,67 @@
// 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 com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer1;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@Singleton
class NoteDbMetrics {
/** End-to-end latency for writing a collection of updates. */
final Timer1<NoteDbTable> updateLatency;
/**
* End-to-end latency for reading changes from NoteDb, including reading
* ref(s) and parsing.
*/
final Timer1<NoteDbTable> readLatency;
/**
* The portion of {@link #readLatency} due to parsing commits, but excluding
* I/O (to a best effort).
*/
final Timer1<NoteDbTable> parseLatency;
@Inject
NoteDbMetrics(MetricMaker metrics) {
Field<NoteDbTable> view = Field.ofEnum(NoteDbTable.class, "table");
updateLatency = metrics.newTimer(
"notedb/update_latency",
new Description("NoteDb update latency by table")
.setCumulative()
.setUnit(Units.MILLISECONDS),
view);
readLatency = metrics.newTimer(
"notedb/read_latency",
new Description("NoteDb read latency by table")
.setCumulative()
.setUnit(Units.MILLISECONDS),
view);
parseLatency = metrics.newTimer(
"notedb/parse_latency",
new Description("NoteDb parse latency by table")
.setCumulative()
.setUnit(Units.MICROSECONDS),
view);
}
}

View File

@@ -0,0 +1,28 @@
// 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;
enum NoteDbTable {
CHANGES;
String key() {
return name().toLowerCase();
}
@Override
public String toString() {
return key();
}
}

View File

@@ -18,9 +18,11 @@ import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap; import com.google.common.collect.ListMultimap;
import com.google.gerrit.metrics.Timer1;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllUsersName; import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.ChainedReceiveCommands; import com.google.gerrit.server.git.ChainedReceiveCommands;
@@ -73,6 +75,7 @@ public class NoteDbUpdateManager {
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final NotesMigration migration; private final NotesMigration migration;
private final AllUsersName allUsersName; private final AllUsersName allUsersName;
private final NoteDbMetrics metrics;
private final Project.NameKey projectName; private final Project.NameKey projectName;
private final ListMultimap<String, ChangeUpdate> changeUpdates; private final ListMultimap<String, ChangeUpdate> changeUpdates;
private final ListMultimap<String, ChangeDraftUpdate> draftUpdates; private final ListMultimap<String, ChangeDraftUpdate> draftUpdates;
@@ -84,10 +87,12 @@ public class NoteDbUpdateManager {
NoteDbUpdateManager(GitRepositoryManager repoManager, NoteDbUpdateManager(GitRepositoryManager repoManager,
NotesMigration migration, NotesMigration migration,
AllUsersName allUsersName, AllUsersName allUsersName,
NoteDbMetrics metrics,
@Assisted Project.NameKey projectName) { @Assisted Project.NameKey projectName) {
this.repoManager = repoManager; this.repoManager = repoManager;
this.migration = migration; this.migration = migration;
this.allUsersName = allUsersName; this.allUsersName = allUsersName;
this.metrics = metrics;
this.projectName = projectName; this.projectName = projectName;
changeUpdates = ArrayListMultimap.create(); changeUpdates = ArrayListMultimap.create();
draftUpdates = ArrayListMultimap.create(); draftUpdates = ArrayListMultimap.create();
@@ -186,7 +191,7 @@ public class NoteDbUpdateManager {
if (isEmpty()) { if (isEmpty()) {
return; return;
} }
try { try (Timer1.Context timer = metrics.updateLatency.start(CHANGES)) {
initChangeRepo(); initChangeRepo();
if (!draftUpdates.isEmpty()) { if (!draftUpdates.isEmpty()) {
initAllUsersRepo(); initAllUsersRepo();

View File

@@ -21,6 +21,8 @@ import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.TimeUtil; import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.extensions.config.FactoryModule; import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.CommentRange; import com.google.gerrit.reviewdb.client.CommentRange;
@@ -168,6 +170,7 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
.toInstance(GitReferenceUpdated.DISABLED); .toInstance(GitReferenceUpdated.DISABLED);
bind(StarredChangesUtil.class) bind(StarredChangesUtil.class)
.toProvider(Providers.<StarredChangesUtil> of(null)); .toProvider(Providers.<StarredChangesUtil> of(null));
bind(MetricMaker.class).to(DisabledMetricMaker.class);
} }
}); });

View File

@@ -462,6 +462,6 @@ public class ChangeNotesParserTest extends AbstractChangeNotesTest {
private ChangeNotesParser newParser(ObjectId tip) throws Exception { private ChangeNotesParser newParser(ObjectId tip) throws Exception {
Change c = newChange(); Change c = newChange();
return new ChangeNotesParser(c.getProject(), c.getId(), tip, walk, return new ChangeNotesParser(c.getProject(), c.getId(), tip, walk,
repoManager, noteUtil); repoManager, noteUtil, args.metrics);
} }
} }

View File

@@ -909,8 +909,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
assertThat(commitWithComments).isNotNull(); assertThat(commitWithComments).isNotNull();
try (RevWalk rw = new RevWalk(repo)) { try (RevWalk rw = new RevWalk(repo)) {
try (ChangeNotesParser notesWithComments = new ChangeNotesParser(project, try (ChangeNotesParser notesWithComments = new ChangeNotesParser(
c.getId(), commitWithComments.copy(), rw, repoManager, noteUtil)) { project, c.getId(), commitWithComments.copy(), rw, repoManager,
noteUtil, args.metrics)) {
notesWithComments.parseAll(); notesWithComments.parseAll();
ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals1 = ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals1 =
notesWithComments.buildApprovals(); notesWithComments.buildApprovals();
@@ -922,7 +923,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
try (RevWalk rw = new RevWalk(repo)) { try (RevWalk rw = new RevWalk(repo)) {
try (ChangeNotesParser notesWithApprovals = new ChangeNotesParser(project, try (ChangeNotesParser notesWithApprovals = new ChangeNotesParser(project,
c.getId(), commitWithApprovals.copy(), rw, repoManager, c.getId(), commitWithApprovals.copy(), rw, repoManager,
noteUtil)) { noteUtil, args.metrics)) {
notesWithApprovals.parseAll(); notesWithApprovals.parseAll();
ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals2 = ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals2 =
notesWithApprovals.buildApprovals(); notesWithApprovals.buildApprovals();