You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by te...@apache.org on 2012/05/26 11:19:00 UTC
svn commit: r1342866 - in /hbase/branches/0.92: ./
src/main/java/org/apache/hadoop/hbase/catalog/
src/main/java/org/apache/hadoop/hbase/client/
src/main/java/org/apache/hadoop/hbase/master/
src/main/java/org/apache/hadoop/hbase/regionserver/ src/main/j...
Author: tedyu
Date: Sat May 26 09:18:59 2012
New Revision: 1342866
URL: http://svn.apache.org/viewvc?rev=1342866&view=rev
Log:
HBASE-5986 Clients can see holes in the META table when regions are being split (Enis)
Modified:
hbase/branches/0.92/CHANGES.txt
hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java
hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java
hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HConnectionManager.java
hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HTable.java
hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java
hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/util/HBaseFsck.java
hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/regionserver/TestEndToEndSplitTransaction.java
Modified: hbase/branches/0.92/CHANGES.txt
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/CHANGES.txt?rev=1342866&r1=1342865&r2=1342866&view=diff
==============================================================================
--- hbase/branches/0.92/CHANGES.txt (original)
+++ hbase/branches/0.92/CHANGES.txt Sat May 26 09:18:59 2012
@@ -72,6 +72,7 @@ Release 0.92.2 - Unreleased
HBASE-5757 TableInputFormat should handle as many errors as possible (Jan Lukavsky)
HBASE-6061 Fix ACL "Admin" Table inconsistent permission check (Matteo Bertozzi)
HBASE-6047 Put.has() can't determine result correctly (Alex Newman)
+ HBASE-5986 Clients can see holes in the META table when regions are being split (Enis)
IMPROVEMENTS
HBASE-5592 Make it easier to get a table from shell (Ben West)
Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java?rev=1342866&r1=1342865&r2=1342866&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java Sat May 26 09:18:59 2012
@@ -33,6 +33,7 @@ import org.apache.hadoop.hbase.client.HT
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.PairOfSameType;
import org.apache.hadoop.hbase.util.Writables;
/**
@@ -132,7 +133,7 @@ public class MetaEditor {
t.close();
}
}
-
+
/**
* Adds a META row for the specified new region.
* @param regionInfo region information
@@ -155,7 +156,7 @@ public class MetaEditor {
List<HRegionInfo> regionInfos)
throws IOException {
List<Put> puts = new ArrayList<Put>();
- for (HRegionInfo regionInfo : regionInfos) {
+ for (HRegionInfo regionInfo : regionInfos) {
puts.add(makePutFromRegionInfo(regionInfo));
}
putsToMetaTable(catalogTracker, puts);
@@ -304,6 +305,18 @@ public class MetaEditor {
return info;
}
+ /**
+ * Returns the daughter regions by reading from the corresponding columns of the .META. table
+ * Result. If the region is not a split parent region, it returns PairOfSameType(null, null).
+ */
+ public static PairOfSameType<HRegionInfo> getDaughterRegions(Result data) throws IOException {
+ HRegionInfo splitA = Writables.getHRegionInfoOrNull(
+ data.getValue(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER));
+ HRegionInfo splitB = Writables.getHRegionInfoOrNull(
+ data.getValue(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER));
+ return new PairOfSameType<HRegionInfo>(splitA, splitB);
+ }
+
private static Put addRegionInfo(final Put p, final HRegionInfo hri)
throws IOException {
p.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java?rev=1342866&r1=1342865&r2=1342866&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java Sat May 26 09:18:59 2012
@@ -53,6 +53,7 @@ import org.apache.hadoop.hbase.ZooKeeper
import org.apache.hadoop.hbase.catalog.CatalogTracker;
import org.apache.hadoop.hbase.catalog.MetaReader;
import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
+import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitorBase;
import org.apache.hadoop.hbase.ipc.HMasterInterface;
import org.apache.hadoop.hbase.ipc.HRegionInterface;
import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
@@ -369,7 +370,7 @@ public class HBaseAdmin implements Abort
++tries) {
// Wait for new table to come on-line
final AtomicInteger actualRegCount = new AtomicInteger(0);
- MetaScannerVisitor visitor = new MetaScannerVisitor() {
+ MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
@Override
public boolean processRow(Result rowResult) throws IOException {
HRegionInfo info = Writables.getHRegionInfoOrNull(
Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HConnectionManager.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HConnectionManager.java?rev=1342866&r1=1342865&r2=1342866&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HConnectionManager.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HConnectionManager.java Sat May 26 09:18:59 2012
@@ -65,6 +65,7 @@ import org.apache.hadoop.hbase.ServerNam
import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
+import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitorBase;
import org.apache.hadoop.hbase.client.coprocessor.Batch;
import org.apache.hadoop.hbase.ipc.CoprocessorProtocol;
import org.apache.hadoop.hbase.ipc.ExecRPCInvoker;
@@ -718,7 +719,7 @@ public class HConnectionManager {
public boolean isTableAvailable(final byte[] tableName) throws IOException {
final AtomicBoolean available = new AtomicBoolean(true);
final AtomicInteger regionCount = new AtomicInteger(0);
- MetaScannerVisitor visitor = new MetaScannerVisitor() {
+ MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
@Override
public boolean processRow(Result row) throws IOException {
byte[] value = row.getValue(HConstants.CATALOG_FAMILY,
@@ -829,7 +830,7 @@ public class HConnectionManager {
final byte[] row) {
// Implement a new visitor for MetaScanner, and use it to walk through
// the .META.
- MetaScannerVisitor visitor = new MetaScannerVisitor() {
+ MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
public boolean processRow(Result result) throws IOException {
try {
byte[] value = result.getValue(HConstants.CATALOG_FAMILY,
Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HTable.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HTable.java?rev=1342866&r1=1342865&r2=1342866&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HTable.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/HTable.java Sat May 26 09:18:59 2012
@@ -54,7 +54,6 @@ import org.apache.hadoop.hbase.NotServin
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.UnknownScannerException;
import org.apache.hadoop.hbase.client.HConnectionManager.HConnectable;
-import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
import org.apache.hadoop.hbase.client.coprocessor.Batch;
import org.apache.hadoop.hbase.ipc.CoprocessorProtocol;
import org.apache.hadoop.hbase.ipc.ExecRPCInvoker;
@@ -62,7 +61,6 @@ import org.apache.hadoop.hbase.regionser
import org.apache.hadoop.hbase.util.Addressing;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;
-import org.apache.hadoop.hbase.util.Writables;
/**
* <p>Used to communicate with a single HBase table.
@@ -451,28 +449,15 @@ public class HTable implements HTableInt
*/
@SuppressWarnings("unchecked")
public Pair<byte[][],byte[][]> getStartEndKeys() throws IOException {
- final List<byte[]> startKeyList = new ArrayList<byte[]>();
- final List<byte[]> endKeyList = new ArrayList<byte[]>();
- MetaScannerVisitor visitor = new MetaScannerVisitor() {
- public boolean processRow(Result rowResult) throws IOException {
- byte [] bytes = rowResult.getValue(HConstants.CATALOG_FAMILY,
- HConstants.REGIONINFO_QUALIFIER);
- if (bytes == null) {
- LOG.warn("Null " + HConstants.REGIONINFO_QUALIFIER + " cell in " +
- rowResult);
- return true;
- }
- HRegionInfo info = Writables.getHRegionInfo(bytes);
- if (Bytes.equals(info.getTableName(), getTableName())) {
- if (!(info.isOffline() || info.isSplit())) {
- startKeyList.add(info.getStartKey());
- endKeyList.add(info.getEndKey());
- }
- }
- return true;
- }
- };
- MetaScanner.metaScan(configuration, visitor, this.tableName);
+ NavigableMap<HRegionInfo, ServerName> regions = getRegionLocations();
+ final List<byte[]> startKeyList = new ArrayList<byte[]>(regions.size());
+ final List<byte[]> endKeyList = new ArrayList<byte[]>(regions.size());
+
+ for (HRegionInfo region : regions.keySet()) {
+ startKeyList.add(region.getStartKey());
+ endKeyList.add(region.getEndKey());
+ }
+
return new Pair<byte [][], byte [][]>(
startKeyList.toArray(new byte[startKeyList.size()][]),
endKeyList.toArray(new byte[endKeyList.size()][]));
@@ -488,32 +473,18 @@ public class HTable implements HTableInt
final Map<HRegionInfo, HServerAddress> regionMap =
new TreeMap<HRegionInfo, HServerAddress>();
- MetaScannerVisitor visitor = new MetaScannerVisitor() {
- public boolean processRow(Result rowResult) throws IOException {
- HRegionInfo info = Writables.getHRegionInfo(
- rowResult.getValue(HConstants.CATALOG_FAMILY,
- HConstants.REGIONINFO_QUALIFIER));
+ final Map<HRegionInfo, ServerName> regionLocations = getRegionLocations();
- if (!(Bytes.equals(info.getTableName(), getTableName()))) {
- return false;
- }
-
- HServerAddress server = new HServerAddress();
- byte [] value = rowResult.getValue(HConstants.CATALOG_FAMILY,
- HConstants.SERVER_QUALIFIER);
- if (value != null && value.length > 0) {
- String hostAndPort = Bytes.toString(value);
- server = new HServerAddress(Addressing.createInetSocketAddressFromHostAndPortStr(hostAndPort));
- }
-
- if (!(info.isOffline() || info.isSplit())) {
- regionMap.put(new UnmodifyableHRegionInfo(info), server);
- }
- return true;
+ for (Map.Entry<HRegionInfo, ServerName> entry : regionLocations.entrySet()) {
+ HServerAddress server = new HServerAddress();
+ ServerName serverName = entry.getValue();
+ if (serverName != null && serverName.getHostAndPort() != null) {
+ server = new HServerAddress(Addressing.createInetSocketAddressFromHostAndPortStr(
+ serverName.getHostAndPort()));
}
+ regionMap.put(entry.getKey(), server);
+ }
- };
- MetaScanner.metaScan(configuration, visitor, tableName);
return regionMap;
}
Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java?rev=1342866&r1=1342865&r2=1342866&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java Sat May 26 09:18:59 2012
@@ -20,6 +20,7 @@
package org.apache.hadoop.hbase.client;
+import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -45,9 +46,13 @@ import org.apache.hadoop.hbase.util.Writ
* Scanner class that contains the <code>.META.</code> table scanning logic
* and uses a Retryable scanner. Provided visitors will be called
* for each row.
- *
+ *
* Although public visibility, this is not a public-facing API and may evolve in
* minor releases.
+ *
+ * <p> 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. </p>
*/
public class MetaScanner {
private static final Log LOG = LogFactory.getLog(MetaScanner.class);
@@ -110,7 +115,7 @@ public class MetaScanner {
* <code>rowLimit</code> of rows.
*
* @param configuration HBase configuration.
- * @param visitor Visitor object.
+ * @param visitor Visitor object. Closes the visitor before returning.
* @param tableName User table name in meta table to start scan at. Pass
* null if not interested in a particular table.
* @param row Name of the row at the user table. The scan will start from
@@ -124,14 +129,18 @@ public class MetaScanner {
final MetaScannerVisitor visitor, final byte[] tableName,
final byte[] row, final int rowLimit, final byte[] metaTableName)
throws IOException {
- HConnectionManager.execute(new HConnectable<Void>(configuration) {
- @Override
- public Void connect(HConnection connection) throws IOException {
- metaScan(conf, connection, visitor, tableName, row, rowLimit,
- metaTableName);
- return null;
- }
- });
+ try {
+ HConnectionManager.execute(new HConnectable<Void>(configuration) {
+ @Override
+ public Void connect(HConnection connection) throws IOException {
+ metaScan(conf, connection, visitor, tableName, row, rowLimit,
+ metaTableName);
+ return null;
+ }
+ });
+ } finally {
+ visitor.close();
+ }
}
private static void metaScan(Configuration configuration, HConnection connection,
@@ -165,7 +174,7 @@ public class MetaScanner {
Bytes.toString(tableName) + ", row=" + Bytes.toStringBinary(searchRow));
}
HRegionInfo regionInfo = Writables.getHRegionInfo(value);
-
+
byte[] rowBefore = regionInfo.getStartKey();
startRow = HRegionInfo.createRegionName(tableName, rowBefore,
HConstants.ZEROES, false);
@@ -253,9 +262,9 @@ public class MetaScanner {
public static List<HRegionInfo> listAllRegions(Configuration conf, final boolean offlined)
throws IOException {
final List<HRegionInfo> regions = new ArrayList<HRegionInfo>();
- MetaScannerVisitor visitor = new MetaScannerVisitor() {
+ MetaScannerVisitor visitor = new BlockingMetaScannerVisitor(conf) {
@Override
- public boolean processRow(Result result) throws IOException {
+ public boolean processRowInternal(Result result) throws IOException {
if (result == null || result.isEmpty()) {
return true;
}
@@ -284,19 +293,16 @@ public class MetaScanner {
* @return Map of all user-space regions to servers
* @throws IOException
*/
- public static NavigableMap<HRegionInfo, ServerName> allTableRegions(Configuration conf, final byte [] tablename, final boolean offlined)
- throws IOException {
+ public static NavigableMap<HRegionInfo, ServerName> allTableRegions(Configuration conf,
+ final byte [] tablename, final boolean offlined) throws IOException {
final NavigableMap<HRegionInfo, ServerName> regions =
new TreeMap<HRegionInfo, ServerName>();
- MetaScannerVisitor visitor = new MetaScannerVisitor() {
+ MetaScannerVisitor visitor = new TableMetaScannerVisitor(conf, tablename) {
@Override
- public boolean processRow(Result rowResult) throws IOException {
+ public boolean processRowInternal(Result rowResult) throws IOException {
HRegionInfo info = Writables.getHRegionInfo(
rowResult.getValue(HConstants.CATALOG_FAMILY,
HConstants.REGIONINFO_QUALIFIER));
- if (!(Bytes.equals(info.getTableName(), tablename))) {
- return false;
- }
byte [] value = rowResult.getValue(HConstants.CATALOG_FAMILY,
HConstants.SERVER_QUALIFIER);
String hostAndPort = null;
@@ -324,7 +330,7 @@ public class MetaScanner {
/**
* Visitor class called to process each row of the .META. table
*/
- public interface MetaScannerVisitor {
+ public interface MetaScannerVisitor extends Closeable {
/**
* Visitor method that accepts a RowResult and the meta region location.
* Implementations can return false to stop the region's loop if it becomes
@@ -336,4 +342,153 @@ public class MetaScanner {
*/
public boolean processRow(Result rowResult) throws IOException;
}
+
+ public static abstract class MetaScannerVisitorBase implements MetaScannerVisitor {
+ @Override
+ public void close() throws IOException {
+ }
+ }
+
+ /**
+ * A MetaScannerVisitor that provides a consistent view of the table's
+ * META entries during concurrent splits (see HBASE-5986 for details). This class
+ * 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
+ extends MetaScannerVisitorBase {
+
+ private static final int DEFAULT_BLOCKING_TIMEOUT = 10000;
+ private Configuration conf;
+ private TreeSet<byte[]> daughterRegions = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
+ private int blockingTimeout;
+ private HTable metaTable;
+
+ public BlockingMetaScannerVisitor(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));
+ 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.");
+ }
+ }
+
+ return processRowInternal(rowResult);
+ }
+
+ 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;
+ }
+ }
+
+ /**
+ * A MetaScannerVisitor for a table. Provides a consistent view of the table's
+ * META entries during concurrent splits (see HBASE-5986 for details). This class
+ * 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 {
+ private byte[] tableName;
+
+ public TableMetaScannerVisitor(Configuration conf, byte[] tableName) {
+ super(conf);
+ this.tableName = tableName;
+ }
+
+ @Override
+ public final boolean processRow(Result rowResult) throws IOException {
+ HRegionInfo info = Writables.getHRegionInfoOrNull(
+ rowResult.getValue(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER));
+ if (info == null) {
+ return true;
+ }
+ if (!(Bytes.equals(info.getTableName(), tableName))) {
+ return false;
+ }
+ return super.processRow(rowResult);
+ }
+
+ }
}
Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/master/HMaster.java?rev=1342866&r1=1342865&r2=1342866&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/master/HMaster.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/master/HMaster.java Sat May 26 09:18:59 2012
@@ -64,6 +64,7 @@ import org.apache.hadoop.hbase.catalog.M
import org.apache.hadoop.hbase.client.HConnectionManager;
import org.apache.hadoop.hbase.client.MetaScanner;
import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
+import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitorBase;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
import org.apache.hadoop.hbase.executor.ExecutorService;
@@ -1210,7 +1211,7 @@ Server {
new AtomicReference<Pair<HRegionInfo, ServerName>>(null);
MetaScannerVisitor visitor =
- new MetaScannerVisitor() {
+ new MetaScannerVisitorBase() {
@Override
public boolean processRow(Result data) throws IOException {
if (data == null || data.size() <= 0) {
Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java?rev=1342866&r1=1342865&r2=1342866&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java Sat May 26 09:18:59 2012
@@ -3294,6 +3294,29 @@ public class HRegionServer implements HR
return wal.rollWriter(true);
}
+ /**
+ * Gets the online regions of the specified table.
+ * This method looks at the in-memory onlineRegions. It does not go to <code>.META.</code>.
+ * Only returns <em>online</em> regions. If a region on this table has been
+ * closed during a disable, etc., it will not be included in the returned list.
+ * So, the returned list may not necessarily be ALL regions in this table, its
+ * all the ONLINE regions in the table.
+ * @param tableName
+ * @return Online regions from <code>tableName</code>
+ */
+ public List<HRegion> getOnlineRegions(byte[] tableName) {
+ List<HRegion> tableRegions = new ArrayList<HRegion>();
+ synchronized (this.onlineRegions) {
+ for (HRegion region: this.onlineRegions.values()) {
+ HRegionInfo regionInfo = region.getRegionInfo();
+ if(Bytes.equals(regionInfo.getTableName(), tableName)) {
+ tableRegions.add(region);
+ }
+ }
+ }
+ return tableRegions;
+ }
+
// used by org/apache/hbase/tmpl/regionserver/RSStatusTmpl.jamon (HBASE-4070).
public String[] getCoprocessors() {
HServerLoad hsl = buildServerLoad();
Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/util/HBaseFsck.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/util/HBaseFsck.java?rev=1342866&r1=1342865&r2=1342866&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/util/HBaseFsck.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/util/HBaseFsck.java Sat May 26 09:18:59 2012
@@ -66,6 +66,7 @@ import org.apache.hadoop.hbase.client.HC
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.MetaScanner;
import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
+import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitorBase;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.io.hfile.CacheConfig;
@@ -2181,7 +2182,7 @@ public class HBaseFsck {
return false;
}
- MetaScannerVisitor visitor = new MetaScannerVisitor() {
+ MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
int countRecord = 1;
// comparator to sort KeyValues with latest modtime
Modified: hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/regionserver/TestEndToEndSplitTransaction.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/regionserver/TestEndToEndSplitTransaction.java?rev=1342866&r1=1342865&r2=1342866&view=diff
==============================================================================
--- hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/regionserver/TestEndToEndSplitTransaction.java (original)
+++ hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/regionserver/TestEndToEndSplitTransaction.java Sat May 26 09:18:59 2012
@@ -17,28 +17,55 @@
*/
package org.apache.hadoop.hbase.regionserver;
-import java.io.IOException;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Random;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.Chore;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HServerAddress;
+import org.apache.hadoop.hbase.NotServingRegionException;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.Stoppable;
+import org.apache.hadoop.hbase.catalog.MetaEditor;
import org.apache.hadoop.hbase.client.Get;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HConnection;
import org.apache.hadoop.hbase.client.HConnectionManager;
import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.MetaScanner;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.Pair;
import org.apache.hadoop.hbase.util.PairOfSameType;
+import org.apache.hadoop.hbase.util.Threads;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Sets;
public class TestEndToEndSplitTransaction {
+ private static final Log LOG = LogFactory.getLog(TestEndToEndSplitTransaction.class);
private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
+ private static final Configuration conf = TEST_UTIL.getConfiguration();
@BeforeClass
public static void beforeAllTests() throws Exception {
@@ -50,7 +77,7 @@ public class TestEndToEndSplitTransactio
public static void afterAllTests() throws Exception {
TEST_UTIL.shutdownMiniCluster();
}
-
+
@Test
public void testMasterOpsWhileSplitting() throws Exception {
byte[] tableName = Bytes.toBytes("TestSplit");
@@ -125,4 +152,336 @@ public class TestEndToEndSplitTransactio
}
return true;
}
+
+ /**
+ * Tests that the client sees meta table changes as atomic during splits
+ */
+ @Test
+ public void testFromClientSideWhileSplitting() throws Throwable {
+ LOG.info("Starting testFromClientSideWhileSplitting");
+ final byte[] TABLENAME = Bytes.toBytes("testFromClientSideWhileSplitting");
+ final byte[] FAMILY = Bytes.toBytes("family");
+
+ //SplitTransaction will update the meta table by offlining the parent region, and adding info
+ //for daughters.
+ HTable table = TEST_UTIL.createTable(TABLENAME, FAMILY);
+
+ Stoppable stopper = new SimpleStoppable();
+ RegionSplitter regionSplitter = new RegionSplitter(table);
+ RegionChecker regionChecker = new RegionChecker(conf, stopper, TABLENAME);
+
+ regionChecker.start();
+ regionSplitter.start();
+
+ //wait until the splitter is finished
+ regionSplitter.join();
+ stopper.stop(null);
+
+ if (regionChecker.ex != null) {
+ throw regionChecker.ex;
+ }
+
+ if (regionSplitter.ex != null) {
+ throw regionSplitter.ex;
+ }
+
+ //one final check
+ regionChecker.verify();
+ }
+
+ private static class SimpleStoppable implements Stoppable {
+ volatile boolean stopped = false;
+
+ @Override
+ public void stop(String why) {
+ this.stopped = true;
+ }
+
+ @Override
+ public boolean isStopped() {
+ return stopped;
+ }
+ }
+
+ static class RegionSplitter extends Thread {
+ Throwable ex;
+ HTable table;
+ byte[] tableName, family;
+ HBaseAdmin admin;
+ HTable metaTable;
+ HRegionServer rs;
+
+ RegionSplitter(HTable table) throws IOException {
+ this.table = table;
+ this.tableName = table.getTableName();
+ this.family = table.getTableDescriptor().getFamiliesKeys().iterator().next();
+ admin = TEST_UTIL.getHBaseAdmin();
+ rs = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0);
+ metaTable = new HTable(conf, HConstants.META_TABLE_NAME);
+ }
+
+ public void run() {
+ try {
+ Random random = new Random();
+ for (int i=0; i< 5; i++) {
+ NavigableMap<HRegionInfo, ServerName> regions = MetaScanner.allTableRegions(conf, tableName, false);
+ if (regions.size() == 0) {
+ continue;
+ }
+ int regionIndex = random.nextInt(regions.size());
+
+ //pick a random region and split it into two
+ HRegionInfo region = Iterators.get(regions.keySet().iterator(), regionIndex);
+
+ //pick the mid split point
+ int start = 0, end = Integer.MAX_VALUE;
+ if (region.getStartKey().length > 0) {
+ start = Bytes.toInt(region.getStartKey());
+ }
+ if (region.getEndKey().length > 0) {
+ end = Bytes.toInt(region.getEndKey());
+ }
+ int mid = start + ((end - start) / 2);
+ byte[] splitPoint = Bytes.toBytes(mid);
+
+ //put some rows to the regions
+ addData(start);
+ addData(mid);
+
+ flushAndBlockUntilDone(region.getRegionName());
+ compactAndBlockUntilDone(region.getRegionName());
+
+ log("Initiating region split for:" + region.getRegionNameAsString());
+ try {
+ admin.split(region.getRegionName(), splitPoint);
+ //wait until the split is complete
+ blockUntilRegionSplit(50000, region.getRegionName(), true);
+
+ } catch (NotServingRegionException ex) {
+ //ignore
+ }
+ }
+ } catch (Throwable ex) {
+ this.ex = ex;
+ } finally {
+ if (metaTable != null) {
+ IOUtils.closeQuietly(metaTable);
+ }
+ }
+ }
+
+ void addData(int start) throws IOException {
+ for (int i=start; i< start + 100; i++) {
+ Put put = new Put(Bytes.toBytes(i));
+
+ put.add(family, family, Bytes.toBytes(i));
+ table.put(put);
+ }
+ table.flushCommits();
+ }
+
+ void flushAndBlockUntilDone(byte[] regionName) throws IOException, InterruptedException {
+ log("flushing region: " + Bytes.toStringBinary(regionName));
+ admin.flush(regionName);
+ log("blocking until flush is complete: " + Bytes.toStringBinary(regionName));
+ Threads.sleepWithoutInterrupt(500);
+ while (rs.cacheFlusher.getFlushQueueSize() > 0) {
+ Threads.sleep(50);
+ }
+ }
+
+ void compactAndBlockUntilDone(byte[] regionName) throws IOException,
+ InterruptedException {
+ log("Compacting region: " + Bytes.toStringBinary(regionName));
+ admin.majorCompact(regionName);
+ log("blocking until compaction is complete: " + Bytes.toStringBinary(regionName));
+ Threads.sleepWithoutInterrupt(500);
+ while (rs.compactSplitThread.getCompactionQueueSize() > 0) {
+ Threads.sleep(50);
+ }
+ }
+
+ /** bloks until the region split is complete in META and region server opens the daughters */
+ void blockUntilRegionSplit(long timeout, final byte[] regionName, boolean waitForDaughters)
+ throws IOException, InterruptedException {
+ long start = System.currentTimeMillis();
+ log("blocking until region is split:" + Bytes.toStringBinary(regionName));
+ HRegionInfo daughterA = null, daughterB = null;
+
+ while (System.currentTimeMillis() - start < timeout) {
+ Result result = getRegionRow(regionName);
+ if (result == null) {
+ break;
+ }
+
+ HRegionInfo region = MetaEditor.getHRegionInfo(result);
+ if(region.isSplitParent()) {
+ log("found parent region: " + region.toString());
+ PairOfSameType<HRegionInfo> pair = MetaEditor.getDaughterRegions(result);
+ daughterA = pair.getFirst();
+ daughterB = pair.getSecond();
+ break;
+ }
+ sleep(100);
+ }
+
+ //if we are here, this means the region split is complete or timed out
+ if (waitForDaughters) {
+ long rem = timeout - (System.currentTimeMillis() - start);
+ blockUntilRegionIsInMeta(rem, daughterA.getRegionName());
+
+ rem = timeout - (System.currentTimeMillis() - start);
+ blockUntilRegionIsInMeta(rem, daughterB.getRegionName());
+
+ rem = timeout - (System.currentTimeMillis() - start);
+ blockUntilRegionIsOpenedByRS(rem, daughterA.getRegionName());
+
+ rem = timeout - (System.currentTimeMillis() - start);
+ blockUntilRegionIsOpenedByRS(rem, daughterB.getRegionName());
+ }
+ }
+
+ Result getRegionRow(byte[] regionName) throws IOException {
+ Get get = new Get(regionName);
+ return metaTable.get(get);
+ }
+
+ void blockUntilRegionIsInMeta(long timeout, byte[] regionName)
+ throws IOException, InterruptedException {
+ log("blocking until region is in META: " + Bytes.toStringBinary(regionName));
+ long start = System.currentTimeMillis();
+ while (System.currentTimeMillis() - start < timeout) {
+ Result result = getRegionRow(regionName);
+ if (result != null) {
+ HRegionInfo info = MetaEditor.getHRegionInfo(result);
+ if (info != null && !info.isOffline()) {
+ log("found region in META: " + Bytes.toStringBinary(regionName));
+ break;
+ }
+ }
+ sleep(10);
+ }
+ }
+
+ void blockUntilRegionIsOpenedByRS(long timeout, byte[] regionName)
+ throws IOException, InterruptedException {
+ log("blocking until region is opened by region server: " + Bytes.toStringBinary(regionName));
+ long start = System.currentTimeMillis();
+ while (System.currentTimeMillis() - start < timeout) {
+ List<HRegion> regions = rs.getOnlineRegions(tableName);
+ for (HRegion region : regions) {
+ if (Bytes.compareTo(region.getRegionName(), regionName) == 0) {
+ log("found region open in RS: " + Bytes.toStringBinary(regionName));
+ return;
+ }
+ }
+ sleep(10);
+ }
+ }
+
+ }
+
+ /**
+ * Checks regions using MetaScanner, MetaReader and HTable methods
+ */
+ static class RegionChecker extends Chore {
+ Configuration conf;
+ byte[] tableName;
+ Throwable ex;
+
+ RegionChecker(Configuration conf, Stoppable stopper, byte[] tableName) {
+ super("RegionChecker", 10, stopper);
+ this.conf = conf;
+ this.tableName = tableName;
+ this.setDaemon(true);
+ }
+
+ /** verify region boundaries obtained from MetaScanner */
+ void verifyRegionsUsingMetaScanner() throws Exception {
+
+ //MetaScanner.allTableRegions()
+ NavigableMap<HRegionInfo, ServerName> regions = MetaScanner.allTableRegions(conf, tableName,
+ false);
+ verifyTableRegions(regions.keySet());
+
+ //MetaScanner.listAllRegions()
+ List<HRegionInfo> regionList = MetaScanner.listAllRegions(conf, false);
+ verifyTableRegions(Sets.newTreeSet(regionList));
+ }
+
+ /** verify region boundaries obtained from HTable.getStartEndKeys() */
+ void verifyRegionsUsingHTable() throws IOException {
+ HTable table = null;
+ try {
+ //HTable.getStartEndKeys()
+ table = new HTable(conf, tableName);
+ Pair<byte[][], byte[][]> keys = table.getStartEndKeys();
+ verifyStartEndKeys(keys);
+
+ //HTable.getRegionsInfo()
+ Map<HRegionInfo, HServerAddress> regions = table.getRegionsInfo();
+ verifyTableRegions(regions.keySet());
+ } finally {
+ IOUtils.closeQuietly(table);
+ }
+ }
+
+ void verify() throws Exception {
+ verifyRegionsUsingMetaScanner();
+ verifyRegionsUsingHTable();
+ }
+
+ void verifyTableRegions(Set<HRegionInfo> regions) {
+ log("Verifying " + regions.size() + " regions");
+
+ byte[][] startKeys = new byte[regions.size()][];
+ byte[][] endKeys = new byte[regions.size()][];
+
+ int i=0;
+ for (HRegionInfo region : regions) {
+ startKeys[i] = region.getStartKey();
+ endKeys[i] = region.getEndKey();
+ i++;
+ }
+
+ Pair<byte[][], byte[][]> keys = new Pair<byte[][], byte[][]>(startKeys, endKeys);
+ verifyStartEndKeys(keys);
+ }
+
+ void verifyStartEndKeys(Pair<byte[][], byte[][]> keys) {
+ byte[][] startKeys = keys.getFirst();
+ byte[][] endKeys = keys.getSecond();
+ assertEquals(startKeys.length, endKeys.length);
+ assertTrue("Found 0 regions for the table", startKeys.length > 0);
+
+ assertArrayEquals("Start key for the first region is not byte[0]",
+ HConstants.EMPTY_START_ROW, startKeys[0]);
+ byte[] prevEndKey = HConstants.EMPTY_START_ROW;
+
+ // ensure that we do not have any gaps
+ for (int i=0; i<startKeys.length; i++) {
+ assertArrayEquals(
+ "Hole in .META. is detected. prevEndKey=" + Bytes.toStringBinary(prevEndKey)
+ + " ,regionStartKey=" + Bytes.toStringBinary(startKeys[i]), prevEndKey,
+ startKeys[i]);
+ prevEndKey = endKeys[i];
+ }
+ assertArrayEquals("End key for the last region is not byte[0]", HConstants.EMPTY_END_ROW,
+ endKeys[endKeys.length - 1]);
+ }
+
+ @Override
+ protected void chore() {
+ try {
+ verify();
+ } catch (Throwable ex) {
+ this.ex = ex;
+ stopper.stop("caught exception");
+ }
+ }
+ }
+
+ public static void log(String msg) {
+ LOG.info(msg);
+ }
}