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.locking;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023
024import java.util.List;
025import org.apache.hadoop.conf.Configuration;
026import org.apache.hadoop.hbase.HBaseClassTestRule;
027import org.apache.hadoop.hbase.HBaseTestingUtility;
028import org.apache.hadoop.hbase.HRegionInfo;
029import org.apache.hadoop.hbase.NamespaceDescriptor;
030import org.apache.hadoop.hbase.TableName;
031import org.apache.hadoop.hbase.master.MasterServices;
032import org.apache.hadoop.hbase.master.procedure.MasterProcedureConstants;
033import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
034import org.apache.hadoop.hbase.procedure2.LockType;
035import org.apache.hadoop.hbase.procedure2.Procedure;
036import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
037import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
038import org.apache.hadoop.hbase.testclassification.MasterTests;
039import org.apache.hadoop.hbase.testclassification.MediumTests;
040import org.junit.After;
041import org.junit.AfterClass;
042import org.junit.BeforeClass;
043import org.junit.ClassRule;
044import org.junit.Rule;
045import org.junit.Test;
046import org.junit.experimental.categories.Category;
047import org.junit.rules.TestName;
048import org.slf4j.Logger;
049import org.slf4j.LoggerFactory;
050
051@Category({ MasterTests.class, MediumTests.class })
052public class TestLockManager {
053
054  @ClassRule
055  public static final HBaseClassTestRule CLASS_RULE =
056    HBaseClassTestRule.forClass(TestLockManager.class);
057
058  @Rule
059  public TestName testName = new TestName();
060  // crank this up if this test turns out to be flaky.
061  private static final int LOCAL_LOCKS_TIMEOUT = 1000;
062
063  private static final Logger LOG = LoggerFactory.getLogger(TestLockManager.class);
064  protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
065  private static MasterServices masterServices;
066
067  private static String namespace = "namespace";
068  private static TableName tableName = TableName.valueOf(namespace, "table");
069  private static HRegionInfo[] tableRegions;
070
071  private static void setupConf(Configuration conf) {
072    conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
073    conf.setBoolean("hbase.procedure.check.owner.set", false); // since rpc user will be null
074    conf.setInt(LockProcedure.LOCAL_MASTER_LOCKS_TIMEOUT_MS_CONF, LOCAL_LOCKS_TIMEOUT);
075  }
076
077  @BeforeClass
078  public static void setupCluster() throws Exception {
079    setupConf(UTIL.getConfiguration());
080    UTIL.startMiniCluster(1);
081    masterServices = UTIL.getMiniHBaseCluster().getMaster();
082    UTIL.getAdmin().createNamespace(NamespaceDescriptor.create(namespace).build());
083    UTIL.createTable(tableName, new byte[][] { "fam".getBytes() }, new byte[][] { "1".getBytes() });
084    List<HRegionInfo> regions = UTIL.getAdmin().getTableRegions(tableName);
085    assert regions.size() > 0;
086    tableRegions = new HRegionInfo[regions.size()];
087    regions.toArray(tableRegions);
088  }
089
090  @AfterClass
091  public static void cleanupTest() throws Exception {
092    try {
093      UTIL.shutdownMiniCluster();
094    } catch (Exception e) {
095      LOG.warn("failure shutting down cluster", e);
096    }
097  }
098
099  @After
100  public void tearDown() throws Exception {
101    for (Procedure<?> proc : getMasterProcedureExecutor().getProcedures()) {
102      if (proc instanceof LockProcedure) {
103        ((LockProcedure) proc).unlock(getMasterProcedureExecutor().getEnvironment());
104        ProcedureTestingUtility.waitProcedure(getMasterProcedureExecutor(), proc);
105      }
106    }
107    assertEquals(0, getMasterProcedureExecutor().getEnvironment().getProcedureScheduler().size());
108  }
109
110  private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
111    return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
112  }
113
114  /**
115   * Tests that basic lock functionality works.
116   */
117  @Test
118  public void testMasterLockAcquire() throws Exception {
119    LockManager.MasterLock lock =
120      masterServices.getLockManager().createMasterLock(namespace, LockType.EXCLUSIVE, "desc");
121    assertTrue(lock.tryAcquire(2000));
122    assertTrue(lock.getProc().isLocked());
123    lock.release();
124    assertEquals(null, lock.getProc());
125  }
126
127  /**
128   * Two locks try to acquire lock on same table, assert that later one times out.
129   */
130  @Test
131  public void testMasterLockAcquireTimeout() throws Exception {
132    LockManager.MasterLock lock =
133      masterServices.getLockManager().createMasterLock(tableName, LockType.EXCLUSIVE, "desc");
134    LockManager.MasterLock lock2 =
135      masterServices.getLockManager().createMasterLock(tableName, LockType.EXCLUSIVE, "desc");
136    assertTrue(lock.tryAcquire(2000));
137    assertFalse(lock2.tryAcquire(LOCAL_LOCKS_TIMEOUT / 2)); // wait less than other lock's timeout
138    assertEquals(null, lock2.getProc());
139    lock.release();
140    assertTrue(lock2.tryAcquire(2000));
141    assertTrue(lock2.getProc().isLocked());
142    lock2.release();
143  }
144
145  /**
146   * Take region lock, they try table exclusive lock, later one should time out.
147   */
148  @Test
149  public void testMasterLockAcquireTimeoutRegionVsTableExclusive() throws Exception {
150    LockManager.MasterLock lock =
151      masterServices.getLockManager().createMasterLock(tableRegions, "desc");
152    LockManager.MasterLock lock2 =
153      masterServices.getLockManager().createMasterLock(tableName, LockType.EXCLUSIVE, "desc");
154    assertTrue(lock.tryAcquire(2000));
155    assertFalse(lock2.tryAcquire(LOCAL_LOCKS_TIMEOUT / 2)); // wait less than other lock's timeout
156    assertEquals(null, lock2.getProc());
157    lock.release();
158    assertTrue(lock2.tryAcquire(2000));
159    assertTrue(lock2.getProc().isLocked());
160    lock2.release();
161  }
162}