/** * Copyright 2009 The Apache Software Foundation * * 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.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HServerAddress; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.NotServingRegionException; import org.apache.hadoop.hbase.TableExistsException; import org.apache.hadoop.hbase.TableNotDisabledException; import org.apache.hadoop.hbase.TableNotFoundException; import org.apache.hadoop.hbase.executor.EventHandler; import org.apache.hadoop.hbase.executor.EventHandler.EventType; import org.apache.hadoop.hbase.executor.ExecutorService; import org.apache.hadoop.hbase.master.MasterServices; import org.apache.hadoop.hbase.util.Bytes; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; /** * Class to test HBaseAdmin. * Spins up the minicluster once at test start and then takes it down afterward. * Add any testing of HBaseAdmin functionality here. */ public class TestAdmin { final Log LOG = LogFactory.getLog(getClass()); private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); private HBaseAdmin admin; @BeforeClass public static void setUpBeforeClass() throws Exception { TEST_UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100); TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 250); TEST_UTIL.getConfiguration().setInt("hbase.client.retries.number", 6); TEST_UTIL.startMiniCluster(3); } @AfterClass public static void tearDownAfterClass() throws Exception { TEST_UTIL.shutdownMiniCluster(); } @Before public void setUp() throws Exception { this.admin = new HBaseAdmin(TEST_UTIL.getConfiguration()); } @Test public void testDisableAndEnableTable() throws IOException { final byte [] row = Bytes.toBytes("row"); final byte [] qualifier = Bytes.toBytes("qualifier"); final byte [] value = Bytes.toBytes("value"); final byte [] table = Bytes.toBytes("testDisableAndEnableTable"); HTable ht = TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY); Put put = new Put(row); put.add(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(table); // Test that table is disabled get = new Get(row); get.addColumn(HConstants.CATALOG_FAMILY, qualifier); boolean ok = false; try { ht.get(get); } catch (NotServingRegionException e) { ok = true; } catch (RetriesExhaustedException e) { ok = true; } assertTrue(ok); this.admin.enableTable(table); // Test that table is enabled try { ht.get(get); } catch (RetriesExhaustedException e) { ok = false; } assertTrue(ok); } @Test public void testCreateTable() throws IOException { HTableDescriptor [] tables = admin.listTables(); int numTables = tables.length; TEST_UTIL.createTable(Bytes.toBytes("testCreateTable"), HConstants.CATALOG_FAMILY); tables = this.admin.listTables(); assertEquals(numTables + 1, tables.length); } @Test public void testGetTableDescriptor() throws IOException { HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); HColumnDescriptor fam2 = new HColumnDescriptor("fam2"); HColumnDescriptor fam3 = new HColumnDescriptor("fam3"); HTableDescriptor htd = new HTableDescriptor("myTestTable"); htd.addFamily(fam1); htd.addFamily(fam2); htd.addFamily(fam3); this.admin.createTable(htd); HTable table = new HTable(TEST_UTIL.getConfiguration(), "myTestTable"); HTableDescriptor confirmedHtd = table.getTableDescriptor(); assertEquals(htd.compareTo(confirmedHtd), 0); } /** * Verify schema modification takes. * @throws IOException */ @Test public void testChangeTableSchema() throws IOException { final byte [] tableName = Bytes.toBytes("changeTableSchema"); HTableDescriptor [] tables = admin.listTables(); int numTables = tables.length; TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY); tables = this.admin.listTables(); assertEquals(numTables + 1, tables.length); // FIRST, do htabledescriptor changes. HTableDescriptor htd = this.admin.getTableDescriptor(tableName); // Make a copy and assert copy is good. HTableDescriptor copy = new HTableDescriptor(htd); assertTrue(htd.equals(copy)); // Now amend the copy. Introduce differences. long newFlushSize = htd.getMemStoreFlushSize() / 2; copy.setMemStoreFlushSize(newFlushSize); final String key = "anyoldkey"; assertTrue(htd.getValue(key) == null); copy.setValue(key, key); boolean expectedException = false; try { this.admin.modifyTable(tableName, copy); } catch (TableNotDisabledException re) { expectedException = true; } assertTrue(expectedException); this.admin.disableTable(tableName); assertTrue(this.admin.isTableDisabled(tableName)); modifyTable(tableName, copy); HTableDescriptor modifiedHtd = this.admin.getTableDescriptor(tableName); // Assert returned modifiedhcd is same as the copy. assertFalse(htd.equals(modifiedHtd)); assertTrue(copy.equals(modifiedHtd)); assertEquals(newFlushSize, modifiedHtd.getMemStoreFlushSize()); assertEquals(key, modifiedHtd.getValue(key)); // Reenable table to test it fails if not disabled. this.admin.enableTable(tableName); assertFalse(this.admin.isTableDisabled(tableName)); // Now work on column family changes. int countOfFamilies = modifiedHtd.getFamilies().size(); assertTrue(countOfFamilies > 0); HColumnDescriptor hcd = modifiedHtd.getFamilies().iterator().next(); int maxversions = hcd.getMaxVersions(); final int newMaxVersions = maxversions + 1; hcd.setMaxVersions(newMaxVersions); final byte [] hcdName = hcd.getName(); expectedException = false; try { this.admin.modifyColumn(tableName, hcd); } catch (TableNotDisabledException re) { expectedException = true; } assertTrue(expectedException); this.admin.disableTable(tableName); assertTrue(this.admin.isTableDisabled(tableName)); // Modify Column is synchronous this.admin.modifyColumn(tableName, hcd); modifiedHtd = this.admin.getTableDescriptor(tableName); HColumnDescriptor modifiedHcd = modifiedHtd.getFamily(hcdName); assertEquals(newMaxVersions, modifiedHcd.getMaxVersions()); // Try adding a column // Reenable table to test it fails if not disabled. this.admin.enableTable(tableName); assertFalse(this.admin.isTableDisabled(tableName)); final String xtracolName = "xtracol"; HColumnDescriptor xtracol = new HColumnDescriptor(xtracolName); xtracol.setValue(xtracolName, xtracolName); try { this.admin.addColumn(tableName, xtracol); } catch (TableNotDisabledException re) { expectedException = true; } assertTrue(expectedException); this.admin.disableTable(tableName); assertTrue(this.admin.isTableDisabled(tableName)); this.admin.addColumn(tableName, xtracol); modifiedHtd = this.admin.getTableDescriptor(tableName); hcd = modifiedHtd.getFamily(xtracol.getName()); assertTrue(hcd != null); assertTrue(hcd.getValue(xtracolName).equals(xtracolName)); // Delete the just-added column. this.admin.deleteColumn(tableName, xtracol.getName()); modifiedHtd = this.admin.getTableDescriptor(tableName); hcd = modifiedHtd.getFamily(xtracol.getName()); assertTrue(hcd == null); // Delete the table this.admin.deleteTable(tableName); this.admin.listTables(); assertFalse(this.admin.tableExists(tableName)); } /** * Modify table is async so wait on completion of the table operation in master. * @param tableName * @param htd * @throws IOException */ private void modifyTable(final byte [] tableName, final HTableDescriptor htd) throws IOException { MasterServices services = TEST_UTIL.getMiniHBaseCluster().getMaster(); ExecutorService executor = services.getExecutorService(); AtomicBoolean done = new AtomicBoolean(false); executor.registerListener(EventType.C_M_MODIFY_TABLE, new DoneListener(done)); this.admin.modifyTable(tableName, htd); while (!done.get()) { synchronized (done) { try { done.wait(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } executor.unregisterListener(EventType.C_M_MODIFY_TABLE); } /** * Listens for when an event is done in Master. */ static class DoneListener implements EventHandler.EventHandlerListener { private final AtomicBoolean done; DoneListener(final AtomicBoolean done) { super(); this.done = done; } @Override public void afterProcess(EventHandler event) { this.done.set(true); synchronized (this.done) { // Wake anyone waiting on this value to change. this.done.notifyAll(); } } @Override public void beforeProcess(EventHandler event) { // continue } } protected void verifyRoundRobinDistribution(HTable ht, int expectedRegions) throws IOException { int numRS = ht.getCurrentNrHRS(); Map<HRegionInfo,HServerAddress> regions = ht.getRegionsInfo(); Map<HServerAddress, List<HRegionInfo>> server2Regions = new HashMap<HServerAddress, List<HRegionInfo>>(); for (Map.Entry<HRegionInfo,HServerAddress> entry : regions.entrySet()) { HServerAddress server = entry.getValue(); List<HRegionInfo> regs = server2Regions.get(server); if (regs == null) { regs = new ArrayList<HRegionInfo>(); server2Regions.put(server, regs); } regs.add(entry.getKey()); } float average = (float) expectedRegions/numRS; int min = (int)Math.floor(average); int max = (int)Math.ceil(average); for (List<HRegionInfo> regionList : server2Regions.values()) { assertTrue(regionList.size() == min || regionList.size() == max); } } @Test public void testCreateTableWithRegions() throws IOException, InterruptedException { byte[] tableName = Bytes.toBytes("testCreateTableWithRegions"); 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); HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName); Map<HRegionInfo,HServerAddress> regions = ht.getRegionsInfo(); assertEquals("Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(), expectedRegions, regions.size()); System.err.println("Found " + regions.size() + " regions"); Iterator<HRegionInfo> hris = regions.keySet().iterator(); HRegionInfo hri = hris.next(); assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0); assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[0])); hri = hris.next(); assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[0])); assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[1])); hri = hris.next(); assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[1])); assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[2])); hri = hris.next(); assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[2])); assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[3])); hri = hris.next(); assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[3])); assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[4])); hri = hris.next(); assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[4])); assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[5])); hri = hris.next(); assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[5])); assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[6])); hri = hris.next(); assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[6])); assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[7])); hri = hris.next(); assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[7])); assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[8])); hri = hris.next(); assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[8])); assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0); verifyRoundRobinDistribution(ht, 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; byte [] TABLE_2 = Bytes.add(tableName, Bytes.toBytes("_2")); desc = new HTableDescriptor(TABLE_2); desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); admin = new HBaseAdmin(TEST_UTIL.getConfiguration()); admin.createTable(desc, startKey, endKey, expectedRegions); ht = new HTable(TEST_UTIL.getConfiguration(), TABLE_2); regions = ht.getRegionsInfo(); assertEquals("Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(), expectedRegions, regions.size()); System.err.println("Found " + regions.size() + " regions"); hris = regions.keySet().iterator(); hri = hris.next(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(ht, 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; byte [] TABLE_3 = Bytes.add(tableName, Bytes.toBytes("_3")); desc = new HTableDescriptor(TABLE_3); desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); admin = new HBaseAdmin(TEST_UTIL.getConfiguration()); admin.createTable(desc, startKey, endKey, expectedRegions); ht = new HTable(TEST_UTIL.getConfiguration(), TABLE_3); regions = ht.getRegionsInfo(); assertEquals("Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(), expectedRegions, regions.size()); System.err.println("Found " + regions.size() + " regions"); verifyRoundRobinDistribution(ht, 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 } }; byte [] TABLE_4 = Bytes.add(tableName, Bytes.toBytes("_4")); desc = new HTableDescriptor(TABLE_4); desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); admin = new HBaseAdmin(TEST_UTIL.getConfiguration()); try { admin.createTable(desc, splitKeys); assertTrue("Should not be able to create this table because of " + "duplicate split keys", false); } catch(IllegalArgumentException iae) { // Expected } } @Test public void testTableExist() throws IOException { final byte [] table = Bytes.toBytes("testTableExist"); boolean exist = false; exist = this.admin.tableExists(table); assertEquals(false, exist); TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY); exist = this.admin.tableExists(table); assertEquals(true, exist); } /** * Tests forcing split from client and having scanners successfully ride over split. * @throws Exception * @throws IOException */ @Test public void testForceSplit() throws Exception { byte [] familyName = HConstants.CATALOG_FAMILY; byte [] tableName = Bytes.toBytes("testForceSplit"); final HTable table = TEST_UTIL.createTable(tableName, familyName); byte[] k = new byte[3]; int rowCount = 0; for (byte b1 = 'a'; b1 < 'z'; b1++) { for (byte b2 = 'a'; b2 < 'z'; b2++) { for (byte b3 = 'a'; b3 < 'z'; b3++) { k[0] = b1; k[1] = b2; k[2] = b3; Put put = new Put(k); put.add(familyName, new byte[0], k); table.put(put); rowCount++; } } } // get the initial layout (should just be one region) Map<HRegionInfo,HServerAddress> m = table.getRegionsInfo(); System.out.println("Initial regions (" + m.size() + "): " + m); assertTrue(m.size() == 1); // Verify row count Scan scan = new Scan(); ResultScanner scanner = table.getScanner(scan); int rows = 0; for(@SuppressWarnings("unused") Result result : scanner) { rows++; } scanner.close(); assertEquals(rowCount, rows); // Have an outstanding scan going on to make sure we can scan over splits. scan = new Scan(); scanner = table.getScanner(scan); // Scan first row so we are into first region before split happens. scanner.next(); final AtomicInteger count = new AtomicInteger(0); Thread t = new Thread("CheckForSplit") { public void run() { for (int i = 0; i < 20; i++) { try { sleep(1000); } catch (InterruptedException e) { continue; } // check again table = new HTable(conf, tableName); Map<HRegionInfo, HServerAddress> regions = null; try { regions = table.getRegionsInfo(); } catch (IOException e) { e.printStackTrace(); } if (regions == null) continue; count.set(regions.size()); if (count.get() >= 2) break; LOG.debug("Cycle waiting on split"); } } }; t.start(); // Split the table this.admin.split(Bytes.toString(tableName)); t.join(); // Verify row count rows = 1; // We counted one row above. for (@SuppressWarnings("unused") Result result : scanner) { rows++; if (rows > rowCount) { scanner.close(); assertTrue("Scanned more than expected (" + rowCount + ")", false); } } scanner.close(); assertEquals(rowCount, rows); } /** * HADOOP-2156 * @throws IOException */ @Test (expected=IllegalArgumentException.class) public void testEmptyHHTableDescriptor() throws IOException { this.admin.createTable(new HTableDescriptor()); } @Test public void testEnableDisableAddColumnDeleteColumn() throws Exception { byte [] tableName = Bytes.toBytes("testMasterAdmin"); TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY); this.admin.disableTable(tableName); try { new HTable(TEST_UTIL.getConfiguration(), tableName); } catch (org.apache.hadoop.hbase.client.RegionOfflineException e) { // Expected } this.admin.addColumn(tableName, new HColumnDescriptor("col2")); this.admin.enableTable(tableName); try { this.admin.deleteColumn(tableName, Bytes.toBytes("col2")); } catch(TableNotDisabledException e) { // Expected } this.admin.disableTable(tableName); this.admin.deleteColumn(tableName, Bytes.toBytes("col2")); this.admin.deleteTable(tableName); } @Test public void testCreateBadTables() throws IOException { String msg = null; try { this.admin.createTable(HTableDescriptor.ROOT_TABLEDESC); } catch (IllegalArgumentException e) { msg = e.toString(); } assertTrue("Unexcepted exception message " + msg, msg != null && msg.startsWith(IllegalArgumentException.class.getName()) && msg.contains(HTableDescriptor.ROOT_TABLEDESC.getNameAsString())); msg = null; try { this.admin.createTable(HTableDescriptor.META_TABLEDESC); } catch(IllegalArgumentException e) { msg = e.toString(); } assertTrue("Unexcepted exception message " + msg, msg != null && msg.startsWith(IllegalArgumentException.class.getName()) && msg.contains(HTableDescriptor.META_TABLEDESC.getNameAsString())); // Now try and do concurrent creation with a bunch of threads. final HTableDescriptor threadDesc = new HTableDescriptor("threaded_testCreateBadTables"); threadDesc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); int count = 10; Thread [] threads = new Thread [count]; final AtomicInteger successes = new AtomicInteger(0); final AtomicInteger failures = new AtomicInteger(0); final HBaseAdmin localAdmin = this.admin; for (int i = 0; i < count; i++) { threads[i] = new Thread(Integer.toString(i)) { @Override public void run() { try { localAdmin.createTable(threadDesc); successes.incrementAndGet(); } catch (TableExistsException e) { failures.incrementAndGet(); } catch (IOException e) { throw new RuntimeException("Failed threaded create" + getName(), e); } } }; } for (int i = 0; i < count; i++) { threads[i].start(); } for (int i = 0; i < count; i++) { while(threads[i].isAlive()) { try { Thread.sleep(1000); } catch (InterruptedException e) { // continue } } } // All threads are now dead. Count up how many tables were created and // how many failed w/ appropriate exception. assertEquals(1, successes.get()); assertEquals(count - 1, failures.get()); } /** * Test for hadoop-1581 'HBASE: Unopenable tablename bug'. * @throws Exception */ @Test public void testTableNameClash() throws Exception { String name = "testTableNameClash"; admin.createTable(new HTableDescriptor(name + "SOMEUPPERCASE")); admin.createTable(new HTableDescriptor(name)); // Before fix, below would fail throwing a NoServerForRegionException. new HTable(TEST_UTIL.getConfiguration(), name); } /** * Test read only tables * @throws Exception */ @Test public void testReadOnlyTable() throws Exception { byte [] name = Bytes.toBytes("testReadOnlyTable"); HTable table = TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY); byte[] value = Bytes.toBytes("somedata"); // This used to use an empty row... That must have been a bug Put put = new Put(value); put.add(HConstants.CATALOG_FAMILY, HConstants.CATALOG_FAMILY, value); table.put(put); } /** * Test that user table names can contain '-' and '.' so long as they do not * start with same. HBASE-771 * @throws IOException */ @Test public void testTableNames() throws IOException { byte[][] illegalNames = new byte[][] { Bytes.toBytes("-bad"), Bytes.toBytes(".bad"), HConstants.ROOT_TABLE_NAME, HConstants.META_TABLE_NAME }; for (int i = 0; i < illegalNames.length; i++) { try { new HTableDescriptor(illegalNames[i]); throw new IOException("Did not detect '" + Bytes.toString(illegalNames[i]) + "' as an illegal user table name"); } catch (IllegalArgumentException e) { // expected } } byte[] legalName = Bytes.toBytes("g-oo.d"); try { new HTableDescriptor(legalName); } catch (IllegalArgumentException e) { throw new IOException("Legal user table name: '" + Bytes.toString(legalName) + "' caused IllegalArgumentException: " + e.getMessage()); } } /** * For HADOOP-2579 * @throws IOException */ @Test (expected=TableExistsException.class) public void testTableNotFoundExceptionWithATable() throws IOException { final byte [] name = Bytes.toBytes("testTableNotFoundExceptionWithATable"); TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY); TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY); } /** * For HADOOP-2579 * @throws IOException */ @Test (expected=TableNotFoundException.class) public void testTableNotFoundExceptionWithoutAnyTables() throws IOException { new HTable(TEST_UTIL.getConfiguration(), "testTableNotFoundExceptionWithoutAnyTables"); } @Test public void testHundredsOfTable() throws IOException{ final int times = 100; HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); HColumnDescriptor fam2 = new HColumnDescriptor("fam2"); HColumnDescriptor fam3 = new HColumnDescriptor("fam3"); for(int i = 0; i < times; i++) { HTableDescriptor htd = new HTableDescriptor("table"+i); htd.addFamily(fam1); htd.addFamily(fam2); htd.addFamily(fam3); this.admin.createTable(htd); } for(int i = 0; i < times; i++) { String tableName = "table"+i; this.admin.disableTable(tableName); byte [] tableNameBytes = Bytes.toBytes(tableName); assertTrue(this.admin.isTableDisabled(tableNameBytes)); this.admin.enableTable(tableName); assertFalse(this.admin.isTableDisabled(tableNameBytes)); this.admin.disableTable(tableName); assertTrue(this.admin.isTableDisabled(tableNameBytes)); this.admin.deleteTable(tableName); } } }