From c2a9e7d1dbfa2475c386f142191d5748d8eaa99f Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 25 Jun 2018 16:45:08 -0400 Subject: [PATCH 1/2] [JENKINS-37575] Keep track of how much content has been copied by writeLog even if the callable is interrupted. --- .../durabletask/FileMonitoringTask.java | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/durabletask/FileMonitoringTask.java b/src/main/java/org/jenkinsci/plugins/durabletask/FileMonitoringTask.java index cbae4446..6b964c18 100644 --- a/src/main/java/org/jenkinsci/plugins/durabletask/FileMonitoringTask.java +++ b/src/main/java/org/jenkinsci/plugins/durabletask/FileMonitoringTask.java @@ -50,7 +50,7 @@ import java.util.logging.Logger; import jenkins.MasterToSlaveFileCallable; import org.apache.commons.io.IOUtils; -import org.jenkinsci.remoting.RoleChecker; +import org.apache.commons.io.output.CountingOutputStream; import javax.annotation.CheckForNull; @@ -125,43 +125,41 @@ protected FileMonitoringController(FilePath ws) throws IOException, InterruptedE @Override public final boolean writeLog(FilePath workspace, OutputStream sink) throws IOException, InterruptedException { FilePath log = getLogFile(workspace); - Long newLocation = log.act(new WriteLog(lastLocation, new RemoteOutputStream(sink))); - if (newLocation != null) { - LOGGER.log(Level.FINE, "copied {0} bytes from {1}", new Object[] {newLocation - lastLocation, log}); - lastLocation = newLocation; - return true; - } else { - return false; + CountingOutputStream cos = new CountingOutputStream(sink); + long start = System.currentTimeMillis(); + try { + log.act(new WriteLog(lastLocation, new RemoteOutputStream(cos))); + return cos.getByteCount() > 0; + } finally { // even if RemoteOutputStream write was interrupted, record what we actually received + long written = cos.getByteCount(); + if (written > 0) { + LOGGER.log(Level.FINE, "copied {0} bytes from {1}", new Object[] {written, log}); + lastLocation += written; + } } } - private static class WriteLog extends MasterToSlaveFileCallable { + private static class WriteLog extends MasterToSlaveFileCallable { private final long lastLocation; private final OutputStream sink; WriteLog(long lastLocation, OutputStream sink) { this.lastLocation = lastLocation; this.sink = sink; } - @Override public Long invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + @Override public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { long len = f.length(); if (len > lastLocation) { - RandomAccessFile raf = new RandomAccessFile(f, "r"); - try { + try (RandomAccessFile raf = new RandomAccessFile(f, "r")) { raf.seek(lastLocation); long toRead = len - lastLocation; if (toRead > Integer.MAX_VALUE) { // >2Gb of output at once is unlikely throw new IOException("large reads not yet implemented"); } - // TODO is this efficient for large amounts of output? Would it be better to stream data, or return a byte[] from the callable? byte[] buf = new byte[(int) toRead]; raf.readFully(buf); sink.write(buf); - } finally { - raf.close(); } - return len; - } else { - return null; } + return null; } } From 6aa54967fd945beabbe8af265d6b61adc6565e6e Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 25 Jun 2018 17:03:55 -0400 Subject: [PATCH 2/2] Forgotten dead code. --- .../org/jenkinsci/plugins/durabletask/FileMonitoringTask.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/durabletask/FileMonitoringTask.java b/src/main/java/org/jenkinsci/plugins/durabletask/FileMonitoringTask.java index 6b964c18..a3c66189 100644 --- a/src/main/java/org/jenkinsci/plugins/durabletask/FileMonitoringTask.java +++ b/src/main/java/org/jenkinsci/plugins/durabletask/FileMonitoringTask.java @@ -126,7 +126,6 @@ protected FileMonitoringController(FilePath ws) throws IOException, InterruptedE @Override public final boolean writeLog(FilePath workspace, OutputStream sink) throws IOException, InterruptedException { FilePath log = getLogFile(workspace); CountingOutputStream cos = new CountingOutputStream(sink); - long start = System.currentTimeMillis(); try { log.act(new WriteLog(lastLocation, new RemoteOutputStream(cos))); return cos.getByteCount() > 0;