RepoCommand: Retry commit on LockFailure
When the target repository is receiving commits from other sources, the repo command commit can fail with a LOCK_FAILURE. We could let callers retry, but then the command needs to redo all the work (opening all subrepos to recreate the tree). Retry the commit in LOCK_FAILURE inside the command. The commit rewrites the whole tree, so it shouldn't have merge errors. Use an exponential delay with jitter for the retries. Change-Id: I517b6f2afd16a4b695e6cf471b5d6cf492024ec4 Signed-off-by: Ivan Frade <ifrade@google.com>
This commit is contained in:
parent
0667b8ec4d
commit
c59626ad7a
|
@ -80,6 +80,13 @@
|
|||
* @since 3.4
|
||||
*/
|
||||
public class RepoCommand extends GitCommand<RevCommit> {
|
||||
private static final int LOCK_FAILURE_MAX_RETRIES = 5;
|
||||
|
||||
// Retry exponentially with delays in this range
|
||||
private static final int LOCK_FAILURE_MIN_RETRY_DELAY_MILLIS = 50;
|
||||
|
||||
private static final int LOCK_FAILURE_MAX_RETRY_DELAY_MILLIS = 5000;
|
||||
|
||||
private String manifestPath;
|
||||
private String baseUri;
|
||||
private URI targetUri;
|
||||
|
@ -686,7 +693,42 @@ public RevCommit call() throws GitAPIException {
|
|||
builder.finish();
|
||||
ObjectId treeId = index.writeTree(inserter);
|
||||
|
||||
// Create a Commit object, populate it and write it
|
||||
long prevDelay = 0;
|
||||
for (int i = 0; i < LOCK_FAILURE_MAX_RETRIES - 1; i++) {
|
||||
try {
|
||||
return commitTreeOnCurrentTip(
|
||||
inserter, rw, treeId);
|
||||
} catch (ConcurrentRefUpdateException e) {
|
||||
prevDelay = FileUtils.delay(prevDelay,
|
||||
LOCK_FAILURE_MIN_RETRY_DELAY_MILLIS,
|
||||
LOCK_FAILURE_MAX_RETRY_DELAY_MILLIS);
|
||||
Thread.sleep(prevDelay);
|
||||
repo.getRefDatabase().refresh();
|
||||
}
|
||||
}
|
||||
// In the last try, just propagate the exceptions
|
||||
return commitTreeOnCurrentTip(inserter, rw, treeId);
|
||||
} catch (GitAPIException | IOException | InterruptedException e) {
|
||||
throw new ManifestErrorException(e);
|
||||
}
|
||||
}
|
||||
try (Git git = new Git(repo)) {
|
||||
for (RepoProject proj : filteredProjects) {
|
||||
addSubmodule(proj.getName(), proj.getUrl(), proj.getPath(),
|
||||
proj.getRevision(), proj.getCopyFiles(),
|
||||
proj.getLinkFiles(), git);
|
||||
}
|
||||
return git.commit().setMessage(RepoText.get().repoCommitMessage)
|
||||
.call();
|
||||
} catch (GitAPIException | IOException e) {
|
||||
throw new ManifestErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private RevCommit commitTreeOnCurrentTip(ObjectInserter inserter,
|
||||
RevWalk rw, ObjectId treeId)
|
||||
throws IOException, ConcurrentRefUpdateException {
|
||||
ObjectId headId = repo.resolve(targetBranch + "^{commit}"); //$NON-NLS-1$
|
||||
if (headId != null && rw.parseCommit(headId).getTree().getId().equals(treeId)) {
|
||||
// No change. Do nothing.
|
||||
|
@ -708,7 +750,6 @@ public RevCommit call() throws GitAPIException {
|
|||
ru.setNewObjectId(commitId);
|
||||
ru.setExpectedOldObjectId(headId != null ? headId : ObjectId.zeroId());
|
||||
Result rc = ru.update(rw);
|
||||
|
||||
switch (rc) {
|
||||
case NEW:
|
||||
case FORCED:
|
||||
|
@ -717,11 +758,9 @@ public RevCommit call() throws GitAPIException {
|
|||
break;
|
||||
case REJECTED:
|
||||
case LOCK_FAILURE:
|
||||
throw new ConcurrentRefUpdateException(
|
||||
MessageFormat.format(
|
||||
JGitText.get().cannotLock, targetBranch),
|
||||
ru.getRef(),
|
||||
rc);
|
||||
throw new ConcurrentRefUpdateException(MessageFormat
|
||||
.format(JGitText.get().cannotLock, targetBranch),
|
||||
ru.getRef(), rc);
|
||||
default:
|
||||
throw new JGitInternalException(MessageFormat.format(
|
||||
JGitText.get().updatingRefFailed,
|
||||
|
@ -729,21 +768,6 @@ public RevCommit call() throws GitAPIException {
|
|||
}
|
||||
|
||||
return rw.parseCommit(commitId);
|
||||
} catch (GitAPIException | IOException e) {
|
||||
throw new ManifestErrorException(e);
|
||||
}
|
||||
}
|
||||
try (Git git = new Git(repo)) {
|
||||
for (RepoProject proj : filteredProjects) {
|
||||
addSubmodule(proj.getName(), proj.getUrl(), proj.getPath(),
|
||||
proj.getRevision(), proj.getCopyFiles(),
|
||||
proj.getLinkFiles(), git);
|
||||
}
|
||||
return git.commit().setMessage(RepoText.get().repoCommitMessage)
|
||||
.call();
|
||||
} catch (GitAPIException | IOException e) {
|
||||
throw new ManifestErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addSubmodule(String name, String url, String path,
|
||||
|
|
Loading…
Reference in New Issue