Merge branch 'stable-2.16'

* stable-2.16:
  GroupsUpdate: Add trace timer on group creation/update operations
  GroupsUpdate: Add debug log on cache eviction for group creation/update
  GroupsUpdate: Rename update... methods to evict...
  VersionedMetaData: Convert debug log of read/save file to trace timers
  TraceContext: Support fourth format parameter
  TestAccount: Add implementation of toString
  Correct URL of change message requests in docs
  Fixing Linux Kernel URL in Why Code Review Doc
  ExternalIdNotes: Only log 'reading external ID note map' when actually doing so
  RestApiException: Fix Javadoc
  Fix groups to prevent javascript errors with url

Change-Id: Ie5c73b3424c62b9e664ec16b7699ae4194a194cc
This commit is contained in:
David Pursehouse
2019-03-27 11:27:51 +01:00
8 changed files with 99 additions and 48 deletions

View File

@@ -60,7 +60,7 @@ older (imperfect) revision is not lost. It can be found via the `git reflog`.
At least two well-known open source projects insist on these practices:
* link:http://git-scm.com/[Git]
* link:http://www.kernel.org/category/about.html/[Linux Kernel]
* link:http://www.kernel.org/category/about.html[Linux Kernel]
However, contributors to these projects dont refine and polish their changes
in private until theyre perfect. Instead, polishing code is part of a review

View File

