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.rsgroup; 019 020import static org.apache.hadoop.hbase.rsgroup.RSGroupInfoManagerImpl.META_FAMILY_BYTES; 021import static org.apache.hadoop.hbase.rsgroup.RSGroupInfoManagerImpl.META_QUALIFIER_BYTES; 022import static org.apache.hadoop.hbase.rsgroup.RSGroupInfoManagerImpl.RSGROUP_TABLE_NAME; 023import static org.junit.jupiter.api.Assertions.assertEquals; 024import static org.junit.jupiter.api.Assertions.assertTrue; 025 026import java.io.IOException; 027import java.util.concurrent.CountDownLatch; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.hbase.HConstants; 030import org.apache.hadoop.hbase.TableDescriptors; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.client.Get; 033import org.apache.hadoop.hbase.client.Put; 034import org.apache.hadoop.hbase.client.Result; 035import org.apache.hadoop.hbase.client.Table; 036import org.apache.hadoop.hbase.client.TableDescriptor; 037import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 038import org.apache.hadoop.hbase.master.HMaster; 039import org.apache.hadoop.hbase.testclassification.MediumTests; 040import org.apache.hadoop.hbase.testclassification.RSGroupTests; 041import org.apache.hadoop.hbase.util.Bytes; 042import org.apache.zookeeper.KeeperException; 043import org.junit.jupiter.api.AfterAll; 044import org.junit.jupiter.api.BeforeAll; 045import org.junit.jupiter.api.Tag; 046import org.junit.jupiter.api.Test; 047import org.junit.jupiter.api.TestInfo; 048 049import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 050import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupProtos; 051 052/** 053 * Testcase for HBASE-22819 054 */ 055@Tag(RSGroupTests.TAG) 056@Tag(MediumTests.TAG) 057public class TestMigrateRSGroupInfo extends TestRSGroupsBase { 058 059 private static String TABLE_NAME_PREFIX = "Table_"; 060 061 private static int NUM_TABLES = 10; 062 063 private static byte[] FAMILY = Bytes.toBytes("family"); 064 065 private static RSGroupAdminClient RS_GROUP_ADMIN_CLIENT; 066 067 @BeforeAll 068 public static void setUp() throws Exception { 069 TEST_UTIL.getConfiguration().setClass(HConstants.MASTER_IMPL, HMasterForTest.class, 070 HMaster.class); 071 // confirm that we could enable rs group by setting the old CP. 072 TEST_UTIL.getConfiguration().setBoolean(RSGroupUtil.RS_GROUP_ENABLED, false); 073 TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, 074 RSGroupAdminEndpoint.class.getName()); 075 setUpTestBeforeClass(); 076 RS_GROUP_ADMIN_CLIENT = new RSGroupAdminClient(TEST_UTIL.getConnection()); 077 for (int i = 0; i < NUM_TABLES; i++) { 078 TEST_UTIL.createTable(TableName.valueOf(TABLE_NAME_PREFIX + i), FAMILY); 079 } 080 } 081 082 @AfterAll 083 public static void tearDown() throws Exception { 084 tearDownAfterClass(); 085 } 086 087 private static CountDownLatch RESUME = new CountDownLatch(1); 088 089 public static final class HMasterForTest extends HMaster { 090 091 public HMasterForTest(Configuration conf) throws IOException, KeeperException { 092 super(conf); 093 } 094 095 @Override 096 public TableDescriptors getTableDescriptors() { 097 if (RESUME != null) { 098 for (StackTraceElement element : Thread.currentThread().getStackTrace()) { 099 if (element.getMethodName().equals("migrate")) { 100 try { 101 RESUME.await(); 102 } catch (InterruptedException e) { 103 } 104 RESUME = null; 105 break; 106 } 107 } 108 } 109 return super.getTableDescriptors(); 110 } 111 } 112 113 @Test 114 public void testMigrate(TestInfo testInfo) throws IOException, InterruptedException { 115 String groupName = getNameWithoutIndex(testInfo.getTestMethod().get().getName()); 116 addGroup(groupName, TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads().size() - 1); 117 RSGroupInfo rsGroupInfo = ADMIN.getRSGroup(groupName); 118 assertTrue(rsGroupInfo.getTables().isEmpty()); 119 for (int i = 0; i < NUM_TABLES; i++) { 120 rsGroupInfo.addTable(TableName.valueOf(TABLE_NAME_PREFIX + i)); 121 } 122 try (Table table = TEST_UTIL.getConnection().getTable(RSGROUP_TABLE_NAME)) { 123 RSGroupProtos.RSGroupInfo proto = ProtobufUtil.toProtoGroupInfo(rsGroupInfo); 124 Put p = new Put(Bytes.toBytes(rsGroupInfo.getName())); 125 p.addColumn(META_FAMILY_BYTES, META_QUALIFIER_BYTES, proto.toByteArray()); 126 table.put(p); 127 } 128 TEST_UTIL.getMiniHBaseCluster().stopMaster(0).join(); 129 RESUME = new CountDownLatch(1); 130 TEST_UTIL.getMiniHBaseCluster().startMaster(); 131 TEST_UTIL.invalidateConnection(); 132 RS_GROUP_ADMIN_CLIENT = new RSGroupAdminClient(TEST_UTIL.getConnection()); 133 134 // wait until we can get the rs group info for a table 135 TEST_UTIL.waitFor(30000, () -> { 136 try { 137 RS_GROUP_ADMIN_CLIENT.getRSGroupInfoOfTable(TableName.valueOf(TABLE_NAME_PREFIX + 0)); 138 return true; 139 } catch (IOException e) { 140 return false; 141 } 142 }); 143 // confirm that before migrating, we could still get the correct rs group for a table. 144 for (int i = 0; i < NUM_TABLES; i++) { 145 RSGroupInfo info = 146 RS_GROUP_ADMIN_CLIENT.getRSGroupInfoOfTable(TableName.valueOf(TABLE_NAME_PREFIX + i)); 147 assertEquals(rsGroupInfo.getName(), info.getName()); 148 assertEquals(NUM_TABLES, info.getTables().size()); 149 } 150 RESUME.countDown(); 151 TEST_UTIL.waitFor(60000, () -> { 152 for (int i = 0; i < NUM_TABLES; i++) { 153 TableDescriptor td; 154 try { 155 td = TEST_UTIL.getAdmin().getDescriptor(TableName.valueOf(TABLE_NAME_PREFIX + i)); 156 } catch (IOException e) { 157 return false; 158 } 159 if (!rsGroupInfo.getName().equals(td.getRegionServerGroup().orElse(null))) { 160 return false; 161 } 162 } 163 return true; 164 }); 165 // make sure that we persist the result to hbase, where we delete all the tables in the rs 166 // group. 167 TEST_UTIL.waitFor(30000, () -> { 168 try (Table table = TEST_UTIL.getConnection().getTable(RSGROUP_TABLE_NAME)) { 169 Result result = table.get(new Get(Bytes.toBytes(rsGroupInfo.getName()))); 170 RSGroupProtos.RSGroupInfo proto = RSGroupProtos.RSGroupInfo 171 .parseFrom(result.getValue(META_FAMILY_BYTES, META_QUALIFIER_BYTES)); 172 RSGroupInfo gi = ProtobufUtil.toGroupInfo(proto); 173 return gi.getTables().isEmpty(); 174 } 175 }); 176 // make sure that the migrate thread has quit. 177 TEST_UTIL.waitFor(30000, () -> Thread.getAllStackTraces().keySet().stream() 178 .noneMatch(t -> t.getName().equals(RSGroupInfoManagerImpl.MIGRATE_THREAD_NAME))); 179 // make sure we could still get the correct rs group info after migration 180 for (int i = 0; i < NUM_TABLES; i++) { 181 RSGroupInfo info = 182 RS_GROUP_ADMIN_CLIENT.getRSGroupInfoOfTable(TableName.valueOf(TABLE_NAME_PREFIX + i)); 183 assertEquals(rsGroupInfo.getName(), info.getName()); 184 assertEquals(NUM_TABLES, info.getTables().size()); 185 } 186 } 187}