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