blob: c3ff4e79cf78d5ee4e1ed1f50581377509bb3712 [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.HdfsConstants;
import org.apache.hadoop.hdfs.server.common.GenerationStamp;
import org.apache.hadoop.hdfs.server.common.HdfsServerConstants;
import java.io.IOException;
/**
* BlockIdManager allocates the generation stamps and the block ID. The
* {@see FSNamesystem} is responsible for persisting the allocations in the
* {@see EditLog}.
*/
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;
public BlockIdManager(BlockManager blockManager) {
this.legacyGenerationStampLimit =
HdfsConstants.GRANDFATHER_GENERATION_STAMP;
this.blockIdGenerator = new SequentialBlockIdGenerator(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 block ID for this filesystem. This is
* the basis for allocating new block IDs.
*/
public void setLastAllocatedBlockId(long blockId) {
blockIdGenerator.skipTo(blockId);
}
/**
* Gets the maximum sequentially allocated block ID for this filesystem
*/
public long getLastAllocatedBlockId() {
return blockIdGenerator.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() {
return blockIdGenerator.nextValue();
}
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);
legacyGenerationStampLimit = HdfsConstants.GRANDFATHER_GENERATION_STAMP;
}
}