diff --git conf/hbase-env.sh conf/hbase-env.sh index da53a27..2874a58 100644 --- conf/hbase-env.sh +++ conf/hbase-env.sh @@ -56,7 +56,7 @@ export HBASE_OPTS="-XX:+UseConcMarkSweepGC" # Uncomment below if you intend to use the EXPERIMENTAL off heap cache. -# export HBASE_OPTS="$HBASE_OPTS -XX:MaxDirectMemorySize=" +export HBASE_OPTS="$HBASE_OPTS -XX:MaxDirectMemorySize=2g" # Set hbase.offheapcache.percentage in hbase-site.xml to a nonzero value. diff --git conf/hbase-site.xml conf/hbase-site.xml index af4c300..d2c4bbb 100644 --- conf/hbase-site.xml +++ conf/hbase-site.xml @@ -22,4 +22,39 @@ */ --> + + hbase.rootdir + hdfs://localhost:9000/hbase-${user.name}/hbase + The directory shared by region servers and into + which HBase persists. The URL should be 'fully-qualified' + to include the filesystem scheme. For example, to specify the + HDFS directory '/hbase' where the HDFS instance's namenode is + running at namenode.example.org on port 9000, set this value to: + hdfs://namenode.example.org:9000/hbase. By default HBase writes + into /tmp. Change this configuration else all data will be lost + on machine restart. + + + + dfs.client.read.shortcircuit + true + + + hbase.hregion.memstore.flush.size + 1342177 + + Memstore will be flushed to disk if size of the memstore + exceeds this number of bytes. Value is checked by a thread that runs + every hbase.server.thread.wakefrequency. + + + + hbase.hregion.max.filesize + 107374182 + + Maximum HStoreFile size. If any one of a column families' HStoreFiles has + grown to exceed this value, the hosting HRegion is split in two. + Default: 10G. + + diff --git src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java index a20c383..3fb6cd8 100644 --- src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java +++ src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java @@ -33,7 +33,6 @@ import java.util.Set; import java.util.TreeMap; import java.util.regex.Matcher; -import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.security.User; @@ -42,8 +41,8 @@ import org.apache.hadoop.io.WritableComparable; /** * HTableDescriptor contains the details about an HBase table such as the descriptors of - * all the column families, is the table a catalog table, -ROOT- or - * .META. , is the table is read only, the maximum size of the memstore, + * all the column families, is the table a catalog table, -ROOT- or + * .META. , is the table is read only, the maximum size of the memstore, * when the region split should occur, coprocessors associated with it etc... */ public class HTableDescriptor implements WritableComparable { @@ -61,7 +60,7 @@ public class HTableDescriptor implements WritableComparable { private String nameAsString = ""; /** - * A map which holds the metadata information of the table. This metadata + * A map which holds the metadata information of the table. This metadata * includes values like IS_ROOT, IS_META, DEFERRED_LOG_FLUSH, SPLIT_POLICY, * MAX_FILE_SIZE, READONLY, MEMSTORE_FLUSHSIZE etc... */ @@ -71,12 +70,12 @@ public class HTableDescriptor implements WritableComparable { private static final String FAMILIES = "FAMILIES"; public static final String SPLIT_POLICY = "SPLIT_POLICY"; - + /** - * INTERNAL Used by HBase Shell interface to access this metadata - * attribute which denotes the maximum size of the store file after which + * INTERNAL Used by HBase Shell interface to access this metadata + * attribute which denotes the maximum size of the store file after which * a region split occurs - * + * * @see #getMaxFileSize() */ public static final String MAX_FILESIZE = "MAX_FILESIZE"; @@ -88,9 +87,9 @@ public class HTableDescriptor implements WritableComparable { new ImmutableBytesWritable(Bytes.toBytes(OWNER)); /** - * INTERNAL Used by rest interface to access this metadata + * INTERNAL Used by rest interface to access this metadata * attribute which denotes if the table is Read Only - * + * * @see #isReadOnly() */ public static final String READONLY = "READONLY"; @@ -98,10 +97,10 @@ public class HTableDescriptor implements WritableComparable { new ImmutableBytesWritable(Bytes.toBytes(READONLY)); /** - * INTERNAL Used by HBase Shell interface to access this metadata - * attribute which represents the maximum size of the memstore after which + * INTERNAL Used by HBase Shell interface to access this metadata + * attribute which represents the maximum size of the memstore after which * its contents are flushed onto the disk - * + * * @see #getMemStoreFlushSize() */ public static final String MEMSTORE_FLUSHSIZE = "MEMSTORE_FLUSHSIZE"; @@ -109,9 +108,9 @@ public class HTableDescriptor implements WritableComparable { new ImmutableBytesWritable(Bytes.toBytes(MEMSTORE_FLUSHSIZE)); /** - * INTERNAL Used by rest interface to access this metadata + * INTERNAL Used by rest interface to access this metadata * attribute which denotes if the table is a -ROOT- region or not - * + * * @see #isRootRegion() */ public static final String IS_ROOT = "IS_ROOT"; @@ -119,10 +118,10 @@ public class HTableDescriptor implements WritableComparable { new ImmutableBytesWritable(Bytes.toBytes(IS_ROOT)); /** - * INTERNAL Used by rest interface to access this metadata + * INTERNAL Used by rest interface to access this metadata * attribute which denotes if it is a catalog table, either * .META. or -ROOT- - * + * * @see #isMetaRegion() */ public static final String IS_META = "IS_META"; @@ -130,7 +129,7 @@ public class HTableDescriptor implements WritableComparable { new ImmutableBytesWritable(Bytes.toBytes(IS_META)); /** - * INTERNAL Used by HBase Shell interface to access this metadata + * INTERNAL Used by HBase Shell interface to access this metadata * attribute which denotes if the deferred log flush option is enabled */ public static final String DEFERRED_LOG_FLUSH = "DEFERRED_LOG_FLUSH"; @@ -149,14 +148,14 @@ public class HTableDescriptor implements WritableComparable { new ImmutableBytesWritable(Bytes.toBytes(Boolean.TRUE.toString())); private static final boolean DEFAULT_DEFERRED_LOG_FLUSH = false; - + /** * Constant that denotes whether the table is READONLY by default and is false */ public static final boolean DEFAULT_READONLY = false; /** - * Constant that denotes the maximum default size of the memstore after which + * Constant that denotes the maximum default size of the memstore after which * the contents are flushed to the store files */ public static final long DEFAULT_MEMSTORE_FLUSH_SIZE = 1024*1024*128L; @@ -260,9 +259,9 @@ public class HTableDescriptor implements WritableComparable { } /* - * Set meta flags on this table. + * Set meta flags on this table. * IS_ROOT_KEY is set if its a -ROOT- table - * IS_META_KEY is set either if its a -ROOT- or a .META. table + * IS_META_KEY is set either if its a -ROOT- or a .META. table * Called by constructors. * @param name */ @@ -274,8 +273,8 @@ public class HTableDescriptor implements WritableComparable { /** * Check if the descriptor represents a -ROOT- region. - * - * @return true if this is a -ROOT- region + * + * @return true if this is a -ROOT- region */ public boolean isRootRegion() { if (this.root == null) { @@ -285,11 +284,11 @@ public class HTableDescriptor implements WritableComparable { } /** - * INTERNAL Used to denote if the current table represents - * -ROOT- region. This is used internally by the - * HTableDescriptor constructors - * - * @param isRoot true if this is the -ROOT- region + * INTERNAL Used to denote if the current table represents + * -ROOT- region. This is used internally by the + * HTableDescriptor constructors + * + * @param isRoot true if this is the -ROOT- region */ protected void setRootRegion(boolean isRoot) { // TODO: Make the value a boolean rather than String of boolean. @@ -298,10 +297,10 @@ public class HTableDescriptor implements WritableComparable { /** * Checks if this table is either -ROOT- or .META. - * region. - * - * @return true if this is either a -ROOT- or .META. - * region + * region. + * + * @return true if this is either a -ROOT- or .META. + * region */ public boolean isMetaRegion() { if (this.meta == null) { @@ -326,31 +325,31 @@ public class HTableDescriptor implements WritableComparable { } /** - * INTERNAL Used to denote if the current table represents - * -ROOT- or .META. region. This is used - * internally by the HTableDescriptor constructors - * - * @param isMeta true if its either -ROOT- or - * .META. region + * INTERNAL Used to denote if the current table represents + * -ROOT- or .META. region. This is used + * internally by the HTableDescriptor constructors + * + * @param isMeta true if its either -ROOT- or + * .META. region */ protected void setMetaRegion(boolean isMeta) { values.put(IS_META_KEY, isMeta? TRUE: FALSE); } - /** - * Checks if the table is a .META. table - * + /** + * Checks if the table is a .META. table + * * @return true if table is .META. region. */ public boolean isMetaTable() { return isMetaRegion() && !isRootRegion(); } - + /** - * Checks of the tableName being passed represents either + * Checks of the tableName being passed represents either * -ROOT- or .META. - * - * @return true if a tablesName is either -ROOT- + * + * @return true if a tablesName is either -ROOT- * or .META. */ public static boolean isMetaTable(final byte [] tableName) { @@ -379,7 +378,7 @@ public class HTableDescriptor implements WritableComparable { "characters': i.e. [a-zA-Z_0-9]: " + Bytes.toString(tableName)); } for (int i = 0; i < tableName.length; i++) { - if (Character.isLetterOrDigit(tableName[i]) || tableName[i] == '_' || + if (Character.isLetterOrDigit(tableName[i]) || tableName[i] == '_' || tableName[i] == '-' || tableName[i] == '.') { continue; } @@ -392,7 +391,7 @@ public class HTableDescriptor implements WritableComparable { /** * Getter for accessing the metadata associated with the key - * + * * @param key The key. * @return The value. * @see #values @@ -410,7 +409,7 @@ public class HTableDescriptor implements WritableComparable { /** * Getter for accessing the metadata associated with the key - * + * * @param key The key. * @return The value. * @see #values @@ -424,7 +423,7 @@ public class HTableDescriptor implements WritableComparable { /** * Getter for fetching an unmodifiable {@link #values} map. - * + * * @return unmodifiable map {@link #values}. * @see #values */ @@ -434,7 +433,7 @@ public class HTableDescriptor implements WritableComparable { /** * Setter for storing metadata as a (key, value) pair in {@link #values} map - * + * * @param key The key. * @param value The value. * @see #values @@ -463,7 +462,7 @@ public class HTableDescriptor implements WritableComparable { /** * Setter for storing metadata as a (key, value) pair in {@link #values} map - * + * * @param key The key. * @param value The value. * @see #values @@ -474,17 +473,17 @@ public class HTableDescriptor implements WritableComparable { /** * Remove metadata represented by the key from the {@link #values} map - * + * * @param key Key whose key and value we're to remove from HTableDescriptor * parameters. */ public void remove(final byte [] key) { values.remove(new ImmutableBytesWritable(key)); } - + /** * Remove metadata represented by the key from the {@link #values} map - * + * * @param key Key whose key and value we're to remove from HTableDescriptor * parameters. */ @@ -493,9 +492,9 @@ public class HTableDescriptor implements WritableComparable { } /** - * Check if the readOnly flag of the table is set. If the readOnly flag is + * Check if the readOnly flag of the table is set. If the readOnly flag is * set then the contents of the table can only be read from but not modified. - * + * * @return true if all columns in the table should be read only */ public boolean isReadOnly() { @@ -504,9 +503,9 @@ public class HTableDescriptor implements WritableComparable { /** * Setting the table as read only sets all the columns in the table as read - * only. By default all tables are modifiable, but if the readOnly flag is + * only. By default all tables are modifiable, but if the readOnly flag is * set to true then the contents of the table can only be read but not modified. - * + * * @param readOnly True if all of the columns in the table should be read * only. */ @@ -515,10 +514,10 @@ public class HTableDescriptor implements WritableComparable { } /** - * Check if deferred log edits are enabled on the table. - * + * Check if deferred log edits are enabled on the table. + * * @return true if that deferred log flush is enabled on the table - * + * * @see #setDeferredLogFlush(boolean) */ public synchronized boolean isDeferredLogFlush() { @@ -530,17 +529,17 @@ public class HTableDescriptor implements WritableComparable { } /** - * This is used to defer the log edits syncing to the file system. Everytime - * an edit is sent to the server it is first sync'd to the file system by the - * log writer. This sync is an expensive operation and thus can be deferred so + * This is used to defer the log edits syncing to the file system. Everytime + * an edit is sent to the server it is first sync'd to the file system by the + * log writer. This sync is an expensive operation and thus can be deferred so * that the edits are kept in memory for a specified period of time as represented * by hbase.regionserver.optionallogflushinterval and not flushed * for every edit. *

