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.protocol;
019
020import java.util.EnumSet;
021import java.util.HashMap;
022import java.util.Map;
023
024import org.apache.hadoop.classification.InterfaceAudience;
025
026/**
027 * This class tracks changes in the layout version of HDFS.
028 * 
029 * Layout version is changed for following reasons:
030 * <ol>
031 * <li>The layout of how namenode or datanode stores information 
032 * on disk changes.</li>
033 * <li>A new operation code is added to the editlog.</li>
034 * <li>Modification such as format of a record, content of a record 
035 * in editlog or fsimage.</li>
036 * </ol>
037 * <br>
038 * <b>How to update layout version:<br></b>
039 * When a change requires new layout version, please add an entry into
040 * {@link Feature} with a short enum name, new layout version and description
041 * of the change. Please see {@link Feature} for further details.
042 * <br>
043 */
044@InterfaceAudience.Private
045public class LayoutVersion {
046 
047  /**
048   * Version in which HDFS-2991 was fixed. This bug caused OP_ADD to
049   * sometimes be skipped for append() calls. If we see such a case when
050   * loading the edits, but the version is known to have that bug, we
051   * workaround the issue. Otherwise we should consider it a corruption
052   * and bail.
053   */
054  public static final int BUGFIX_HDFS_2991_VERSION = -40;
055
056  /**
057   * Enums for features that change the layout version.
058   * <br><br>
059   * To add a new layout version:
060   * <ul>
061   * <li>Define a new enum constant with a short enum name, the new layout version 
062   * and description of the added feature.</li>
063   * <li>When adding a layout version with an ancestor that is not same as
064   * its immediate predecessor, use the constructor where a spacific ancestor
065   * can be passed.
066   * </li>
067   * </ul>
068   */
069  public static enum Feature {
070    NAMESPACE_QUOTA(-16, "Support for namespace quotas"),
071    FILE_ACCESS_TIME(-17, "Support for access time on files"),
072    DISKSPACE_QUOTA(-18, "Support for disk space quotas"),
073    STICKY_BIT(-19, "Support for sticky bits"),
074    APPEND_RBW_DIR(-20, "Datanode has \"rbw\" subdirectory for append"),
075    ATOMIC_RENAME(-21, "Support for atomic rename"),
076    CONCAT(-22, "Support for concat operation"),
077    SYMLINKS(-23, "Support for symbolic links"),
078    DELEGATION_TOKEN(-24, "Support for delegation tokens for security"),
079    FSIMAGE_COMPRESSION(-25, "Support for fsimage compression"),
080    FSIMAGE_CHECKSUM(-26, "Support checksum for fsimage"),
081    REMOVE_REL13_DISK_LAYOUT_SUPPORT(-27, "Remove support for 0.13 disk layout"),
082    EDITS_CHESKUM(-28, "Support checksum for editlog"),
083    UNUSED(-29, "Skipped version"),
084    FSIMAGE_NAME_OPTIMIZATION(-30, "Store only last part of path in fsimage"),
085    RESERVED_REL20_203(-31, -19, "Reserved for release 0.20.203", true,
086        DELEGATION_TOKEN),
087    RESERVED_REL20_204(-32, -31, "Reserved for release 0.20.204", true),
088    RESERVED_REL22(-33, -27, "Reserved for release 0.22", true),
089    RESERVED_REL23(-34, -30, "Reserved for release 0.23", true),
090    FEDERATION(-35, "Support for namenode federation"),
091    LEASE_REASSIGNMENT(-36, "Support for persisting lease holder reassignment"),
092    STORED_TXIDS(-37, "Transaction IDs are stored in edits log and image files"),
093    TXID_BASED_LAYOUT(-38, "File names in NN Storage are based on transaction IDs"), 
094    EDITLOG_OP_OPTIMIZATION(-39,
095        "Use LongWritable and ShortWritable directly instead of ArrayWritable of UTF8"),
096    OPTIMIZE_PERSIST_BLOCKS(-40,
097        "Serialize block lists with delta-encoded variable length ints, " +
098        "add OP_UPDATE_BLOCKS"),
099    RESERVED_REL1_2_0(-41, -32, "Reserved for release 1.2.0", true, CONCAT),
100    ADD_INODE_ID(-42, -40, "Assign a unique inode id for each inode", false),
101    SNAPSHOT(-43, "Support for snapshot feature"),
102    RESERVED_REL1_3_0(-44, -41,
103        "Reserved for release 1.3.0", true, ADD_INODE_ID, SNAPSHOT),
104    OPTIMIZE_SNAPSHOT_INODES(-45, -43,
105        "Reduce snapshot inode memory footprint", false),
106    SEQUENTIAL_BLOCK_ID(-46, "Allocate block IDs sequentially and store " +
107        "block IDs in the edits log and image files"),
108    EDITLOG_SUPPORT_RETRYCACHE(-47, "Record ClientId and CallId in editlog to " 
109        + "enable rebuilding retry cache in case of HA failover"),
110    EDITLOG_ADD_BLOCK(-48, "Add new editlog that only records allocation of "
111        + "the new block instead of the entire block list"),
112    ADD_DATANODE_AND_STORAGE_UUIDS(-49, "Replace StorageID with DatanodeUuid."
113        + " Use distinct StorageUuid per storage directory."),
114    ADD_LAYOUT_FLAGS(-50, "Add support for layout flags."),
115    CACHING(-51, "Support for cache pools and path-based caching");
116
117    final int lv;
118    final int ancestorLV;
119    final String description;
120    final boolean reserved;
121    final Feature[] specialFeatures;
122    
123    /**
124     * Feature that is added at layout version {@code lv} - 1. 
125     * @param lv new layout version with the addition of this feature
126     * @param description description of the feature
127     */
128    Feature(final int lv, final String description) {
129      this(lv, lv + 1, description, false);
130    }
131
132    /**
133     * Feature that is added at layout version {@code ancestoryLV}.
134     * @param lv new layout version with the addition of this feature
135     * @param ancestorLV layout version from which the new lv is derived from.
136     * @param description description of the feature
137     * @param reserved true when this is a layout version reserved for previous
138     *          verions
139     * @param features set of features that are to be enabled for this version
140     */
141    Feature(final int lv, final int ancestorLV, final String description,
142        boolean reserved, Feature... features) {
143      this.lv = lv;
144      this.ancestorLV = ancestorLV;
145      this.description = description;
146      this.reserved = reserved;
147      specialFeatures = features;
148    }
149    
150    /** 
151     * Accessor method for feature layout version 
152     * @return int lv value
153     */
154    public int getLayoutVersion() {
155      return lv;
156    }
157
158    /** 
159     * Accessor method for feature ancestor layout version 
160     * @return int ancestor LV value
161     */
162    public int getAncestorLayoutVersion() {
163      return ancestorLV;
164    }
165
166    /** 
167     * Accessor method for feature description 
168     * @return String feature description 
169     */
170    public String getDescription() {
171      return description;
172    }
173    
174    public boolean isReservedForOldRelease() {
175      return reserved;
176    }
177  }
178  
179  // Build layout version and corresponding feature matrix
180  static final Map<Integer, EnumSet<Feature>>map = 
181    new HashMap<Integer, EnumSet<Feature>>();
182  
183  // Static initialization 
184  static {
185    initMap();
186  }
187  
188  /**
189   * Initialize the map of a layout version and EnumSet of {@link Feature}s 
190   * supported.
191   */
192  private static void initMap() {
193    // Go through all the enum constants and build a map of
194    // LayoutVersion <-> EnumSet of all supported features in that LayoutVersion
195    for (Feature f : Feature.values()) {
196      EnumSet<Feature> ancestorSet = map.get(f.ancestorLV);
197      if (ancestorSet == null) {
198        ancestorSet = EnumSet.noneOf(Feature.class); // Empty enum set
199        map.put(f.ancestorLV, ancestorSet);
200      }
201      EnumSet<Feature> featureSet = EnumSet.copyOf(ancestorSet);
202      if (f.specialFeatures != null) {
203        for (Feature specialFeature : f.specialFeatures) {
204          featureSet.add(specialFeature);
205        }
206      }
207      featureSet.add(f);
208      map.put(f.lv, featureSet);
209    }
210  }
211  
212  /**
213   * Gets formatted string that describes {@link LayoutVersion} information.
214   */
215  public static String getString() {
216    final StringBuilder buf = new StringBuilder();
217    buf.append("Feature List:\n");
218    for (Feature f : Feature.values()) {
219      buf.append(f).append(" introduced in layout version ")
220          .append(f.lv).append(" (").
221      append(f.description).append(")\n");
222    }
223    
224    buf.append("\n\nLayoutVersion and supported features:\n");
225    for (Feature f : Feature.values()) {
226      buf.append(f.lv).append(": ").append(map.get(f.lv))
227          .append("\n");
228    }
229    return buf.toString();
230  }
231  
232  /**
233   * Returns true if a given feature is supported in the given layout version
234   * @param f Feature
235   * @param lv LayoutVersion
236   * @return true if {@code f} is supported in layout version {@code lv}
237   */
238  public static boolean supports(final Feature f, final int lv) {
239    final EnumSet<Feature> set =  map.get(lv);
240    return set != null && set.contains(f);
241  }
242  
243  /**
244   * Get the current layout version
245   */
246  public static int getCurrentLayoutVersion() {
247    Feature[] values = Feature.values();
248    for (int i = values.length -1; i >= 0; i--) {
249      if (!values[i].isReservedForOldRelease()) {
250        return values[i].lv;
251      }
252    }
253    throw new AssertionError("All layout versions are reserved.");
254  }
255}
256