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     */
018    package org.apache.hadoop.hdfs.protocol;
019    
020    import java.util.EnumSet;
021    import java.util.HashMap;
022    import java.util.Map;
023    
024    import 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
045    public 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