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