View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  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,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.hadoop.hbase.quotas;
18  
19  import java.util.HashMap;
20  import java.util.HashSet;
21  import java.util.Iterator;
22  import java.util.Map;
23  import java.util.Set;
24  import java.util.concurrent.TimeUnit;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.conf.Configuration;
29  import org.apache.hadoop.hbase.HRegionInfo;
30  import org.apache.hadoop.hbase.ScheduledChore;
31  import org.apache.hadoop.hbase.classification.InterfaceAudience;
32  import org.apache.hadoop.hbase.regionserver.HRegion;
33  import org.apache.hadoop.hbase.regionserver.HRegionServer;
34  import org.apache.hadoop.hbase.regionserver.Region;
35  import org.apache.hadoop.hbase.regionserver.Store;
36  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
37  
38  /**
39   * A chore which computes the size of each {@link HRegion} on the FileSystem hosted by the given {@link HRegionServer}.
40   */
41  @InterfaceAudience.Private
42  public class FileSystemUtilizationChore extends ScheduledChore {
43    private static final Log LOG = LogFactory.getLog(FileSystemUtilizationChore.class);
44    static final String FS_UTILIZATION_CHORE_PERIOD_KEY = "hbase.regionserver.quotas.fs.utilization.chore.period";
45    static final int FS_UTILIZATION_CHORE_PERIOD_DEFAULT = 1000 * 60 * 5; // 5 minutes in millis
46  
47    static final String FS_UTILIZATION_CHORE_DELAY_KEY = "hbase.regionserver.quotas.fs.utilization.chore.delay";
48    static final long FS_UTILIZATION_CHORE_DELAY_DEFAULT = 1000L * 60L; // 1 minute
49  
50    static final String FS_UTILIZATION_CHORE_TIMEUNIT_KEY = "hbase.regionserver.quotas.fs.utilization.chore.timeunit";
51    static final String FS_UTILIZATION_CHORE_TIMEUNIT_DEFAULT = TimeUnit.MILLISECONDS.name();
52  
53    static final String FS_UTILIZATION_MAX_ITERATION_DURATION_KEY = "hbase.regionserver.quotas.fs.utilization.chore.max.iteration.millis";
54    static final long FS_UTILIZATION_MAX_ITERATION_DURATION_DEFAULT = 5000L;
55  
56    private int numberOfCyclesToSkip = 0, prevNumberOfCyclesToSkip = 0;
57    private static final int CYCLE_UPPER_BOUND = 32;
58  
59    private final HRegionServer rs;
60    private final long maxIterationMillis;
61    private Iterator<Region> leftoverRegions;
62  
63    public FileSystemUtilizationChore(HRegionServer rs) {
64      super(FileSystemUtilizationChore.class.getSimpleName(), rs, getPeriod(rs.getConfiguration()),
65          getInitialDelay(rs.getConfiguration()), getTimeUnit(rs.getConfiguration()));
66      this.rs = rs;
67      this.maxIterationMillis = rs.getConfiguration().getLong(
68          FS_UTILIZATION_MAX_ITERATION_DURATION_KEY, FS_UTILIZATION_MAX_ITERATION_DURATION_DEFAULT);
69    }
70  
71    @Override
72    protected void chore() {
73      if (numberOfCyclesToSkip > 0) {
74        numberOfCyclesToSkip--;
75        return;
76      }
77      final Map<HRegionInfo,Long> onlineRegionSizes = new HashMap<>();
78      final Set<Region> onlineRegions = new HashSet<>(rs.getOnlineRegions());
79      // Process the regions from the last run if we have any. If we are somehow having difficulty
80      // processing the Regions, we want to avoid creating a backlog in memory of Region objs.
81      Iterator<Region> oldRegionsToProcess = getLeftoverRegions();
82      final Iterator<Region> iterator;
83      final boolean processingLeftovers;
84      if (oldRegionsToProcess == null) {
85        iterator = onlineRegions.iterator();
86        processingLeftovers = false;
87      } else {
88        iterator = oldRegionsToProcess;
89        processingLeftovers = true;
90      }
91      // Reset the leftoverRegions and let the loop re-assign if necessary.
92      setLeftoverRegions(null);
93      long regionSizesCalculated = 0L;
94      long offlineRegionsSkipped = 0L;
95      long skippedSplitParents = 0L;
96      long skippedRegionReplicas = 0L;
97      final long start = EnvironmentEdgeManager.currentTime();
98      while (iterator.hasNext()) {
99        // Make sure this chore doesn't hog the thread.
100       long timeRunning = EnvironmentEdgeManager.currentTime() - start;
101       if (timeRunning > maxIterationMillis) {
102         LOG.debug("Preempting execution of FileSystemUtilizationChore because it exceeds the"
103             + " maximum iteration configuration value. Will process remaining iterators"
104             + " on a subsequent invocation.");
105         setLeftoverRegions(iterator);
106         break;
107       }
108 
109       final Region region = iterator.next();
110       // If we're processing leftover regions, the region may no-longer be online.
111       // If so, we can can skip it.
112       if (processingLeftovers && !onlineRegions.contains(region)) {
113         offlineRegionsSkipped++;
114         continue;
115       }
116       // Avoid computing the size of regions which are the parent of split.
117       if (region.getRegionInfo().isSplitParent()) {
118         skippedSplitParents++;
119         continue;
120       }
121       // Avoid computing the size of region replicas.
122       if (HRegionInfo.DEFAULT_REPLICA_ID != region.getRegionInfo().getReplicaId()) {
123         skippedRegionReplicas++;
124         continue;
125       }
126       final long sizeInBytes = computeSize(region);
127       onlineRegionSizes.put(region.getRegionInfo(), sizeInBytes);
128       regionSizesCalculated++;
129     }
130     if (LOG.isTraceEnabled()) {
131       LOG.trace("Computed the size of " + regionSizesCalculated + " Regions. Skipped computation"
132           + " of " + offlineRegionsSkipped + " regions due to not being online on this RS, "
133           + skippedSplitParents + " regions due to being the parent of a split, and"
134           + skippedRegionReplicas + " regions due to being region replicas.");
135     }
136     if (!reportRegionSizesToMaster(onlineRegionSizes)) {
137       // backoff reporting
138       numberOfCyclesToSkip = prevNumberOfCyclesToSkip > 0 ? 2 * prevNumberOfCyclesToSkip : 1;
139       if (numberOfCyclesToSkip > CYCLE_UPPER_BOUND) {
140         numberOfCyclesToSkip = CYCLE_UPPER_BOUND;
141       }
142       prevNumberOfCyclesToSkip = numberOfCyclesToSkip;
143     }
144   }
145 
146   /**
147    * Returns an {@link Iterator} over the Regions which were skipped last invocation of the chore.
148    *
149    * @return Regions from the previous invocation to process, or null.
150    */
151   Iterator<Region> getLeftoverRegions() {
152     return leftoverRegions;
153   }
154 
155   /**
156    * Sets a new collection of Regions as leftovers.
157    */
158   void setLeftoverRegions(Iterator<Region> newLeftovers) {
159     this.leftoverRegions = newLeftovers;
160   }
161 
162   /**
163    * Computes total FileSystem size for the given {@link Region}.
164    *
165    * @param r The region
166    * @return The size, in bytes, of the Region.
167    */
168   long computeSize(Region r) {
169     long regionSize = 0L;
170     for (Store store : r.getStores()) {
171       // StoreFile/StoreFileReaders are already instantiated with the file length cached.
172       // Can avoid extra NN ops.
173       regionSize += store.getStorefilesSize();
174     }
175     return regionSize;
176   }
177 
178   /**
179    * Reports the computed region sizes to the currently active Master.
180    *
181    * @param onlineRegionSizes The computed region sizes to report.
182    * @return {@code false} if FileSystemUtilizationChore should pause reporting to master,
183    *    {@code true} otherwise.
184    */
185   boolean reportRegionSizesToMaster(Map<HRegionInfo,Long> onlineRegionSizes) {
186     return this.rs.reportRegionSizesForQuotas(onlineRegionSizes);
187   }
188 
189   /**
190    * Extracts the period for the chore from the configuration.
191    *
192    * @param conf The configuration object.
193    * @return The configured chore period or the default value.
194    */
195   static int getPeriod(Configuration conf) {
196     return conf.getInt(FS_UTILIZATION_CHORE_PERIOD_KEY, FS_UTILIZATION_CHORE_PERIOD_DEFAULT);
197   }
198 
199   /**
200    * Extracts the initial delay for the chore from the configuration.
201    *
202    * @param conf The configuration object.
203    * @return The configured chore initial delay or the default value.
204    */
205   static long getInitialDelay(Configuration conf) {
206     return conf.getLong(FS_UTILIZATION_CHORE_DELAY_KEY, FS_UTILIZATION_CHORE_DELAY_DEFAULT);
207   }
208 
209   /**
210    * Extracts the time unit for the chore period and initial delay from the configuration. The
211    * configuration value for {@link #FS_UTILIZATION_CHORE_TIMEUNIT_KEY} must correspond to a
212    * {@link TimeUnit} value.
213    *
214    * @param conf The configuration object.
215    * @return The configured time unit for the chore period and initial delay or the default value.
216    */
217   static TimeUnit getTimeUnit(Configuration conf) {
218     return TimeUnit.valueOf(conf.get(FS_UTILIZATION_CHORE_TIMEUNIT_KEY,
219         FS_UTILIZATION_CHORE_TIMEUNIT_DEFAULT));
220   }
221 }