You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by sh...@apache.org on 2015/02/19 09:16:42 UTC

hadoop git commit: MAPREDUCE-6228. Add truncate operation to SLive. Contributed by Plamen Jeliazkov.

Repository: hadoop
Updated Branches:
  refs/heads/branch-2 10da1bfce -> 32d215dc8


MAPREDUCE-6228. Add truncate operation to SLive. Contributed by Plamen Jeliazkov.

Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/32d215dc
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/32d215dc
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/32d215dc

Branch: refs/heads/branch-2
Commit: 32d215dc89726c87d35a39ab881760cd9b92e4df
Parents: 10da1bf
Author: Plamen Jeliazkov <pl...@gmail.com>
Authored: Thu Feb 19 00:14:11 2015 -0800
Committer: Konstantin V Shvachko <sh...@apache.org>
Committed: Thu Feb 19 00:14:11 2015 -0800

----------------------------------------------------------------------
 hadoop-mapreduce-project/CHANGES.txt            |   6 +-
 .../apache/hadoop/fs/slive/ArgumentParser.java  |   2 +
 .../apache/hadoop/fs/slive/ConfigExtractor.java |  59 ++++++++++
 .../apache/hadoop/fs/slive/ConfigMerger.java    |  35 ++++++
 .../apache/hadoop/fs/slive/ConfigOption.java    |   9 ++
 .../org/apache/hadoop/fs/slive/Constants.java   |   4 +-
 .../hadoop/fs/slive/OperationFactory.java       |   3 +
 .../org/apache/hadoop/fs/slive/TestSlive.java   |  27 +++++
 .../org/apache/hadoop/fs/slive/TruncateOp.java  | 114 +++++++++++++++++++
 9 files changed, 255 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hadoop/blob/32d215dc/hadoop-mapreduce-project/CHANGES.txt
----------------------------------------------------------------------
diff --git a/hadoop-mapreduce-project/CHANGES.txt b/hadoop-mapreduce-project/CHANGES.txt
index 79c14ca..da119f6 100644
--- a/hadoop-mapreduce-project/CHANGES.txt
+++ b/hadoop-mapreduce-project/CHANGES.txt
@@ -6,6 +6,10 @@ Release 2.7.0 - UNRELEASED
 
   NEW FEATURES
 
+    MAPREDUCE-6227. DFSIO for truncate. (shv via yliu)
+
+    MAPREDUCE-6228. Add truncate operation to SLive. (Plamen Jeliazkov via shv)
+
   IMPROVEMENTS
 
     MAPREDUCE-6149. Document override log4j.properties in MR job.
@@ -42,8 +46,6 @@ Release 2.7.0 - UNRELEASED
     MAPREDUCE-5800. Use Job#getInstance instead of deprecated constructors
     (aajisaka)
 
-    MAPREDUCE-6227. DFSIO for truncate. (shv via yliu)
-
     MAPREDUCE-6253. Update use of Iterator to Iterable. (Ray Chiang via devaraj)
 
     MAPREDUCE-5335. Rename Job Tracker terminology in ShuffleSchedulerImpl.

