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