1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with this 4 * work for additional information regarding copyright ownership. The ASF 5 * licenses this file to you under the Apache License, Version 2.0 (the 6 * "License"); you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 * License for the specific language governing permissions and limitations under 15 * the License. 16 */ 17 package org.apache.hadoop.hdfs.server.namenode.startupprogress; 18 19 import java.util.EnumSet; 20 import java.util.HashMap; 21 import java.util.Map; 22 import java.util.TreeSet; 23 24 import org.apache.hadoop.classification.InterfaceAudience; 25 import org.apache.hadoop.util.Time; 26 27 /** 28 * StartupProgressView is an immutable, consistent, read-only view of namenode 29 * startup progress. Callers obtain an instance by calling 30 * {@link StartupProgress#createView()} to clone current startup progress state. 31 * Subsequent updates to startup progress will not alter the view. This isolates 32 * the reader from ongoing updates and establishes a guarantee that the values 33 * returned by the view are consistent and unchanging across multiple related 34 * read operations. Calculations that require aggregation, such as overall 35 * percent complete, will not be impacted by mutations performed in other threads 36 * mid-way through the calculation. 37 * 38 * Methods that return primitive long may return {@link Long#MIN_VALUE} as a 39 * sentinel value to indicate that the property is undefined. 40 */ 41 @InterfaceAudience.Private 42 public class StartupProgressView { 43 44 private final Map<Phase, PhaseTracking> phases; 45 46 /** 47 * Returns the sum of the counter values for all steps in the specified phase. 48 * 49 * @param phase Phase to get 50 * @return long sum of counter values for all steps 51 */ getCount(Phase phase)52 public long getCount(Phase phase) { 53 long sum = 0; 54 for (Step step: getSteps(phase)) { 55 sum += getCount(phase, step); 56 } 57 return sum; 58 } 59 60 /** 61 * Returns the counter value for the specified phase and step. 62 * 63 * @param phase Phase to get 64 * @param step Step to get 65 * @return long counter value for phase and step 66 */ getCount(Phase phase, Step step)67 public long getCount(Phase phase, Step step) { 68 StepTracking tracking = getStepTracking(phase, step); 69 return tracking != null ? tracking.count.get() : 0; 70 } 71 72 /** 73 * Returns overall elapsed time, calculated as time between start of loading 74 * fsimage and end of safemode. 75 * 76 * @return long elapsed time 77 */ getElapsedTime()78 public long getElapsedTime() { 79 return getElapsedTime(phases.get(Phase.LOADING_FSIMAGE), 80 phases.get(Phase.SAFEMODE)); 81 } 82 83 /** 84 * Returns elapsed time for the specified phase, calculated as (end - begin) if 85 * phase is complete or (now - begin) if phase is running or 0 if the phase is 86 * still pending. 87 * 88 * @param phase Phase to get 89 * @return long elapsed time 90 */ getElapsedTime(Phase phase)91 public long getElapsedTime(Phase phase) { 92 return getElapsedTime(phases.get(phase)); 93 } 94 95 /** 96 * Returns elapsed time for the specified phase and step, calculated as 97 * (end - begin) if step is complete or (now - begin) if step is running or 0 98 * if the step is still pending. 99 * 100 * @param phase Phase to get 101 * @param step Step to get 102 * @return long elapsed time 103 */ getElapsedTime(Phase phase, Step step)104 public long getElapsedTime(Phase phase, Step step) { 105 return getElapsedTime(getStepTracking(phase, step)); 106 } 107 108 /** 109 * Returns the optional file name associated with the specified phase, possibly 110 * null. 111 * 112 * @param phase Phase to get 113 * @return String optional file name, possibly null 114 */ getFile(Phase phase)115 public String getFile(Phase phase) { 116 return phases.get(phase).file; 117 } 118 119 /** 120 * Returns overall percent complete, calculated by aggregating percent complete 121 * of all phases. This is an approximation that assumes all phases have equal 122 * running time. In practice, this isn't true, but there isn't sufficient 123 * information available to predict proportional weights for each phase. 124 * 125 * @return float percent complete 126 */ getPercentComplete()127 public float getPercentComplete() { 128 if (getStatus(Phase.SAFEMODE) == Status.COMPLETE) { 129 return 1.0f; 130 } else { 131 float total = 0.0f; 132 int numPhases = 0; 133 for (Phase phase: phases.keySet()) { 134 ++numPhases; 135 total += getPercentComplete(phase); 136 } 137 return getBoundedPercent(total / numPhases); 138 } 139 } 140 141 /** 142 * Returns percent complete for the specified phase, calculated by aggregating 143 * the counter values and totals for all steps within the phase. 144 * 145 * @param phase Phase to get 146 * @return float percent complete 147 */ getPercentComplete(Phase phase)148 public float getPercentComplete(Phase phase) { 149 if (getStatus(phase) == Status.COMPLETE) { 150 return 1.0f; 151 } else { 152 long total = getTotal(phase); 153 long count = 0; 154 for (Step step: getSteps(phase)) { 155 count += getCount(phase, step); 156 } 157 return total > 0 ? getBoundedPercent(1.0f * count / total) : 0.0f; 158 } 159 } 160 161 /** 162 * Returns percent complete for the specified phase and step, calculated as 163 * counter value divided by total. 164 * 165 * @param phase Phase to get 166 * @param step Step to get 167 * @return float percent complete 168 */ getPercentComplete(Phase phase, Step step)169 public float getPercentComplete(Phase phase, Step step) { 170 if (getStatus(phase) == Status.COMPLETE) { 171 return 1.0f; 172 } else { 173 long total = getTotal(phase, step); 174 long count = getCount(phase, step); 175 return total > 0 ? getBoundedPercent(1.0f * count / total) : 0.0f; 176 } 177 } 178 179 /** 180 * Returns all phases. 181 * 182 * @return Iterable<Phase> containing all phases 183 */ getPhases()184 public Iterable<Phase> getPhases() { 185 return EnumSet.allOf(Phase.class); 186 } 187 188 /** 189 * Returns all steps within a phase. 190 * 191 * @param phase Phase to get 192 * @return Iterable<Step> all steps 193 */ getSteps(Phase phase)194 public Iterable<Step> getSteps(Phase phase) { 195 return new TreeSet<Step>(phases.get(phase).steps.keySet()); 196 } 197 198 /** 199 * Returns the optional size in bytes associated with the specified phase, 200 * possibly Long.MIN_VALUE if undefined. 201 * 202 * @param phase Phase to get 203 * @return long optional size in bytes, possibly Long.MIN_VALUE 204 */ getSize(Phase phase)205 public long getSize(Phase phase) { 206 return phases.get(phase).size; 207 } 208 209 /** 210 * Returns the current run status of the specified phase. 211 * 212 * @param phase Phase to get 213 * @return Status run status of phase 214 */ getStatus(Phase phase)215 public Status getStatus(Phase phase) { 216 PhaseTracking tracking = phases.get(phase); 217 if (tracking.beginTime == Long.MIN_VALUE) { 218 return Status.PENDING; 219 } else if (tracking.endTime == Long.MIN_VALUE) { 220 return Status.RUNNING; 221 } else { 222 return Status.COMPLETE; 223 } 224 } 225 226 /** 227 * Returns the sum of the totals for all steps in the specified phase. 228 * 229 * @param phase Phase to get 230 * @return long sum of totals for all steps 231 */ getTotal(Phase phase)232 public long getTotal(Phase phase) { 233 long sum = 0; 234 for (StepTracking tracking: phases.get(phase).steps.values()) { 235 if (tracking.total != Long.MIN_VALUE) { 236 sum += tracking.total; 237 } 238 } 239 return sum; 240 } 241 242 /** 243 * Returns the total for the specified phase and step. 244 * 245 * @param phase Phase to get 246 * @param step Step to get 247 * @return long total 248 */ getTotal(Phase phase, Step step)249 public long getTotal(Phase phase, Step step) { 250 StepTracking tracking = getStepTracking(phase, step); 251 return tracking != null && tracking.total != Long.MIN_VALUE ? 252 tracking.total : 0; 253 } 254 255 /** 256 * Creates a new StartupProgressView by cloning data from the specified 257 * StartupProgress. 258 * 259 * @param prog StartupProgress to clone 260 */ StartupProgressView(StartupProgress prog)261 StartupProgressView(StartupProgress prog) { 262 phases = new HashMap<Phase, PhaseTracking>(); 263 for (Map.Entry<Phase, PhaseTracking> entry: prog.phases.entrySet()) { 264 phases.put(entry.getKey(), entry.getValue().clone()); 265 } 266 } 267 268 /** 269 * Returns elapsed time, calculated as (end - begin) if both are defined or 270 * (now - begin) if end is undefined or 0 if both are undefined. Begin and end 271 * time come from the same AbstractTracking instance. 272 * 273 * @param tracking AbstractTracking containing begin and end time 274 * @return long elapsed time 275 */ getElapsedTime(AbstractTracking tracking)276 private long getElapsedTime(AbstractTracking tracking) { 277 return getElapsedTime(tracking, tracking); 278 } 279 280 /** 281 * Returns elapsed time, calculated as (end - begin) if both are defined or 282 * (now - begin) if end is undefined or 0 if both are undefined. Begin and end 283 * time may come from different AbstractTracking instances. 284 * 285 * @param beginTracking AbstractTracking containing begin time 286 * @param endTracking AbstractTracking containing end time 287 * @return long elapsed time 288 */ getElapsedTime(AbstractTracking beginTracking, AbstractTracking endTracking)289 private long getElapsedTime(AbstractTracking beginTracking, 290 AbstractTracking endTracking) { 291 final long elapsed; 292 if (beginTracking != null && beginTracking.beginTime != Long.MIN_VALUE && 293 endTracking != null && endTracking.endTime != Long.MIN_VALUE) { 294 elapsed = endTracking.endTime - beginTracking.beginTime; 295 } else if (beginTracking != null && 296 beginTracking.beginTime != Long.MIN_VALUE) { 297 elapsed = Time.monotonicNow() - beginTracking.beginTime; 298 } else { 299 elapsed = 0; 300 } 301 return Math.max(0, elapsed); 302 } 303 304 /** 305 * Returns the StepTracking internal data structure for the specified phase 306 * and step, possibly null if not found. 307 * 308 * @param phase Phase to get 309 * @param step Step to get 310 * @return StepTracking for phase and step, possibly null 311 */ getStepTracking(Phase phase, Step step)312 private StepTracking getStepTracking(Phase phase, Step step) { 313 PhaseTracking phaseTracking = phases.get(phase); 314 Map<Step, StepTracking> steps = phaseTracking != null ? 315 phaseTracking.steps : null; 316 return steps != null ? steps.get(step) : null; 317 } 318 319 /** 320 * Returns the given value restricted to the range [0.0, 1.0]. 321 * 322 * @param percent float value to restrict 323 * @return float value restricted to range [0.0, 1.0] 324 */ getBoundedPercent(float percent)325 private static float getBoundedPercent(float percent) { 326 return Math.max(0.0f, Math.min(1.0f, percent)); 327 } 328 } 329