Skip to content

Commit c6298c7

Browse files
2005hithljcarp84
authored andcommitted
HBASE-27091 Speed up the loading of table descriptor from filesystem (#4493)
Signed-off-by: Huaxiang Sun <huaxiangsun@apache.org> Signed-off-by: Yu Li <liyu@apache.org>
1 parent 86b7b02 commit c6298c7

6 files changed

Lines changed: 124 additions & 12 deletions

File tree

hbase-server/src/main/java/org/apache/hadoop/hbase/HBaseServerBase.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,10 @@ protected final void initializeFileSystem() throws IOException {
231231
// init the filesystem
232232
this.dataFs = new HFileSystem(this.conf, useHBaseChecksum);
233233
this.dataRootDir = CommonFSUtils.getRootDir(this.conf);
234+
int tableDescriptorParallelLoadThreads =
235+
conf.getInt("hbase.tabledescriptor.parallel.load.threads", 0);
234236
this.tableDescriptors = new FSTableDescriptors(this.dataFs, this.dataRootDir,
235-
!canUpdateTableDescriptor(), cacheTableDescriptor());
237+
!canUpdateTableDescriptor(), cacheTableDescriptor(), tableDescriptorParallelLoadThreads);
236238
}
237239

238240
public HBaseServerBase(Configuration conf, String name) throws IOException {
@@ -466,6 +468,17 @@ protected final void closeZooKeeper() {
466468
}
467469
}
468470

