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 java.io.IOException; 021import java.util.concurrent.CountDownLatch; 022import java.util.concurrent.TimeUnit; 023import org.apache.hadoop.hbase.TableName; 024import org.apache.hadoop.hbase.client.RegionInfo; 025import org.apache.hadoop.hbase.master.HMaster; 026import org.apache.hadoop.hbase.procedure2.LockType; 027import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 028import org.apache.hadoop.hbase.util.NonceKey; 029import org.apache.yetus.audience.InterfaceAudience; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033/** 034 * Functions to acquire lock on table/namespace/regions. 035 */ 036@InterfaceAudience.Private 037public final class LockManager { 038 private static final Logger LOG = LoggerFactory.getLogger(LockManager.class); 039 private final HMaster master; 040 private final RemoteLocks remoteLocks; 041 042 public LockManager(HMaster master) { 043 this.master = master; 044 this.remoteLocks = new RemoteLocks(); 045 } 046 047 public RemoteLocks remoteLocks() { 048 return remoteLocks; 049 } 050 051 public MasterLock createMasterLock(final String namespace, final LockType type, 052 final String description) { 053 return new MasterLock(namespace, type, description); 054 } 055 056 public MasterLock createMasterLock(final TableName tableName, final LockType type, 057 final String description) { 058 return new MasterLock(tableName, type, description); 059 } 060 061 public MasterLock createMasterLock(final RegionInfo[] regionInfos, final String description) { 062 return new MasterLock(regionInfos, description); 063 } 064 065 private void submitProcedure(final LockProcedure proc, final NonceKey nonceKey) { 066 proc.setOwner(master.getMasterProcedureExecutor().getEnvironment().getRequestUser()); 067 master.getMasterProcedureExecutor().submitProcedure(proc, nonceKey); 068 } 069 070 /** 071 * Locks on namespace/table/regions. Underneath, uses procedure framework and queues a 072 * {@link LockProcedure} which waits in a queue until scheduled. Use this lock instead 073 * LockManager.remoteLocks() for MASTER ONLY operations for two advantages: - no need of polling 074 * on LockProcedure to check if lock was acquired. - Generous timeout for lock preemption (default 075 * 10 min), no need to spawn thread for heartbeats. (timeout configuration 076 * {@link LockProcedure#DEFAULT_LOCAL_MASTER_LOCKS_TIMEOUT_MS}). 077 */ 078 public class MasterLock { 079 private final String namespace; 080 private final TableName tableName; 081 private final RegionInfo[] regionInfos; 082 private final LockType type; 083 private final String description; 084 085 private LockProcedure proc = null; 086 087 public MasterLock(final String namespace, final LockType type, final String description) { 088 this.namespace = namespace; 089 this.tableName = null; 090 this.regionInfos = null; 091 this.type = type; 092 this.description = description; 093 } 094 095 public MasterLock(final TableName tableName, final LockType type, final String description) { 096 this.namespace = null; 097 this.tableName = tableName; 098 this.regionInfos = null; 099 this.type = type; 100 this.description = description; 101 } 102 103 public MasterLock(final RegionInfo[] regionInfos, final String description) { 104 this.namespace = null; 105 this.tableName = null; 106 this.regionInfos = regionInfos; 107 this.type = LockType.EXCLUSIVE; 108 this.description = description; 109 } 110 111 /** 112 * Acquire the lock within a wait time. 113 * @param timeoutMs The maximum time (in milliseconds) to wait for the lock, 0 to wait 114 * indefinitely 115 * @return True if the lock was acquired, false if waiting time elapsed before the lock was 116 * acquired 117 * @throws InterruptedException If the thread is interrupted while waiting to acquire the lock 118 */ 119 public boolean tryAcquire(final long timeoutMs) throws InterruptedException { 120 if (proc != null && proc.isLocked()) { 121 return true; 122 } 123 // Use new condition and procedure every time lock is requested. 124 final CountDownLatch lockAcquireLatch = new CountDownLatch(1); 125 if (regionInfos != null) { 126 proc = new LockProcedure(master.getConfiguration(), regionInfos, type, description, 127 lockAcquireLatch); 128 } else if (tableName != null) { 129 proc = new LockProcedure(master.getConfiguration(), tableName, type, description, 130 lockAcquireLatch); 131 } else if (namespace != null) { 132 proc = new LockProcedure(master.getConfiguration(), namespace, type, description, 133 lockAcquireLatch); 134 } else { 135 throw new UnsupportedOperationException("no namespace/table/region provided"); 136 } 137 138 // The user of a MasterLock should be 'hbase', the only case where this is not true 139 // is if from inside a coprocessor we try to take a master lock (which should be avoided) 140 proc.setOwner(master.getMasterProcedureExecutor().getEnvironment().getRequestUser()); 141 master.getMasterProcedureExecutor().submitProcedure(proc); 142 143 long deadline = 144 (timeoutMs > 0) ? EnvironmentEdgeManager.currentTime() + timeoutMs : Long.MAX_VALUE; 145 while (deadline >= EnvironmentEdgeManager.currentTime() && !proc.isLocked()) { 146 try { 147 lockAcquireLatch.await(deadline - EnvironmentEdgeManager.currentTime(), 148 TimeUnit.MILLISECONDS); 149 } catch (InterruptedException e) { 150 LOG.info("InterruptedException when waiting for lock: " + proc.toString()); 151 // kind of weird, releasing a lock which is not locked. This is to make the procedure 152 // finish immediately whenever it gets scheduled so that it doesn't hold the lock. 153 release(); 154 throw e; 155 } 156 } 157 if (!proc.isLocked()) { 158 LOG.info("Timed out waiting to acquire procedure lock: " + proc.toString()); 159 release(); 160 return false; 161 } 162 return true; 163 } 164 165 /** 166 * Release the lock. No-op if the lock was never acquired. 167 */ 168 public void release() { 169 if (proc != null) { 170 proc.unlock(master.getMasterProcedureExecutor().getEnvironment()); 171 } 172 proc = null; 173 } 174 175 @Override 176 public String toString() { 177 return "MasterLock: proc = " + proc.toString(); 178 } 179 180 LockProcedure getProc() { 181 return proc; 182 } 183 } 184 185 /** 186 * Locks on namespace/table/regions for remote operations. Since remote operations are unreliable 187 * and the client/RS may die anytime and never release locks, regular heartbeats are required to 188 * keep the lock held. 189 */ 190 public class RemoteLocks { 191 public long requestNamespaceLock(final String namespace, final LockType type, 192 final String description, final NonceKey nonceKey) 193 throws IllegalArgumentException, IOException { 194 master.getMasterCoprocessorHost().preRequestLock(namespace, null, null, type, description); 195 final LockProcedure proc = 196 new LockProcedure(master.getConfiguration(), namespace, type, description, null); 197 submitProcedure(proc, nonceKey); 198 master.getMasterCoprocessorHost().postRequestLock(namespace, null, null, type, description); 199 return proc.getProcId(); 200 } 201 202 public long requestTableLock(final TableName tableName, final LockType type, 203 final String description, final NonceKey nonceKey) 204 throws IllegalArgumentException, IOException { 205 master.getMasterCoprocessorHost().preRequestLock(null, tableName, null, type, description); 206 final LockProcedure proc = 207 new LockProcedure(master.getConfiguration(), tableName, type, description, null); 208 submitProcedure(proc, nonceKey); 209 master.getMasterCoprocessorHost().postRequestLock(null, tableName, null, type, description); 210 return proc.getProcId(); 211 } 212 213 /** 214 * @throws IllegalArgumentException if all regions are not from same table. 215 */ 216 public long requestRegionsLock(final RegionInfo[] regionInfos, final String description, 217 final NonceKey nonceKey) throws IllegalArgumentException, IOException { 218 master.getMasterCoprocessorHost().preRequestLock(null, null, regionInfos, LockType.EXCLUSIVE, 219 description); 220 final LockProcedure proc = new LockProcedure(master.getConfiguration(), regionInfos, 221 LockType.EXCLUSIVE, description, null); 222 submitProcedure(proc, nonceKey); 223 master.getMasterCoprocessorHost().postRequestLock(null, null, regionInfos, LockType.EXCLUSIVE, 224 description); 225 return proc.getProcId(); 226 } 227 228 /** 229 * @param keepAlive if false, release the lock. 230 * @return true, if procedure is found and it has the lock; else false. 231 */ 232 public boolean lockHeartbeat(final long procId, final boolean keepAlive) throws IOException { 233 final LockProcedure proc = 234 master.getMasterProcedureExecutor().getProcedure(LockProcedure.class, procId); 235 if (proc == null) return false; 236 237 master.getMasterCoprocessorHost().preLockHeartbeat(proc, keepAlive); 238 239 proc.updateHeartBeat(); 240 if (!keepAlive) { 241 proc.unlock(master.getMasterProcedureExecutor().getEnvironment()); 242 } 243 244 master.getMasterCoprocessorHost().postLockHeartbeat(proc, keepAlive); 245 246 return proc.isLocked(); 247 } 248 } 249}