http://git-wip-us.apache.org/repos/asf/hadoop/blob/32d215dc/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ArgumentParser.java
----------------------------------------------------------------------
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ArgumentParser.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ArgumentParser.java
index 19a55ff..12df4dc 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ArgumentParser.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ArgumentParser.java
@@ -144,6 +144,7 @@ class ArgumentParser {
     cliopt.addOption(ConfigOption.DURATION);
     cliopt.addOption(ConfigOption.EXIT_ON_ERROR);
     cliopt.addOption(ConfigOption.SLEEP_TIME);
+    cliopt.addOption(ConfigOption.TRUNCATE_WAIT);
     cliopt.addOption(ConfigOption.FILES);
     cliopt.addOption(ConfigOption.DIR_SIZE);
     cliopt.addOption(ConfigOption.BASE_DIR);
@@ -167,6 +168,7 @@ class ArgumentParser {
     cliopt.addOption(ConfigOption.READ_SIZE);
     cliopt.addOption(ConfigOption.WRITE_SIZE);
     cliopt.addOption(ConfigOption.APPEND_SIZE);
+    cliopt.addOption(ConfigOption.TRUNCATE_SIZE);
     cliopt.addOption(ConfigOption.RANDOM_SEED);
     cliopt.addOption(ConfigOption.QUEUE_NAME);
     cliopt.addOption(ConfigOption.HELP);

http://git-wip-us.apache.org/repos/asf/hadoop/blob/32d215dc/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigExtractor.java
----------------------------------------------------------------------
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigExtractor.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigExtractor.java
index a03c812..ef4e436 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigExtractor.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigExtractor.java
@@ -131,6 +131,32 @@ class ConfigExtractor {
   }
 
   /**
+   * @return whether the mapper or reducer should wait for truncate recovery
+   */
+  boolean shouldWaitOnTruncate() {
+    return shouldWaitOnTruncate(null);
+  }
+
+  /**
+   * @param primary
+   *          primary the initial string to be used for the value of this
+   *          configuration option (if not provided then config and then the
+   *          default are used)
+   *
+   * @return whether the mapper or reducer should wait for truncate recovery
+   */
+  boolean shouldWaitOnTruncate(String primary) {
+    String val = primary;
+    if (val == null) {
+      val = config.get(ConfigOption.EXIT_ON_ERROR.getCfgOption());
+    }
+    if (val == null) {
+      return ConfigOption.EXIT_ON_ERROR.getDefault();
+    }
+    return Boolean.parseBoolean(val);
+  }
+
+  /**
    * @return the number of reducers to use
    */
   Integer getReducerAmount() {
@@ -537,6 +563,24 @@ class ConfigExtractor {
    * @param primary
    *          the initial string to be used for the value of this configuration
    *          option (if not provided then config and then the default are used)
+   * @return the truncate byte size range (or null if none)
+   */
+  Range<Long> getTruncateSize(String primary) {
+    return getMinMaxBytes(ConfigOption.TRUNCATE_SIZE, primary);
+  }
+
+  /**
+   * @return the truncate byte size range (or null if none) using config and
+   *         default for lookup
+   */
+  Range<Long> getTruncateSize() {
+    return getTruncateSize(null);
+  }
+
+  /**
+   * @param primary
+   *          the initial string to be used for the value of this configuration
+   *          option (if not provided then config and then the default are used)
    * @return the sleep range (or null if none)
    */
   Range<Long> getSleepRange(String primary) {
@@ -600,6 +644,21 @@ class ConfigExtractor {
   }
 
   /**
+   * Returns whether the truncate range should use the block size range
+   *
+   * @return true|false
+   */
+  boolean shouldTruncateUseBlockSize() {
+    Range<Long> truncateRange = getTruncateSize();
+    if (truncateRange == null
+        || (truncateRange.getLower() == truncateRange.getUpper()
+            && (truncateRange.getUpper() == Long.MAX_VALUE))) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
    * Returns whether the read range should use the entire file
    * 
    * @return true|false

http://git-wip-us.apache.org/repos/asf/hadoop/blob/32d215dc/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigMerger.java
----------------------------------------------------------------------
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigMerger.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigMerger.java
index b7be8d8..4bb3500 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigMerger.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigMerger.java
@@ -282,6 +282,18 @@ class ConfigMerger {
             "Error extracting & merging exit on error value", e);
       }
     }
+    // overwrite the truncate wait setting
+    {
+      try {
+        boolean waitOnTruncate = extractor.shouldWaitOnTruncate(opts
+            .getValue(ConfigOption.TRUNCATE_WAIT.getOpt()));
+        base.setBoolean(ConfigOption.TRUNCATE_WAIT.getCfgOption(),
+            waitOnTruncate);
+      } catch (Exception e) {
+        throw new ConfigException(
+            "Error extracting & merging wait on truncate value", e);
+      }
+    }
     // verify and set file limit and ensure > 0
     {
       Integer fileAm = null;
@@ -553,6 +565,29 @@ class ConfigMerger {
             .set(ConfigOption.APPEND_SIZE.getCfgOption(), appendSize.toString());
       }
     }
+    // set the truncate size range
+    {
+      Range<Long> truncateSize = null;
+      try {
+        truncateSize = extractor.getTruncateSize(opts
+            .getValue(ConfigOption.TRUNCATE_SIZE.getOpt()));
+      } catch (Exception e) {
+        throw new ConfigException(
+            "Error extracting & merging truncate size range", e);
+      }
+      if (truncateSize != null) {
+        if (truncateSize.getLower() > truncateSize.getUpper()) {
+          throw new ConfigException(
+              "Truncate size minimum is greater than its maximum");
+        }
+        if (truncateSize.getLower() < 0) {
+          throw new ConfigException(
+              "Truncate size minimum must be greater than or equal to zero");
+        }
+        base
+            .set(ConfigOption.TRUNCATE_SIZE.getCfgOption(), truncateSize.toString());
+      }
+    }
     // set the seed
     {
       Long seed = null;

http://git-wip-us.apache.org/repos/asf/hadoop/blob/32d215dc/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigOption.java
----------------------------------------------------------------------
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigOption.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigOption.java
index 340473a..bd66336 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigOption.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/ConfigOption.java
@@ -97,6 +97,15 @@ class ConfigOption<T> extends Option {
       "Min,max for size to append (min=max=MAX_LONG=blocksize)", SLIVE_PREFIX
           + ".op.append.size", null);
 
+  static final ConfigOption<Boolean> TRUNCATE_WAIT = new ConfigOption<Boolean>(
+      "truncateWait", true, "Should wait for truncate recovery", SLIVE_PREFIX
+      + ".op.truncate.wait", true);
+
+  static final ConfigOption<Long> TRUNCATE_SIZE = new ConfigOption<Long>(
+      "truncateSize", true,
+      "Min,max for size to truncate (min=max=MAX_LONG=blocksize)", SLIVE_PREFIX
+      + ".op.truncate.size", null);
+
   static final ConfigOption<Long> RANDOM_SEED = new ConfigOption<Long>(
       "seed", true, "Random number seed", SLIVE_PREFIX + ".seed", null);
 

http://git-wip-us.apache.org/repos/asf/hadoop/blob/32d215dc/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/Constants.java
----------------------------------------------------------------------
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/Constants.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/Constants.java
index 6f4f442..0642052 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/Constants.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/Constants.java
@@ -43,7 +43,7 @@ class Constants {
    * Allowed operation types
    */
   enum OperationType {
-    READ, APPEND, RENAME, LS, MKDIR, DELETE, CREATE;
+    READ, APPEND, RENAME, LS, MKDIR, DELETE, CREATE, TRUNCATE;
     String lowerName() {
       return this.name().toLowerCase();
     }
@@ -51,7 +51,7 @@ class Constants {
 
   // program info
   static final String PROG_NAME = SliveTest.class.getSimpleName();
-  static final String PROG_VERSION = "0.0.2";
+  static final String PROG_VERSION = "0.1.0";
 
   // useful constants
   static final int MEGABYTES = 1048576;

http://git-wip-us.apache.org/repos/asf/hadoop/blob/32d215dc/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/OperationFactory.java
----------------------------------------------------------------------
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/OperationFactory.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/OperationFactory.java
index 52a4c9f..6af825f 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/OperationFactory.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/OperationFactory.java
@@ -75,6 +75,9 @@ class OperationFactory {
     case CREATE:
       op = new CreateOp(this.config, rnd);
       break;
+    case TRUNCATE:
+      op = new TruncateOp(this.config, rnd);
+      break;
     }
     typedOperations.put(type, op);
     return op;

http://git-wip-us.apache.org/repos/asf/hadoop/blob/32d215dc/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/TestSlive.java
----------------------------------------------------------------------
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/TestSlive.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/TestSlive.java
index 3db7695..25e3340 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/TestSlive.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/TestSlive.java
@@ -136,6 +136,8 @@ public class TestSlive {
       args.add("10");
       args.add("-" + ConfigOption.FILES.getOpt());
       args.add("10");
+      args.add("-" + ConfigOption.TRUNCATE_SIZE.getOpt());
+      args.add("0,1M");
     }
     return args.toArray(new String[args.size()]);
   }
@@ -237,6 +239,9 @@ public class TestSlive {
     Range<Long> wRange = extractor.getWriteSize();
     assertEquals(wRange.getLower().intValue(), Constants.MEGABYTES * 1);
     assertEquals(wRange.getUpper().intValue(), Constants.MEGABYTES * 2);
+    Range<Long> trRange = extractor.getTruncateSize();
+    assertEquals(trRange.getLower().intValue(), 0);
+    assertEquals(trRange.getUpper().intValue(), Constants.MEGABYTES * 1);
     Range<Long> bRange = extractor.getBlockSize();
     assertEquals(bRange.getLower().intValue(), Constants.MEGABYTES * 1);
     assertEquals(bRange.getUpper().intValue(), Constants.MEGABYTES * 2);
@@ -534,4 +539,26 @@ public class TestSlive {
     };
     runOperationOk(extractor, aop, false);
   }
+
+  @Test
+  public void testTruncateOp() throws Exception {
+    // setup a valid config
+    ConfigExtractor extractor = getTestConfig(false);
+    // ensure file created before append
+    final Path fn = new Path(getTestFile().getCanonicalPath());
+    CreateOp op = new CreateOp(extractor, rnd) {
+      protected Path getCreateFile() {
+        return fn;
+      }
+    };
+    runOperationOk(extractor, op, true);
+    // local file system (ChecksumFileSystem) currently doesn't support truncate -
+    // but we'll leave this test here anyways but can't check the results..
+    TruncateOp top = new TruncateOp(extractor, rnd) {
+      protected Path getTruncateFile() {
+        return fn;
+      }
+    };
+    runOperationOk(extractor, top, false);
+  }
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/32d215dc/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/TruncateOp.java
----------------------------------------------------------------------
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/TruncateOp.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/TruncateOp.java
new file mode 100644
index 0000000..c845ac1
--- /dev/null
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/slive/TruncateOp.java
@@ -0,0 +1,114 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.fs.slive;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+import java.util.Random;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.slive.OperationOutput.OutputType;
+
+/**
+ * Operation which selects a random file and truncates a random amount of bytes
+ * (selected from the configuration for truncate size) from that file,
+ * if it exists.
+ * 
+ * This operation will capture statistics on success for bytes written, time
+ * taken (milliseconds), and success count and on failure it will capture the
+ * number of failures and the time taken (milliseconds) to fail.
+ */
+class TruncateOp extends Operation {
+
+  private static final Log LOG = LogFactory.getLog(TruncateOp.class);
+
+  TruncateOp(ConfigExtractor cfg, Random rnd) {
+    super(TruncateOp.class.getSimpleName(), cfg, rnd);
+  }
+
+  /**
+   * Gets the file to truncate from
+   * 
+   * @return Path
+   */
+  protected Path getTruncateFile() {
+    Path fn = getFinder().getFile();
+    return fn;
+  }
+
+  @Override // Operation
+  List<OperationOutput> run(FileSystem fs) {
+    List<OperationOutput> out = super.run(fs);
+    try {
+      Path fn = getTruncateFile();
+      boolean waitOnTruncate = getConfig().shouldWaitOnTruncate();
+      long currentSize = fs.getFileStatus(fn).getLen();
+      // determine file status for file length requirement
+      // to know if should fill in partial bytes
+      Range<Long> truncateSizeRange = getConfig().getTruncateSize();
+      if (getConfig().shouldTruncateUseBlockSize()) {
+        truncateSizeRange = getConfig().getBlockSize();
+      }
+      long truncateSize = Math.max(0L,
+          currentSize - Range.betweenPositive(getRandom(), truncateSizeRange));
+      long timeTaken = 0;
+      LOG.info("Attempting to truncate file at " + fn + " to size "
+          + Helper.toByteInfo(truncateSize));
+      {
+        // truncate
+        long startTime = Timer.now();
+        boolean completed = fs.truncate(fn, truncateSize);
+        if(!completed && waitOnTruncate)
+          waitForRecovery(fs, fn, truncateSize);
+        timeTaken += Timer.elapsed(startTime);
+      }
+      out.add(new OperationOutput(OutputType.LONG, getType(),
+          ReportWriter.BYTES_WRITTEN, 0));
+      out.add(new OperationOutput(OutputType.LONG, getType(),
+          ReportWriter.OK_TIME_TAKEN, timeTaken));
+      out.add(new OperationOutput(OutputType.LONG, getType(),
+          ReportWriter.SUCCESSES, 1L));
+      LOG.info("Truncate file " + fn + " to " + Helper.toByteInfo(truncateSize)
+          + " in " + timeTaken + " milliseconds");
+    } catch (FileNotFoundException e) {
+      out.add(new OperationOutput(OutputType.LONG, getType(),
+          ReportWriter.NOT_FOUND, 1L));
+      LOG.warn("Error with truncating", e);
+    } catch (IOException e) {
+      out.add(new OperationOutput(OutputType.LONG, getType(),
+          ReportWriter.FAILURES, 1L));
+      LOG.warn("Error with truncating", e);
+    }
+    return out;
+  }
+
+  private void waitForRecovery(FileSystem fs, Path fn, long newLength)
+      throws IOException {
+    LOG.info("Waiting on truncate file recovery for " + fn);
+    for(;;) {
+      FileStatus stat = fs.getFileStatus(fn);
+      if(stat.getLen() == newLength) break;
+      try {Thread.sleep(1000);} catch(InterruptedException ignored) {}
+    }
+  }
+}