View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.client;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertNotNull;
24  import static org.junit.Assert.assertTrue;
25  import static org.junit.Assert.fail;
26  
27  import java.io.IOException;
28  import java.util.ArrayList;
29  import java.util.HashMap;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Set;
34  import java.util.concurrent.atomic.AtomicInteger;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.apache.hadoop.hbase.HBaseTestingUtility;
39  import org.apache.hadoop.hbase.HColumnDescriptor;
40  import org.apache.hadoop.hbase.HConstants;
41  import org.apache.hadoop.hbase.HRegionInfo;
42  import org.apache.hadoop.hbase.HTableDescriptor;
43  import org.apache.hadoop.hbase.InvalidFamilyOperationException;
44  import org.apache.hadoop.hbase.MetaTableAccessor;
45  import org.apache.hadoop.hbase.ServerName;
46  import org.apache.hadoop.hbase.TableName;
47  import org.apache.hadoop.hbase.TableNotDisabledException;
48  import org.apache.hadoop.hbase.TableNotEnabledException;
49  import org.apache.hadoop.hbase.TableNotFoundException;
50  import org.apache.hadoop.hbase.Waiter;
51  import org.apache.hadoop.hbase.exceptions.MergeRegionException;
52  import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
53  import org.apache.hadoop.hbase.testclassification.LargeTests;
54  import org.apache.hadoop.hbase.util.Bytes;
55  import org.apache.hadoop.hbase.util.FSUtils;
56  import org.apache.hadoop.hbase.zookeeper.ZKTableStateClientSideReader;
57  import org.apache.hadoop.hbase.master.AssignmentManager;
58  import org.apache.hadoop.hbase.master.RegionState;
59  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
60  import org.apache.hadoop.hbase.protobuf.RequestConverter;
61  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.AdminService;
62  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DispatchMergingRegionsRequest;
63  import org.apache.hadoop.hbase.regionserver.HRegion;
64  import org.apache.hadoop.hbase.regionserver.Store;
65  import org.apache.hadoop.hbase.regionserver.StoreFile;
66  import org.apache.hadoop.hbase.util.Pair;
67  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
68  import org.junit.After;
69  import org.junit.AfterClass;
70  import org.junit.Before;
71  import org.junit.BeforeClass;
72  import org.junit.Test;
73  import org.junit.experimental.categories.Category;
74  
75  import com.google.protobuf.ServiceException;
76  
77  /**
78   * Class to test HBaseAdmin.
79   * Spins up the minicluster once at test start and then takes it down afterward.
80   * Add any testing of HBaseAdmin functionality here.
81   */
82  @Category(LargeTests.class)
83  public class TestAdmin1 {
84    private static final Log LOG = LogFactory.getLog(TestAdmin1.class);
85    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
86    private Admin admin;
87  
88    @BeforeClass
89    public static void setUpBeforeClass() throws Exception {
90      TEST_UTIL.getConfiguration().setBoolean("hbase.online.schema.update.enable", true);
91      TEST_UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100);
92      TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 250);
93      TEST_UTIL.getConfiguration().setInt("hbase.client.retries.number", 6);
94      TEST_UTIL.getConfiguration().setBoolean(
95          "hbase.master.enabletable.roundrobin", true);
96      TEST_UTIL.startMiniCluster(3);
97    }
98  
99    @AfterClass
100   public static void tearDownAfterClass() throws Exception {
101     TEST_UTIL.shutdownMiniCluster();
102   }
103 
104   @Before
105   public void setUp() throws Exception {
106     this.admin = TEST_UTIL.getHBaseAdmin();
107   }
108 
109   @After
110   public void tearDown() throws Exception {
111     for (HTableDescriptor htd : this.admin.listTables()) {
112       TEST_UTIL.deleteTable(htd.getName());
113     }
114   }
115 
116   @Test (timeout=300000)
117   public void testSplitFlushCompactUnknownTable() throws InterruptedException {
118     final TableName unknowntable = TableName.valueOf("fubar");
119     Exception exception = null;
120     try {
121       this.admin.compact(unknowntable);
122     } catch (IOException e) {
123       exception = e;
124     }
125     assertTrue(exception instanceof TableNotFoundException);
126 
127     exception = null;
128     try {
129       this.admin.flush(unknowntable);
130     } catch (IOException e) {
131       exception = e;
132     }
133     assertTrue(exception instanceof TableNotFoundException);
134 
135     exception = null;
136     try {
137       this.admin.split(unknowntable);
138     } catch (IOException e) {
139       exception = e;
140     }
141     assertTrue(exception instanceof TableNotFoundException);
142   }
143 
144   @Test (timeout=300000)
145   public void testDeleteEditUnknownColumnFamilyAndOrTable() throws IOException {
146     // Test we get exception if we try to
147     final TableName nonexistentTable = TableName.valueOf("nonexistent");
148     final byte[] nonexistentColumn = Bytes.toBytes("nonexistent");
149     HColumnDescriptor nonexistentHcd = new HColumnDescriptor(nonexistentColumn);
150     Exception exception = null;
151     try {
152       this.admin.addColumn(nonexistentTable, nonexistentHcd);
153     } catch (IOException e) {
154       exception = e;
155     }
156     assertTrue(exception instanceof TableNotFoundException);
157 
158     exception = null;
159     try {
160       this.admin.deleteTable(nonexistentTable);
161     } catch (IOException e) {
162       exception = e;
163     }
164     assertTrue(exception instanceof TableNotFoundException);
165 
166     exception = null;
167     try {
168       this.admin.deleteColumn(nonexistentTable, nonexistentColumn);
169     } catch (IOException e) {
170       exception = e;
171     }
172     assertTrue(exception instanceof TableNotFoundException);
173 
174     exception = null;
175     try {
176       this.admin.disableTable(nonexistentTable);
177     } catch (IOException e) {
178       exception = e;
179     }
180     assertTrue(exception instanceof TableNotFoundException);
181 
182     exception = null;
183     try {
184       this.admin.enableTable(nonexistentTable);
185     } catch (IOException e) {
186       exception = e;
187     }
188     assertTrue(exception instanceof TableNotFoundException);
189 
190     exception = null;
191     try {
192       this.admin.modifyColumn(nonexistentTable, nonexistentHcd);
193     } catch (IOException e) {
194       exception = e;
195     }
196     assertTrue(exception instanceof TableNotFoundException);
197 
198     exception = null;
199     try {
200       HTableDescriptor htd = new HTableDescriptor(nonexistentTable);
201       htd.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
202       this.admin.modifyTable(htd.getTableName(), htd);
203     } catch (IOException e) {
204       exception = e;
205     }
206     assertTrue(exception instanceof TableNotFoundException);
207 
208     // Now make it so at least the table exists and then do tests against a
209     // nonexistent column family -- see if we get right exceptions.
210     final TableName tableName =
211       TableName.valueOf("testDeleteEditUnknownColumnFamilyAndOrTable" + System.currentTimeMillis());
212     HTableDescriptor htd = new HTableDescriptor(tableName);
213     htd.addFamily(new HColumnDescriptor("cf"));
214     this.admin.createTable(htd);
215     try {
216       exception = null;
217       try {
218         this.admin.deleteColumn(htd.getTableName(), nonexistentHcd.getName());
219       } catch (IOException e) {
220         exception = e;
221       }
222       assertNotNull(exception);
223       assertTrue("found=" + exception.getClass().getName(),
224           exception instanceof InvalidFamilyOperationException);
225 
226       exception = null;
227       try {
228         this.admin.modifyColumn(htd.getTableName(), nonexistentHcd);
229       } catch (IOException e) {
230         exception = e;
231       }
232       assertNotNull(exception);
233       assertTrue("found=" + exception.getClass().getName(),
234           exception instanceof InvalidFamilyOperationException);
235     } finally {
236       this.admin.disableTable(tableName);
237       this.admin.deleteTable(tableName);
238     }
239   }
240 
241   @Test (timeout=300000)
242   public void testDisableAndEnableTable() throws IOException {
243     final byte [] row = Bytes.toBytes("row");
244     final byte [] qualifier = Bytes.toBytes("qualifier");
245     final byte [] value = Bytes.toBytes("value");
246     final TableName table = TableName.valueOf("testDisableAndEnableTable");
247     Table ht = TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
248     Put put = new Put(row);
249     put.add(HConstants.CATALOG_FAMILY, qualifier, value);
250     ht.put(put);
251     Get get = new Get(row);
252     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
253     ht.get(get);
254 
255     this.admin.disableTable(ht.getName());
256     assertTrue("Table must be disabled.", TEST_UTIL.getHBaseCluster()
257         .getMaster().getAssignmentManager().getTableStateManager().isTableState(
258         ht.getName(), ZooKeeperProtos.Table.State.DISABLED));
259 
260     // Test that table is disabled
261     get = new Get(row);
262     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
263     boolean ok = false;
264     try {
265       ht.get(get);
266     } catch (TableNotEnabledException e) {
267       ok = true;
268     }
269     ok = false;
270     // verify that scan encounters correct exception
271     Scan scan = new Scan();
272     try {
273       ResultScanner scanner = ht.getScanner(scan);
274       Result res = null;
275       do {
276         res = scanner.next();
277       } while (res != null);
278     } catch (TableNotEnabledException e) {
279       ok = true;
280     }
281     assertTrue(ok);
282     this.admin.enableTable(table);
283     assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster()
284         .getMaster().getAssignmentManager().getTableStateManager().isTableState(
285         ht.getName(), ZooKeeperProtos.Table.State.ENABLED));
286 
287     // Test that table is enabled
288     try {
289       ht.get(get);
290     } catch (RetriesExhaustedException e) {
291       ok = false;
292     }
293     assertTrue(ok);
294     ht.close();
295   }
296 
297   @Test (timeout=300000)
298   public void testDisableAndEnableTables() throws IOException {
299     final byte [] row = Bytes.toBytes("row");
300     final byte [] qualifier = Bytes.toBytes("qualifier");
301     final byte [] value = Bytes.toBytes("value");
302     final TableName table1 = TableName.valueOf("testDisableAndEnableTable1");
303     final TableName table2 = TableName.valueOf("testDisableAndEnableTable2");
304     Table ht1 = TEST_UTIL.createTable(table1, HConstants.CATALOG_FAMILY);
305     Table ht2 = TEST_UTIL.createTable(table2, HConstants.CATALOG_FAMILY);
306     Put put = new Put(row);
307     put.add(HConstants.CATALOG_FAMILY, qualifier, value);
308     ht1.put(put);
309     ht2.put(put);
310     Get get = new Get(row);
311     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
312     ht1.get(get);
313     ht2.get(get);
314 
315     this.admin.disableTables("testDisableAndEnableTable.*");
316 
317     // Test that tables are disabled
318     get = new Get(row);
319     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
320     boolean ok = false;
321     try {
322       ht1.get(get);
323       ht2.get(get);
324     } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
325       ok = true;
326     }
327 
328     assertTrue(ok);
329     this.admin.enableTables("testDisableAndEnableTable.*");
330 
331     // Test that tables are enabled
332     try {
333       ht1.get(get);
334     } catch (IOException e) {
335       ok = false;
336     }
337     try {
338       ht2.get(get);
339     } catch (IOException e) {
340       ok = false;
341     }
342     assertTrue(ok);
343 
344     ht1.close();
345     ht2.close();
346   }
347 
348   @Test (timeout=300000)
349   public void testCreateTable() throws IOException {
350     HTableDescriptor [] tables = admin.listTables();
351     int numTables = tables.length;
352     TEST_UTIL.createTable(TableName.valueOf("testCreateTable"), HConstants.CATALOG_FAMILY).close();
353     tables = this.admin.listTables();
354     assertEquals(numTables + 1, tables.length);
355     assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster()
356         .getMaster().getAssignmentManager().getTableStateManager().isTableState(
357         TableName.valueOf("testCreateTable"), ZooKeeperProtos.Table.State.ENABLED));
358   }
359 
360   @Test (timeout=300000)
361   public void testTruncateTable() throws IOException {
362     testTruncateTable(TableName.valueOf("testTruncateTable"), false);
363   }
364 
365   @Test (timeout=300000)
366   public void testTruncateTablePreservingSplits() throws IOException {
367     testTruncateTable(TableName.valueOf("testTruncateTablePreservingSplits"), true);
368   }
369 
370   private void testTruncateTable(final TableName tableName, boolean preserveSplits)
371       throws IOException {
372     byte[][] splitKeys = new byte[2][];
373     splitKeys[0] = Bytes.toBytes(4);
374     splitKeys[1] = Bytes.toBytes(8);
375 
376     // Create & Fill the table
377     HTable table = TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY, splitKeys);
378     try {
379       TEST_UTIL.loadNumericRows(table, HConstants.CATALOG_FAMILY, 0, 10);
380       assertEquals(10, TEST_UTIL.countRows(table));
381     } finally {
382       table.close();
383     }
384     assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
385 
386     // Truncate & Verify
387     this.admin.disableTable(tableName);
388     this.admin.truncateTable(tableName, preserveSplits);
389     table = new HTable(TEST_UTIL.getConfiguration(), tableName);
390     try {
391       assertEquals(0, TEST_UTIL.countRows(table));
392     } finally {
393       table.close();
394     }
395     if (preserveSplits) {
396       assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
397     } else {
398       assertEquals(1, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
399     }
400   }
401 
402   @Test (timeout=300000)
403   public void testGetTableDescriptor() throws IOException {
404     HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
405     HColumnDescriptor fam2 = new HColumnDescriptor("fam2");
406     HColumnDescriptor fam3 = new HColumnDescriptor("fam3");
407     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf("myTestTable"));
408     htd.addFamily(fam1);
409     htd.addFamily(fam2);
410     htd.addFamily(fam3);
411     this.admin.createTable(htd);
412     Table table = new HTable(TEST_UTIL.getConfiguration(), htd.getTableName());
413     HTableDescriptor confirmedHtd = table.getTableDescriptor();
414     assertEquals(htd.compareTo(confirmedHtd), 0);
415     table.close();
416   }
417 
418   @Test (timeout=300000)
419   public void testCompactionTimestamps() throws Exception {
420     HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
421     TableName tableName = TableName.valueOf("testCompactionTimestampsTable");
422     HTableDescriptor htd = new HTableDescriptor(tableName);
423     htd.addFamily(fam1);
424     this.admin.createTable(htd);
425     HTable table = (HTable)TEST_UTIL.getConnection().getTable(htd.getTableName());
426     long ts = this.admin.getLastMajorCompactionTimestamp(tableName);
427     assertEquals(0, ts);
428     Put p = new Put(Bytes.toBytes("row1"));
429     p.add(Bytes.toBytes("fam1"), Bytes.toBytes("fam1"), Bytes.toBytes("fam1"));
430     table.put(p);
431     ts = this.admin.getLastMajorCompactionTimestamp(tableName);
432     // no files written -> no data
433     assertEquals(0, ts);
434 
435     this.admin.flush(tableName);
436     ts = this.admin.getLastMajorCompactionTimestamp(tableName);
437     // still 0, we flushed a file, but no major compaction happened
438     assertEquals(0, ts);
439 
440     byte[] regionName =
441         table.getRegionLocator().getAllRegionLocations().get(0).getRegionInfo().getRegionName();
442     long ts1 = this.admin.getLastMajorCompactionTimestampForRegion(regionName);
443     assertEquals(ts, ts1);
444     p = new Put(Bytes.toBytes("row2"));
445     p.add(Bytes.toBytes("fam1"), Bytes.toBytes("fam1"), Bytes.toBytes("fam1"));
446     table.put(p);
447     this.admin.flush(tableName);
448     ts = this.admin.getLastMajorCompactionTimestamp(tableName);
449     // make sure the region API returns the same value, as the old file is still around
450     assertEquals(ts1, ts);
451 
452     TEST_UTIL.compact(tableName, true);
453     table.put(p);
454     // forces a wait for the compaction
455     this.admin.flush(tableName);
456     ts = this.admin.getLastMajorCompactionTimestamp(tableName);
457     // after a compaction our earliest timestamp will have progressed forward
458     assertTrue(ts > ts1);
459 
460     // region api still the same
461     ts1 = this.admin.getLastMajorCompactionTimestampForRegion(regionName);
462     assertEquals(ts, ts1);
463     table.put(p);
464     this.admin.flush(tableName);
465     ts = this.admin.getLastMajorCompactionTimestamp(tableName);
466     assertEquals(ts, ts1);
467     table.close();
468   }
469 
470   @Test (timeout=300000)
471   public void testHColumnValidName() {
472        boolean exceptionThrown;
473        try {
474          new HColumnDescriptor("\\test\\abc");
475        } catch(IllegalArgumentException iae) {
476            exceptionThrown = true;
477            assertTrue(exceptionThrown);
478        }
479    }
480 
481   /**
482    * Verify schema modification takes.
483    * @throws IOException
484    * @throws InterruptedException
485    */
486   @Test (timeout=300000)
487   public void testOnlineChangeTableSchema() throws IOException, InterruptedException {
488     final TableName tableName =
489         TableName.valueOf("changeTableSchemaOnline");
490     TEST_UTIL.getMiniHBaseCluster().getMaster().getConfiguration().setBoolean(
491         "hbase.online.schema.update.enable", true);
492     HTableDescriptor [] tables = admin.listTables();
493     int numTables = tables.length;
494     TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
495     tables = this.admin.listTables();
496     assertEquals(numTables + 1, tables.length);
497 
498     // FIRST, do htabledescriptor changes.
499     HTableDescriptor htd = this.admin.getTableDescriptor(tableName);
500     // Make a copy and assert copy is good.
501     HTableDescriptor copy = new HTableDescriptor(htd);
502     assertTrue(htd.equals(copy));
503     // Now amend the copy. Introduce differences.
504     long newFlushSize = htd.getMemStoreFlushSize() / 2;
505     if (newFlushSize <=0) {
506       newFlushSize = HTableDescriptor.DEFAULT_MEMSTORE_FLUSH_SIZE / 2;
507     }
508     copy.setMemStoreFlushSize(newFlushSize);
509     final String key = "anyoldkey";
510     assertTrue(htd.getValue(key) == null);
511     copy.setValue(key, key);
512     boolean expectedException = false;
513     try {
514       admin.modifyTable(tableName, copy);
515     } catch (TableNotDisabledException re) {
516       expectedException = true;
517     }
518     assertFalse(expectedException);
519     HTableDescriptor modifiedHtd = this.admin.getTableDescriptor(tableName);
520     assertFalse(htd.equals(modifiedHtd));
521     assertTrue(copy.equals(modifiedHtd));
522     assertEquals(newFlushSize, modifiedHtd.getMemStoreFlushSize());
523     assertEquals(key, modifiedHtd.getValue(key));
524 
525     // Now work on column family changes.
526     int countOfFamilies = modifiedHtd.getFamilies().size();
527     assertTrue(countOfFamilies > 0);
528     HColumnDescriptor hcd = modifiedHtd.getFamilies().iterator().next();
529     int maxversions = hcd.getMaxVersions();
530     final int newMaxVersions = maxversions + 1;
531     hcd.setMaxVersions(newMaxVersions);
532     final byte [] hcdName = hcd.getName();
533     expectedException = false;
534     try {
535       this.admin.modifyColumn(tableName, hcd);
536     } catch (TableNotDisabledException re) {
537       expectedException = true;
538     }
539     assertFalse(expectedException);
540     modifiedHtd = this.admin.getTableDescriptor(tableName);
541     HColumnDescriptor modifiedHcd = modifiedHtd.getFamily(hcdName);
542     assertEquals(newMaxVersions, modifiedHcd.getMaxVersions());
543 
544     // Try adding a column
545     assertFalse(this.admin.isTableDisabled(tableName));
546     final String xtracolName = "xtracol";
547     HColumnDescriptor xtracol = new HColumnDescriptor(xtracolName);
548     xtracol.setValue(xtracolName, xtracolName);
549     expectedException = false;
550     try {
551       this.admin.addColumn(tableName, xtracol);
552     } catch (TableNotDisabledException re) {
553       expectedException = true;
554     }
555     // Add column should work even if the table is enabled
556     assertFalse(expectedException);
557     modifiedHtd = this.admin.getTableDescriptor(tableName);
558     hcd = modifiedHtd.getFamily(xtracol.getName());
559     assertTrue(hcd != null);
560     assertTrue(hcd.getValue(xtracolName).equals(xtracolName));
561 
562     // Delete the just-added column.
563     this.admin.deleteColumn(tableName, xtracol.getName());
564     modifiedHtd = this.admin.getTableDescriptor(tableName);
565     hcd = modifiedHtd.getFamily(xtracol.getName());
566     assertTrue(hcd == null);
567 
568     // Delete the table
569     this.admin.disableTable(tableName);
570     this.admin.deleteTable(tableName);
571     this.admin.listTables();
572     assertFalse(this.admin.tableExists(tableName));
573   }
574 
575   @Test (timeout=300000)
576   public void testShouldFailOnlineSchemaUpdateIfOnlineSchemaIsNotEnabled()
577       throws Exception {
578     final TableName tableName = TableName.valueOf("changeTableSchemaOnlineFailure");
579     TEST_UTIL.getMiniHBaseCluster().getMaster().getConfiguration().setBoolean(
580         "hbase.online.schema.update.enable", false);
581     HTableDescriptor[] tables = admin.listTables();
582     int numTables = tables.length;
583     TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
584     tables = this.admin.listTables();
585     assertEquals(numTables + 1, tables.length);
586 
587     // FIRST, do htabledescriptor changes.
588     HTableDescriptor htd = this.admin.getTableDescriptor(tableName);
589     // Make a copy and assert copy is good.
590     HTableDescriptor copy = new HTableDescriptor(htd);
591     assertTrue(htd.equals(copy));
592     // Now amend the copy. Introduce differences.
593     long newFlushSize = htd.getMemStoreFlushSize() / 2;
594     if (newFlushSize <=0) {
595       newFlushSize = HTableDescriptor.DEFAULT_MEMSTORE_FLUSH_SIZE / 2;
596     }
597     copy.setMemStoreFlushSize(newFlushSize);
598     final String key = "anyoldkey";
599     assertTrue(htd.getValue(key) == null);
600     copy.setValue(key, key);
601     boolean expectedException = false;
602     try {
603       admin.modifyTable(tableName, copy);
604     } catch (TableNotDisabledException re) {
605       expectedException = true;
606     }
607     assertTrue("Online schema update should not happen.", expectedException);
608 
609     // Reset the value for the other tests
610     TEST_UTIL.getMiniHBaseCluster().getMaster().getConfiguration().setBoolean(
611         "hbase.online.schema.update.enable", true);
612   }
613 
614   @SuppressWarnings("deprecation")
615   protected void verifyRoundRobinDistribution(HTable ht, int expectedRegions) throws IOException {
616     int numRS = ht.getConnection().getCurrentNrHRS();
617     Map<HRegionInfo, ServerName> regions = ht.getRegionLocations();
618     Map<ServerName, List<HRegionInfo>> server2Regions = new HashMap<ServerName, List<HRegionInfo>>();
619     for (Map.Entry<HRegionInfo, ServerName> entry : regions.entrySet()) {
620       ServerName server = entry.getValue();
621       List<HRegionInfo> regs = server2Regions.get(server);
622       if (regs == null) {
623         regs = new ArrayList<HRegionInfo>();
624         server2Regions.put(server, regs);
625       }
626       regs.add(entry.getKey());
627     }
628     float average = (float) expectedRegions/numRS;
629     int min = (int)Math.floor(average);
630     int max = (int)Math.ceil(average);
631     for (List<HRegionInfo> regionList : server2Regions.values()) {
632       assertTrue(regionList.size() == min || regionList.size() == max);
633     }
634   }
635 
636   @Test (timeout=300000)
637   public void testCreateTableNumberOfRegions() throws IOException, InterruptedException {
638     TableName tableName = TableName.valueOf("testCreateTableNumberOfRegions");
639     HTableDescriptor desc = new HTableDescriptor(tableName);
640     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
641     admin.createTable(desc);
642     HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName);
643     Map<HRegionInfo, ServerName> regions = ht.getRegionLocations();
644     assertEquals("Table should have only 1 region", 1, regions.size());
645     ht.close();
646 
647     TableName TABLE_2 = TableName.valueOf(tableName.getNameAsString() + "_2");
648     desc = new HTableDescriptor(TABLE_2);
649     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
650     admin.createTable(desc, new byte[][]{new byte[]{42}});
651     HTable ht2 = new HTable(TEST_UTIL.getConfiguration(), TABLE_2);
652     regions = ht2.getRegionLocations();
653     assertEquals("Table should have only 2 region", 2, regions.size());
654     ht2.close();
655 
656     TableName TABLE_3 = TableName.valueOf(tableName.getNameAsString() + "_3");
657     desc = new HTableDescriptor(TABLE_3);
658     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
659     admin.createTable(desc, "a".getBytes(), "z".getBytes(), 3);
660     HTable ht3 = new HTable(TEST_UTIL.getConfiguration(), TABLE_3);
661     regions = ht3.getRegionLocations();
662     assertEquals("Table should have only 3 region", 3, regions.size());
663     ht3.close();
664 
665     TableName TABLE_4 = TableName.valueOf(tableName.getNameAsString() + "_4");
666     desc = new HTableDescriptor(TABLE_4);
667     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
668     try {
669       admin.createTable(desc, "a".getBytes(), "z".getBytes(), 2);
670       fail("Should not be able to create a table with only 2 regions using this API.");
671     } catch (IllegalArgumentException eae) {
672     // Expected
673     }
674 
675     TableName TABLE_5 = TableName.valueOf(tableName.getNameAsString() + "_5");
676     desc = new HTableDescriptor(TABLE_5);
677     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
678     admin.createTable(desc, new byte[] {1}, new byte[] {127}, 16);
679     HTable ht5 = new HTable(TEST_UTIL.getConfiguration(), TABLE_5);
680     regions = ht5.getRegionLocations();
681     assertEquals("Table should have 16 region", 16, regions.size());
682     ht5.close();
683   }
684 
685   @Test (timeout=300000)
686   public void testCreateTableWithRegions() throws IOException, InterruptedException {
687 
688     TableName tableName = TableName.valueOf("testCreateTableWithRegions");
689 
690     byte [][] splitKeys = {
691         new byte [] { 1, 1, 1 },
692         new byte [] { 2, 2, 2 },
693         new byte [] { 3, 3, 3 },
694         new byte [] { 4, 4, 4 },
695         new byte [] { 5, 5, 5 },
696         new byte [] { 6, 6, 6 },
697         new byte [] { 7, 7, 7 },
698         new byte [] { 8, 8, 8 },
699         new byte [] { 9, 9, 9 },
700     };
701     int expectedRegions = splitKeys.length + 1;
702 
703     HTableDescriptor desc = new HTableDescriptor(tableName);
704     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
705     admin.createTable(desc, splitKeys);
706 
707     boolean tableAvailable = admin.isTableAvailable(tableName, splitKeys);
708     assertTrue("Table should be created with splitKyes + 1 rows in META", tableAvailable);
709 
710     HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName);
711     Map<HRegionInfo, ServerName> regions = ht.getRegionLocations();
712     assertEquals("Tried to create " + expectedRegions + " regions " +
713         "but only found " + regions.size(),
714         expectedRegions, regions.size());
715     System.err.println("Found " + regions.size() + " regions");
716 
717     Iterator<HRegionInfo> hris = regions.keySet().iterator();
718     HRegionInfo hri = hris.next();
719     assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
720     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[0]));
721     hri = hris.next();
722     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[0]));
723     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[1]));
724     hri = hris.next();
725     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[1]));
726     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[2]));
727     hri = hris.next();
728     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[2]));
729     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[3]));
730     hri = hris.next();
731     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[3]));
732     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[4]));
733     hri = hris.next();
734     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[4]));
735     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[5]));
736     hri = hris.next();
737     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[5]));
738     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[6]));
739     hri = hris.next();
740     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[6]));
741     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[7]));
742     hri = hris.next();
743     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[7]));
744     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[8]));
745     hri = hris.next();
746     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[8]));
747     assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
748 
749     verifyRoundRobinDistribution(ht, expectedRegions);
750     ht.close();
751 
752     // Now test using start/end with a number of regions
753 
754     // Use 80 bit numbers to make sure we aren't limited
755     byte [] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
756     byte [] endKey =   { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
757 
758     // Splitting into 10 regions, we expect (null,1) ... (9, null)
759     // with (1,2) (2,3) (3,4) (4,5) (5,6) (6,7) (7,8) (8,9) in the middle
760 
761     expectedRegions = 10;
762 
763     TableName TABLE_2 = TableName.valueOf(tableName.getNameAsString() + "_2");
764 
765     desc = new HTableDescriptor(TABLE_2);
766     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
767     admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
768     admin.createTable(desc, startKey, endKey, expectedRegions);
769 
770     HTable ht2 = new HTable(TEST_UTIL.getConfiguration(), TABLE_2);
771     regions = ht2.getRegionLocations();
772     assertEquals("Tried to create " + expectedRegions + " regions " +
773         "but only found " + regions.size(),
774         expectedRegions, regions.size());
775     System.err.println("Found " + regions.size() + " regions");
776 
777     hris = regions.keySet().iterator();
778     hri = hris.next();
779     assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
780     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {1,1,1,1,1,1,1,1,1,1}));
781     hri = hris.next();
782     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {1,1,1,1,1,1,1,1,1,1}));
783     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {2,2,2,2,2,2,2,2,2,2}));
784     hri = hris.next();
785     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {2,2,2,2,2,2,2,2,2,2}));
786     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {3,3,3,3,3,3,3,3,3,3}));
787     hri = hris.next();
788     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {3,3,3,3,3,3,3,3,3,3}));
789     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {4,4,4,4,4,4,4,4,4,4}));
790     hri = hris.next();
791     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {4,4,4,4,4,4,4,4,4,4}));
792     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {5,5,5,5,5,5,5,5,5,5}));
793     hri = hris.next();
794     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {5,5,5,5,5,5,5,5,5,5}));
795     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {6,6,6,6,6,6,6,6,6,6}));
796     hri = hris.next();
797     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {6,6,6,6,6,6,6,6,6,6}));
798     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {7,7,7,7,7,7,7,7,7,7}));
799     hri = hris.next();
800     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {7,7,7,7,7,7,7,7,7,7}));
801     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {8,8,8,8,8,8,8,8,8,8}));
802     hri = hris.next();
803     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {8,8,8,8,8,8,8,8,8,8}));
804     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {9,9,9,9,9,9,9,9,9,9}));
805     hri = hris.next();
806     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {9,9,9,9,9,9,9,9,9,9}));
807     assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
808 
809     verifyRoundRobinDistribution(ht2, expectedRegions);
810     ht2.close();
811 
812     // Try once more with something that divides into something infinite
813 
814     startKey = new byte [] { 0, 0, 0, 0, 0, 0 };
815     endKey = new byte [] { 1, 0, 0, 0, 0, 0 };
816 
817     expectedRegions = 5;
818 
819     TableName TABLE_3 = TableName.valueOf(tableName.getNameAsString() + "_3");
820 
821     desc = new HTableDescriptor(TABLE_3);
822     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
823     admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
824     admin.createTable(desc, startKey, endKey, expectedRegions);
825 
826 
827     HTable ht3 = new HTable(TEST_UTIL.getConfiguration(), TABLE_3);
828     regions = ht3.getRegionLocations();
829     assertEquals("Tried to create " + expectedRegions + " regions " +
830         "but only found " + regions.size(),
831         expectedRegions, regions.size());
832     System.err.println("Found " + regions.size() + " regions");
833 
834     verifyRoundRobinDistribution(ht3, expectedRegions);
835     ht3.close();
836 
837 
838     // Try an invalid case where there are duplicate split keys
839     splitKeys = new byte [][] {
840         new byte [] { 1, 1, 1 },
841         new byte [] { 2, 2, 2 },
842         new byte [] { 3, 3, 3 },
843         new byte [] { 2, 2, 2 }
844     };
845 
846     TableName TABLE_4 = TableName.valueOf(tableName.getNameAsString() + "_4");
847     desc = new HTableDescriptor(TABLE_4);
848     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
849     Admin ladmin = new HBaseAdmin(TEST_UTIL.getConfiguration());
850     try {
851       ladmin.createTable(desc, splitKeys);
852       assertTrue("Should not be able to create this table because of " +
853           "duplicate split keys", false);
854     } catch(IllegalArgumentException iae) {
855       // Expected
856     }
857     ladmin.close();
858   }
859 
860   @Test (timeout=300000)
861   public void testTableAvailableWithRandomSplitKeys() throws Exception {
862     TableName tableName = TableName.valueOf("testTableAvailableWithRandomSplitKeys");
863     HTableDescriptor desc = new HTableDescriptor(tableName);
864     desc.addFamily(new HColumnDescriptor("col"));
865     byte[][] splitKeys = new byte[1][];
866     splitKeys = new byte [][] {
867         new byte [] { 1, 1, 1 },
868         new byte [] { 2, 2, 2 }
869     };
870     admin.createTable(desc);
871     boolean tableAvailable = admin.isTableAvailable(tableName, splitKeys);
872     assertFalse("Table should be created with 1 row in META", tableAvailable);
873   }
874 
875   @Test (timeout=300000)
876   public void testCreateTableWithOnlyEmptyStartRow() throws IOException {
877     byte[] tableName = Bytes.toBytes("testCreateTableWithOnlyEmptyStartRow");
878     byte[][] splitKeys = new byte[1][];
879     splitKeys[0] = HConstants.EMPTY_BYTE_ARRAY;
880     HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
881     desc.addFamily(new HColumnDescriptor("col"));
882     try {
883       admin.createTable(desc, splitKeys);
884       fail("Test case should fail as empty split key is passed.");
885     } catch (IllegalArgumentException e) {
886     }
887   }
888 
889   @Test (timeout=300000)
890   public void testCreateTableWithEmptyRowInTheSplitKeys() throws IOException{
891     byte[] tableName = Bytes.toBytes("testCreateTableWithEmptyRowInTheSplitKeys");
892     byte[][] splitKeys = new byte[3][];
893     splitKeys[0] = "region1".getBytes();
894     splitKeys[1] = HConstants.EMPTY_BYTE_ARRAY;
895     splitKeys[2] = "region2".getBytes();
896     HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
897     desc.addFamily(new HColumnDescriptor("col"));
898     try {
899       admin.createTable(desc, splitKeys);
900       fail("Test case should fail as empty split key is passed.");
901     } catch (IllegalArgumentException e) {
902       LOG.info("Expected ", e);
903     }
904   }
905 
906   @Test (timeout=120000)
907   public void testTableExist() throws IOException {
908     final TableName table = TableName.valueOf("testTableExist");
909     boolean exist;
910     exist = this.admin.tableExists(table);
911     assertEquals(false, exist);
912     TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
913     exist = this.admin.tableExists(table);
914     assertEquals(true, exist);
915   }
916 
917   /**
918    * Tests forcing split from client and having scanners successfully ride over split.
919    * @throws Exception
920    * @throws IOException
921    */
922   @Test (timeout=400000)
923   public void testForceSplit() throws Exception {
924     byte[][] familyNames = new byte[][] { Bytes.toBytes("cf") };
925     int[] rowCounts = new int[] { 6000 };
926     int numVersions = HColumnDescriptor.DEFAULT_VERSIONS;
927     int blockSize = 256;
928     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
929 
930     byte[] splitKey = Bytes.toBytes(3500);
931     splitTest(splitKey, familyNames, rowCounts, numVersions, blockSize);
932   }
933 
934   /**
935    * Test retain assignment on enableTable.
936    *
937    * @throws IOException
938    */
939   @Test (timeout=300000)
940   public void testEnableTableRetainAssignment() throws IOException {
941     final TableName tableName = TableName.valueOf("testEnableTableAssignment");
942     byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 },
943         new byte[] { 3, 3, 3 }, new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 },
944         new byte[] { 6, 6, 6 }, new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 },
945         new byte[] { 9, 9, 9 } };
946     int expectedRegions = splitKeys.length + 1;
947     HTableDescriptor desc = new HTableDescriptor(tableName);
948     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
949     admin.createTable(desc, splitKeys);
950     HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName);
951     Map<HRegionInfo, ServerName> regions = ht.getRegionLocations();
952     assertEquals("Tried to create " + expectedRegions + " regions "
953         + "but only found " + regions.size(), expectedRegions, regions.size());
954     // Disable table.
955     admin.disableTable(tableName);
956     // Enable table, use retain assignment to assign regions.
957     admin.enableTable(tableName);
958     Map<HRegionInfo, ServerName> regions2 = ht.getRegionLocations();
959 
960     // Check the assignment.
961     assertEquals(regions.size(), regions2.size());
962     for (Map.Entry<HRegionInfo, ServerName> entry : regions.entrySet()) {
963       assertEquals(regions2.get(entry.getKey()), entry.getValue());
964     }
965   }
966 
967   /**
968    * Multi-family scenario. Tests forcing split from client and
969    * having scanners successfully ride over split.
970    * @throws Exception
971    * @throws IOException
972    */
973   @Test (timeout=800000)
974   public void testForceSplitMultiFamily() throws Exception {
975     int numVersions = HColumnDescriptor.DEFAULT_VERSIONS;
976 
977     // use small HFile block size so that we can have lots of blocks in HFile
978     // Otherwise, if there is only one block,
979     // HFileBlockIndex.midKey()'s value == startKey
980     int blockSize = 256;
981     byte[][] familyNames = new byte[][] { Bytes.toBytes("cf1"),
982       Bytes.toBytes("cf2") };
983 
984     // one of the column families isn't splittable
985     int[] rowCounts = new int[] { 6000, 1 };
986     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
987 
988     rowCounts = new int[] { 1, 6000 };
989     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
990 
991     // one column family has much smaller data than the other
992     // the split key should be based on the largest column family
993     rowCounts = new int[] { 6000, 300 };
994     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
995 
996     rowCounts = new int[] { 300, 6000 };
997     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
998 
999   }
1000 
1001   void splitTest(byte[] splitPoint, byte[][] familyNames, int[] rowCounts,
1002     int numVersions, int blockSize) throws Exception {
1003     TableName tableName = TableName.valueOf("testForceSplit");
1004     StringBuilder sb = new StringBuilder();
1005     // Add tail to String so can see better in logs where a test is running.
1006     for (int i = 0; i < rowCounts.length; i++) {
1007       sb.append("_").append(Integer.toString(rowCounts[i]));
1008     }
1009     assertFalse(admin.tableExists(tableName));
1010     final HTable table = TEST_UTIL.createTable(tableName, familyNames,
1011       numVersions, blockSize);
1012 
1013     int rowCount = 0;
1014     byte[] q = new byte[0];
1015 
1016     // insert rows into column families. The number of rows that have values
1017     // in a specific column family is decided by rowCounts[familyIndex]
1018     for (int index = 0; index < familyNames.length; index++) {
1019       ArrayList<Put> puts = new ArrayList<Put>(rowCounts[index]);
1020       for (int i = 0; i < rowCounts[index]; i++) {
1021         byte[] k = Bytes.toBytes(i);
1022         Put put = new Put(k);
1023         put.add(familyNames[index], q, k);
1024         puts.add(put);
1025       }
1026       table.put(puts);
1027 
1028       if ( rowCount < rowCounts[index] ) {
1029         rowCount = rowCounts[index];
1030       }
1031     }
1032 
1033     // get the initial layout (should just be one region)
1034     Map<HRegionInfo, ServerName> m = table.getRegionLocations();
1035     LOG.info("Initial regions (" + m.size() + "): " + m);
1036     assertTrue(m.size() == 1);
1037 
1038     // Verify row count
1039     Scan scan = new Scan();
1040     ResultScanner scanner = table.getScanner(scan);
1041     int rows = 0;
1042     for(@SuppressWarnings("unused") Result result : scanner) {
1043       rows++;
1044     }
1045     scanner.close();
1046     assertEquals(rowCount, rows);
1047 
1048     // Have an outstanding scan going on to make sure we can scan over splits.
1049     scan = new Scan();
1050     scanner = table.getScanner(scan);
1051     // Scan first row so we are into first region before split happens.
1052     scanner.next();
1053 
1054     // Split the table
1055     this.admin.split(tableName, splitPoint);
1056 
1057     final AtomicInteger count = new AtomicInteger(0);
1058     Thread t = new Thread("CheckForSplit") {
1059       @Override
1060       public void run() {
1061         for (int i = 0; i < 45; i++) {
1062           try {
1063             sleep(1000);
1064           } catch (InterruptedException e) {
1065             continue;
1066           }
1067           // check again    table = new HTable(conf, tableName);
1068           Map<HRegionInfo, ServerName> regions = null;
1069           try {
1070             regions = table.getRegionLocations();
1071           } catch (IOException e) {
1072             e.printStackTrace();
1073           }
1074           if (regions == null) continue;
1075           count.set(regions.size());
1076           if (count.get() >= 2) {
1077             LOG.info("Found: " + regions);
1078             break;
1079           }
1080           LOG.debug("Cycle waiting on split");
1081         }
1082         LOG.debug("CheckForSplit thread exited, current region count: " + count.get());
1083       }
1084     };
1085     t.setPriority(Thread.NORM_PRIORITY - 2);
1086     t.start();
1087     t.join();
1088 
1089     // Verify row count
1090     rows = 1; // We counted one row above.
1091     for (@SuppressWarnings("unused") Result result : scanner) {
1092       rows++;
1093       if (rows > rowCount) {
1094         scanner.close();
1095         assertTrue("Scanned more than expected (" + rowCount + ")", false);
1096       }
1097     }
1098     scanner.close();
1099     assertEquals(rowCount, rows);
1100 
1101     Map<HRegionInfo, ServerName> regions = null;
1102     try {
1103       regions = table.getRegionLocations();
1104     } catch (IOException e) {
1105       e.printStackTrace();
1106     }
1107     assertNotNull(regions);
1108     assertEquals(2, regions.size());
1109     Set<HRegionInfo> hRegionInfos = regions.keySet();
1110     HRegionInfo[] r = hRegionInfos.toArray(new HRegionInfo[hRegionInfos.size()]);
1111     if (splitPoint != null) {
1112       // make sure the split point matches our explicit configuration
1113       assertEquals(Bytes.toString(splitPoint),
1114           Bytes.toString(r[0].getEndKey()));
1115       assertEquals(Bytes.toString(splitPoint),
1116           Bytes.toString(r[1].getStartKey()));
1117       LOG.debug("Properly split on " + Bytes.toString(splitPoint));
1118     } else {
1119       if (familyNames.length > 1) {
1120         int splitKey = Bytes.toInt(r[0].getEndKey());
1121         // check if splitKey is based on the largest column family
1122         // in terms of it store size
1123         int deltaForLargestFamily = Math.abs(rowCount/2 - splitKey);
1124         LOG.debug("SplitKey=" + splitKey + "&deltaForLargestFamily=" + deltaForLargestFamily +
1125           ", r=" + r[0]);
1126         for (int index = 0; index < familyNames.length; index++) {
1127           int delta = Math.abs(rowCounts[index]/2 - splitKey);
1128           if (delta < deltaForLargestFamily) {
1129             assertTrue("Delta " + delta + " for family " + index
1130               + " should be at least deltaForLargestFamily " + deltaForLargestFamily,
1131               false);
1132           }
1133         }
1134       }
1135     }
1136     TEST_UTIL.deleteTable(tableName);
1137     table.close();
1138   }
1139 
1140   @Test
1141   public void testSplitAndMergeWithReplicaTable() throws Exception {
1142     // The test tries to directly split replica regions and directly merge replica regions. These
1143     // are not allowed. The test validates that. Then the test does a valid split/merge of allowed
1144     // regions.
1145     // Set up a table with 3 regions and replication set to 3
1146     TableName tableName = TableName.valueOf("testSplitAndMergeWithReplicaTable");
1147     byte[] cf = "f".getBytes();
1148     createReplicaTable(tableName, cf);
1149     List<HRegion> oldRegions;
1150     do {
1151       oldRegions = TEST_UTIL.getHBaseCluster().getRegions(tableName);
1152       Thread.sleep(10);
1153     } while (oldRegions.size() != 9); //3 regions * 3 replicas
1154     // write some data to the table
1155     HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName);
1156     List<Put> puts = new ArrayList<Put>();
1157     byte[] qualifier = "c".getBytes();
1158     Put put = new Put(new byte[]{(byte)'1'});
1159     put.add(cf, qualifier, "100".getBytes());
1160     puts.add(put);
1161     put = new Put(new byte[]{(byte)'6'});
1162     put.add(cf, qualifier, "100".getBytes());
1163     puts.add(put);
1164     put = new Put(new byte[]{(byte)'8'});
1165     put.add(cf, qualifier, "100".getBytes());
1166     puts.add(put);
1167     ht.put(puts);
1168     ht.flushCommits();
1169     ht.close();
1170     List<Pair<HRegionInfo, ServerName>> regions =
1171         MetaTableAccessor.getTableRegionsAndLocations(TEST_UTIL.getZooKeeperWatcher(),
1172                           TEST_UTIL.getConnection(), tableName);
1173     boolean gotException = false;
1174     // the element at index 1 would be a replica (since the metareader gives us ordered
1175     // regions). Try splitting that region via the split API . Should fail
1176     try {
1177       TEST_UTIL.getHBaseAdmin().split(regions.get(1).getFirst().getRegionName());
1178     } catch (IllegalArgumentException ex) {
1179       gotException = true;
1180     }
1181     assertTrue(gotException);
1182     gotException = false;
1183     // the element at index 1 would be a replica (since the metareader gives us ordered
1184     // regions). Try splitting that region via a different split API (the difference is
1185     // this API goes direct to the regionserver skipping any checks in the admin). Should fail
1186     try {
1187       TEST_UTIL.getHBaseAdmin().split(regions.get(1).getSecond(), regions.get(1).getFirst(),
1188           new byte[]{(byte)'1'});
1189     } catch (IOException ex) {
1190       gotException = true;
1191     }
1192     assertTrue(gotException);
1193     gotException = false;
1194     // Try merging a replica with another. Should fail.
1195     try {
1196       TEST_UTIL.getHBaseAdmin().mergeRegions(regions.get(1).getFirst().getEncodedNameAsBytes(),
1197           regions.get(2).getFirst().getEncodedNameAsBytes(), true);
1198     } catch (IllegalArgumentException m) {
1199       gotException = true;
1200     }
1201     assertTrue(gotException);
1202     // Try going to the master directly (that will skip the check in admin)
1203     try {
1204       DispatchMergingRegionsRequest request = RequestConverter
1205           .buildDispatchMergingRegionsRequest(regions.get(1).getFirst().getEncodedNameAsBytes(),
1206               regions.get(2).getFirst().getEncodedNameAsBytes(), true);
1207       TEST_UTIL.getHBaseAdmin().getConnection().getMaster().dispatchMergingRegions(null, request);
1208     } catch (ServiceException m) {
1209       Throwable t = m.getCause();
1210       do {
1211         if (t instanceof MergeRegionException) {
1212           gotException = true;
1213           break;
1214         }
1215         t = t.getCause();
1216       } while (t != null);
1217     }
1218     assertTrue(gotException);
1219     gotException = false;
1220     // Try going to the regionservers directly
1221     // first move the region to the same regionserver
1222     if (!regions.get(2).getSecond().equals(regions.get(1).getSecond())) {
1223       TEST_UTIL.moveRegionAndWait(regions.get(2).getFirst(), regions.get(1).getSecond());
1224     }
1225     try {
1226       AdminService.BlockingInterface admin = TEST_UTIL.getHBaseAdmin().getConnection()
1227           .getAdmin(regions.get(1).getSecond());
1228       ProtobufUtil.mergeRegions(null, admin, regions.get(1).getFirst(), regions.get(2).getFirst(),
1229         true, null);
1230     } catch (MergeRegionException mm) {
1231       gotException = true;
1232     }
1233     assertTrue(gotException);
1234   }
1235 
1236   /**
1237    * Test case to validate whether parent's replica regions are cleared from AM's memory after
1238    * SPLIT/MERGE.
1239    */
1240   @Test
1241   public void testRegionStateCleanupFromAMMemoryAfterRegionSplitAndMerge() throws Exception {
1242     final TableName tableName =
1243         TableName.valueOf("testRegionStateCleanupFromAMMemoryAfterRegionSplitAndMerge");
1244     createReplicaTable(tableName, "f".getBytes());
1245     final int regionReplication = admin.getTableDescriptor(tableName).getRegionReplication();
1246 
1247     List<Pair<HRegionInfo, ServerName>> regions = MetaTableAccessor.getTableRegionsAndLocations(
1248       TEST_UTIL.getZooKeeperWatcher(), TEST_UTIL.getConnection(), tableName);
1249     assertEquals(9, regions.size());
1250     final int primaryRegionCount = regions.size() / regionReplication;
1251 
1252     final AssignmentManager am = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager();
1253     List<HRegionInfo> splitRegions =
1254         am.getRegionStates().getRegionByStateOfTable(tableName).get(RegionState.State.SPLIT);
1255     assertEquals(0, splitRegions.size());
1256 
1257     // Validate region split
1258     byte[] regionName = regions.get(0).getFirst().getRegionName();
1259     try {
1260       TEST_UTIL.getHBaseAdmin().split(regionName, Bytes.toBytes('2'));
1261     } catch (IllegalArgumentException ex) {
1262       fail("Exception occured during region split" + ex);
1263     }
1264 
1265     // Wait for replica region to become online
1266     TEST_UTIL.waitFor(60000, 500, new Waiter.Predicate<IOException>() {
1267       @Override
1268       public boolean evaluate() throws IOException {
1269         return am.getRegionStates().getRegionByStateOfTable(tableName).get(RegionState.State.OPEN)
1270             .size() == (primaryRegionCount + 1) * regionReplication;
1271       }
1272     });
1273 
1274     regions = MetaTableAccessor.getTableRegionsAndLocations(TEST_UTIL.getZooKeeperWatcher(),
1275       TEST_UTIL.getConnection(), tableName);
1276     assertEquals(12, regions.size());
1277     final int primaryRegionCountAfterSplit = regions.size() / regionReplication;
1278 
1279     // Split region after region split
1280     splitRegions =
1281         am.getRegionStates().getRegionByStateOfTable(tableName).get(RegionState.State.SPLIT);
1282     // Parent's replica region should be removed from AM's memory.
1283     assertEquals(1, splitRegions.size());
1284 
1285     // Validate region merge
1286     HRegionInfo regionA = regions.get(3).getFirst();
1287     HRegionInfo regionB = regions.get(6).getFirst();
1288     try {
1289       TEST_UTIL.getHBaseAdmin().mergeRegions(regionA.getRegionName(), regionB.getRegionName(),
1290         true);
1291     } catch (IllegalArgumentException ex) {
1292       fail("Exception occured during region merge" + ex);
1293     }
1294 
1295     // Wait for replica regions to become online
1296     TEST_UTIL.waitFor(60000, 500, new Waiter.Predicate<IOException>() {
1297       @Override
1298       public boolean evaluate() throws IOException {
1299         return am.getRegionStates().getRegionByStateOfTable(tableName).get(RegionState.State.OPEN)
1300             .size() == (primaryRegionCountAfterSplit - 1) * regionReplication;
1301       }
1302     });
1303 
1304     regions = MetaTableAccessor.getTableRegionsAndLocations(TEST_UTIL.getZooKeeperWatcher(),
1305       TEST_UTIL.getConnection(), tableName);
1306     assertEquals(9, regions.size());
1307     // Offline region after region merge
1308     List<HRegionInfo> offlineRegions =
1309         am.getRegionStates().getRegionByStateOfTable(tableName).get(RegionState.State.OFFLINE);
1310     // Parent's replica region should be removed from AM's memory.
1311     assertEquals(0, offlineRegions.size());
1312   }
1313 
1314   private byte[] createReplicaTable(TableName tableName, byte[] cf) throws IOException {
1315     HTableDescriptor desc = new HTableDescriptor(tableName);
1316     desc.setRegionReplication(3);
1317     HColumnDescriptor hcd = new HColumnDescriptor(cf);
1318     desc.addFamily(hcd);
1319     byte[][] splitRows = new byte[2][];
1320     splitRows[0] = new byte[] { (byte) '4' };
1321     splitRows[1] = new byte[] { (byte) '7' };
1322     TEST_UTIL.getHBaseAdmin().createTable(desc, splitRows);
1323     return cf;
1324   }
1325 
1326   /**
1327    * HADOOP-2156
1328    * @throws IOException
1329    */
1330   @SuppressWarnings("deprecation")
1331   @Test (expected=IllegalArgumentException.class, timeout=300000)
1332   public void testEmptyHTableDescriptor() throws IOException {
1333     this.admin.createTable(new HTableDescriptor());
1334   }
1335 
1336   @Test (expected=IllegalArgumentException.class, timeout=300000)
1337   public void testInvalidHColumnDescriptor() throws IOException {
1338      new HColumnDescriptor("/cfamily/name");
1339   }
1340 
1341   @Test (timeout=300000)
1342   public void testEnableDisableAddColumnDeleteColumn() throws Exception {
1343     ZooKeeperWatcher zkw = HBaseTestingUtility.getZooKeeperWatcher(TEST_UTIL);
1344     TableName tableName = TableName.valueOf("testEnableDisableAddColumnDeleteColumn");
1345     TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
1346     while (!ZKTableStateClientSideReader.isEnabledTable(zkw,
1347       TableName.valueOf("testEnableDisableAddColumnDeleteColumn"))) {
1348       Thread.sleep(10);
1349     }
1350     this.admin.disableTable(tableName);
1351     try {
1352       new HTable(TEST_UTIL.getConfiguration(), tableName);
1353     } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
1354       //expected
1355     }
1356 
1357     this.admin.addColumn(tableName, new HColumnDescriptor("col2"));
1358     this.admin.enableTable(tableName);
1359     try {
1360       this.admin.deleteColumn(tableName, Bytes.toBytes("col2"));
1361     } catch (TableNotDisabledException e) {
1362       LOG.info(e);
1363     }
1364     this.admin.disableTable(tableName);
1365     this.admin.deleteTable(tableName);
1366   }
1367 
1368   @Test (timeout=300000)
1369   public void testDeleteLastColumnFamily() throws Exception {
1370     TableName tableName = TableName.valueOf("testDeleteLastColumnFamily");
1371     TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
1372     while (!this.admin.isTableEnabled(TableName.valueOf("testDeleteLastColumnFamily"))) {
1373       Thread.sleep(10);
1374     }
1375 
1376     // test for enabled table
1377     try {
1378       this.admin.deleteColumn(tableName, HConstants.CATALOG_FAMILY);
1379       fail("Should have failed to delete the only column family of a table");
1380     } catch (InvalidFamilyOperationException ex) {
1381       // expected
1382     }
1383 
1384     // test for disabled table
1385     this.admin.disableTable(tableName);
1386 
1387     try {
1388       this.admin.deleteColumn(tableName, HConstants.CATALOG_FAMILY);
1389       fail("Should have failed to delete the only column family of a table");
1390     } catch (InvalidFamilyOperationException ex) {
1391       // expected
1392     }
1393 
1394     this.admin.deleteTable(tableName);
1395   }
1396 
1397   /*
1398    * Test DFS replication for column families, where one CF has default replication(3) and the other
1399    * is set to 1.
1400    */
1401   @Test(timeout = 300000)
1402   public void testHFileReplication() throws Exception {
1403     TableName name = TableName.valueOf("testHFileReplication");
1404     String fn1 = "rep1";
1405     HColumnDescriptor hcd1 = new HColumnDescriptor(fn1);
1406     hcd1.setDFSReplication((short) 1);
1407     String fn = "defaultRep";
1408     HColumnDescriptor hcd = new HColumnDescriptor(fn);
1409     HTableDescriptor htd = new HTableDescriptor(name);
1410     htd.addFamily(hcd);
1411     htd.addFamily(hcd1);
1412     Table table = TEST_UTIL.createTable(htd, null);
1413     TEST_UTIL.waitTableAvailable(name);
1414     Put p = new Put(Bytes.toBytes("defaultRep_rk"));
1415     byte[] q1 = Bytes.toBytes("q1");
1416     byte[] v1 = Bytes.toBytes("v1");
1417     p.addColumn(Bytes.toBytes(fn), q1, v1);
1418     List<Put> puts = new ArrayList<Put>(2);
1419     puts.add(p);
1420     p = new Put(Bytes.toBytes("rep1_rk"));
1421     p.addColumn(Bytes.toBytes(fn1), q1, v1);
1422     puts.add(p);
1423     try {
1424       table.put(puts);
1425       admin.flush(name);
1426 
1427       List<HRegion> regions = TEST_UTIL.getMiniHBaseCluster().getRegions(name);
1428       for (HRegion r : regions) {
1429         Store store = r.getStore(Bytes.toBytes(fn));
1430         for (StoreFile sf : store.getStorefiles()) {
1431           assertTrue(sf.toString().contains(fn));
1432           assertTrue("Column family " + fn + " should have 3 copies",
1433             FSUtils.getDefaultReplication(TEST_UTIL.getTestFileSystem(), sf.getPath()) == (sf
1434                 .getFileInfo().getFileStatus().getReplication()));
1435         }
1436 
1437         store = r.getStore(Bytes.toBytes(fn1));
1438         for (StoreFile sf : store.getStorefiles()) {
1439           assertTrue(sf.toString().contains(fn1));
1440           assertTrue("Column family " + fn1 + " should have only 1 copy", 1 == sf.getFileInfo()
1441               .getFileStatus().getReplication());
1442         }
1443       }
1444     } finally {
1445       if (admin.isTableEnabled(name)) {
1446         this.admin.disableTable(name);
1447         this.admin.deleteTable(name);
1448       }
1449     }
1450   }
1451 
1452   @Test
1453   public void testMergeRegions() throws Exception {
1454     TableName tableName = TableName.valueOf("testMergeWithFullRegionName");
1455     HColumnDescriptor cd = new HColumnDescriptor("d");
1456     HTableDescriptor td = new HTableDescriptor(tableName);
1457     td.addFamily(cd);
1458     byte[][] splitRows = new byte[2][];
1459     splitRows[0] = new byte[]{(byte)'3'};
1460     splitRows[1] = new byte[]{(byte)'6'};
1461     try {
1462       TEST_UTIL.createTable(td, splitRows);
1463       TEST_UTIL.waitTableAvailable(tableName);
1464 
1465       List<HRegionInfo> tableRegions;
1466       HRegionInfo regionA;
1467       HRegionInfo regionB;
1468 
1469       // merge with full name
1470       tableRegions = admin.getTableRegions(tableName);
1471       assertEquals(3, admin.getTableRegions(tableName).size());
1472       regionA = tableRegions.get(0);
1473       regionB = tableRegions.get(1);
1474       admin.mergeRegions(regionA.getRegionName(), regionB.getRegionName(), false);
1475       Thread.sleep(1000);
1476       assertEquals(2, admin.getTableRegions(tableName).size());
1477 
1478       // merge with encoded name
1479       tableRegions = admin.getTableRegions(tableName);
1480       regionA = tableRegions.get(0);
1481       regionB = tableRegions.get(1);
1482       admin.mergeRegions(regionA.getEncodedNameAsBytes(), regionB.getEncodedNameAsBytes(), false);
1483       Thread.sleep(1000);
1484       assertEquals(1, admin.getTableRegions(tableName).size());
1485     } finally {
1486       this.admin.disableTable(tableName);
1487       this.admin.deleteTable(tableName);
1488     }
1489   }
1490 
1491   @Test (timeout=30000)
1492   public void testTableNotFoundException() throws Exception {
1493     ZooKeeperWatcher zkw = TEST_UTIL.getZooKeeperWatcher();
1494     TableName table = TableName.valueOf("tableNotExists");
1495     try {
1496       ZKTableStateClientSideReader.isDisabledTable(zkw, table);
1497       fail("Shouldn't be here");
1498     } catch (TableNotFoundException e) {
1499       // This is expected.
1500     }
1501   }
1502 }