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}