CreateTag: Convert ConcurrentRefUpdateException into LockFailureException
Creating a tag may fail with a ConcurrentRefUpdateException that
represents a LOCK_FAILURE. Convert this exception into a
LockFailureException so that our auto-retrying logic for LOCK_FAILURES
can recognize the LOCK_FAILURE and retry the operation, instead of
failing immediately.
Example stacktrace:
Cannot create tag "refs/tags/foo-bar" [CONTEXT project="my-project" request="REST /projects/*/tags/*" ]
org.eclipse.jgit.api.errors.ConcurrentRefUpdateException: Could not lock HEAD. RefUpdate return code was: LOCK_FAILURE
at org.eclipse.jgit.api.TagCommand.updateTagRef(TagCommand.java:178)
at org.eclipse.jgit.api.TagCommand.call(TagCommand.java:129)
at com.google.gerrit.server.restapi.project.CreateTag.apply(CreateTag.java:144)
at com.google.gerrit.server.restapi.project.CreateTag.apply(CreateTag.java:59)
at com.google.gerrit.httpd.restapi.RestApiServlet.lambda$invokeRestCollectionCreateViewWithRetry$7(RestApiServlet.java:897)
...
Caused by: org.eclipse.jgit.api.errors.ConcurrentRefUpdateException: Could not lock HEAD. RefUpdate return code was: LOCK_FAILURE
at org.eclipse.jgit.api.TagCommand.updateTagRef(TagCommand.java:178)
at org.eclipse.jgit.api.TagCommand.call(TagCommand.java:129)
at com.google.gerrit.server.restapi.project.CreateTag.apply(CreateTag.java:144)
... 227 more
Release-Notes: skip
Change-Id: I2ff60b01ab0e2305fdf8739cd884038091f2b888
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/java/com/google/gerrit/git/GitUpdateFailureException.java b/java/com/google/gerrit/git/GitUpdateFailureException.java
index 7fcb828..339339c 100644
--- a/java/com/google/gerrit/git/GitUpdateFailureException.java
+++ b/java/com/google/gerrit/git/GitUpdateFailureException.java
@@ -46,6 +46,11 @@
.collect(toImmutableList());
}
+ protected GitUpdateFailureException(String message, Throwable cause) {
+ super(message, cause);
+ this.failures = ImmutableList.of();
+ }
+
/** Returns the names of the refs for which the update failed. */
public ImmutableList<String> getFailedRefs() {
return failures.stream().map(GitUpdateFailure::ref).collect(toImmutableList());
diff --git a/java/com/google/gerrit/git/LockFailureException.java b/java/com/google/gerrit/git/LockFailureException.java
index 371488d..2908db2 100644
--- a/java/com/google/gerrit/git/LockFailureException.java
+++ b/java/com/google/gerrit/git/LockFailureException.java
@@ -14,6 +14,7 @@
package com.google.gerrit.git;
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.RefUpdate;
@@ -21,6 +22,9 @@
public class LockFailureException extends GitUpdateFailureException {
private static final long serialVersionUID = 1L;
+ private static final String REF_UPDATE_RETURN_CODE_WAS_LOCK_FAILURE =
+ "RefUpdate return code was: LOCK_FAILURE";
+
public LockFailureException(String message, RefUpdate refUpdate) {
super(message, refUpdate);
}
@@ -28,4 +32,15 @@
public LockFailureException(String message, BatchRefUpdate batchRefUpdate) {
super(message, batchRefUpdate);
}
+
+ protected LockFailureException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public static void throwIfLockFailure(ConcurrentRefUpdateException e)
+ throws LockFailureException {
+ if (e.getMessage().contains(REF_UPDATE_RETURN_CODE_WAS_LOCK_FAILURE)) {
+ throw new LockFailureException(e.getMessage(), e);
+ }
+ }
}
diff --git a/java/com/google/gerrit/server/restapi/project/CreateTag.java b/java/com/google/gerrit/server/restapi/project/CreateTag.java
index 34c3ff7..b12ceef 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateTag.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateTag.java
@@ -29,6 +29,7 @@
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
+import com.google.gerrit.git.LockFailureException;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -48,6 +49,7 @@
import java.time.ZoneId;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.TagCommand;
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@@ -141,18 +143,23 @@
.newCommitterIdent(TimeUtil.now(), ZoneId.systemDefault()));
}
- Ref result = tag.call();
- tagCache.updateFastForward(
- resource.getNameKey(), ref, ObjectId.zeroId(), result.getObjectId());
- referenceUpdated.fire(
- resource.getNameKey(),
- ref,
- ObjectId.zeroId(),
- result.getObjectId(),
- resource.getUser().asIdentifiedUser().state());
- try (RevWalk w = new RevWalk(repo)) {
- return Response.created(
- ListTags.createTagInfo(perm, result, w, resource.getProjectState(), links));
+ try {
+ Ref result = tag.call();
+ tagCache.updateFastForward(
+ resource.getNameKey(), ref, ObjectId.zeroId(), result.getObjectId());
+ referenceUpdated.fire(
+ resource.getNameKey(),
+ ref,
+ ObjectId.zeroId(),
+ result.getObjectId(),
+ resource.getUser().asIdentifiedUser().state());
+ try (RevWalk w = new RevWalk(repo)) {
+ return Response.created(
+ ListTags.createTagInfo(perm, result, w, resource.getProjectState(), links));
+ }
+ } catch (ConcurrentRefUpdateException e) {
+ LockFailureException.throwIfLockFailure(e);
+ throw e;
}
}
} catch (GitAPIException e) {