001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertNotEquals; 022import static org.junit.jupiter.api.Assertions.assertNotNull; 023import static org.junit.jupiter.api.Assertions.assertNull; 024import static org.junit.jupiter.api.Assertions.assertTrue; 025 026import java.io.IOException; 027import java.lang.invoke.MethodHandles; 028import java.lang.reflect.Field; 029import java.lang.reflect.Modifier; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.hbase.client.Connection; 032import org.apache.hadoop.hbase.client.ConnectionFactory; 033import org.apache.hadoop.hbase.client.RegionInfo; 034import org.apache.hadoop.hbase.master.HMaster; 035import org.apache.hadoop.hbase.testclassification.MediumTests; 036import org.apache.hadoop.hbase.testclassification.MiscTests; 037import org.apache.hadoop.hbase.util.Bytes; 038import org.apache.hadoop.hbase.util.Pair; 039import org.junit.jupiter.api.AfterAll; 040import org.junit.jupiter.api.BeforeAll; 041import org.junit.jupiter.api.Tag; 042import org.junit.jupiter.api.Test; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045 046/** 047 * Test {@link org.apache.hadoop.hbase.TestMetaTableForReplica}. 048 */ 049@Tag(MiscTests.TAG) 050@Tag(MediumTests.TAG) 051@SuppressWarnings("deprecation") 052public class TestMetaTableForReplica { 053 054 private static final Logger LOG = LoggerFactory.getLogger(TestMetaTableForReplica.class); 055 private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 056 private static Connection connection; 057 private static Field metaTableName; 058 private static Object originalMetaTableName; 059 060 @BeforeAll 061 public static void beforeClass() throws Exception { 062 Configuration c = UTIL.getConfiguration(); 063 // quicker heartbeat interval for faster DN death notification 064 c.setInt("hbase.ipc.client.connect.max.retries", 1); 065 c.setInt(HConstants.ZK_SESSION_TIMEOUT, 1000); 066 // Start cluster having non-default hbase meta table name 067 UTIL.startMiniCluster(3); 068 connection = ConnectionFactory.createConnection(c); 069 // Save the original value of META_TABLE_NAME before any test runs.x 070 metaTableName = TableName.class.getDeclaredField("META_TABLE_NAME"); 071 originalMetaTableName = metaTableName.get(null); 072 } 073 074 @AfterAll 075 public static void afterClass() throws Exception { 076 connection.close(); 077 UTIL.shutdownMiniCluster(); 078 } 079 080 @Test 081 public void testStateOfMetaForReplica() { 082 HMaster m = UTIL.getMiniHBaseCluster().getMaster(); 083 assertTrue(m.waitForMetaOnline()); 084 } 085 086 @Test 087 public void testMetaTableNameForReplicaWithoutSuffix() throws IOException { 088 testNameOfMetaForReplica(); 089 testGetNonExistentRegionFromMetaFromReplica(); 090 testGetExistentRegionFromMetaFromReplica(); 091 } 092 093 private void testNameOfMetaForReplica() { 094 // Check the correctness of the meta table for replica 095 String metaTableName = TableName.META_TABLE_NAME.getNameWithNamespaceInclAsString(); 096 assertNotNull(metaTableName); 097 098 // Check if name of the meta table for replica is same as the default meta table 099 assertEquals(0, 100 TableName.META_TABLE_NAME.compareTo(TableName.getDefaultNameOfMetaForReplica())); 101 } 102 103 private void testGetNonExistentRegionFromMetaFromReplica() throws IOException { 104 LOG.info("Started testGetNonExistentRegionFromMetaFromReplica"); 105 Pair<RegionInfo, ServerName> pair = 106 MetaTableAccessor.getRegion(connection, Bytes.toBytes("nonexistent-region")); 107 assertNull(pair); 108 LOG.info("Finished testGetNonExistentRegionFromMetaFromReplica"); 109 } 110 111 private void testGetExistentRegionFromMetaFromReplica() throws IOException { 112 final TableName tableName = TableName.valueOf("testMetaTableNameForReplicaWithoutSuffix"); 113 LOG.info("Started " + tableName); 114 UTIL.createTable(tableName, HConstants.CATALOG_FAMILY); 115 assertEquals(1, MetaTableAccessor.getTableRegions(connection, tableName).size()); 116 } 117 118 @Test 119 public void testMetaTableNameForReplicaWithSuffix() throws Exception { 120 // This test actively changes the META_TABLE_NAME to a non-default value and verifies it. 121 Configuration conf = HBaseConfiguration.create(); 122 String suffix = "replica1"; 123 conf.set(HConstants.HBASE_META_TABLE_SUFFIX, suffix); 124 125 // Re-initialize the static final META_TABLE_NAME for the testing to a non-default value. 126 TableName expectedMetaTableName = TableName.initializeHbaseMetaTableName(conf); 127 setStaticFinalField(metaTableName, expectedMetaTableName); 128 129 TableName currentMetaName = TableName.META_TABLE_NAME; 130 TableName defaultMetaName = TableName.getDefaultNameOfMetaForReplica(); 131 132 // The current meta table name is not the default one. 133 assertNotEquals(defaultMetaName, currentMetaName, 134 "META_TABLE_NAME should not be the default. "); 135 136 // The current meta table name has the configured suffix. 137 assertEquals(expectedMetaTableName, currentMetaName, 138 "META_TABLE_NAME should have the configured suffix"); 139 140 // restore default value of META_TABLE_NAME 141 setDefaultMetaTableName(); 142 } 143 144 private static void setDefaultMetaTableName() throws Exception { 145 if (originalMetaTableName != null) { 146 setStaticFinalField(metaTableName, originalMetaTableName); 147 } 148 } 149 150 /** 151 * A helper method to modify a static final field using reflection. This is necessary for testing 152 * code that reads a configuration only once during class loading. 153 * @param field The field to modify. 154 * @param newValue The new value to set. 155 * @throws Exception if reflection fails. 156 */ 157 private static void setStaticFinalField(Field field, Object newValue) throws Exception { 158 field.setAccessible(true); 159 // Using MethodHandles to get a trusted lookup with the necessary permissions to modify it. 160 // NOTE: For this to work, the JVM running the test must be started with arguments like: 161 // --add-opens=java.base/java.lang.reflect=ALL-UNNAMED 162 var lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup()); 163 var handle = lookup.findVarHandle(Field.class, "modifiers", int.class); 164 handle.set(field, field.getModifiers() & ~Modifier.FINAL); 165 field.set(null, newValue); 166 } 167}