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