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.io; 019 020import static org.apache.hadoop.hbase.io.ByteBuffAllocator.HEAP; 021import static org.apache.hadoop.hbase.io.ByteBuffAllocator.getHeapAllocationRatio; 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertFalse; 024import static org.junit.Assert.assertSame; 025import static org.junit.Assert.assertTrue; 026import static org.junit.Assert.fail; 027 028import java.nio.ByteBuffer; 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.hbase.HBaseClassTestRule; 031import org.apache.hadoop.hbase.HBaseConfiguration; 032import org.apache.hadoop.hbase.nio.ByteBuff; 033import org.apache.hadoop.hbase.nio.MultiByteBuff; 034import org.apache.hadoop.hbase.nio.SingleByteBuff; 035import org.apache.hadoop.hbase.testclassification.RPCTests; 036import org.apache.hadoop.hbase.testclassification.SmallTests; 037import org.junit.Assert; 038import org.junit.ClassRule; 039import org.junit.Test; 040import org.junit.experimental.categories.Category; 041 042@Category({ RPCTests.class, SmallTests.class }) 043public class TestByteBuffAllocator { 044 045 @ClassRule 046 public static final HBaseClassTestRule CLASS_RULE = 047 HBaseClassTestRule.forClass(TestByteBuffAllocator.class); 048 049 @Test 050 public void testRecycleOnlyPooledBuffers() { 051 int maxBuffersInPool = 10; 052 int bufSize = 1024; 053 int minSize = bufSize / 8; 054 ByteBuffAllocator alloc = new ByteBuffAllocator(true, maxBuffersInPool, bufSize, minSize); 055 056 ByteBuff buff = alloc.allocate(minSize - 1); 057 assertSame(ByteBuffAllocator.NONE, buff.getRefCnt().getRecycler()); 058 059 alloc = new ByteBuffAllocator(true, 0, bufSize, minSize); 060 buff = alloc.allocate(minSize * 2); 061 assertSame(ByteBuffAllocator.NONE, buff.getRefCnt().getRecycler()); 062 } 063 064 @Test 065 public void testAllocateByteBuffToReadInto() { 066 int maxBuffersInPool = 10; 067 int bufSize = 6 * 1024; 068 ByteBuffAllocator alloc = new ByteBuffAllocator(true, maxBuffersInPool, bufSize, bufSize / 6); 069 assertEquals(0, alloc.getUsedBufferCount()); 070 071 ByteBuff buff = alloc.allocate(10 * bufSize); 072 assertEquals(61440, alloc.getPoolAllocationBytes()); 073 assertEquals(0, alloc.getHeapAllocationBytes()); 074 assertEquals(10, alloc.getUsedBufferCount()); 075 buff.release(); 076 // When the request size is less than 1/6th of the pool buffer size. We should use on demand 077 // created on heap Buffer 078 buff = alloc.allocate(200); 079 assertTrue(buff.hasArray()); 080 assertEquals(maxBuffersInPool, alloc.getFreeBufferCount()); 081 assertEquals(maxBuffersInPool, alloc.getTotalBufferCount()); 082 assertEquals(61440, alloc.getPoolAllocationBytes()); 083 assertEquals(200, alloc.getHeapAllocationBytes()); 084 assertEquals(10, alloc.getUsedBufferCount()); 085 buff.release(); 086 // When the request size is > 1/6th of the pool buffer size. 087 buff = alloc.allocate(1024); 088 assertFalse(buff.hasArray()); 089 assertEquals(maxBuffersInPool - 1, alloc.getFreeBufferCount()); 090 assertEquals(67584, alloc.getPoolAllocationBytes()); 091 assertEquals(200, alloc.getHeapAllocationBytes()); 092 assertEquals(10, alloc.getUsedBufferCount()); 093 buff.release();// ByteBuff Recycler#free should put back the BB to pool. 094 assertEquals(maxBuffersInPool, alloc.getFreeBufferCount()); 095 // Request size> pool buffer size 096 buff = alloc.allocate(7 * 1024); 097 assertFalse(buff.hasArray()); 098 assertTrue(buff instanceof MultiByteBuff); 099 ByteBuffer[] bbs = buff.nioByteBuffers(); 100 assertEquals(2, bbs.length); 101 assertTrue(bbs[0].isDirect()); 102 assertTrue(bbs[1].isDirect()); 103 assertEquals(6 * 1024, bbs[0].limit()); 104 assertEquals(1024, bbs[1].limit()); 105 assertEquals(maxBuffersInPool - 2, alloc.getFreeBufferCount()); 106 assertEquals(79872, alloc.getPoolAllocationBytes()); 107 assertEquals(200, alloc.getHeapAllocationBytes()); 108 assertEquals(10, alloc.getUsedBufferCount()); 109 buff.release(); 110 assertEquals(maxBuffersInPool, alloc.getFreeBufferCount()); 111 112 buff = alloc.allocate(6 * 1024 + 200); 113 assertFalse(buff.hasArray()); 114 assertTrue(buff instanceof MultiByteBuff); 115 bbs = buff.nioByteBuffers(); 116 assertEquals(2, bbs.length); 117 assertTrue(bbs[0].isDirect()); 118 assertFalse(bbs[1].isDirect()); 119 assertEquals(6 * 1024, bbs[0].limit()); 120 assertEquals(200, bbs[1].limit()); 121 assertEquals(maxBuffersInPool - 1, alloc.getFreeBufferCount()); 122 assertEquals(86016, alloc.getPoolAllocationBytes()); 123 assertEquals(400, alloc.getHeapAllocationBytes()); 124 assertEquals(10, alloc.getUsedBufferCount()); 125 buff.release(); 126 assertEquals(maxBuffersInPool, alloc.getFreeBufferCount()); 127 128 alloc.allocate(bufSize * (maxBuffersInPool - 1)); 129 assertEquals(141312, alloc.getPoolAllocationBytes()); 130 assertEquals(400, alloc.getHeapAllocationBytes()); 131 assertEquals(10, alloc.getUsedBufferCount()); 132 133 buff = alloc.allocate(20 * 1024); 134 assertFalse(buff.hasArray()); 135 assertTrue(buff instanceof MultiByteBuff); 136 bbs = buff.nioByteBuffers(); 137 assertEquals(2, bbs.length); 138 assertTrue(bbs[0].isDirect()); 139 assertFalse(bbs[1].isDirect()); 140 assertEquals(6 * 1024, bbs[0].limit()); 141 assertEquals(14 * 1024, bbs[1].limit()); 142 assertEquals(0, alloc.getFreeBufferCount()); 143 assertEquals(147456, alloc.getPoolAllocationBytes()); 144 assertEquals(14736, alloc.getHeapAllocationBytes()); 145 assertEquals(10, alloc.getUsedBufferCount()); 146 147 buff.release(); 148 assertEquals(1, alloc.getFreeBufferCount()); 149 alloc.allocateOneBuffer(); 150 assertEquals(153600, alloc.getPoolAllocationBytes()); 151 assertEquals(14736, alloc.getHeapAllocationBytes()); 152 assertEquals(10, alloc.getUsedBufferCount()); 153 154 buff = alloc.allocate(7 * 1024); 155 assertTrue(buff.hasArray()); 156 assertTrue(buff instanceof SingleByteBuff); 157 assertEquals(7 * 1024, buff.nioByteBuffers()[0].limit()); 158 assertEquals(153600, alloc.getPoolAllocationBytes()); 159 assertEquals(21904, alloc.getHeapAllocationBytes()); 160 assertEquals(10, alloc.getUsedBufferCount()); 161 buff.release(); 162 } 163 164 @Test 165 public void testNegativeAllocatedSize() { 166 int maxBuffersInPool = 10; 167 ByteBuffAllocator allocator = new ByteBuffAllocator(true, maxBuffersInPool, 6 * 1024, 1024); 168 try { 169 allocator.allocate(-1); 170 fail("Should throw exception when size < 0"); 171 } catch (IllegalArgumentException e) { 172 // expected exception 173 } 174 ByteBuff bb = allocator.allocate(0); 175 assertEquals(0, allocator.getHeapAllocationBytes()); 176 bb.release(); 177 } 178 179 @Test 180 public void testAllocateOneBuffer() { 181 // Allocate from on-heap 182 ByteBuffAllocator allocator = HEAP; 183 ByteBuff buf = allocator.allocateOneBuffer(); 184 assertTrue(buf.hasArray()); 185 assertEquals(ByteBuffAllocator.DEFAULT_BUFFER_SIZE, buf.remaining()); 186 buf.release(); 187 188 // Allocate from off-heap 189 int bufSize = 10; 190 allocator = new ByteBuffAllocator(true, 1, 10, 3); 191 buf = allocator.allocateOneBuffer(); 192 assertFalse(buf.hasArray()); 193 assertEquals(buf.remaining(), bufSize); 194 // The another one will be allocated from on-heap because the pool has only one ByteBuffer, 195 // and still not be cleaned. 196 ByteBuff buf2 = allocator.allocateOneBuffer(); 197 assertTrue(buf2.hasArray()); 198 assertEquals(buf2.remaining(), bufSize); 199 // free the first one 200 buf.release(); 201 // The next one will be off-heap again. 202 buf = allocator.allocateOneBuffer(); 203 assertFalse(buf.hasArray()); 204 assertEquals(buf.remaining(), bufSize); 205 buf.release(); 206 } 207 208 @Test 209 public void testReferenceCount() { 210 int bufSize = 64; 211 ByteBuffAllocator alloc = new ByteBuffAllocator(true, 2, bufSize, 3); 212 ByteBuff buf1 = alloc.allocate(bufSize * 2); 213 assertFalse(buf1.hasArray()); 214 // The next one will be allocated from heap 215 ByteBuff buf2 = alloc.allocateOneBuffer(); 216 assertTrue(buf2.hasArray()); 217 218 // duplicate the buf2, if the dup released, buf2 will also be released (SingleByteBuffer) 219 ByteBuff dup2 = buf2.duplicate(); 220 dup2.release(); 221 assertEquals(0, buf2.refCnt()); 222 assertEquals(0, dup2.refCnt()); 223 assertEquals(0, alloc.getFreeBufferCount()); 224 assertException(dup2::position); 225 assertException(buf2::position); 226 227 // duplicate the buf1, if the dup1 released, buf1 will also be released (MultipleByteBuffer) 228 ByteBuff dup1 = buf1.duplicate(); 229 dup1.release(); 230 assertEquals(0, buf1.refCnt()); 231 assertEquals(0, dup1.refCnt()); 232 assertEquals(2, alloc.getFreeBufferCount()); 233 assertException(dup1::position); 234 assertException(buf1::position); 235 236 // slice the buf3, if the slice3 released, buf3 will also be released (SingleByteBuffer) 237 ByteBuff buf3 = alloc.allocateOneBuffer(); 238 assertFalse(buf3.hasArray()); 239 ByteBuff slice3 = buf3.slice(); 240 slice3.release(); 241 assertEquals(0, buf3.refCnt()); 242 assertEquals(0, slice3.refCnt()); 243 assertEquals(2, alloc.getFreeBufferCount()); 244 245 // slice the buf4, if the slice4 released, buf4 will also be released (MultipleByteBuffer) 246 ByteBuff buf4 = alloc.allocate(bufSize * 2); 247 assertFalse(buf4.hasArray()); 248 ByteBuff slice4 = buf4.slice(); 249 slice4.release(); 250 assertEquals(0, buf4.refCnt()); 251 assertEquals(0, slice4.refCnt()); 252 assertEquals(2, alloc.getFreeBufferCount()); 253 254 // Test multiple reference for the same ByteBuff (SingleByteBuff) 255 ByteBuff buf5 = alloc.allocateOneBuffer(); 256 ByteBuff slice5 = buf5.duplicate().duplicate().duplicate().slice().slice(); 257 slice5.release(); 258 assertEquals(0, buf5.refCnt()); 259 assertEquals(0, slice5.refCnt()); 260 assertEquals(2, alloc.getFreeBufferCount()); 261 assertException(slice5::position); 262 assertException(buf5::position); 263 264 // Test multiple reference for the same ByteBuff (SingleByteBuff) 265 ByteBuff buf6 = alloc.allocate(bufSize >> 2); 266 ByteBuff slice6 = buf6.duplicate().duplicate().duplicate().slice().slice(); 267 slice6.release(); 268 assertEquals(0, buf6.refCnt()); 269 assertEquals(0, slice6.refCnt()); 270 assertEquals(2, alloc.getFreeBufferCount()); 271 272 // Test retain the parent SingleByteBuff (duplicate) 273 ByteBuff parent = alloc.allocateOneBuffer(); 274 ByteBuff child = parent.duplicate(); 275 child.retain(); 276 parent.release(); 277 assertEquals(1, child.refCnt()); 278 assertEquals(1, parent.refCnt()); 279 assertEquals(1, alloc.getFreeBufferCount()); 280 parent.release(); 281 assertEquals(0, child.refCnt()); 282 assertEquals(0, parent.refCnt()); 283 assertEquals(2, alloc.getFreeBufferCount()); 284 285 // Test retain parent MultiByteBuff (duplicate) 286 parent = alloc.allocate(bufSize << 1); 287 child = parent.duplicate(); 288 child.retain(); 289 parent.release(); 290 assertEquals(1, child.refCnt()); 291 assertEquals(1, parent.refCnt()); 292 assertEquals(0, alloc.getFreeBufferCount()); 293 parent.release(); 294 assertEquals(0, child.refCnt()); 295 assertEquals(0, parent.refCnt()); 296 assertEquals(2, alloc.getFreeBufferCount()); 297 298 // Test retain the parent SingleByteBuff (slice) 299 parent = alloc.allocateOneBuffer(); 300 child = parent.slice(); 301 child.retain(); 302 parent.release(); 303 assertEquals(1, child.refCnt()); 304 assertEquals(1, parent.refCnt()); 305 assertEquals(1, alloc.getFreeBufferCount()); 306 parent.release(); 307 assertEquals(0, child.refCnt()); 308 assertEquals(0, parent.refCnt()); 309 assertEquals(2, alloc.getFreeBufferCount()); 310 311 // Test retain parent MultiByteBuff (slice) 312 parent = alloc.allocate(bufSize << 1); 313 child = parent.slice(); 314 child.retain(); 315 parent.release(); 316 assertEquals(1, child.refCnt()); 317 assertEquals(1, parent.refCnt()); 318 assertEquals(0, alloc.getFreeBufferCount()); 319 parent.release(); 320 assertEquals(0, child.refCnt()); 321 assertEquals(0, parent.refCnt()); 322 assertEquals(2, alloc.getFreeBufferCount()); 323 } 324 325 @Test 326 public void testReverseRef() { 327 int bufSize = 64; 328 ByteBuffAllocator alloc = new ByteBuffAllocator(true, 1, bufSize, 3); 329 ByteBuff buf1 = alloc.allocate(bufSize); 330 ByteBuff dup1 = buf1.duplicate(); 331 assertEquals(1, buf1.refCnt()); 332 assertEquals(1, dup1.refCnt()); 333 buf1.release(); 334 assertEquals(0, buf1.refCnt()); 335 assertEquals(0, dup1.refCnt()); 336 assertEquals(1, alloc.getFreeBufferCount()); 337 assertException(buf1::position); 338 assertException(dup1::position); 339 } 340 341 @Test 342 public void testByteBuffUnsupportedMethods() { 343 int bufSize = 64; 344 ByteBuffAllocator alloc = new ByteBuffAllocator(true, 1, bufSize, 3); 345 ByteBuff buf = alloc.allocate(bufSize); 346 assertException(() -> buf.retain(2)); 347 assertException(() -> buf.release(2)); 348 } 349 350 private void assertException(Runnable r) { 351 try { 352 r.run(); 353 fail(); 354 } catch (Exception e) { 355 // expected exception. 356 } 357 } 358 359 @Test 360 public void testDeprecatedConfigs() { 361 Configuration conf = HBaseConfiguration.create(); 362 conf.setInt(ByteBuffAllocator.DEPRECATED_MAX_BUFFER_COUNT_KEY, 10); 363 conf.setInt(ByteBuffAllocator.DEPRECATED_BUFFER_SIZE_KEY, 1024); 364 ByteBuffAllocator allocator = ByteBuffAllocator.create(conf, true); 365 Assert.assertEquals(1024, allocator.getBufferSize()); 366 Assert.assertEquals(10, allocator.getTotalBufferCount()); 367 368 conf = new Configuration(); 369 conf.setInt(ByteBuffAllocator.MAX_BUFFER_COUNT_KEY, 11); 370 conf.setInt(ByteBuffAllocator.BUFFER_SIZE_KEY, 2048); 371 allocator = ByteBuffAllocator.create(conf, true); 372 Assert.assertEquals(2048, allocator.getBufferSize()); 373 Assert.assertEquals(11, allocator.getTotalBufferCount()); 374 375 conf = new Configuration(); 376 conf.setBoolean(ByteBuffAllocator.DEPRECATED_ALLOCATOR_POOL_ENABLED_KEY, false); 377 Assert.assertFalse(conf.getBoolean(ByteBuffAllocator.ALLOCATOR_POOL_ENABLED_KEY, true)); 378 conf.setBoolean(ByteBuffAllocator.DEPRECATED_ALLOCATOR_POOL_ENABLED_KEY, true); 379 Assert.assertTrue(conf.getBoolean(ByteBuffAllocator.ALLOCATOR_POOL_ENABLED_KEY, false)); 380 conf.setBoolean(ByteBuffAllocator.ALLOCATOR_POOL_ENABLED_KEY, true); 381 Assert.assertTrue(conf.getBoolean(ByteBuffAllocator.ALLOCATOR_POOL_ENABLED_KEY, false)); 382 conf.setBoolean(ByteBuffAllocator.ALLOCATOR_POOL_ENABLED_KEY, false); 383 Assert.assertFalse(conf.getBoolean(ByteBuffAllocator.ALLOCATOR_POOL_ENABLED_KEY, true)); 384 } 385 386 @Test 387 public void testHeapAllocationRatio() { 388 Configuration conf = new Configuration(); 389 conf.setInt(ByteBuffAllocator.MAX_BUFFER_COUNT_KEY, 11); 390 conf.setInt(ByteBuffAllocator.BUFFER_SIZE_KEY, 2048); 391 ByteBuffAllocator alloc1 = ByteBuffAllocator.create(conf, true); 392 Assert.assertEquals(getHeapAllocationRatio(alloc1), 0.0f, 1e-6); 393 alloc1.allocate(1); 394 Assert.assertEquals(getHeapAllocationRatio(alloc1), 1.0f, 1e-6); 395 396 alloc1.allocate(2048 / 6 - 1); 397 Assert.assertEquals(getHeapAllocationRatio(alloc1), 1.0f, 1e-6); 398 399 alloc1.allocate(24); 400 alloc1.allocate(1024); 401 Assert.assertEquals(getHeapAllocationRatio(alloc1), 24 / (24f + 2048), 1e-6); 402 Assert.assertEquals(getHeapAllocationRatio(alloc1), 0.0f, 1e-6); 403 404 // Allocate something from HEAP 405 HEAP.allocate(1024); 406 alloc1.allocate(24); 407 alloc1.allocate(1024); 408 Assert.assertEquals(getHeapAllocationRatio(HEAP, alloc1), (1024f + 24) / (1024f + 24 + 2048), 409 1e-6); 410 Assert.assertEquals(getHeapAllocationRatio(HEAP, alloc1), 0.0f, 1e-6); 411 412 // Check duplicated heap allocator, say even if we passed (HEAP, HEAP, alloc1), it will only 413 // caculate the allocation from (HEAP, alloc1). 414 HEAP.allocate(1024); 415 alloc1.allocate(1024); 416 Assert.assertEquals(getHeapAllocationRatio(HEAP, HEAP, alloc1), 1024f / (1024f + 2048f), 1e-6); 417 } 418}