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.hdfs.server.blockmanagement;
019
020import com.google.common.annotations.VisibleForTesting;
021import com.google.common.base.Preconditions;
022import org.apache.hadoop.hdfs.protocol.Block;
023import org.apache.hadoop.hdfs.protocol.HdfsConstants;
024import org.apache.hadoop.hdfs.server.common.GenerationStamp;
025
026import java.io.IOException;
027
028/**
029 * BlockIdManager allocates the generation stamps and the block ID. The
030 * {@see FSNamesystem} is responsible for persisting the allocations in the
031 * {@see EditLog}.
032 */
033public class BlockIdManager {
034  /**
035   * The global generation stamp for legacy blocks with randomly
036   * generated block IDs.
037   */
038  private final GenerationStamp generationStampV1 = new GenerationStamp();
039  /**
040   * The global generation stamp for this file system.
041   */
042  private final GenerationStamp generationStampV2 = new GenerationStamp();
043  /**
044   * The value of the generation stamp when the first switch to sequential
045   * block IDs was made. Blocks with generation stamps below this value
046   * have randomly allocated block IDs. Blocks with generation stamps above
047   * this value had sequentially allocated block IDs. Read from the fsImage
048   * (or initialized as an offset from the V1 (legacy) generation stamp on
049   * upgrade).
050   */
051  private long generationStampV1Limit;
052  /**
053   * The global block ID space for this file system.
054   */
055  private final SequentialBlockIdGenerator blockIdGenerator;
056
057  public BlockIdManager(BlockManager blockManager) {
058    this.generationStampV1Limit = GenerationStamp.GRANDFATHER_GENERATION_STAMP;
059    this.blockIdGenerator = new SequentialBlockIdGenerator(blockManager);
060  }
061
062  /**
063   * Upgrades the generation stamp for the filesystem
064   * by reserving a sufficient range for all existing blocks.
065   * Should be invoked only during the first upgrade to
066   * sequential block IDs.
067   */
068  public long upgradeGenerationStampToV2() {
069    Preconditions.checkState(generationStampV2.getCurrentValue() ==
070      GenerationStamp.LAST_RESERVED_STAMP);
071    generationStampV2.skipTo(generationStampV1.getCurrentValue() +
072      HdfsConstants.RESERVED_GENERATION_STAMPS_V1);
073
074    generationStampV1Limit = generationStampV2.getCurrentValue();
075    return generationStampV2.getCurrentValue();
076  }
077
078  /**
079   * Sets the generation stamp that delineates random and sequentially
080   * allocated block IDs.
081   *
082   * @param stamp set generation stamp limit to this value
083   */
084  public void setGenerationStampV1Limit(long stamp) {
085    Preconditions.checkState(generationStampV1Limit == GenerationStamp
086      .GRANDFATHER_GENERATION_STAMP);
087    generationStampV1Limit = stamp;
088  }
089
090  /**
091   * Gets the value of the generation stamp that delineates sequential
092   * and random block IDs.
093   */
094  public long getGenerationStampAtblockIdSwitch() {
095    return generationStampV1Limit;
096  }
097
098  @VisibleForTesting
099  SequentialBlockIdGenerator getBlockIdGenerator() {
100    return blockIdGenerator;
101  }
102
103  /**
104   * Sets the maximum allocated block ID for this filesystem. This is
105   * the basis for allocating new block IDs.
106   */
107  public void setLastAllocatedBlockId(long blockId) {
108    blockIdGenerator.skipTo(blockId);
109  }
110
111  /**
112   * Gets the maximum sequentially allocated block ID for this filesystem
113   */
114  public long getLastAllocatedBlockId() {
115    return blockIdGenerator.getCurrentValue();
116  }
117
118  /**
119   * Sets the current generation stamp for legacy blocks
120   */
121  public void setGenerationStampV1(long stamp) {
122    generationStampV1.setCurrentValue(stamp);
123  }
124
125  /**
126   * Gets the current generation stamp for legacy blocks
127   */
128  public long getGenerationStampV1() {
129    return generationStampV1.getCurrentValue();
130  }
131
132  /**
133   * Gets the current generation stamp for this filesystem
134   */
135  public void setGenerationStampV2(long stamp) {
136    generationStampV2.setCurrentValue(stamp);
137  }
138
139  public long getGenerationStampV2() {
140    return generationStampV2.getCurrentValue();
141  }
142
143  /**
144   * Increments, logs and then returns the stamp
145   */
146  public long nextGenerationStamp(boolean legacyBlock) throws IOException {
147    return legacyBlock ? getNextGenerationStampV1() :
148      getNextGenerationStampV2();
149  }
150
151  @VisibleForTesting
152  long getNextGenerationStampV1() throws IOException {
153    long genStampV1 = generationStampV1.nextValue();
154
155    if (genStampV1 >= generationStampV1Limit) {
156      // We ran out of generation stamps for legacy blocks. In practice, it
157      // is extremely unlikely as we reserved 1T v1 generation stamps. The
158      // result is that we can no longer append to the legacy blocks that
159      // were created before the upgrade to sequential block IDs.
160      throw new OutOfV1GenerationStampsException();
161    }
162
163    return genStampV1;
164  }
165
166  @VisibleForTesting
167  long getNextGenerationStampV2() {
168    return generationStampV2.nextValue();
169  }
170
171  public long getGenerationStampV1Limit() {
172    return generationStampV1Limit;
173  }
174
175  /**
176   * Determine whether the block ID was randomly generated (legacy) or
177   * sequentially generated. The generation stamp value is used to
178   * make the distinction.
179   *
180   * @return true if the block ID was randomly generated, false otherwise.
181   */
182  public boolean isLegacyBlock(Block block) {
183    return block.getGenerationStamp() < getGenerationStampV1Limit();
184  }
185
186  /**
187   * Increments, logs and then returns the block ID
188   */
189  public long nextBlockId() {
190    return blockIdGenerator.nextValue();
191  }
192
193  public boolean isGenStampInFuture(Block block) {
194    if (isLegacyBlock(block)) {
195      return block.getGenerationStamp() > getGenerationStampV1();
196    } else {
197      return block.getGenerationStamp() > getGenerationStampV2();
198    }
199  }
200
201  public void clear() {
202    generationStampV1.setCurrentValue(GenerationStamp.LAST_RESERVED_STAMP);
203    generationStampV2.setCurrentValue(GenerationStamp.LAST_RESERVED_STAMP);
204    getBlockIdGenerator().setCurrentValue(SequentialBlockIdGenerator
205      .LAST_RESERVED_BLOCK_ID);
206    generationStampV1Limit = GenerationStamp.GRANDFATHER_GENERATION_STAMP;
207  }
208}