/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you 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 org.apache.hadoop.hdfs.server.blockmanagement;
import io.hops.exception.StorageException;
import io.hops.exception.TransactionContextException;
import io.hops.transaction.EntityManager;
import org.apache.hadoop.hdfs.protocol.Block;
import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.BlockUCState;
import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.ReplicaState;
import org.apache.hadoop.hdfs.server.namenode.NameNode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Represents a block that is currently being constructed.<br>
* This is usually the last block of a file opened for write or append.
*/
public class BlockInfoUnderConstruction extends BlockInfo {
private static final List<ReplicaUnderConstruction> EMPTY_REPLICAS_ARRAY =
Collections.unmodifiableList(new ArrayList<ReplicaUnderConstruction>());
/**
* Block state. See {@link BlockUCState}
*/
private BlockUCState blockUCState;
/**
* A data-node responsible for block recovery.
*/
private int primaryNodeIndex = -1;
/**
* The new generation stamp, which this block will have after the recovery
* succeeds. Also used as a recovery id to identify the right recovery if any
* of the abandoned recoveries re-appear.
*/
private long blockRecoveryId = 0;
/**
* Create block and set its state to {@link BlockUCState#UNDER_CONSTRUCTION}.
*/
public BlockInfoUnderConstruction(Block blk, int inodeId) {
this(blk, inodeId, BlockUCState.UNDER_CONSTRUCTION);
}
/**
* Create a block that is currently being constructed.
*/
private BlockInfoUnderConstruction(Block blk, int inodeId,
BlockUCState state) {
super(blk, inodeId);
assert getBlockUCState() !=
BlockUCState.COMPLETE : "BlockInfoUnderConstruction cannot be in COMPLETE state";
this.blockUCState = state;
}
/**
* Create a block that is currently being constructed.
*/
public BlockInfoUnderConstruction(Block blk, int inodeId, BlockUCState state,
DatanodeDescriptor[] targets)
throws StorageException, TransactionContextException {
this(blk, inodeId, state);
setExpectedLocations(targets);
}
/**
* Convert an under construction block to a complete block.
*
* @return BlockInfo - a complete block.
* @throws IOException
* if the state of the block (the generation stamp and the
* length) has not been committed by the client or it does not have at
* least a
* minimal number of replicas reported from data-nodes.
*/
BlockInfo convertToCompleteBlock()
throws StorageException, TransactionContextException {
assert getBlockUCState() !=
BlockUCState.COMPLETE : "Trying to convert a COMPLETE block";
complete();
return new BlockInfo(this);
}
/**
* Set expected locations
*/
public void setExpectedLocations(DatanodeDescriptor[] targets)
throws StorageException, TransactionContextException {
for (DatanodeDescriptor dn : targets) {
addExpectedReplica(dn.getSId(), ReplicaState.RBW);
}
}
/**
* Create array of expected replica locations (as has been assigned by
* chooseTargets()).
*/
public DatanodeDescriptor[] getExpectedLocations(DatanodeManager datanodeMgr)
throws StorageException, TransactionContextException {
List<ReplicaUnderConstruction> rpls = getExpectedReplicas();
return getDatanodes(datanodeMgr, rpls);
}
/**
* Get the number of expected locations
*/
public int getNumExpectedLocations()
throws StorageException, TransactionContextException {
return getExpectedReplicas().size();
}
/**
* Return the state of the block under construction.
*
* @see BlockUCState
*/
@Override // BlockInfo
public BlockUCState getBlockUCState() {
return blockUCState;
}
public void setBlockUCStateNoPersistance(BlockUCState s) {
blockUCState = s;
}
/**
* Get block recovery ID
*/
public long getBlockRecoveryId() {
return blockRecoveryId;
}
/**
* Commit block's length and generation stamp as reported by the client. Set
* block state to {@link BlockUCState#COMMITTED}.
*
* @param block
* - contains client reported block length and generation
* @throws IOException
* if block ids are inconsistent.
*/
void commitBlock(Block block) throws IOException {
if (getBlockId() != block.getBlockId()) {
throw new IOException(
"Trying to commit inconsistent block: id = " + block.getBlockId() +
", expected id = " + getBlockId());
}
blockUCState = BlockUCState.COMMITTED;
this.set(getBlockId(), block.getNumBytes(), block.getGenerationStamp());
}
/**
* Initialize lease recovery for this block. Find the first alive data-node
* starting from the previous primary and make it primary.
*/
public void initializeBlockRecovery(long recoveryId,
DatanodeManager datanodeMgr)
throws StorageException, TransactionContextException {
setBlockUCState(BlockUCState.UNDER_RECOVERY);
List<ReplicaUnderConstruction> replicas = getExpectedReplicas();
setBlockRecoveryId(recoveryId);
if (replicas.isEmpty()) {
NameNode.blockStateChangeLog.warn(
"BLOCK*" + " BlockInfoUnderConstruction.initLeaseRecovery:" +
" No blocks found, lease removed.");
}
int previous = primaryNodeIndex;
for (int i = 1; i <= replicas.size(); i++) {
int j = (previous + i) % replicas.size();
ReplicaUnderConstruction replica = replicas.get(j);
DatanodeDescriptor primary =
datanodeMgr.getDatanode(replica.getStorageId());
if (primary.isAlive) {
primaryNodeIndex = j;
primary.addBlockToBeRecovered(this);
NameNode.blockStateChangeLog
.info("BLOCK* " + this + " recovery started, primary=" + primary);
return;
}
}
}
void addReplicaIfNotPresent(DatanodeDescriptor dn, Block block,
ReplicaState rState)
throws StorageException, TransactionContextException {
for (ReplicaUnderConstruction r : getExpectedReplicas()) {
if (r.getStorageId() == dn.getSId()) {
return;
}
}
addExpectedReplica(dn.getSId(), rState);
}
@Override // BlockInfo
// BlockInfoUnderConstruction participates in maps the same way as BlockInfo
public int hashCode() {
return super.hashCode();
}
@Override // BlockInfo
public boolean equals(Object obj) {
// Sufficient to rely on super's implementation
return (this == obj) || super.equals(obj);
}
@Override
public String toString() {
return "BlkInfoUnderConstruction " + super.toString();
}
@Override
public void appendStringTo(StringBuilder sb) {
super.appendStringTo(sb);
appendUCParts(sb);
}
private void appendUCParts(StringBuilder sb) {
// sb.append("{blockUCState=").append(blockUCState)
// .append(", primaryNodeIndex=").append(primaryNodeIndex)
// .append(", replicas=[");
//HOP: FIXME:
/*Iterator<ReplicaUnderConstruction> iter = replicas.iterator();
if (iter.hasNext()) {
iter.next().appendStringTo(sb);
while (iter.hasNext()) {
sb.append(", ");
iter.next().appendStringTo(sb);
}
}
sb.append("]}");*/
}
private List<ReplicaUnderConstruction> getExpectedReplicas()
throws StorageException, TransactionContextException {
List<ReplicaUnderConstruction> replicas =
(List<ReplicaUnderConstruction>) EntityManager
.findList(ReplicaUnderConstruction.Finder.ByBlockIdAndINodeId,
getBlockId(), getInodeId());
if (replicas != null) {
Collections.sort(replicas, ReplicaUnderConstruction.Order.ByStorageId);
} else {
replicas = EMPTY_REPLICAS_ARRAY;
}
return replicas;
}
private ReplicaUnderConstruction addExpectedReplica(int storageId,
ReplicaState rState)
throws StorageException, TransactionContextException {
if (hasExpectedReplicaIn(storageId)) {
NameNode.blockStateChangeLog.warn(
"BLOCK* Trying to store multiple blocks of the file on one DataNode. Returning null");
return null;
}
ReplicaUnderConstruction replica =
new ReplicaUnderConstruction(rState, storageId, getBlockId(),
getInodeId());
add(replica);
return replica;
}
private boolean hasExpectedReplicaIn(int storageId)
throws StorageException, TransactionContextException {
for (ReplicaUnderConstruction replica : getExpectedReplicas()) {
if (replica.getStorageId() == storageId) {
return true;
}
}
return false;
}
public void setBlockRecoveryIdNoPersistance(long recoveryId) {
this.blockRecoveryId = recoveryId;
}
public void setPrimaryNodeIndexNoPersistance(int nodeIndex) {
this.primaryNodeIndex = nodeIndex;
}
public int getPrimaryNodeIndex() {
return this.primaryNodeIndex;
}
private void complete() throws StorageException, TransactionContextException {
for (ReplicaUnderConstruction rep : getExpectedReplicas()) {
EntityManager.remove(rep);
}
}
public void setBlockUCState(BlockUCState s)
throws StorageException, TransactionContextException {
setBlockUCStateNoPersistance(s);
save();
}
public void setBlockRecoveryId(long recoveryId)
throws StorageException, TransactionContextException {
setBlockRecoveryIdNoPersistance(recoveryId);
save();
}
public void setPrimaryNodeIndex(int nodeIndex)
throws StorageException, TransactionContextException {
setPrimaryNodeIndexNoPersistance(nodeIndex);
save();
}
}