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