You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by la...@apache.org on 2012/04/18 22:31:18 UTC

svn commit: r1327668 - /hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/regionserver/wal/HLogPerformanceEvaluation.java

Author: larsh
Date: Wed Apr 18 20:31:18 2012
New Revision: 1327668

URL: http://svn.apache.org/viewvc?rev=1327668&view=rev
Log:
HBASE-5792 HLog Performance Evaluation Tool

Added:
    hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/regionserver/wal/HLogPerformanceEvaluation.java

Added: hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/regionserver/wal/HLogPerformanceEvaluation.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/regionserver/wal/HLogPerformanceEvaluation.java?rev=1327668&view=auto
==============================================================================
--- hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/regionserver/wal/HLogPerformanceEvaluation.java (added)
+++ hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/regionserver/wal/HLogPerformanceEvaluation.java Wed Apr 18 20:31:18 2012
@@ -0,0 +1,355 @@
+/**
+ * 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.hbase.regionserver.wal;
+
+import java.util.Map;
+import java.util.List;
+import java.util.Random;
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.util.Tool;
+import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.conf.Configured;
+import org.apache.hadoop.classification.InterfaceAudience;
+
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.regionserver.HRegion;
+import org.apache.hadoop.hbase.regionserver.wal.HLog;
+import org.apache.hadoop.hbase.regionserver.wal.HLog.Entry;
+import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
+
+/**
+ * This class runs performance benchmarks for {@link HLog}.
+ * See usage for this tool by running:
+ * <code>$ hbase org.apache.hadoop.hbase.regionserver.wal.HLogPerformanceEvaluation -h</code>
+ */
+@InterfaceAudience.Private
+public final class HLogPerformanceEvaluation extends Configured implements Tool {
+  static final Log LOG = LogFactory.getLog(HLogPerformanceEvaluation.class.getName());
+
+  private final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
+
+  static final String TABLE_NAME = "HLogPerformanceEvaluation";
+  static final String QUALIFIER_PREFIX = "q";
+  static final String FAMILY_PREFIX = "cf";
+
+  private int numQualifiers = 1;
+  private int valueSize = 512;
+  private int keySize = 16;
+
+  /**
+   * Perform HLog.append() of Put object, for the number of iterations requested.
+   * Keys and Vaues are generated randomly, the number of column familes,
+   * qualifiers and key/value size is tunable by the user.
+   */
+  class HLogPutBenchmark implements Runnable {
+    private final long numIterations;
+    private final int numFamilies;
+    private final boolean noSync;
+    private final HRegion region;
+    private final HTableDescriptor htd;
+
+    HLogPutBenchmark(final HRegion region, final HTableDescriptor htd,
+        final long numIterations, final boolean noSync) {
+      this.numIterations = numIterations;
+      this.noSync = noSync;
+      this.numFamilies = htd.getColumnFamilies().length;
+      this.region = region;
+      this.htd = htd;
+    }
+
+    public void run() {
+      byte[] key = new byte[keySize];
+      byte[] value = new byte[valueSize];
+      Random rand = new Random(Thread.currentThread().getId());
+      HLog hlog = region.getLog();
+
+      try {
+        long startTime = System.currentTimeMillis();
+        for (int i = 0; i < numIterations; ++i) {
+          Put put = setupPut(rand, key, value, numFamilies);
+          long now = System.currentTimeMillis();
+          WALEdit walEdit = new WALEdit();
+          addFamilyMapToWALEdit(put.getFamilyMap(), walEdit);
+          HRegionInfo hri = region.getRegionInfo();
+          if (this.noSync) {
+            hlog.appendNoSync(hri, hri.getTableName(), walEdit,
+                              HConstants.DEFAULT_CLUSTER_ID, now, htd);
+          } else {
+            hlog.append(hri, hri.getTableName(), walEdit, now, htd);
+          }
+        }
+        long totalTime = (System.currentTimeMillis() - startTime);
+        logBenchmarkResult(Thread.currentThread().getName(), numIterations, totalTime);
+      } catch (Exception e) {
+        LOG.error(getClass().getSimpleName() + " Thread failed", e);
+      }
+    }
+  }
+
+  @Override
+  public int run(String[] args) throws Exception {
+    Path rootRegionDir = null;
+    int numThreads = 1;
+    long numIterations = 10000;
+    int numFamilies = 1;
+    boolean noSync = false;
+    boolean verify = false;
+    boolean verbose = false;
+    long roll = Long.MAX_VALUE;
+    // Process command line args
+    for (int i = 0; i < args.length; i++) {
+      String cmd = args[i];
+      try {
+        if (cmd.equals("-threads")) {
+          numThreads = Integer.parseInt(args[++i]);
+        } else if (cmd.equals("-iterations")) {
+          numIterations = Long.parseLong(args[++i]);
+        } else if (cmd.equals("-path")) {
+          rootRegionDir = new Path(args[++i]);
+        } else if (cmd.equals("-families")) {
+          numFamilies = Integer.parseInt(args[++i]);
+        } else if (cmd.equals("-qualifiers")) {
+          numQualifiers = Integer.parseInt(args[++i]);
+        } else if (cmd.equals("-keySize")) {
+          keySize = Integer.parseInt(args[++i]);
+        } else if (cmd.equals("-valueSize")) {
+          valueSize = Integer.parseInt(args[++i]);
+        } else if (cmd.equals("-nosync")) {
+          noSync = true;
+        } else if (cmd.equals("-verify")) {
+          verify = true;
+        } else if (cmd.equals("-verbose")) {
+          verbose = true;
+        } else if (cmd.equals("-roll")) {
+          roll = Long.parseLong(args[++i]);
+        } else if (cmd.equals("-h")) {
+          printUsageAndExit();
+        } else if (cmd.equals("--help")) {
+          printUsageAndExit();
+        } else {
+          System.err.println("UNEXPECTED: " + cmd);
+          printUsageAndExit();
+        }
+      } catch (Exception e) {
+        printUsageAndExit();
+      }
+    }
+
+    // Run HLog Performance Evaluation
+    FileSystem fs = FileSystem.get(getConf());
+    LOG.info("" + fs);
+    try {
+      if (rootRegionDir == null) {
+        rootRegionDir = TEST_UTIL.getDataTestDir("HLogPerformanceEvaluation");
+      }
+      rootRegionDir = rootRegionDir.makeQualified(fs);
+      cleanRegionRootDir(fs, rootRegionDir);
+      // Initialize Table Descriptor
+      HTableDescriptor htd = createHTableDescriptor(numFamilies);
+      final long whenToRoll = roll;
+      HLog hlog = new HLog(fs, new Path(rootRegionDir, "wals"),
+          new Path(rootRegionDir, "old.wals"), getConf()) {
+        int appends = 0;
+        protected void doWrite(HRegionInfo info, HLogKey logKey, WALEdit logEdit,
+            HTableDescriptor htd)
+        throws IOException {
+          this.appends++;
+          if (this.appends % whenToRoll == 0) {
+            LOG.info("Rolling after " + appends + " edits");
+            rollWriter();
+          }
+          super.doWrite(info, logKey, logEdit, htd);
+        };
+      };
+      hlog.rollWriter();
+      HRegion region = null;
+      try {
+        region = openRegion(fs, rootRegionDir, htd, hlog);
+        long putTime = runBenchmark(new HLogPutBenchmark(region, htd, numIterations, noSync), numThreads);
+        logBenchmarkResult("Summary: threads=" + numThreads + ", iterations=" + numIterations,
+          numIterations * numThreads, putTime);
+        if (region != null) {
+          closeRegion(region);
+          region = null;
+        }
+        if (verify) {
+          Path dir = hlog.getDir();
+          long editCount = 0;
+          for (FileStatus fss: fs.listStatus(dir)) {
+            editCount += verify(fss.getPath(), verbose);
+          }
+          long expected = numIterations * numThreads;
+          if (editCount != expected) {
+            throw new IllegalStateException("Counted=" + editCount + ", expected=" + expected);
+          }
+        }
+      } finally {
+        if (region != null) closeRegion(region);
+        // Remove the root dir for this test region
+        cleanRegionRootDir(fs, rootRegionDir);
+      }
+    } finally {
+      fs.close();
+    }
+
+    return(0);
+  }
+
+  private static HTableDescriptor createHTableDescriptor(final int numFamilies) {
+    HTableDescriptor htd = new HTableDescriptor(TABLE_NAME);
+    for (int i = 0; i < numFamilies; ++i) {
+      HColumnDescriptor colDef = new HColumnDescriptor(FAMILY_PREFIX + i);
+      htd.addFamily(colDef);
+    }
+    return htd;
+  }
+
+  /**
+   * Verify the content of the WAL file.
+   * Verify that sequenceids are ascending and that the file has expected number
+   * of edits.
+   * @param wal
+   * @return Count of edits.
+   * @throws IOException
+   */
+  private long verify(final Path wal, final boolean verbose) throws IOException {
+    HLog.Reader reader = HLog.getReader(wal.getFileSystem(getConf()), wal, getConf());
+    long previousSeqid = -1;
+    long count = 0;
+    try {
+      while (true) {
+        Entry e = reader.next();
+        if (e == null) break;
+        count++;
+        long seqid = e.getKey().getLogSeqNum();
+        if (verbose) LOG.info("seqid=" + seqid);
+        if (previousSeqid >= seqid) {
+          throw new IllegalStateException("wal=" + wal.getName() +
+            ", previousSeqid=" + previousSeqid + ", seqid=" + seqid);
+        }
+        previousSeqid = seqid;
+      }
+    } finally {
+      reader.close();
+    }
+    return count;
+  }
+
+  private static void logBenchmarkResult(String testName, long numTests, long totalTime) {
+    float tsec = totalTime / 1000.0f;
+    LOG.info(String.format("%s took %.3fs %.3fops/s", testName, tsec, numTests / tsec));
+  }
+
+  private void printUsageAndExit() {
+    System.err.printf("Usage: bin/hbase %s [options]\n", getClass().getName());
+    System.err.println(" where [options] are:");
+    System.err.println("  -h|-help         Show this help and exit.");
+    System.err.println("  -threads <N>     Number of threads writing on the WAL.");
+    System.err.println("  -iterations <N>  Number of iterations per thread.");
+    System.err.println("  -path <PATH>     Path where region's root directory is created.");
+    System.err.println("  -families <N>    Number of column families to write.");
+    System.err.println("  -qualifiers <N>  Number of qualifiers to write.");
+    System.err.println("  -keySize <N>     Row key size in byte.");
+    System.err.println("  -valueSize <N>   Row/Col value size in byte.");
+    System.err.println("  -nosync          Append without syncing");
+    System.err.println("  -verify          Verify edits written in sequence");
+    System.err.println("  -verbose         Output extra info; e.g. all edit seq ids when verifying");
+    System.err.println("  -roll <N>        Roll the way every N appends");
+    System.err.println("");
+    System.err.println("Examples:");
+    System.err.println("");
+    System.err.println(" To run 100 threads on hdfs with log rolling every 10k edits and verification afterward do:");
+    System.err.println(" $ ./bin/hbase org.apache.hadoop.hbase.regionserver.wal.HLogPerformanceEvaluation \\");
+    System.err.println("    -conf ./core-site.xml -path hdfs://example.org:7000/tmp -threads 100 -roll 10000 -verify");
+    System.exit(1);
+  }
+
+  private HRegion openRegion(final FileSystem fs, final Path dir, final HTableDescriptor htd, final HLog hlog)
+  throws IOException {
+    // Initialize HRegion
+    HRegionInfo regionInfo = new HRegionInfo(htd.getName());
+    return HRegion.createHRegion(regionInfo, dir, getConf(), htd, hlog);
+  }
+
+  private void closeRegion(final HRegion region) throws IOException {
+    if (region != null) {
+      region.close();
+      HLog wal = region.getLog();
+      if (wal != null) wal.close();
+    }
+  }
+
+  private void cleanRegionRootDir(final FileSystem fs, final Path dir) throws IOException {
+    if (fs.exists(dir)) {
+      fs.delete(dir, true);
+    }
+  }
+
+  private Put setupPut(Random rand, byte[] key, byte[] value, final int numFamilies) {
+    rand.nextBytes(key);
+    Put put = new Put(key);
+    for (int cf = 0; cf < numFamilies; ++cf) {
+      for (int q = 0; q < numQualifiers; ++q) {
+        rand.nextBytes(value);
+        put.add(Bytes.toBytes(FAMILY_PREFIX + cf), Bytes.toBytes(QUALIFIER_PREFIX + q), value);
+      }
+    }
+    return put;
+  }
+
+  private void addFamilyMapToWALEdit(Map<byte[], List<KeyValue>> familyMap, WALEdit walEdit) {
+    for (List<KeyValue> edits : familyMap.values()) {
+      for (KeyValue kv : edits) {
+        walEdit.add(kv);
+      }
+    }
+  }
+
+  private long runBenchmark(Runnable runnable, final int numThreads) throws InterruptedException {
+    Thread[] threads = new Thread[numThreads];
+    long startTime = System.currentTimeMillis();
+    for (int i = 0; i < numThreads; ++i) {
+      threads[i] = new Thread(runnable);
+      threads[i].start();
+    }
+    for (Thread t : threads) t.join();
+    long endTime = System.currentTimeMillis();
+    return(endTime - startTime);
+  }
+
+  public static void main(String[] args) throws Exception {
+    int exitCode = ToolRunner.run(HBaseConfiguration.create(), new HLogPerformanceEvaluation(), args);
+    System.exit(exitCode);
+  }
+}