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