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}