diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/HFileLink.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/HFileLink.java
index e2c80b24d32..74836ce39c6 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/HFileLink.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/HFileLink.java
@@ -79,8 +79,7 @@ public class HFileLink extends FileLink {
RegionInfoBuilder.ENCODED_REGION_NAME_REGEX, StoreFileInfo.HFILE_NAME_REGEX);
/** Define the HFile Link name parser in the form of: table=region-hfile */
- //made package private for testing
- static final Pattern LINK_NAME_PATTERN =
+ public static final Pattern LINK_NAME_PATTERN =
Pattern.compile(String.format("^(?:(%s)(?:\\=))?(%s)=(%s)-(%s)$",
TableName.VALID_NAMESPACE_REGEX, TableName.VALID_TABLE_QUALIFIER_REGEX,
RegionInfoBuilder.ENCODED_REGION_NAME_REGEX, StoreFileInfo.HFILE_NAME_REGEX));
@@ -400,15 +399,40 @@ public class HFileLink extends FileLink {
String tableName = CommonFSUtils.getTableName(dstFamilyPath.getParent().getParent())
.getNameAsString();
+ return create(conf, fs, dstFamilyPath, familyName, tableName, regionName, linkedTable,
+ linkedRegion, hfileName, createBackRef);
+ }
+
+ /**
+ * Create a new HFileLink
+ *
+ *
It also adds a back-reference to the hfile back-reference directory
+ * to simplify the reference-count and the cleaning process.
+ * @param conf {@link Configuration} to read for the archive directory name
+ * @param fs {@link FileSystem} on which to write the HFileLink
+ * @param dstFamilyPath - Destination path (table/region/cf/)
+ * @param dstTableName - Destination table name
+ * @param dstRegionName - Destination region name
+ * @param linkedTable - Linked Table Name
+ * @param linkedRegion - Linked Region Name
+ * @param hfileName - Linked HFile name
+ * @param createBackRef - Whether back reference should be created. Defaults to true.
+ * @return true if the file is created, otherwise the file exists.
+ * @throws IOException on file or parent directory creation failure
+ */
+ public static boolean create(final Configuration conf, final FileSystem fs,
+ final Path dstFamilyPath, final String familyName, final String dstTableName,
+ final String dstRegionName, final TableName linkedTable, final String linkedRegion,
+ final String hfileName, final boolean createBackRef) throws IOException {
String name = createHFileLinkName(linkedTable, linkedRegion, hfileName);
- String refName = createBackReferenceName(tableName, regionName);
+ String refName = createBackReferenceName(dstTableName, dstRegionName);
// Make sure the destination directory exists
fs.mkdirs(dstFamilyPath);
// Make sure the FileLink reference directory exists
Path archiveStoreDir = HFileArchiveUtil.getStoreArchivePath(conf,
- linkedTable, linkedRegion, familyName);
+ linkedTable, linkedRegion, familyName);
Path backRefPath = null;
if (createBackRef) {
Path backRefssDir = getBackReferencesDir(archiveStoreDir, hfileName);
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java
index 26d0a4b4eb3..fbd87290d8c 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java
@@ -33,6 +33,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
+
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
@@ -142,13 +143,14 @@ public class SplitTableRegionProcedure
.setSplit(false)
.setRegionId(rid)
.build();
- if(tableDescriptor.getRegionSplitPolicyClassName() != null) {
+
+ if (tableDescriptor.getRegionSplitPolicyClassName() != null) {
// Since we don't have region reference here, creating the split policy instance without it.
// This can be used to invoke methods which don't require Region reference. This instantiation
// of a class on Master-side though it only makes sense on the RegionServer-side is
// for Phoenix Local Indexing. Refer HBASE-12583 for more information.
Class extends RegionSplitPolicy> clazz =
- RegionSplitPolicy.getSplitPolicyClass(tableDescriptor, conf);
+ RegionSplitPolicy.getSplitPolicyClass(tableDescriptor, conf);
this.splitPolicy = ReflectionUtils.newInstance(clazz, conf);
}
}
@@ -624,16 +626,16 @@ public class SplitTableRegionProcedure
Pair expectedReferences = splitStoreFiles(env, regionFs);
- assertReferenceFileCount(fs, expectedReferences.getFirst(),
+ assertSplitResultFilesCount(fs, expectedReferences.getFirst(),
regionFs.getSplitsDir(daughterOneRI));
regionFs.commitDaughterRegion(daughterOneRI);
- assertReferenceFileCount(fs, expectedReferences.getFirst(),
+ assertSplitResultFilesCount(fs, expectedReferences.getFirst(),
new Path(tabledir, daughterOneRI.getEncodedName()));
- assertReferenceFileCount(fs, expectedReferences.getSecond(),
+ assertSplitResultFilesCount(fs, expectedReferences.getSecond(),
regionFs.getSplitsDir(daughterTwoRI));
regionFs.commitDaughterRegion(daughterTwoRI);
- assertReferenceFileCount(fs, expectedReferences.getSecond(),
+ assertSplitResultFilesCount(fs, expectedReferences.getSecond(),
new Path(tabledir, daughterTwoRI.getEncodedName()));
}
@@ -773,11 +775,15 @@ public class SplitTableRegionProcedure
return new Pair(daughterA, daughterB);
}
- private void assertReferenceFileCount(final FileSystem fs, final int expectedReferenceFileCount,
- final Path dir) throws IOException {
- if (expectedReferenceFileCount != 0 &&
- expectedReferenceFileCount != FSUtils.getRegionReferenceFileCount(fs, dir)) {
- throw new IOException("Failing split. Expected reference file count isn't equal.");
+ private void assertSplitResultFilesCount(final FileSystem fs,
+ final int expectedSplitResultFileCount, Path dir)
+ throws IOException {
+ if (expectedSplitResultFileCount != 0) {
+ int resultFileCount = FSUtils.getRegionReferenceAndLinkFileCount(fs, dir);
+ if (expectedSplitResultFileCount != resultFileCount) {
+ throw new IOException("Failing split. Didn't have expected reference and HFileLink files"
+ + ", expected=" + expectedSplitResultFileCount + ", actual=" + resultFileCount);
+ }
}
}
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
index 8afadc797dc..dd9720c75a9 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
@@ -1068,8 +1068,8 @@ public class HRegion implements HeapSize, PropagatingConfigurationObserver, Regi
}
}
- LOG.info("Opened {}; next sequenceid={}; {}, {}",
- this.getRegionInfo().getShortNameToLog(), nextSeqId, this.splitPolicy, this.flushPolicy);
+ LOG.info("Opened {}; next sequenceid={}; {}, {}", this.getRegionInfo().getShortNameToLog(),
+ nextSeqId, this.splitPolicy, this.flushPolicy);
// A region can be reopened if failed a split; reset flags
this.closing.set(false);
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionFileSystem.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionFileSystem.java
index 69d9c8ff37d..667eabfcd28 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionFileSystem.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionFileSystem.java
@@ -17,6 +17,7 @@
*/
package org.apache.hadoop.hbase.regionserver;
+import static org.apache.hadoop.hbase.io.HFileLink.LINK_NAME_PATTERN;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -27,6 +28,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
+import java.util.regex.Matcher;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
@@ -39,11 +41,13 @@ import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.PrivateCellUtil;
+import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.backup.HFileArchiver;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.fs.HFileSystem;
+import org.apache.hadoop.hbase.io.HFileLink;
import org.apache.hadoop.hbase.io.Reference;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.CommonFSUtils;
@@ -53,7 +57,6 @@ import org.apache.hadoop.hbase.util.ServerRegionReplicaUtil;
import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
/**
@@ -662,15 +665,17 @@ public class HRegionFileSystem {
LOG.warn("Found an already existing split file for {}. Assuming this is a recovery.", p);
return p;
}
+ boolean createLinkFile = false;
if (splitPolicy == null || !splitPolicy.skipStoreFileRangeCheck(familyName)) {
// Check whether the split row lies in the range of the store file
// If it is outside the range, return directly.
f.initReader();
try {
Cell splitKey = PrivateCellUtil.createFirstOnRow(splitRow);
+ Optional lastKey = f.getLastKey();
+ Optional firstKey = f.getFirstKey();
if (top) {
//check if larger than last key.
- Optional lastKey = f.getLastKey();
// If lastKey is null means storefile is empty.
if (!lastKey.isPresent()) {
return null;
@@ -678,9 +683,12 @@ public class HRegionFileSystem {
if (f.getComparator().compare(splitKey, lastKey.get()) > 0) {
return null;
}
+ if (firstKey.isPresent() && f.getComparator().compare(splitKey, firstKey.get()) <= 0) {
+ LOG.debug("Will create HFileLink file for {}, top=true", f.getPath());
+ createLinkFile = true;
+ }
} else {
//check if smaller than first key
- Optional firstKey = f.getFirstKey();
// If firstKey is null means storefile is empty.
if (!firstKey.isPresent()) {
return null;
@@ -688,11 +696,44 @@ public class HRegionFileSystem {
if (f.getComparator().compare(splitKey, firstKey.get()) < 0) {
return null;
}
+ if (lastKey.isPresent() && f.getComparator().compare(splitKey, lastKey.get()) >= 0) {
+ LOG.debug("Will create HFileLink file for {}, top=false", f.getPath());
+ createLinkFile = true;
+ }
}
} finally {
f.closeStoreFile(f.getCacheConf() != null ? f.getCacheConf().shouldEvictOnClose() : true);
}
}
+ if (createLinkFile) {
+ // create HFileLink file instead of Reference file for child
+ String hfileName = f.getPath().getName();
+ TableName linkedTable = regionInfoForFs.getTable();
+ String linkedRegion = regionInfoForFs.getEncodedName();
+ try {
+ if (HFileLink.isHFileLink(hfileName)) {
+ Matcher m = LINK_NAME_PATTERN.matcher(hfileName);
+ if (!m.matches()) {
+ throw new IllegalArgumentException(hfileName + " is not a valid HFileLink name!");
+ }
+ linkedTable = TableName.valueOf(m.group(1), m.group(2));
+ linkedRegion = m.group(3);
+ hfileName = m.group(4);
+ }
+ // must create back reference here
+ HFileLink.create(conf, fs, splitDir, familyName, hri.getTable().getNameAsString(),
+ hri.getEncodedName(), linkedTable, linkedRegion, hfileName, true);
+ Path path =
+ new Path(splitDir, HFileLink.createHFileLinkName(linkedTable, linkedRegion, hfileName));
+ LOG.info("Created linkFile:" + path.toString() + " for child: " + hri.getEncodedName()
+ + ", parent: " + regionInfoForFs.getEncodedName());
+ return path;
+ } catch (IOException e) {
+ // if create HFileLink file failed, then just skip the error and create Reference file
+ LOG.error("Create link file for " + hfileName + " for child " + hri.getEncodedName()
+ + "failed, will create Reference file", e);
+ }
+ }
// A reference to the bottom half of the hsf store file.
Reference r =
top ? Reference.createTopReference(splitRow): Reference.createBottomReference(splitRow);
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionSplitPolicy.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionSplitPolicy.java
index 4a13030b533..b59e0ac7083 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionSplitPolicy.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionSplitPolicy.java
@@ -20,7 +20,6 @@ package org.apache.hadoop.hbase.regionserver;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
-
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.HBaseInterfaceAudience;
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/util/FSUtils.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/util/FSUtils.java
index b32d4979c48..1156b1768bb 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/util/FSUtils.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/util/FSUtils.java
@@ -1056,28 +1056,65 @@ public final class FSUtils {
* @return List of paths to valid family directories in region dir.
* @throws IOException
*/
- public static List getFamilyDirs(final FileSystem fs, final Path regionDir) throws IOException {
+ public static List getFamilyDirs(final FileSystem fs, final Path regionDir)
+ throws IOException {
// assumes we are in a region dir.
- FileStatus[] fds = fs.listStatus(regionDir, new FamilyDirFilter(fs));
- List familyDirs = new ArrayList<>(fds.length);
- for (FileStatus fdfs: fds) {
- Path fdPath = fdfs.getPath();
- familyDirs.add(fdPath);
- }
- return familyDirs;
+ return getFilePaths(fs, regionDir, new FamilyDirFilter(fs));
}
- public static List getReferenceFilePaths(final FileSystem fs, final Path familyDir) throws IOException {
- List fds = listStatusWithStatusFilter(fs, familyDir, new ReferenceFileFilter(fs));
- if (fds == null) {
- return Collections.emptyList();
- }
- List referenceFiles = new ArrayList<>(fds.size());
+ public static List getReferenceFilePaths(final FileSystem fs, final Path familyDir)
+ throws IOException {
+ return getFilePaths(fs, familyDir, new ReferenceFileFilter(fs));
+ }
+
+ public static List getReferenceAndLinkFilePaths(final FileSystem fs, final Path familyDir)
+ throws IOException {
+ return getFilePaths(fs, familyDir, new ReferenceAndLinkFileFilter(fs));
+ }
+
+ private static List getFilePaths(final FileSystem fs, final Path dir,
+ final PathFilter pathFilter) throws IOException {
+ FileStatus[] fds = fs.listStatus(dir, pathFilter);
+ List files = new ArrayList<>(fds.length);
for (FileStatus fdfs: fds) {
Path fdPath = fdfs.getPath();
- referenceFiles.add(fdPath);
+ files.add(fdPath);
+ }
+ return files;
+ }
+
+ public static int getRegionReferenceAndLinkFileCount(final FileSystem fs, final Path p) {
+ int result = 0;
+ try {
+ for (Path familyDir : getFamilyDirs(fs, p)) {
+ result += getReferenceAndLinkFilePaths(fs, familyDir).size();
+ }
+ } catch (IOException e) {
+ LOG.warn("Error Counting reference files.", e);
+ }
+ return result;
+ }
+
+ public static class ReferenceAndLinkFileFilter implements PathFilter {
+
+ private final FileSystem fs;
+
+ public ReferenceAndLinkFileFilter(FileSystem fs) {
+ this.fs = fs;
+ }
+
+ @Override
+ public boolean accept(Path rd) {
+ try {
+ // only files can be references.
+ return !fs.getFileStatus(rd).isDirectory() && (StoreFileInfo.isReference(rd) ||
+ HFileLink.isHFileLink(rd));
+ } catch (IOException ioe) {
+ // Maybe the file was moved or the fs was disconnected.
+ LOG.warn("Skipping file " + rd +" due to IOException", ioe);
+ return false;
+ }
}
- return referenceFiles;
}
/**
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHStoreFile.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHStoreFile.java
index e6f0357204f..cdef341965b 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHStoreFile.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHStoreFile.java
@@ -132,14 +132,17 @@ public class TestHStoreFile {
/**
* Write a file and then assert that we can read from top and bottom halves using two
- * HalfMapFiles.
+ * HalfMapFiles, as well as one HalfMapFile and one HFileLink file.
*/
@Test
- public void testBasicHalfMapFile() throws Exception {
+ public void testBasicHalfAndHFileLinkMapFile() throws Exception {
final RegionInfo hri =
- RegionInfoBuilder.newBuilder(TableName.valueOf("testBasicHalfMapFileTb")).build();
+ RegionInfoBuilder.newBuilder(TableName.valueOf("testBasicHalfAndHFileLinkMapFile")).build();
+ // The locations of HFileLink refers hfiles only should be consistent with the table dir
+ // create by CommonFSUtils directory, so we should make the region directory under
+ // the mode of CommonFSUtils.getTableDir here.
HRegionFileSystem regionFs = HRegionFileSystem.createRegionOnFileSystem(conf, fs,
- new Path(testDir, hri.getTable().getNameAsString()), hri);
+ CommonFSUtils.getTableDir(CommonFSUtils.getRootDir(conf), hri.getTable()), hri);
HFileContext meta = new HFileContextBuilder().withBlockSize(2 * 1024).build();
StoreFileWriter writer = new StoreFileWriter.Builder(conf, cacheConf, this.fs)
@@ -395,6 +398,8 @@ public class TestHStoreFile {
f.initReader();
Cell midkey = f.getReader().midKey().get();
KeyValue midKV = (KeyValue) midkey;
+ // 1. test using the midRow as the splitKey, this test will generate two Reference files
+ // in the children
byte[] midRow = CellUtil.cloneRow(midKV);
// Create top split.
RegionInfo topHri =
@@ -455,7 +460,7 @@ public class TestHStoreFile {
regionFs.cleanupDaughterRegion(topHri);
regionFs.cleanupDaughterRegion(bottomHri);
- // Next test using a midkey that does not exist in the file.
+ // 2. test using a midkey which will generate one Reference file and one HFileLink file.
// First, do a key that is < than first key. Ensure splits behave
// properly.
byte[] badmidkey = Bytes.toBytes(" .");
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java
index d163a6d9738..652c019ff04 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java
@@ -17,6 +17,7 @@
*/
package org.apache.hadoop.hbase.regionserver;
+import static org.apache.hadoop.hbase.client.TableDescriptorBuilder.SPLIT_POLICY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -41,6 +42,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.CellComparator;
import org.apache.hadoop.hbase.Coprocessor;
import org.apache.hadoop.hbase.CoprocessorEnvironment;
import org.apache.hadoop.hbase.DoNotRetryIOException;
@@ -48,6 +50,7 @@ import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HBaseTestingUtil;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.MasterNotRunningException;
+import org.apache.hadoop.hbase.PrivateCellUtil;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.SingleProcessHBaseCluster;
import org.apache.hadoop.hbase.StartTestingClusterOption;
@@ -74,6 +77,7 @@ import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor;
import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
import org.apache.hadoop.hbase.coprocessor.MasterObserver;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
+import org.apache.hadoop.hbase.io.HFileLink;
import org.apache.hadoop.hbase.io.Reference;
import org.apache.hadoop.hbase.master.HMaster;
import org.apache.hadoop.hbase.master.MasterRpcServices;
@@ -138,13 +142,13 @@ public class TestSplitTransactionOnCluster {
private SingleProcessHBaseCluster cluster = null;
private static final int NB_SERVERS = 3;
- static final HBaseTestingUtil TESTING_UTIL =
- new HBaseTestingUtil();
+ static final HBaseTestingUtil TESTING_UTIL = new HBaseTestingUtil();
@Rule
public TestName name = new TestName();
- @BeforeClass public static void before() throws Exception {
+ @BeforeClass
+ public static void before() throws Exception {
TESTING_UTIL.getConfiguration().setInt(HConstants.HBASE_BALANCER_PERIOD, 60000);
StartTestingClusterOption option = StartTestingClusterOption.builder()
.masterClass(MyMaster.class).numRegionServers(NB_SERVERS).
@@ -152,11 +156,13 @@ public class TestSplitTransactionOnCluster {
TESTING_UTIL.startMiniCluster(option);
}
- @AfterClass public static void after() throws Exception {
+ @AfterClass
+ public static void after() throws Exception {
TESTING_UTIL.shutdownMiniCluster();
}
- @Before public void setup() throws IOException {
+ @Before
+ public void setup() throws IOException {
TESTING_UTIL.ensureSomeNonStoppedRegionServersAvailable(NB_SERVERS);
this.admin = TESTING_UTIL.getAdmin();
this.cluster = TESTING_UTIL.getMiniHBaseCluster();
@@ -359,6 +365,114 @@ public class TestSplitTransactionOnCluster {
admin.deleteTable(tableName);
}
+ @Test
+ public void testContinuousSplitUsingLinkFile() throws Exception {
+ final TableName tableName = TableName.valueOf(name.getMethodName());
+ // Create table then get the single region for our new table.
+ byte[] cf = Bytes.toBytes("cf");
+ TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName)
+ .setColumnFamily(ColumnFamilyDescriptorBuilder.of(cf));
+ String splitPolicy = ConstantSizeRegionSplitPolicy.class.getName();
+ builder.setValue(SPLIT_POLICY, splitPolicy);
+
+ admin.createTable(builder.build());
+ admin.compactionSwitch(false, new ArrayList<>());
+
+ assertNotEquals("Unable to retrieve regions of the table", -1,
+ TESTING_UTIL.waitFor(10000, () -> cluster.getRegions(tableName).size() == 1));
+ Table table = TESTING_UTIL.getConnection().getTable(tableName);
+ // insert data
+ insertData(tableName, admin, table, 10);
+ insertData(tableName, admin, table, 20);
+ insertData(tableName, admin, table, 40);
+ int rowCount = 3 * 4;
+ Scan scan = new Scan();
+ scanValidate(scan, rowCount, table);
+
+ // Split
+ admin.splitRegionAsync(cluster.getRegions(tableName).get(0).getRegionInfo().getRegionName(),
+ Bytes.toBytes("row14"));
+ // wait for the split to complete or get interrupted. If the split completes successfully,
+ // the procedure will return true; if the split fails, the procedure would throw exception.
+ Thread.sleep(3000);
+ assertNotEquals("Table is not split properly?", -1,
+ TESTING_UTIL.waitFor(3000, () -> cluster.getRegions(tableName).size() == 2));
+ // we have 2 daughter regions
+ HRegion hRegion1 = cluster.getRegions(tableName).get(0);
+ HRegion hRegion2 = cluster.getRegions(tableName).get(1);
+ HStore hStore1 = hRegion1.getStore(cf);
+ HStore hStore2 = hRegion2.getStore(cf);
+ // the sum of store files of the two children should be equal to their parent
+ assertEquals(3, hStore1.getStorefilesCount() + hStore2.getStorefilesCount());
+ // both the two children should have link files
+ for (StoreFile sf : hStore1.getStorefiles()) {
+ assertTrue(HFileLink.isHFileLink(sf.getPath()));
+ }
+ for (StoreFile sf : hStore2.getStorefiles()) {
+ assertTrue(HFileLink.isHFileLink(sf.getPath()));
+ }
+ // validate children data
+ scan = new Scan();
+ scanValidate(scan, rowCount, table);
+
+ //Continuous Split
+ findRegionToSplit(tableName, "row24");
+ Thread.sleep(3000);
+ assertNotEquals("Table is not split properly?", -1,
+ TESTING_UTIL.waitFor(3000, () -> cluster.getRegions(tableName).size() == 3));
+ // now table has 3 region, each region should have one link file
+ for (HRegion newRegion : cluster.getRegions(tableName)) {
+ assertEquals(1, newRegion.getStore(cf).getStorefilesCount());
+ assertTrue(
+ HFileLink.isHFileLink(newRegion.getStore(cf).getStorefiles().iterator().next().getPath()));
+ }
+
+ scan = new Scan();
+ scanValidate(scan, rowCount, table);
+
+ //Continuous Split, random split HFileLink, generate Reference files.
+ //After this, can not continuous split, because there are reference files.
+ findRegionToSplit(tableName, "row11");
+ Thread.sleep(3000);
+ assertNotEquals("Table is not split properly?", -1,
+ TESTING_UTIL.waitFor(3000, () -> cluster.getRegions(tableName).size() == 4));
+
+ scan = new Scan();
+ scanValidate(scan, rowCount, table);
+ }
+
+ private void findRegionToSplit(TableName tableName, String splitRowKey) throws Exception {
+ HRegion toSplit = null;
+ byte[] toSplitKey = Bytes.toBytes(splitRowKey);
+ for(HRegion rg : cluster.getRegions(tableName)) {
+ LOG.debug("startKey=" +
+ Bytes.toStringBinary(rg.getRegionInfo().getStartKey()) + ", getEndKey()=" +
+ Bytes.toStringBinary(rg.getRegionInfo().getEndKey()) + ", row=" + splitRowKey);
+ if((rg.getRegionInfo().getStartKey().length==0||
+ CellComparator.getInstance().compare(
+ PrivateCellUtil.createFirstOnRow(rg.getRegionInfo().getStartKey()),
+ PrivateCellUtil.createFirstOnRow(toSplitKey)) <= 0) &&(
+ rg.getRegionInfo().getEndKey().length==0||
+ CellComparator.getInstance().compare(
+ PrivateCellUtil.createFirstOnRow(rg.getRegionInfo().getEndKey()),
+ PrivateCellUtil.createFirstOnRow(toSplitKey)) >= 0)){
+ toSplit = rg;
+ }
+ }
+ assertNotNull(toSplit);
+ admin.splitRegionAsync(toSplit.getRegionInfo().getRegionName(), toSplitKey);
+ }
+
+ private static void scanValidate(Scan scan, int expectedRowCount, Table table) throws IOException{
+ ResultScanner scanner = table.getScanner(scan);
+ int rows = 0;
+ for (Result result : scanner) {
+ rows++;
+ }
+ scanner.close();
+ assertEquals(expectedRowCount, rows);
+ }
+
public static class FailingSplitMasterObserver implements MasterCoprocessor, MasterObserver {
volatile CountDownLatch latch;
| | | |