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 java.util.ArrayList; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.function.Function; 025import org.apache.hadoop.hbase.ServerName; 026import org.apache.hadoop.hbase.TableName; 027import org.apache.hadoop.hbase.master.locking.LockProcedure; 028import org.apache.hadoop.hbase.procedure2.LockAndQueue; 029import org.apache.hadoop.hbase.procedure2.LockType; 030import org.apache.hadoop.hbase.procedure2.LockedResource; 031import org.apache.hadoop.hbase.procedure2.LockedResourceType; 032import org.apache.hadoop.hbase.procedure2.Procedure; 033import org.apache.yetus.audience.InterfaceAudience; 034 035import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableMap; 036 037/** 038 * <p> 039 * Locks on namespaces, tables, and regions. 040 * </p> 041 * <p> 042 * Since LockAndQueue implementation is NOT thread-safe, schedLock() guards all calls to these 043 * locks. 044 * </p> 045 */ 046@InterfaceAudience.Private 047class SchemaLocking { 048 049 private final Function<Long, Procedure<?>> procedureRetriever; 050 private final Map<ServerName, LockAndQueue> serverLocks = new HashMap<>(); 051 private final Map<String, LockAndQueue> namespaceLocks = new HashMap<>(); 052 private final Map<TableName, LockAndQueue> tableLocks = new HashMap<>(); 053 // Single map for all regions irrespective of tables. Key is encoded region name. 054 private final Map<String, LockAndQueue> regionLocks = new HashMap<>(); 055 private final Map<String, LockAndQueue> peerLocks = new HashMap<>(); 056 private final LockAndQueue metaLock; 057 058 public SchemaLocking(Function<Long, Procedure<?>> procedureRetriever) { 059 this.procedureRetriever = procedureRetriever; 060 this.metaLock = new LockAndQueue(procedureRetriever); 061 } 062 063 private <T> LockAndQueue getLock(Map<T, LockAndQueue> map, T key) { 064 LockAndQueue lock = map.get(key); 065 if (lock == null) { 066 lock = new LockAndQueue(procedureRetriever); 067 map.put(key, lock); 068 } 069 return lock; 070 } 071 072 LockAndQueue getTableLock(TableName tableName) { 073 return getLock(tableLocks, tableName); 074 } 075 076 LockAndQueue removeTableLock(TableName tableName) { 077 return tableLocks.remove(tableName); 078 } 079 080 LockAndQueue getNamespaceLock(String namespace) { 081 return getLock(namespaceLocks, namespace); 082 } 083 084 LockAndQueue getRegionLock(String encodedRegionName) { 085 return getLock(regionLocks, encodedRegionName); 086 } 087 088 /** 089 * @deprecated only used for {@link RecoverMetaProcedure}. Should be removed along with 090 * {@link RecoverMetaProcedure}. 091 */ 092 @Deprecated 093 LockAndQueue getMetaLock() { 094 return metaLock; 095 } 096 097 LockAndQueue removeRegionLock(String encodedRegionName) { 098 return regionLocks.remove(encodedRegionName); 099 } 100 101 LockAndQueue getServerLock(ServerName serverName) { 102 return getLock(serverLocks, serverName); 103 } 104 105 LockAndQueue removeServerLock(ServerName serverName) { 106 return serverLocks.remove(serverName); 107 } 108 109 LockAndQueue getPeerLock(String peerId) { 110 return getLock(peerLocks, peerId); 111 } 112 113 LockAndQueue removePeerLock(String peerId) { 114 return peerLocks.remove(peerId); 115 } 116 117 private LockedResource createLockedResource(LockedResourceType resourceType, String resourceName, 118 LockAndQueue queue) { 119 LockType lockType; 120 Procedure<?> exclusiveLockOwnerProcedure; 121 int sharedLockCount; 122 123 if (queue.hasExclusiveLock()) { 124 lockType = LockType.EXCLUSIVE; 125 exclusiveLockOwnerProcedure = queue.getExclusiveLockOwnerProcedure(); 126 sharedLockCount = 0; 127 } else { 128 lockType = LockType.SHARED; 129 exclusiveLockOwnerProcedure = null; 130 sharedLockCount = queue.getSharedLockCount(); 131 } 132 133 List<Procedure<?>> waitingProcedures = new ArrayList<>(); 134 135 queue.filterWaitingQueue(p -> p instanceof LockProcedure) 136 .forEachOrdered(waitingProcedures::add); 137 138 return new LockedResource(resourceType, resourceName, lockType, exclusiveLockOwnerProcedure, 139 sharedLockCount, waitingProcedures); 140 } 141 142 private <T> void addToLockedResources(List<LockedResource> lockedResources, 143 Map<T, LockAndQueue> locks, Function<T, String> keyTransformer, 144 LockedResourceType resourcesType) { 145 locks.entrySet().stream().filter(e -> e.getValue().isLocked()) 146 .map(e -> createLockedResource(resourcesType, keyTransformer.apply(e.getKey()), e.getValue())) 147 .forEachOrdered(lockedResources::add); 148 } 149 150 /** 151 * List lock queues. 152 * @return the locks 153 */ 154 List<LockedResource> getLocks() { 155 List<LockedResource> lockedResources = new ArrayList<>(); 156 addToLockedResources(lockedResources, serverLocks, sn -> sn.getServerName(), 157 LockedResourceType.SERVER); 158 addToLockedResources(lockedResources, namespaceLocks, Function.identity(), 159 LockedResourceType.NAMESPACE); 160 addToLockedResources(lockedResources, tableLocks, tn -> tn.getNameAsString(), 161 LockedResourceType.TABLE); 162 addToLockedResources(lockedResources, regionLocks, Function.identity(), 163 LockedResourceType.REGION); 164 addToLockedResources(lockedResources, peerLocks, Function.identity(), LockedResourceType.PEER); 165 addToLockedResources(lockedResources, ImmutableMap.of(TableName.META_TABLE_NAME, metaLock), 166 tn -> tn.getNameAsString(), LockedResourceType.META); 167 return lockedResources; 168 } 169 170 /** 171 * @return {@link LockedResource} for resource of specified type & name. null if resource is not 172 * locked. 173 */ 174 LockedResource getLockResource(LockedResourceType resourceType, String resourceName) { 175 LockAndQueue queue; 176 switch (resourceType) { 177 case SERVER: 178 queue = serverLocks.get(ServerName.valueOf(resourceName)); 179 break; 180 case NAMESPACE: 181 queue = namespaceLocks.get(resourceName); 182 break; 183 case TABLE: 184 queue = tableLocks.get(TableName.valueOf(resourceName)); 185 break; 186 case REGION: 187 queue = regionLocks.get(resourceName); 188 break; 189 case PEER: 190 queue = peerLocks.get(resourceName); 191 break; 192 case META: 193 queue = metaLock; 194 default: 195 queue = null; 196 } 197 return queue != null ? createLockedResource(resourceType, resourceName, queue) : null; 198 } 199 200 /** 201 * Removes all locks by clearing the maps. Used when procedure executor is stopped for failure and 202 * recovery testing. 203 */ 204 void clear() { 205 serverLocks.clear(); 206 namespaceLocks.clear(); 207 tableLocks.clear(); 208 regionLocks.clear(); 209 peerLocks.clear(); 210 } 211 212 @Override 213 public String toString() { 214 return "serverLocks=" + filterUnlocked(this.serverLocks) + ", namespaceLocks=" + 215 filterUnlocked(this.namespaceLocks) + ", tableLocks=" + filterUnlocked(this.tableLocks) + 216 ", regionLocks=" + filterUnlocked(this.regionLocks) + ", peerLocks=" + 217 filterUnlocked(this.peerLocks) + ", metaLocks=" + 218 filterUnlocked(ImmutableMap.of(TableName.META_TABLE_NAME, metaLock)); 219 } 220 221 private String filterUnlocked(Map<?, LockAndQueue> locks) { 222 StringBuilder sb = new StringBuilder("{"); 223 int initialLength = sb.length(); 224 for (Map.Entry<?, LockAndQueue> entry : locks.entrySet()) { 225 if (!entry.getValue().isLocked()) { 226 continue; 227 } 228 if (sb.length() > initialLength) { 229 sb.append(", "); 230 } 231 sb.append("{").append(entry.getKey()).append("=").append(entry.getValue()).append("}"); 232 } 233 sb.append("}"); 234 return sb.toString(); 235 } 236}