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