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