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 LockAndQueue metaLock; 060 061 public SchemaLocking(Function<Long, Procedure<?>> procedureRetriever) { 062 this.procedureRetriever = procedureRetriever; 063 this.metaLock = new LockAndQueue(procedureRetriever); 064 } 065 066 private <T> LockAndQueue getLock(Map<T, LockAndQueue> map, T key) { 067 LockAndQueue lock = map.get(key); 068 if (lock == null) { 069 lock = new LockAndQueue(procedureRetriever); 070 map.put(key, lock); 071 } 072 return lock; 073 } 074 075 LockAndQueue getTableLock(TableName tableName) { 076 return getLock(tableLocks, tableName); 077 } 078 079 LockAndQueue removeTableLock(TableName tableName) { 080 return tableLocks.remove(tableName); 081 } 082 083 LockAndQueue getNamespaceLock(String namespace) { 084 return getLock(namespaceLocks, namespace); 085 } 086 087 LockAndQueue getRegionLock(String encodedRegionName) { 088 return getLock(regionLocks, encodedRegionName); 089 } 090 091 /** 092 * @deprecated only used for {@link RecoverMetaProcedure}. Should be removed along with 093 * {@link RecoverMetaProcedure}. 094 */ 095 @Deprecated 096 LockAndQueue getMetaLock() { 097 return metaLock; 098 } 099 100 LockAndQueue removeRegionLock(String encodedRegionName) { 101 return regionLocks.remove(encodedRegionName); 102 } 103 104 LockAndQueue getServerLock(ServerName serverName) { 105 return getLock(serverLocks, serverName); 106 } 107 108 LockAndQueue removeServerLock(ServerName serverName) { 109 return serverLocks.remove(serverName); 110 } 111 112 LockAndQueue getPeerLock(String peerId) { 113 return getLock(peerLocks, peerId); 114 } 115 116 LockAndQueue removePeerLock(String peerId) { 117 return peerLocks.remove(peerId); 118 } 119 120 private LockedResource createLockedResource(LockedResourceType resourceType, String resourceName, 121 LockAndQueue queue) { 122 LockType lockType; 123 Procedure<?> exclusiveLockOwnerProcedure; 124 int sharedLockCount; 125 126 if (queue.hasExclusiveLock()) { 127 lockType = LockType.EXCLUSIVE; 128 exclusiveLockOwnerProcedure = queue.getExclusiveLockOwnerProcedure(); 129 sharedLockCount = 0; 130 } else { 131 lockType = LockType.SHARED; 132 exclusiveLockOwnerProcedure = null; 133 sharedLockCount = queue.getSharedLockCount(); 134 } 135 136 List<Procedure<?>> waitingProcedures = new ArrayList<>(); 137 138 queue.filterWaitingQueue(p -> p instanceof LockProcedure) 139 .forEachOrdered(waitingProcedures::add); 140 141 return new LockedResource(resourceType, resourceName, lockType, exclusiveLockOwnerProcedure, 142 sharedLockCount, waitingProcedures); 143 } 144 145 private <T> void addToLockedResources(List<LockedResource> lockedResources, 146 Map<T, LockAndQueue> locks, Function<T, String> keyTransformer, 147 LockedResourceType resourcesType) { 148 locks.entrySet().stream().filter(e -> e.getValue().isLocked()) 149 .map(e -> createLockedResource(resourcesType, keyTransformer.apply(e.getKey()), e.getValue())) 150 .forEachOrdered(lockedResources::add); 151 } 152 153 /** 154 * List lock queues. 155 * @return the locks 156 */ 157 List<LockedResource> getLocks() { 158 List<LockedResource> lockedResources = new ArrayList<>(); 159 addToLockedResources(lockedResources, serverLocks, sn -> sn.getServerName(), 160 LockedResourceType.SERVER); 161 addToLockedResources(lockedResources, namespaceLocks, Function.identity(), 162 LockedResourceType.NAMESPACE); 163 addToLockedResources(lockedResources, tableLocks, tn -> tn.getNameAsString(), 164 LockedResourceType.TABLE); 165 addToLockedResources(lockedResources, regionLocks, Function.identity(), 166 LockedResourceType.REGION); 167 addToLockedResources(lockedResources, peerLocks, Function.identity(), LockedResourceType.PEER); 168 addToLockedResources(lockedResources, ImmutableMap.of(TableName.META_TABLE_NAME, metaLock), 169 tn -> tn.getNameAsString(), LockedResourceType.META); 170 return lockedResources; 171 } 172 173 /** 174 * @return {@link LockedResource} for resource of specified type & name. null if resource is not 175 * locked. 176 */ 177 LockedResource getLockResource(LockedResourceType resourceType, String resourceName) { 178 LockAndQueue queue; 179 switch (resourceType) { 180 case SERVER: 181 queue = serverLocks.get(ServerName.valueOf(resourceName)); 182 break; 183 case NAMESPACE: 184 queue = namespaceLocks.get(resourceName); 185 break; 186 case TABLE: 187 queue = tableLocks.get(TableName.valueOf(resourceName)); 188 break; 189 case REGION: 190 queue = regionLocks.get(resourceName); 191 break; 192 case PEER: 193 queue = peerLocks.get(resourceName); 194 break; 195 case META: 196 queue = metaLock; 197 default: 198 queue = null; 199 } 200 return queue != null ? createLockedResource(resourceType, resourceName, queue) : null; 201 } 202 203 /** 204 * Removes all locks by clearing the maps. Used when procedure executor is stopped for failure and 205 * recovery testing. 206 */ 207 void clear() { 208 serverLocks.clear(); 209 namespaceLocks.clear(); 210 tableLocks.clear(); 211 regionLocks.clear(); 212 peerLocks.clear(); 213 } 214 215 @Override 216 public String toString() { 217 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) 218 .append("serverLocks", filterUnlocked(serverLocks)) 219 .append("namespaceLocks", filterUnlocked(namespaceLocks)) 220 .append("tableLocks", filterUnlocked(tableLocks)) 221 .append("regionLocks", filterUnlocked(regionLocks)) 222 .append("peerLocks", filterUnlocked(peerLocks)) 223 .append("metaLocks", filterUnlocked(ImmutableMap.of(TableName.META_TABLE_NAME, metaLock))) 224 .build(); 225 226 } 227 228 private String filterUnlocked(Map<?, LockAndQueue> locks) { 229 return locks.entrySet().stream().filter(val -> !val.getValue().isLocked()) 230 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).toString(); 231 } 232}