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 static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertNotNull; 022import static org.mockito.Matchers.any; 023import static org.mockito.Mockito.doAnswer; 024import static org.mockito.Mockito.mock; 025import static org.mockito.Mockito.when; 026 027import java.util.ArrayList; 028import java.util.Arrays; 029import java.util.Collection; 030import java.util.Collections; 031import java.util.Iterator; 032import java.util.List; 033import java.util.Map; 034import java.util.concurrent.TimeUnit; 035import org.apache.hadoop.conf.Configuration; 036import org.apache.hadoop.hbase.HBaseClassTestRule; 037import org.apache.hadoop.hbase.HBaseConfiguration; 038import org.apache.hadoop.hbase.client.RegionInfo; 039import org.apache.hadoop.hbase.regionserver.HRegionServer; 040import org.apache.hadoop.hbase.regionserver.Region; 041import org.apache.hadoop.hbase.regionserver.Store; 042import org.apache.hadoop.hbase.testclassification.SmallTests; 043import org.junit.ClassRule; 044import org.junit.Test; 045import org.junit.experimental.categories.Category; 046import org.mockito.Mockito; 047import org.mockito.invocation.InvocationOnMock; 048import org.mockito.stubbing.Answer; 049 050/** 051 * Test class for {@link FileSystemUtilizationChore}. 052 */ 053@Category(SmallTests.class) 054public class TestFileSystemUtilizationChore { 055 056 @ClassRule 057 public static final HBaseClassTestRule CLASS_RULE = 058 HBaseClassTestRule.forClass(TestFileSystemUtilizationChore.class); 059 060 @Test 061 public void testNoOnlineRegions() { 062 // One region with a store size of one. 063 final List<Long> regionSizes = Collections.emptyList(); 064 final Configuration conf = getDefaultHBaseConfiguration(); 065 final HRegionServer rs = mockRegionServer(conf); 066 final FileSystemUtilizationChore chore = new FileSystemUtilizationChore(rs); 067 doAnswer(new ExpectedRegionSizeSummationAnswer(sum(regionSizes))) 068 .when(rs) 069 .reportRegionSizesForQuotas(any(RegionSizeStore.class)); 070 071 final Region region = mockRegionWithSize(regionSizes); 072 Mockito.doReturn(Arrays.asList(region)).when(rs).getRegions(); 073 chore.chore(); 074 } 075 076 @Test 077 public void testRegionSizes() { 078 // One region with a store size of one. 079 final List<Long> regionSizes = Arrays.asList(1024L); 080 final Configuration conf = getDefaultHBaseConfiguration(); 081 final HRegionServer rs = mockRegionServer(conf); 082 final FileSystemUtilizationChore chore = new FileSystemUtilizationChore(rs); 083 doAnswer(new ExpectedRegionSizeSummationAnswer(sum(regionSizes))) 084 .when(rs) 085 .reportRegionSizesForQuotas(any(RegionSizeStore.class)); 086 087 final Region region = mockRegionWithSize(regionSizes); 088 Mockito.doReturn(Arrays.asList(region)).when(rs).getRegions(); 089 chore.chore(); 090 } 091 092 @Test 093 public void testMultipleRegionSizes() { 094 final Configuration conf = getDefaultHBaseConfiguration(); 095 final HRegionServer rs = mockRegionServer(conf); 096 097 // Three regions with multiple store sizes 098 final List<Long> r1Sizes = Arrays.asList(1024L, 2048L); 099 final long r1Sum = sum(r1Sizes); 100 final List<Long> r2Sizes = Arrays.asList(1024L * 1024L); 101 final long r2Sum = sum(r2Sizes); 102 final List<Long> r3Sizes = Arrays.asList(10L * 1024L * 1024L); 103 final long r3Sum = sum(r3Sizes); 104 105 final FileSystemUtilizationChore chore = new FileSystemUtilizationChore(rs); 106 doAnswer(new ExpectedRegionSizeSummationAnswer(sum(Arrays.asList(r1Sum, r2Sum, r3Sum)))) 107 .when(rs) 108 .reportRegionSizesForQuotas(any(RegionSizeStore.class)); 109 110 final Region r1 = mockRegionWithSize(r1Sizes); 111 final Region r2 = mockRegionWithSize(r2Sizes); 112 final Region r3 = mockRegionWithSize(r3Sizes); 113 Mockito.doReturn(Arrays.asList(r1, r2, r3)).when(rs).getRegions(); 114 chore.chore(); 115 } 116 117 @Test 118 public void testDefaultConfigurationProperties() { 119 final Configuration conf = getDefaultHBaseConfiguration(); 120 final HRegionServer rs = mockRegionServer(conf); 121 final FileSystemUtilizationChore chore = new FileSystemUtilizationChore(rs); 122 // Verify that the expected default values are actually represented. 123 assertEquals( 124 FileSystemUtilizationChore.FS_UTILIZATION_CHORE_PERIOD_DEFAULT, chore.getPeriod()); 125 assertEquals( 126 FileSystemUtilizationChore.FS_UTILIZATION_CHORE_DELAY_DEFAULT, chore.getInitialDelay()); 127 assertEquals( 128 TimeUnit.valueOf(FileSystemUtilizationChore.FS_UTILIZATION_CHORE_TIMEUNIT_DEFAULT), 129 chore.getTimeUnit()); 130 } 131 132 @Test 133 public void testNonDefaultConfigurationProperties() { 134 final Configuration conf = getDefaultHBaseConfiguration(); 135 // Override the default values 136 final int period = 60 * 10; 137 final long delay = 30L; 138 final TimeUnit timeUnit = TimeUnit.SECONDS; 139 conf.setInt(FileSystemUtilizationChore.FS_UTILIZATION_CHORE_PERIOD_KEY, period); 140 conf.setLong(FileSystemUtilizationChore.FS_UTILIZATION_CHORE_DELAY_KEY, delay); 141 conf.set(FileSystemUtilizationChore.FS_UTILIZATION_CHORE_TIMEUNIT_KEY, timeUnit.name()); 142 143 // Verify that the chore reports these non-default values 144 final HRegionServer rs = mockRegionServer(conf); 145 final FileSystemUtilizationChore chore = new FileSystemUtilizationChore(rs); 146 assertEquals(period, chore.getPeriod()); 147 assertEquals(delay, chore.getInitialDelay()); 148 assertEquals(timeUnit, chore.getTimeUnit()); 149 } 150 151 @Test 152 public void testProcessingLeftoverRegions() { 153 final Configuration conf = getDefaultHBaseConfiguration(); 154 final HRegionServer rs = mockRegionServer(conf); 155 156 // Some leftover regions from a previous chore() 157 final List<Long> leftover1Sizes = Arrays.asList(1024L, 4096L); 158 final long leftover1Sum = sum(leftover1Sizes); 159 final List<Long> leftover2Sizes = Arrays.asList(2048L); 160 final long leftover2Sum = sum(leftover2Sizes); 161 162 final Region lr1 = mockRegionWithSize(leftover1Sizes); 163 final Region lr2 = mockRegionWithSize(leftover2Sizes); 164 final FileSystemUtilizationChore chore = new FileSystemUtilizationChore(rs) { 165 @Override 166 Iterator<Region> getLeftoverRegions() { 167 return Arrays.asList(lr1, lr2).iterator(); 168 } 169 }; 170 doAnswer(new ExpectedRegionSizeSummationAnswer(sum(Arrays.asList(leftover1Sum, leftover2Sum)))) 171 .when(rs) 172 .reportRegionSizesForQuotas(any(RegionSizeStore.class)); 173 174 // We shouldn't compute all of these region sizes, just the leftovers 175 final Region r1 = mockRegionWithSize(Arrays.asList(1024L, 2048L)); 176 final Region r2 = mockRegionWithSize(Arrays.asList(1024L * 1024L)); 177 final Region r3 = mockRegionWithSize(Arrays.asList(10L * 1024L * 1024L)); 178 Mockito.doReturn(Arrays.asList(r1, r2, r3, lr1, lr2)).when(rs).getRegions(); 179 180 chore.chore(); 181 } 182 183 @Test 184 public void testProcessingNowOfflineLeftoversAreIgnored() { 185 final Configuration conf = getDefaultHBaseConfiguration(); 186 final HRegionServer rs = mockRegionServer(conf); 187 188 // Some leftover regions from a previous chore() 189 final List<Long> leftover1Sizes = Arrays.asList(1024L, 4096L); 190 final long leftover1Sum = sum(leftover1Sizes); 191 final List<Long> leftover2Sizes = Arrays.asList(2048L); 192 193 final Region lr1 = mockRegionWithSize(leftover1Sizes); 194 final Region lr2 = mockRegionWithSize(leftover2Sizes); 195 final FileSystemUtilizationChore chore = new FileSystemUtilizationChore(rs) { 196 @Override 197 Iterator<Region> getLeftoverRegions() { 198 return Arrays.asList(lr1, lr2).iterator(); 199 } 200 }; 201 doAnswer(new ExpectedRegionSizeSummationAnswer(sum(Arrays.asList(leftover1Sum)))) 202 .when(rs) 203 .reportRegionSizesForQuotas(any(RegionSizeStore.class)); 204 205 // We shouldn't compute all of these region sizes, just the leftovers 206 final Region r1 = mockRegionWithSize(Arrays.asList(1024L, 2048L)); 207 final Region r2 = mockRegionWithSize(Arrays.asList(1024L * 1024L)); 208 final Region r3 = mockRegionWithSize(Arrays.asList(10L * 1024L * 1024L)); 209 // lr2 is no longer online, so it should be ignored 210 Mockito.doReturn(Arrays.asList(r1, r2, r3, lr1)).when(rs).getRegions(); 211 212 chore.chore(); 213 } 214 215 @Test 216 public void testIgnoreSplitParents() { 217 final Configuration conf = getDefaultHBaseConfiguration(); 218 final HRegionServer rs = mockRegionServer(conf); 219 220 // Three regions with multiple store sizes 221 final List<Long> r1Sizes = Arrays.asList(1024L, 2048L); 222 final long r1Sum = sum(r1Sizes); 223 final List<Long> r2Sizes = Arrays.asList(1024L * 1024L); 224 225 final FileSystemUtilizationChore chore = new FileSystemUtilizationChore(rs); 226 doAnswer(new ExpectedRegionSizeSummationAnswer(sum(Arrays.asList(r1Sum)))) 227 .when(rs) 228 .reportRegionSizesForQuotas(any(RegionSizeStore.class)); 229 230 final Region r1 = mockRegionWithSize(r1Sizes); 231 final Region r2 = mockSplitParentRegionWithSize(r2Sizes); 232 Mockito.doReturn(Arrays.asList(r1, r2)).when(rs).getRegions(); 233 chore.chore(); 234 } 235 236 @Test 237 public void testIgnoreRegionReplicas() { 238 final Configuration conf = getDefaultHBaseConfiguration(); 239 final HRegionServer rs = mockRegionServer(conf); 240 241 // Two regions with multiple store sizes 242 final List<Long> r1Sizes = Arrays.asList(1024L, 2048L); 243 final long r1Sum = sum(r1Sizes); 244 final List<Long> r2Sizes = Arrays.asList(1024L * 1024L); 245 246 final FileSystemUtilizationChore chore = new FileSystemUtilizationChore(rs); 247 doAnswer(new ExpectedRegionSizeSummationAnswer(r1Sum)) 248 .when(rs) 249 .reportRegionSizesForQuotas(any(RegionSizeStore.class)); 250 251 final Region r1 = mockRegionWithSize(r1Sizes); 252 final Region r2 = mockRegionReplicaWithSize(r2Sizes); 253 Mockito.doReturn(Arrays.asList(r1, r2)).when(rs).getRegions(); 254 chore.chore(); 255 } 256 257 @Test 258 public void testNonHFilesAreIgnored() { 259 final Configuration conf = getDefaultHBaseConfiguration(); 260 final HRegionServer rs = mockRegionServer(conf); 261 262 // Region r1 has two store files, one hfile link and one hfile 263 final List<Long> r1StoreFileSizes = Arrays.asList(1024L, 2048L); 264 final List<Long> r1HFileSizes = Arrays.asList(0L, 2048L); 265 final long r1HFileSizeSum = sum(r1HFileSizes); 266 // Region r2 has one store file which is a hfile link 267 final List<Long> r2StoreFileSizes = Arrays.asList(1024L * 1024L); 268 final List<Long> r2HFileSizes = Arrays.asList(0L); 269 final long r2HFileSizeSum = sum(r2HFileSizes); 270 271 // We expect that only the hfiles would be counted (hfile links are ignored) 272 final FileSystemUtilizationChore chore = new FileSystemUtilizationChore(rs); 273 doAnswer(new ExpectedRegionSizeSummationAnswer( 274 sum(Arrays.asList(r1HFileSizeSum, r2HFileSizeSum)))) 275 .when(rs).reportRegionSizesForQuotas(any(RegionSizeStore.class)); 276 277 final Region r1 = mockRegionWithHFileLinks(r1StoreFileSizes, r1HFileSizes); 278 final Region r2 = mockRegionWithHFileLinks(r2StoreFileSizes, r2HFileSizes); 279 Mockito.doReturn(Arrays.asList(r1, r2)).when(rs).getRegions(); 280 chore.chore(); 281 } 282 283 /** 284 * Creates an HBase Configuration object for the default values. 285 */ 286 private Configuration getDefaultHBaseConfiguration() { 287 final Configuration conf = HBaseConfiguration.create(); 288 conf.addResource("hbase-default.xml"); 289 return conf; 290 } 291 292 /** 293 * Creates an HRegionServer using the given Configuration. 294 */ 295 private HRegionServer mockRegionServer(Configuration conf) { 296 final HRegionServer rs = mock(HRegionServer.class); 297 final RegionServerSpaceQuotaManager quotaManager = mock(RegionServerSpaceQuotaManager.class); 298 when(rs.getConfiguration()).thenReturn(conf); 299 when(rs.getRegionServerSpaceQuotaManager()).thenReturn(quotaManager); 300 when(quotaManager.getRegionSizeStore()).thenReturn(new RegionSizeStoreImpl()); 301 return rs; 302 } 303 304 /** 305 * Sums the collection of non-null numbers. 306 */ 307 private long sum(Collection<Long> values) { 308 long sum = 0L; 309 for (Long value : values) { 310 assertNotNull(value); 311 sum += value; 312 } 313 return sum; 314 } 315 316 /** 317 * Creates a region with a number of Stores equal to the length of {@code storeSizes}. Each 318 * {@link Store} will have a reported size corresponding to the element in {@code storeSizes}. 319 * 320 * @param storeSizes A list of sizes for each Store. 321 * @return A mocked Region. 322 */ 323 private Region mockRegionWithSize(Collection<Long> storeSizes) { 324 final Region r = mock(Region.class); 325 final RegionInfo info = mock(RegionInfo.class); 326 when(r.getRegionInfo()).thenReturn(info); 327 List<Store> stores = new ArrayList<>(); 328 when(r.getStores()).thenReturn((List) stores); 329 for (Long storeSize : storeSizes) { 330 final Store s = mock(Store.class); 331 stores.add(s); 332 when(s.getHFilesSize()).thenReturn(storeSize); 333 } 334 return r; 335 } 336 337 private Region mockRegionWithHFileLinks(Collection<Long> storeSizes, Collection<Long> hfileSizes) { 338 final Region r = mock(Region.class); 339 final RegionInfo info = mock(RegionInfo.class); 340 when(r.getRegionInfo()).thenReturn(info); 341 List<Store> stores = new ArrayList<>(); 342 when(r.getStores()).thenReturn((List) stores); 343 assertEquals( 344 "Logic error, storeSizes and linkSizes must be equal in size", storeSizes.size(), 345 hfileSizes.size()); 346 Iterator<Long> storeSizeIter = storeSizes.iterator(); 347 Iterator<Long> hfileSizeIter = hfileSizes.iterator(); 348 while (storeSizeIter.hasNext() && hfileSizeIter.hasNext()) { 349 final long storeSize = storeSizeIter.next(); 350 final long hfileSize = hfileSizeIter.next(); 351 final Store s = mock(Store.class); 352 stores.add(s); 353 when(s.getStorefilesSize()).thenReturn(storeSize); 354 when(s.getHFilesSize()).thenReturn(hfileSize); 355 } 356 return r; 357 } 358 359 /** 360 * Creates a region which is the parent of a split. 361 * 362 * @param storeSizes A list of sizes for each Store. 363 * @return A mocked Region. 364 */ 365 private Region mockSplitParentRegionWithSize(Collection<Long> storeSizes) { 366 final Region r = mockRegionWithSize(storeSizes); 367 final RegionInfo info = r.getRegionInfo(); 368 when(info.isSplitParent()).thenReturn(true); 369 return r; 370 } 371 372 /** 373 * Creates a region who has a replicaId of <code>1</code>. 374 * 375 * @param storeSizes A list of sizes for each Store. 376 * @return A mocked Region. 377 */ 378 private Region mockRegionReplicaWithSize(Collection<Long> storeSizes) { 379 final Region r = mockRegionWithSize(storeSizes); 380 final RegionInfo info = r.getRegionInfo(); 381 when(info.getReplicaId()).thenReturn(1); 382 return r; 383 } 384 385 /** 386 * An Answer implementation which verifies the sum of the Region sizes to report is as expected. 387 */ 388 private static class ExpectedRegionSizeSummationAnswer implements Answer<Void> { 389 private final long expectedSize; 390 391 public ExpectedRegionSizeSummationAnswer(long expectedSize) { 392 this.expectedSize = expectedSize; 393 } 394 395 @Override 396 public Void answer(InvocationOnMock invocation) throws Throwable { 397 Object[] args = invocation.getArguments(); 398 assertEquals(1, args.length); 399 @SuppressWarnings("unchecked") 400 Map<RegionInfo,Long> regionSizes = (Map<RegionInfo,Long>) args[0]; 401 long sum = 0L; 402 for (Long regionSize : regionSizes.values()) { 403 sum += regionSize; 404 } 405 assertEquals(expectedSize, sum); 406 return null; 407 } 408 } 409}