ProjectRepairer: Report an interrupted copy as InterruptedIOException copy() declares InterruptedException, but the only one that escapes comes from the halt() call in its own catch block, leaving copyInOrder() to catch an interrupt it takes no part in. The interrupt copy() does take part in becomes a -1 exit code, and as throwing InterruptedException clears the interrupt status, nothing above can tell an interrupted transfer from a failed one, or that the repair was cancelled at all. Absorb the halt() interrupt where it happens, and report the copy's own interrupt as InterruptedIOException, which is what java.io raises when an interrupt terminates a transfer. It unwinds the repair, so the copies and destinations left after a cancel are skipped instead of running to completion. The SSH command lets it escape, which BaseCommand already treats as the client having dropped off, exiting 127 without logging. Auto-repair logs the interrupt and skips its follow-up replication. Change-Id: Ie61e0329aaa3131c8ac1f8b4932e57e48c5e5e0b
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/AutoRepairHandler.java b/src/main/java/com/googlesource/gerrit/plugins/replication/AutoRepairHandler.java index 1be8758..b1462c4 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/AutoRepairHandler.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/AutoRepairHandler.java
@@ -24,6 +24,7 @@ import com.googlesource.gerrit.plugins.replication.api.ReplicationConfig; import com.googlesource.gerrit.plugins.replication.events.dispatcher.EventDispatcher; import java.io.ByteArrayOutputStream; +import java.io.InterruptedIOException; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; @@ -89,7 +90,14 @@ @Override public void run() { ByteArrayOutputStream buf = new ByteArrayOutputStream(); - boolean isRepaired = projectRepairer.repair(project, uri, buf, true); + boolean isRepaired; + try { + isRepaired = projectRepairer.repair(project, uri, buf, true); + } catch (InterruptedIOException e) { + repLog.atWarning().withCause(e).log( + "Auto-repair interrupted for project %s to %s", project.get(), uri); + return; + } (isRepaired ? repLog.atInfo() : repLog.atWarning()) .log( "Auto-repair %s for project %s to %s:%s",
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ProjectRepairer.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ProjectRepairer.java index 669a374..b9b8f82 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/ProjectRepairer.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ProjectRepairer.java
@@ -23,6 +23,7 @@ import com.google.inject.Singleton; import com.googlesource.gerrit.plugins.replication.api.ReplicationConfig; import java.io.IOException; +import java.io.InterruptedIOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; @@ -44,7 +45,8 @@ this.replicationConfig = replicationConfig; } - public boolean repair(Project.NameKey project, URIish uri, OutputStream out, boolean copyPacks) { + public boolean repair(Project.NameKey project, URIish uri, OutputStream out, boolean copyPacks) + throws InterruptedIOException { if (copyPacks && !copyPackTo(project, uri, out)) { repLog.atSevere().log("Repair failed for %s on %s", project.get(), uri); return false; @@ -56,7 +58,8 @@ return AdminApiFactory.isSSH(uri) && !AdminApiFactory.isGerrit(uri); } - private boolean copyPackTo(Project.NameKey project, URIish uri, OutputStream out) { + private boolean copyPackTo(Project.NameKey project, URIish uri, OutputStream out) + throws InterruptedIOException { if (Strings.isNullOrEmpty(uri.getHost())) { repLog.atSevere().log("Cannot repair %s: URI has no host: %s", project.get(), uri); return false; @@ -82,18 +85,14 @@ return copyInOrder(packDir, uri, out); } - private boolean copyInOrder(Path packDir, URIish uri, OutputStream out) { - try { - return copy(packDir, uri, out, "*.pack") == 0 - && copy(packDir, uri, out, "*.idx", "*.bitmap", "*.rev") == 0; - } catch (InterruptedException e) { - repLog.atWarning().withCause(e).log("Interrupted during copy to %s", uri); - return false; - } + private boolean copyInOrder(Path packDir, URIish uri, OutputStream out) + throws InterruptedIOException { + return copy(packDir, uri, out, "*.pack") == 0 + && copy(packDir, uri, out, "*.idx", "*.bitmap", "*.rev") == 0; } private int copy(Path src, URIish uri, OutputStream out, String... includes) - throws InterruptedException { + throws InterruptedIOException { List<String> cmd = new ArrayList<>(); cmd.add(replicationConfig.getRsyncPath()); cmd.add("-av"); @@ -131,8 +130,13 @@ return code; } catch (InterruptedException e) { p.destroyForcibly(); - outStream.halt(); - return -1; + try { + outStream.halt(); + } catch (InterruptedException ignored) { + // ignore + } + throw (InterruptedIOException) + new InterruptedIOException("Interrupted during copy to " + uri).initCause(e); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/RepairCommand.java b/src/main/java/com/googlesource/gerrit/plugins/replication/RepairCommand.java index dd5e506..ad6648a 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/RepairCommand.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/RepairCommand.java
@@ -23,6 +23,7 @@ import com.google.inject.Inject; import com.googlesource.gerrit.plugins.replication.api.ReplicationConfig; import java.io.IOException; +import java.io.InterruptedIOException; import java.io.OutputStream; import java.util.Collection; import java.util.Collections; @@ -62,7 +63,7 @@ private final Object outputLock = new Object(); @Override - protected void run() throws Failure { + protected void run() throws Failure, InterruptedIOException { Project.NameKey project = Project.nameKey(projectName); try { if (projectCache.get(project).isEmpty()) { @@ -82,7 +83,7 @@ } } - private Set<URIish> repair(Project.NameKey project) throws Failure { + private Set<URIish> repair(Project.NameKey project) throws Failure, InterruptedIOException { Set<URIish> copyTargets = new HashSet<>(); Collection<URIish> destUris = destinations