/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.client;
import static org.apache.hadoop.hbase.TableName.META_TABLE_NAME;
import static org.apache.hadoop.hbase.client.AsyncProcess.START_LOG_ERRORS_AFTER_COUNT_KEY;
import static org.apache.hadoop.hbase.TableName.META_TABLE_NAME;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletionException;
import java.util.regex.Pattern;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.AsyncMetaTableAccessor;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HRegionLocation;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.TableNotEnabledException;
import org.apache.hadoop.hbase.master.MasterFileSystem;
import org.apache.hadoop.hbase.testclassification.ClientTests;
import org.apache.hadoop.hbase.testclassification.LargeTests;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.FSTableDescriptors;
import org.apache.hadoop.hbase.util.FSUtils;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TestName;
/**
* Class to test asynchronous table admin operations.
*/
@Category({LargeTests.class, ClientTests.class})
public class TestAsyncTableAdminApi extends TestAsyncAdminBase {
@Rule
public TestName name = new TestName();
@Test
public void testTableExist() throws Exception {
final TableName tableName = TableName.valueOf(name.getMethodName());
boolean exist;
exist = admin.tableExists(tableName).get();
assertEquals(false, exist);
TEST_UTIL.createTable(tableName, FAMILY);
exist = admin.tableExists(tableName).get();
assertEquals(true, exist);
exist = admin.tableExists(TableName.META_TABLE_NAME).get();
assertEquals(true, exist);
}
@Test
public void testListTables() throws Exception {
int numTables = admin.listTables().get().length;
final TableName tableName1 = TableName.valueOf(name.getMethodName() + "1");
final TableName tableName2 = TableName.valueOf(name.getMethodName() + "2");
final TableName tableName3 = TableName.valueOf(name.getMethodName() + "3");
TableName[] tables = new TableName[] { tableName1, tableName2, tableName3 };
for (int i = 0; i < tables.length; i++) {
TEST_UTIL.createTable(tables[i], FAMILY);
}
TableDescriptor[] tableDescs = admin.listTables().get();
int size = tableDescs.length;
assertTrue(size >= tables.length);
for (int i = 0; i < tables.length && i < size; i++) {
boolean found = false;
for (int j = 0; j < tableDescs.length; j++) {
if (tableDescs[j].getTableName().equals(tables[i])) {
found = true;
break;
}
}
assertTrue("Not found: " + tables[i], found);
}
TableName[] tableNames = admin.listTableNames().get();
size = tableNames.length;
assertTrue(size == (numTables + tables.length));
for (int i = 0; i < tables.length && i < size; i++) {
boolean found = false;
for (int j = 0; j < tableNames.length; j++) {
if (tableNames[j].equals(tables[i])) {
found = true;
break;
}
}
assertTrue("Not found: " + tables[i], found);
}
for (int i = 0; i < tables.length; i++) {
TEST_UTIL.deleteTable(tables[i]);
}
tableDescs = admin.listTables((Pattern) null, true).get();
assertTrue("Not found system tables", tableDescs.length > 0);
tableNames = admin.listTableNames((Pattern) null, true).get();
assertTrue("Not found system tables", tableNames.length > 0);
}
@Test(timeout = 300000)
public void testGetTableDescriptor() throws Exception {
HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
HColumnDescriptor fam2 = new HColumnDescriptor("fam2");
HColumnDescriptor fam3 = new HColumnDescriptor("fam3");
HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
htd.addFamily(fam1);
htd.addFamily(fam2);
htd.addFamily(fam3);
admin.createTable(htd).join();
TableDescriptor confirmedHtd = admin.getTableDescriptor(htd.getTableName()).get();
assertEquals(htd.compareTo(new HTableDescriptor(confirmedHtd)), 0);
}
@Test(timeout = 300000)
public void testCreateTable() throws Exception {
TableDescriptor[] tables = admin.listTables().get();
int numTables = tables.length;
final TableName tableName = TableName.valueOf(name.getMethodName());
admin.createTable(new HTableDescriptor(tableName).addFamily(new HColumnDescriptor(FAMILY)))
.join();
tables = admin.listTables().get();
assertEquals(numTables + 1, tables.length);
assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster().getMaster()
.getTableStateManager().isTableState(tableName, TableState.State.ENABLED));
assertEquals(TableState.State.ENABLED, getStateFromMeta(tableName));
}
private TableState.State getStateFromMeta(TableName table) throws Exception {
Optional<TableState> state = AsyncMetaTableAccessor.getTableState(
ASYNC_CONN.getRawTable(TableName.META_TABLE_NAME), table).get();
assertTrue(state.isPresent());
return state.get().getState();
}
@Test(timeout = 300000)
public void testCreateTableNumberOfRegions() throws Exception {
final TableName tableName = TableName.valueOf(name.getMethodName());
HTableDescriptor desc = new HTableDescriptor(tableName);
desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
admin.createTable(desc).join();
List<HRegionLocation> regions;
try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
regions = l.getAllRegionLocations();
assertEquals("Table should have only 1 region", 1, regions.size());
}
final TableName tableName2 = TableName.valueOf(tableName.getNameAsString() + "_2");
desc = new HTableDescriptor(tableName2);
desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
admin.createTable(desc, new byte[][] { new byte[] { 42 } }).join();
try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName2)) {
regions = l.getAllRegionLocations();
assertEquals("Table should have only 2 region", 2, regions.size());
}
final TableName tableName3 = TableName.valueOf(tableName.getNameAsString() + "_3");
desc = new HTableDescriptor(tableName3);
desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
admin.createTable(desc, "a".getBytes(), "z".getBytes(), 3).join();
try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName3)) {
regions = l.getAllRegionLocations();
assertEquals("Table should have only 3 region", 3, regions.size());
}
final TableName tableName4 = TableName.valueOf(tableName.getNameAsString() + "_4");
desc = new HTableDescriptor(tableName4);
desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
try {
admin.createTable(desc, "a".getBytes(), "z".getBytes(), 2).join();
fail("Should not be able to create a table with only 2 regions using this API.");
} catch (CompletionException e) {
assertTrue(e.getCause() instanceof IllegalArgumentException);
}
final TableName tableName5 = TableName.valueOf(tableName.getNameAsString() + "_5");
desc = new HTableDescriptor(tableName5);
desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
admin.createTable(desc, new byte[] { 1 }, new byte[] { 127 }, 16).join();
try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName5)) {
regions = l.getAllRegionLocations();
assertEquals("Table should have 16 region", 16, regions.size());
}
}
@Test(timeout = 300000)
public void testCreateTableWithRegions() throws Exception {
final TableName tableName = TableName.valueOf(name.getMethodName());
byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 }, new byte[] { 3, 3, 3 },
new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 }, new byte[] { 6, 6, 6 },
new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 }, new byte[] { 9, 9, 9 }, };
int expectedRegions = splitKeys.length + 1;
HTableDescriptor desc = new HTableDescriptor(tableName);
desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
admin.createTable(desc, splitKeys).join();
boolean tableAvailable = admin.isTableAvailable(tableName, splitKeys).get();
assertTrue("Table should be created with splitKyes + 1 rows in META", tableAvailable);
List<HRegionLocation> regions;
Iterator<HRegionLocation> hris;
HRegionInfo hri;
ClusterConnection conn = (ClusterConnection) TEST_UTIL.getConnection();
try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
regions = l.getAllRegionLocations();
assertEquals(
"Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
expectedRegions, regions.size());
System.err.println("Found " + regions.size() + " regions");
hris = regions.iterator();
hri = hris.next().getRegionInfo();
assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[0]));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[0]));
assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[1]));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[1]));
assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[2]));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[2]));
assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[3]));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[3]));
assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[4]));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[4]));
assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[5]));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[5]));
assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[6]));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[6]));
assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[7]));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[7]));
assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[8]));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[8]));
assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
verifyRoundRobinDistribution(conn, l, expectedRegions);
}
// Now test using start/end with a number of regions
// Use 80 bit numbers to make sure we aren't limited
byte[] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
byte[] endKey = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
// Splitting into 10 regions, we expect (null,1) ... (9, null)
// with (1,2) (2,3) (3,4) (4,5) (5,6) (6,7) (7,8) (8,9) in the middle
expectedRegions = 10;
final TableName tableName2 = TableName.valueOf(tableName.getNameAsString() + "_2");
desc = new HTableDescriptor(tableName2);
desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
admin.createTable(desc, startKey, endKey, expectedRegions).join();
try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName2)) {
regions = l.getAllRegionLocations();
assertEquals(
"Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
expectedRegions, regions.size());
System.err.println("Found " + regions.size() + " regions");
hris = regions.iterator();
hri = hris.next().getRegionInfo();
assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }));
assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }));
assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }));
assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }));
assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }));
assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }));
assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }));
assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }));
assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }));
hri = hris.next().getRegionInfo();
assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }));
assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
verifyRoundRobinDistribution(conn, l, expectedRegions);
}
// Try once more with something that divides into something infinite
startKey = new byte[] { 0, 0, 0, 0, 0, 0 };
endKey = new byte[] { 1, 0, 0, 0, 0, 0 };
expectedRegions = 5;
final TableName tableName3 = TableName.valueOf(tableName.getNameAsString() + "_3");
desc = new HTableDescriptor(tableName3);
desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
admin.createTable(desc, startKey, endKey, expectedRegions).join();
try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName3)) {
regions = l.getAllRegionLocations();
assertEquals(
"Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
expectedRegions, regions.size());
System.err.println("Found " + regions.size() + " regions");
verifyRoundRobinDistribution(conn, l, expectedRegions);
}
// Try an invalid case where there are duplicate split keys
splitKeys = new byte[][] { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 },
new byte[] { 3, 3, 3 }, new byte[] { 2, 2, 2 } };
final TableName tableName4 = TableName.valueOf(tableName.getNameAsString() + "_4");
desc = new HTableDescriptor(tableName4);
desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
try {
admin.createTable(desc, splitKeys).join();
fail("Should not be able to create this table because of " + "duplicate split keys");
} catch (CompletionException e) {
assertTrue(e.getCause() instanceof IllegalArgumentException);
}
}
private void verifyRoundRobinDistribution(ClusterConnection c, RegionLocator regionLocator,
int expectedRegions) throws IOException {
int numRS = c.getCurrentNrHRS();
List<HRegionLocation> regions = regionLocator.getAllRegionLocations();
Map<ServerName, List<HRegionInfo>> server2Regions = new HashMap<>();
regions.stream().forEach((loc) -> {
ServerName server = loc.getServerName();
server2Regions.computeIfAbsent(server, (s) -> new ArrayList<>()).add(loc.getRegionInfo());
});
if (numRS >= 2) {
// Ignore the master region server,
// which contains less regions by intention.
numRS--;
}
float average = (float) expectedRegions / numRS;
int min = (int) Math.floor(average);
int max = (int) Math.ceil(average);
server2Regions.values().forEach((regionList) -> {
assertTrue(regionList.size() == min || regionList.size() == max);
});
}
@Test(timeout = 300000)
public void testCreateTableWithOnlyEmptyStartRow() throws IOException {
byte[] tableName = Bytes.toBytes(name.getMethodName());
byte[][] splitKeys = new byte[1][];
splitKeys[0] = HConstants.EMPTY_BYTE_ARRAY;
HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
desc.addFamily(new HColumnDescriptor("col"));
try {
admin.createTable(desc, splitKeys).join();
fail("Test case should fail as empty split key is passed.");
} catch (CompletionException e) {
assertTrue(e.getCause() instanceof IllegalArgumentException);
}
}
@Test(timeout = 300000)
public void testCreateTableWithEmptyRowInTheSplitKeys() throws IOException {
byte[] tableName = Bytes.toBytes(name.getMethodName());
byte[][] splitKeys = new byte[3][];
splitKeys[0] = "region1".getBytes();
splitKeys[1] = HConstants.EMPTY_BYTE_ARRAY;
splitKeys[2] = "region2".getBytes();
HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
desc.addFamily(new HColumnDescriptor("col"));
try {
admin.createTable(desc, splitKeys).join();
fail("Test case should fail as empty split key is passed.");
} catch (CompletionException e) {
assertTrue(e.getCause() instanceof IllegalArgumentException);
}
}
@Test(timeout = 300000)
public void testDeleteTable() throws Exception {
final TableName tableName = TableName.valueOf(name.getMethodName());
admin.createTable(new HTableDescriptor(tableName).addFamily(new HColumnDescriptor(FAMILY))).join();
assertTrue(admin.tableExists(tableName).get());
TEST_UTIL.getAdmin().disableTable(tableName);
admin.deleteTable(tableName).join();
assertFalse(admin.tableExists(tableName).get());
}
@Test(timeout = 300000)
public void testDeleteTables() throws Exception {
TableName[] tables = { TableName.valueOf(name.getMethodName() + "1"),
TableName.valueOf(name.getMethodName() + "2"), TableName.valueOf(name.getMethodName() + "3") };
Arrays.stream(tables).map(HTableDescriptor::new)
.map((table) -> table.addFamily(new HColumnDescriptor(FAMILY))).forEach((table) -> {
admin.createTable(table).join();
admin.tableExists(table.getTableName()).thenAccept((exist) -> assertTrue(exist)).join();
try {
TEST_UTIL.getAdmin().disableTable(table.getTableName());
} catch (Exception e) {
}
});
TableDescriptor[] failed = admin.deleteTables(Pattern.compile("testDeleteTables.*")).get();
assertEquals(0, failed.length);
Arrays.stream(tables).forEach((table) -> {
admin.tableExists(table).thenAccept((exist) -> assertFalse(exist)).join();
});
}
@Test(timeout = 300000)
public void testTruncateTable() throws IOException {
testTruncateTable(TableName.valueOf(name.getMethodName()), false);
}
@Test(timeout = 300000)
public void testTruncateTablePreservingSplits() throws IOException {
testTruncateTable(TableName.valueOf(name.getMethodName()), true);
}
private void testTruncateTable(final TableName tableName, boolean preserveSplits)
throws IOException {
byte[][] splitKeys = new byte[2][];
splitKeys[0] = Bytes.toBytes(4);
splitKeys[1] = Bytes.toBytes(8);
// Create & Fill the table
Table table = TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY, splitKeys);
try {
TEST_UTIL.loadNumericRows(table, HConstants.CATALOG_FAMILY, 0, 10);
assertEquals(10, TEST_UTIL.countRows(table));
} finally {
table.close();
}
assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
// Truncate & Verify
TEST_UTIL.getAdmin().disableTable(tableName);
admin.truncateTable(tableName, preserveSplits).join();
table = TEST_UTIL.getConnection().getTable(tableName);
try {
assertEquals(0, TEST_UTIL.countRows(table));
} finally {
table.close();
}
if (preserveSplits) {
assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
} else {
assertEquals(1, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
}
}
@Test(timeout = 300000)
public void testDisableAndEnableTable() throws Exception {
final byte[] row = Bytes.toBytes("row");
final byte[] qualifier = Bytes.toBytes("qualifier");
final byte[] value = Bytes.toBytes("value");
final TableName tableName = TableName.valueOf(name.getMethodName());
Table ht = TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY);
Put put = new Put(row);
put.addColumn(HConstants.CATALOG_FAMILY, qualifier, value);
ht.put(put);
Get get = new Get(row);
get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
ht.get(get);
this.admin.disableTable(ht.getName()).join();
assertTrue("Table must be disabled.", TEST_UTIL.getHBaseCluster().getMaster()
.getTableStateManager().isTableState(ht.getName(), TableState.State.DISABLED));
assertEquals(TableState.State.DISABLED, getStateFromMeta(tableName));
// Test that table is disabled
get = new Get(row);
get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
boolean ok = false;
try {
ht.get(get);
} catch (TableNotEnabledException e) {
ok = true;
}
ok = false;
// verify that scan encounters correct exception
Scan scan = new Scan();
try {
ResultScanner scanner = ht.getScanner(scan);
Result res = null;
do {
res = scanner.next();
} while (res != null);
} catch (TableNotEnabledException e) {
ok = true;
}
assertTrue(ok);
this.admin.enableTable(tableName).join();
assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster().getMaster()
.getTableStateManager().isTableState(ht.getName(), TableState.State.ENABLED));
assertEquals(TableState.State.ENABLED, getStateFromMeta(tableName));
// Test that table is enabled
try {
ht.get(get);
} catch (RetriesExhaustedException e) {
ok = false;
}
assertTrue(ok);
ht.close();
}
@Test(timeout = 300000)
public void testDisableAndEnableTables() throws Exception {
final byte[] row = Bytes.toBytes("row");
final byte[] qualifier = Bytes.toBytes("qualifier");
final byte[] value = Bytes.toBytes("value");
final TableName tableName1 = TableName.valueOf(name.getMethodName() + "1");
final TableName tableName2 = TableName.valueOf(name.getMethodName());
Table ht1 = TEST_UTIL.createTable(tableName1, HConstants.CATALOG_FAMILY);
Table ht2 = TEST_UTIL.createTable(tableName2, HConstants.CATALOG_FAMILY);
Put put = new Put(row);
put.addColumn(HConstants.CATALOG_FAMILY, qualifier, value);
ht1.put(put);
ht2.put(put);
Get get = new Get(row);
get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
ht1.get(get);
ht2.get(get);
this.admin.disableTables("testDisableAndEnableTable.*").join();
// Test that tables are disabled
get = new Get(row);
get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
boolean ok = false;
try {
ht1.get(get);
ht2.get(get);
} catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
ok = true;
}
assertEquals(TableState.State.DISABLED, getStateFromMeta(tableName1));
assertEquals(TableState.State.DISABLED, getStateFromMeta(tableName2));
assertTrue(ok);
this.admin.enableTables("testDisableAndEnableTable.*").join();
// Test that tables are enabled
try {
ht1.get(get);
} catch (IOException e) {
ok = false;
}
try {
ht2.get(get);
} catch (IOException e) {
ok = false;
}
assertTrue(ok);
ht1.close();
ht2.close();
assertEquals(TableState.State.ENABLED, getStateFromMeta(tableName1));
assertEquals(TableState.State.ENABLED, getStateFromMeta(tableName2));
}
@Test(timeout = 300000)
public void testEnableTableRetainAssignment() throws Exception {
final TableName tableName = TableName.valueOf(name.getMethodName());
byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 }, new byte[] { 3, 3, 3 },
new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 }, new byte[] { 6, 6, 6 },
new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 }, new byte[] { 9, 9, 9 } };
int expectedRegions = splitKeys.length + 1;
HTableDescriptor desc = new HTableDescriptor(tableName);
desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
admin.createTable(desc, splitKeys).join();
try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
List<HRegionLocation> regions = l.getAllRegionLocations();
assertEquals(
"Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
expectedRegions, regions.size());
// Disable table.
admin.disableTable(tableName).join();
// Enable table, use retain assignment to assign regions.
admin.enableTable(tableName).join();
List<HRegionLocation> regions2 = l.getAllRegionLocations();
// Check the assignment.
assertEquals(regions.size(), regions2.size());
assertTrue(regions2.containsAll(regions));
}
}
@Test(timeout = 300000)
public void testDisableCatalogTable() throws Exception {
try {
this.admin.disableTable(TableName.META_TABLE_NAME).join();
fail("Expected to throw ConstraintException");
} catch (Exception e) {
}
// Before the fix for HBASE-6146, the below table creation was failing as the hbase:meta table
// actually getting disabled by the disableTable() call.
HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName().getBytes()));
HColumnDescriptor hcd = new HColumnDescriptor("cf1".getBytes());
htd.addFamily(hcd);
admin.createTable(htd).join();
}
@Test
public void testAddColumnFamily() throws IOException {
final TableName tableName = TableName.valueOf(name.getMethodName());
// Create a table with two families
HTableDescriptor baseHtd = new HTableDescriptor(tableName);
baseHtd.addFamily(new HColumnDescriptor(FAMILY_0));
admin.createTable(baseHtd).join();
admin.disableTable(tableName).join();
try {
// Verify the table descriptor
verifyTableDescriptor(tableName, FAMILY_0);
// Modify the table removing one family and verify the descriptor
admin.addColumnFamily(tableName, new HColumnDescriptor(FAMILY_1)).join();
verifyTableDescriptor(tableName, FAMILY_0, FAMILY_1);
} finally {
admin.deleteTable(tableName);
}
}
@Test
public void testAddSameColumnFamilyTwice() throws Exception {
final TableName tableName = TableName.valueOf(name.getMethodName());
// Create a table with one families
HTableDescriptor baseHtd = new HTableDescriptor(tableName);
baseHtd.addFamily(new HColumnDescriptor(FAMILY_0));
admin.createTable(baseHtd).join();
admin.disableTable(tableName).join();
try {
// Verify the table descriptor
verifyTableDescriptor(tableName, FAMILY_0);
// Modify the table removing one family and verify the descriptor
this.admin.addColumnFamily(tableName, new HColumnDescriptor(FAMILY_1)).join();
verifyTableDescriptor(tableName, FAMILY_0, FAMILY_1);
try {
// Add same column family again - expect failure
this.admin.addColumnFamily(tableName, new HColumnDescriptor(FAMILY_1)).join();
Assert.fail("Delete a non-exist column family should fail");
} catch (Exception e) {
// Expected.
}
} finally {
admin.deleteTable(tableName).join();
}
}
@Test
public void testModifyColumnFamily() throws Exception {
final TableName tableName = TableName.valueOf(name.getMethodName());
HColumnDescriptor cfDescriptor = new HColumnDescriptor(FAMILY_0);
int blockSize = cfDescriptor.getBlocksize();
// Create a table with one families
HTableDescriptor baseHtd = new HTableDescriptor(tableName);
baseHtd.addFamily(cfDescriptor);
admin.createTable(baseHtd).join();
admin.disableTable(tableName).join();
try {
// Verify the table descriptor
verifyTableDescriptor(tableName, FAMILY_0);
int newBlockSize = 2 * blockSize;
cfDescriptor.setBlocksize(newBlockSize);
// Modify colymn family
admin.modifyColumnFamily(tableName, cfDescriptor).join();
TableDescriptor htd = admin.getTableDescriptor(tableName).get();
HColumnDescriptor hcfd = htd.getFamily(FAMILY_0);
assertTrue(hcfd.getBlocksize() == newBlockSize);
} finally {
admin.deleteTable(tableName).join();
}
}
@Test
public void testModifyNonExistingColumnFamily() throws IOException {
final TableName tableName = TableName.valueOf(name.getMethodName());
HColumnDescriptor cfDescriptor = new HColumnDescriptor(FAMILY_1);
int blockSize = cfDescriptor.getBlocksize();
// Create a table with one families
HTableDescriptor baseHtd = new HTableDescriptor(tableName);
baseHtd.addFamily(new HColumnDescriptor(FAMILY_0));
admin.createTable(baseHtd).join();
admin.disableTable(tableName).join();
try {
// Verify the table descriptor
verifyTableDescriptor(tableName, FAMILY_0);
int newBlockSize = 2 * blockSize;
cfDescriptor.setBlocksize(newBlockSize);
// Modify a column family that is not in the table.
try {
admin.modifyColumnFamily(tableName, cfDescriptor).join();
Assert.fail("Modify a non-exist column family should fail");
} catch (Exception e) {
// Expected.
}
} finally {
admin.deleteTable(tableName).join();
}
}
@Test
public void testDeleteColumnFamily() throws IOException {
final TableName tableName = TableName.valueOf(name.getMethodName());
// Create a table with two families
HTableDescriptor baseHtd = new HTableDescriptor(tableName);
baseHtd.addFamily(new HColumnDescriptor(FAMILY_0));
baseHtd.addFamily(new HColumnDescriptor(FAMILY_1));
admin.createTable(baseHtd).join();
admin.disableTable(tableName).join();
try {
// Verify the table descriptor
verifyTableDescriptor(tableName, FAMILY_0, FAMILY_1);
// Modify the table removing one family and verify the descriptor
admin.deleteColumnFamily(tableName, FAMILY_1).join();
verifyTableDescriptor(tableName, FAMILY_0);
} finally {
admin.deleteTable(tableName).join();
}
}
@Test
public void testDeleteSameColumnFamilyTwice() throws IOException {
final TableName tableName = TableName.valueOf(name.getMethodName());
// Create a table with two families
HTableDescriptor baseHtd = new HTableDescriptor(tableName);
baseHtd.addFamily(new HColumnDescriptor(FAMILY_0));
baseHtd.addFamily(new HColumnDescriptor(FAMILY_1));
admin.createTable(baseHtd).join();
admin.disableTable(tableName).join();
try {
// Verify the table descriptor
verifyTableDescriptor(tableName, FAMILY_0, FAMILY_1);
// Modify the table removing one family and verify the descriptor
admin.deleteColumnFamily(tableName, FAMILY_1).join();
verifyTableDescriptor(tableName, FAMILY_0);
try {
// Delete again - expect failure
admin.deleteColumnFamily(tableName, FAMILY_1).join();
Assert.fail("Delete a non-exist column family should fail");
} catch (Exception e) {
// Expected.
}
} finally {
admin.deleteTable(tableName).join();
}
}
private void verifyTableDescriptor(final TableName tableName, final byte[]... families)
throws IOException {
Admin admin = TEST_UTIL.getAdmin();
// Verify descriptor from master
HTableDescriptor htd = admin.getTableDescriptor(tableName);
verifyTableDescriptor(htd, tableName, families);
// Verify descriptor from HDFS
MasterFileSystem mfs = TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterFileSystem();
Path tableDir = FSUtils.getTableDir(mfs.getRootDir(), tableName);
HTableDescriptor td = FSTableDescriptors
.getTableDescriptorFromFs(mfs.getFileSystem(), tableDir);
verifyTableDescriptor(td, tableName, families);
}
private void verifyTableDescriptor(final HTableDescriptor htd, final TableName tableName,
final byte[]... families) {
Set<byte[]> htdFamilies = htd.getFamiliesKeys();
assertEquals(tableName, htd.getTableName());
assertEquals(families.length, htdFamilies.size());
for (byte[] familyName : families) {
assertTrue("Expected family " + Bytes.toString(familyName), htdFamilies.contains(familyName));
}
}
@Test
public void testIsTableEnabledAndDisabled() throws Exception {
final TableName table = TableName.valueOf("testIsTableEnabledAndDisabled");
HTableDescriptor desc = new HTableDescriptor(table);
desc.addFamily(new HColumnDescriptor(FAMILY));
admin.createTable(desc).join();
assertTrue(admin.isTableEnabled(table).get());
assertFalse(admin.isTableDisabled(table).get());
admin.disableTable(table).join();
assertFalse(admin.isTableEnabled(table).get());
assertTrue(admin.isTableDisabled(table).get());
admin.deleteTable(table).join();
}
@Test
public void testTableAvailableWithRandomSplitKeys() throws Exception {
TableName tableName = TableName.valueOf("testTableAvailableWithRandomSplitKeys");
HTableDescriptor desc = new HTableDescriptor(tableName);
desc.addFamily(new HColumnDescriptor("col"));
byte[][] splitKeys = new byte[1][];
splitKeys = new byte[][] { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 } };
admin.createTable(desc).join();
boolean tableAvailable = admin.isTableAvailable(tableName, splitKeys).get();
assertFalse("Table should be created with 1 row in META", tableAvailable);
}
}