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.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertFalse; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023import static org.junit.jupiter.api.Assertions.fail; 024 025import java.util.Optional; 026import java.util.Set; 027import java.util.function.Supplier; 028import org.apache.hadoop.fs.Path; 029import org.apache.hadoop.hbase.HBaseParameterizedTestTemplate; 030import org.apache.hadoop.hbase.TableName; 031import org.apache.hadoop.hbase.master.MasterFileSystem; 032import org.apache.hadoop.hbase.testclassification.ClientTests; 033import org.apache.hadoop.hbase.testclassification.LargeTests; 034import org.apache.hadoop.hbase.util.Bytes; 035import org.apache.hadoop.hbase.util.CommonFSUtils; 036import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 037import org.apache.hadoop.hbase.util.FSTableDescriptors; 038import org.junit.Assert; 039import org.junit.jupiter.api.AfterAll; 040import org.junit.jupiter.api.BeforeAll; 041import org.junit.jupiter.api.Tag; 042import org.junit.jupiter.api.TestTemplate; 043 044/** 045 * Class to test asynchronous table admin operations 046 * @see TestAsyncTableAdminApi This test and it used to be joined it was taking longer than our ten 047 * minute timeout so they were split. 048 */ 049@Tag(LargeTests.TAG) 050@Tag(ClientTests.TAG) 051@HBaseParameterizedTestTemplate(name = "{index}: policy = {0}") 052public class TestAsyncTableAdminApi2 extends TestAsyncAdminBase { 053 054 public TestAsyncTableAdminApi2(Supplier<AsyncAdmin> admin) { 055 super(admin); 056 } 057 058 @BeforeAll 059 public static void setUpBeforeClass() throws Exception { 060 TestAsyncAdminBase.setUpBeforeClass(); 061 } 062 063 @AfterAll 064 public static void tearDownAfterClass() throws Exception { 065 TestAsyncAdminBase.tearDownAfterClass(); 066 } 067 068 @TestTemplate 069 public void testDisableCatalogTable() throws Exception { 070 try { 071 this.admin.disableTable(TableName.META_TABLE_NAME).join(); 072 fail("Expected to throw ConstraintException"); 073 } catch (Exception e) { 074 } 075 // Before the fix for HBASE-6146, the below table creation was failing as the hbase:meta table 076 // actually getting disabled by the disableTable() call. 077 createTableWithDefaultConf(tableName); 078 } 079 080 @TestTemplate 081 public void testAddColumnFamily() throws Exception { 082 // Create a table with two families 083 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName); 084 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY_0)); 085 admin.createTable(builder.build()).join(); 086 admin.disableTable(tableName).join(); 087 // Verify the table descriptor 088 verifyTableDescriptor(tableName, FAMILY_0); 089 090 // Modify the table removing one family and verify the descriptor 091 admin.addColumnFamily(tableName, ColumnFamilyDescriptorBuilder.of(FAMILY_1)).join(); 092 verifyTableDescriptor(tableName, FAMILY_0, FAMILY_1); 093 } 094 095 @TestTemplate 096 public void testAddSameColumnFamilyTwice() throws Exception { 097 // Create a table with one families 098 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName); 099 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY_0)); 100 admin.createTable(builder.build()).join(); 101 admin.disableTable(tableName).join(); 102 // Verify the table descriptor 103 verifyTableDescriptor(tableName, FAMILY_0); 104 105 // Modify the table removing one family and verify the descriptor 106 admin.addColumnFamily(tableName, ColumnFamilyDescriptorBuilder.of(FAMILY_1)).join(); 107 verifyTableDescriptor(tableName, FAMILY_0, FAMILY_1); 108 109 try { 110 // Add same column family again - expect failure 111 this.admin.addColumnFamily(tableName, ColumnFamilyDescriptorBuilder.of(FAMILY_1)).join(); 112 Assert.fail("Delete a non-exist column family should fail"); 113 } catch (Exception e) { 114 // Expected. 115 } 116 } 117 118 @TestTemplate 119 public void testModifyColumnFamily() throws Exception { 120 TableDescriptorBuilder tdBuilder = TableDescriptorBuilder.newBuilder(tableName); 121 ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder.of(FAMILY_0); 122 int blockSize = cfd.getBlocksize(); 123 admin.createTable(tdBuilder.setColumnFamily(cfd).build()).join(); 124 admin.disableTable(tableName).join(); 125 // Verify the table descriptor 126 verifyTableDescriptor(tableName, FAMILY_0); 127 128 int newBlockSize = 2 * blockSize; 129 cfd = ColumnFamilyDescriptorBuilder.newBuilder(FAMILY_0).setBlocksize(newBlockSize).build(); 130 // Modify colymn family 131 admin.modifyColumnFamily(tableName, cfd).join(); 132 133 TableDescriptor htd = admin.getDescriptor(tableName).get(); 134 ColumnFamilyDescriptor hcfd = htd.getColumnFamily(FAMILY_0); 135 assertTrue(hcfd.getBlocksize() == newBlockSize); 136 } 137 138 @TestTemplate 139 public void testModifyNonExistingColumnFamily() throws Exception { 140 TableDescriptorBuilder tdBuilder = TableDescriptorBuilder.newBuilder(tableName); 141 ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder.of(FAMILY_0); 142 int blockSize = cfd.getBlocksize(); 143 admin.createTable(tdBuilder.setColumnFamily(cfd).build()).join(); 144 admin.disableTable(tableName).join(); 145 // Verify the table descriptor 146 verifyTableDescriptor(tableName, FAMILY_0); 147 148 int newBlockSize = 2 * blockSize; 149 cfd = ColumnFamilyDescriptorBuilder.newBuilder(FAMILY_1).setBlocksize(newBlockSize).build(); 150 151 // Modify a column family that is not in the table. 152 try { 153 admin.modifyColumnFamily(tableName, cfd).join(); 154 Assert.fail("Modify a non-exist column family should fail"); 155 } catch (Exception e) { 156 // Expected. 157 } 158 } 159 160 @TestTemplate 161 public void testDeleteColumnFamily() throws Exception { 162 // Create a table with two families 163 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName); 164 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY_0)) 165 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY_1)); 166 admin.createTable(builder.build()).join(); 167 admin.disableTable(tableName).join(); 168 // Verify the table descriptor 169 verifyTableDescriptor(tableName, FAMILY_0, FAMILY_1); 170 171 // Modify the table removing one family and verify the descriptor 172 admin.deleteColumnFamily(tableName, FAMILY_1).join(); 173 verifyTableDescriptor(tableName, FAMILY_0); 174 } 175 176 @TestTemplate 177 public void testDeleteSameColumnFamilyTwice() throws Exception { 178 // Create a table with two families 179 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName); 180 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY_0)) 181 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY_1)); 182 admin.createTable(builder.build()).join(); 183 admin.disableTable(tableName).join(); 184 // Verify the table descriptor 185 verifyTableDescriptor(tableName, FAMILY_0, FAMILY_1); 186 187 // Modify the table removing one family and verify the descriptor 188 admin.deleteColumnFamily(tableName, FAMILY_1).join(); 189 verifyTableDescriptor(tableName, FAMILY_0); 190 191 try { 192 // Delete again - expect failure 193 admin.deleteColumnFamily(tableName, FAMILY_1).join(); 194 Assert.fail("Delete a non-exist column family should fail"); 195 } catch (Exception e) { 196 // Expected. 197 } 198 } 199 200 private void verifyTableDescriptor(final TableName tableName, final byte[]... families) 201 throws Exception { 202 // Verify descriptor from master 203 TableDescriptor htd = admin.getDescriptor(tableName).get(); 204 verifyTableDescriptor(htd, tableName, families); 205 206 // Verify descriptor from HDFS 207 MasterFileSystem mfs = TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterFileSystem(); 208 Path tableDir = CommonFSUtils.getTableDir(mfs.getRootDir(), tableName); 209 TableDescriptor td = FSTableDescriptors.getTableDescriptorFromFs(mfs.getFileSystem(), tableDir); 210 verifyTableDescriptor(td, tableName, families); 211 } 212 213 private void verifyTableDescriptor(final TableDescriptor htd, final TableName tableName, 214 final byte[]... families) { 215 Set<byte[]> htdFamilies = htd.getColumnFamilyNames(); 216 assertEquals(tableName, htd.getTableName()); 217 assertEquals(families.length, htdFamilies.size()); 218 for (byte[] familyName : families) { 219 assertTrue(htdFamilies.contains(familyName), "Expected family " + Bytes.toString(familyName)); 220 } 221 } 222 223 @TestTemplate 224 public void testCompactionTimestamps() throws Exception { 225 createTableWithDefaultConf(tableName); 226 AsyncTable<?> table = ASYNC_CONN.getTable(tableName); 227 Optional<Long> ts = admin.getLastMajorCompactionTimestamp(tableName).get(); 228 assertFalse(ts.isPresent()); 229 Put p = new Put(Bytes.toBytes("row1")); 230 p.addColumn(FAMILY, Bytes.toBytes("q"), Bytes.toBytes("v")); 231 table.put(p).join(); 232 ts = admin.getLastMajorCompactionTimestamp(tableName).get(); 233 // no files written -> no data 234 assertFalse(ts.isPresent()); 235 236 admin.flush(tableName).join(); 237 ts = admin.getLastMajorCompactionTimestamp(tableName).get(); 238 // still 0, we flushed a file, but no major compaction happened 239 assertFalse(ts.isPresent()); 240 241 byte[] regionName = ASYNC_CONN.getRegionLocator(tableName) 242 .getRegionLocation(Bytes.toBytes("row1")).get().getRegion().getRegionName(); 243 Optional<Long> ts1 = admin.getLastMajorCompactionTimestampForRegion(regionName).get(); 244 assertFalse(ts1.isPresent()); 245 p = new Put(Bytes.toBytes("row2")); 246 p.addColumn(FAMILY, Bytes.toBytes("q"), Bytes.toBytes("v")); 247 table.put(p).join(); 248 admin.flush(tableName).join(); 249 ts1 = admin.getLastMajorCompactionTimestamp(tableName).get(); 250 // make sure the region API returns the same value, as the old file is still around 251 assertFalse(ts1.isPresent()); 252 253 for (int i = 0; i < 3; i++) { 254 table.put(p).join(); 255 admin.flush(tableName).join(); 256 } 257 admin.majorCompact(tableName).join(); 258 long curt = EnvironmentEdgeManager.currentTime(); 259 long waitTime = 10000; 260 long endt = curt + waitTime; 261 CompactionState state = admin.getCompactionState(tableName).get(); 262 LOG.info("Current compaction state 1 is " + state); 263 while (state == CompactionState.NONE && curt < endt) { 264 Thread.sleep(100); 265 state = admin.getCompactionState(tableName).get(); 266 curt = EnvironmentEdgeManager.currentTime(); 267 LOG.info("Current compaction state 2 is " + state); 268 } 269 // Now, should have the right compaction state, let's wait until the compaction is done 270 if (state == CompactionState.MAJOR) { 271 state = admin.getCompactionState(tableName).get(); 272 LOG.info("Current compaction state 3 is " + state); 273 while (state != CompactionState.NONE && curt < endt) { 274 Thread.sleep(10); 275 state = admin.getCompactionState(tableName).get(); 276 LOG.info("Current compaction state 4 is " + state); 277 } 278 } 279 // Sleep to wait region server report 280 Thread 281 .sleep(TEST_UTIL.getConfiguration().getInt("hbase.regionserver.msginterval", 3 * 1000) * 2); 282 283 ts = admin.getLastMajorCompactionTimestamp(tableName).get(); 284 // after a compaction our earliest timestamp will have progressed forward 285 assertTrue(ts.isPresent()); 286 assertTrue(ts.get() > 0); 287 // region api still the same 288 ts1 = admin.getLastMajorCompactionTimestampForRegion(regionName).get(); 289 assertTrue(ts1.isPresent()); 290 assertEquals(ts.get(), ts1.get()); 291 table.put(p).join(); 292 admin.flush(tableName).join(); 293 ts = admin.getLastMajorCompactionTimestamp(tableName).join(); 294 assertTrue(ts.isPresent()); 295 assertEquals(ts.get(), ts1.get()); 296 ts1 = admin.getLastMajorCompactionTimestampForRegion(regionName).get(); 297 assertTrue(ts1.isPresent()); 298 assertEquals(ts.get(), ts1.get()); 299 } 300}