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.hbase.quotas; 019 020import java.util.HashSet; 021import java.util.Iterator; 022import java.util.Set; 023import java.util.concurrent.TimeUnit; 024import org.apache.hadoop.conf.Configuration; 025import org.apache.hadoop.hbase.ScheduledChore; 026import org.apache.hadoop.hbase.client.RegionInfo; 027import org.apache.hadoop.hbase.regionserver.HRegion; 028import org.apache.hadoop.hbase.regionserver.HRegionServer; 029import org.apache.hadoop.hbase.regionserver.Region; 030import org.apache.hadoop.hbase.regionserver.Store; 031import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 032import org.apache.yetus.audience.InterfaceAudience; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036/** 037 * A chore which computes the size of each {@link HRegion} on the FileSystem hosted by the given 038 * {@link HRegionServer}. The results of this computation are stored in the 039 * {@link RegionServerSpaceQuotaManager}'s {@link RegionSizeStore} object. 040 */ 041@InterfaceAudience.Private 042public class FileSystemUtilizationChore extends ScheduledChore { 043 private static final Logger LOG = LoggerFactory.getLogger(FileSystemUtilizationChore.class); 044 static final String FS_UTILIZATION_CHORE_PERIOD_KEY = 045 "hbase.regionserver.quotas.fs.utilization.chore.period"; 046 static final int FS_UTILIZATION_CHORE_PERIOD_DEFAULT = 1000 * 60 * 5; // 5 minutes in millis 047 048 static final String FS_UTILIZATION_CHORE_DELAY_KEY = 049 "hbase.regionserver.quotas.fs.utilization.chore.delay"; 050 static final long FS_UTILIZATION_CHORE_DELAY_DEFAULT = 1000L * 60L; // 1 minute 051 052 static final String FS_UTILIZATION_CHORE_TIMEUNIT_KEY = 053 "hbase.regionserver.quotas.fs.utilization.chore.timeunit"; 054 static final String FS_UTILIZATION_CHORE_TIMEUNIT_DEFAULT = TimeUnit.MILLISECONDS.name(); 055 056 static final String FS_UTILIZATION_MAX_ITERATION_DURATION_KEY = 057 "hbase.regionserver.quotas.fs.utilization.chore.max.iteration.millis"; 058 static final long FS_UTILIZATION_MAX_ITERATION_DURATION_DEFAULT = 5000L; 059 060 private final HRegionServer rs; 061 private final long maxIterationMillis; 062 private Iterator<Region> leftoverRegions; 063 064 public FileSystemUtilizationChore(HRegionServer rs) { 065 super(FileSystemUtilizationChore.class.getSimpleName(), rs, getPeriod(rs.getConfiguration()), 066 getInitialDelay(rs.getConfiguration()), getTimeUnit(rs.getConfiguration())); 067 this.rs = rs; 068 this.maxIterationMillis = rs.getConfiguration().getLong( 069 FS_UTILIZATION_MAX_ITERATION_DURATION_KEY, FS_UTILIZATION_MAX_ITERATION_DURATION_DEFAULT); 070 } 071 072 @Override 073 protected void chore() { 074 final RegionSizeStore regionSizeStore = getRegionSizeStore(); 075 final Set<Region> onlineRegions = new HashSet<>(rs.getRegions()); 076 // Process the regions from the last run if we have any. If we are somehow having difficulty 077 // processing the Regions, we want to avoid creating a backlog in memory of Region objs. 078 Iterator<Region> oldRegionsToProcess = getLeftoverRegions(); 079 final Iterator<Region> iterator; 080 final boolean processingLeftovers; 081 if (oldRegionsToProcess == null) { 082 iterator = onlineRegions.iterator(); 083 processingLeftovers = false; 084 } else { 085 iterator = oldRegionsToProcess; 086 processingLeftovers = true; 087 } 088 // Reset the leftoverRegions and let the loop re-assign if necessary. 089 setLeftoverRegions(null); 090 long regionSizesCalculated = 0L; 091 long offlineRegionsSkipped = 0L; 092 long skippedSplitParents = 0L; 093 long skippedRegionReplicas = 0L; 094 final long start = EnvironmentEdgeManager.currentTime(); 095 while (iterator.hasNext()) { 096 // Make sure this chore doesn't hog the thread. 097 long timeRunning = EnvironmentEdgeManager.currentTime() - start; 098 if (timeRunning > maxIterationMillis) { 099 LOG.debug("Preempting execution of FileSystemUtilizationChore because it exceeds the" 100 + " maximum iteration configuration value. Will process remaining Regions" 101 + " on a subsequent invocation."); 102 setLeftoverRegions(iterator); 103 break; 104 } 105 106 final Region region = iterator.next(); 107 // If we're processing leftover regions, the region may no-longer be online. 108 // If so, we can skip it. 109 if (processingLeftovers && !onlineRegions.contains(region)) { 110 offlineRegionsSkipped++; 111 continue; 112 } 113 // Avoid computing the size of regions which are the parent of split. 114 if (region.getRegionInfo().isSplitParent()) { 115 skippedSplitParents++; 116 continue; 117 } 118 // Avoid computing the size of region replicas. 119 if (RegionInfo.DEFAULT_REPLICA_ID != region.getRegionInfo().getReplicaId()) { 120 skippedRegionReplicas++; 121 continue; 122 } 123 final long sizeInBytes = computeSize(region); 124 regionSizeStore.put(region.getRegionInfo(), sizeInBytes); 125 regionSizesCalculated++; 126 } 127 if (LOG.isTraceEnabled()) { 128 LOG.trace("Computed the size of " + regionSizesCalculated + " Regions. Skipped computation" 129 + " of " + offlineRegionsSkipped + " regions due to not being online on this RS, " 130 + skippedSplitParents + " regions due to being the parent of a split, and" 131 + skippedRegionReplicas + " regions due to being region replicas."); 132 } 133 } 134 135 /** 136 * Returns an {@link Iterator} over the Regions which were skipped last invocation of the chore. 137 * @return Regions from the previous invocation to process, or null. 138 */ 139 Iterator<Region> getLeftoverRegions() { 140 return leftoverRegions; 141 } 142 143 /** 144 * Sets a new collection of Regions as leftovers. 145 */ 146 void setLeftoverRegions(Iterator<Region> newLeftovers) { 147 this.leftoverRegions = newLeftovers; 148 } 149 150 /** 151 * Computes total FileSystem size for the given {@link Region}. 152 * @param r The region 153 * @return The size, in bytes, of the Region. 154 */ 155 long computeSize(Region r) { 156 long regionSize = 0L; 157 for (Store store : r.getStores()) { 158 regionSize += store.getHFilesSize(); 159 } 160 if (LOG.isTraceEnabled()) { 161 LOG.trace("Size of " + r + " is " + regionSize); 162 } 163 return regionSize; 164 } 165 166 // visible for testing 167 RegionSizeStore getRegionSizeStore() { 168 return rs.getRegionServerSpaceQuotaManager().getRegionSizeStore(); 169 } 170 171 /** 172 * Extracts the period for the chore from the configuration. 173 * @param conf The configuration object. 174 * @return The configured chore period or the default value. 175 */ 176 static int getPeriod(Configuration conf) { 177 return conf.getInt(FS_UTILIZATION_CHORE_PERIOD_KEY, FS_UTILIZATION_CHORE_PERIOD_DEFAULT); 178 } 179 180 /** 181 * Extracts the initial delay for the chore from the configuration. 182 * @param conf The configuration object. 183 * @return The configured chore initial delay or the default value. 184 */ 185 static long getInitialDelay(Configuration conf) { 186 return conf.getLong(FS_UTILIZATION_CHORE_DELAY_KEY, FS_UTILIZATION_CHORE_DELAY_DEFAULT); 187 } 188 189 /** 190 * Extracts the time unit for the chore period and initial delay from the configuration. The 191 * configuration value for {@link #FS_UTILIZATION_CHORE_TIMEUNIT_KEY} must correspond to a 192 * {@link TimeUnit} value. 193 * @param conf The configuration object. 194 * @return The configured time unit for the chore period and initial delay or the default value. 195 */ 196 static TimeUnit getTimeUnit(Configuration conf) { 197 return TimeUnit 198 .valueOf(conf.get(FS_UTILIZATION_CHORE_TIMEUNIT_KEY, FS_UTILIZATION_CHORE_TIMEUNIT_DEFAULT)); 199 } 200}