View Javadoc

1   /**
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.master.handler;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertTrue;
24  
25  import java.io.IOException;
26  import java.util.Set;
27  
28  import org.apache.hadoop.fs.Path;
29  import org.apache.hadoop.hbase.HBaseTestingUtility;
30  import org.apache.hadoop.hbase.HColumnDescriptor;
31  import org.apache.hadoop.hbase.HTableDescriptor;
32  import org.apache.hadoop.hbase.testclassification.LargeTests;
33  import org.apache.hadoop.hbase.InvalidFamilyOperationException;
34  import org.apache.hadoop.hbase.TableName;
35  import org.apache.hadoop.hbase.client.Admin;
36  import org.apache.hadoop.hbase.master.MasterFileSystem;
37  import org.apache.hadoop.hbase.util.Bytes;
38  import org.apache.hadoop.hbase.util.FSTableDescriptors;
39  import org.apache.hadoop.hbase.util.FSUtils;
40  import org.junit.AfterClass;
41  import org.junit.Assert;
42  import org.junit.Before;
43  import org.junit.BeforeClass;
44  import org.junit.Rule;
45  import org.junit.Test;
46  import org.junit.experimental.categories.Category;
47  import org.junit.rules.TestName;
48  
49  /**
50   * Verify that the HTableDescriptor is updated after
51   * addColumn(), deleteColumn() and modifyTable() operations.
52   */
53  @Category(LargeTests.class)
54  public class TestTableDescriptorModification {
55  
56    @Rule public TestName name = new TestName();
57    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
58    private static TableName TABLE_NAME = null;
59    private static final byte[] FAMILY_0 = Bytes.toBytes("cf0");
60    private static final byte[] FAMILY_1 = Bytes.toBytes("cf1");
61  
62    /**
63     * Start up a mini cluster and put a small table of empty regions into it.
64     *
65     * @throws Exception
66     */
67    @BeforeClass
68    public static void beforeAllTests() throws Exception {
69      TEST_UTIL.startMiniCluster(1);
70    }
71  
72    @Before
73    public void setup() {
74      TABLE_NAME = TableName.valueOf(name.getMethodName());
75  
76    }
77  
78    @AfterClass
79    public static void afterAllTests() throws Exception {
80      TEST_UTIL.shutdownMiniCluster();
81    }
82  
83    @Test
84    public void testModifyTable() throws IOException {
85      Admin admin = TEST_UTIL.getHBaseAdmin();
86      // Create a table with one family
87      HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME);
88      baseHtd.addFamily(new HColumnDescriptor(FAMILY_0));
89      admin.createTable(baseHtd);
90      admin.disableTable(TABLE_NAME);
91      try {
92        // Verify the table descriptor
93        verifyTableDescriptor(TABLE_NAME, FAMILY_0);
94  
95        // Modify the table adding another family and verify the descriptor
96        HTableDescriptor modifiedHtd = new HTableDescriptor(TABLE_NAME);
97        modifiedHtd.addFamily(new HColumnDescriptor(FAMILY_0));
98        modifiedHtd.addFamily(new HColumnDescriptor(FAMILY_1));
99        admin.modifyTable(TABLE_NAME, modifiedHtd);
100       verifyTableDescriptor(TABLE_NAME, FAMILY_0, FAMILY_1);
101     } finally {
102       admin.deleteTable(TABLE_NAME);
103     }
104   }
105 
106   @Test
107   public void testAddColumn() throws IOException {
108     Admin admin = TEST_UTIL.getHBaseAdmin();
109     // Create a table with two families
110     HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME);
111     baseHtd.addFamily(new HColumnDescriptor(FAMILY_0));
112     admin.createTable(baseHtd);
113     admin.disableTable(TABLE_NAME);
114     try {
115       // Verify the table descriptor
116       verifyTableDescriptor(TABLE_NAME, FAMILY_0);
117 
118       // Modify the table removing one family and verify the descriptor
119       admin.addColumn(TABLE_NAME, new HColumnDescriptor(FAMILY_1));
120       verifyTableDescriptor(TABLE_NAME, FAMILY_0, FAMILY_1);
121     } finally {
122       admin.deleteTable(TABLE_NAME);
123     }
124   }
125 
126   @Test
127   public void testAddSameColumnFamilyTwice() throws IOException {
128     Admin admin = TEST_UTIL.getHBaseAdmin();
129     // Create a table with one families
130     HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME);
131     baseHtd.addFamily(new HColumnDescriptor(FAMILY_0));
132     admin.createTable(baseHtd);
133     admin.disableTable(TABLE_NAME);
134     try {
135       // Verify the table descriptor
136       verifyTableDescriptor(TABLE_NAME, FAMILY_0);
137 
138       // Modify the table removing one family and verify the descriptor
139       admin.addColumn(TABLE_NAME, new HColumnDescriptor(FAMILY_1));
140       verifyTableDescriptor(TABLE_NAME, FAMILY_0, FAMILY_1);
141 
142       try {
143         // Add same column family again - expect failure
144         admin.addColumn(TABLE_NAME, new HColumnDescriptor(FAMILY_1));
145         Assert.fail("Delete a non-exist column family should fail");
146       } catch (InvalidFamilyOperationException e) {
147         // Expected.
148       }
149 
150     } finally {
151       admin.deleteTable(TABLE_NAME);
152     }
153   }
154 
155   @Test
156   public void testModifyColumnFamily() throws IOException {
157     Admin admin = TEST_UTIL.getHBaseAdmin();
158 
159     HColumnDescriptor cfDescriptor = new HColumnDescriptor(FAMILY_0);
160     int blockSize = cfDescriptor.getBlocksize();
161     // Create a table with one families
162     HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME);
163     baseHtd.addFamily(cfDescriptor);
164     admin.createTable(baseHtd);
165     admin.disableTable(TABLE_NAME);
166     try {
167       // Verify the table descriptor
168       verifyTableDescriptor(TABLE_NAME, FAMILY_0);
169 
170       int newBlockSize = 2 * blockSize;
171       cfDescriptor.setBlocksize(newBlockSize);
172 
173       // Modify colymn family
174       admin.modifyColumn(TABLE_NAME, cfDescriptor);
175 
176       HTableDescriptor htd = admin.getTableDescriptor(TABLE_NAME);
177       HColumnDescriptor hcfd = htd.getFamily(FAMILY_0);
178       assertTrue(hcfd.getBlocksize() == newBlockSize);
179     } finally {
180       admin.deleteTable(TABLE_NAME);
181     }
182   }
183 
184   @Test
185   public void testModifyNonExistingColumnFamily() throws IOException {
186     Admin admin = TEST_UTIL.getHBaseAdmin();
187 
188     HColumnDescriptor cfDescriptor = new HColumnDescriptor(FAMILY_1);
189     int blockSize = cfDescriptor.getBlocksize();
190     // Create a table with one families
191     HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME);
192     baseHtd.addFamily(new HColumnDescriptor(FAMILY_0));
193     admin.createTable(baseHtd);
194     admin.disableTable(TABLE_NAME);
195     try {
196       // Verify the table descriptor
197       verifyTableDescriptor(TABLE_NAME, FAMILY_0);
198 
199       int newBlockSize = 2 * blockSize;
200       cfDescriptor.setBlocksize(newBlockSize);
201 
202       // Modify a column family that is not in the table.
203       try {
204         admin.modifyColumn(TABLE_NAME, cfDescriptor);
205         Assert.fail("Modify a non-exist column family should fail");
206       } catch (InvalidFamilyOperationException e) {
207         // Expected.
208       }
209 
210     } finally {
211       admin.deleteTable(TABLE_NAME);
212     }
213   }
214 
215   @Test
216   public void testDeleteColumn() throws IOException {
217     Admin admin = TEST_UTIL.getHBaseAdmin();
218     // Create a table with two families
219     HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME);
220     baseHtd.addFamily(new HColumnDescriptor(FAMILY_0));
221     baseHtd.addFamily(new HColumnDescriptor(FAMILY_1));
222     admin.createTable(baseHtd);
223     admin.disableTable(TABLE_NAME);
224     try {
225       // Verify the table descriptor
226       verifyTableDescriptor(TABLE_NAME, FAMILY_0, FAMILY_1);
227 
228       // Modify the table removing one family and verify the descriptor
229       admin.deleteColumn(TABLE_NAME, FAMILY_1);
230       verifyTableDescriptor(TABLE_NAME, FAMILY_0);
231     } finally {
232       admin.deleteTable(TABLE_NAME);
233     }
234   }
235 
236   @Test
237   public void testDeleteSameColumnFamilyTwice() throws IOException {
238     Admin admin = TEST_UTIL.getHBaseAdmin();
239     // Create a table with two families
240     HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME);
241     baseHtd.addFamily(new HColumnDescriptor(FAMILY_0));
242     baseHtd.addFamily(new HColumnDescriptor(FAMILY_1));
243     admin.createTable(baseHtd);
244     admin.disableTable(TABLE_NAME);
245     try {
246       // Verify the table descriptor
247       verifyTableDescriptor(TABLE_NAME, FAMILY_0, FAMILY_1);
248 
249       // Modify the table removing one family and verify the descriptor
250       admin.deleteColumn(TABLE_NAME, FAMILY_1);
251       verifyTableDescriptor(TABLE_NAME, FAMILY_0);
252 
253       try {
254         // Delete again - expect failure
255         admin.deleteColumn(TABLE_NAME, FAMILY_1);
256         Assert.fail("Delete a non-exist column family should fail");
257       } catch (Exception e) {
258         // Expected.
259       }
260     } finally {
261       admin.deleteTable(TABLE_NAME);
262     }
263   }
264 
265   private void verifyTableDescriptor(final TableName tableName,
266                                      final byte[]... families) throws IOException {
267     Admin admin = TEST_UTIL.getHBaseAdmin();
268 
269     // Verify descriptor from master
270     HTableDescriptor htd = admin.getTableDescriptor(tableName);
271     verifyTableDescriptor(htd, tableName, families);
272 
273     // Verify descriptor from HDFS
274     MasterFileSystem mfs = TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterFileSystem();
275     Path tableDir = FSUtils.getTableDir(mfs.getRootDir(), tableName);
276     htd = FSTableDescriptors.getTableDescriptorFromFs(mfs.getFileSystem(), tableDir);
277     verifyTableDescriptor(htd, tableName, families);
278   }
279 
280   private void verifyTableDescriptor(final HTableDescriptor htd,
281       final TableName tableName, final byte[]... families) {
282     Set<byte[]> htdFamilies = htd.getFamiliesKeys();
283     assertEquals(tableName, htd.getTableName());
284     assertEquals(families.length, htdFamilies.size());
285     for (byte[] familyName: families) {
286       assertTrue("Expected family " + Bytes.toString(familyName), htdFamilies.contains(familyName));
287     }
288   }
289 }