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.client;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertFalse;
022import static org.junit.jupiter.api.Assertions.assertNotNull;
023import static org.junit.jupiter.api.Assertions.assertThrows;
024import static org.junit.jupiter.api.Assertions.assertTrue;
025import static org.junit.jupiter.api.Assertions.fail;
026
027import java.io.IOException;
028import java.util.ArrayList;
029import java.util.Collection;
030import java.util.Collections;
031import java.util.EnumSet;
032import java.util.HashMap;
033import java.util.HashSet;
034import java.util.List;
035import java.util.concurrent.ThreadLocalRandom;
036import java.util.concurrent.atomic.AtomicInteger;
037import java.util.stream.Collectors;
038import org.apache.hadoop.hbase.ClusterMetrics.Option;
039import org.apache.hadoop.hbase.HConstants;
040import org.apache.hadoop.hbase.ServerName;
041import org.apache.hadoop.hbase.SingleProcessHBaseCluster;
042import org.apache.hadoop.hbase.TableExistsException;
043import org.apache.hadoop.hbase.TableName;
044import org.apache.hadoop.hbase.TableNotDisabledException;
045import org.apache.hadoop.hbase.TableNotEnabledException;
046import org.apache.hadoop.hbase.TableNotFoundException;
047import org.apache.hadoop.hbase.UnknownRegionException;
048import org.apache.hadoop.hbase.Waiter.Predicate;
049import org.apache.hadoop.hbase.constraint.ConstraintException;
050import org.apache.hadoop.hbase.master.HMaster;
051import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
052import org.apache.hadoop.hbase.regionserver.HRegion;
053import org.apache.hadoop.hbase.regionserver.HRegionServer;
054import org.apache.hadoop.hbase.regionserver.HStore;
055import org.apache.hadoop.hbase.testclassification.ClientTests;
056import org.apache.hadoop.hbase.testclassification.LargeTests;
057import org.apache.hadoop.hbase.util.Bytes;
058import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
059import org.apache.hadoop.hbase.util.FutureUtils;
060import org.apache.hadoop.hbase.wal.AbstractFSWALProvider;
061import org.junit.jupiter.api.Tag;
062import org.junit.jupiter.api.Test;
063import org.slf4j.Logger;
064import org.slf4j.LoggerFactory;
065
066import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
067import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
068
069/**
070 * Class to test HBaseAdmin. Spins up the minicluster once at test start and then takes it down
071 * afterward. Add any testing of HBaseAdmin functionality here.
072 */
073@Tag(LargeTests.TAG)
074@Tag(ClientTests.TAG)
075public class TestAdmin2 extends TestAdminBase {
076
077  private static final Logger LOG = LoggerFactory.getLogger(TestAdmin2.class);
078
079  @Test
080  public void testCreateBadTables() throws IOException {
081    String msg = null;
082    try {
083      ADMIN.createTable(TableDescriptorBuilder.newBuilder(TableName.META_TABLE_NAME).build());
084    } catch (TableExistsException e) {
085      msg = e.toString();
086    }
087    assertTrue(
088      msg != null && msg.startsWith(TableExistsException.class.getName())
089        && msg.contains(TableName.META_TABLE_NAME.getNameAsString()),
090      "Unexcepted exception message " + msg);
091
092    // Now try and do concurrent creation with a bunch of threads.
093    TableDescriptor tableDescriptor =
094      TableDescriptorBuilder.newBuilder(TableName.valueOf(methodName))
095        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY)).build();
096    int count = 10;
097    Thread[] threads = new Thread[count];
098    final AtomicInteger successes = new AtomicInteger(0);
099    final AtomicInteger failures = new AtomicInteger(0);
100    final Admin localAdmin = ADMIN;
101    for (int i = 0; i < count; i++) {
102      threads[i] = new Thread(Integer.toString(i)) {
103        @Override
104        public void run() {
105          try {
106            localAdmin.createTable(tableDescriptor);
107            successes.incrementAndGet();
108          } catch (TableExistsException e) {
109            failures.incrementAndGet();
110          } catch (IOException e) {
111            throw new RuntimeException("Failed threaded create" + getName(), e);
112          }
113        }
114      };
115    }
116    for (int i = 0; i < count; i++) {
117      threads[i].start();
118    }
119    for (int i = 0; i < count; i++) {
120      while (threads[i].isAlive()) {
121        try {
122          Thread.sleep(100);
123        } catch (InterruptedException e) {
124          // continue
125        }
126      }
127    }
128    // All threads are now dead. Count up how many tables were created and
129    // how many failed w/ appropriate exception.
130    assertEquals(1, successes.get());
131    assertEquals(count - 1, failures.get());
132  }
133
134  /**
135   * Test for hadoop-1581 'HBASE: Unopenable tablename bug'.
136   */
137  @Test
138  public void testTableNameClash() throws Exception {
139    TableDescriptor tableDescriptor1 =
140      TableDescriptorBuilder.newBuilder(TableName.valueOf(methodName + "SOMEUPPERCASE"))
141        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY)).build();
142    TableDescriptor tableDescriptor2 =
143      TableDescriptorBuilder.newBuilder(TableName.valueOf(methodName))
144        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY)).build();
145    ADMIN.createTable(tableDescriptor1);
146    ADMIN.createTable(tableDescriptor2);
147    // Before fix, below would fail throwing a NoServerForRegionException.
148    TEST_UTIL.getConnection().getTable(tableDescriptor2.getTableName()).close();
149  }
150
151  /***
152   * HMaster.createTable used to be kind of synchronous call Thus creating of table with lots of
153   * regions can cause RPC timeout After the fix to make createTable truly async, RPC timeout
154   * shouldn't be an issue anymore
155   */
156  @Test
157  public void testCreateTableRPCTimeOut() throws Exception {
158    int oldTimeout = TEST_UTIL.getConfiguration().getInt(HConstants.HBASE_RPC_TIMEOUT_KEY,
159      HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
160    TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 1500);
161    try {
162      int expectedRegions = 100;
163      // Use 80 bit numbers to make sure we aren't limited
164      byte[] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
165      byte[] endKey = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
166      Admin hbaseadmin = TEST_UTIL.getAdmin();
167      TableDescriptor tableDescriptor =
168        TableDescriptorBuilder.newBuilder(TableName.valueOf(methodName))
169          .setColumnFamily(ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY)).build();
170      hbaseadmin.createTable(tableDescriptor, startKey, endKey, expectedRegions);
171    } finally {
172      TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, oldTimeout);
173    }
174  }
175
176  /**
177   * Test read only tables
178   */
179  @Test
180  public void testReadOnlyTable() throws Exception {
181    final TableName name = TableName.valueOf(methodName);
182    Table table = TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY);
183    byte[] value = Bytes.toBytes("somedata");
184    // This used to use an empty row... That must have been a bug
185    Put put = new Put(value);
186    put.addColumn(HConstants.CATALOG_FAMILY, HConstants.CATALOG_FAMILY, value);
187    table.put(put);
188    table.close();
189  }
190
191  /**
192   * Test that user table names can contain '-' and '.' so long as they do not start with same.
193   * HBASE-771
194   */
195  @Test
196  public void testTableNames() throws IOException {
197    byte[][] illegalNames = new byte[][] { Bytes.toBytes("-bad"), Bytes.toBytes(".bad") };
198    for (byte[] illegalName : illegalNames) {
199      assertThrows(IllegalArgumentException.class, () -> TableName.valueOf(illegalName),
200        "Did not detect '" + Bytes.toString(illegalName) + "' as an illegal user table name");
201    }
202    byte[] legalName = Bytes.toBytes("g-oo.d");
203    try {
204      TableName.valueOf(legalName);
205    } catch (IllegalArgumentException e) {
206      fail("Legal user table name: '" + Bytes.toString(legalName)
207        + "' caused IllegalArgumentException: " + e.getMessage());
208    }
209  }
210
211  /**
212   * For HADOOP-2579
213   */
214  @Test
215  public void testTableExistsExceptionWithATable() throws IOException {
216    final TableName name = TableName.valueOf(methodName);
217    TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY).close();
218    assertThrows(TableExistsException.class,
219      () -> TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY));
220  }
221
222  /**
223   * Can't disable a table if the table isn't in enabled state
224   */
225  @Test
226  public void testTableNotEnabledExceptionWithATable() throws IOException {
227    final TableName name = TableName.valueOf(methodName);
228    TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY).close();
229    ADMIN.disableTable(name);
230    assertThrows(TableNotEnabledException.class, () -> ADMIN.disableTable(name));
231  }
232
233  /**
234   * Can't enable a table if the table isn't in disabled state
235   */
236  @Test
237  public void testTableNotDisabledExceptionWithATable() throws IOException {
238    final TableName name = TableName.valueOf(methodName);
239    try (Table t = TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY)) {
240      assertThrows(TableNotDisabledException.class, () -> ADMIN.enableTable(name));
241    }
242  }
243
244  /**
245   * For HADOOP-2579
246   */
247  @Test
248  public void testTableNotFoundExceptionWithoutAnyTables() throws IOException {
249    TableName tableName = TableName.valueOf("testTableNotFoundExceptionWithoutAnyTables");
250    try (Table ht = TEST_UTIL.getConnection().getTable(tableName)) {
251      assertThrows(TableNotFoundException.class, () -> ht.get(new Get(Bytes.toBytes("e"))));
252    }
253  }
254
255  @Test
256  public void testShouldUnassignTheRegion() throws Exception {
257    final TableName tableName = TableName.valueOf(methodName);
258    createTableWithDefaultConf(tableName);
259
260    RegionInfo info = null;
261    HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(tableName);
262    List<RegionInfo> onlineRegions = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices());
263    for (RegionInfo regionInfo : onlineRegions) {
264      if (!regionInfo.getTable().isSystemTable()) {
265        info = regionInfo;
266        ADMIN.unassign(regionInfo.getRegionName(), true);
267      }
268    }
269    boolean isInList = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices()).contains(info);
270    long timeout = EnvironmentEdgeManager.currentTime() + 10000;
271    while ((EnvironmentEdgeManager.currentTime() < timeout) && (isInList)) {
272      Thread.sleep(100);
273      isInList = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices()).contains(info);
274    }
275
276    assertFalse(isInList, "The region should not be present in online regions list.");
277  }
278
279  @Test
280  public void testCloseRegionIfInvalidRegionNameIsPassed() throws Exception {
281    byte[] tableName = Bytes.toBytes(methodName);
282    createTableWithDefaultConf(tableName);
283
284    RegionInfo info = null;
285    HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TableName.valueOf(tableName));
286    List<RegionInfo> onlineRegions = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices());
287    for (RegionInfo regionInfo : onlineRegions) {
288      if (!regionInfo.isMetaRegion()) {
289        if (regionInfo.getRegionNameAsString().contains(methodName)) {
290          info = regionInfo;
291          assertThrows(UnknownRegionException.class, () -> ADMIN.unassign(
292            Bytes.toBytes("test,,1358563771069.acc1ad1b7962564fc3a43e5907e8db33."), true));
293        }
294      }
295    }
296    onlineRegions = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices());
297    assertTrue(onlineRegions.contains(info),
298      "The region should be present in online regions list.");
299  }
300
301  @Test
302  public void testCloseRegionThatFetchesTheHRIFromMeta() throws Exception {
303    final TableName tableName = TableName.valueOf(methodName);
304    createTableWithDefaultConf(tableName);
305
306    RegionInfo info = null;
307    HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(tableName);
308    List<RegionInfo> onlineRegions = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices());
309    for (RegionInfo regionInfo : onlineRegions) {
310      if (!regionInfo.isMetaRegion()) {
311        if (regionInfo.getRegionNameAsString().contains("TestHBACloseRegion2")) {
312          info = regionInfo;
313          ADMIN.unassign(regionInfo.getRegionName(), true);
314        }
315      }
316    }
317
318    boolean isInList = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices()).contains(info);
319    long timeout = EnvironmentEdgeManager.currentTime() + 10000;
320    while ((EnvironmentEdgeManager.currentTime() < timeout) && (isInList)) {
321      Thread.sleep(100);
322      isInList = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices()).contains(info);
323    }
324
325    assertFalse(isInList, "The region should not be present in online regions list.");
326  }
327
328  private Admin createTable(TableName tableName) throws IOException {
329    Admin admin = TEST_UTIL.getAdmin();
330
331    TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(tableName);
332    ColumnFamilyDescriptor columnFamilyDescriptor =
333      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("value")).build();
334
335    tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
336    admin.createTable(tableDescriptorBuilder.build());
337    return admin;
338  }
339
340  private void createTableWithDefaultConf(byte[] TABLENAME) throws IOException {
341    createTableWithDefaultConf(TableName.valueOf(TABLENAME));
342  }
343
344  private void createTableWithDefaultConf(TableName TABLENAME) throws IOException {
345    TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(TABLENAME);
346    ColumnFamilyDescriptor columnFamilyDescriptor =
347      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("value")).build();
348    tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
349
350    ADMIN.createTable(tableDescriptorBuilder.build());
351  }
352
353  /**
354   * For HBASE-2556
355   */
356  @Test
357  public void testGetTableRegions() throws IOException {
358    final TableName tableName = TableName.valueOf(methodName);
359
360    int expectedRegions = 10;
361
362    // Use 80 bit numbers to make sure we aren't limited
363    byte[] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
364    byte[] endKey = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
365
366    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName)
367      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY)).build();
368    ADMIN.createTable(tableDescriptor, startKey, endKey, expectedRegions);
369
370    List<RegionInfo> RegionInfos = ADMIN.getRegions(tableName);
371
372    assertEquals(expectedRegions, RegionInfos.size(),
373      "Tried to create " + expectedRegions + " regions " + "but only found " + RegionInfos.size());
374  }
375
376  @Test
377  public void testMoveToPreviouslyAssignedRS() throws IOException, InterruptedException {
378    SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
379    HMaster master = cluster.getMaster();
380    final TableName tableName = TableName.valueOf(methodName);
381    Admin localAdmin = createTable(tableName);
382    List<RegionInfo> tableRegions = localAdmin.getRegions(tableName);
383    RegionInfo hri = tableRegions.get(0);
384    AssignmentManager am = master.getAssignmentManager();
385    ServerName server = am.getRegionStates().getRegionServerOfRegion(hri);
386    localAdmin.move(hri.getEncodedNameAsBytes(), server);
387    assertEquals(server, am.getRegionStates().getRegionServerOfRegion(hri),
388      "Current region server and region server before move should be same.");
389  }
390
391  @Test
392  public void testWALRollWriting() throws Exception {
393    setUpforLogRolling();
394    String className = this.getClass().getName();
395    StringBuilder v = new StringBuilder(className);
396    while (v.length() < 1000) {
397      v.append(className);
398    }
399    byte[] value = Bytes.toBytes(v.toString());
400    HRegionServer regionServer = startAndWriteData(TableName.valueOf(methodName), value);
401    LOG.info("after writing there are "
402      + AbstractFSWALProvider.getNumRolledLogFiles(regionServer.getWAL(null)) + " log files");
403
404    // flush all regions
405    for (HRegion r : regionServer.getOnlineRegionsLocalContext()) {
406      r.flush(true);
407    }
408    ADMIN.rollWALWriter(regionServer.getServerName());
409    TEST_UTIL.waitFor(5000, () -> {
410      int count = AbstractFSWALProvider.getNumRolledLogFiles(regionServer.getWAL(null));
411      LOG.info("after flushing all regions and rolling logs there are " + count + " log files");
412      return count <= 2;
413    });
414  }
415
416  private void setUpforLogRolling() {
417    // Force a region split after every 768KB
418    TEST_UTIL.getConfiguration().setLong(HConstants.HREGION_MAX_FILESIZE, 768L * 1024L);
419
420    // We roll the log after every 32 writes
421    TEST_UTIL.getConfiguration().setInt("hbase.regionserver.maxlogentries", 32);
422
423    TEST_UTIL.getConfiguration().setInt("hbase.regionserver.logroll.errors.tolerated", 2);
424    TEST_UTIL.getConfiguration().setInt("hbase.rpc.timeout", 10 * 1000);
425
426    // For less frequently updated regions flush after every 2 flushes
427    TEST_UTIL.getConfiguration().setInt("hbase.hregion.memstore.optionalflushcount", 2);
428
429    // We flush the cache after every 8192 bytes
430    TEST_UTIL.getConfiguration().setInt(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, 8192);
431
432    // Increase the amount of time between client retries
433    TEST_UTIL.getConfiguration().setLong("hbase.client.pause", 10 * 1000);
434
435    // Reduce thread wake frequency so that other threads can get
436    // a chance to run.
437    TEST_UTIL.getConfiguration().setInt(HConstants.THREAD_WAKE_FREQUENCY, 2 * 1000);
438
439    /**** configuration for testLogRollOnDatanodeDeath ****/
440    // lower the namenode & datanode heartbeat so the namenode
441    // quickly detects datanode failures
442    TEST_UTIL.getConfiguration().setInt("dfs.namenode.heartbeat.recheck-interval", 5000);
443    TEST_UTIL.getConfiguration().setInt("dfs.heartbeat.interval", 1);
444    // the namenode might still try to choose the recently-dead datanode
445    // for a pipeline, so try to a new pipeline multiple times
446    TEST_UTIL.getConfiguration().setInt("dfs.client.block.write.retries", 30);
447    TEST_UTIL.getConfiguration().setInt("hbase.regionserver.hlog.tolerable.lowreplication", 2);
448    TEST_UTIL.getConfiguration().setInt("hbase.regionserver.hlog.lowreplication.rolllimit", 3);
449  }
450
451  private HRegionServer startAndWriteData(TableName tableName, byte[] value)
452    throws IOException, InterruptedException {
453    // When the hbase:meta table can be opened, the region servers are running
454    TEST_UTIL.getConnection().getTable(TableName.META_TABLE_NAME).close();
455
456    // Create the test table and open it
457    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName)
458      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY)).build();
459    ADMIN.createTable(tableDescriptor);
460    try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
461      HRegionServer regionServer = TEST_UTIL.getRSForFirstRegionInTable(tableName);
462      for (int i = 1; i <= 256; i++) { // 256 writes should cause 8 log rolls
463        Put put = new Put(Bytes.toBytes("row" + String.format("%1$04d", i)));
464        put.addColumn(HConstants.CATALOG_FAMILY, null, value);
465        table.put(put);
466        if (i % 32 == 0) {
467          // After every 32 writes sleep to let the log roller run
468          try {
469            Thread.sleep(2000);
470          } catch (InterruptedException e) {
471            // continue
472          }
473        }
474      }
475      return regionServer;
476    }
477  }
478
479  @Test
480  public void testDisableCatalogTable() throws Exception {
481    try {
482      ADMIN.disableTable(TableName.META_TABLE_NAME);
483      fail("Expected to throw ConstraintException");
484    } catch (ConstraintException e) {
485    }
486    // Before the fix for HBASE-6146, the below table creation was failing as the hbase:meta table
487    // actually getting disabled by the disableTable() call.
488    TableDescriptor tableDescriptor =
489      TableDescriptorBuilder.newBuilder(TableName.valueOf(Bytes.toBytes(methodName)))
490        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(Bytes.toBytes("cf1"))).build();
491    TEST_UTIL.getAdmin().createTable(tableDescriptor);
492  }
493
494  @Test
495  public void testIsEnabledOrDisabledOnUnknownTable() throws Exception {
496    try {
497      ADMIN.isTableEnabled(TableName.valueOf(methodName));
498      fail("Test should fail if isTableEnabled called on unknown table.");
499    } catch (IOException e) {
500    }
501
502    try {
503      ADMIN.isTableDisabled(TableName.valueOf(methodName));
504      fail("Test should fail if isTableDisabled called on unknown table.");
505    } catch (IOException e) {
506    }
507  }
508
509  @Test
510  public void testBalancer() throws Exception {
511    boolean initialState = ADMIN.isBalancerEnabled();
512
513    // Start the balancer, wait for it.
514    boolean prevState = ADMIN.balancerSwitch(!initialState, true);
515
516    // The previous state should be the original state we observed
517    assertEquals(initialState, prevState);
518
519    // Current state should be opposite of the original
520    assertEquals(!initialState, ADMIN.isBalancerEnabled());
521
522    // Reset it back to what it was
523    prevState = ADMIN.balancerSwitch(initialState, true);
524
525    // The previous state should be the opposite of the initial state
526    assertEquals(!initialState, prevState);
527    // Current state should be the original state again
528    assertEquals(initialState, ADMIN.isBalancerEnabled());
529  }
530
531  @Test
532  public void testRegionNormalizer() throws Exception {
533    boolean initialState = ADMIN.isNormalizerEnabled();
534
535    // flip state
536    boolean prevState = ADMIN.normalizerSwitch(!initialState);
537
538    // The previous state should be the original state we observed
539    assertEquals(initialState, prevState);
540
541    // Current state should be opposite of the original
542    assertEquals(!initialState, ADMIN.isNormalizerEnabled());
543
544    // Reset it back to what it was
545    prevState = ADMIN.normalizerSwitch(initialState);
546
547    // The previous state should be the opposite of the initial state
548    assertEquals(!initialState, prevState);
549    // Current state should be the original state again
550    assertEquals(initialState, ADMIN.isNormalizerEnabled());
551  }
552
553  @Test
554  public void testAbortProcedureFail() throws Exception {
555    long procId = ThreadLocalRandom.current().nextLong();
556    boolean abortResult = ADMIN.abortProcedure(procId, true);
557    assertFalse(abortResult);
558  }
559
560  @Test
561  public void testGetProcedures() throws Exception {
562    String procList = ADMIN.getProcedures();
563    assertTrue(procList.startsWith("["));
564  }
565
566  @Test
567  public void testGetLocks() throws Exception {
568    String lockList = ADMIN.getLocks();
569    assertTrue(lockList.startsWith("["));
570  }
571
572  @Test
573  public void testDecommissionRegionServers() throws Exception {
574    List<ServerName> decommissionedRegionServers = ADMIN.listDecommissionedRegionServers();
575    assertTrue(decommissionedRegionServers.isEmpty());
576
577    final TableName tableName = TableName.valueOf(methodName);
578    TEST_UTIL.createMultiRegionTable(tableName, Bytes.toBytes("f"), 6);
579
580    ArrayList<ServerName> clusterRegionServers = new ArrayList<>(
581      ADMIN.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).getLiveServerMetrics().keySet());
582
583    assertEquals(3, clusterRegionServers.size());
584
585    HashMap<ServerName, List<RegionInfo>> serversToDecommssion = new HashMap<>();
586    // Get a server that has meta online. We will decommission two of the servers,
587    // leaving one online.
588    int i;
589    for (i = 0; i < clusterRegionServers.size(); i++) {
590      List<RegionInfo> regionsOnServer = ADMIN.getRegions(clusterRegionServers.get(i));
591      if (ADMIN.getRegions(clusterRegionServers.get(i)).stream().anyMatch(p -> p.isMetaRegion())) {
592        serversToDecommssion.put(clusterRegionServers.get(i), regionsOnServer);
593        break;
594      }
595    }
596
597    clusterRegionServers.remove(i);
598    // Get another server to decommission.
599    serversToDecommssion.put(clusterRegionServers.get(0),
600      ADMIN.getRegions(clusterRegionServers.get(0)));
601
602    ServerName remainingServer = clusterRegionServers.get(1);
603
604    // Decommission
605    ADMIN.decommissionRegionServers(new ArrayList<ServerName>(serversToDecommssion.keySet()), true);
606    assertEquals(2, ADMIN.listDecommissionedRegionServers().size());
607
608    // Verify the regions have been off the decommissioned servers, all on the one
609    // remaining server.
610    for (ServerName server : serversToDecommssion.keySet()) {
611      for (RegionInfo region : serversToDecommssion.get(server)) {
612        TEST_UTIL.assertRegionOnServer(region, remainingServer, 10000);
613      }
614    }
615
616    // Recommission and load the regions.
617    for (ServerName server : serversToDecommssion.keySet()) {
618      List<byte[]> encodedRegionNames = serversToDecommssion.get(server).stream()
619        .map(region -> region.getEncodedNameAsBytes()).collect(Collectors.toList());
620      ADMIN.recommissionRegionServer(server, encodedRegionNames);
621    }
622    assertTrue(ADMIN.listDecommissionedRegionServers().isEmpty());
623    // Verify the regions have been moved to the recommissioned servers
624    for (ServerName server : serversToDecommssion.keySet()) {
625      for (RegionInfo region : serversToDecommssion.get(server)) {
626        TEST_UTIL.assertRegionOnServer(region, server, 10000);
627      }
628    }
629  }
630
631  /**
632   * TestCase for HBASE-21355
633   */
634  @Test
635  public void testGetRegionInfo() throws Exception {
636    final TableName tableName = TableName.valueOf(methodName);
637    Table table = TEST_UTIL.createTable(tableName, Bytes.toBytes("f"));
638    for (int i = 0; i < 100; i++) {
639      table.put(new Put(Bytes.toBytes(i)).addColumn(Bytes.toBytes("f"), Bytes.toBytes("q"),
640        Bytes.toBytes(i)));
641    }
642    ADMIN.flush(tableName);
643
644    HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(table.getName());
645    List<HRegion> regions = rs.getRegions(tableName);
646    assertEquals(1, regions.size());
647
648    HRegion region = regions.get(0);
649    byte[] regionName = region.getRegionInfo().getRegionName();
650    HStore store = region.getStore(Bytes.toBytes("f"));
651    long expectedStoreFilesSize = store.getStorefilesSize();
652    assertNotNull(store);
653    assertEquals(expectedStoreFilesSize, store.getSize());
654    for (int i = 0; i < 10; i++) {
655      RegionInfo ri = ProtobufUtil
656        .toRegionInfo(TEST_UTIL.getAsyncConnection().getRegionServerAdmin(rs.getServerName())
657          .getRegionInfo(RequestConverter.buildGetRegionInfoRequest(regionName)).get()
658          .getRegionInfo());
659
660      assertEquals(region.getRegionInfo(), ri);
661
662      // Make sure that the store size is still the actual file system's store size.
663      assertEquals(expectedStoreFilesSize, store.getSize());
664    }
665
666    // Test querying using the encoded name only. When encoded name passed,
667    // and the target server is the Master, we return the full region name.
668    // Convenience.
669    ServerName sn = null;
670    try (Admin admin = TEST_UTIL.getConnection().getAdmin()) {
671      sn = admin.getMaster();
672    }
673    RegionInfo ri = region.getRegionInfo();
674    testGetWithRegionName(sn, ri, ri.getEncodedNameAsBytes());
675    testGetWithRegionName(sn, ri, ri.getRegionName());
676    // Try querying meta encoded name.
677    ri = RegionInfoBuilder.FIRST_META_REGIONINFO;
678    testGetWithRegionName(sn, ri, ri.getEncodedNameAsBytes());
679    testGetWithRegionName(sn, ri, ri.getRegionName());
680  }
681
682  /**
683   * Do get of RegionInfo from Master using encoded region name.
684   */
685  private void testGetWithRegionName(ServerName sn, RegionInfo inputRI, byte[] regionName)
686    throws IOException {
687    RegionInfo ri = ProtobufUtil
688      .toRegionInfo(FutureUtils.get(TEST_UTIL.getAsyncConnection().getRegionServerAdmin(sn)
689        .getRegionInfo(ProtobufUtil.getGetRegionInfoRequest(regionName))).getRegionInfo());
690    assertEquals(inputRI, ri);
691  }
692
693  @Test
694  public void testTableSplitFollowedByModify() throws Exception {
695    final TableName tableName = TableName.valueOf(methodName);
696    TEST_UTIL.createTable(tableName, Bytes.toBytes("f"));
697
698    // get the original table region count
699    List<RegionInfo> regions = ADMIN.getRegions(tableName);
700    int originalCount = regions.size();
701    assertEquals(1, originalCount);
702
703    // split the table and wait until region count increases
704    ADMIN.split(tableName, Bytes.toBytes(3));
705    TEST_UTIL.waitFor(30000, new Predicate<Exception>() {
706
707      @Override
708      public boolean evaluate() throws Exception {
709        return ADMIN.getRegions(tableName).size() > originalCount;
710      }
711    });
712
713    // do some table modification
714    TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(ADMIN.getDescriptor(tableName))
715      .setMaxFileSize(11111111).build();
716    ADMIN.modifyTable(tableDesc);
717    assertEquals(11111111, ADMIN.getDescriptor(tableName).getMaxFileSize());
718  }
719
720  @SuppressWarnings("FutureReturnValueIgnored")
721  @Test
722  public void testTableMergeFollowedByModify() throws Exception {
723    final TableName tableName = TableName.valueOf(methodName);
724    TEST_UTIL.createTable(tableName, new byte[][] { Bytes.toBytes("f") },
725      new byte[][] { Bytes.toBytes(3) });
726
727    // assert we have at least 2 regions in the table
728    List<RegionInfo> regions = ADMIN.getRegions(tableName);
729    int originalCount = regions.size();
730    assertTrue(originalCount >= 2);
731
732    byte[] nameOfRegionA = regions.get(0).getEncodedNameAsBytes();
733    byte[] nameOfRegionB = regions.get(1).getEncodedNameAsBytes();
734
735    // merge the table regions and wait until region count decreases
736    ADMIN.mergeRegionsAsync(nameOfRegionA, nameOfRegionB, true);
737    TEST_UTIL.waitFor(30000, new Predicate<Exception>() {
738
739      @Override
740      public boolean evaluate() throws Exception {
741        return ADMIN.getRegions(tableName).size() < originalCount;
742      }
743    });
744
745    // do some table modification
746    TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(ADMIN.getDescriptor(tableName))
747      .setMaxFileSize(11111111).build();
748    ADMIN.modifyTable(tableDesc);
749    assertEquals(11111111, ADMIN.getDescriptor(tableName).getMaxFileSize());
750  }
751
752  @Test
753  public void testSnapshotCleanupAsync() throws Exception {
754    testSnapshotCleanup(false);
755  }
756
757  @Test
758  public void testSnapshotCleanupSync() throws Exception {
759    testSnapshotCleanup(true);
760  }
761
762  private void testSnapshotCleanup(final boolean synchronous) throws IOException {
763    final boolean initialState = ADMIN.isSnapshotCleanupEnabled();
764    // Switch the snapshot auto cleanup state to opposite to initial state
765    boolean prevState = ADMIN.snapshotCleanupSwitch(!initialState, synchronous);
766    // The previous state should be the original state we observed
767    assertEquals(initialState, prevState);
768    // Current state should be opposite of the initial state
769    assertEquals(!initialState, ADMIN.isSnapshotCleanupEnabled());
770    // Reset the state back to what it was initially
771    prevState = ADMIN.snapshotCleanupSwitch(initialState, synchronous);
772    // The previous state should be the opposite of the initial state
773    assertEquals(!initialState, prevState);
774    // Current state should be the original state again
775    assertEquals(initialState, ADMIN.isSnapshotCleanupEnabled());
776  }
777
778  @Test
779  public void testSlowLogResponses() throws Exception {
780    // get all live server names
781    Collection<ServerName> serverNames = ADMIN.getRegionServers();
782    List<ServerName> serverNameList = new ArrayList<>(serverNames);
783
784    // clean up slowlog responses maintained in memory by RegionServers
785    List<Boolean> areSlowLogsCleared = ADMIN.clearSlowLogResponses(new HashSet<>(serverNameList));
786
787    int countFailedClearSlowResponse = 0;
788    for (Boolean isSlowLogCleared : areSlowLogsCleared) {
789      if (!isSlowLogCleared) {
790        ++countFailedClearSlowResponse;
791      }
792    }
793    assertEquals(countFailedClearSlowResponse, 0);
794
795    List<LogEntry> onlineLogRecords = ADMIN.getLogEntries(new HashSet<>(serverNames), "SLOW_LOG",
796      ServerType.REGION_SERVER, 100, null);
797    // after cleanup of slowlog responses, total count of slowlog payloads should be 0
798    assertEquals(onlineLogRecords.size(), 0);
799    List<LogEntry> balancerDecisionRecords =
800      ADMIN.getLogEntries(null, "BALANCER_DECISION", ServerType.MASTER, 100, null);
801    assertEquals(balancerDecisionRecords.size(), 0);
802  }
803
804  @Test
805  public void testGetRegionServers() throws Exception {
806    // get all live server names
807    List<ServerName> serverNames = new ArrayList<>(ADMIN.getRegionServers(true));
808    assertEquals(3, serverNames.size());
809
810    List<ServerName> serversToDecom = new ArrayList<>();
811    ServerName serverToDecommission = serverNames.get(0);
812
813    serversToDecom.add(serverToDecommission);
814    ADMIN.decommissionRegionServers(serversToDecom, false);
815    waitForServerCommissioned(serverToDecommission, true);
816
817    assertEquals(2, ADMIN.getRegionServers(true).size());
818    assertEquals(3, ADMIN.getRegionServers(false).size());
819
820    ADMIN.recommissionRegionServer(serverToDecommission, Collections.emptyList());
821    waitForServerCommissioned(null, false);
822
823    assertEquals(3, ADMIN.getRegionServers(true).size());
824    assertEquals(3, ADMIN.getRegionServers(false).size());
825  }
826
827  private static void waitForServerCommissioned(ServerName excludeServer,
828    boolean anyServerDecommissioned) {
829    TEST_UTIL.waitFor(3000, () -> {
830      try {
831        List<ServerName> decomServers = TEST_UTIL.getAdmin().listDecommissionedRegionServers();
832        if (anyServerDecommissioned) {
833          return decomServers.size() == 1 && decomServers.get(0).equals(excludeServer);
834        } else {
835          return decomServers.size() == 0;
836        }
837      } catch (IOException e) {
838        throw new RuntimeException(e);
839      }
840    });
841  }
842
843}