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.master.procedure; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.io.IOException; 025 026import org.apache.hadoop.hbase.ConcurrentTableModificationException; 027import org.apache.hadoop.hbase.DoNotRetryIOException; 028import org.apache.hadoop.hbase.HBaseClassTestRule; 029import org.apache.hadoop.hbase.HColumnDescriptor; 030import org.apache.hadoop.hbase.HTableDescriptor; 031import org.apache.hadoop.hbase.InvalidFamilyOperationException; 032import org.apache.hadoop.hbase.TableName; 033import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 034import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 035import org.apache.hadoop.hbase.client.PerClientRandomNonceGenerator; 036import org.apache.hadoop.hbase.client.RegionInfo; 037import org.apache.hadoop.hbase.client.TableDescriptor; 038import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 039import org.apache.hadoop.hbase.master.procedure.MasterProcedureTestingUtility.StepHook; 040import org.apache.hadoop.hbase.procedure2.Procedure; 041import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 042import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; 043import org.apache.hadoop.hbase.testclassification.MasterTests; 044import org.apache.hadoop.hbase.testclassification.MediumTests; 045import org.apache.hadoop.hbase.util.Bytes; 046import org.apache.hadoop.hbase.util.NonceKey; 047import org.apache.hadoop.hbase.util.TableDescriptorChecker; 048import org.junit.Assert; 049import org.junit.ClassRule; 050import org.junit.Rule; 051import org.junit.Test; 052import org.junit.experimental.categories.Category; 053import org.junit.rules.TestName; 054 055@Category({MasterTests.class, MediumTests.class}) 056public class TestModifyTableProcedure extends TestTableDDLProcedureBase { 057 058 @ClassRule 059 public static final HBaseClassTestRule CLASS_RULE = 060 HBaseClassTestRule.forClass(TestModifyTableProcedure.class); 061 062 @Rule public TestName name = new TestName(); 063 064 private static final String column_Family1 = "cf1"; 065 private static final String column_Family2 = "cf2"; 066 private static final String column_Family3 = "cf3"; 067 068 @Test 069 public void testModifyTable() throws Exception { 070 final TableName tableName = TableName.valueOf(name.getMethodName()); 071 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 072 073 MasterProcedureTestingUtility.createTable(procExec, tableName, null, "cf"); 074 UTIL.getAdmin().disableTable(tableName); 075 076 // Modify the table descriptor 077 HTableDescriptor htd = new HTableDescriptor(UTIL.getAdmin().getTableDescriptor(tableName)); 078 079 // Test 1: Modify 1 property 080 long newMaxFileSize = htd.getMaxFileSize() * 2; 081 htd.setMaxFileSize(newMaxFileSize); 082 htd.setRegionReplication(3); 083 084 long procId1 = ProcedureTestingUtility.submitAndWait( 085 procExec, new ModifyTableProcedure(procExec.getEnvironment(), htd)); 086 ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId1)); 087 088 HTableDescriptor currentHtd = UTIL.getAdmin().getTableDescriptor(tableName); 089 assertEquals(newMaxFileSize, currentHtd.getMaxFileSize()); 090 091 // Test 2: Modify multiple properties 092 boolean newReadOnlyOption = htd.isReadOnly() ? false : true; 093 long newMemStoreFlushSize = htd.getMemStoreFlushSize() * 2; 094 htd.setReadOnly(newReadOnlyOption); 095 htd.setMemStoreFlushSize(newMemStoreFlushSize); 096 097 long procId2 = ProcedureTestingUtility.submitAndWait( 098 procExec, new ModifyTableProcedure(procExec.getEnvironment(), htd)); 099 ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId2)); 100 101 currentHtd = UTIL.getAdmin().getTableDescriptor(tableName); 102 assertEquals(newReadOnlyOption, currentHtd.isReadOnly()); 103 assertEquals(newMemStoreFlushSize, currentHtd.getMemStoreFlushSize()); 104 } 105 106 @Test 107 public void testModifyTableAddCF() throws Exception { 108 final TableName tableName = TableName.valueOf(name.getMethodName()); 109 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 110 111 MasterProcedureTestingUtility.createTable(procExec, tableName, null, "cf1"); 112 HTableDescriptor currentHtd = UTIL.getAdmin().getTableDescriptor(tableName); 113 assertEquals(1, currentHtd.getFamiliesKeys().size()); 114 115 // Test 1: Modify the table descriptor online 116 String cf2 = "cf2"; 117 HTableDescriptor htd = new HTableDescriptor(UTIL.getAdmin().getTableDescriptor(tableName)); 118 htd.addFamily(new HColumnDescriptor(cf2)); 119 120 long procId = ProcedureTestingUtility.submitAndWait( 121 procExec, new ModifyTableProcedure(procExec.getEnvironment(), htd)); 122 ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId)); 123 124 currentHtd = UTIL.getAdmin().getTableDescriptor(tableName); 125 assertEquals(2, currentHtd.getFamiliesKeys().size()); 126 assertTrue(currentHtd.hasFamily(Bytes.toBytes(cf2))); 127 128 // Test 2: Modify the table descriptor offline 129 UTIL.getAdmin().disableTable(tableName); 130 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 131 String cf3 = "cf3"; 132 HTableDescriptor htd2 = 133 new HTableDescriptor(UTIL.getAdmin().getTableDescriptor(tableName)); 134 htd2.addFamily(new HColumnDescriptor(cf3)); 135 136 long procId2 = 137 ProcedureTestingUtility.submitAndWait(procExec, 138 new ModifyTableProcedure(procExec.getEnvironment(), htd2)); 139 ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId2)); 140 141 currentHtd = UTIL.getAdmin().getTableDescriptor(tableName); 142 assertTrue(currentHtd.hasFamily(Bytes.toBytes(cf3))); 143 assertEquals(3, currentHtd.getFamiliesKeys().size()); 144 } 145 146 @Test 147 public void testModifyTableDeleteCF() throws Exception { 148 final TableName tableName = TableName.valueOf(name.getMethodName()); 149 final String cf1 = "cf1"; 150 final String cf2 = "cf2"; 151 final String cf3 = "cf3"; 152 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 153 154 MasterProcedureTestingUtility.createTable(procExec, tableName, null, cf1, cf2, cf3); 155 HTableDescriptor currentHtd = UTIL.getAdmin().getTableDescriptor(tableName); 156 assertEquals(3, currentHtd.getFamiliesKeys().size()); 157 158 // Test 1: Modify the table descriptor 159 HTableDescriptor htd = new HTableDescriptor(UTIL.getAdmin().getTableDescriptor(tableName)); 160 htd.removeFamily(Bytes.toBytes(cf2)); 161 162 long procId = ProcedureTestingUtility.submitAndWait( 163 procExec, new ModifyTableProcedure(procExec.getEnvironment(), htd)); 164 ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId)); 165 166 currentHtd = UTIL.getAdmin().getTableDescriptor(tableName); 167 assertEquals(2, currentHtd.getFamiliesKeys().size()); 168 assertFalse(currentHtd.hasFamily(Bytes.toBytes(cf2))); 169 170 // Test 2: Modify the table descriptor offline 171 UTIL.getAdmin().disableTable(tableName); 172 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 173 174 HTableDescriptor htd2 = 175 new HTableDescriptor(UTIL.getAdmin().getTableDescriptor(tableName)); 176 htd2.removeFamily(Bytes.toBytes(cf3)); 177 // Disable Sanity check 178 htd2.setConfiguration(TableDescriptorChecker.TABLE_SANITY_CHECKS, Boolean.FALSE.toString()); 179 180 long procId2 = 181 ProcedureTestingUtility.submitAndWait(procExec, 182 new ModifyTableProcedure(procExec.getEnvironment(), htd2)); 183 ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId2)); 184 185 currentHtd = UTIL.getAdmin().getTableDescriptor(tableName); 186 assertEquals(1, currentHtd.getFamiliesKeys().size()); 187 assertFalse(currentHtd.hasFamily(Bytes.toBytes(cf3))); 188 189 //Removing the last family will fail 190 HTableDescriptor htd3 = 191 new HTableDescriptor(UTIL.getAdmin().getTableDescriptor(tableName)); 192 htd3.removeFamily(Bytes.toBytes(cf1)); 193 long procId3 = 194 ProcedureTestingUtility.submitAndWait(procExec, 195 new ModifyTableProcedure(procExec.getEnvironment(), htd3)); 196 final Procedure<?> result = procExec.getResult(procId3); 197 assertEquals(true, result.isFailed()); 198 Throwable cause = ProcedureTestingUtility.getExceptionCause(result); 199 assertTrue("expected DoNotRetryIOException, got " + cause, 200 cause instanceof DoNotRetryIOException); 201 assertEquals(1, currentHtd.getFamiliesKeys().size()); 202 assertTrue(currentHtd.hasFamily(Bytes.toBytes(cf1))); 203 } 204 205 @Test 206 public void testRecoveryAndDoubleExecutionOffline() throws Exception { 207 final TableName tableName = TableName.valueOf(name.getMethodName()); 208 final String cf2 = "cf2"; 209 final String cf3 = "cf3"; 210 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 211 212 // create the table 213 RegionInfo[] regions = MasterProcedureTestingUtility.createTable( 214 procExec, tableName, null, "cf1", cf3); 215 UTIL.getAdmin().disableTable(tableName); 216 217 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 218 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 219 220 // Modify multiple properties of the table. 221 TableDescriptor oldDescriptor = UTIL.getAdmin().getDescriptor(tableName); 222 TableDescriptor newDescriptor = TableDescriptorBuilder.newBuilder(oldDescriptor) 223 .setCompactionEnabled(!oldDescriptor.isCompactionEnabled()) 224 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(cf2)) 225 .removeColumnFamily(Bytes.toBytes(cf3)) 226 .setRegionReplication(3) 227 .build(); 228 229 // Start the Modify procedure && kill the executor 230 long procId = procExec.submitProcedure( 231 new ModifyTableProcedure(procExec.getEnvironment(), newDescriptor)); 232 233 // Restart the executor and execute the step twice 234 MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId); 235 236 // Validate descriptor 237 TableDescriptor currentDescriptor = UTIL.getAdmin().getDescriptor(tableName); 238 assertEquals(newDescriptor.isCompactionEnabled(), currentDescriptor.isCompactionEnabled()); 239 assertEquals(2, newDescriptor.getColumnFamilyNames().size()); 240 241 // cf2 should be added cf3 should be removed 242 MasterProcedureTestingUtility.validateTableCreation(UTIL.getHBaseCluster().getMaster(), 243 tableName, regions, false, "cf1", cf2); 244 } 245 246 @Test 247 public void testRecoveryAndDoubleExecutionOnline() throws Exception { 248 final TableName tableName = TableName.valueOf(name.getMethodName()); 249 final String cf2 = "cf2"; 250 final String cf3 = "cf3"; 251 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 252 253 // create the table 254 RegionInfo[] regions = MasterProcedureTestingUtility.createTable( 255 procExec, tableName, null, "cf1", cf3); 256 257 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 258 259 // Modify multiple properties of the table. 260 HTableDescriptor htd = new HTableDescriptor(UTIL.getAdmin().getTableDescriptor(tableName)); 261 boolean newCompactionEnableOption = htd.isCompactionEnabled() ? false : true; 262 htd.setCompactionEnabled(newCompactionEnableOption); 263 htd.addFamily(new HColumnDescriptor(cf2)); 264 htd.removeFamily(Bytes.toBytes(cf3)); 265 266 // Start the Modify procedure && kill the executor 267 long procId = procExec.submitProcedure( 268 new ModifyTableProcedure(procExec.getEnvironment(), htd)); 269 270 // Restart the executor and execute the step twice 271 MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId); 272 273 // Validate descriptor 274 HTableDescriptor currentHtd = UTIL.getAdmin().getTableDescriptor(tableName); 275 assertEquals(newCompactionEnableOption, currentHtd.isCompactionEnabled()); 276 assertEquals(2, currentHtd.getFamiliesKeys().size()); 277 assertTrue(currentHtd.hasFamily(Bytes.toBytes(cf2))); 278 assertFalse(currentHtd.hasFamily(Bytes.toBytes(cf3))); 279 280 // cf2 should be added cf3 should be removed 281 MasterProcedureTestingUtility.validateTableCreation(UTIL.getHBaseCluster().getMaster(), 282 tableName, regions, "cf1", cf2); 283 } 284 285 @Test 286 public void testColumnFamilyAdditionTwiceWithNonce() throws Exception { 287 final TableName tableName = TableName.valueOf(name.getMethodName()); 288 final String cf2 = "cf2"; 289 final String cf3 = "cf3"; 290 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 291 292 // create the table 293 RegionInfo[] regions = 294 MasterProcedureTestingUtility.createTable(procExec, tableName, null, "cf1", cf3); 295 296 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 297 // Modify multiple properties of the table. 298 TableDescriptor td = UTIL.getAdmin().getDescriptor(tableName); 299 TableDescriptor newTd = 300 TableDescriptorBuilder.newBuilder(td).setCompactionEnabled(!td.isCompactionEnabled()) 301 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(cf2)).build(); 302 303 PerClientRandomNonceGenerator nonceGenerator = PerClientRandomNonceGenerator.get(); 304 long nonceGroup = nonceGenerator.getNonceGroup(); 305 long newNonce = nonceGenerator.newNonce(); 306 NonceKey nonceKey = new NonceKey(nonceGroup, newNonce); 307 procExec.registerNonce(nonceKey); 308 309 // Start the Modify procedure && kill the executor 310 final long procId = procExec 311 .submitProcedure(new ModifyTableProcedure(procExec.getEnvironment(), newTd), nonceKey); 312 313 // Restart the executor after MODIFY_TABLE_UPDATE_TABLE_DESCRIPTOR and try to add column family 314 // as nonce are there , we should not fail 315 MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId, new StepHook() { 316 @Override 317 public boolean execute(int step) throws IOException { 318 if (step == 3) { 319 return procId == UTIL.getHBaseCluster().getMaster().addColumn(tableName, 320 ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(cf2)).build(), nonceGroup, 321 newNonce); 322 } 323 return true; 324 } 325 }); 326 327 //Try with different nonce, now it should fail the checks 328 try { 329 UTIL.getHBaseCluster().getMaster().addColumn(tableName, 330 ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(cf2)).build(), nonceGroup, 331 nonceGenerator.newNonce()); 332 Assert.fail(); 333 } catch (InvalidFamilyOperationException e) { 334 } 335 336 // Validate descriptor 337 TableDescriptor currentHtd = UTIL.getAdmin().getDescriptor(tableName); 338 assertEquals(!td.isCompactionEnabled(), currentHtd.isCompactionEnabled()); 339 assertEquals(3, currentHtd.getColumnFamilyCount()); 340 assertTrue(currentHtd.hasColumnFamily(Bytes.toBytes(cf2))); 341 assertTrue(currentHtd.hasColumnFamily(Bytes.toBytes(cf3))); 342 343 // cf2 should be added 344 MasterProcedureTestingUtility.validateTableCreation(UTIL.getHBaseCluster().getMaster(), 345 tableName, regions, "cf1", cf2, cf3); 346 } 347 348 @Test 349 public void testRollbackAndDoubleExecutionOnline() throws Exception { 350 final TableName tableName = TableName.valueOf(name.getMethodName()); 351 final String familyName = "cf2"; 352 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 353 354 // create the table 355 RegionInfo[] regions = MasterProcedureTestingUtility.createTable( 356 procExec, tableName, null, "cf1"); 357 358 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 359 360 TableDescriptor td = UTIL.getAdmin().getDescriptor(tableName); 361 TableDescriptor newTd = 362 TableDescriptorBuilder.newBuilder(td).setCompactionEnabled(!td.isCompactionEnabled()) 363 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(familyName)).build(); 364 365 // Start the Modify procedure && kill the executor 366 long procId = 367 procExec.submitProcedure(new ModifyTableProcedure(procExec.getEnvironment(), newTd)); 368 369 int lastStep = 3; // failing before MODIFY_TABLE_UPDATE_TABLE_DESCRIPTOR 370 MasterProcedureTestingUtility.testRollbackAndDoubleExecution(procExec, procId, lastStep); 371 372 // cf2 should not be present 373 MasterProcedureTestingUtility.validateTableCreation(UTIL.getHBaseCluster().getMaster(), 374 tableName, regions, "cf1"); 375 } 376 377 @Test 378 public void testRollbackAndDoubleExecutionOffline() throws Exception { 379 final TableName tableName = TableName.valueOf(name.getMethodName()); 380 final String familyName = "cf2"; 381 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 382 383 // create the table 384 RegionInfo[] regions = MasterProcedureTestingUtility.createTable( 385 procExec, tableName, null, "cf1"); 386 UTIL.getAdmin().disableTable(tableName); 387 388 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 389 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 390 391 TableDescriptor td = UTIL.getAdmin().getDescriptor(tableName); 392 TableDescriptor newTd = 393 TableDescriptorBuilder.newBuilder(td).setCompactionEnabled(!td.isCompactionEnabled()) 394 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(familyName)).setRegionReplication(3) 395 .build(); 396 397 // Start the Modify procedure && kill the executor 398 long procId = procExec.submitProcedure( 399 new ModifyTableProcedure(procExec.getEnvironment(), newTd)); 400 401 // Restart the executor and rollback the step twice 402 int lastStep = 3; // failing before MODIFY_TABLE_UPDATE_TABLE_DESCRIPTOR 403 MasterProcedureTestingUtility.testRollbackAndDoubleExecution(procExec, procId, lastStep); 404 405 // cf2 should not be present 406 MasterProcedureTestingUtility.validateTableCreation(UTIL.getHBaseCluster().getMaster(), 407 tableName, regions, "cf1"); 408 } 409 410 @Test 411 public void testConcurrentAddColumnFamily() throws IOException, InterruptedException { 412 final TableName tableName = TableName.valueOf(name.getMethodName()); 413 UTIL.createTable(tableName, column_Family1); 414 415 class ConcurrentAddColumnFamily extends Thread { 416 TableName tableName = null; 417 HColumnDescriptor hcd = null; 418 boolean exception; 419 420 public ConcurrentAddColumnFamily(TableName tableName, HColumnDescriptor hcd) { 421 this.tableName = tableName; 422 this.hcd = hcd; 423 this.exception = false; 424 } 425 426 public void run() { 427 try { 428 UTIL.getAdmin().addColumnFamily(tableName, hcd); 429 } catch (Exception e) { 430 if (e.getClass().equals(ConcurrentTableModificationException.class)) { 431 this.exception = true; 432 } 433 } 434 } 435 } 436 ConcurrentAddColumnFamily t1 = 437 new ConcurrentAddColumnFamily(tableName, new HColumnDescriptor(column_Family2)); 438 ConcurrentAddColumnFamily t2 = 439 new ConcurrentAddColumnFamily(tableName, new HColumnDescriptor(column_Family3)); 440 441 t1.start(); 442 t2.start(); 443 444 t1.join(); 445 t2.join(); 446 int noOfColumnFamilies = UTIL.getAdmin().getDescriptor(tableName).getColumnFamilies().length; 447 assertTrue("Expected ConcurrentTableModificationException.", 448 ((t1.exception || t2.exception) && noOfColumnFamilies == 2) || noOfColumnFamilies == 3); 449 } 450 451 @Test 452 public void testConcurrentDeleteColumnFamily() throws IOException, InterruptedException { 453 final TableName tableName = TableName.valueOf(name.getMethodName()); 454 HTableDescriptor htd = new HTableDescriptor(tableName); 455 htd.addFamily(new HColumnDescriptor(column_Family1)); 456 htd.addFamily(new HColumnDescriptor(column_Family2)); 457 htd.addFamily(new HColumnDescriptor(column_Family3)); 458 UTIL.getAdmin().createTable(htd); 459 460 class ConcurrentCreateDeleteTable extends Thread { 461 TableName tableName = null; 462 String columnFamily = null; 463 boolean exception; 464 465 public ConcurrentCreateDeleteTable(TableName tableName, String columnFamily) { 466 this.tableName = tableName; 467 this.columnFamily = columnFamily; 468 this.exception = false; 469 } 470 471 public void run() { 472 try { 473 UTIL.getAdmin().deleteColumnFamily(tableName, columnFamily.getBytes()); 474 } catch (Exception e) { 475 if (e.getClass().equals(ConcurrentTableModificationException.class)) { 476 this.exception = true; 477 } 478 } 479 } 480 } 481 ConcurrentCreateDeleteTable t1 = new ConcurrentCreateDeleteTable(tableName, column_Family2); 482 ConcurrentCreateDeleteTable t2 = new ConcurrentCreateDeleteTable(tableName, column_Family3); 483 484 t1.start(); 485 t2.start(); 486 487 t1.join(); 488 t2.join(); 489 int noOfColumnFamilies = UTIL.getAdmin().getDescriptor(tableName).getColumnFamilies().length; 490 assertTrue("Expected ConcurrentTableModificationException.", 491 ((t1.exception || t2.exception) && noOfColumnFamilies == 2) || noOfColumnFamilies == 1); 492 } 493 494 @Test 495 public void testConcurrentModifyColumnFamily() throws IOException, InterruptedException { 496 final TableName tableName = TableName.valueOf(name.getMethodName()); 497 UTIL.createTable(tableName, column_Family1); 498 499 class ConcurrentModifyColumnFamily extends Thread { 500 TableName tableName = null; 501 ColumnFamilyDescriptor hcd = null; 502 boolean exception; 503 504 public ConcurrentModifyColumnFamily(TableName tableName, ColumnFamilyDescriptor hcd) { 505 this.tableName = tableName; 506 this.hcd = hcd; 507 this.exception = false; 508 } 509 510 public void run() { 511 try { 512 UTIL.getAdmin().modifyColumnFamily(tableName, hcd); 513 } catch (Exception e) { 514 if (e.getClass().equals(ConcurrentTableModificationException.class)) { 515 this.exception = true; 516 } 517 } 518 } 519 } 520 ColumnFamilyDescriptor modColumnFamily1 = ColumnFamilyDescriptorBuilder 521 .newBuilder(column_Family1.getBytes()).setMaxVersions(5).build(); 522 ColumnFamilyDescriptor modColumnFamily2 = ColumnFamilyDescriptorBuilder 523 .newBuilder(column_Family1.getBytes()).setMaxVersions(6).build(); 524 525 ConcurrentModifyColumnFamily t1 = new ConcurrentModifyColumnFamily(tableName, modColumnFamily1); 526 ConcurrentModifyColumnFamily t2 = new ConcurrentModifyColumnFamily(tableName, modColumnFamily2); 527 528 t1.start(); 529 t2.start(); 530 531 t1.join(); 532 t2.join(); 533 534 int maxVersions = UTIL.getAdmin().getDescriptor(tableName) 535 .getColumnFamily(column_Family1.getBytes()).getMaxVersions(); 536 assertTrue("Expected ConcurrentTableModificationException.", (t1.exception && maxVersions == 5) 537 || (t2.exception && maxVersions == 6) || !(t1.exception && t2.exception)); 538 } 539 540 @Test 541 public void testConcurrentModifyTable() throws IOException, InterruptedException { 542 final TableName tableName = TableName.valueOf(name.getMethodName()); 543 UTIL.createTable(tableName, column_Family1); 544 545 class ConcurrentModifyTable extends Thread { 546 TableName tableName = null; 547 TableDescriptor htd = null; 548 boolean exception; 549 550 public ConcurrentModifyTable(TableName tableName, TableDescriptor htd) { 551 this.tableName = tableName; 552 this.htd = htd; 553 this.exception = false; 554 } 555 556 public void run() { 557 try { 558 UTIL.getAdmin().modifyTable(tableName, htd); 559 } catch (Exception e) { 560 if (e.getClass().equals(ConcurrentTableModificationException.class)) { 561 this.exception = true; 562 } 563 } 564 } 565 } 566 TableDescriptor htd = UTIL.getAdmin().getDescriptor(tableName); 567 TableDescriptor modifiedDescriptor = 568 TableDescriptorBuilder.newBuilder(htd).setCompactionEnabled(false).build(); 569 570 ConcurrentModifyTable t1 = new ConcurrentModifyTable(tableName, modifiedDescriptor); 571 ConcurrentModifyTable t2 = new ConcurrentModifyTable(tableName, modifiedDescriptor); 572 573 t1.start(); 574 t2.start(); 575 576 t1.join(); 577 t2.join(); 578 assertFalse("Expected ConcurrentTableModificationException.", (t1.exception || t2.exception)); 579 } 580}