/*
* Copyright (C) 2015 hops.io.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.hops.transaction.lock;
import com.google.common.collect.Iterables;
import io.hops.exception.StorageException;
import io.hops.exception.TransactionContextException;
import io.hops.metadata.hdfs.dal.INodeDataAccess;
import io.hops.resolvingcache.Cache;
import io.hops.metadata.hdfs.dal.BlockInfoDataAccess;
import io.hops.metadata.hdfs.entity.INodeCandidatePrimaryKey;
import io.hops.transaction.EntityManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hdfs.server.namenode.INode;
import org.apache.hadoop.hdfs.server.namenode.INodeAttributes;
import org.apache.hadoop.hdfs.server.namenode.INodeDirectoryWithQuota;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
public abstract class BaseINodeLock extends Lock {
protected final static Log LOG = LogFactory.getLog(BaseINodeLock.class);
private final Map<INode, TransactionLockTypes.INodeLockType>
allLockedInodesInTx;
private final ResolvedINodesMap resolvedINodesMap;
private boolean isPartitionKeyAlreaySet = false;
protected static TransactionLockTypes.INodeLockType DEFAULT_INODE_LOCK_TYPE =
TransactionLockTypes.INodeLockType.READ_COMMITTED;
protected static boolean setPartitionKeyEnabled = false;
protected static boolean setRandomParitionKeyEnabled = false;
protected static Random rand = new Random(System.currentTimeMillis());
public static void setDefaultLockType(
TransactionLockTypes.INodeLockType defaultLockType) {
DEFAULT_INODE_LOCK_TYPE = defaultLockType;
}
static void enableSetPartitionKey(boolean enable) {
setPartitionKeyEnabled = enable;
}
static void enableSetRandomPartitionKey(boolean enable) {
setRandomParitionKeyEnabled = enable;
}
protected BaseINodeLock() {
this.allLockedInodesInTx =
new HashMap<INode, TransactionLockTypes.INodeLockType>();
this.resolvedINodesMap = new ResolvedINodesMap();
}
private class ResolvedINodesMap {
private final Map<String, PathRelatedINodes> pathToPathINodes =
new HashMap<String, PathRelatedINodes>();
private final Collection<INode> individualInodes = new ArrayList<INode>();
private class PathRelatedINodes {
private List<INode> pathINodes;
private List<INode> childINodes;
}
private PathRelatedINodes getWithLazyInit(String path) {
if (!pathToPathINodes.containsKey(path)) {
PathRelatedINodes pathRelatedINodes = new PathRelatedINodes();
pathToPathINodes.put(path, pathRelatedINodes);
return pathRelatedINodes;
}
return pathToPathINodes.get(path);
}
private void putPathINodes(String path, List<INode> iNodes) {
PathRelatedINodes pathRelatedINodes = getWithLazyInit(path);
pathRelatedINodes.pathINodes = iNodes;
}
private void putChildINodes(String path, List<INode> iNodes) {
PathRelatedINodes pathRelatedINodes = getWithLazyInit(path);
pathRelatedINodes.childINodes = iNodes;
}
private void putIndividualINode(INode iNode) {
individualInodes.add(iNode);
}
private List<INode> getPathINodes(String path) {
PathRelatedINodes pri = pathToPathINodes.get(path);
return pri.pathINodes;
}
private List<INode> getChildINodes(String path) {
return pathToPathINodes.get(path).childINodes;
}
public Iterable<INode> getAll() {
Iterable iterable = null;
for (PathRelatedINodes pathRelatedINodes : pathToPathINodes.values()) {
List<INode> pathINodes =
pathRelatedINodes.pathINodes == null ? Collections.EMPTY_LIST :
pathRelatedINodes.pathINodes;
List<INode> childINodes =
pathRelatedINodes.childINodes == null ? Collections.EMPTY_LIST :
pathRelatedINodes.childINodes;
if (iterable == null) {
iterable = Iterables.concat(pathINodes, childINodes);
} else {
iterable = Iterables.concat(iterable, pathINodes, childINodes);
}
}
if (iterable == null) {
iterable = Collections.EMPTY_LIST;
}
return Iterables.concat(iterable, individualInodes);
}
}
Iterable<INode> getAllResolvedINodes() {
return resolvedINodesMap.getAll();
}
void addPathINodesAndUpdateResolvingCache(String path, List<INode> iNodes) {
addPathINodes(path, iNodes);
updateResolvingCache(path, iNodes);
}
void updateResolvingCache(String path, List<INode> iNodes){
Cache.getInstance().set(path, iNodes);
}
void updateResolvingCache(INode inode){
Cache.getInstance().set(inode);
}
void addPathINodes(String path, List<INode> iNodes) {
resolvedINodesMap.putPathINodes(path, iNodes);
}
void addChildINodes(String path, List<INode> iNodes) {
resolvedINodesMap.putChildINodes(path, iNodes);
}
void addIndividualINode(INode iNode) {
resolvedINodesMap.putIndividualINode(iNode);
}
List<INode> getPathINodes(String path) {
return resolvedINodesMap.getPathINodes(path);
}
INode getTargetINode(String path) {
List<INode> list = resolvedINodesMap.getPathINodes(path);
return list.get(list.size() - 1);
}
List<INode> getChildINodes(String path) {
return resolvedINodesMap.getChildINodes(path);
}
public TransactionLockTypes.INodeLockType getLockedINodeLockType(
INode inode) {
return allLockedInodesInTx.get(inode);
}
protected INode find(TransactionLockTypes.INodeLockType lock, String name,
int parentId, int partitionId, int possibleINodeId)
throws StorageException, TransactionContextException {
setINodeLockType(lock);
INode inode = EntityManager
.find(INode.Finder.ByNameParentIdAndPartitionId, name, parentId, partitionId, possibleINodeId);
addLockedINodes(inode, lock);
return inode;
}
protected INode find(TransactionLockTypes.INodeLockType lock, String name,
int parentId, int partitionId) throws StorageException, TransactionContextException {
setINodeLockType(lock);
INode inode =
EntityManager.find(INode.Finder.ByNameParentIdAndPartitionId, name, parentId, partitionId);
addLockedINodes(inode, lock);
return inode;
}
protected INode find(TransactionLockTypes.INodeLockType lock, int id)
throws StorageException, TransactionContextException {
setINodeLockType(lock);
INode inode = EntityManager.find(INode.Finder.ByINodeIdFTIS, id);
addLockedINodes(inode, lock);
return inode;
}
protected List<INode> find(TransactionLockTypes.INodeLockType lock,
String[] names, int[] parentIds, int[] partitionIds, boolean checkLocalCache)
throws StorageException, TransactionContextException {
setINodeLockType(lock);
List<INode> inodes = (List<INode>) EntityManager.findList(checkLocalCache ? INode.Finder
.ByNamesParentIdsAndPartitionIdsCheckLocal : INode.Finder
.ByNamesParentIdsAndPartitionIds, names, parentIds, partitionIds);
if(inodes != null) {
for (INode inode : inodes) {
addLockedINodes(inode, lock);
}
}
return inodes;
}
protected void addLockedINodes(INode inode,
TransactionLockTypes.INodeLockType lock) {
if (inode == null) {
return;
}
TransactionLockTypes.INodeLockType oldLock = allLockedInodesInTx.get(inode);
if (oldLock == null || oldLock.compareTo(lock) < 0) {
allLockedInodesInTx.put(inode, lock);
}
}
protected void setINodeLockType(TransactionLockTypes.INodeLockType lock)
throws StorageException {
switch (lock) {
case WRITE:
case WRITE_ON_TARGET_AND_PARENT:
EntityManager.writeLock();
break;
case READ:
EntityManager.readLock();
break;
case READ_COMMITTED:
EntityManager.readCommited();
break;
}
}
protected void acquireINodeAttributes()
throws StorageException, TransactionContextException {
List<INodeCandidatePrimaryKey> pks =
new ArrayList<INodeCandidatePrimaryKey>();
for (INode inode : getAllResolvedINodes()) {
if (inode instanceof INodeDirectoryWithQuota) {
INodeCandidatePrimaryKey pk =
new INodeCandidatePrimaryKey(inode.getId());
pks.add(pk);
}
}
acquireLockList(DEFAULT_LOCK_TYPE, INodeAttributes.Finder.ByINodeIds, pks);
}
protected void setPartitioningKey(Integer partitionId)
throws StorageException, TransactionContextException {
if (setPartitionKeyEnabled && partitionId != null && !isPartitionKeyAlreaySet ) {
//set partitioning key
Object[] key = new Object[3];
key[0] = partitionId;
key[1] = 0;
key[2] = "";
EntityManager.setPartitionKey(INodeDataAccess.class, key);
isPartitionKeyAlreaySet=true; //to avoid setting partition key multiple times. It can happen during rename
LOG.debug("Setting PartitionKey to be " + partitionId);
} else {
LOG.debug("Transaction PartitionKey is not Set");
}
}
@Override
protected final Type getType() {
return Type.INode;
}
}