* NOTE:- This option might result in data loss if the region server crashes - * before these deferred edits in memory are flushed onto the filesystem. + * before these deferred edits in memory are flushed onto the filesystem. *

- * + * * @param isDeferredLogFlush */ public void setDeferredLogFlush(final boolean isDeferredLogFlush) { @@ -550,8 +549,8 @@ public class HTableDescriptor implements WritableComparable { /** * Get the name of the table as a byte array. - * - * @return name of table + * + * @return name of table */ public byte [] getName() { return name; @@ -559,19 +558,19 @@ public class HTableDescriptor implements WritableComparable { /** * Get the name of the table as a String - * - * @return name of table as a String + * + * @return name of table as a String */ public String getNameAsString() { return this.nameAsString; } - + /** - * This get the class associated with the region split policy which + * This get the class associated with the region split policy which * determines when a region split should occur. The class used by * default is {@link org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy} * which split the region base on a constant {@link #getMaxFileSize()} - * + * * @return the class name of the region split policy for this table. * If this returns null, the default constant size based split policy * is used. @@ -581,9 +580,9 @@ public class HTableDescriptor implements WritableComparable { } /** - * Set the name of the table. - * - * @param name name of table + * Set the name of the table. + * + * @param name name of table */ public void setName(byte[] name) { this.name = name; @@ -591,7 +590,7 @@ public class HTableDescriptor implements WritableComparable { setMetaFlags(this.name); } - /** + /** * Returns the maximum size upto which a region can grow to after which a region * split is triggered. The region size is represented by the size of the biggest * store file in that region. @@ -610,16 +609,16 @@ public class HTableDescriptor implements WritableComparable { /** * Sets the maximum size upto which a region can grow to after which a region - * split is triggered. The region size is represented by the size of the biggest - * store file in that region, i.e. If the biggest store file grows beyond the - * maxFileSize, then the region split is triggered. This defaults to a value of + * split is triggered. The region size is represented by the size of the biggest + * store file in that region, i.e. If the biggest store file grows beyond the + * maxFileSize, then the region split is triggered. This defaults to a value of * 256 MB. *

- * This is not an absolute value and might vary. Assume that a single row exceeds + * This is not an absolute value and might vary. Assume that a single row exceeds * the maxFileSize then the storeFileSize will be greater than maxFileSize since - * a single row cannot be split across multiple regions + * a single row cannot be split across multiple regions *

- * + * * @param maxFileSize The maximum file size that a store file can grow to * before a split is triggered. */ @@ -643,9 +642,9 @@ public class HTableDescriptor implements WritableComparable { } /** - * Represents the maximum size of the memstore after which the contents of the + * Represents the maximum size of the memstore after which the contents of the * memstore are flushed to the filesystem. This defaults to a size of 64 MB. - * + * * @param memstoreFlushSize memory cache flush size for each hregion */ public void setMemStoreFlushSize(long memstoreFlushSize) { @@ -761,12 +760,12 @@ public class HTableDescriptor implements WritableComparable { } /** - * Compare the contents of the descriptor with another one passed as a parameter. + * Compare the contents of the descriptor with another one passed as a parameter. * Checks if the obj passed is an instance of HTableDescriptor, if yes then the * contents of the descriptors are compared. - * + * * @return true if the contents of the the two descriptors exactly match - * + * * @see java.lang.Object#equals(java.lang.Object) */ @Override @@ -801,7 +800,7 @@ public class HTableDescriptor implements WritableComparable { // Writable /** - * INTERNAL This method is a part of {@link WritableComparable} interface + * INTERNAL This method is a part of {@link WritableComparable} interface * and is used for de-serialization of the HTableDescriptor over RPC */ @Override @@ -836,7 +835,7 @@ public class HTableDescriptor implements WritableComparable { } /** - * INTERNAL This method is a part of {@link WritableComparable} interface + * INTERNAL This method is a part of {@link WritableComparable} interface * and is used for serialization of the HTableDescriptor over RPC */ @Override @@ -864,9 +863,9 @@ public class HTableDescriptor implements WritableComparable { /** * Compares the descriptor with another descriptor which is passed as a parameter. * This compares the content of the two descriptors and not the reference. - * - * @return 0 if the contents of the descriptors are exactly matching, - * 1 if there is a mismatch in the contents + * + * @return 0 if the contents of the descriptors are exactly matching, + * 1 if there is a mismatch in the contents */ @Override public int compareTo(final HTableDescriptor other) { @@ -899,58 +898,58 @@ public class HTableDescriptor implements WritableComparable { } /** - * Returns an unmodifiable collection of all the {@link HColumnDescriptor} + * Returns an unmodifiable collection of all the {@link HColumnDescriptor} * of all the column families of the table. - * + * * @return Immutable collection of {@link HColumnDescriptor} of all the - * column families. + * column families. */ public Collection getFamilies() { return Collections.unmodifiableCollection(this.families.values()); } /** - * Returns all the column family names of the current table. The map of - * HTableDescriptor contains mapping of family name to HColumnDescriptors. - * This returns all the keys of the family map which represents the column - * family names of the table. - * + * Returns all the column family names of the current table. The map of + * HTableDescriptor contains mapping of family name to HColumnDescriptors. + * This returns all the keys of the family map which represents the column + * family names of the table. + * * @return Immutable sorted set of the keys of the families. */ public Set getFamiliesKeys() { return Collections.unmodifiableSet(this.families.keySet()); } - /** - * Returns an array all the {@link HColumnDescriptor} of the column families + /** + * Returns an array all the {@link HColumnDescriptor} of the column families * of the table. - * - * @return Array of all the HColumnDescriptors of the current table - * + * + * @return Array of all the HColumnDescriptors of the current table + * * @see #getFamilies() */ public HColumnDescriptor[] getColumnFamilies() { return getFamilies().toArray(new HColumnDescriptor[0]); } - + /** - * Returns the HColumnDescriptor for a specific column family with name as + * Returns the HColumnDescriptor for a specific column family with name as * specified by the parameter column. - * - * @param column Column family name + * + * @param column Column family name * @return Column descriptor for the passed family name or the family on * passed in column. */ public HColumnDescriptor getFamily(final byte [] column) { return this.families.get(column); } - + /** - * Removes the HColumnDescriptor with name specified by the parameter column + * Removes the HColumnDescriptor with name specified by the parameter column * from the table descriptor - * + * * @param column Name of the column family to be removed. * @return Column descriptor for the passed family name or the family on * passed in column. @@ -958,7 +957,7 @@ public class HTableDescriptor implements WritableComparable { public HColumnDescriptor removeFamily(final byte [] column) { return this.families.remove(column); } - + /** * Add a table coprocessor to this table. The coprocessor @@ -974,7 +973,7 @@ public class HTableDescriptor implements WritableComparable { addCoprocessor(className, null, Coprocessor.PRIORITY_USER, null); } - + /** * Add a table coprocessor to this table. The coprocessor * type must be {@link org.apache.hadoop.hbase.coprocessor.RegionObserver} @@ -1127,11 +1126,11 @@ public class HTableDescriptor implements WritableComparable { if (match != null) this.values.remove(match); } - + /** - * Returns the {@link Path} object representing the table directory under - * path rootdir - * + * Returns the {@link Path} object representing the table directory under + * path rootdir + * * @param rootdir qualified path of HBase root directory * @param tableName name of table * @return {@link Path} for table @@ -1164,6 +1163,17 @@ public class HTableDescriptor implements WritableComparable { .setScope(HConstants.REPLICATION_SCOPE_LOCAL) }); + static { + try { + META_TABLEDESC.addCoprocessor( + "org.apache.hadoop.hbase.coprocessor.MultiRowMutationEndpoint", + null, Coprocessor.PRIORITY_SYSTEM, null); + } catch (IOException ex) { + //LOG.warn("exception in loading coprocessor for the META table"); + throw new RuntimeException(ex); + } + } + @Deprecated public void setOwner(User owner) { setOwnerString(owner != null ? owner.getShortName() : null); diff --git src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java index c7ce684..cca2353 100644 --- src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java +++ src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.io.InterruptedIOException; import java.net.ConnectException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.apache.commons.logging.Log; @@ -35,6 +34,7 @@ import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Mutation; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.coprocessor.MultiRowMutationProtocol; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.PairOfSameType; import org.apache.hadoop.hbase.util.Writables; @@ -199,33 +199,6 @@ public class MetaEditor { LOG.info("Added " + puts.size() + " regions in META"); } - /** - * Offline parent in meta. - * Used when splitting. - * @param catalogTracker - * @param parent - * @param a Split daughter region A - * @param b Split daughter region B - * @throws NotAllMetaRegionsOnlineException - * @throws IOException - */ - public static void offlineParentInMeta(CatalogTracker catalogTracker, - HRegionInfo parent, final HRegionInfo a, final HRegionInfo b) - throws NotAllMetaRegionsOnlineException, IOException { - HRegionInfo copyOfParent = new HRegionInfo(parent); - copyOfParent.setOffline(true); - copyOfParent.setSplit(true); - Put put = new Put(copyOfParent.getRegionName()); - addRegionInfo(put, copyOfParent); - put.add(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER, - Writables.getBytes(a)); - put.add(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER, - Writables.getBytes(b)); - putToMetaTable(catalogTracker, put); - LOG.info("Offlined parent region " + parent.getRegionNameAsString() + - " in META"); - } - public static void addDaughter(final CatalogTracker catalogTracker, final HRegionInfo regionInfo, final ServerName sn) throws NotAllMetaRegionsOnlineException, IOException { @@ -238,6 +211,68 @@ public class MetaEditor { } /** + * Adds split daughters to the Put + */ + public static Put addDaughtersToPut(Put put, HRegionInfo splitA, HRegionInfo splitB) + throws IOException { + if (splitA != null) { + put.add(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER, Writables.getBytes(splitA)); + } + if (splitB != null) { + put.add(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER, Writables.getBytes(splitB)); + } + return put; + } + + /** + * Splits the region into two in an atomic operation. Offlines the parent + * region with the information that it is split into two, and also adds + * the daughter regions. Does not add the location information to the daughter + * regions since they are not open yet. + * @param catalogTracker the catalog tracker + * @param parent the parent region which is split + * @param splitA Split daughter region A + * @param splitB Split daughter region A + * @param sn the location of the region + */ + public static void splitRegion(final CatalogTracker catalogTracker, + HRegionInfo parent, HRegionInfo splitA, HRegionInfo splitB, + ServerName sn) throws IOException { + HTable meta = MetaReader.getMetaHTable(catalogTracker); + HRegionInfo copyOfParent = new HRegionInfo(parent); + copyOfParent.setOffline(true); + copyOfParent.setSplit(true); + + //Put for parent + Put putParent = makePutFromRegionInfo(copyOfParent); + addDaughtersToPut(putParent, splitA, splitB); + + //Puts for daughters + Put putA = makePutFromRegionInfo(splitA); + Put putB = makePutFromRegionInfo(splitB); + + addLocation(putA, sn); + addLocation(putB, sn); + + byte[] tableRow = parent.getRegionName(); + multiPut(meta, tableRow, putParent, putA, putB); + } + + /** + * Performs an atomic multi-Put operation against the given table. + */ + private static void multiPut(HTable table, byte[] row, Put... puts) throws IOException { + MultiRowMutationProtocol endpoint = table.coprocessorProxy(MultiRowMutationProtocol.class, row); + + List mutations = new ArrayList(puts.length); + for (Put put : puts) { + mutations.add(put); + } + endpoint.mutateRows(mutations); + } + + + /** * Updates the location of the specified META region in ROOT to be the * specified server hostname and startcode. *

diff --git src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java index 1f1bc2b..23d7077 100644 --- src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java +++ src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java @@ -26,7 +26,6 @@ import java.util.ArrayList; import java.util.List; import java.util.NavigableMap; import java.util.TreeMap; -import java.util.TreeSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -49,7 +48,7 @@ import org.apache.hadoop.hbase.util.Writables; * *

Note that during concurrent region splits, the scanner might not see * META changes across rows (for parent and daughter entries) consistently. - * see HBASE-5986, and {@link BlockingMetaScannerVisitor} for details.

+ * see HBASE-5986, and {@link DefaultMetaScannerVisitor} for details.

*/ public class MetaScanner { private static final Log LOG = LogFactory.getLog(MetaScanner.class); @@ -259,7 +258,7 @@ public class MetaScanner { public static List listAllRegions(Configuration conf, final boolean offlined) throws IOException { final List regions = new ArrayList(); - MetaScannerVisitor visitor = new BlockingMetaScannerVisitor(conf) { + MetaScannerVisitor visitor = new DefaultMetaScannerVisitor(conf) { @Override public boolean processRowInternal(Result result) throws IOException { if (result == null || result.isEmpty()) { @@ -297,9 +296,7 @@ public class MetaScanner { MetaScannerVisitor visitor = new TableMetaScannerVisitor(conf, tablename) { @Override public boolean processRowInternal(Result rowResult) throws IOException { - HRegionInfo info = Writables.getHRegionInfo( - rowResult.getValue(HConstants.CATALOG_FAMILY, - HConstants.REGIONINFO_QUALIFIER)); + HRegionInfo info = getHRegionInfo(rowResult); byte [] value = rowResult.getValue(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER); String hostAndPort = null; @@ -310,13 +307,12 @@ public class MetaScanner { HConstants.STARTCODE_QUALIFIER); long startcode = -1L; if (value != null && value.length > 0) startcode = Bytes.toLong(value); - if (!(info.isOffline() || info.isSplit())) { - ServerName sn = null; - if (hostAndPort != null && hostAndPort.length() > 0) { - sn = new ServerName(hostAndPort, startcode); - } - regions.put(new UnmodifyableHRegionInfo(info), sn); + + ServerName sn = null; + if (hostAndPort != null && hostAndPort.length() > 0) { + sn = new ServerName(hostAndPort, startcode); } + regions.put(new UnmodifyableHRegionInfo(info), sn); return true; } }; @@ -352,112 +348,46 @@ public class MetaScanner { * does not guarantee ordered traversal of meta entries, and can block until the * META entries for daughters are available during splits. */ - public static abstract class BlockingMetaScannerVisitor + public static abstract class DefaultMetaScannerVisitor extends MetaScannerVisitorBase { - private static final int DEFAULT_BLOCKING_TIMEOUT = 10000; - private Configuration conf; - private TreeSet daughterRegions = new TreeSet(Bytes.BYTES_COMPARATOR); - private int blockingTimeout; - private HTable metaTable; + protected Configuration conf; - public BlockingMetaScannerVisitor(Configuration conf) { + public DefaultMetaScannerVisitor(Configuration conf) { this.conf = conf; - this.blockingTimeout = conf.getInt(HConstants.HBASE_CLIENT_OPERATION_TIMEOUT, - DEFAULT_BLOCKING_TIMEOUT); } public abstract boolean processRowInternal(Result rowResult) throws IOException; @Override - public void close() throws IOException { - super.close(); - if (metaTable != null) { - metaTable.close(); - metaTable = null; - } - } - - public HTable getMetaTable() throws IOException { - if (metaTable == null) { - metaTable = new HTable(conf, HConstants.META_TABLE_NAME); - } - return metaTable; - } - - @Override public boolean processRow(Result rowResult) throws IOException { - HRegionInfo info = Writables.getHRegionInfoOrNull( - rowResult.getValue(HConstants.CATALOG_FAMILY, - HConstants.REGIONINFO_QUALIFIER)); + HRegionInfo info = getHRegionInfo(rowResult); if (info == null) { return true; } - if (daughterRegions.remove(info.getRegionName())) { - return true; //we have already processed this row - } - - if (info.isSplitParent()) { - /* we have found a parent region which was split. We have to ensure that it's daughters are - * seen by this scanner as well, so we block until they are added to the META table. Even - * though we are waiting for META entries, ACID semantics in HBase indicates that this - * scanner might not see the new rows. So we manually query the daughter rows */ - HRegionInfo splitA = Writables.getHRegionInfo(rowResult.getValue(HConstants.CATALOG_FAMILY, - HConstants.SPLITA_QUALIFIER)); - HRegionInfo splitB = Writables.getHRegionInfo(rowResult.getValue(HConstants.CATALOG_FAMILY, - HConstants.SPLITB_QUALIFIER)); - - HTable metaTable = getMetaTable(); - long start = System.currentTimeMillis(); - Result resultA = getRegionResultBlocking(metaTable, blockingTimeout, - splitA.getRegionName()); - if (resultA != null) { - processRow(resultA); - daughterRegions.add(splitA.getRegionName()); - } else { - throw new RegionOfflineException("Split daughter region " + - splitA.getRegionNameAsString() + " cannot be found in META."); - } - long rem = blockingTimeout - (System.currentTimeMillis() - start); - - Result resultB = getRegionResultBlocking(metaTable, rem, - splitB.getRegionName()); - if (resultB != null) { - processRow(resultB); - daughterRegions.add(splitB.getRegionName()); - } else { - throw new RegionOfflineException("Split daughter region " + - splitB.getRegionNameAsString() + " cannot be found in META."); - } + //skip over offline and split regions + if (!(info.isOffline() || info.isSplit())) { + return processRowInternal(rowResult); } - return processRowInternal(rowResult); + return true; } + } - private Result getRegionResultBlocking(HTable metaTable, long timeout, byte[] regionName) - throws IOException { - if (LOG.isDebugEnabled()) { - LOG.debug("blocking until region is in META: " + Bytes.toStringBinary(regionName)); - } - long start = System.currentTimeMillis(); - while (System.currentTimeMillis() - start < timeout) { - Get get = new Get(regionName); - Result result = metaTable.get(get); - HRegionInfo info = Writables.getHRegionInfoOrNull( - result.getValue(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER)); - if (info != null) { - return result; - } - try { - Thread.sleep(10); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - break; - } - } - return null; - } + /** + * Returns HRegionInfo object from the column + * HConstants.CATALOG_FAMILY:HConstants.REGIONINFO_QUALIFIER of the catalog + * table Result. + * @param data a Result object from the catalog table scan + * @return HRegionInfo or null + */ + public static HRegionInfo getHRegionInfo(Result data) throws IOException { + byte [] bytes = + data.getValue(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER); + if (bytes == null) return null; + HRegionInfo info = Writables.getHRegionInfo(bytes); + return info; } /** @@ -466,7 +396,7 @@ public class MetaScanner { * does not guarantee ordered traversal of meta entries, and can block until the * META entries for daughters are available during splits. */ - public static abstract class TableMetaScannerVisitor extends BlockingMetaScannerVisitor { + public static abstract class TableMetaScannerVisitor extends DefaultMetaScannerVisitor { private byte[] tableName; public TableMetaScannerVisitor(Configuration conf, byte[] tableName) { diff --git src/main/java/org/apache/hadoop/hbase/master/HMaster.java src/main/java/org/apache/hadoop/hbase/master/HMaster.java index bb19dc6..0bbc5e5 100644 --- src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -41,9 +41,6 @@ import java.util.concurrent.atomic.AtomicReference; import javax.management.ObjectName; -import com.google.common.collect.ClassToInstanceMap; -import com.google.common.collect.Maps; -import com.google.common.collect.MutableClassToInstanceMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; @@ -102,9 +99,9 @@ import org.apache.hadoop.hbase.monitoring.MonitoredTask; import org.apache.hadoop.hbase.monitoring.TaskMonitor; import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription; import org.apache.hadoop.hbase.replication.regionserver.Replication; +import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.snapshot.HSnapshotDescription; import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils; -import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.FSTableDescriptors; import org.apache.hadoop.hbase.util.HFileArchiveUtil; @@ -128,6 +125,11 @@ import org.apache.hadoop.net.DNS; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; +import com.google.common.collect.ClassToInstanceMap; +import com.google.common.collect.Maps; +import com.google.common.collect.MutableClassToInstanceMap; +import com.google.protobuf.ServiceException; + /** * HMaster is the "master server" for HBase. An HBase cluster has one active * master. If many masters are started, all compete. Whichever wins goes on to @@ -190,7 +192,7 @@ Server { private CatalogTracker catalogTracker; // Cluster status zk tracker and local setter private ClusterStatusTracker clusterStatusTracker; - + // buffer for "fatal error" notices from region servers // in the cluster. This is only used for assisting // operations/debugging. @@ -359,7 +361,7 @@ Server { "(Also watching cluster state node)"); Thread.sleep(c.getInt("zookeeper.session.timeout", 180 * 1000)); } - + } /** @@ -397,7 +399,7 @@ Server { } } catch (Throwable t) { // HBASE-5680: Likely hadoop23 vs hadoop 20.x/1.x incompatibility - if (t instanceof NoClassDefFoundError && + if (t instanceof NoClassDefFoundError && t.getMessage().contains("org/apache/hadoop/hdfs/protocol/FSConstants$SafeModeAction")) { // improved error message for this special case abort("HBase is having a problem with its Hadoop jars. You may need to " @@ -409,7 +411,7 @@ Server { } } finally { startupStatus.cleanup(); - + stopChores(); // Wait for all the remaining region servers to report in IFF we were // running a cluster shutdown AND we were NOT aborting. @@ -432,7 +434,7 @@ Server { /** * Try becoming active master. - * @param startupStatus + * @param startupStatus * @return True if we could successfully become the active master. * @throws InterruptedException */ @@ -513,7 +515,7 @@ Server { *
  • Ensure assignment of root and meta regions
  • *
  • Handle either fresh cluster start or master failover
  • * - * @param masterRecovery + * @param masterRecovery * * @throws IOException * @throws InterruptedException @@ -550,7 +552,7 @@ Server { status.setStatus("Initializing ZK system trackers"); initializeZKBasedSystemTrackers(); - + if (!masterRecovery) { // initialize master side coprocessors before we start handling requests status.setStatus("Initializing master coprocessors"); @@ -597,7 +599,9 @@ Server { this.balancer.setClusterStatus(getClusterStatus()); this.balancer.setMasterServices(this); - // Fixing up missing daughters if any + // Fixing up missing daughters if any. + //After HBASE-7721, we do not need this, but we keep it since in 0.94 during + //rolling restart this might be needed status.setStatus("Fixing up missing daughters"); fixupDaughters(status); @@ -619,7 +623,7 @@ Server { // removing dead server with same hostname and port of rs which is trying to check in before // master initialization. See HBASE-5916. this.serverManager.clearDeadServersWithSameHostNameAndPortOfOnlineServer(); - + if (!masterRecovery) { if (this.cpHost != null) { // don't let cp initialization errors kill the master @@ -631,11 +635,11 @@ Server { } } } - + /** * If ServerShutdownHandler is disabled, we enable it and expire those dead * but not expired servers. - * + * * @throws IOException */ private void enableServerShutdownHandler() throws IOException { @@ -644,7 +648,7 @@ Server { this.serverManager.expireDeadNotExpiredServers(); } } - + /** * Useful for testing purpose also where we have * master restart scenarios. @@ -870,7 +874,7 @@ Server { * need to install an unexpected exception handler. */ private void startServiceThreads() throws IOException{ - + // Start the executor service pools this.executorService.startExecutorService(ExecutorType.MASTER_OPEN_REGION, conf.getInt("hbase.master.executor.openregion.threads", 5)); @@ -880,7 +884,7 @@ Server { conf.getInt("hbase.master.executor.serverops.threads", 3)); this.executorService.startExecutorService(ExecutorType.MASTER_META_SERVER_OPERATIONS, conf.getInt("hbase.master.executor.serverops.threads", 5)); - + // We depend on there being only one instance of this executor running // at a time. To do concurrency, would need fencing of enable/disable of // tables. @@ -1137,11 +1141,11 @@ Server { newValue = this.cpHost.preBalanceSwitch(newValue); } if (mode == BalanceSwitchMode.SYNC) { - synchronized (this.balancer) { + synchronized (this.balancer) { this.balanceSwitch = newValue; } } else { - this.balanceSwitch = newValue; + this.balanceSwitch = newValue; } LOG.info("BalanceSwitch=" + newValue); if (this.cpHost != null) { @@ -1150,14 +1154,14 @@ Server { } catch (IOException ioe) { LOG.warn("Error flipping balance switch", ioe); } - return oldValue; + return oldValue; } - + @Override public boolean synchronousBalanceSwitch(final boolean b) { return switchBalancer(b, BalanceSwitchMode.SYNC); } - + @Override public boolean balanceSwitch(final boolean b) { return switchBalancer(b, BalanceSwitchMode.ASYNC); @@ -1190,10 +1194,10 @@ Server { } else { dest = new ServerName(Bytes.toString(destServerName)); } - + // Now we can do the move RegionPlan rp = new RegionPlan(p.getFirst(), p.getSecond(), dest); - + try { checkInitialized(); if (this.cpHost != null) { @@ -1280,7 +1284,7 @@ Server { * @return Pair indicating the number of regions updated Pair.getFirst is the * regions that are yet to be updated Pair.getSecond is the total number * of regions of the table - * @throws IOException + * @throws IOException */ public Pair getAlterStatus(byte[] tableName) throws IOException { @@ -1631,7 +1635,7 @@ Server { public AssignmentManager getAssignmentManager() { return this.assignmentManager; } - + public MemoryBoundedLogMessageBuffer getRegionServerFatalLogBuffer() { return rsFatals; } @@ -1695,13 +1699,13 @@ Server { public boolean isAborted() { return this.abort; } - + void checkInitialized() throws PleaseHoldException { if (!this.initialized) { throw new PleaseHoldException("Master is initializing"); } } - + /** * Report whether this master is currently the active master or not. * If not active master, we are parked on ZK waiting to become active. @@ -1759,8 +1763,8 @@ Server { cpHost.postAssign(pair.getFirst()); } } - - + + public void assignRegion(HRegionInfo hri) { assignmentManager.assign(hri, true); @@ -1791,7 +1795,7 @@ Server { } /** - * Get HTD array for given tables + * Get HTD array for given tables * @param tableNames * @return HTableDescriptor[] */ diff --git src/main/java/org/apache/hadoop/hbase/master/MasterFileSystem.java src/main/java/org/apache/hadoop/hbase/master/MasterFileSystem.java index 54ce8c5..53b3924 100644 --- src/main/java/org/apache/hadoop/hbase/master/MasterFileSystem.java +++ src/main/java/org/apache/hadoop/hbase/master/MasterFileSystem.java @@ -385,16 +385,18 @@ public class MasterFileSystem { if (!FSUtils.rootRegionExists(fs, rd)) { bootstrap(rd, c); } - createRootTableInfo(rd); + createRootAndMetaTableInfo(rd); return rd; } - private void createRootTableInfo(Path rd) throws IOException { + private void createRootAndMetaTableInfo(Path rd) throws IOException { // Create ROOT tableInfo if required. if (!FSTableDescriptors.isTableInfoExists(fs, rd, Bytes.toString(HRegionInfo.ROOT_REGIONINFO.getTableName()))) { FSTableDescriptors.createTableDescriptor(HTableDescriptor.ROOT_TABLEDESC, this.conf); } + //create or update the META table descriptor + FSTableDescriptors.createTableDescriptor(HTableDescriptor.META_TABLEDESC, this.conf); } /** diff --git src/main/java/org/apache/hadoop/hbase/master/handler/ServerShutdownHandler.java src/main/java/org/apache/hadoop/hbase/master/handler/ServerShutdownHandler.java index 8b889f7..728c661 100644 --- src/main/java/org/apache/hadoop/hbase/master/handler/ServerShutdownHandler.java +++ src/main/java/org/apache/hadoop/hbase/master/handler/ServerShutdownHandler.java @@ -90,12 +90,12 @@ public class ServerShutdownHandler extends EventHandler { } /** - * Before assign the ROOT region, ensure it haven't + * Before assign the ROOT region, ensure it haven't * been assigned by other place *

    * Under some scenarios, the ROOT region can be opened twice, so it seemed online * in two regionserver at the same time. - * If the ROOT region has been assigned, so the operation can be canceled. + * If the ROOT region has been assigned, so the operation can be canceled. * @throws InterruptedException * @throws IOException * @throws KeeperException @@ -151,7 +151,7 @@ public class ServerShutdownHandler extends EventHandler { } } } - + /** * @return True if the server we are processing was carrying -ROOT- */ @@ -441,6 +441,8 @@ public class ServerShutdownHandler extends EventHandler { if (MetaReader.getRegion(catalogTracker, hri.getRegionName()) == null) { return false; } + //After HBASE-7721, we do not need this, but we keep it since in 0.94 during + //rolling restart this might be needed fixupDaughters(result, assignmentManager, catalogTracker); return false; } @@ -508,7 +510,7 @@ public class ServerShutdownHandler extends EventHandler { * Daughter could have been split over on regionserver before a run of the * catalogJanitor had chance to clear reference from parent. * @param daughter Daughter region to search for. - * @throws IOException + * @throws IOException */ private static boolean isDaughterMissing(final CatalogTracker catalogTracker, final HRegionInfo daughter) throws IOException { diff --git src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java index 1d42fea..7f44125 100644 --- src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java +++ src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java @@ -66,12 +66,12 @@ import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HConstants.OperationStatusCode; import org.apache.hadoop.hbase.HDFSBlocksDistribution; -import org.apache.hadoop.hbase.HealthCheckChore; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HServerAddress; import org.apache.hadoop.hbase.HServerInfo; import org.apache.hadoop.hbase.HServerLoad; import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.HealthCheckChore; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.MasterAddressTracker; import org.apache.hadoop.hbase.NotServingRegionException; @@ -172,7 +172,6 @@ import org.apache.hadoop.util.ReflectionUtils; import org.apache.hadoop.util.StringUtils; import org.apache.zookeeper.KeeperException; import org.codehaus.jackson.map.ObjectMapper; -import org.joda.time.field.MillisDurationField; import com.google.common.base.Function; import com.google.common.collect.Lists; @@ -246,7 +245,7 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, RpcServer rpcServer; // Server to handle client requests. - private HBaseServer server; + private HBaseServer server; private final InetSocketAddress isa; @@ -264,7 +263,7 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, /** region server process name */ public static final String REGIONSERVER = "regionserver"; - + /** region server configuration name */ public static final String REGIONSERVER_CONF = "regionserver_conf"; @@ -1515,10 +1514,10 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, .getCompactionQueueSize()); this.metrics.flushQueueSize.set(cacheFlusher .getFlushQueueSize()); - this.metrics.updatesBlockedSeconds.update(updatesBlockedMs > 0 ? + this.metrics.updatesBlockedSeconds.update(updatesBlockedMs > 0 ? updatesBlockedMs/1000: 0); final long updatesBlockedMsHigherWater = cacheFlusher.getUpdatesBlockedMsHighWater().get(); - this.metrics.updatesBlockedSecondsHighWater.update(updatesBlockedMsHigherWater > 0 ? + this.metrics.updatesBlockedSecondsHighWater.update(updatesBlockedMsHigherWater > 0 ? updatesBlockedMsHigherWater/1000: 0); BlockCache blockCache = cacheConfig.getBlockCache(); @@ -1637,7 +1636,7 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, this.splitLogWorker = new SplitLogWorker(this.zooKeeper, this.getConfiguration(), this.getServerName().toString()); splitLogWorker.start(); - + } /** @@ -1723,12 +1722,10 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, } @Override - public void postOpenDeployTasks(final HRegion r, final CatalogTracker ct, - final boolean daughter) + public void postOpenDeployTasks(final HRegion r, final CatalogTracker ct) throws KeeperException, IOException { checkOpen(); - LOG.info("Post open deploy tasks for region=" + r.getRegionNameAsString() + - ", daughter=" + daughter); + LOG.info("Post open deploy tasks for region=" + r.getRegionNameAsString()); // Do checks to see if we need to compact (references or too many files) for (Store s : r.getStores().values()) { if (s.hasReferences() || s.needsCompaction()) { @@ -1743,17 +1740,11 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, MetaEditor.updateMetaLocation(ct, r.getRegionInfo(), this.serverNameFromMasterPOV); } else { - if (daughter) { - // If daughter of a split, update whole row, not just location. - MetaEditor.addDaughter(ct, r.getRegionInfo(), - this.serverNameFromMasterPOV); - } else { - MetaEditor.updateRegionLocation(ct, r.getRegionInfo(), - this.serverNameFromMasterPOV); - } + MetaEditor.updateRegionLocation(ct, r.getRegionInfo(), + this.serverNameFromMasterPOV); } LOG.info("Done with post open deploy task for region=" + - r.getRegionNameAsString() + ", daughter=" + daughter); + r.getRegionNameAsString()); } @@ -2316,7 +2307,7 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, int nCF = columnFamilies.size(); return region.getStoreFileList(columnFamilies.toArray(new byte[nCF][])); } - + /** * Flushes the given region */ @@ -2355,7 +2346,7 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, } return region.getLastFlushTime(); } - + /** * * @param regionName @@ -3043,7 +3034,7 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, */ protected boolean closeRegion(HRegionInfo region, final boolean abort, final boolean zk, final int versionOfClosingNode) { - + HRegion actualRegion = this.getFromOnlineRegions(region.getEncodedName()); if (actualRegion != null && actualRegion.getCoprocessorHost() != null) { try { @@ -3282,7 +3273,7 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, public boolean removeFromOnlineRegions(final String encodedName) { HRegion toReturn = null; toReturn = this.onlineRegions.remove(encodedName); - + //Clear all of the dynamic metrics as they are now probably useless. //This is a clear because dynamic metrics could include metrics per cf and //per hfile. Figuring out which cfs, hfiles, and regions are still relevant to @@ -3601,7 +3592,7 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, try { if (action instanceof Delete || action instanceof Put) { - mutations.add(a); + mutations.add(a); } else if (action instanceof Get) { response.add(regionName, originalIndex, get(regionName, (Get)action)); diff --git src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java index 1c66704..86774cd 100644 --- src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java +++ src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java @@ -59,15 +59,13 @@ public interface RegionServerServices extends OnlineRegions { /** * Tasks to perform after region open to complete deploy of region on * regionserver - * + * * @param r Region to open. * @param ct Instance of {@link CatalogTracker} - * @param daughter True if this is daughter of a split * @throws KeeperException * @throws IOException */ - public void postOpenDeployTasks(final HRegion r, final CatalogTracker ct, - final boolean daughter) + public void postOpenDeployTasks(final HRegion r, final CatalogTracker ct) throws KeeperException, IOException; /** diff --git src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransaction.java src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransaction.java index 058393d..51fd7c8 100644 --- src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransaction.java +++ src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransaction.java @@ -202,7 +202,7 @@ public class SplitTransaction { private static IOException closedByOtherException = new IOException( "Failed to close region: already closed by another thread"); - + /** * Prepare the regions and region files. * @param server Hosting server instance. Can be null when testing (won't try @@ -259,7 +259,7 @@ public class SplitTransaction { } createSplitDir(this.parent.getFilesystem(), this.splitdir); this.journal.add(JournalEntry.CREATE_SPLIT_DIR); - + List hstoreFilesToSplit = null; Exception exceptionToThrow = null; try{ @@ -320,14 +320,18 @@ public class SplitTransaction { // regions. // We should add PONR JournalEntry before offlineParentInMeta,so even if // OfflineParentInMeta timeout,this will cause regionserver exit,and then - // master ServerShutdownHandler will fix daughter & avoid data loss. (See + // master ServerShutdownHandler will fix daughter & avoid data loss. (See // HBase-4562). this.journal.add(JournalEntry.PONR); - // Edit parent in meta. Offlines parent region and adds splita and splitb. + // Edit parent in meta. Offlines parent region and adds splita and splitb + // as an atomic update. See HBASE-7721. This update to META makes the region + // will determine whether the region is split or not in case of failures. + // If it is successful, master will roll-forward, if not, master will rollback + // and assign the parent region. if (!testing) { - MetaEditor.offlineParentInMeta(server.getCatalogTracker(), - this.parent.getRegionInfo(), a.getRegionInfo(), b.getRegionInfo()); + MetaEditor.splitRegion(server.getCatalogTracker(), parent.getRegionInfo(), + a.getRegionInfo(), b.getRegionInfo(), server.getServerName()); } return new PairOfSameType(a, b); } @@ -377,10 +381,10 @@ public class SplitTransaction { if (services != null) { try { // add 2nd daughter first (see HBASE-4335) - services.postOpenDeployTasks(b, server.getCatalogTracker(), true); + services.postOpenDeployTasks(b, server.getCatalogTracker()); // Should add it to OnlineRegions services.addToOnlineRegions(b); - services.postOpenDeployTasks(a, server.getCatalogTracker(), true); + services.postOpenDeployTasks(a, server.getCatalogTracker()); services.addToOnlineRegions(a); } catch (KeeperException ke) { throw new IOException(ke); @@ -704,7 +708,7 @@ public class SplitTransaction { r.setOpMetricsReadRequestCount(halfParentReadRequestCount); long halfParentWriteRequest = this.parent.getWriteRequestsCount() / 2; r.writeRequestsCount.set(halfParentWriteRequest); - r.setOpMetricsWriteRequestCount(halfParentWriteRequest); + r.setOpMetricsWriteRequestCount(halfParentWriteRequest); HRegion.moveInitialFilesIntoPlace(fs, regionDir, r.getRegionDir()); return r; } @@ -749,7 +753,7 @@ public class SplitTransaction { while (iterator.hasPrevious()) { JournalEntry je = iterator.previous(); switch(je) { - + case SET_SPLITTING_IN_ZK: if (server != null && server.getZooKeeper() != null) { cleanZK(server, this.parent.getRegionInfo()); @@ -866,8 +870,8 @@ public class SplitTransaction { * @param region region to be created as offline * @param serverName server event originates from * @return Version of znode created. - * @throws KeeperException - * @throws IOException + * @throws KeeperException + * @throws IOException */ void createNodeSplitting(final ZooKeeperWatcher zkw, final HRegionInfo region, final ServerName serverName) throws KeeperException, IOException { @@ -914,7 +918,7 @@ public class SplitTransaction { * @param serverName server event originates from * @return version of node after transition, -1 if unsuccessful transition * @throws KeeperException if unexpected zookeeper exception - * @throws IOException + * @throws IOException */ private static int transitionNodeSplit(ZooKeeperWatcher zkw, HRegionInfo parent, HRegionInfo a, HRegionInfo b, ServerName serverName, @@ -927,7 +931,7 @@ public class SplitTransaction { } /** - * + * * @param zkw zk reference * @param parent region to be transitioned to splitting * @param serverName server event originates from diff --git src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java index 203e031..5580c70 100644 --- src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java +++ src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java @@ -237,7 +237,7 @@ public class OpenRegionHandler extends EventHandler { public void run() { try { this.services.postOpenDeployTasks(this.region, - this.server.getCatalogTracker(), false); + this.server.getCatalogTracker()); } catch (Exception e) { LOG.warn("Exception running postOpenDeployTasks; region=" + this.region.getRegionInfo().getEncodedName(), e); diff --git src/main/java/org/apache/hadoop/hbase/util/FSTableDescriptors.java src/main/java/org/apache/hadoop/hbase/util/FSTableDescriptors.java index 786b334..0a3ef45 100644 --- src/main/java/org/apache/hadoop/hbase/util/FSTableDescriptors.java +++ src/main/java/org/apache/hadoop/hbase/util/FSTableDescriptors.java @@ -49,7 +49,7 @@ import org.apache.hadoop.hbase.TableInfoMissingException; * passed filesystem. It expects descriptors to be in a file under the * table's directory in FS. Can be read-only -- i.e. does not modify * the filesystem or can be read and write. - * + * *

    Also has utility for keeping up the table descriptors tableinfo file. * The table schema file is kept under the table directory in the filesystem. * It has a {@link #TABLEINFO_NAME} prefix and then a suffix that is the @@ -157,7 +157,7 @@ public class FSTableDescriptors implements TableDescriptors { return cachedtdm.getTableDescriptor(); } } - + TableDescriptorModtime tdmt = null; try { tdmt = getTableDescriptorModtime(this.fs, this.rootdir, tablename); @@ -168,7 +168,7 @@ public class FSTableDescriptors implements TableDescriptors { LOG.debug("Exception during readTableDecriptor. Current table name = " + tablename, ioe); } - + if (tdmt == null) { LOG.warn("The following folder is in HBase's root directory and " + "doesn't contain a table descriptor, " + @@ -235,7 +235,7 @@ public class FSTableDescriptors implements TableDescriptors { /** * Checks if .tableinfo exists for given table - * + * * @param fs file system * @param rootdir root directory of HBase installation * @param tableName name of table @@ -473,7 +473,7 @@ public class FSTableDescriptors implements TableDescriptors { * @param tableDir * @param status * @return Descriptor file or null if we failed write. - * @throws IOException + * @throws IOException */ private static Path writeTableDescriptor(final FileSystem fs, final HTableDescriptor hTableDescriptor, final Path tableDir, @@ -543,7 +543,7 @@ public class FSTableDescriptors implements TableDescriptors { /** * Create new HTableDescriptor in HDFS. Happens when we are creating table. - * + * * @param htableDescriptor * @param conf */ @@ -557,7 +557,7 @@ public class FSTableDescriptors implements TableDescriptors { * Create new HTableDescriptor in HDFS. Happens when we are creating table. If * forceCreation is true then even if previous table descriptor is present it * will be overwritten - * + * * @param htableDescriptor * @param conf * @param forceCreation True if we are to overwrite existing file. @@ -587,7 +587,7 @@ public class FSTableDescriptors implements TableDescriptors { * Create new HTableDescriptor in HDFS. Happens when we are creating table. If * forceCreation is true then even if previous table descriptor is present it * will be overwritten - * + * * @param fs * @param htableDescriptor * @param rootdir @@ -620,8 +620,10 @@ public class FSTableDescriptors implements TableDescriptors { LOG.info("Current tableInfoPath = " + status.getPath()); if (!forceCreation) { if (fs.exists(status.getPath()) && status.getLen() > 0) { - LOG.info("TableInfo already exists.. Skipping creation"); - return false; + if (getTableDescriptor(fs, status.getPath().getParent()).equals(htableDescriptor)) { + LOG.info("TableInfo already exists.. Skipping creation"); + return false; + } } } } diff --git src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterCoprocessorExceptionWithAbort.java src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterCoprocessorExceptionWithAbort.java index 0e0b422..f701765 100644 --- src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterCoprocessorExceptionWithAbort.java +++ src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterCoprocessorExceptionWithAbort.java @@ -187,9 +187,7 @@ public class TestMasterCoprocessorExceptionWithAbort { // (namely the part that shows the set of loaded coprocessors). // In this test, there is only a single coprocessor (BuggyMasterObserver). assertTrue(master.getLoadedCoprocessors(). - equals("[" + - TestMasterCoprocessorExceptionWithAbort.BuggyMasterObserver.class.getName() + - "]")); + contains(TestMasterCoprocessorExceptionWithAbort.BuggyMasterObserver.class.getName())); CreateTableThread createTableThread = new CreateTableThread(UTIL); diff --git src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterCoprocessorExceptionWithRemove.java src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterCoprocessorExceptionWithRemove.java index d7e0f65..77da489 100644 --- src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterCoprocessorExceptionWithRemove.java +++ src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterCoprocessorExceptionWithRemove.java @@ -20,11 +20,21 @@ package org.apache.hadoop.hbase.coprocessor; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.IOException; -import java.io.InterruptedIOException; import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.*; +import org.apache.hadoop.hbase.Abortable; +import org.apache.hadoop.hbase.CoprocessorEnvironment; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.MediumTests; +import org.apache.hadoop.hbase.MiniHBaseCluster; import org.apache.hadoop.hbase.client.HBaseAdmin; import org.apache.hadoop.hbase.master.HMaster; import org.apache.hadoop.hbase.master.MasterCoprocessorHost; @@ -36,8 +46,6 @@ import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; -import static org.junit.Assert.*; - /** * Tests unhandled exceptions thrown by coprocessors running on master. * Expected result is that the master will remove the buggy coprocessor from @@ -174,7 +182,7 @@ public class TestMasterCoprocessorExceptionWithRemove { // In this test, there is only a single coprocessor (BuggyMasterObserver). String coprocessorName = BuggyMasterObserver.class.getName(); - assertTrue(master.getLoadedCoprocessors().equals("[" + coprocessorName + "]")); + assertTrue(master.getLoadedCoprocessors().contains(coprocessorName)); HTableDescriptor htd1 = new HTableDescriptor(TEST_TABLE1); htd1.addFamily(new HColumnDescriptor(TEST_FAMILY1)); @@ -202,7 +210,7 @@ public class TestMasterCoprocessorExceptionWithRemove { masterTracker.masterZKNodeWasDeleted); String loadedCoprocessors = master.getLoadedCoprocessors(); - assertTrue(loadedCoprocessors.equals("[" + coprocessorName + "]")); + assertTrue(loadedCoprocessors.contains(coprocessorName)); // Verify that BuggyMasterObserver has been removed due to its misbehavior // by creating another table: should not have a problem this time. diff --git src/test/java/org/apache/hadoop/hbase/regionserver/TestEndToEndSplitTransaction.java src/test/java/org/apache/hadoop/hbase/regionserver/TestEndToEndSplitTransaction.java index 4e62a1f..3187d78 100644 --- src/test/java/org/apache/hadoop/hbase/regionserver/TestEndToEndSplitTransaction.java +++ src/test/java/org/apache/hadoop/hbase/regionserver/TestEndToEndSplitTransaction.java @@ -117,7 +117,7 @@ public class TestEndToEndSplitTransaction { // 3. finish phase II // note that this replicates some code from SplitTransaction // 2nd daughter first - server.postOpenDeployTasks(regions.getSecond(), server.getCatalogTracker(), true); + server.postOpenDeployTasks(regions.getSecond(), server.getCatalogTracker()); // Add to online regions server.addToOnlineRegions(regions.getSecond()); // THIS is the crucial point: @@ -127,7 +127,7 @@ public class TestEndToEndSplitTransaction { assertTrue(test(con, tableName, lastRow, server)); // first daughter second - server.postOpenDeployTasks(regions.getFirst(), server.getCatalogTracker(), true); + server.postOpenDeployTasks(regions.getFirst(), server.getCatalogTracker()); // Add to online regions server.addToOnlineRegions(regions.getFirst()); assertTrue(test(con, tableName, firstRow, server)); diff --git src/test/java/org/apache/hadoop/hbase/util/MockRegionServerServices.java src/test/java/org/apache/hadoop/hbase/util/MockRegionServerServices.java index f115fb2..96c2c95 100644 --- src/test/java/org/apache/hadoop/hbase/util/MockRegionServerServices.java +++ src/test/java/org/apache/hadoop/hbase/util/MockRegionServerServices.java @@ -47,7 +47,7 @@ import org.apache.zookeeper.KeeperException; public class MockRegionServerServices implements RegionServerServices { private final Map regions = new HashMap(); private boolean stopping = false; - private final ConcurrentSkipListMap rit = + private final ConcurrentSkipListMap rit = new ConcurrentSkipListMap(Bytes.BYTES_COMPARATOR); private HFileSystem hfs = null; @@ -71,7 +71,7 @@ public class MockRegionServerServices implements RegionServerServices { } @Override - public void postOpenDeployTasks(HRegion r, CatalogTracker ct, boolean daughter) + public void postOpenDeployTasks(HRegion r, CatalogTracker ct) throws KeeperException, IOException { addToOnlineRegions(r); } @@ -110,7 +110,7 @@ public class MockRegionServerServices implements RegionServerServices { public ZooKeeperWatcher getZooKeeper() { return null; } - + public RegionServerAccounting getRegionServerAccounting() { return null; } diff --git src/test/java/org/apache/hadoop/hbase/util/TestFSTableDescriptors.java src/test/java/org/apache/hadoop/hbase/util/TestFSTableDescriptors.java index c5dda2f..ea87e3d 100644 --- src/test/java/org/apache/hadoop/hbase/util/TestFSTableDescriptors.java +++ src/test/java/org/apache/hadoop/hbase/util/TestFSTableDescriptors.java @@ -17,7 +17,12 @@ */ package org.apache.hadoop.hbase.util; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.FileNotFoundException; import java.io.IOException; @@ -28,7 +33,13 @@ 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.hbase.*; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.MediumTests; +import org.apache.hadoop.hbase.TableDescriptors; +import org.apache.hadoop.hbase.TableExistsException; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -265,6 +276,24 @@ public class TestFSTableDescriptors { } } + @Test + public void testCreateTableDescriptorUpdatesIfExistsAlready() throws IOException { + Path testdir = UTIL.getDataTestDir("testCreateTableDescriptorUpdatesIfThereExistsAlready"); + HTableDescriptor htd = new HTableDescriptor( + "testCreateTableDescriptorUpdatesIfThereExistsAlready"); + FileSystem fs = FileSystem.get(UTIL.getConfiguration()); + assertTrue(FSTableDescriptors.createTableDescriptor(fs, testdir, htd)); + assertFalse(FSTableDescriptors.createTableDescriptor(fs, testdir, htd)); + htd.setValue(Bytes.toBytes("mykey"), Bytes.toBytes("myValue")); + assertTrue(FSTableDescriptors.createTableDescriptor(fs, testdir, htd)); //this will re-create + Path tableDir = FSUtils.getTablePath(testdir, htd.getName()); + Path tmpTableDir = new Path(tableDir, ".tmp"); + FileStatus[] statuses = fs.listStatus(tmpTableDir); + assertTrue(statuses.length == 0); + + assertEquals(htd, FSTableDescriptors.getTableDescriptor(fs, tableDir)); + } + @org.junit.Rule public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu = new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();