blob: bec6ec8368120eaeea2e83bee64e7055ea706351 [file] [log] [blame]
/**
* 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 com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.apache.hadoop.hdfs.protocol.Block;
import org.apache.hadoop.hdfs.protocol.BlockType;
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
import org.apache.hadoop.hdfs.server.common.GenerationStamp;
import org.apache.hadoop.hdfs.server.common.HdfsServerConstants;
import org.apache.hadoop.hdfs.server.namenode.FSNamesystem;
import org.apache.hadoop.hdfs.server.namenode.FSEditLog;
import java.io.IOException;
import static org.apache.hadoop.hdfs.protocol.BlockType.STRIPED;
/**
* BlockIdManager allocates the generation stamps and the block ID. The
* {@link FSNamesystem} is responsible for persisting the allocations in the
* {@link FSEditLog}.
*/
public class BlockIdManager {
/**
* The global generation stamp for legacy blocks with randomly
* generated block IDs.
*/
private final GenerationStamp legacyGenerationStamp = new GenerationStamp();
/**
* The global generation stamp for this file system.
*/
private final GenerationStamp generationStamp = new GenerationStamp();
/**
* Most recent global generation stamp as seen on Active NameNode.
* Used by StandbyNode only.<p/>
* StandbyNode does not update its global {@link #generationStamp} during
* edits tailing. The global generation stamp on StandbyNode is updated
* <ol><li>when the block with the next generation stamp is actually
* received</li>
* <li>during fail-over it is bumped to the last value received from the
* Active NN through edits and stored as
* {@link #impendingGenerationStamp}</li></ol>
* The former helps to avoid a race condition with IBRs during edits tailing.
* The latter guarantees that generation stamps are never reused by new
* Active after fail-over.
* <p/> See HDFS-14941 for more details.
*/
private final GenerationStamp impendingGenerationStamp
= new GenerationStamp();
/**
* The value of the generation stamp when the first switch to sequential
* block IDs was made. Blocks with generation stamps below this value
* have randomly allocated block IDs. Blocks with generation stamps above
* this value had sequentially allocated block IDs. Read from the fsImage
* (or initialized as an offset from the V1 (legacy) generation stamp on
* upgrade).
*/
private long legacyGenerationStampLimit;
/**
* The global block ID space for this file system.
*/
private final SequentialBlockIdGenerator blockIdGenerator;
private final SequentialBlockGroupIdGenerator blockGroupIdGenerator;
public BlockIdManager(BlockManager blockManager) {
this.legacyGenerationStampLimit =
HdfsConstants.GRANDFATHER_GENERATION_STAMP;
this.blockIdGenerator = new SequentialBlockIdGenerator(blockManager);
this.blockGroupIdGenerator = new SequentialBlockGroupIdGenerator(blockManager);
}
/**
* Upgrades the generation stamp for the filesystem
* by reserving a sufficient range for all existing blocks.
* Should be invoked only during the first upgrade to
* sequential block IDs.
*/
public long upgradeLegacyGenerationStamp() {
Preconditions.checkState(generationStamp.getCurrentValue() ==
GenerationStamp.LAST_RESERVED_STAMP);
generationStamp.skipTo(legacyGenerationStamp.getCurrentValue() +
HdfsServerConstants.RESERVED_LEGACY_GENERATION_STAMPS);
legacyGenerationStampLimit = generationStamp.getCurrentValue();
return generationStamp.getCurrentValue();
}
/**
* Sets the generation stamp that delineates random and sequentially
* allocated block IDs.
*
* @param stamp set generation stamp limit to this value
*/
public void setLegacyGenerationStampLimit(long stamp) {
Preconditions.checkState(legacyGenerationStampLimit ==
HdfsConstants.GRANDFATHER_GENERATION_STAMP);
legacyGenerationStampLimit = stamp;
}
/**
* Gets the value of the generation stamp that delineates sequential
* and random block IDs.
*/
public long getGenerationStampAtblockIdSwitch() {
return legacyGenerationStampLimit;
}
@VisibleForTesting
SequentialBlockIdGenerator getBlockIdGenerator() {
return blockIdGenerator;
}
/**
* Sets the maximum allocated contiguous block ID for this filesystem. This is
* the basis for allocating new block IDs.
*/
public void setLastAllocatedContiguousBlockId(long blockId) {
blockIdGenerator.skipTo(blockId);
}
/**
* Gets the maximum sequentially allocated contiguous block ID for this
* filesystem
*/
public long getLastAllocatedContiguousBlockId() {
return blockIdGenerator.getCurrentValue();
}
/**
* Sets the maximum allocated striped block ID for this filesystem. This is
* the basis for allocating new block IDs.
*/
public void setLastAllocatedStripedBlockId(long blockId) {
blockGroupIdGenerator.skipTo(blockId);
}
/**
* Gets the maximum sequentially allocated striped block ID for this
* filesystem
*/
public long getLastAllocatedStripedBlockId() {
return blockGroupIdGenerator.getCurrentValue();
}
/**
* Sets the current generation stamp for legacy blocks
*/
public void setLegacyGenerationStamp(long stamp) {
legacyGenerationStamp.setCurrentValue(stamp);
}
/**
* Gets the current generation stamp for legacy blocks
*/
public long getLegacyGenerationStamp() {
return legacyGenerationStamp.getCurrentValue();
}
/**
* Gets the current generation stamp for this filesystem
*/
public void setGenerationStamp(long stamp) {
generationStamp.setCurrentValue(stamp);
}
/**
* Set the currently highest gen stamp from active. Used
* by Standby only.
* @param stamp new genstamp
*/
public void setImpendingGenerationStamp(long stamp) {
impendingGenerationStamp.setIfGreater(stamp);
}
/**
* Set the current genstamp to the impending genstamp.
*/
public void applyImpendingGenerationStamp() {
setGenerationStampIfGreater(impendingGenerationStamp.getCurrentValue());
}
@VisibleForTesting
public long getImpendingGenerationStamp() {
return impendingGenerationStamp.getCurrentValue();
}
/**
* Set genstamp only when the given one is higher.
* @param stamp
*/
public void setGenerationStampIfGreater(long stamp) {
generationStamp.setIfGreater(stamp);
}
public long getGenerationStamp() {
return generationStamp.getCurrentValue();
}
/**
* Increments, logs and then returns the stamp
*/
long nextGenerationStamp(boolean legacyBlock) throws IOException {
return legacyBlock ? getNextLegacyGenerationStamp() :
getNextGenerationStamp();
}
@VisibleForTesting
long getNextLegacyGenerationStamp() throws IOException {
long legacyGenStamp = legacyGenerationStamp.nextValue();
if (legacyGenStamp >= legacyGenerationStampLimit) {
// We ran out of generation stamps for legacy blocks. In practice, it
// is extremely unlikely as we reserved 1T legacy generation stamps. The
// result is that we can no longer append to the legacy blocks that
// were created before the upgrade to sequential block IDs.
throw new OutOfLegacyGenerationStampsException();
}
return legacyGenStamp;
}
@VisibleForTesting
long getNextGenerationStamp() {
return generationStamp.nextValue();
}
public long getLegacyGenerationStampLimit() {
return legacyGenerationStampLimit;
}
/**
* Determine whether the block ID was randomly generated (legacy) or
* sequentially generated. The generation stamp value is used to
* make the distinction.
*
* @return true if the block ID was randomly generated, false otherwise.
*/
boolean isLegacyBlock(Block block) {
return block.getGenerationStamp() < getLegacyGenerationStampLimit();
}
/**
* Increments, logs and then returns the block ID
*/
long nextBlockId(BlockType blockType) {
switch(blockType) {
case CONTIGUOUS: return blockIdGenerator.nextValue();
case STRIPED: return blockGroupIdGenerator.nextValue();
default:
throw new IllegalArgumentException(
"nextBlockId called with an unsupported BlockType");
}
}
boolean isGenStampInFuture(Block block) {
if (isLegacyBlock(block)) {
return block.getGenerationStamp() > getLegacyGenerationStamp();
} else {
return block.getGenerationStamp() > getGenerationStamp();
}
}
void clear() {
legacyGenerationStamp.setCurrentValue(GenerationStamp.LAST_RESERVED_STAMP);
generationStamp.setCurrentValue(GenerationStamp.LAST_RESERVED_STAMP);
getBlockIdGenerator().setCurrentValue(SequentialBlockIdGenerator
.LAST_RESERVED_BLOCK_ID);
getBlockGroupIdGenerator().setCurrentValue(Long.MIN_VALUE);
legacyGenerationStampLimit = HdfsConstants.GRANDFATHER_GENERATION_STAMP;
}
/**
* Return true if the block is a striped block.
*
* Before HDFS-4645, block ID was randomly generated (legacy), so it is
* possible that legacy block ID to be negative, which should not be
* considered as striped block ID.
*
* @see #isLegacyBlock(Block) detecting legacy block IDs.
*/
public boolean isStripedBlock(Block block) {
return isStripedBlockID(block.getBlockId()) && !isLegacyBlock(block);
}
/**
* See {@link #isStripedBlock(Block)}, we should not use this function alone
* to determine a block is striped block.
*/
public static boolean isStripedBlockID(long id) {
return BlockType.fromBlockId(id) == STRIPED;
}
/**
* The last 4 bits of HdfsConstants.BLOCK_GROUP_INDEX_MASK(15) is 1111,
* so the last 4 bits of (~HdfsConstants.BLOCK_GROUP_INDEX_MASK) is 0000
* and the other 60 bits are 1. Group ID is the first 60 bits of any
* data/parity block id in the same striped block group.
*/
static long convertToStripedID(long id) {
return id & (~HdfsServerConstants.BLOCK_GROUP_INDEX_MASK);
}
public static byte getBlockIndex(Block reportedBlock) {
return (byte) (reportedBlock.getBlockId() &
HdfsServerConstants.BLOCK_GROUP_INDEX_MASK);
}
SequentialBlockGroupIdGenerator getBlockGroupIdGenerator() {
return blockGroupIdGenerator;
}
}