diff --git hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java index 5eddd7f..fd45a47 100644 --- hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java +++ hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java @@ -153,6 +153,13 @@ public final class HConstants { /** by default every master is a possible primary master unless the conf explicitly overrides it */ public static final boolean DEFAULT_MASTER_TYPE_BACKUP = false; + /** Preload table names and descriptors at startup */ + public static final String MASTER_PRELOAD_TABLEDESCRIPTORS = + "hbase.master.preload.tabledescriptors"; + + public static final boolean DEFAULT_MASTER_PRELOAD_TABLEDESCRIPTORS = + true; + /** Name of ZooKeeper quorum configuration parameter. */ public static final String ZOOKEEPER_QUORUM = "hbase.zookeeper.quorum"; diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java index af5e297..d0284dd 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -360,6 +360,7 @@ MasterServices, Server { private final ServerName serverName; private TableDescriptors tableDescriptors; + private final boolean preLoadTableDescriptors; // Table level lock manager for schema changes private TableLockManager tableLockManager; @@ -484,6 +485,10 @@ MasterServices, Server { this.metricsMaster = new MetricsMaster( new MetricsMasterWrapperImpl(this)); + // preload table descriptor at startup + this.preLoadTableDescriptors = conf.getBoolean(HConstants.MASTER_PRELOAD_TABLEDESCRIPTORS, + HConstants.DEFAULT_MASTER_PRELOAD_TABLEDESCRIPTORS); + // Health checker thread. int sleepTime = this.conf.getInt(HConstants.HEALTH_CHORE_WAKE_FREQ, HConstants.DEFAULT_THREAD_WAKE_FREQUENCY); @@ -803,6 +808,12 @@ MasterServices, Server { new FSTableDescriptors(this.fileSystemManager.getFileSystem(), this.fileSystemManager.getRootDir()); + // warm-up HTDs cache on master initialization + if (preLoadTableDescriptors) { + status.setStatus("Pre-loading table descriptors"); + this.tableDescriptors.getAll(); + } + // publish cluster ID status.setStatus("Publishing Cluster ID in ZooKeeper"); ZKClusterId.setClusterId(this.zooKeeper, fileSystemManager.getClusterId()); diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/CreateTableHandler.java hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/CreateTableHandler.java index 16d5297..bb9e4ec 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/CreateTableHandler.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/CreateTableHandler.java @@ -263,6 +263,9 @@ public class CreateTableHandler extends EventHandler { throw new IOException("Unable to ensure that " + tableName + " will be" + " enabled because of a ZooKeeper issue", e); } + + // 7. Update the tabledescriptor cache. + ((HMaster) this.server).getTableDescriptors().get(tableName); } private void releaseTableLock() { diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/util/FSTableDescriptors.java hbase-server/src/main/java/org/apache/hadoop/hbase/util/FSTableDescriptors.java index e335752..33537a3 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/util/FSTableDescriptors.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/util/FSTableDescriptors.java @@ -49,7 +49,6 @@ import org.apache.hadoop.hbase.protobuf.ProtobufUtil; import com.google.common.annotations.VisibleForTesting; import com.google.common.primitives.Ints; - /** * Implementation of {@link TableDescriptors} that reads descriptors from the * passed filesystem. It expects descriptors to be in a file in the @@ -74,6 +73,7 @@ public class FSTableDescriptors implements TableDescriptors { private final FileSystem fs; private final Path rootdir; private final boolean fsreadonly; + @VisibleForTesting long cachehits = 0; @VisibleForTesting long invocations = 0; @@ -100,6 +100,11 @@ public class FSTableDescriptors implements TableDescriptors { this.modtime = modtime; } + TableDescriptorAndModtime(final HTableDescriptor htd) { + this.htd = htd; + this.modtime = EnvironmentEdgeManager.currentTimeMillis(); + } + long getModtime() { return this.modtime; } @@ -117,7 +122,7 @@ public class FSTableDescriptors implements TableDescriptors { public FSTableDescriptors(final Configuration conf) throws IOException { this(FSUtils.getCurrentFileSystem(conf), FSUtils.getRootDir(conf)); } - + public FSTableDescriptors(final FileSystem fs, final Path rootdir) { this(fs, rootdir, false); } @@ -127,7 +132,7 @@ public class FSTableDescriptors implements TableDescriptors { * operations; i.e. on remove, we do not do delete in fs. */ public FSTableDescriptors(final FileSystem fs, - final Path rootdir, final boolean fsreadonly) { + final Path rootdir, final boolean fsreadonly) { super(); this.fs = fs; this.rootdir = rootdir; @@ -151,37 +156,31 @@ public class FSTableDescriptors implements TableDescriptors { // hbase:meta is already handled. If some one tries to get the descriptor for // .logs, .oldlogs or .corrupt throw an exception. if (HConstants.HBASE_NON_USER_TABLE_DIRS.contains(tablename.getNameAsString())) { - throw new IOException("No descriptor found for non table = " + tablename); + throw new IOException("No descriptor found for non table = " + tablename); } // Look in cache of descriptors. TableDescriptorAndModtime cachedtdm = this.cache.get(tablename); - if (cachedtdm != null) { - // Check mod time has not changed (this is trip to NN). - if (getTableInfoModtime(tablename) <= cachedtdm.getModtime()) { - cachehits++; - return cachedtdm.getTableDescriptor(); - } + cachehits++; + return cachedtdm.getTableDescriptor(); } - TableDescriptorAndModtime tdmt = null; try { tdmt = getTableDescriptorAndModtime(tablename); } catch (NullPointerException e) { LOG.debug("Exception during readTableDecriptor. Current table name = " - + tablename, e); + + tablename, e); } catch (IOException ioe) { LOG.debug("Exception during readTableDecriptor. Current table name = " - + tablename, ioe); + + tablename, ioe); } - + if (tdmt != null) { this.cache.put(tablename, tdmt); } return tdmt == null ? null : tdmt.getTableDescriptor(); } - /** * Returns a map from table name to table descriptor for all tables. */ @@ -189,19 +188,33 @@ public class FSTableDescriptors implements TableDescriptors { public Map getAll() throws IOException { Map htds = new TreeMap(); - List tableDirs = FSUtils.getTableDirs(fs, rootdir); - for (Path d: tableDirs) { - HTableDescriptor htd = null; - try { - htd = get(FSUtils.getTableName(d)); - } catch (FileNotFoundException fnfe) { - // inability of retrieving one HTD shouldn't stop getting the remaining - LOG.warn("Trouble retrieving htd", fnfe); + + // if no entries then get all tables + if (this.cache.keySet().size() == 0) { + if (LOG.isDebugEnabled()) { + LOG.debug("table cache is empty!"); } - if (htd == null) continue; - htds.put(htd.getTableName().getNameAsString(), htd); + for (Path d : FSUtils.getTableDirs(fs, rootdir)) { + HTableDescriptor htd = null; + try { + htd = get(FSUtils.getTableName(d)); + } + catch (FileNotFoundException fnfe) { + // inability of retrieving one HTD shouldn't stop getting the remaining + LOG.warn("Trouble retrieving htd", fnfe); + } + if (htd == null) continue; + htds.put(htd.getTableName().getNameAsString(), htd); + } + return htds; + } else { + for (TableName t : this.cache.keySet()) { + HTableDescriptor htd = null; + htd = get(t); + htds.put(htd.getTableName().getNameAsString(), htd); + } + return htds; } - return htds; } /* (non-Javadoc) @@ -244,7 +257,8 @@ public class FSTableDescriptors implements TableDescriptors { "Cannot add a table descriptor for a reserved subdirectory name: " + htd.getNameAsString()); } updateTableDescriptor(htd); - long modtime = getTableInfoModtime(htd.getTableName()); + // we are adding a new HTD, no need to go the file system to obtain the modification time + long modtime = EnvironmentEdgeManager.currentTimeMillis(); this.cache.put(htd.getTableName(), new TableDescriptorAndModtime(modtime, htd)); } @@ -437,7 +451,6 @@ public class FSTableDescriptors implements TableDescriptors { } /** - * @param tabledir * @param sequenceid * @return Name of tableinfo file. */ @@ -446,8 +459,6 @@ public class FSTableDescriptors implements TableDescriptors { } /** - * @param fs - * @param rootdir * @param tableName * @return Modification time for the table {@link #TABLEINFO_FILE_PREFIX} file * or 0 if no tableinfo file found. @@ -539,7 +550,7 @@ public class FSTableDescriptors implements TableDescriptors { } /** - * Update table descriptor on the file system + * Update table descriptor on the file system and cache * @throws IOException Thrown if failed update. * @throws NotImplementedException if in read only mode */ @@ -550,6 +561,7 @@ public class FSTableDescriptors implements TableDescriptors { } Path tableDir = getTableDir(htd.getTableName()); Path p = writeTableDescriptor(fs, htd, tableDir, getTableInfoPath(tableDir)); + this.cache.put(htd.getTableName(), new TableDescriptorAndModtime(htd)); if (p == null) throw new IOException("Failed update"); LOG.info("Updated tableinfo=" + p); return p; @@ -600,7 +612,7 @@ public class FSTableDescriptors implements TableDescriptors { * * @return Descriptor file or null if we failed write. */ - private static Path writeTableDescriptor(final FileSystem fs, + private static Path writeTableDescriptor(final FileSystem fs, final HTableDescriptor htd, final Path tableDir, final FileStatus currentDescriptorFile) throws IOException { @@ -719,8 +731,19 @@ public class FSTableDescriptors implements TableDescriptors { } } Path p = writeTableDescriptor(fs, htd, tableDir, status); - return p != null; + if (p != null) { + // if new table descriptor was succesful the add new entry to cache + this.cache.put(htd.getTableName(), new TableDescriptorAndModtime(htd)); + return true; + } + return false; + } + + /** + * Returns number of elements cached by FSTableDescriptor + */ + public int getCacheSize() { + return this.cache.size(); } - }