001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.master.locking; 020 021import java.io.IOException; 022import java.util.concurrent.CountDownLatch; 023import java.util.concurrent.TimeUnit; 024 025import org.apache.hadoop.hbase.TableName; 026import org.apache.hadoop.hbase.client.RegionInfo; 027import org.apache.hadoop.hbase.master.HMaster; 028import org.apache.hadoop.hbase.procedure2.LockType; 029import org.apache.hadoop.hbase.util.NonceKey; 030import org.apache.yetus.audience.InterfaceAudience; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 034 035/** 036 * Functions to acquire lock on table/namespace/regions. 037 */ 038@InterfaceAudience.Private 039public final class LockManager { 040 private static final Logger LOG = LoggerFactory.getLogger(LockManager.class); 041 private final HMaster master; 042 private final RemoteLocks remoteLocks; 043 044 public LockManager(HMaster master) { 045 this.master = master; 046 this.remoteLocks = new RemoteLocks(); 047 } 048 049 public RemoteLocks remoteLocks() { 050 return remoteLocks; 051 } 052 053 public MasterLock createMasterLock(final String namespace, 054 final LockType type, final String description) { 055 return new MasterLock(namespace, type, description); 056 } 057 058 public MasterLock createMasterLock(final TableName tableName, 059 final LockType type, final String description) { 060 return new MasterLock(tableName, type, description); 061 } 062 063 public MasterLock createMasterLock(final RegionInfo[] regionInfos, final String description) { 064 return new MasterLock(regionInfos, description); 065 } 066 067 private void submitProcedure(final LockProcedure proc, final NonceKey nonceKey) { 068 proc.setOwner(master.getMasterProcedureExecutor().getEnvironment().getRequestUser()); 069 master.getMasterProcedureExecutor().submitProcedure(proc, nonceKey); 070 } 071 072 /** 073 * Locks on namespace/table/regions. 074 * Underneath, uses procedure framework and queues a {@link LockProcedure} which waits in a 075 * queue until scheduled. 076 * Use this lock instead LockManager.remoteLocks() for MASTER ONLY operations for two advantages: 077 * - no need of polling on LockProcedure to check if lock was acquired. 078 * - Generous timeout for lock preemption (default 10 min), no need to spawn thread for heartbeats. 079 * (timeout configuration {@link LockProcedure#DEFAULT_LOCAL_MASTER_LOCKS_TIMEOUT_MS}). 080 */ 081 public class MasterLock { 082 private final String namespace; 083 private final TableName tableName; 084 private final RegionInfo[] regionInfos; 085 private final LockType type; 086 private final String description; 087 088 private LockProcedure proc = null; 089 090 public MasterLock(final String namespace, 091 final LockType type, final String description) { 092 this.namespace = namespace; 093 this.tableName = null; 094 this.regionInfos = null; 095 this.type = type; 096 this.description = description; 097 } 098 099 public MasterLock(final TableName tableName, 100 final LockType type, final String description) { 101 this.namespace = null; 102 this.tableName = tableName; 103 this.regionInfos = null; 104 this.type = type; 105 this.description = description; 106 } 107 108 public MasterLock(final RegionInfo[] regionInfos, final String description) { 109 this.namespace = null; 110 this.tableName = null; 111 this.regionInfos = regionInfos; 112 this.type = LockType.EXCLUSIVE; 113 this.description = description; 114 } 115 116 /** 117 * Acquire the lock, waiting indefinitely until the lock is released or 118 * the thread is interrupted. 119 * @throws InterruptedException If current thread is interrupted while 120 * waiting for the lock 121 */ 122 public boolean acquire() throws InterruptedException { 123 return tryAcquire(0); 124 } 125 126 /** 127 * Acquire the lock within a wait time. 128 * @param timeoutMs The maximum time (in milliseconds) to wait for the lock, 129 * 0 to wait indefinitely 130 * @return True if the lock was acquired, false if waiting time elapsed 131 * before the lock was acquired 132 * @throws InterruptedException If the thread is interrupted while waiting to 133 * acquire the lock 134 */ 135 public boolean tryAcquire(final long timeoutMs) throws InterruptedException { 136 if (proc != null && proc.isLocked()) { 137 return true; 138 } 139 // Use new condition and procedure every time lock is requested. 140 final CountDownLatch lockAcquireLatch = new CountDownLatch(1); 141 if (regionInfos != null) { 142 proc = new LockProcedure(master.getConfiguration(), regionInfos, type, 143 description, lockAcquireLatch); 144 } else if (tableName != null) { 145 proc = new LockProcedure(master.getConfiguration(), tableName, type, 146 description, lockAcquireLatch); 147 } else if (namespace != null) { 148 proc = new LockProcedure(master.getConfiguration(), namespace, type, 149 description, lockAcquireLatch); 150 } else { 151 throw new UnsupportedOperationException("no namespace/table/region provided"); 152 } 153 154 // The user of a MasterLock should be 'hbase', the only case where this is not true 155 // is if from inside a coprocessor we try to take a master lock (which should be avoided) 156 proc.setOwner(master.getMasterProcedureExecutor().getEnvironment().getRequestUser()); 157 master.getMasterProcedureExecutor().submitProcedure(proc); 158 159 long deadline = (timeoutMs > 0) ? System.currentTimeMillis() + timeoutMs : Long.MAX_VALUE; 160 while (deadline >= System.currentTimeMillis() && !proc.isLocked()) { 161 try { 162 lockAcquireLatch.await(deadline - System.currentTimeMillis(), TimeUnit.MILLISECONDS); 163 } catch (InterruptedException e) { 164 LOG.info("InterruptedException when waiting for lock: " + proc.toString()); 165 // kind of weird, releasing a lock which is not locked. This is to make the procedure 166 // finish immediately whenever it gets scheduled so that it doesn't hold the lock. 167 release(); 168 throw e; 169 } 170 } 171 if (!proc.isLocked()) { 172 LOG.info("Timed out waiting to acquire procedure lock: " + proc.toString()); 173 release(); 174 return false; 175 } 176 return true; 177 } 178 179 /** 180 * Release the lock. 181 * No-op if the lock was never acquired. 182 */ 183 public void release() { 184 if (proc != null) { 185 proc.unlock(master.getMasterProcedureExecutor().getEnvironment()); 186 } 187 proc = null; 188 } 189 190 @Override 191 public String toString() { 192 return "MasterLock: proc = " + proc.toString(); 193 } 194 195 @VisibleForTesting 196 LockProcedure getProc() { 197 return proc; 198 } 199 } 200 201 /** 202 * Locks on namespace/table/regions for remote operations. 203 * Since remote operations are unreliable and the client/RS may die anytime and never release 204 * locks, regular heartbeats are required to keep the lock held. 205 */ 206 public class RemoteLocks { 207 public long requestNamespaceLock(final String namespace, final LockType type, 208 final String description, final NonceKey nonceKey) 209 throws IllegalArgumentException, IOException { 210 master.getMasterCoprocessorHost().preRequestLock(namespace, null, null, type, description); 211 final LockProcedure proc = new LockProcedure(master.getConfiguration(), namespace, 212 type, description, null); 213 submitProcedure(proc, nonceKey); 214 master.getMasterCoprocessorHost().postRequestLock(namespace, null, null, type, description); 215 return proc.getProcId(); 216 } 217 218 public long requestTableLock(final TableName tableName, final LockType type, 219 final String description, final NonceKey nonceKey) 220 throws IllegalArgumentException, IOException { 221 master.getMasterCoprocessorHost().preRequestLock(null, tableName, null, type, description); 222 final LockProcedure proc = new LockProcedure(master.getConfiguration(), tableName, 223 type, description, null); 224 submitProcedure(proc, nonceKey); 225 master.getMasterCoprocessorHost().postRequestLock(null, tableName, null, type, description); 226 return proc.getProcId(); 227 } 228 229 /** 230 * @throws IllegalArgumentException if all regions are not from same table. 231 */ 232 public long requestRegionsLock(final RegionInfo[] regionInfos, final String description, 233 final NonceKey nonceKey) 234 throws IllegalArgumentException, IOException { 235 master.getMasterCoprocessorHost().preRequestLock(null, null, regionInfos, 236 LockType.EXCLUSIVE, description); 237 final LockProcedure proc = new LockProcedure(master.getConfiguration(), regionInfos, 238 LockType.EXCLUSIVE, description, null); 239 submitProcedure(proc, nonceKey); 240 master.getMasterCoprocessorHost().postRequestLock(null, null, regionInfos, 241 LockType.EXCLUSIVE, description); 242 return proc.getProcId(); 243 } 244 245 /** 246 * @param keepAlive if false, release the lock. 247 * @return true, if procedure is found and it has the lock; else false. 248 */ 249 public boolean lockHeartbeat(final long procId, final boolean keepAlive) throws IOException { 250 final LockProcedure proc = master.getMasterProcedureExecutor() 251 .getProcedure(LockProcedure.class, procId); 252 if (proc == null) return false; 253 254 master.getMasterCoprocessorHost().preLockHeartbeat(proc, keepAlive); 255 256 proc.updateHeartBeat(); 257 if (!keepAlive) { 258 proc.unlock(master.getMasterProcedureExecutor().getEnvironment()); 259 } 260 261 master.getMasterCoprocessorHost().postLockHeartbeat(proc, keepAlive); 262 263 return proc.isLocked(); 264 } 265 } 266}