001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertNotEquals;
023import static org.junit.Assert.assertNotNull;
024import static org.junit.Assert.assertNull;
025import static org.junit.Assert.assertTrue;
026import static org.junit.Assert.fail;
027
028import java.io.IOException;
029import java.util.ArrayList;
030import java.util.HashMap;
031import java.util.Iterator;
032import java.util.List;
033import java.util.Map;
034import org.apache.hadoop.hbase.HBaseClassTestRule;
035import org.apache.hadoop.hbase.HConstants;
036import org.apache.hadoop.hbase.HRegionLocation;
037import org.apache.hadoop.hbase.ServerName;
038import org.apache.hadoop.hbase.TableExistsException;
039import org.apache.hadoop.hbase.TableName;
040import org.apache.hadoop.hbase.TableNotFoundException;
041import org.apache.hadoop.hbase.master.LoadBalancer;
042import org.apache.hadoop.hbase.testclassification.ClientTests;
043import org.apache.hadoop.hbase.testclassification.LargeTests;
044import org.apache.hadoop.hbase.util.Bytes;
045import org.junit.ClassRule;
046import org.junit.Test;
047import org.junit.experimental.categories.Category;
048import org.slf4j.Logger;
049import org.slf4j.LoggerFactory;
050
051@Category({ LargeTests.class, ClientTests.class })
052public class TestAdmin extends TestAdminBase {
053
054  @ClassRule
055  public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule.forClass(TestAdmin.class);
056
057  private static final Logger LOG = LoggerFactory.getLogger(TestAdmin.class);
058
059  @Test
060  public void testCreateTable() throws IOException {
061    List<TableDescriptor> tables = ADMIN.listTableDescriptors();
062    int numTables = tables.size();
063    final TableName tableName = TableName.valueOf(name.getMethodName());
064    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
065    tables = ADMIN.listTableDescriptors();
066    assertEquals(numTables + 1, tables.size());
067    assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster().getMaster()
068      .getTableStateManager().isTableState(tableName, TableState.State.ENABLED));
069    assertEquals(TableState.State.ENABLED, getStateFromMeta(tableName));
070  }
071
072  @Test
073  public void testTruncateTable() throws IOException {
074    testTruncateTable(TableName.valueOf(name.getMethodName()), false);
075  }
076
077  @Test
078  public void testTruncateTablePreservingSplits() throws IOException {
079    testTruncateTable(TableName.valueOf(name.getMethodName()), true);
080  }
081
082  private void testTruncateTable(final TableName tableName, boolean preserveSplits)
083      throws IOException {
084    byte[][] splitKeys = new byte[2][];
085    splitKeys[0] = Bytes.toBytes(4);
086    splitKeys[1] = Bytes.toBytes(8);
087
088    // Create & Fill the table
089    Table table = TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY, splitKeys);
090    try {
091      TEST_UTIL.loadNumericRows(table, HConstants.CATALOG_FAMILY, 0, 10);
092      assertEquals(10, TEST_UTIL.countRows(table));
093    } finally {
094      table.close();
095    }
096    assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
097
098    // Truncate & Verify
099    ADMIN.disableTable(tableName);
100    ADMIN.truncateTable(tableName, preserveSplits);
101    table = TEST_UTIL.getConnection().getTable(tableName);
102    try {
103      assertEquals(0, TEST_UTIL.countRows(table));
104    } finally {
105      table.close();
106    }
107    if (preserveSplits) {
108      assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
109    } else {
110      assertEquals(1, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
111    }
112  }
113
114  @Test
115  public void testCreateTableNumberOfRegions() throws IOException, InterruptedException {
116    TableName table = TableName.valueOf(name.getMethodName());
117    ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY);
118    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table).setColumnFamily(cfd).build());
119    List<HRegionLocation> regions;
120    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table)) {
121      regions = l.getAllRegionLocations();
122      assertEquals("Table should have only 1 region", 1, regions.size());
123    }
124
125    TableName table2 = TableName.valueOf(table.getNameAsString() + "_2");
126    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table2).setColumnFamily(cfd).build(),
127      new byte[][] { new byte[] { 42 } });
128    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table2)) {
129      regions = l.getAllRegionLocations();
130      assertEquals("Table should have only 2 region", 2, regions.size());
131    }
132
133    TableName table3 = TableName.valueOf(table.getNameAsString() + "_3");
134    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table3).setColumnFamily(cfd).build(),
135      Bytes.toBytes("a"), Bytes.toBytes("z"), 3);
136    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table3)) {
137      regions = l.getAllRegionLocations();
138      assertEquals("Table should have only 3 region", 3, regions.size());
139    }
140
141    TableName table4 = TableName.valueOf(table.getNameAsString() + "_4");
142    try {
143      ADMIN.createTable(TableDescriptorBuilder.newBuilder(table4).setColumnFamily(cfd).build(),
144        Bytes.toBytes("a"), Bytes.toBytes("z"), 2);
145      fail("Should not be able to create a table with only 2 regions using this API.");
146    } catch (IllegalArgumentException eae) {
147      // Expected
148    }
149
150    TableName table5 = TableName.valueOf(table.getNameAsString() + "_5");
151    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table5).setColumnFamily(cfd).build(),
152      new byte[] { 1 }, new byte[] { 127 }, 16);
153    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table5)) {
154      regions = l.getAllRegionLocations();
155      assertEquals("Table should have 16 region", 16, regions.size());
156    }
157  }
158
159  @Test
160  public void testCreateTableWithRegions() throws IOException, InterruptedException {
161    TableName table = TableName.valueOf(name.getMethodName());
162    ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY);
163    byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 }, new byte[] { 3, 3, 3 },
164      new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 }, new byte[] { 6, 6, 6 },
165      new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 }, new byte[] { 9, 9, 9 }, };
166    int expectedRegions = splitKeys.length + 1;
167
168    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table).setColumnFamily(cfd).build(),
169      splitKeys);
170
171    boolean tableAvailable = ADMIN.isTableAvailable(table);
172    assertTrue("Table should be created with splitKyes + 1 rows in META", tableAvailable);
173
174    List<HRegionLocation> regions;
175    Iterator<HRegionLocation> hris;
176    RegionInfo hri;
177    ClusterConnection conn = (ClusterConnection) TEST_UTIL.getConnection();
178    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table)) {
179      regions = l.getAllRegionLocations();
180
181      assertEquals(
182        "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
183        expectedRegions, regions.size());
184      System.err.println("Found " + regions.size() + " regions");
185
186      hris = regions.iterator();
187      hri = hris.next().getRegion();
188      assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
189      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[0]));
190      hri = hris.next().getRegion();
191      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[0]));
192      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[1]));
193      hri = hris.next().getRegion();
194      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[1]));
195      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[2]));
196      hri = hris.next().getRegion();
197      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[2]));
198      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[3]));
199      hri = hris.next().getRegion();
200      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[3]));
201      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[4]));
202      hri = hris.next().getRegion();
203      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[4]));
204      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[5]));
205      hri = hris.next().getRegion();
206      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[5]));
207      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[6]));
208      hri = hris.next().getRegion();
209      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[6]));
210      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[7]));
211      hri = hris.next().getRegion();
212      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[7]));
213      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[8]));
214      hri = hris.next().getRegion();
215      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[8]));
216      assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
217
218      verifyRoundRobinDistribution(l, expectedRegions);
219    }
220
221    // Now test using start/end with a number of regions
222
223    // Use 80 bit numbers to make sure we aren't limited
224    byte[] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
225    byte[] endKey = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
226
227    // Splitting into 10 regions, we expect (null,1) ... (9, null)
228    // with (1,2) (2,3) (3,4) (4,5) (5,6) (6,7) (7,8) (8,9) in the middle
229
230    expectedRegions = 10;
231
232    TableName table2 = TableName.valueOf(table.getNameAsString() + "_2");
233    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table2).setColumnFamily(cfd).build(),
234      startKey, endKey, expectedRegions);
235
236    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table2)) {
237      regions = l.getAllRegionLocations();
238      assertEquals(
239        "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
240        expectedRegions, regions.size());
241      System.err.println("Found " + regions.size() + " regions");
242
243      hris = regions.iterator();
244      hri = hris.next().getRegion();
245      assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
246      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }));
247      hri = hris.next().getRegion();
248      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }));
249      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }));
250      hri = hris.next().getRegion();
251      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }));
252      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }));
253      hri = hris.next().getRegion();
254      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }));
255      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }));
256      hri = hris.next().getRegion();
257      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }));
258      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }));
259      hri = hris.next().getRegion();
260      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }));
261      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }));
262      hri = hris.next().getRegion();
263      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }));
264      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }));
265      hri = hris.next().getRegion();
266      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }));
267      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }));
268      hri = hris.next().getRegion();
269      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }));
270      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }));
271      hri = hris.next().getRegion();
272      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }));
273      assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
274
275      verifyRoundRobinDistribution(l, expectedRegions);
276    }
277
278    // Try once more with something that divides into something infinite
279
280    startKey = new byte[] { 0, 0, 0, 0, 0, 0 };
281    endKey = new byte[] { 1, 0, 0, 0, 0, 0 };
282
283    expectedRegions = 5;
284
285    TableName table3 = TableName.valueOf(table.getNameAsString() + "_3");
286    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table3).setColumnFamily(cfd).build(),
287      startKey, endKey, expectedRegions);
288
289    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table3)) {
290      regions = l.getAllRegionLocations();
291      assertEquals(
292        "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
293        expectedRegions, regions.size());
294      System.err.println("Found " + regions.size() + " regions");
295
296      verifyRoundRobinDistribution(l, expectedRegions);
297    }
298
299    // Try an invalid case where there are duplicate split keys
300    splitKeys = new byte[][] { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 },
301      new byte[] { 3, 3, 3 }, new byte[] { 2, 2, 2 } };
302
303    TableName table4 = TableName.valueOf(table.getNameAsString() + "_4");
304    try {
305      ADMIN.createTable(TableDescriptorBuilder.newBuilder(table4).setColumnFamily(cfd).build(),
306        splitKeys);
307      assertTrue("Should not be able to create this table because of " + "duplicate split keys",
308        false);
309    } catch (IllegalArgumentException iae) {
310      // Expected
311    }
312  }
313
314  @Test
315  public void testCreateTableWithOnlyEmptyStartRow() throws IOException {
316    final byte[] tableName = Bytes.toBytes(name.getMethodName());
317    byte[][] splitKeys = new byte[1][];
318    splitKeys[0] = HConstants.EMPTY_BYTE_ARRAY;
319    TableDescriptor desc = TableDescriptorBuilder.newBuilder(TableName.valueOf(tableName))
320      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("col")).build();
321    try {
322      ADMIN.createTable(desc, splitKeys);
323      fail("Test case should fail as empty split key is passed.");
324    } catch (IllegalArgumentException e) {
325    }
326  }
327
328  @Test
329  public void testCreateTableWithEmptyRowInTheSplitKeys() throws IOException {
330    final byte[] tableName = Bytes.toBytes(name.getMethodName());
331    byte[][] splitKeys = new byte[3][];
332    splitKeys[0] = Bytes.toBytes("region1");
333    splitKeys[1] = HConstants.EMPTY_BYTE_ARRAY;
334    splitKeys[2] = Bytes.toBytes("region2");
335    TableDescriptor desc = TableDescriptorBuilder.newBuilder(TableName.valueOf(tableName))
336      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("col")).build();
337    try {
338      ADMIN.createTable(desc, splitKeys);
339      fail("Test case should fail as empty split key is passed.");
340    } catch (IllegalArgumentException e) {
341      LOG.info("Expected ", e);
342    }
343  }
344
345  private void verifyRoundRobinDistribution(RegionLocator regionLocator, int expectedRegions)
346      throws IOException {
347    int numRS = TEST_UTIL.getMiniHBaseCluster().getNumLiveRegionServers();
348    List<HRegionLocation> regions = regionLocator.getAllRegionLocations();
349    Map<ServerName, List<RegionInfo>> server2Regions = new HashMap<>();
350    for (HRegionLocation loc : regions) {
351      ServerName server = loc.getServerName();
352      List<RegionInfo> regs = server2Regions.get(server);
353      if (regs == null) {
354        regs = new ArrayList<>();
355        server2Regions.put(server, regs);
356      }
357      regs.add(loc.getRegion());
358    }
359    boolean tablesOnMaster = LoadBalancer.isTablesOnMaster(TEST_UTIL.getConfiguration());
360    if (tablesOnMaster) {
361      // Ignore the master region server,
362      // which contains less regions by intention.
363      numRS--;
364    }
365    float average = (float) expectedRegions / numRS;
366    int min = (int) Math.floor(average);
367    int max = (int) Math.ceil(average);
368    for (List<RegionInfo> regionList : server2Regions.values()) {
369      assertTrue(
370        "numRS=" + numRS + ", min=" + min + ", max=" + max + ", size=" + regionList.size() +
371          ", tablesOnMaster=" + tablesOnMaster,
372        regionList.size() == min || regionList.size() == max);
373    }
374  }
375
376  @Test
377  public void testCloneTableSchema() throws Exception {
378    final TableName tableName = TableName.valueOf(name.getMethodName());
379    final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new");
380    testCloneTableSchema(tableName, newTableName, false);
381  }
382
383  @Test
384  public void testCloneTableSchemaPreservingSplits() throws Exception {
385    final TableName tableName = TableName.valueOf(name.getMethodName());
386    final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new");
387    testCloneTableSchema(tableName, newTableName, true);
388  }
389
390  private void testCloneTableSchema(final TableName tableName, final TableName newTableName,
391      boolean preserveSplits) throws Exception {
392    byte[] FAMILY_0 = Bytes.toBytes("cf0");
393    byte[] FAMILY_1 = Bytes.toBytes("cf1");
394    byte[][] splitKeys = new byte[2][];
395    splitKeys[0] = Bytes.toBytes(4);
396    splitKeys[1] = Bytes.toBytes(8);
397    int NUM_FAMILYS = 2;
398    int NUM_REGIONS = 3;
399    int BLOCK_SIZE = 1024;
400    int TTL = 86400;
401    boolean BLOCK_CACHE = false;
402
403    // Create the table
404    TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(tableName)
405      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY_0))
406      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(FAMILY_1).setBlocksize(BLOCK_SIZE)
407        .setBlockCacheEnabled(BLOCK_CACHE).setTimeToLive(TTL).build())
408      .build();
409    ADMIN.createTable(tableDesc, splitKeys);
410
411    assertEquals(NUM_REGIONS, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
412    assertTrue("Table should be created with splitKyes + 1 rows in META",
413      ADMIN.isTableAvailable(tableName));
414
415    // clone & Verify
416    ADMIN.cloneTableSchema(tableName, newTableName, preserveSplits);
417    TableDescriptor newTableDesc = ADMIN.getDescriptor(newTableName);
418
419    assertEquals(NUM_FAMILYS, newTableDesc.getColumnFamilyCount());
420    assertEquals(BLOCK_SIZE, newTableDesc.getColumnFamily(FAMILY_1).getBlocksize());
421    assertEquals(BLOCK_CACHE, newTableDesc.getColumnFamily(FAMILY_1).isBlockCacheEnabled());
422    assertEquals(TTL, newTableDesc.getColumnFamily(FAMILY_1).getTimeToLive());
423    TEST_UTIL.verifyTableDescriptorIgnoreTableName(tableDesc, newTableDesc);
424
425    if (preserveSplits) {
426      assertEquals(NUM_REGIONS, TEST_UTIL.getHBaseCluster().getRegions(newTableName).size());
427      assertTrue("New table should be created with splitKyes + 1 rows in META",
428        ADMIN.isTableAvailable(newTableName));
429    } else {
430      assertEquals(1, TEST_UTIL.getHBaseCluster().getRegions(newTableName).size());
431    }
432  }
433
434  @Test
435  public void testCloneTableSchemaWithNonExistentSourceTable() throws Exception {
436    final TableName tableName = TableName.valueOf(name.getMethodName());
437    final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new");
438    // test for non-existent source table
439    try {
440      ADMIN.cloneTableSchema(tableName, newTableName, false);
441      fail("Should have failed to create a new table by cloning non-existent source table.");
442    } catch (TableNotFoundException ex) {
443      // expected
444    }
445  }
446
447  @Test
448  public void testCloneTableSchemaWithExistentDestinationTable() throws Exception {
449    final TableName tableName = TableName.valueOf(name.getMethodName());
450    final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new");
451    byte[] FAMILY_0 = Bytes.toBytes("cf0");
452    TEST_UTIL.createTable(tableName, FAMILY_0);
453    TEST_UTIL.createTable(newTableName, FAMILY_0);
454    // test for existent destination table
455    try {
456      ADMIN.cloneTableSchema(tableName, newTableName, false);
457      fail("Should have failed to create a existent table.");
458    } catch (TableExistsException ex) {
459      // expected
460    }
461  }
462
463  @Test
464  public void testModifyTableOnTableWithRegionReplicas() throws Exception {
465    TableName tableName = TableName.valueOf(name.getMethodName());
466    TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
467      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(Bytes.toBytes("cf")))
468      .setRegionReplication(5).build();
469
470    ADMIN.createTable(desc);
471
472    int maxFileSize = 10000000;
473    TableDescriptor newDesc =
474      TableDescriptorBuilder.newBuilder(desc).setMaxFileSize(maxFileSize).build();
475
476    ADMIN.modifyTable(newDesc);
477    TableDescriptor newTableDesc = ADMIN.getDescriptor(tableName);
478    assertEquals(maxFileSize, newTableDesc.getMaxFileSize());
479  }
480
481  /**
482   * Verify schema modification takes.
483   */
484  @Test
485  public void testOnlineChangeTableSchema() throws IOException, InterruptedException {
486    final TableName tableName = TableName.valueOf(name.getMethodName());
487    List<TableDescriptor> tables = ADMIN.listTableDescriptors();
488    int numTables = tables.size();
489    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
490    tables = ADMIN.listTableDescriptors();
491    assertEquals(numTables + 1, tables.size());
492    // FIRST, do htabledescriptor changes.
493    TableDescriptor htd = ADMIN.getDescriptor(tableName);
494    // Make a copy and assert copy is good.
495    TableDescriptor copy = TableDescriptorBuilder.newBuilder(htd).build();
496    assertEquals(htd, copy);
497    String key = "anyoldkey";
498    assertNull(htd.getValue(key));
499    // Now amend the copy. Introduce differences.
500    long newFlushSize = htd.getMemStoreFlushSize() / 2;
501    if (newFlushSize <= 0) {
502      newFlushSize = TableDescriptorBuilder.DEFAULT_MEMSTORE_FLUSH_SIZE / 2;
503    }
504    copy = TableDescriptorBuilder.newBuilder(copy).setMemStoreFlushSize(newFlushSize)
505      .setValue(key, key).build();
506    ADMIN.modifyTable(copy);
507    TableDescriptor modifiedHtd = ADMIN.getDescriptor(tableName);
508    assertNotEquals(htd, modifiedHtd);
509    assertEquals(copy, modifiedHtd);
510    assertEquals(newFlushSize, modifiedHtd.getMemStoreFlushSize());
511    assertEquals(key, modifiedHtd.getValue(key));
512
513    // Now work on column family changes.
514    int countOfFamilies = modifiedHtd.getColumnFamilyCount();
515    assertTrue(countOfFamilies > 0);
516    ColumnFamilyDescriptor hcd = modifiedHtd.getColumnFamilies()[0];
517    int maxversions = hcd.getMaxVersions();
518    int newMaxVersions = maxversions + 1;
519    hcd = ColumnFamilyDescriptorBuilder.newBuilder(hcd).setMaxVersions(newMaxVersions).build();
520    byte[] hcdName = hcd.getName();
521    ADMIN.modifyColumnFamily(tableName, hcd);
522    modifiedHtd = ADMIN.getDescriptor(tableName);
523    ColumnFamilyDescriptor modifiedHcd = modifiedHtd.getColumnFamily(hcdName);
524    assertEquals(newMaxVersions, modifiedHcd.getMaxVersions());
525
526    // Try adding a column
527    assertFalse(ADMIN.isTableDisabled(tableName));
528    String xtracolName = "xtracol";
529    ColumnFamilyDescriptor xtracol = ColumnFamilyDescriptorBuilder
530      .newBuilder(Bytes.toBytes(xtracolName)).setValue(xtracolName, xtracolName).build();
531    ADMIN.addColumnFamily(tableName, xtracol);
532    modifiedHtd = ADMIN.getDescriptor(tableName);
533    hcd = modifiedHtd.getColumnFamily(xtracol.getName());
534    assertNotNull(hcd);
535    assertEquals(xtracolName, Bytes.toString(hcd.getValue(Bytes.toBytes(xtracolName))));
536
537    // Delete the just-added column.
538    ADMIN.deleteColumnFamily(tableName, xtracol.getName());
539    modifiedHtd = ADMIN.getDescriptor(tableName);
540    hcd = modifiedHtd.getColumnFamily(xtracol.getName());
541    assertNull(hcd);
542
543    // Delete the table
544    ADMIN.disableTable(tableName);
545    ADMIN.deleteTable(tableName);
546    ADMIN.listTableDescriptors();
547    assertFalse(ADMIN.tableExists(tableName));
548  }
549}