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