HBASE-29691: Change TableName.META_TABLE_NAME from being a global static#7730
HBASE-29691: Change TableName.META_TABLE_NAME from being a global static#7730Kota-SH wants to merge 1 commit intoapache:masterfrom
Conversation
Change TableName.META_TABLE_NAME from a global static constant to a dynamically discovered value from ConnectionRegistry. Key changes: - Add ConnectionRegistry.getMetaTableName() method for dynamic discovery - Add MetaTableNameStore for persisting meta table name in master region - Update TableName to support dynamic meta table name - Update HMaster to integrate with MetaTableNameStore - Update all client and server code to use dynamic meta table name - Add protobuf changes for meta table name in Registry.proto Refactoring for the test classes to be handled separately.
anmolnar
left a comment
There was a problem hiding this comment.
lgtm. What are the build failures?
| // TODO: How come Meta regions still do not have encoded region names? Fix. | ||
| // hbase:meta,,1.1588230740 should be the hbase:meta first region name. | ||
| // TODO: For now, hardcode to default. Future: lazy initialization based on config or make it use | ||
| // conenction |
There was a problem hiding this comment.
nit:
| // conenction | |
| // connection |
| @@ -787,8 +787,13 @@ public static CellComparator getCellComparator(TableName tableName) { | |||
| */ | |||
| public static CellComparator getCellComparator(byte[] tableName) { | |||
| // FYI, TableName.toBytes does not create an array; just returns existing array pointer. | |||
There was a problem hiding this comment.
nit: Is this comment still applicable?
| if (!createMetaEntriesFailures.isEmpty()) { | ||
| LOG.warn( | ||
| "Failed to create entries in hbase:meta for {}/{} RegionInfo descriptors. First" | ||
| "Failed to create entries in {}} for {}/{} RegionInfo descriptors. First" |
There was a problem hiding this comment.
nit
| "Failed to create entries in {}} for {}/{} RegionInfo descriptors. First" | |
| "Failed to create entries in {} for {}/{} RegionInfo descriptors. First" |
| if (!cfs.contains(family)) { | ||
| throw new HBaseIOException( | ||
| "Delete of hbase:meta column family " + Bytes.toString(family)); | ||
| "Delete of " + env.getMetaTableName() + " column family " + Bytes.toString(family)); |
There was a problem hiding this comment.
nit: IMO this could be a little more clear
| "Delete of " + env.getMetaTableName() + " column family " + Bytes.toString(family)); | |
| "Invalid deletion of " + env.getMetaTableName() + " column family: " + Bytes.toString(family)); |
| LOG.debug("Loaded meta table name from Master Local Region: {}", cachedMetaTableName); | ||
| return cachedMetaTableName; | ||
| } | ||
| LOG.info("No stored meta table name found in Master Local Region: {}", cachedMetaTableName); |
There was a problem hiding this comment.
nit
| LOG.info("No stored meta table name found in Master Local Region: {}", cachedMetaTableName); | |
| LOG.info("No stored meta table name found in Master Local Region: {}", cachedMetaTableName); |
| startEndKeys.get(idx + 1).getFirst()) == 0) | ||
| ) { | ||
| throw new IOException("The endkey of one region for table " + tableName | ||
| + " is not equal to the startkey of the next region in hbase:meta." |
| LOG.warn( | ||
| "Unable to close region " + hi.getRegionNameAsString() | ||
| + " because {} had invalid or missing " + HConstants.CATALOG_FAMILY_STR + ":" | ||
| + Bytes.toString(HConstants.REGIONINFO_QUALIFIER) + " qualifier value.", | ||
| connection.getMetaTableName()); |
There was a problem hiding this comment.
nit: I see a mixture of using string concatenation as well as {} in the LOG.warn() method. IMO is should consistently use the {} convention.
| LOG.info("Patching {} with .regioninfo: " + hbi.getHdfsHRI(), | ||
| connection.getMetaTableName()); |
There was a problem hiding this comment.
nit: Mixture of string concatenation and {} in log method
| LOG.info("Patching {} with with .regioninfo: " + hbi.getHdfsHRI(), | ||
| connection.getMetaTableName()); |
There was a problem hiding this comment.
nit: Mixture of string concatenation and {} in log method
| + hbi.getMetaEntry().regionServer + " but is multiply assigned to region servers " | ||
| + Joiner.on(", ").join(hbi.getDeployedOn())); | ||
| "Region " + descriptiveName + " is listed in " + connection.getMetaTableName() | ||
| + " on region server " + hbi.getMetaEntry().regionServer + " but is multiply assigned" |
There was a problem hiding this comment.
nit: Do you have any idea what they mean by "multiply assigned"? Like "multi-assigned"?
|
Just came back from vacation... |
There was a problem hiding this comment.
Pull request overview
This pull request implements HBASE-29691, which changes TableName.META_TABLE_NAME from a global static constant to a dynamically discovered value. The change enables support for cluster-specific meta table names, particularly for read replica clusters and other specialized configurations.
Changes:
- Adds
ConnectionRegistry.getMetaTableName()method for dynamic meta table name discovery - Introduces
MetaTableNameStoreclass to persist the meta table name in the Master Local Region - Updates
Connection,AsyncConnection, andMasterServicesinterfaces to exposegetMetaTableName()method - Modifies
TableName.isMetaTableName()to support both default "hbase:meta" and variant table names with suffixes - Updates all client and server code paths to use the dynamic meta table name instead of the static constant
- Adds new RPC endpoint
GetMetaTableNamein Registry.proto for retrieving the meta table name from the master
Reviewed changes
Copilot reviewed 98 out of 98 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| hbase-protocol-shaded/src/main/protobuf/server/Registry.proto | Adds GetMetaTableName RPC request/response messages |
| hbase-server/src/main/java/org/apache/hadoop/hbase/master/MetaTableNameStore.java | New class to store/retrieve meta table name from Master Local Region |
| hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java | Integrates MetaTableNameStore and overrides getMetaTableName() |
| hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java | Implements getMetaTableName() to fetch from master via connection |
| hbase-client/src/main/java/org/apache/hadoop/hbase/client/Connection.java | Adds getMetaTableName() and getMetaTable() helper methods |
| hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncConnection.java | Adds getMetaTableName() and getMetaTable() helper methods |
| hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionRegistry.java | Adds getMetaTableName() method for discovering meta table name |
| hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncConnectionImpl.java | Stores and exposes meta table name fetched from registry |
| hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionFactory.java | Fetches meta table name from registry during connection creation |
| hbase-common/src/main/java/org/apache/hadoop/hbase/TableName.java | Updates isMetaTableName() to support meta table name variants, deprecates META_TABLE_NAME |
| hbase-common/src/main/java/org/apache/hadoop/hbase/CellComparatorImpl.java | Updates comparator selection to support dynamic meta table names |
| hbase-server/src/main/java/org/apache/hadoop/hbase/HBaseServerBase.java | Provides default getMetaTableName() implementation |
| hbase-server/src/main/java/org/apache/hadoop/hbase/HBaseRpcServicesBase.java | Implements getMetaTableName() RPC handler |
| Multiple server-side utilities | Updates HBaseFsck, RegionMover, MetaTableAccessor, and other utilities to use dynamic meta table name |
| Multiple test files | Updates test infrastructure and mocks to support dynamic meta table name |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| LOG.info( | ||
| "The {} config is {} while the replica count in TableDescriptor is {}" | ||
| + " for hbase:meta, altering...", | ||
| HConstants.META_REPLICAS_NUM, replicasNumInConf, metaDesc.getRegionReplication()); | ||
| HConstants.META_REPLICAS_NUM, replicasNumInConf, metaDesc.getRegionReplication(), | ||
| getMetaTableName()); |
There was a problem hiding this comment.
The log format at line 1207 has incorrect parameter count. The format string has 3 placeholders ("The {} config is {} while the replica count in TableDescriptor is {} for hbase:meta, altering...") but 4 arguments are provided: HConstants.META_REPLICAS_NUM, replicasNumInConf, metaDesc.getRegionReplication(), and getMetaTableName(). This will cause a logging error. The fourth parameter getMetaTableName() should be part of the format string template, not an extra argument.
| } catch (IOException e) { | ||
| LOG.info("Exception loading/storing meta table name from master region"); | ||
| throw new RuntimeException(e); | ||
| } |
There was a problem hiding this comment.
The error message at line 1689 is missing important context. When logging an exception, it should describe what operation failed. The message "Exception loading/storing meta table name from master region" should be logged with the exception using LOG.error() or LOG.warn() with the exception as the second parameter, not LOG.info(). Also, rethrowing as RuntimeException loses the original exception message - consider using new RuntimeException("Exception loading/storing meta table name from master region", e) to preserve the exception chain.
| } | ||
|
|
||
| static boolean isMetaTable(byte[] tableName) { | ||
| return Bytes.startsWith(tableName, TableName.META_TABLE_NAME.getName()); |
There was a problem hiding this comment.
The implementation uses TableName.META_TABLE_NAME.getName() as a prefix check, but this could match unintended table names. For example, if someone creates a table "hbase:metadata", this check would incorrectly return true because it starts with "hbase:meta". The logic should verify exact match or use a more precise delimiter-based check. Consider checking if the bytes are exactly equal OR if they match the pattern with an underscore separator.
| return Bytes.startsWith(tableName, TableName.META_TABLE_NAME.getName()); | |
| byte[] metaName = TableName.META_TABLE_NAME.getName(); | |
| // Match exactly "hbase:meta" or names starting with "hbase:meta_" (e.g., "hbase:meta_1"). | |
| if (Bytes.equals(tableName, metaName)) { | |
| return true; | |
| } | |
| if (tableName == null || tableName.length <= metaName.length) { | |
| return false; | |
| } | |
| if (!Bytes.startsWith(tableName, metaName)) { | |
| return false; | |
| } | |
| // Require an underscore delimiter after "hbase:meta" to avoid matching names like "hbase:metadata". | |
| return tableName[metaName.length] == (byte) '_'; |
| // TODO: How come Meta regions still do not have encoded region names? Fix. | ||
| // hbase:meta,,1.1588230740 should be the hbase:meta first region name. | ||
| // TODO: For now, hardcode to default. Future: lazy initialization based on config or make it use | ||
| // conenction |
There was a problem hiding this comment.
The comment on line 46 has a typo: "conenction" should be "connection".
| // conenction | |
| // connection |
| private static TableName getMetaTableNameFromConf(Configuration conf) { | ||
| // TODO: Support replica-specific meta table names from masterRegion | ||
| return TableName.META_TABLE_NAME; |
There was a problem hiding this comment.
The TODO comment on line 147 mentions "Support replica-specific meta table names from masterRegion" but this contradicts the PR's design. The PR adds MetaTableNameStore which stores the meta table name in the master region. This method, however, is static and doesn't have access to the master region. The comment and implementation should be aligned - either this method should not exist (and callers should use the dynamic meta table name from the connection/master), or the TODO should explain how static methods will interact with the dynamic meta table name system.
| LOG.info("No stored meta table name found in Master Local Region: {}", cachedMetaTableName); | ||
| return cachedMetaTableName; |
There was a problem hiding this comment.
The log message at line 89 is misleading. When no stored meta table name is found, cachedMetaTableName is still null, but the log message says "No stored meta table name found in Master Local Region: {}", cachedMetaTableName. This will log null, which is confusing. The message should be clearer that no value was found and null will be returned.
| if (!createMetaEntriesFailures.isEmpty()) { | ||
| LOG.warn( | ||
| "Failed to create entries in hbase:meta for {}/{} RegionInfo descriptors. First" | ||
| "Failed to create entries in {}} for {}/{} RegionInfo descriptors. First" |
There was a problem hiding this comment.
The log parameter format string has a typo - there's an extra closing brace. The format string shows "Failed to create entries in {}} for {}/{}" but should be "Failed to create entries in {} for {}/{}" with only one closing brace after the first placeholder.
| throw new IllegalArgumentException(OLD_META_STR + " no longer exists. The table has been " | ||
| + "renamed to hbase:meta or hbase:meta_suffix in conf"); |
There was a problem hiding this comment.
The error message at line 305-306 references "hbase:meta or hbase:meta_suffix in conf" but this is confusing because the meta table name is not directly configured. The message should be updated to reflect that the meta table name is dynamically discovered, not configured. Consider: "The table has been renamed to hbase:meta (or a cluster-specific meta table name)"
| @Override | ||
| public TableName getMetaTableName() { | ||
| return toAsyncConnection().getMetaTableName(); | ||
| } |
There was a problem hiding this comment.
Calling toAsyncConnection().getMetaTableName() on line 374 will throw NotImplementedException because toAsyncConnection() on line 379 throws NotImplementedException. This creates a method that always throws an exception, which is a bug. The getMetaTableName() implementation should either throw NotImplementedException directly or have a proper implementation that doesn't depend on toAsyncConnection().
| public TableName getMetaTableName() { | ||
| if (asyncClusterConnection != null) { | ||
| try { | ||
| return asyncClusterConnection.getMetaTableName(); | ||
| } catch (Exception e) { | ||
| LOG.warn("Failed to get meta table name from Master", e); | ||
| } | ||
| } | ||
| // Bootstrap | ||
| return super.getMetaTableName(); | ||
| } |
There was a problem hiding this comment.
The getMetaTableName() method in HRegionServer could return different values over time, which could cause inconsistencies. During bootstrap (before asyncClusterConnection is set), it returns the default meta table name from the parent class. After the connection is established, it returns the value from the connection. If there's any caching or state that depends on this value, changing it mid-execution could cause issues. Consider caching the value once it's successfully retrieved from the connection to ensure consistency.
HBASE-29691: Change TableName.META_TABLE_NAME from being a global static
Change TableName.META_TABLE_NAME from a global static constant to a dynamically discovered value from ConnectionRegistry.
Please refer to earlier discussion on the PR #7558 for further details.
Key changes:
Refactoring for the test classes to be handled separately.