Author: stack
Date: Wed Mar 16 18:27:19 2011
New Revision: 1082249
URL: http://svn.apache.org/viewvc?rev=1082249&view=rev
Log:
HBASE-3440 Clean out load_table.rb and make sure all roads lead to
completebulkload tool
Modified:
hbase/branches/0.90/CHANGES.txt
hbase/branches/0.90/bin/loadtable.rb
hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/mapreduce/LoadIncrementalHFiles.java
hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/mapreduce/TestLoadIncrementalHFiles.java
Modified: hbase/branches/0.90/CHANGES.txt
URL:
http://svn.apache.org/viewvc/hbase/branches/0.90/CHANGES.txt?rev=1082249&r1=1082248&r2=1082249&view=diff
==============================================================================
--- hbase/branches/0.90/CHANGES.txt (original)
+++ hbase/branches/0.90/CHANGES.txt Wed Mar 16 18:27:19 2011
@@ -52,6 +52,8 @@ Release 0.90.2 - February 9th, 2011
HBASE-3496 HFile CLI Improvements
HBASE-3596 [replication] Wait a few seconds before transferring queues
HBASE-3600 Update our jruby to 1.6.0
+ HBASE-3440 Clean out load_table.rb and make sure all roads lead to
+ completebulkload tool (Vidhyashankar Venkataraman via Stack)
Release 0.90.1 - February 9th, 2011
Modified: hbase/branches/0.90/bin/loadtable.rb
URL:
http://svn.apache.org/viewvc/hbase/branches/0.90/bin/loadtable.rb?rev=1082249&r1=1082248&r2=1082249&view=diff
==============================================================================
--- hbase/branches/0.90/bin/loadtable.rb (original)
+++ hbase/branches/0.90/bin/loadtable.rb Wed Mar 16 18:27:19 2011
@@ -17,134 +17,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-# Script that takes over from
org.apache.hadoop.hbase.mapreduce.HFileOutputFormat.
-# Pass it output directory of HFileOutputFormat. It will read the passed files,
-# move them into place and update the catalog table appropriately. Warning:
-# it will overwrite anything that exists already for passed table.
-# It expects hbase to be up and running so it can insert table info.
-#
-# To see usage for this script, run:
-#
-# ${HBASE_HOME}/bin/hbase org.jruby.Main loadtable.rb
-#
-include Java
-import java.util.TreeMap
-import org.apache.hadoop.hbase.client.HTable
-import org.apache.hadoop.hbase.client.Put
-import org.apache.hadoop.hbase.util.FSUtils
-import org.apache.hadoop.hbase.util.Bytes
-import org.apache.hadoop.hbase.util.Writables
-import org.apache.hadoop.hbase.HConstants
-import org.apache.hadoop.hbase.HBaseConfiguration
-import org.apache.hadoop.hbase.HRegionInfo
-import org.apache.hadoop.hbase.HTableDescriptor
-import org.apache.hadoop.hbase.HColumnDescriptor
-import org.apache.hadoop.hbase.HRegionInfo
-import org.apache.hadoop.hbase.io.hfile.HFile
-import org.apache.hadoop.fs.Path
-import org.apache.hadoop.fs.FileSystem
-import org.apache.hadoop.mapred.OutputLogFilter
-import org.apache.commons.logging.Log
-import org.apache.commons.logging.LogFactory
-
-# Name of this script
-NAME = "loadtable"
-
-# Print usage for this script
-def usage
- puts 'Usage: %s.rb TABLENAME HFILEOUTPUTFORMAT_OUTPUT_DIR' % NAME
- exit!
-end
-
-# Passed 'dir' exists and is a directory else exception
-def isDirExists(fs, dir)
- raise IOError.new("Does not exit: " + dir.toString()) unless fs.exists(dir)
- raise IOError.new("Not a directory: " + dir.toString()) unless
fs.isDirectory(dir)
-end
-
-# Check arguments
-if ARGV.size != 2
- usage
-end
-
-# Check good table names were passed.
-tableName = HTableDescriptor.isLegalTableName(ARGV[0].to_java_bytes)
-outputdir = Path.new(ARGV[1])
-
-# Get configuration to use.
-c = HBaseConfiguration.new()
-# Get a logger and a metautils instance.
-LOG = LogFactory.getLog(NAME)
-
-# Set hadoop filesystem configuration using the hbase.rootdir.
-# Otherwise, we'll always use localhost though the hbase.rootdir
-# might be pointing at hdfs location.
-c.set("fs.defaultFS", c.get(HConstants::HBASE_DIR))
-fs = FileSystem.get(c)
-
-# If hfiles directory does not exist, exit.
-isDirExists(fs, outputdir)
-# Create table dir if it doesn't exist.
-rootdir = FSUtils.getRootDir(c)
-tableDir = Path.new(rootdir, Path.new(Bytes.toString(tableName)))
-fs.mkdirs(tableDir) unless fs.exists(tableDir)
-
-# Start. Per hfile, move it, and insert an entry in catalog table.
-families = fs.listStatus(outputdir, OutputLogFilter.new())
-throw IOError.new("Can do one family only") if families.length > 1
-# Read meta on all files. Put in map keyed by start key.
-map = TreeMap.new(Bytes::ByteArrayComparator.new())
-family = families[0]
-# Make sure this subdir exists under table
-hfiles = fs.listStatus(family.getPath())
-LOG.info("Found " + hfiles.length.to_s + " hfiles");
-count = 0
-for hfile in hfiles
- reader = HFile::Reader.new(fs, hfile.getPath(), nil, false)
- begin
- fileinfo = reader.loadFileInfo()
- firstkey = reader.getFirstKey()
- # First key is row/column/ts. We just want the row part.
- rowlen = Bytes.toShort(firstkey)
- firstkeyrow = firstkey[2, rowlen]
- LOG.info(count.to_s + " read firstkey of " +
- Bytes.toString(firstkeyrow) + " from " + hfile.getPath().toString())
- map.put(firstkeyrow, [hfile, fileinfo])
- count = count + 1
- ensure
- reader.close()
- end
-end
-# Now I have sorted list of fileinfo+paths. Start insert.
-# Get a client on catalog table.
-meta = HTable.new(c, HConstants::META_TABLE_NAME)
-# I can't find out from hfile how its compressed.
-# Using all defaults. Change manually after loading if
-# something else wanted in column or table attributes.
-familyName = family.getPath().getName()
-hcd = HColumnDescriptor.new(familyName)
-htd = HTableDescriptor.new(tableName)
-htd.addFamily(hcd)
-previouslastkey = HConstants::EMPTY_START_ROW
-count = map.size()
-for i in map.descendingKeySet().iterator()
- tuple = map.get(i)
- startkey = i
- count = count - 1
- # If last time through loop, set start row as EMPTY_START_ROW
- startkey = HConstants::EMPTY_START_ROW unless count > 0
- # Next time around, lastkey is this startkey
- hri = HRegionInfo.new(htd, startkey, previouslastkey)
- previouslastkey = startkey
- LOG.info(hri.toString())
- hfile = tuple[0].getPath()
- rdir = Path.new(Path.new(tableDir, hri.getEncodedName().to_s), familyName)
- fs.mkdirs(rdir)
- tgt = Path.new(rdir, hfile.getName())
- fs.rename(hfile, tgt)
- LOG.info("Moved " + hfile.toString() + " to " + tgt.toString())
- p = Put.new(hri.getRegionName())
- p.add(HConstants::CATALOG_FAMILY, HConstants::REGIONINFO_QUALIFIER,
Writables.getBytes(hri))
- meta.put(p)
- LOG.info("Inserted " + hri.toString())
-end
+puts 'DISABLED!!!! Use completebulkload instead. See tail of
http://hbase.apache.org/bulk-loads.html'
Modified:
hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/mapreduce/LoadIncrementalHFiles.java
URL:
http://svn.apache.org/viewvc/hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/mapreduce/LoadIncrementalHFiles.java?rev=1082249&r1=1082248&r2=1082249&view=diff
==============================================================================
---
hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/mapreduce/LoadIncrementalHFiles.java
(original)
+++
hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/mapreduce/LoadIncrementalHFiles.java
Wed Mar 16 18:27:19 2011
@@ -24,6 +24,7 @@ import java.io.IOException;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Map;
+import java.util.ArrayList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -51,6 +52,11 @@ import org.apache.hadoop.hbase.regionser
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import java.util.TreeMap;
+
/**
* Tool to load the output of HFileOutputFormat into an existing table.
@@ -58,14 +64,21 @@ import org.apache.hadoop.util.ToolRunner
*/
public class LoadIncrementalHFiles extends Configured implements Tool {
- static Log LOG = LogFactory.getLog(LoadIncrementalHFiles.class);
+ private static Log LOG = LogFactory.getLog(LoadIncrementalHFiles.class);
+ private static final int TABLE_CREATE_MAX_RETRIES = 20;
+ private static final long TABLE_CREATE_SLEEP = 60000;
+ private HBaseAdmin hbAdmin;
public static String NAME = "completebulkload";
- public LoadIncrementalHFiles(Configuration conf) {
+ public LoadIncrementalHFiles(Configuration conf) throws Exception {
super(conf);
+ this.hbAdmin = new HBaseAdmin(conf);
}
+ /* This constructor does not add HBase configuration.
+ * Explicit addition is necessary. Do we need this constructor?
+ */
public LoadIncrementalHFiles() {
super();
}
@@ -299,6 +312,125 @@ public class LoadIncrementalHFiles exten
return !HFile.isReservedFileInfoKey(key);
}
+ private boolean doesTableExist(String tableName) throws Exception {
+ return hbAdmin.tableExists(tableName);
+ }
+
+ /*
+ * Infers region boundaries for a new table.
+ * Parameter:
+ * bdryMap is a map between keys to an integer belonging to {+1, -1}
+ * If a key is a start key of a file, then it maps to +1
+ * If a key is an end key of a file, then it maps to -1
+ * Algo:
+ * 1) Poll on the keys in order:
+ * a) Keep adding the mapped values to these keys (runningSum)
+ * b) Each time runningSum reaches 0, add the start Key from when the
runningSum had started to a boundary list.
+ * 2) Return the boundary list.
+ */
+ public static byte[][] inferBoundaries(TreeMap<byte[], Integer> bdryMap) {
+ ArrayList<byte[]> keysArray = new ArrayList<byte[]>();
+ int runningValue = 0;
+ byte[] currStartKey = null;
+ boolean firstBoundary = true;
+
+ for (Map.Entry<byte[], Integer> item: bdryMap.entrySet()) {
+ if (runningValue == 0) currStartKey = item.getKey();
+ runningValue += item.getValue();
+ if (runningValue == 0) {
+ if (!firstBoundary) keysArray.add(currStartKey);
+ firstBoundary = false;
+ }
+ }
+
+ return keysArray.toArray(new byte[0][0]);
+ }
+
+ /*
+ * If the table is created for the first time, then "completebulkload" reads
the files twice.
+ * More modifications necessary if we want to avoid doing it.
+ */
+ private void createTable(String tableName, String dirPath) throws Exception {
+ Path hfofDir = new Path(dirPath);
+ FileSystem fs = hfofDir.getFileSystem(getConf());
+
+ if (!fs.exists(hfofDir)) {
+ throw new FileNotFoundException("HFileOutputFormat dir " +
+ hfofDir + " not found");
+ }
+
+ FileStatus[] familyDirStatuses = fs.listStatus(hfofDir);
+ if (familyDirStatuses == null) {
+ throw new FileNotFoundException("No families found in " + hfofDir);
+ }
+
+ HTableDescriptor htd = new HTableDescriptor(tableName);
+ HColumnDescriptor hcd = null;
+
+ // Add column families
+ // Build a set of keys
+ byte[][] keys = null;
+ TreeMap<byte[], Integer> map = new TreeMap<byte[],
Integer>(Bytes.BYTES_COMPARATOR);
+
+ for (FileStatus stat : familyDirStatuses) {
+ if (!stat.isDir()) {
+ LOG.warn("Skipping non-directory " + stat.getPath());
+ continue;
+ }
+ Path familyDir = stat.getPath();
+ // Skip _logs, etc
+ if (familyDir.getName().startsWith("_")) continue;
+ byte[] family = familyDir.getName().getBytes();
+
+ hcd = new HColumnDescriptor(family);
+ htd.addFamily(hcd);
+
+ Path[] hfiles = FileUtil.stat2Paths(fs.listStatus(familyDir));
+ for (Path hfile : hfiles) {
+ if (hfile.getName().startsWith("_")) continue;
+
+ HFile.Reader reader = new HFile.Reader(fs, hfile, null, false, false);
+ final byte[] first, last;
+ try {
+ reader.loadFileInfo();
+ first = reader.getFirstRowKey();
+ last = reader.getLastRowKey();
+
+ LOG.info("Trying to figure out region boundaries hfile=" + hfile +
+ " first=" + Bytes.toStringBinary(first) +
+ " last=" + Bytes.toStringBinary(last));
+
+ // To eventually infer start key-end key boundaries
+ Integer value = map.containsKey(first)?(Integer)map.get(first):0;
+ map.put(first, value+1);
+
+ value = map.containsKey(last)?(Integer)map.get(last):0;
+ map.put(last, value-1);
+ } finally {
+ reader.close();
+ }
+ }
+ }
+
+ keys = LoadIncrementalHFiles.inferBoundaries(map);
+ try {
+ this.hbAdmin.createTableAsync(htd, keys);
+ } catch (java.net.SocketTimeoutException e) {
+ System.err.println("Caught Socket timeout.. Mostly caused by a slow
region assignment by master!");
+ }
+
+ HTable table = new HTable(tableName);
+
+ HConnection conn = table.getConnection();
+ int ctr = 0;
+ while (!conn.isTableAvailable(table.getTableName()) &&
(ctr<TABLE_CREATE_MAX_RETRIES)) {
+ LOG.info("Table " + tableName + "not yet available... Sleeping for 60
more seconds...");
+ /* Every TABLE_CREATE_SLEEP milliseconds, wakes up and checks if the
table is available*/
+ Thread.sleep(TABLE_CREATE_SLEEP);
+ ctr++;
+ }
+ LOG.info("Table "+ tableName +" is finally available!!");
+ }
@Override
public int run(String[] args) throws Exception {
@@ -307,15 +439,21 @@ public class LoadIncrementalHFiles exten
return -1;
}
- Path hfofDir = new Path(args[0]);
- HTable table = new HTable(this.getConf(), args[1]);
+ String dirPath = args[0];
+ String tableName = args[1];
+
+ boolean tableExists = this.doesTableExist(tableName);
+ if (!tableExists) this.createTable(tableName,dirPath);
+
+ Path hfofDir = new Path(dirPath);
+ HTable table = new HTable(tableName);
doBulkLoad(hfofDir, table);
return 0;
}
public static void main(String[] args) throws Exception {
- ToolRunner.run(new LoadIncrementalHFiles(), args);
+ ToolRunner.run(new LoadIncrementalHFiles(HBaseConfiguration.create()),
args);
}
}
Modified:
hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/mapreduce/TestLoadIncrementalHFiles.java
URL:
http://svn.apache.org/viewvc/hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/mapreduce/TestLoadIncrementalHFiles.java?rev=1082249&r1=1082248&r2=1082249&view=diff
==============================================================================
---
hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/mapreduce/TestLoadIncrementalHFiles.java
(original)
+++
hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/mapreduce/TestLoadIncrementalHFiles.java
Wed Mar 16 18:27:19 2011
@@ -22,6 +22,8 @@ package org.apache.hadoop.hbase.mapreduc
import static org.junit.Assert.assertEquals;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.TreeMap;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
@@ -186,4 +188,70 @@ public class TestLoadIncrementalHFiles {
writer.close();
}
}
+
+ private void addStartEndKeysForTest(TreeMap<byte[], Integer> map, byte[]
first, byte[] last) {
+ Integer value = map.containsKey(first)?(Integer)map.get(first):0;
+ map.put(first, value+1);
+
+ value = map.containsKey(last)?(Integer)map.get(last):0;
+ map.put(last, value-1);
+ }
+
+ @Test
+ public void testInferBoundaries() {
+ TreeMap<byte[], Integer> map = new TreeMap<byte[],
Integer>(Bytes.BYTES_COMPARATOR);
+
+ /* Toy example
+ * c---------i o------p s---------t v------x
+ * a------e g-----k m-------------q r----s u----w
+ *
+ * Should be inferred as:
+ * a-----------------k m-------------q r--------------t u---------x
+ *
+ * The output should be (m,r,u)
+ */
+
+ String first;
+ String last;
+
+ first = "a"; last = "e";
+ addStartEndKeysForTest(map, first.getBytes(), last.getBytes());
+
+ first = "r"; last = "s";
+ addStartEndKeysForTest(map, first.getBytes(), last.getBytes());
+
+ first = "o"; last = "p";
+ addStartEndKeysForTest(map, first.getBytes(), last.getBytes());
+
+ first = "g"; last = "k";
+ addStartEndKeysForTest(map, first.getBytes(), last.getBytes());
+
+ first = "v"; last = "x";
+ addStartEndKeysForTest(map, first.getBytes(), last.getBytes());
+
+ first = "c"; last = "i";
+ addStartEndKeysForTest(map, first.getBytes(), last.getBytes());
+
+ first = "m"; last = "q";
+ addStartEndKeysForTest(map, first.getBytes(), last.getBytes());
+
+ first = "s"; last = "t";
+ addStartEndKeysForTest(map, first.getBytes(), last.getBytes());
+
+ first = "u"; last = "w";
+ addStartEndKeysForTest(map, first.getBytes(), last.getBytes());
+
+ byte[][] keysArray = LoadIncrementalHFiles.inferBoundaries(map);
+ byte[][] compare = new byte[3][];
+ compare[0] = "m".getBytes();
+ compare[1] = "r".getBytes();
+ compare[2] = "u".getBytes();
+
+ assertEquals(keysArray.length, 3);
+
+ for (int row = 0; row<keysArray.length; row++){
+ assertArrayEquals(keysArray[row], compare[row]);
+ }
+ }
+
}