@@ -2467,7 +2467,7 @@ As response a list of link:#change-message-info[ChangeMessageInfo] entities is r
Retrieves a change message including link:#detailed-accounts[detailed account information].
--
'GET /changes/link:#change-id[\{change-id\}]/message/link:#change-message-id[\{change-message-id\}]'
'GET /changes/link:#change-id[\{change-id\}]/messages/link:#change-message-id[\{change-message-id\}]'
--
As response a link:#change-message-info[ChangeMessageInfo] entity is returned.
@@ -2496,8 +2496,8 @@ As response a link:#change-message-info[ChangeMessageInfo] entity is returned.
[[delete-change-message]]
=== Delete Change Message
--
'DELETE /changes/link:#change-id[\{change-id\}]/message/link:#change-message-id[\{change-message-id\}]' +
'POST /changes/link:#change-id[\{change-id\}]//message/link:#change-message-id[\{change-message-id\}]/delete'
'DELETE /changes/link:#change-id[\{change-id\}]/messages/link:#change-message-id[\{change-message-id\}]' +
'POST /changes/link:#change-id[\{change-id\}]/messages/link:#change-message-id[\{change-message-id\}]/delete'
--
Deletes a change message by replacing the change message with a new message,
@@ -2513,14 +2513,14 @@ To delete a change message, send a DELETE request:
.Request
----
DELETE /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/message/aaee04dcb46bafc8be24d8aa70b3b1beb7df5780 HTTP/1.0
DELETE /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/messages/aaee04dcb46bafc8be24d8aa70b3b1beb7df5780 HTTP/1.0
----
To provide a reason for the deletion, use a POST request:
.Request
----
POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/message/aaee04dcb46bafc8be24d8aa70b3b1beb7df5780/delete HTTP/1.0
POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/messages/aaee04dcb46bafc8be24d8aa70b3b1beb7df5780/delete HTTP/1.0
Content-Type: application/json; charset=UTF-8
{

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.acceptance;
import static java.util.stream.Collectors.toList;
import com.google.common.base.MoreObjects;
import com.google.common.net.InetAddresses;
import com.google.gerrit.mail.Address;
import com.google.gerrit.reviewdb.client.Account;
@@ -71,4 +72,9 @@ public class TestAccount {
public Account.Id getId() {
return id;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("id", id).add("username", username).toString();
}
}

View File

@@ -14,7 +14,7 @@
package com.google.gerrit.extensions.restapi;
/** Root exception type for JSON API failures. */
/** Root exception type for REST API failures. */
public class RestApiException extends Exception {
private static final long serialVersionUID = 1L;
private CacheControl caching = CacheControl.NONE;

View File

@@ -656,9 +656,12 @@ public class ExternalIdNotes extends VersionedMetaData {
@Override
protected void onLoad() throws IOException, ConfigInvalidException {
if (revision != null) {
logger.atFine().log("Reading external ID note map");
noteMap = revision != null ? NoteMap.read(reader, revision) : NoteMap.newEmptyMap();
noteMap = NoteMap.read(reader, revision);
} else {
noteMap = NoteMap.newEmptyMap();
}
if (afterReadRevision != null) {
afterReadRevision.run();

View File

@@ -17,10 +17,11 @@ package com.google.gerrit.server.git.meta;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.base.MoreObjects;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.git.LockFailureException;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
@@ -64,8 +65,6 @@ import org.eclipse.jgit.util.RawParseUtils;
* read from the repository, or format an update that can later be written back to the repository.
*/
public abstract class VersionedMetaData {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
/**
* Path information that does not hold references to any repository data structures, allowing the
* application to retain this object for long periods of time.
@@ -497,10 +496,11 @@ public abstract class VersionedMetaData {
return new byte[] {};
}
logger.atFine().log(
try (TraceTimer timer =
TraceContext.newTimer(
"Read file '%s' from ref '%s' of project '%s' from revision '%s'",
fileName, getRefName(), projectName, revision.name());
try (TreeWalk tw = TreeWalk.forPath(reader, fileName, revision.getTree())) {
TreeWalk tw = TreeWalk.forPath(reader, fileName, revision.getTree())) {
if (tw != null) {
ObjectLoader obj = reader.open(tw.getObjectId(0), Constants.OBJ_BLOB);
return obj.getCachedBytes(Integer.MAX_VALUE);
@@ -572,8 +572,9 @@ public abstract class VersionedMetaData {
}
protected void saveFile(String fileName, byte[] raw) throws IOException {
logger.atFine().log(
"Save file '%s' in ref '%s' of project '%s'", fileName, getRefName(), projectName);
try (TraceTimer timer =
TraceContext.newTimer(
"Save file '%s' in ref '%s' of project '%s'", fileName, getRefName(), projectName)) {
DirCacheEditor editor = newTree.editor();
if (raw != null && 0 < raw.length) {
final ObjectId blobId = inserter.insert(Constants.OBJ_BLOB, raw);
@@ -591,3 +592,4 @@ public abstract class VersionedMetaData {
editor.finish();
}
}
}

View File

@@ -19,6 +19,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.git.LockFailureException;
@@ -40,6 +41,8 @@ import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.group.GroupAuditService;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.index.group.GroupIndexer;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gwtorm.server.OrmDuplicateKeyException;
@@ -95,6 +98,8 @@ public class GroupsUpdate {
GroupsUpdate createWithServerIdent();
}
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final GitRepositoryManager repoManager;
private final AllUsersName allUsersName;
private final GroupCache groupCache;
@@ -258,11 +263,15 @@ public class GroupsUpdate {
public InternalGroup createGroup(
InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
throws OrmDuplicateKeyException, IOException, ConfigInvalidException {
try (TraceTimer timer =
TraceContext.newTimer(
"Creating group '%s'", groupUpdate.getName().orElseGet(groupCreation::getNameKey))) {
InternalGroup createdGroup = createGroupInNoteDbWithRetry(groupCreation, groupUpdate);
updateCachesOnGroupCreation(createdGroup);
evictCachesOnGroupCreation(createdGroup);
dispatchAuditEventsOnGroupCreation(createdGroup);
return createdGroup;
}
}
/**
* Updates the specified group.
@@ -276,6 +285,7 @@ public class GroupsUpdate {
*/
public void updateGroup(AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
throws OrmDuplicateKeyException, IOException, NoSuchGroupException, ConfigInvalidException {
try (TraceTimer timer = TraceContext.newTimer("Updating group %s", groupUuid)) {
Optional<Timestamp> updatedOn = groupUpdate.getUpdatedOn();
if (!updatedOn.isPresent()) {
updatedOn = Optional.of(TimeUtil.nowTs());
@@ -284,9 +294,10 @@ public class GroupsUpdate {
UpdateResult result = updateGroupInNoteDbWithRetry(groupUuid, groupUpdate);
updateNameInProjectConfigsIfNecessary(result);
updateCachesOnGroupUpdate(result);
evictCachesOnGroupUpdate(result);
dispatchAuditEventsOnGroupUpdate(result, updatedOn.get());
}
}
private InternalGroup createGroupInNoteDbWithRetry(
InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
@@ -424,7 +435,8 @@ public class GroupsUpdate {
allUsersName, batchRefUpdate, currentUser.map(user -> user.state()).orElse(null));
}
private void updateCachesOnGroupCreation(InternalGroup createdGroup) throws IOException {
private void evictCachesOnGroupCreation(InternalGroup createdGroup) throws IOException {
logger.atFine().log("evict caches on creation of group %s", createdGroup.getGroupUUID());
// By UUID is used for the index and hence should be evicted before refreshing the index.
groupCache.evict(createdGroup.getGroupUUID());
indexer.get().index(createdGroup.getGroupUUID());
@@ -436,7 +448,8 @@ public class GroupsUpdate {
createdGroup.getSubgroups().forEach(groupIncludeCache::evictParentGroupsOf);
}
private void updateCachesOnGroupUpdate(UpdateResult result) throws IOException {
private void evictCachesOnGroupUpdate(UpdateResult result) throws IOException {
logger.atFine().log("evict caches on update of group %s", result.getGroupUuid());
// By UUID is used for the index and hence should be evicted before refreshing the index.
groupCache.evict(result.getGroupUuid());
indexer.get().index(result.getGroupUuid());

View File

@@ -206,6 +206,23 @@ public class TraceContext implements AutoCloseable {
return new TraceTimer(format, arg1, arg2, arg3);
}
/**
* Opens a new timer that logs the time for an operation if request tracing is enabled.
*
* <p>If request tracing is not enabled this is a no-op.
*
* @param format the message format string
* @param arg1 first argument for the message
* @param arg2 second argument for the message
* @param arg3 third argument for the message
* @param arg4 fourth argument for the message
* @return the trace timer
*/
public static TraceTimer newTimer(
String format, Object arg1, Object arg2, Object arg3, Object arg4) {
return new TraceTimer(format, arg1, arg2, arg3, arg4);
}
public static class TraceTimer implements AutoCloseable {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -229,6 +246,16 @@ public class TraceContext implements AutoCloseable {
this(elapsedMs -> logger.atFine().log(format + " (%d ms)", arg1, arg2, arg3, elapsedMs));
}
private TraceTimer(
String format,
@Nullable Object arg1,
@Nullable Object arg2,
@Nullable Object arg3,
@Nullable Object arg4) {
this(
elapsedMs -> logger.atFine().log(format + " (%d ms)", arg1, arg2, arg3, arg4, elapsedMs));
}
private TraceTimer(Consumer<Long> logFn) {
this.logFn = logFn;
this.stopwatch = Stopwatch.createStarted();