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.assertNull; 023import static org.junit.Assert.assertSame; 024import static org.junit.Assert.assertTrue; 025 026import java.io.IOException; 027import java.lang.reflect.Field; 028import java.lang.reflect.Method; 029import java.util.Arrays; 030import java.util.HashMap; 031import java.util.List; 032import java.util.Map; 033import org.apache.hadoop.hbase.HBaseClassTestRule; 034import org.apache.hadoop.hbase.ServerName; 035import org.apache.hadoop.hbase.TableName; 036import org.apache.hadoop.hbase.client.RegionInfo; 037import org.apache.hadoop.hbase.client.RegionInfoBuilder; 038import org.apache.hadoop.hbase.master.locking.LockProcedure; 039import org.apache.hadoop.hbase.master.procedure.TableProcedureInterface.TableOperationType; 040import org.apache.hadoop.hbase.procedure2.LockType; 041import org.apache.hadoop.hbase.procedure2.LockedResource; 042import org.apache.hadoop.hbase.procedure2.LockedResourceType; 043import org.apache.hadoop.hbase.procedure2.Procedure; 044import org.apache.hadoop.hbase.procedure2.ProcedureEvent; 045import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility.TestProcedure; 046import org.apache.hadoop.hbase.testclassification.MasterTests; 047import org.apache.hadoop.hbase.testclassification.SmallTests; 048import org.apache.hadoop.hbase.util.Bytes; 049import org.junit.After; 050import org.junit.Before; 051import org.junit.ClassRule; 052import org.junit.Rule; 053import org.junit.Test; 054import org.junit.experimental.categories.Category; 055import org.junit.rules.TestName; 056import org.slf4j.Logger; 057import org.slf4j.LoggerFactory; 058 059@Category({ MasterTests.class, SmallTests.class }) 060public class TestMasterProcedureScheduler { 061 062 @ClassRule 063 public static final HBaseClassTestRule CLASS_RULE = 064 HBaseClassTestRule.forClass(TestMasterProcedureScheduler.class); 065 066 private static final Logger LOG = LoggerFactory.getLogger(TestMasterProcedureScheduler.class); 067 068 private MasterProcedureScheduler queue; 069 070 private Map<Long, Procedure<?>> procedures; 071 072 @Rule 073 public TestName name = new TestName(); 074 075 @Before 076 public void setUp() throws IOException { 077 procedures = new HashMap<>(); 078 queue = new MasterProcedureScheduler(procedures::get); 079 queue.start(); 080 } 081 082 @After 083 public void tearDown() throws IOException { 084 assertEquals("proc-queue expected to be empty", 0, queue.size()); 085 queue.stop(); 086 queue.clear(); 087 } 088 089 /** 090 * Verify simple create/insert/fetch/delete of the table queue. 091 */ 092 @Test 093 public void testSimpleTableOpsQueues() throws Exception { 094 final int NUM_TABLES = 10; 095 final int NUM_ITEMS = 10; 096 097 int count = 0; 098 for (int i = 1; i <= NUM_TABLES; ++i) { 099 TableName tableName = TableName.valueOf(String.format("test-%04d", i)); 100 // insert items 101 for (int j = 1; j <= NUM_ITEMS; ++j) { 102 queue.addBack(new TestTableProcedure(i * 1000 + j, tableName, 103 TableProcedureInterface.TableOperationType.REGION_EDIT)); 104 assertEquals(++count, queue.size()); 105 } 106 } 107 assertEquals(NUM_TABLES * NUM_ITEMS, queue.size()); 108 109 for (int j = 1; j <= NUM_ITEMS; ++j) { 110 for (int i = 1; i <= NUM_TABLES; ++i) { 111 Procedure<?> proc = queue.poll(); 112 assertTrue(proc != null); 113 TableName tableName = ((TestTableProcedure) proc).getTableName(); 114 queue.waitTableExclusiveLock(proc, tableName); 115 queue.wakeTableExclusiveLock(proc, tableName); 116 queue.completionCleanup(proc); 117 assertEquals(--count, queue.size()); 118 assertEquals(i * 1000 + j, proc.getProcId()); 119 } 120 } 121 assertEquals(0, queue.size()); 122 123 for (int i = 1; i <= NUM_TABLES; ++i) { 124 final TableName tableName = TableName.valueOf(String.format("test-%04d", i)); 125 final TestTableProcedure dummyProc = 126 new TestTableProcedure(100, tableName, TableProcedureInterface.TableOperationType.DELETE); 127 // complete the table deletion 128 assertTrue(queue.markTableAsDeleted(tableName, dummyProc)); 129 } 130 } 131 132 /** 133 * Check that the table queue is not deletable until every procedure in-progress is completed 134 * (this is a special case for write-locks). 135 */ 136 @Test 137 public void testCreateDeleteTableOperationsWithWriteLock() throws Exception { 138 final TableName tableName = TableName.valueOf(name.getMethodName()); 139 140 final TestTableProcedure dummyProc = 141 new TestTableProcedure(100, tableName, TableProcedureInterface.TableOperationType.DELETE); 142 143 queue.addBack( 144 new TestTableProcedure(1, tableName, TableProcedureInterface.TableOperationType.EDIT)); 145 146 // table can't be deleted because one item is in the queue 147 assertFalse(queue.markTableAsDeleted(tableName, dummyProc)); 148 149 // fetch item and take a lock 150 Procedure<?> proc = queue.poll(); 151 assertEquals(1, proc.getProcId()); 152 // take the xlock 153 assertEquals(false, queue.waitTableExclusiveLock(proc, tableName)); 154 // table can't be deleted because we have the lock 155 assertEquals(0, queue.size()); 156 assertFalse(queue.markTableAsDeleted(tableName, dummyProc)); 157 // release the xlock 158 queue.wakeTableExclusiveLock(proc, tableName); 159 // complete the table deletion 160 assertTrue(queue.markTableAsDeleted(tableName, proc)); 161 } 162 163 /** 164 * Check that the table queue is not deletable until every procedure in-progress is completed 165 * (this is a special case for read-locks). 166 */ 167 @Test 168 public void testCreateDeleteTableOperationsWithReadLock() throws Exception { 169 final TableName tableName = TableName.valueOf(name.getMethodName()); 170 final int nitems = 2; 171 172 final TestTableProcedure dummyProc = 173 new TestTableProcedure(100, tableName, TableProcedureInterface.TableOperationType.DELETE); 174 175 for (int i = 1; i <= nitems; ++i) { 176 queue.addBack( 177 new TestTableProcedure(i, tableName, TableProcedureInterface.TableOperationType.READ)); 178 } 179 180 // table can't be deleted because one item is in the queue 181 assertFalse(queue.markTableAsDeleted(tableName, dummyProc)); 182 183 Procedure<?>[] procs = new Procedure[nitems]; 184 for (int i = 0; i < nitems; ++i) { 185 // fetch item and take a lock 186 Procedure<?> proc = queue.poll(); 187 procs[i] = proc; 188 assertEquals(i + 1, proc.getProcId()); 189 // take the rlock 190 assertEquals(false, queue.waitTableSharedLock(proc, tableName)); 191 // table can't be deleted because we have locks and/or items in the queue 192 assertFalse(queue.markTableAsDeleted(tableName, dummyProc)); 193 } 194 195 for (int i = 0; i < nitems; ++i) { 196 // table can't be deleted because we have locks 197 assertFalse(queue.markTableAsDeleted(tableName, dummyProc)); 198 // release the rlock 199 queue.wakeTableSharedLock(procs[i], tableName); 200 } 201 202 // there are no items and no lock in the queeu 203 assertEquals(0, queue.size()); 204 // complete the table deletion 205 assertTrue(queue.markTableAsDeleted(tableName, dummyProc)); 206 } 207 208 /** 209 * Verify the correct logic of RWLocks on the queue 210 */ 211 @Test 212 public void testVerifyRwLocks() throws Exception { 213 final TableName tableName = TableName.valueOf(name.getMethodName()); 214 queue.addBack( 215 new TestTableProcedure(1, tableName, TableProcedureInterface.TableOperationType.EDIT)); 216 queue.addBack( 217 new TestTableProcedure(2, tableName, TableProcedureInterface.TableOperationType.READ)); 218 queue.addBack( 219 new TestTableProcedure(3, tableName, TableProcedureInterface.TableOperationType.EDIT)); 220 221 // Fetch the 1st item and take the write lock 222 Procedure<?> proc = queue.poll(); 223 assertEquals(1, proc.getProcId()); 224 assertEquals(false, queue.waitTableExclusiveLock(proc, tableName)); 225 226 // Fetch the 2nd item and verify that the lock can't be acquired 227 assertEquals(null, queue.poll(0)); 228 229 // Release the write lock and acquire the read lock 230 releaseTableExclusiveLockAndComplete(proc, tableName); 231 232 // Fetch the 2nd item and take the read lock 233 Procedure<?> rdProc = queue.poll(); 234 assertEquals(2, rdProc.getProcId()); 235 assertEquals(false, queue.waitTableSharedLock(rdProc, tableName)); 236 237 // Fetch the 3rd item and verify that the lock can't be acquired 238 assertEquals(null, queue.poll(0)); 239 240 // release the rdlock of item 2 and take the wrlock for the 3d item 241 queue.wakeTableSharedLock(rdProc, tableName); 242 243 queue.addBack( 244 new TestTableProcedure(4, tableName, TableProcedureInterface.TableOperationType.READ)); 245 queue.addBack( 246 new TestTableProcedure(5, tableName, TableProcedureInterface.TableOperationType.READ)); 247 248 // Fetch the 3rd item and take the write lock 249 Procedure<?> wrProc = queue.poll(); 250 assertEquals(false, queue.waitTableExclusiveLock(wrProc, tableName)); 251 252 // Fetch 4th item and verify that the lock can't be acquired 253 assertEquals(null, queue.poll(0)); 254 255 // Release the write lock and acquire the read lock 256 releaseTableExclusiveLockAndComplete(wrProc, tableName); 257 258 // Fetch the 4th item and take the read lock 259 rdProc = queue.poll(); 260 assertEquals(4, rdProc.getProcId()); 261 assertEquals(false, queue.waitTableSharedLock(rdProc, tableName)); 262 263 // Fetch the 4th item and take the read lock 264 Procedure<?> rdProc2 = queue.poll(); 265 assertEquals(5, rdProc2.getProcId()); 266 assertEquals(false, queue.waitTableSharedLock(rdProc2, tableName)); 267 268 // Release 4th and 5th read-lock 269 queue.wakeTableSharedLock(rdProc, tableName); 270 queue.wakeTableSharedLock(rdProc2, tableName); 271 272 // remove table queue 273 assertEquals(0, queue.size()); 274 assertTrue("queue should be deleted", queue.markTableAsDeleted(tableName, wrProc)); 275 } 276 277 @Test 278 public void testVerifyNamespaceRwLocks() throws Exception { 279 String nsName1 = "ns1"; 280 String nsName2 = "ns2"; 281 TableName tableName1 = TableName.valueOf(nsName1, name.getMethodName()); 282 TableName tableName2 = TableName.valueOf(nsName2, name.getMethodName()); 283 queue.addBack( 284 new TestNamespaceProcedure(1, nsName1, TableProcedureInterface.TableOperationType.EDIT)); 285 queue.addBack( 286 new TestTableProcedure(2, tableName1, TableProcedureInterface.TableOperationType.EDIT)); 287 queue.addBack( 288 new TestTableProcedure(3, tableName2, TableProcedureInterface.TableOperationType.EDIT)); 289 queue.addBack( 290 new TestNamespaceProcedure(4, nsName2, TableProcedureInterface.TableOperationType.EDIT)); 291 292 // Fetch the 1st item and take the write lock 293 Procedure<?> procNs1 = queue.poll(); 294 assertEquals(1, procNs1.getProcId()); 295 assertFalse(queue.waitNamespaceExclusiveLock(procNs1, nsName1)); 296 297 // namespace table has higher priority so we still return procedure for it 298 Procedure<?> procNs2 = queue.poll(); 299 assertEquals(4, procNs2.getProcId()); 300 assertFalse(queue.waitNamespaceExclusiveLock(procNs2, nsName2)); 301 queue.wakeNamespaceExclusiveLock(procNs2, nsName2); 302 303 // add procNs2 back in the queue 304 queue.yield(procNs2); 305 306 // again 307 procNs2 = queue.poll(); 308 assertEquals(4, procNs2.getProcId()); 309 assertFalse(queue.waitNamespaceExclusiveLock(procNs2, nsName2)); 310 311 // ns1 and ns2 are both locked so we get nothing 312 assertNull(queue.poll()); 313 314 // release the ns1 lock 315 queue.wakeNamespaceExclusiveLock(procNs1, nsName1); 316 317 // we are now able to execute table of ns1 318 long procId = queue.poll().getProcId(); 319 assertEquals(2, procId); 320 321 // release ns2 322 queue.wakeNamespaceExclusiveLock(procNs2, nsName2); 323 324 // we are now able to execute table of ns2 325 procId = queue.poll().getProcId(); 326 assertEquals(3, procId); 327 } 328 329 @Test 330 public void testVerifyNamespaceXLock() throws Exception { 331 String nsName = "ns1"; 332 TableName tableName = TableName.valueOf(nsName, name.getMethodName()); 333 queue.addBack( 334 new TestNamespaceProcedure(1, nsName, TableProcedureInterface.TableOperationType.CREATE)); 335 queue.addBack( 336 new TestTableProcedure(2, tableName, TableProcedureInterface.TableOperationType.READ)); 337 338 // Fetch the ns item and take the xlock 339 Procedure<?> proc = queue.poll(); 340 assertEquals(1, proc.getProcId()); 341 assertEquals(false, queue.waitNamespaceExclusiveLock(proc, nsName)); 342 343 // the table operation can't be executed because the ns is locked 344 assertEquals(null, queue.poll(0)); 345 346 // release the ns lock 347 queue.wakeNamespaceExclusiveLock(proc, nsName); 348 349 proc = queue.poll(); 350 assertEquals(2, proc.getProcId()); 351 assertEquals(false, queue.waitTableExclusiveLock(proc, tableName)); 352 queue.wakeTableExclusiveLock(proc, tableName); 353 } 354 355 @Test 356 public void testXLockWaitingForExecutingSharedLockToRelease() { 357 final TableName tableName = TableName.valueOf(name.getMethodName()); 358 final RegionInfo regionA = RegionInfoBuilder.newBuilder(tableName) 359 .setStartKey(Bytes.toBytes("a")).setEndKey(Bytes.toBytes("b")).build(); 360 361 queue.addBack(new TestRegionProcedure(1, tableName, 362 TableProcedureInterface.TableOperationType.REGION_ASSIGN, regionA)); 363 queue.addBack( 364 new TestTableProcedure(2, tableName, TableProcedureInterface.TableOperationType.EDIT)); 365 366 // Fetch the 1st item and take the shared lock 367 Procedure<?> proc = queue.poll(); 368 assertEquals(1, proc.getProcId()); 369 assertEquals(false, queue.waitRegion(proc, regionA)); 370 371 // the xlock operation in the queue can't be executed 372 assertEquals(null, queue.poll(0)); 373 374 // release the shared lock 375 queue.wakeRegion(proc, regionA); 376 377 // Fetch the 2nd item and take the xlock 378 proc = queue.poll(); 379 assertEquals(2, proc.getProcId()); 380 assertEquals(false, queue.waitTableExclusiveLock(proc, tableName)); 381 382 queue.addBack(new TestRegionProcedure(3, tableName, 383 TableProcedureInterface.TableOperationType.REGION_UNASSIGN, regionA)); 384 385 // everything is locked by the table operation 386 assertEquals(null, queue.poll(0)); 387 388 // release the table xlock 389 queue.wakeTableExclusiveLock(proc, tableName); 390 391 // grab the last item in the queue 392 proc = queue.poll(); 393 assertEquals(3, proc.getProcId()); 394 395 // lock and unlock the region 396 assertEquals(false, queue.waitRegion(proc, regionA)); 397 assertEquals(null, queue.poll(0)); 398 queue.wakeRegion(proc, regionA); 399 } 400 401 @Test 402 public void testVerifyRegionLocks() throws Exception { 403 final TableName tableName = TableName.valueOf(name.getMethodName()); 404 final RegionInfo regionA = RegionInfoBuilder.newBuilder(tableName) 405 .setStartKey(Bytes.toBytes("a")).setEndKey(Bytes.toBytes("b")).build(); 406 final RegionInfo regionB = RegionInfoBuilder.newBuilder(tableName) 407 .setStartKey(Bytes.toBytes("b")).setEndKey(Bytes.toBytes("c")).build(); 408 final RegionInfo regionC = RegionInfoBuilder.newBuilder(tableName) 409 .setStartKey(Bytes.toBytes("c")).setEndKey(Bytes.toBytes("d")).build(); 410 411 queue.addBack( 412 new TestTableProcedure(1, tableName, TableProcedureInterface.TableOperationType.EDIT)); 413 queue.addBack(new TestRegionProcedure(2, tableName, 414 TableProcedureInterface.TableOperationType.REGION_MERGE, regionA, regionB)); 415 queue.addBack(new TestRegionProcedure(3, tableName, 416 TableProcedureInterface.TableOperationType.REGION_SPLIT, regionA)); 417 queue.addBack(new TestRegionProcedure(4, tableName, 418 TableProcedureInterface.TableOperationType.REGION_SPLIT, regionB)); 419 queue.addBack(new TestRegionProcedure(5, tableName, 420 TableProcedureInterface.TableOperationType.REGION_UNASSIGN, regionC)); 421 422 // Fetch the 1st item and take the write lock 423 Procedure<?> proc = queue.poll(); 424 assertEquals(1, proc.getProcId()); 425 assertEquals(false, queue.waitTableExclusiveLock(proc, tableName)); 426 427 // everything is locked by the table operation 428 assertEquals(null, queue.poll(0)); 429 430 // release the table lock 431 queue.wakeTableExclusiveLock(proc, tableName); 432 433 // Fetch the 2nd item and the the lock on regionA and regionB 434 Procedure<?> mergeProc = queue.poll(); 435 assertEquals(2, mergeProc.getProcId()); 436 assertEquals(false, queue.waitRegions(mergeProc, tableName, regionA, regionB)); 437 438 // Fetch the 3rd item and the try to lock region A which will fail 439 // because already locked. this procedure will go in waiting. 440 // (this stuff will be explicit until we get rid of the zk-lock) 441 Procedure<?> procA = queue.poll(); 442 assertEquals(3, procA.getProcId()); 443 assertEquals(true, queue.waitRegions(procA, tableName, regionA)); 444 445 // Fetch the 4th item, same story as the 3rd 446 Procedure<?> procB = queue.poll(); 447 assertEquals(4, procB.getProcId()); 448 assertEquals(true, queue.waitRegions(procB, tableName, regionB)); 449 450 // Fetch the 5th item, since it is a non-locked region we are able to execute it 451 Procedure<?> procC = queue.poll(); 452 assertEquals(5, procC.getProcId()); 453 assertEquals(false, queue.waitRegions(procC, tableName, regionC)); 454 455 // 3rd and 4th are in the region suspended queue 456 assertEquals(null, queue.poll(0)); 457 458 // Release region A-B from merge operation (procId=2) 459 queue.wakeRegions(mergeProc, tableName, regionA, regionB); 460 461 // Fetch the 3rd item, now the lock on the region is available 462 procA = queue.poll(); 463 assertEquals(3, procA.getProcId()); 464 assertEquals(false, queue.waitRegions(procA, tableName, regionA)); 465 466 // Fetch the 4th item, now the lock on the region is available 467 procB = queue.poll(); 468 assertEquals(4, procB.getProcId()); 469 assertEquals(false, queue.waitRegions(procB, tableName, regionB)); 470 471 // release the locks on the regions 472 queue.wakeRegions(procA, tableName, regionA); 473 queue.wakeRegions(procB, tableName, regionB); 474 queue.wakeRegions(procC, tableName, regionC); 475 } 476 477 @Test 478 public void testVerifySubProcRegionLocks() throws Exception { 479 final TableName tableName = TableName.valueOf(name.getMethodName()); 480 final RegionInfo regionA = RegionInfoBuilder.newBuilder(tableName) 481 .setStartKey(Bytes.toBytes("a")).setEndKey(Bytes.toBytes("b")).build(); 482 final RegionInfo regionB = RegionInfoBuilder.newBuilder(tableName) 483 .setStartKey(Bytes.toBytes("b")).setEndKey(Bytes.toBytes("c")).build(); 484 final RegionInfo regionC = RegionInfoBuilder.newBuilder(tableName) 485 .setStartKey(Bytes.toBytes("c")).setEndKey(Bytes.toBytes("d")).build(); 486 487 queue.addBack( 488 new TestTableProcedure(1, tableName, TableProcedureInterface.TableOperationType.ENABLE)); 489 490 // Fetch the 1st item from the queue, "the root procedure" and take the table lock 491 Procedure<?> rootProc = queue.poll(); 492 assertEquals(1, rootProc.getProcId()); 493 assertEquals(false, queue.waitTableExclusiveLock(rootProc, tableName)); 494 assertEquals(null, queue.poll(0)); 495 496 // Execute the 1st step of the root-proc. 497 // we should get 3 sub-proc back, one for each region. 498 // (this step is done by the executor/rootProc, we are simulating it) 499 Procedure<?>[] subProcs = new Procedure[] { 500 new TestRegionProcedure(1, 2, tableName, 501 TableProcedureInterface.TableOperationType.REGION_EDIT, regionA), 502 new TestRegionProcedure(1, 3, tableName, 503 TableProcedureInterface.TableOperationType.REGION_EDIT, regionB), 504 new TestRegionProcedure(1, 4, tableName, 505 TableProcedureInterface.TableOperationType.REGION_EDIT, regionC), }; 506 507 // at this point the rootProc is going in a waiting state 508 // and the sub-procedures will be added in the queue. 509 // (this step is done by the executor, we are simulating it) 510 for (int i = subProcs.length - 1; i >= 0; --i) { 511 queue.addFront(subProcs[i]); 512 } 513 assertEquals(subProcs.length, queue.size()); 514 515 // we should be able to fetch and execute all the sub-procs, 516 // since they are operating on different regions 517 for (int i = 0; i < subProcs.length; ++i) { 518 TestRegionProcedure regionProc = (TestRegionProcedure) queue.poll(0); 519 assertEquals(subProcs[i].getProcId(), regionProc.getProcId()); 520 assertEquals(false, queue.waitRegions(regionProc, tableName, regionProc.getRegionInfo())); 521 } 522 523 // nothing else in the queue 524 assertEquals(null, queue.poll(0)); 525 526 // release all the region locks 527 for (int i = 0; i < subProcs.length; ++i) { 528 TestRegionProcedure regionProc = (TestRegionProcedure) subProcs[i]; 529 queue.wakeRegions(regionProc, tableName, regionProc.getRegionInfo()); 530 } 531 532 // nothing else in the queue 533 assertEquals(null, queue.poll(0)); 534 535 // release the table lock (for the root procedure) 536 queue.wakeTableExclusiveLock(rootProc, tableName); 537 } 538 539 @Test 540 public void testInheritedRegionXLock() { 541 final TableName tableName = TableName.valueOf(name.getMethodName()); 542 final RegionInfo region = RegionInfoBuilder.newBuilder(tableName) 543 .setStartKey(Bytes.toBytes("a")).setEndKey(Bytes.toBytes("b")).build(); 544 545 queue.addBack(new TestRegionProcedure(1, tableName, 546 TableProcedureInterface.TableOperationType.REGION_SPLIT, region)); 547 queue.addBack(new TestRegionProcedure(1, 2, tableName, 548 TableProcedureInterface.TableOperationType.REGION_UNASSIGN, region)); 549 queue.addBack(new TestRegionProcedure(3, tableName, 550 TableProcedureInterface.TableOperationType.REGION_EDIT, region)); 551 552 // fetch the root proc and take the lock on the region 553 Procedure<?> rootProc = queue.poll(); 554 assertEquals(1, rootProc.getProcId()); 555 assertEquals(false, queue.waitRegion(rootProc, region)); 556 557 // fetch the sub-proc and take the lock on the region (inherited lock) 558 Procedure<?> childProc = queue.poll(); 559 assertEquals(2, childProc.getProcId()); 560 assertEquals(false, queue.waitRegion(childProc, region)); 561 562 // proc-3 will be fetched but it can't take the lock 563 Procedure<?> proc = queue.poll(); 564 assertEquals(3, proc.getProcId()); 565 assertEquals(true, queue.waitRegion(proc, region)); 566 567 // release the child lock 568 queue.wakeRegion(childProc, region); 569 570 // nothing in the queue (proc-3 is suspended) 571 assertEquals(null, queue.poll(0)); 572 573 // release the root lock 574 queue.wakeRegion(rootProc, region); 575 576 // proc-3 should be now available 577 proc = queue.poll(); 578 assertEquals(3, proc.getProcId()); 579 assertEquals(false, queue.waitRegion(proc, region)); 580 queue.wakeRegion(proc, region); 581 } 582 583 @Test 584 public void testSuspendedProcedure() throws Exception { 585 final TableName tableName = TableName.valueOf(name.getMethodName()); 586 587 queue.addBack( 588 new TestTableProcedure(1, tableName, TableProcedureInterface.TableOperationType.READ)); 589 queue.addBack( 590 new TestTableProcedure(2, tableName, TableProcedureInterface.TableOperationType.READ)); 591 592 Procedure<?> proc = queue.poll(); 593 assertEquals(1, proc.getProcId()); 594 595 // suspend 596 ProcedureEvent<?> event = new ProcedureEvent<>("testSuspendedProcedureEvent"); 597 assertEquals(true, event.suspendIfNotReady(proc)); 598 599 proc = queue.poll(); 600 assertEquals(2, proc.getProcId()); 601 assertEquals(null, queue.poll(0)); 602 603 // resume 604 event.wake(queue); 605 606 proc = queue.poll(); 607 assertEquals(1, proc.getProcId()); 608 assertEquals(null, queue.poll(0)); 609 } 610 611 private static RegionInfo[] generateRegionInfo(final TableName tableName) { 612 return new RegionInfo[] { 613 RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes("a")) 614 .setEndKey(Bytes.toBytes("b")).build(), 615 RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes("b")) 616 .setEndKey(Bytes.toBytes("c")).build(), 617 RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes("c")) 618 .setEndKey(Bytes.toBytes("d")).build() }; 619 } 620 621 @Test 622 public void testParentXLockAndChildrenSharedLock() throws Exception { 623 final TableName tableName = TableName.valueOf(name.getMethodName()); 624 final RegionInfo[] regions = generateRegionInfo(tableName); 625 final TestRegionProcedure[] childProcs = new TestRegionProcedure[regions.length]; 626 for (int i = 0; i < regions.length; ++i) { 627 childProcs[i] = new TestRegionProcedure(1, 2 + i, tableName, 628 TableProcedureInterface.TableOperationType.REGION_ASSIGN, regions[i]); 629 } 630 testInheritedXLockAndChildrenSharedLock(tableName, 631 new TestTableProcedure(1, tableName, TableProcedureInterface.TableOperationType.CREATE), 632 childProcs); 633 } 634 635 @Test 636 public void testRootXLockAndChildrenSharedLock() throws Exception { 637 final TableName tableName = TableName.valueOf(name.getMethodName()); 638 final RegionInfo[] regions = generateRegionInfo(tableName); 639 final TestRegionProcedure[] childProcs = new TestRegionProcedure[regions.length]; 640 for (int i = 0; i < regions.length; ++i) { 641 childProcs[i] = new TestRegionProcedure(1, 2, 3 + i, tableName, 642 TableProcedureInterface.TableOperationType.REGION_ASSIGN, regions[i]); 643 } 644 testInheritedXLockAndChildrenSharedLock(tableName, 645 new TestTableProcedure(1, tableName, TableProcedureInterface.TableOperationType.CREATE), 646 childProcs); 647 } 648 649 private void releaseTableExclusiveLockAndComplete(Procedure<?> proc, TableName tableName) { 650 // release xlock 651 queue.wakeTableExclusiveLock(proc, tableName); 652 // mark the procedure as complete 653 queue.completionCleanup(proc); 654 } 655 656 private void testInheritedXLockAndChildrenSharedLock(final TableName tableName, 657 final TestTableProcedure rootProc, final TestRegionProcedure[] childProcs) throws Exception { 658 queue.addBack(rootProc); 659 660 // fetch and acquire first xlock proc 661 Procedure<?> parentProc = queue.poll(); 662 assertEquals(rootProc, parentProc); 663 assertEquals(false, queue.waitTableExclusiveLock(parentProc, tableName)); 664 665 // add child procedure 666 for (int i = 0; i < childProcs.length; ++i) { 667 queue.addFront(childProcs[i]); 668 } 669 670 // add another xlock procedure (no parent) 671 queue.addBack( 672 new TestTableProcedure(100, tableName, TableProcedureInterface.TableOperationType.EDIT)); 673 674 // fetch and execute child 675 for (int i = 0; i < childProcs.length; ++i) { 676 TestRegionProcedure childProc = (TestRegionProcedure) queue.poll(); 677 LOG.debug("fetch children " + childProc); 678 assertEquals(false, queue.waitRegions(childProc, tableName, childProc.getRegionInfo())); 679 queue.wakeRegions(childProc, tableName, childProc.getRegionInfo()); 680 } 681 682 // nothing available, until xlock release 683 assertEquals(null, queue.poll(0)); 684 685 // release xlock 686 releaseTableExclusiveLockAndComplete(parentProc, tableName); 687 688 // fetch the other xlock proc 689 Procedure<?> proc = queue.poll(); 690 assertEquals(100, proc.getProcId()); 691 assertEquals(false, queue.waitTableExclusiveLock(proc, tableName)); 692 releaseTableExclusiveLockAndComplete(proc, tableName); 693 } 694 695 @Test 696 public void testParentXLockAndChildrenXLock() throws Exception { 697 final TableName tableName = TableName.valueOf(name.getMethodName()); 698 testInheritedXLockAndChildrenXLock(tableName, 699 new TestTableProcedure(1, tableName, TableProcedureInterface.TableOperationType.EDIT), 700 new TestTableProcedure(1, 2, tableName, TableProcedureInterface.TableOperationType.EDIT)); 701 } 702 703 @Test 704 public void testRootXLockAndChildrenXLock() throws Exception { 705 final TableName tableName = TableName.valueOf(name.getMethodName()); 706 // simulate 3 procedures: 1 (root), (2) child of root, (3) child of proc-2 707 testInheritedXLockAndChildrenXLock(tableName, 708 new TestTableProcedure(1, tableName, TableProcedureInterface.TableOperationType.EDIT), 709 new TestTableProcedure(1, 1, 2, tableName, TableProcedureInterface.TableOperationType.EDIT), 710 new TestTableProcedure(1, 2, 3, tableName, TableProcedureInterface.TableOperationType.EDIT)); 711 } 712 713 private void testInheritedXLockAndChildrenXLock(final TableName tableName, 714 final TestTableProcedure rootProc, final TestTableProcedure... childProcs) throws Exception { 715 procedures.put(rootProc.getProcId(), rootProc); 716 for (TestTableProcedure childProc : childProcs) { 717 procedures.put(childProc.getProcId(), childProc); 718 } 719 queue.addBack(rootProc); 720 721 // fetch and acquire first xlock proc 722 Procedure<?> parentProc = queue.poll(); 723 assertSame(rootProc, parentProc); 724 assertEquals(false, queue.waitTableExclusiveLock(parentProc, tableName)); 725 726 TestTableProcedure childProc = childProcs[childProcs.length - 1]; 727 // add child procedure 728 queue.addFront(childProc); 729 730 // fetch the other xlock proc 731 Procedure<?> proc = queue.poll(); 732 assertSame(childProc, proc); 733 assertEquals(false, queue.waitTableExclusiveLock(proc, tableName)); 734 releaseTableExclusiveLockAndComplete(proc, tableName); 735 736 // release xlock 737 releaseTableExclusiveLockAndComplete(proc, tableName); 738 } 739 740 @Test 741 public void testYieldWithXLockHeld() throws Exception { 742 final TableName tableName = TableName.valueOf(name.getMethodName()); 743 744 queue.addBack( 745 new TestTableProcedure(1, tableName, TableProcedureInterface.TableOperationType.EDIT)); 746 queue.addBack( 747 new TestTableProcedure(2, tableName, TableProcedureInterface.TableOperationType.EDIT)); 748 749 // fetch from the queue and acquire xlock for the first proc 750 Procedure<?> proc = queue.poll(); 751 assertEquals(1, proc.getProcId()); 752 assertEquals(false, queue.waitTableExclusiveLock(proc, tableName)); 753 754 // nothing available, until xlock release 755 assertEquals(null, queue.poll(0)); 756 757 // put the proc in the queue 758 queue.yield(proc); 759 760 // fetch from the queue, it should be the one with just added back 761 proc = queue.poll(); 762 assertEquals(1, proc.getProcId()); 763 764 // release the xlock 765 releaseTableExclusiveLockAndComplete(proc, tableName); 766 767 proc = queue.poll(); 768 assertEquals(2, proc.getProcId()); 769 } 770 771 @Test 772 public void testYieldWithSharedLockHeld() throws Exception { 773 final TableName tableName = TableName.valueOf(name.getMethodName()); 774 775 queue.addBack( 776 new TestTableProcedure(1, tableName, TableProcedureInterface.TableOperationType.READ)); 777 queue.addBack( 778 new TestTableProcedure(2, tableName, TableProcedureInterface.TableOperationType.READ)); 779 queue.addBack( 780 new TestTableProcedure(3, tableName, TableProcedureInterface.TableOperationType.EDIT)); 781 782 // fetch and acquire the first shared-lock 783 Procedure<?> proc1 = queue.poll(); 784 assertEquals(1, proc1.getProcId()); 785 assertEquals(false, queue.waitTableSharedLock(proc1, tableName)); 786 787 // fetch and acquire the second shared-lock 788 Procedure<?> proc2 = queue.poll(); 789 assertEquals(2, proc2.getProcId()); 790 assertEquals(false, queue.waitTableSharedLock(proc2, tableName)); 791 792 // nothing available, until xlock release 793 assertEquals(null, queue.poll(0)); 794 795 // put the procs back in the queue 796 queue.yield(proc1); 797 queue.yield(proc2); 798 799 // fetch from the queue, it should fetch the ones with just added back 800 proc1 = queue.poll(); 801 assertEquals(1, proc1.getProcId()); 802 proc2 = queue.poll(); 803 assertEquals(2, proc2.getProcId()); 804 805 // release the xlock 806 queue.wakeTableSharedLock(proc1, tableName); 807 queue.wakeTableSharedLock(proc2, tableName); 808 809 Procedure<?> proc3 = queue.poll(); 810 assertEquals(3, proc3.getProcId()); 811 } 812 813 public static class TestTableProcedure extends TestProcedure implements TableProcedureInterface { 814 private final TableOperationType opType; 815 private final TableName tableName; 816 817 public TestTableProcedure() { 818 throw new UnsupportedOperationException("recovery should not be triggered here"); 819 } 820 821 public TestTableProcedure(long procId, TableName tableName, TableOperationType opType) { 822 this(-1, procId, tableName, opType); 823 } 824 825 public TestTableProcedure(long parentProcId, long procId, TableName tableName, 826 TableOperationType opType) { 827 this(-1, parentProcId, procId, tableName, opType); 828 } 829 830 public TestTableProcedure(long rootProcId, long parentProcId, long procId, TableName tableName, 831 TableOperationType opType) { 832 super(procId, parentProcId, rootProcId, null); 833 this.tableName = tableName; 834 this.opType = opType; 835 } 836 837 @Override 838 public TableName getTableName() { 839 return tableName; 840 } 841 842 @Override 843 public TableOperationType getTableOperationType() { 844 return opType; 845 } 846 847 @Override 848 public void toStringClassDetails(final StringBuilder sb) { 849 sb.append(getClass().getSimpleName()); 850 sb.append("(table="); 851 sb.append(getTableName()); 852 sb.append(")"); 853 } 854 } 855 856 public static class TestTableProcedureWithEvent extends TestTableProcedure { 857 private final ProcedureEvent<?> event; 858 859 public TestTableProcedureWithEvent(long procId, TableName tableName, 860 TableOperationType opType) { 861 super(procId, tableName, opType); 862 event = new ProcedureEvent<>(tableName + " procId=" + procId); 863 } 864 865 public ProcedureEvent<?> getEvent() { 866 return event; 867 } 868 } 869 870 public static class TestRegionProcedure extends TestTableProcedure { 871 private final RegionInfo[] regionInfos; 872 873 public TestRegionProcedure() { 874 throw new UnsupportedOperationException("recovery should not be triggered here"); 875 } 876 877 public TestRegionProcedure(long procId, TableName tableName, TableOperationType opType, 878 RegionInfo... regionInfos) { 879 this(-1, procId, tableName, opType, regionInfos); 880 } 881 882 public TestRegionProcedure(long parentProcId, long procId, TableName tableName, 883 TableOperationType opType, RegionInfo... regionInfos) { 884 this(-1, parentProcId, procId, tableName, opType, regionInfos); 885 } 886 887 public TestRegionProcedure(long rootProcId, long parentProcId, long procId, TableName tableName, 888 TableOperationType opType, RegionInfo... regionInfos) { 889 super(rootProcId, parentProcId, procId, tableName, opType); 890 this.regionInfos = regionInfos; 891 } 892 893 public RegionInfo[] getRegionInfo() { 894 return regionInfos; 895 } 896 897 @Override 898 public void toStringClassDetails(final StringBuilder sb) { 899 sb.append(getClass().getSimpleName()); 900 sb.append("(regions="); 901 sb.append(Arrays.toString(getRegionInfo())); 902 sb.append(")"); 903 } 904 } 905 906 public static class TestNamespaceProcedure extends TestProcedure 907 implements TableProcedureInterface { 908 private final TableOperationType opType; 909 private final String nsName; 910 911 public TestNamespaceProcedure() { 912 throw new UnsupportedOperationException("recovery should not be triggered here"); 913 } 914 915 public TestNamespaceProcedure(long procId, String nsName, TableOperationType opType) { 916 super(procId); 917 this.nsName = nsName; 918 this.opType = opType; 919 } 920 921 @Override 922 public TableName getTableName() { 923 return TableProcedureInterface.DUMMY_NAMESPACE_TABLE_NAME; 924 } 925 926 @Override 927 public TableOperationType getTableOperationType() { 928 return opType; 929 } 930 931 @Override 932 public void toStringClassDetails(final StringBuilder sb) { 933 sb.append(getClass().getSimpleName()); 934 sb.append("(ns="); 935 sb.append(nsName); 936 sb.append(")"); 937 } 938 } 939 940 public static class TestPeerProcedure extends TestProcedure implements PeerProcedureInterface { 941 private final String peerId; 942 private final PeerOperationType opType; 943 944 public TestPeerProcedure(long procId, String peerId, PeerOperationType opType) { 945 super(procId); 946 this.peerId = peerId; 947 this.opType = opType; 948 } 949 950 @Override 951 public String getPeerId() { 952 return peerId; 953 } 954 955 @Override 956 public PeerOperationType getPeerOperationType() { 957 return opType; 958 } 959 } 960 961 public static class TestGlobalProcedure extends TestProcedure 962 implements GlobalProcedureInterface { 963 private final String globalId; 964 965 public TestGlobalProcedure(long procId, String globalId) { 966 super(procId); 967 this.globalId = globalId; 968 } 969 970 @Override 971 public String getGlobalId() { 972 return globalId; 973 } 974 } 975 976 private static LockProcedure createLockProcedure(LockType lockType, long procId) 977 throws Exception { 978 LockProcedure procedure = new LockProcedure(); 979 980 Field typeField = LockProcedure.class.getDeclaredField("type"); 981 typeField.setAccessible(true); 982 typeField.set(procedure, lockType); 983 984 Method setProcIdMethod = Procedure.class.getDeclaredMethod("setProcId", long.class); 985 setProcIdMethod.setAccessible(true); 986 setProcIdMethod.invoke(procedure, procId); 987 988 return procedure; 989 } 990 991 private static LockProcedure createExclusiveLockProcedure(long procId) throws Exception { 992 return createLockProcedure(LockType.EXCLUSIVE, procId); 993 } 994 995 private static LockProcedure createSharedLockProcedure(long procId) throws Exception { 996 return createLockProcedure(LockType.SHARED, procId); 997 } 998 999 private static void assertLockResource(LockedResource resource, LockedResourceType resourceType, 1000 String resourceName) { 1001 assertEquals(resourceType, resource.getResourceType()); 1002 assertEquals(resourceName, resource.getResourceName()); 1003 } 1004 1005 private static void assertExclusiveLock(LockedResource resource, Procedure<?> procedure) { 1006 assertEquals(LockType.EXCLUSIVE, resource.getLockType()); 1007 assertEquals(procedure, resource.getExclusiveLockOwnerProcedure()); 1008 assertEquals(0, resource.getSharedLockCount()); 1009 } 1010 1011 private static void assertSharedLock(LockedResource resource, int lockCount) { 1012 assertEquals(LockType.SHARED, resource.getLockType()); 1013 assertEquals(lockCount, resource.getSharedLockCount()); 1014 } 1015 1016 @Test 1017 public void testListLocksServer() throws Exception { 1018 LockProcedure procedure = createExclusiveLockProcedure(0); 1019 queue.waitServerExclusiveLock(procedure, ServerName.valueOf("server1,1234,0")); 1020 1021 List<LockedResource> resources = queue.getLocks(); 1022 assertEquals(1, resources.size()); 1023 1024 LockedResource serverResource = resources.get(0); 1025 assertLockResource(serverResource, LockedResourceType.SERVER, "server1,1234,0"); 1026 assertExclusiveLock(serverResource, procedure); 1027 assertTrue(serverResource.getWaitingProcedures().isEmpty()); 1028 } 1029 1030 @Test 1031 public void testListLocksNamespace() throws Exception { 1032 LockProcedure procedure = createExclusiveLockProcedure(1); 1033 queue.waitNamespaceExclusiveLock(procedure, "ns1"); 1034 1035 List<LockedResource> locks = queue.getLocks(); 1036 assertEquals(2, locks.size()); 1037 1038 LockedResource namespaceResource = locks.get(0); 1039 assertLockResource(namespaceResource, LockedResourceType.NAMESPACE, "ns1"); 1040 assertExclusiveLock(namespaceResource, procedure); 1041 assertTrue(namespaceResource.getWaitingProcedures().isEmpty()); 1042 1043 LockedResource tableResource = locks.get(1); 1044 assertLockResource(tableResource, LockedResourceType.TABLE, 1045 TableProcedureInterface.DUMMY_NAMESPACE_TABLE_NAME.getNameAsString()); 1046 assertSharedLock(tableResource, 1); 1047 assertTrue(tableResource.getWaitingProcedures().isEmpty()); 1048 } 1049 1050 @Test 1051 public void testListLocksTable() throws Exception { 1052 LockProcedure procedure = createExclusiveLockProcedure(2); 1053 queue.waitTableExclusiveLock(procedure, TableName.valueOf("ns2", "table2")); 1054 1055 List<LockedResource> locks = queue.getLocks(); 1056 assertEquals(2, locks.size()); 1057 1058 LockedResource namespaceResource = locks.get(0); 1059 assertLockResource(namespaceResource, LockedResourceType.NAMESPACE, "ns2"); 1060 assertSharedLock(namespaceResource, 1); 1061 assertTrue(namespaceResource.getWaitingProcedures().isEmpty()); 1062 1063 LockedResource tableResource = locks.get(1); 1064 assertLockResource(tableResource, LockedResourceType.TABLE, "ns2:table2"); 1065 assertExclusiveLock(tableResource, procedure); 1066 assertTrue(tableResource.getWaitingProcedures().isEmpty()); 1067 } 1068 1069 @Test 1070 public void testListLocksRegion() throws Exception { 1071 LockProcedure procedure = createExclusiveLockProcedure(3); 1072 RegionInfo regionInfo = 1073 RegionInfoBuilder.newBuilder(TableName.valueOf("ns3", "table3")).build(); 1074 1075 queue.waitRegion(procedure, regionInfo); 1076 1077 List<LockedResource> resources = queue.getLocks(); 1078 assertEquals(3, resources.size()); 1079 1080 LockedResource namespaceResource = resources.get(0); 1081 assertLockResource(namespaceResource, LockedResourceType.NAMESPACE, "ns3"); 1082 assertSharedLock(namespaceResource, 1); 1083 assertTrue(namespaceResource.getWaitingProcedures().isEmpty()); 1084 1085 LockedResource tableResource = resources.get(1); 1086 assertLockResource(tableResource, LockedResourceType.TABLE, "ns3:table3"); 1087 assertSharedLock(tableResource, 1); 1088 assertTrue(tableResource.getWaitingProcedures().isEmpty()); 1089 1090 LockedResource regionResource = resources.get(2); 1091 assertLockResource(regionResource, LockedResourceType.REGION, regionInfo.getEncodedName()); 1092 assertExclusiveLock(regionResource, procedure); 1093 assertTrue(regionResource.getWaitingProcedures().isEmpty()); 1094 } 1095 1096 @Test 1097 public void testListLocksPeer() throws Exception { 1098 String peerId = "1"; 1099 LockProcedure procedure = createExclusiveLockProcedure(4); 1100 queue.waitPeerExclusiveLock(procedure, peerId); 1101 1102 List<LockedResource> locks = queue.getLocks(); 1103 assertEquals(1, locks.size()); 1104 1105 LockedResource resource = locks.get(0); 1106 assertLockResource(resource, LockedResourceType.PEER, peerId); 1107 assertExclusiveLock(resource, procedure); 1108 assertTrue(resource.getWaitingProcedures().isEmpty()); 1109 1110 // Try to acquire the exclusive lock again with same procedure 1111 assertFalse(queue.waitPeerExclusiveLock(procedure, peerId)); 1112 1113 // Try to acquire the exclusive lock again with new procedure 1114 LockProcedure procedure2 = createExclusiveLockProcedure(5); 1115 assertTrue(queue.waitPeerExclusiveLock(procedure2, peerId)); 1116 1117 // Same peerId, still only has 1 LockedResource 1118 locks = queue.getLocks(); 1119 assertEquals(1, locks.size()); 1120 1121 resource = locks.get(0); 1122 assertLockResource(resource, LockedResourceType.PEER, peerId); 1123 // LockedResource owner still is the origin procedure 1124 assertExclusiveLock(resource, procedure); 1125 // The new procedure should in the waiting list 1126 assertEquals(1, resource.getWaitingProcedures().size()); 1127 } 1128 1129 @Test 1130 public void testListLocksGlobal() throws Exception { 1131 String globalId = "1"; 1132 LockProcedure procedure = createExclusiveLockProcedure(4); 1133 queue.waitGlobalExclusiveLock(procedure, globalId); 1134 1135 List<LockedResource> locks = queue.getLocks(); 1136 assertEquals(1, locks.size()); 1137 1138 LockedResource resource = locks.get(0); 1139 assertLockResource(resource, LockedResourceType.GLOBAL, globalId); 1140 assertExclusiveLock(resource, procedure); 1141 assertTrue(resource.getWaitingProcedures().isEmpty()); 1142 1143 // Try to acquire the exclusive lock again with same procedure 1144 assertFalse(queue.waitGlobalExclusiveLock(procedure, globalId)); 1145 1146 // Try to acquire the exclusive lock again with new procedure 1147 LockProcedure procedure2 = createExclusiveLockProcedure(5); 1148 assertTrue(queue.waitGlobalExclusiveLock(procedure2, globalId)); 1149 1150 // Same peerId, still only has 1 LockedResource 1151 locks = queue.getLocks(); 1152 assertEquals(1, locks.size()); 1153 1154 resource = locks.get(0); 1155 assertLockResource(resource, LockedResourceType.GLOBAL, globalId); 1156 // LockedResource owner still is the origin procedure 1157 assertExclusiveLock(resource, procedure); 1158 // The new procedure should in the waiting list 1159 assertEquals(1, resource.getWaitingProcedures().size()); 1160 } 1161 1162 @Test 1163 public void testListLocksWaiting() throws Exception { 1164 LockProcedure procedure1 = createExclusiveLockProcedure(1); 1165 queue.waitTableExclusiveLock(procedure1, TableName.valueOf("ns4", "table4")); 1166 1167 LockProcedure procedure2 = createSharedLockProcedure(2); 1168 queue.waitTableSharedLock(procedure2, TableName.valueOf("ns4", "table4")); 1169 1170 LockProcedure procedure3 = createExclusiveLockProcedure(3); 1171 queue.waitTableExclusiveLock(procedure3, TableName.valueOf("ns4", "table4")); 1172 1173 List<LockedResource> resources = queue.getLocks(); 1174 assertEquals(2, resources.size()); 1175 1176 LockedResource namespaceResource = resources.get(0); 1177 assertLockResource(namespaceResource, LockedResourceType.NAMESPACE, "ns4"); 1178 assertSharedLock(namespaceResource, 1); 1179 assertTrue(namespaceResource.getWaitingProcedures().isEmpty()); 1180 1181 LockedResource tableLock = resources.get(1); 1182 assertLockResource(tableLock, LockedResourceType.TABLE, "ns4:table4"); 1183 assertExclusiveLock(tableLock, procedure1); 1184 1185 List<Procedure<?>> waitingProcedures = tableLock.getWaitingProcedures(); 1186 assertEquals(2, waitingProcedures.size()); 1187 1188 LockProcedure waitingProcedure2 = (LockProcedure) waitingProcedures.get(0); 1189 assertEquals(LockType.SHARED, waitingProcedure2.getType()); 1190 assertEquals(procedure2, waitingProcedure2); 1191 1192 LockProcedure waitingProcedure3 = (LockProcedure) waitingProcedures.get(1); 1193 assertEquals(LockType.EXCLUSIVE, waitingProcedure3.getType()); 1194 assertEquals(procedure3, waitingProcedure3); 1195 } 1196 1197 @Test 1198 public void testAcquireSharedLockWhileParentHoldingExclusiveLock() { 1199 TableName tableName = TableName.valueOf(name.getMethodName()); 1200 RegionInfo regionInfo = RegionInfoBuilder.newBuilder(tableName).build(); 1201 1202 TestTableProcedure parentProc = new TestTableProcedure(1, tableName, TableOperationType.EDIT); 1203 TestRegionProcedure proc = 1204 new TestRegionProcedure(1, 2, tableName, TableOperationType.REGION_EDIT, regionInfo); 1205 queue.addBack(parentProc); 1206 1207 assertSame(parentProc, queue.poll()); 1208 assertFalse(queue.waitTableExclusiveLock(parentProc, tableName)); 1209 1210 // The queue for this table should be added back to run queue as the parent has the xlock, so we 1211 // can poll it out. 1212 queue.addFront(proc); 1213 assertSame(proc, queue.poll()); 1214 // the parent has xlock on the table, and it is OK for us to acquire shared lock on the table, 1215 // this is what this test wants to confirm 1216 assertFalse(queue.waitRegion(proc, regionInfo)); 1217 1218 queue.wakeRegion(proc, regionInfo); 1219 queue.wakeTableExclusiveLock(parentProc, tableName); 1220 } 1221}