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