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