471+
protected final void closeTableDescriptors() {
472+
if (this.tableDescriptors != null) {
473+
LOG.info("Close table descriptors");
474+
try {
475+
this.tableDescriptors.close();
476+
} catch (IOException e) {
477+
LOG.debug("Failed to close table descriptors gracefully", e);
478+
}
479+
}
480+
}
481+
469482
/**
470483
* In order to register ShutdownHook, this method is called when HMaster and HRegionServer are
471484
* started. For details, please refer to HBASE-26951

hbase-server/src/main/java/org/apache/hadoop/hbase/TableDescriptors.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
package org.apache.hadoop.hbase;
1919

20+
import java.io.Closeable;
2021
import java.io.IOException;
2122
import java.util.Map;
2223
import org.apache.hadoop.hbase.client.TableDescriptor;
@@ -26,7 +27,7 @@
2627
* Get, remove and modify table descriptors.
2728
*/
2829
@InterfaceAudience.Private
29-
public interface TableDescriptors {
30+
public interface TableDescriptors extends Closeable {
3031

3132
/**
3233
* Test whether a given table exists, i.e, has a table descriptor.
@@ -35,6 +36,11 @@ default boolean exists(TableName tableName) throws IOException {
3536
return get(tableName) != null;
3637
}
3738

39+
@Override
40+
default void close() throws IOException {
41+
// do nothing by default
42+
}
43+
3844
/**
3945
* @return TableDescriptor for tablename
4046
*/

hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,7 @@ public void run() {
601601
this.rpcServices.stop();
602602
}
603603
closeZooKeeper();
604+
closeTableDescriptors();
604605
span.setStatus(StatusCode.OK);
605606
} finally {
606607
span.end();

hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,7 @@ public void run() {
977977
ZNodeClearer.deleteMyEphemeralNodeOnDisk();
978978

979979
closeZooKeeper();
980+
closeTableDescriptors();
980981
LOG.info("Exiting; stopping=" + this.serverName + "; zookeeper connection closed.");
981982
span.setStatus(StatusCode.OK);
982983
} finally {

hbase-server/src/main/java/org/apache/hadoop/hbase/util/FSTableDescriptors.java

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,20 @@
2121
import edu.umd.cs.findbugs.annotations.Nullable;
2222
import java.io.EOFException;
2323
import java.io.IOException;
24+
import java.io.InterruptedIOException;
2425
import java.util.Arrays;
2526
import java.util.Comparator;
2627
import java.util.List;
2728
import java.util.Map;
2829
import java.util.Optional;
2930
import java.util.TreeMap;
3031
import java.util.concurrent.ConcurrentHashMap;
32+
import java.util.concurrent.ConcurrentSkipListMap;
33+
import java.util.concurrent.CountDownLatch;
34+
import java.util.concurrent.LinkedBlockingQueue;
35+
import java.util.concurrent.ThreadPoolExecutor;
36+
import java.util.concurrent.TimeUnit;
37+
import java.util.concurrent.atomic.AtomicBoolean;
3138
import org.apache.commons.lang3.NotImplementedException;
3239
import org.apache.hadoop.conf.Configuration;
3340
import org.apache.hadoop.fs.FSDataInputStream;
@@ -55,6 +62,7 @@
5562
import org.slf4j.LoggerFactory;
5663

5764
import org.apache.hbase.thirdparty.com.google.common.primitives.Ints;
65+
import org.apache.hbase.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
5866

5967
/**
6068
* Implementation of {@link TableDescriptors} that reads descriptors from the passed filesystem. It
@@ -79,6 +87,8 @@ public class FSTableDescriptors implements TableDescriptors {
7987
private final boolean fsreadonly;
8088
private final boolean usecache;
8189
private volatile boolean fsvisited;
90+
private boolean tableDescriptorParallelLoadEnable = false;
91+
private ThreadPoolExecutor executor;
8292

8393
long cachehits = 0;
8494
long invocations = 0;
@@ -108,10 +118,23 @@ public FSTableDescriptors(final FileSystem fs, final Path rootdir) {
108118

109119
public FSTableDescriptors(final FileSystem fs, final Path rootdir, final boolean fsreadonly,
110120
final boolean usecache) {
121+
this(fs, rootdir, fsreadonly, usecache, 0);
122+
}
123+
124+
public FSTableDescriptors(final FileSystem fs, final Path rootdir, final boolean fsreadonly,
125+
final boolean usecache, final int tableDescriptorParallelLoadThreads) {
111126
this.fs = fs;
112127
this.rootdir = rootdir;
113128
this.fsreadonly = fsreadonly;
114129
this.usecache = usecache;
130+
if (tableDescriptorParallelLoadThreads > 0) {
131+
tableDescriptorParallelLoadEnable = true;
132+
executor = new ThreadPoolExecutor(tableDescriptorParallelLoadThreads,
133+
tableDescriptorParallelLoadThreads, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>(),
134+
new ThreadFactoryBuilder().setNameFormat("FSTableDescriptorLoad-pool-%d").setDaemon(true)
135+
.setUncaughtExceptionHandler(Threads.LOGGING_EXCEPTION_HANDLER).build());
136+
executor.allowCoreThreadTimeOut(true);
137+
}
115138
}
116139

117140
public static void tryUpdateMetaTableDescriptor(Configuration conf) throws IOException {
@@ -235,27 +258,56 @@ public TableDescriptor get(TableName tableName) {
235258
*/
236259
@Override
237260
public Map<String, TableDescriptor> getAll() throws IOException {
238-
Map<String, TableDescriptor> tds = new TreeMap<>();
261+
Map<String, TableDescriptor> tds = new ConcurrentSkipListMap<>();
239262
if (fsvisited) {
240263
for (Map.Entry<TableName, TableDescriptor> entry : this.cache.entrySet()) {
241264
tds.put(entry.getKey().getNameWithNamespaceInclAsString(), entry.getValue());
242265
}
243266
} else {
244-
LOG.trace("Fetching table descriptors from the filesystem.");
245-
boolean allvisited = usecache;
246-
for (Path d : FSUtils.getTableDirs(fs, rootdir)) {
247-
TableDescriptor htd = get(CommonFSUtils.getTableName(d));
248-
if (htd == null) {
249-
allvisited = false;
250-
} else {
251-
tds.put(htd.getTableName().getNameWithNamespaceInclAsString(), htd);
267+
LOG.info("Fetching table descriptors from the filesystem.");
268+
final long startTime = EnvironmentEdgeManager.currentTime();
269+
AtomicBoolean allvisited = new AtomicBoolean(usecache);
270+
List<Path> tableDirs = FSUtils.getTableDirs(fs, rootdir);
271+
if (!tableDescriptorParallelLoadEnable) {
272+
for (Path dir : tableDirs) {
273+
internalGet(dir, tds, allvisited);
274+
}
275+
} else {
276+
CountDownLatch latch = new CountDownLatch(tableDirs.size());
277+
for (Path dir : tableDirs) {
278+
executor.submit(new Runnable() {
279+
@Override
280+
public void run() {
281+
try {
282+
internalGet(dir, tds, allvisited);
283+
} finally {
284+
latch.countDown();
285+
}
286+
}
287+
});
288+
}
289+
try {
290+
latch.await();
291+
} catch (InterruptedException ie) {
292+
throw (InterruptedIOException) new InterruptedIOException().initCause(ie);
252293
}
253294
}
254-
fsvisited = allvisited;
295+
fsvisited = allvisited.get();
296+
LOG.info("Fetched table descriptors(size=" + tds.size() + ") cost "
297+
+ (EnvironmentEdgeManager.currentTime() - startTime) + "ms.");
255298
}
256299
return tds;
257300
}
258301

302+
private void internalGet(Path dir, Map<String, TableDescriptor> tds, AtomicBoolean allvisited) {
303+
TableDescriptor htd = get(CommonFSUtils.getTableName(dir));
304+
if (htd == null) {
305+
allvisited.set(false);
306+
} else {
307+
tds.put(htd.getTableName().getNameWithNamespaceInclAsString(), htd);
308+
}
309+
}
310+
259311
/**
260312
* Find descriptors by namespace.
261313
* @see #get(org.apache.hadoop.hbase.TableName)
@@ -379,6 +431,14 @@ private static String formatTableInfoSequenceId(final int number) {
379431
return Bytes.toString(b);
380432
}
381433

434+
@Override
435+
public void close() throws IOException {
436+
// Close the executor when parallel loading enabled.
437+
if (tableDescriptorParallelLoadEnable) {
438+
this.executor.shutdown();
439+
}
440+
}
441+
382442
static final class SequenceIdAndFileLength {
383443

384444
final int sequenceId;

hbase-server/src/test/java/org/apache/hadoop/hbase/util/TestFSTableDescriptors.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,32 @@ public void testGetAll() throws IOException, InterruptedException {
285285
+ htds.getAll().size(), count + 1, htds.getAll().size());
286286
}
287287

288+
@Test
289+
public void testParallelGetAll() throws IOException, InterruptedException {
290+
final String name = "testParallelGetAll";
291+
FileSystem fs = FileSystem.get(UTIL.getConfiguration());
292+
// Enable parallel load table descriptor.
293+
FSTableDescriptors htds = new FSTableDescriptorsTest(fs, testDir, true, 20);
294+
final int count = 100;
295+
// Write out table infos.
296+
for (int i = 0; i < count; i++) {
297+
htds.createTableDescriptor(
298+
TableDescriptorBuilder.newBuilder(TableName.valueOf(name + i)).build());
299+
}
300+
// add hbase:meta
301+
htds
302+
.createTableDescriptor(TableDescriptorBuilder.newBuilder(TableName.META_TABLE_NAME).build());
303+
304+
int getTableDescriptorSize = htds.getAll().size();
305+
assertEquals("getAll() didn't return all TableDescriptors, expected: " + (count + 1) + " got: "
306+
+ getTableDescriptorSize, count + 1, getTableDescriptorSize);
307+
308+
// get again to check whether the cache works well
309+
getTableDescriptorSize = htds.getAll().size();
310+
assertEquals("getAll() didn't return all TableDescriptors with cache, expected: " + (count + 1)
311+
+ " got: " + getTableDescriptorSize, count + 1, getTableDescriptorSize);
312+
}
313+
288314
@Test
289315
public void testGetAllOrdering() throws Exception {
290316
FileSystem fs = FileSystem.get(UTIL.getConfiguration());
@@ -467,6 +493,11 @@ public FSTableDescriptorsTest(FileSystem fs, Path rootdir, boolean usecache) {
467493
super(fs, rootdir, false, usecache);
468494
}
469495

496+
public FSTableDescriptorsTest(FileSystem fs, Path rootdir, boolean usecache,
497+
int tableDescriptorParallelLoadThreads) {
498+
super(fs, rootdir, false, usecache, tableDescriptorParallelLoadThreads);
499+
}
500+
470501
@Override
471502
public TableDescriptor get(TableName tablename) {
472503
LOG.info((super.isUsecache() ? "Cached" : "Non-Cached") + " TableDescriptor.get() on "

0 commit comments

Comments
 (0)