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.regionserver; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNull; 023import static org.junit.Assert.assertTrue; 024import static org.mockito.Mockito.doReturn; 025import static org.mockito.Mockito.mock; 026import static org.mockito.Mockito.when; 027 028import java.io.IOException; 029import java.util.ArrayList; 030import java.util.List; 031import java.util.Optional; 032import org.apache.hadoop.conf.Configuration; 033import org.apache.hadoop.hbase.HBaseClassTestRule; 034import org.apache.hadoop.hbase.HBaseConfiguration; 035import org.apache.hadoop.hbase.HConstants; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.client.RegionInfo; 038import org.apache.hadoop.hbase.client.RegionInfoBuilder; 039import org.apache.hadoop.hbase.client.TableDescriptor; 040import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 041import org.apache.hadoop.hbase.testclassification.RegionServerTests; 042import org.apache.hadoop.hbase.testclassification.SmallTests; 043import org.apache.hadoop.hbase.util.Bytes; 044import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 045import org.junit.Before; 046import org.junit.ClassRule; 047import org.junit.Test; 048import org.junit.experimental.categories.Category; 049 050@Category({ RegionServerTests.class, SmallTests.class }) 051public class TestRegionSplitPolicy { 052 053 @ClassRule 054 public static final HBaseClassTestRule CLASS_RULE = 055 HBaseClassTestRule.forClass(TestRegionSplitPolicy.class); 056 057 private Configuration conf; 058 private HRegion mockRegion; 059 private List<HStore> stores; 060 private static final TableName TABLENAME = TableName.valueOf("t"); 061 062 @Before 063 public void setupMocks() { 064 conf = HBaseConfiguration.create(); 065 RegionInfo hri = RegionInfoBuilder.newBuilder(TABLENAME).build(); 066 mockRegion = mock(HRegion.class); 067 doReturn(hri).when(mockRegion).getRegionInfo(); 068 doReturn(true).when(mockRegion).isAvailable(); 069 stores = new ArrayList<>(); 070 doReturn(stores).when(mockRegion).getStores(); 071 } 072 073 @Test 074 public void testForceSplitRegionWithReference() throws IOException { 075 TableDescriptor td = TableDescriptorBuilder.newBuilder(TABLENAME).setMaxFileSize(1024L).build(); 076 doReturn(td).when(mockRegion).getTableDescriptor(); 077 // Add a store above the requisite size. Should split. 078 HStore mockStore = mock(HStore.class); 079 doReturn(2000L).when(mockStore).getSize(); 080 // Act as if there's a reference file or some other reason it can't split. 081 // This should prevent splitting even though it's big enough. 082 doReturn(false).when(mockStore).canSplit(); 083 stores.add(mockStore); 084 085 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY, 086 ConstantSizeRegionSplitPolicy.class.getName()); 087 ConstantSizeRegionSplitPolicy policy = 088 (ConstantSizeRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 089 assertFalse(policy.shouldSplit()); 090 091 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY, 092 IncreasingToUpperBoundRegionSplitPolicy.class.getName()); 093 policy = (IncreasingToUpperBoundRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 094 assertFalse(policy.shouldSplit()); 095 } 096 097 @Test 098 public void testIncreasingToUpperBoundRegionSplitPolicy() throws IOException { 099 // Configure IncreasingToUpperBoundRegionSplitPolicy as our split policy 100 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY, 101 IncreasingToUpperBoundRegionSplitPolicy.class.getName()); 102 // Now make it so the mock region has a RegionServerService that will 103 // return 'online regions'. 104 RegionServerServices rss = mock(RegionServerServices.class); 105 final List<HRegion> regions = new ArrayList<>(); 106 doReturn(regions).when(rss).getRegions(TABLENAME); 107 when(mockRegion.getRegionServerServices()).thenReturn(rss); 108 // Set max size for this 'table'. 109 long maxSplitSize = 1024L; 110 // Set flush size to 1/8. IncreasingToUpperBoundRegionSplitPolicy 111 // grows by the cube of the number of regions times flushsize each time. 112 long flushSize = maxSplitSize / 8; 113 conf.setLong(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, flushSize); 114 TableDescriptor td = TableDescriptorBuilder.newBuilder(TABLENAME).setMaxFileSize(maxSplitSize) 115 .setMemStoreFlushSize(flushSize).build(); 116 doReturn(td).when(mockRegion).getTableDescriptor(); 117 // If RegionServerService with no regions in it -- 'online regions' == 0 -- 118 // then IncreasingToUpperBoundRegionSplitPolicy should act like a 119 // ConstantSizePolicy 120 IncreasingToUpperBoundRegionSplitPolicy policy = 121 (IncreasingToUpperBoundRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 122 doConstantSizePolicyTests(policy); 123 124 // Add a store in excess of split size. Because there are "no regions" 125 // on this server -- rss.getOnlineRegions is 0 -- then we should split 126 // like a constantsizeregionsplitpolicy would 127 HStore mockStore = mock(HStore.class); 128 doReturn(2000L).when(mockStore).getSize(); 129 doReturn(true).when(mockStore).canSplit(); 130 stores.add(mockStore); 131 // It should split 132 assertTrue(policy.shouldSplit()); 133 134 // Now test that we increase our split size as online regions for a table 135 // grows. With one region, split size should be flushsize. 136 regions.add(mockRegion); 137 doReturn(flushSize).when(mockStore).getSize(); 138 // Should not split since store is flush size. 139 assertFalse(policy.shouldSplit()); 140 // Set size of store to be > 2*flush size and we should split 141 doReturn(flushSize * 2 + 1).when(mockStore).getSize(); 142 assertTrue(policy.shouldSplit()); 143 // Add another region to the 'online regions' on this server and we should 144 // now be no longer be splittable since split size has gone up. 145 regions.add(mockRegion); 146 assertFalse(policy.shouldSplit()); 147 // make sure its just over; verify it'll split 148 doReturn((long) (maxSplitSize * 1.25 + 1)).when(mockStore).getSize(); 149 assertTrue(policy.shouldSplit()); 150 151 // Finally assert that even if loads of regions, we'll split at max size 152 assertWithinJitter(maxSplitSize, policy.getSizeToCheck(1000)); 153 // Assert same is true if count of regions is zero. 154 assertWithinJitter(maxSplitSize, policy.getSizeToCheck(0)); 155 } 156 157 @Test 158 public void testSteppingSplitPolicyWithRegionReplication() throws IOException { 159 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY, SteppingSplitPolicy.class.getName()); 160 161 RegionServerServices rss = mock(RegionServerServices.class); 162 doReturn(rss).when(mockRegion).getRegionServerServices(); 163 164 long maxFileSize = HConstants.DEFAULT_MAX_FILE_SIZE; 165 TableDescriptor td = 166 TableDescriptorBuilder.newBuilder(TABLENAME).setMaxFileSize(maxFileSize).build(); 167 doReturn(td).when(mockRegion).getTableDescriptor(); 168 assertEquals(td.getMaxFileSize(), maxFileSize); 169 170 List<HStore> storefiles = new ArrayList<>(); 171 HStore mockStore = mock(HStore.class); 172 long flushSize = conf.getLong(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, 173 TableDescriptorBuilder.DEFAULT_MEMSTORE_FLUSH_SIZE); 174 long exceedSize = flushSize * 2 + 1; 175 doReturn(exceedSize).when(mockStore).getSize(); 176 doReturn(true).when(mockStore).canSplit(); 177 storefiles.add(mockStore); 178 doReturn(storefiles).when(mockRegion).getStores(); 179 180 List<HRegion> regions = new ArrayList<>(); 181 HRegion r1 = mock(HRegion.class); 182 RegionInfo regionInfo1 = mock(RegionInfo.class); 183 doReturn(regionInfo1).when(r1).getRegionInfo(); 184 doReturn(RegionInfo.DEFAULT_REPLICA_ID).when(regionInfo1).getReplicaId(); 185 HRegion r2 = mock(HRegion.class); 186 RegionInfo regionInfo2 = mock(RegionInfo.class); 187 doReturn(regionInfo2).when(r2).getRegionInfo(); 188 doReturn(1).when(regionInfo2).getReplicaId(); 189 regions.add(r1); 190 regions.add(r2); 191 doReturn(regions).when(rss).getRegions(td.getTableName()); 192 193 SteppingSplitPolicy policy = (SteppingSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 194 assertTrue(policy.shouldSplit()); 195 } 196 197 @Test 198 public void testIsExceedSize() throws IOException { 199 // Configure SteppingAllStoresSizeSplitPolicy as our split policy 200 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY, 201 ConstantSizeRegionSplitPolicy.class.getName()); 202 // Now make it so the mock region has a RegionServerService that will 203 // return 'online regions'. 204 RegionServerServices rss = mock(RegionServerServices.class); 205 final List<HRegion> regions = new ArrayList<>(); 206 doReturn(regions).when(rss).getRegions(TABLENAME); 207 when(mockRegion.getRegionServerServices()).thenReturn(rss); 208 209 TableDescriptor td = TableDescriptorBuilder.newBuilder(TABLENAME).build(); 210 doReturn(td).when(mockRegion).getTableDescriptor(); 211 ConstantSizeRegionSplitPolicy policy = 212 (ConstantSizeRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 213 regions.add(mockRegion); 214 215 HStore mockStore1 = mock(HStore.class); 216 doReturn(100L).when(mockStore1).getSize(); 217 HStore mockStore2 = mock(HStore.class); 218 doReturn(924L).when(mockStore2).getSize(); 219 HStore mockStore3 = mock(HStore.class); 220 doReturn(925L).when(mockStore3).getSize(); 221 222 // test sum of store's size not greater than sizeToCheck 223 stores.add(mockStore1); 224 stores.add(mockStore2); 225 assertFalse(policy.isExceedSize(1024)); 226 stores.clear(); 227 228 // test sum of store's size greater than sizeToCheck 229 stores.add(mockStore1); 230 stores.add(mockStore3); 231 assertTrue(policy.isExceedSize(1024)); 232 } 233 234 @Test 235 public void testBusyRegionSplitPolicy() throws Exception { 236 doReturn(TableDescriptorBuilder.newBuilder(TABLENAME).build()).when(mockRegion) 237 .getTableDescriptor(); 238 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY, BusyRegionSplitPolicy.class.getName()); 239 conf.setLong("hbase.busy.policy.minAge", 1000000L); 240 conf.setFloat("hbase.busy.policy.blockedRequests", 0.1f); 241 242 RegionServerServices rss = mock(RegionServerServices.class); 243 final List<HRegion> regions = new ArrayList<>(); 244 doReturn(regions).when(rss).getRegions(TABLENAME); 245 when(mockRegion.getRegionServerServices()).thenReturn(rss); 246 when(mockRegion.getBlockedRequestsCount()).thenReturn(0L); 247 when(mockRegion.getWriteRequestsCount()).thenReturn(0L); 248 249 BusyRegionSplitPolicy policy = 250 (BusyRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 251 252 when(mockRegion.getBlockedRequestsCount()).thenReturn(10L); 253 when(mockRegion.getWriteRequestsCount()).thenReturn(10L); 254 // Not enough time since region came online 255 assertFalse(policy.shouldSplit()); 256 257 // Reset min age for split to zero 258 conf.setLong("hbase.busy.policy.minAge", 0L); 259 // Aggregate over 500 ms periods 260 conf.setLong("hbase.busy.policy.aggWindow", 500L); 261 policy = (BusyRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 262 long start = EnvironmentEdgeManager.currentTime(); 263 when(mockRegion.getBlockedRequestsCount()).thenReturn(10L); 264 when(mockRegion.getWriteRequestsCount()).thenReturn(20L); 265 Thread.sleep(300); 266 assertFalse(policy.shouldSplit()); 267 when(mockRegion.getBlockedRequestsCount()).thenReturn(12L); 268 when(mockRegion.getWriteRequestsCount()).thenReturn(30L); 269 Thread.sleep(2); 270 // Enough blocked requests since last time, but aggregate blocked request 271 // rate over last 500 ms is still low, because major portion of the window is constituted 272 // by the previous zero blocked request period which lasted at least 300 ms off last 500 ms. 273 if (EnvironmentEdgeManager.currentTime() - start < 500) { 274 assertFalse(policy.shouldSplit()); 275 } 276 when(mockRegion.getBlockedRequestsCount()).thenReturn(14L); 277 when(mockRegion.getWriteRequestsCount()).thenReturn(40L); 278 Thread.sleep(200); 279 assertTrue(policy.shouldSplit()); 280 } 281 282 private void assertWithinJitter(long maxSplitSize, long sizeToCheck) { 283 assertTrue("Size greater than lower bound of jitter", 284 (long) (maxSplitSize * 0.75) <= sizeToCheck); 285 assertTrue("Size less than upper bound of jitter", (long) (maxSplitSize * 1.25) >= sizeToCheck); 286 } 287 288 @Test 289 public void testCreateDefault() throws IOException { 290 conf.setLong(HConstants.HREGION_MAX_FILESIZE, 1234L); 291 TableDescriptor td = TableDescriptorBuilder.newBuilder(TABLENAME).build(); 292 doReturn(td).when(mockRegion).getTableDescriptor(); 293 // Using a default HTD, should pick up the file size from 294 // configuration. 295 ConstantSizeRegionSplitPolicy policy = 296 (ConstantSizeRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 297 assertWithinJitter(1234L, policy.getDesiredMaxFileSize()); 298 299 // If specified in HTD, should use that 300 td = TableDescriptorBuilder.newBuilder(TABLENAME).setMaxFileSize(9999L).build(); 301 doReturn(td).when(mockRegion).getTableDescriptor(); 302 policy = (ConstantSizeRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 303 assertWithinJitter(9999L, policy.getDesiredMaxFileSize()); 304 } 305 306 /** 307 * Test setting up a customized split policy 308 */ 309 @Test 310 public void testCustomPolicy() throws IOException { 311 TableDescriptor td = TableDescriptorBuilder.newBuilder(TABLENAME) 312 .setRegionSplitPolicyClassName(KeyPrefixRegionSplitPolicy.class.getName()) 313 .setValue(KeyPrefixRegionSplitPolicy.PREFIX_LENGTH_KEY, "2").build(); 314 315 doReturn(td).when(mockRegion).getTableDescriptor(); 316 317 HStore mockStore = mock(HStore.class); 318 doReturn(2000L).when(mockStore).getSize(); 319 doReturn(true).when(mockStore).canSplit(); 320 doReturn(Optional.of(Bytes.toBytes("abcd"))).when(mockStore).getSplitPoint(); 321 stores.add(mockStore); 322 323 KeyPrefixRegionSplitPolicy policy = 324 (KeyPrefixRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 325 326 assertEquals("ab", Bytes.toString(policy.getSplitPoint())); 327 } 328 329 @Test 330 public void testConstantSizePolicy() throws IOException { 331 TableDescriptor td = TableDescriptorBuilder.newBuilder(TABLENAME).setMaxFileSize(1024L).build(); 332 doReturn(td).when(mockRegion).getTableDescriptor(); 333 ConstantSizeRegionSplitPolicy policy = 334 (ConstantSizeRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 335 doConstantSizePolicyTests(policy); 336 } 337 338 /** 339 * Run through tests for a ConstantSizeRegionSplitPolicy 340 */ 341 private void doConstantSizePolicyTests(final ConstantSizeRegionSplitPolicy policy) { 342 // For no stores, should not split 343 assertFalse(policy.shouldSplit()); 344 345 // Add a store above the requisite size. Should split. 346 HStore mockStore = mock(HStore.class); 347 doReturn(2000L).when(mockStore).getSize(); 348 doReturn(true).when(mockStore).canSplit(); 349 stores.add(mockStore); 350 351 assertTrue(policy.shouldSplit()); 352 353 // Act as if there's a reference file or some other reason it can't split. 354 // This should prevent splitting even though it's big enough. 355 doReturn(false).when(mockStore).canSplit(); 356 assertFalse(policy.shouldSplit()); 357 358 // Reset splittability after above 359 doReturn(true).when(mockStore).canSplit(); 360 361 // Set to a small size, should not split 362 doReturn(100L).when(mockStore).getSize(); 363 assertFalse(policy.shouldSplit()); 364 365 // Clear families we added above 366 stores.clear(); 367 } 368 369 @Test 370 public void testGetSplitPoint() throws IOException { 371 TableDescriptor td = TableDescriptorBuilder.newBuilder(TABLENAME).build(); 372 doReturn(td).when(mockRegion).getTableDescriptor(); 373 374 ConstantSizeRegionSplitPolicy policy = 375 (ConstantSizeRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 376 377 // For no stores, should not split 378 assertFalse(policy.shouldSplit()); 379 assertNull(policy.getSplitPoint()); 380 381 // Add a store above the requisite size. Should split. 382 HStore mockStore = mock(HStore.class); 383 doReturn(2000L).when(mockStore).getSize(); 384 doReturn(true).when(mockStore).canSplit(); 385 doReturn(Optional.of(Bytes.toBytes("store 1 split"))).when(mockStore).getSplitPoint(); 386 stores.add(mockStore); 387 388 assertEquals("store 1 split", Bytes.toString(policy.getSplitPoint())); 389 390 // Add a bigger store. The split point should come from that one 391 HStore mockStore2 = mock(HStore.class); 392 doReturn(4000L).when(mockStore2).getSize(); 393 doReturn(true).when(mockStore2).canSplit(); 394 doReturn(Optional.of(Bytes.toBytes("store 2 split"))).when(mockStore2).getSplitPoint(); 395 stores.add(mockStore2); 396 397 assertEquals("store 2 split", Bytes.toString(policy.getSplitPoint())); 398 } 399 400 @Test 401 public void testDelimitedKeyPrefixRegionSplitPolicy() throws IOException { 402 TableDescriptor td = TableDescriptorBuilder.newBuilder(TABLENAME) 403 .setRegionSplitPolicyClassName(DelimitedKeyPrefixRegionSplitPolicy.class.getName()) 404 .setValue(DelimitedKeyPrefixRegionSplitPolicy.DELIMITER_KEY, ",").build(); 405 406 doReturn(td).when(mockRegion).getTableDescriptor(); 407 doReturn(stores).when(mockRegion).getStores(); 408 409 HStore mockStore = mock(HStore.class); 410 doReturn(2000L).when(mockStore).getSize(); 411 doReturn(true).when(mockStore).canSplit(); 412 doReturn(Optional.of(Bytes.toBytes("ab,cd"))).when(mockStore).getSplitPoint(); 413 stores.add(mockStore); 414 415 DelimitedKeyPrefixRegionSplitPolicy policy = 416 (DelimitedKeyPrefixRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 417 418 assertEquals("ab", Bytes.toString(policy.getSplitPoint())); 419 420 doReturn(Optional.of(Bytes.toBytes("ijk"))).when(mockStore).getSplitPoint(); 421 assertEquals("ijk", Bytes.toString(policy.getSplitPoint())); 422 } 423 424 @Test 425 public void testConstantSizePolicyWithJitter() throws IOException { 426 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY, 427 ConstantSizeRegionSplitPolicy.class.getName()); 428 TableDescriptor td = 429 TableDescriptorBuilder.newBuilder(TABLENAME).setMaxFileSize(Long.MAX_VALUE).build(); 430 doReturn(td).when(mockRegion).getTableDescriptor(); 431 boolean positiveJitter = false; 432 ConstantSizeRegionSplitPolicy policy = null; 433 while (!positiveJitter) { 434 policy = (ConstantSizeRegionSplitPolicy) RegionSplitPolicy.create(mockRegion, conf); 435 positiveJitter = policy.positiveJitterRate(); 436 } 437 // add a store 438 HStore mockStore = mock(HStore.class); 439 doReturn(2000L).when(mockStore).getSize(); 440 doReturn(true).when(mockStore).canSplit(); 441 stores.add(mockStore); 442 // Jitter shouldn't cause overflow when HTableDescriptor.MAX_FILESIZE set to Long.MAX_VALUE 443 assertFalse(policy.shouldSplit()); 444 } 445}