| /** |
| * 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.Iterator; |
| 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 |
| } |
| } |
| |
| @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); |
| |
| // 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); |
| |
| // 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"); |
| |
| // 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); |
| } |
| } |
| } |