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.assertNotNull; 023import static org.junit.Assert.assertTrue; 024 025import java.lang.management.ManagementFactory; 026import java.nio.ByteBuffer; 027import java.util.Iterator; 028import java.util.NavigableMap; 029import java.util.NavigableSet; 030import java.util.SortedSet; 031import org.apache.hadoop.conf.Configuration; 032import org.apache.hadoop.hbase.Cell; 033import org.apache.hadoop.hbase.CellComparator; 034import org.apache.hadoop.hbase.CellUtil; 035import org.apache.hadoop.hbase.HBaseClassTestRule; 036import org.apache.hadoop.hbase.KeyValue; 037import org.apache.hadoop.hbase.KeyValueUtil; 038import org.apache.hadoop.hbase.io.util.MemorySizeUtil; 039import org.apache.hadoop.hbase.testclassification.RegionServerTests; 040import org.apache.hadoop.hbase.testclassification.SmallTests; 041import org.apache.hadoop.hbase.util.ByteBufferUtils; 042import org.apache.hadoop.hbase.util.Bytes; 043import org.apache.hadoop.hbase.util.ClassSize; 044import org.junit.Before; 045import org.junit.ClassRule; 046import org.junit.Test; 047import org.junit.experimental.categories.Category; 048import org.junit.runner.RunWith; 049import org.junit.runners.Parameterized; 050 051@Category({RegionServerTests.class, SmallTests.class}) 052@RunWith(Parameterized.class) 053public class TestCellFlatSet { 054 055 @ClassRule 056 public static final HBaseClassTestRule CLASS_RULE = 057 HBaseClassTestRule.forClass(TestCellFlatSet.class); 058 059 @Parameterized.Parameters 060 public static Object[] data() { 061 return new Object[] { "SMALL_CHUNKS", "NORMAL_CHUNKS" }; // test with different chunk sizes 062 } 063 private static final int NUM_OF_CELLS = 4; 064 private static final int SMALL_CHUNK_SIZE = 64; 065 private Cell ascCells[]; 066 private CellArrayMap ascCbOnHeap; 067 private Cell descCells[]; 068 private CellArrayMap descCbOnHeap; 069 private final static Configuration CONF = new Configuration(); 070 private KeyValue lowerOuterCell; 071 private KeyValue upperOuterCell; 072 073 074 private CellChunkMap ascCCM; // for testing ascending CellChunkMap with one chunk in array 075 private CellChunkMap descCCM; // for testing descending CellChunkMap with one chunk in array 076 private final boolean smallChunks; 077 private static ChunkCreator chunkCreator; 078 079 080 public TestCellFlatSet(String chunkType){ 081 long globalMemStoreLimit = (long) (ManagementFactory.getMemoryMXBean().getHeapMemoryUsage() 082 .getMax() * MemorySizeUtil.getGlobalMemStoreHeapPercent(CONF, false)); 083 if (chunkType.equals("NORMAL_CHUNKS")) { 084 chunkCreator = ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 085 globalMemStoreLimit, 0.2f, MemStoreLAB.POOL_INITIAL_SIZE_DEFAULT, 086 null, MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT); 087 assertNotNull(chunkCreator); 088 smallChunks = false; 089 } else { 090 // chunkCreator with smaller chunk size, so only 3 cell-representations can accommodate a chunk 091 chunkCreator = ChunkCreator.initialize(SMALL_CHUNK_SIZE, false, 092 globalMemStoreLimit, 0.2f, MemStoreLAB.POOL_INITIAL_SIZE_DEFAULT, 093 null, MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT); 094 assertNotNull(chunkCreator); 095 smallChunks = true; 096 } 097 } 098 099 @Before 100 public void setUp() throws Exception { 101 // create array of Cells to bass to the CellFlatMap under CellSet 102 final byte[] one = Bytes.toBytes(15); 103 final byte[] two = Bytes.toBytes(25); 104 final byte[] three = Bytes.toBytes(35); 105 final byte[] four = Bytes.toBytes(45); 106 107 final byte[] f = Bytes.toBytes("f"); 108 final byte[] q = Bytes.toBytes("q"); 109 final byte[] v = Bytes.toBytes(4); 110 111 final KeyValue kv1 = new KeyValue(one, f, q, 10, v); 112 final KeyValue kv2 = new KeyValue(two, f, q, 20, v); 113 final KeyValue kv3 = new KeyValue(three, f, q, 30, v); 114 final KeyValue kv4 = new KeyValue(four, f, q, 40, v); 115 lowerOuterCell = new KeyValue(Bytes.toBytes(10), f, q, 10, v); 116 upperOuterCell = new KeyValue(Bytes.toBytes(50), f, q, 10, v); 117 ascCells = new Cell[] {kv1,kv2,kv3,kv4}; 118 ascCbOnHeap = new CellArrayMap(CellComparator.getInstance(), ascCells,0, NUM_OF_CELLS,false); 119 descCells = new Cell[] {kv4,kv3,kv2,kv1}; 120 descCbOnHeap = new CellArrayMap(CellComparator.getInstance(), descCells,0, NUM_OF_CELLS,true); 121 122 CONF.setBoolean(MemStoreLAB.USEMSLAB_KEY, true); 123 CONF.setFloat(MemStoreLAB.CHUNK_POOL_MAXSIZE_KEY, 0.2f); 124 ChunkCreator.chunkPoolDisabled = false; 125 126 // create ascending and descending CellChunkMaps 127 // according to parameter, once built with normal chunks and at second with small chunks 128 ascCCM = setUpCellChunkMap(true); 129 descCCM = setUpCellChunkMap(false); 130 131 if (smallChunks) { // check jumbo chunks as well 132 ascCCM = setUpJumboCellChunkMap(true); 133 } 134 } 135 136 /* Create and test ascending CellSet based on CellArrayMap */ 137 @Test 138 public void testCellArrayMapAsc() throws Exception { 139 CellSet cs = new CellSet(ascCbOnHeap); 140 testCellBlocks(cs); 141 testIterators(cs); 142 } 143 144 /* Create and test ascending and descending CellSet based on CellChunkMap */ 145 @Test 146 public void testCellChunkMap() throws Exception { 147 CellSet cs = new CellSet(ascCCM); 148 testCellBlocks(cs); 149 testIterators(cs); 150 testSubSet(cs); 151 cs = new CellSet(descCCM); 152 testSubSet(cs); 153// cs = new CellSet(ascMultCCM); 154// testCellBlocks(cs); 155// testSubSet(cs); 156// cs = new CellSet(descMultCCM); 157// testSubSet(cs); 158 } 159 160 @Test 161 public void testAsc() throws Exception { 162 CellSet ascCs = new CellSet(ascCbOnHeap); 163 assertEquals(NUM_OF_CELLS, ascCs.size()); 164 testSubSet(ascCs); 165 } 166 @Test 167 public void testDesc() throws Exception { 168 CellSet descCs = new CellSet(descCbOnHeap); 169 assertEquals(NUM_OF_CELLS, descCs.size()); 170 testSubSet(descCs); 171 } 172 173 private void testSubSet(CellSet cs) throws Exception { 174 for (int i = 0; i != ascCells.length; ++i) { 175 NavigableSet<Cell> excludeTail = cs.tailSet(ascCells[i], false); 176 NavigableSet<Cell> includeTail = cs.tailSet(ascCells[i], true); 177 assertEquals(ascCells.length - 1 - i, excludeTail.size()); 178 assertEquals(ascCells.length - i, includeTail.size()); 179 Iterator<Cell> excludeIter = excludeTail.iterator(); 180 Iterator<Cell> includeIter = includeTail.iterator(); 181 for (int j = 1 + i; j != ascCells.length; ++j) { 182 assertEquals(true, CellUtil.equals(excludeIter.next(), ascCells[j])); 183 } 184 for (int j = i; j != ascCells.length; ++j) { 185 assertEquals(true, CellUtil.equals(includeIter.next(), ascCells[j])); 186 } 187 } 188 assertEquals(NUM_OF_CELLS, cs.tailSet(lowerOuterCell, false).size()); 189 assertEquals(0, cs.tailSet(upperOuterCell, false).size()); 190 for (int i = 0; i != ascCells.length; ++i) { 191 NavigableSet<Cell> excludeHead = cs.headSet(ascCells[i], false); 192 NavigableSet<Cell> includeHead = cs.headSet(ascCells[i], true); 193 assertEquals(i, excludeHead.size()); 194 assertEquals(i + 1, includeHead.size()); 195 Iterator<Cell> excludeIter = excludeHead.iterator(); 196 Iterator<Cell> includeIter = includeHead.iterator(); 197 for (int j = 0; j != i; ++j) { 198 assertEquals(true, CellUtil.equals(excludeIter.next(), ascCells[j])); 199 } 200 for (int j = 0; j != i + 1; ++j) { 201 assertEquals(true, CellUtil.equals(includeIter.next(), ascCells[j])); 202 } 203 } 204 assertEquals(0, cs.headSet(lowerOuterCell, false).size()); 205 assertEquals(NUM_OF_CELLS, cs.headSet(upperOuterCell, false).size()); 206 207 NavigableMap<Cell, Cell> sub = cs.getDelegatee().subMap(lowerOuterCell, true, upperOuterCell, true); 208 assertEquals(NUM_OF_CELLS, sub.size()); 209 Iterator<Cell> iter = sub.values().iterator(); 210 for (int i = 0; i != ascCells.length; ++i) { 211 assertEquals(true, CellUtil.equals(iter.next(), ascCells[i])); 212 } 213 } 214 215 /* Generic basic test for immutable CellSet */ 216 private void testCellBlocks(CellSet cs) throws Exception { 217 final byte[] oneAndHalf = Bytes.toBytes(20); 218 final byte[] f = Bytes.toBytes("f"); 219 final byte[] q = Bytes.toBytes("q"); 220 final byte[] v = Bytes.toBytes(4); 221 final KeyValue outerCell = new KeyValue(oneAndHalf, f, q, 10, v); 222 223 assertEquals(NUM_OF_CELLS, cs.size()); // check size 224 assertFalse(cs.contains(outerCell)); // check outer cell 225 226 assertTrue(cs.contains(ascCells[0])); // check existence of the first 227 Cell first = cs.first(); 228 assertTrue(ascCells[0].equals(first)); 229 230 assertTrue(cs.contains(ascCells[NUM_OF_CELLS - 1])); // check last 231 Cell last = cs.last(); 232 assertTrue(ascCells[NUM_OF_CELLS - 1].equals(last)); 233 234 SortedSet<Cell> tail = cs.tailSet(ascCells[1]); // check tail abd head sizes 235 assertEquals(NUM_OF_CELLS - 1, tail.size()); 236 SortedSet<Cell> head = cs.headSet(ascCells[1]); 237 assertEquals(1, head.size()); 238 239 SortedSet<Cell> tailOuter = cs.tailSet(outerCell); // check tail starting from outer cell 240 assertEquals(NUM_OF_CELLS - 1, tailOuter.size()); 241 242 Cell tailFirst = tail.first(); 243 assertTrue(ascCells[1].equals(tailFirst)); 244 Cell tailLast = tail.last(); 245 assertTrue(ascCells[NUM_OF_CELLS - 1].equals(tailLast)); 246 247 Cell headFirst = head.first(); 248 assertTrue(ascCells[0].equals(headFirst)); 249 Cell headLast = head.last(); 250 assertTrue(ascCells[0].equals(headLast)); 251 } 252 253 /* Generic iterators test for immutable CellSet */ 254 private void testIterators(CellSet cs) throws Exception { 255 256 // Assert that we have NUM_OF_CELLS values and that they are in order 257 int count = 0; 258 for (Cell kv: cs) { 259 assertEquals("\n\n-------------------------------------------------------------------\n" 260 + "Comparing iteration number " + (count + 1) + " the returned cell: " + kv 261 + ", the first Cell in the CellBlocksMap: " + ascCells[count] 262 + ", and the same transformed to String: " + ascCells[count].toString() 263 + "\n-------------------------------------------------------------------\n", 264 ascCells[count], kv); 265 count++; 266 } 267 assertEquals(NUM_OF_CELLS, count); 268 269 // Test descending iterator 270 count = 0; 271 for (Iterator<Cell> i = cs.descendingIterator(); i.hasNext();) { 272 Cell kv = i.next(); 273 assertEquals(ascCells[NUM_OF_CELLS - (count + 1)], kv); 274 count++; 275 } 276 assertEquals(NUM_OF_CELLS, count); 277 } 278 279 /* Create CellChunkMap with four cells inside the index chunk */ 280 private CellChunkMap setUpCellChunkMap(boolean asc) { 281 282 // allocate new chunks and use the data chunk to hold the full data of the cells 283 // and the index chunk to hold the cell-representations 284 Chunk dataChunk = chunkCreator.getChunk(CompactingMemStore.IndexType.CHUNK_MAP); 285 Chunk idxChunk = chunkCreator.getChunk(CompactingMemStore.IndexType.CHUNK_MAP); 286 // the array of index chunks to be used as a basis for CellChunkMap 287 Chunk chunkArray[] = new Chunk[8]; // according to test currently written 8 is way enough 288 int chunkArrayIdx = 0; 289 chunkArray[chunkArrayIdx++] = idxChunk; 290 291 ByteBuffer idxBuffer = idxChunk.getData(); // the buffers of the chunks 292 ByteBuffer dataBuffer = dataChunk.getData(); 293 int dataOffset = ChunkCreator.SIZEOF_CHUNK_HEADER; // offset inside data buffer 294 int idxOffset = ChunkCreator.SIZEOF_CHUNK_HEADER; // skip the space for chunk ID 295 296 Cell[] cellArray = asc ? ascCells : descCells; 297 298 for (Cell kv: cellArray) { 299 // do we have enough space to write the cell data on the data chunk? 300 if (dataOffset + kv.getSerializedSize() > chunkCreator.getChunkSize()) { 301 // allocate more data chunks if needed 302 dataChunk = chunkCreator.getChunk(CompactingMemStore.IndexType.CHUNK_MAP); 303 dataBuffer = dataChunk.getData(); 304 dataOffset = ChunkCreator.SIZEOF_CHUNK_HEADER; 305 } 306 int dataStartOfset = dataOffset; 307 dataOffset = KeyValueUtil.appendTo(kv, dataBuffer, dataOffset, false); // write deep cell data 308 309 // do we have enough space to write the cell-representation on the index chunk? 310 if (idxOffset + ClassSize.CELL_CHUNK_MAP_ENTRY > chunkCreator.getChunkSize()) { 311 // allocate more index chunks if needed 312 idxChunk = chunkCreator.getChunk(CompactingMemStore.IndexType.CHUNK_MAP); 313 idxBuffer = idxChunk.getData(); 314 idxOffset = ChunkCreator.SIZEOF_CHUNK_HEADER; 315 chunkArray[chunkArrayIdx++] = idxChunk; 316 } 317 idxOffset = ByteBufferUtils.putInt(idxBuffer, idxOffset, dataChunk.getId()); // write data chunk id 318 idxOffset = ByteBufferUtils.putInt(idxBuffer, idxOffset, dataStartOfset); // offset 319 idxOffset = ByteBufferUtils.putInt(idxBuffer, idxOffset, kv.getSerializedSize()); // length 320 idxOffset = ByteBufferUtils.putLong(idxBuffer, idxOffset, kv.getSequenceId()); // seqId 321 } 322 323 return new CellChunkMap(CellComparator.getInstance(),chunkArray,0,NUM_OF_CELLS,!asc); 324 } 325 326 /* Create CellChunkMap with four cells inside the data jumbo chunk. This test is working only 327 ** with small chunks sized SMALL_CHUNK_SIZE (64) bytes */ 328 private CellChunkMap setUpJumboCellChunkMap(boolean asc) { 329 int smallChunkSize = SMALL_CHUNK_SIZE+8; 330 // allocate new chunks and use the data JUMBO chunk to hold the full data of the cells 331 // and the normal index chunk to hold the cell-representations 332 Chunk dataJumboChunk = 333 chunkCreator.getChunk(CompactingMemStore.IndexType.CHUNK_MAP, smallChunkSize); 334 Chunk idxChunk = chunkCreator.getChunk(CompactingMemStore.IndexType.CHUNK_MAP); 335 // the array of index chunks to be used as a basis for CellChunkMap 336 Chunk[] chunkArray = new Chunk[8]; // according to test currently written 8 is way enough 337 int chunkArrayIdx = 0; 338 chunkArray[chunkArrayIdx++] = idxChunk; 339 340 ByteBuffer idxBuffer = idxChunk.getData(); // the buffers of the chunks 341 ByteBuffer dataBuffer = dataJumboChunk.getData(); 342 int dataOffset = ChunkCreator.SIZEOF_CHUNK_HEADER; // offset inside data buffer 343 int idxOffset = ChunkCreator.SIZEOF_CHUNK_HEADER; // skip the space for chunk ID 344 345 Cell[] cellArray = asc ? ascCells : descCells; 346 347 for (Cell kv: cellArray) { 348 int dataStartOfset = dataOffset; 349 dataOffset = KeyValueUtil.appendTo(kv, dataBuffer, dataOffset, false); // write deep cell data 350 351 // do we have enough space to write the cell-representation on the index chunk? 352 if (idxOffset + ClassSize.CELL_CHUNK_MAP_ENTRY > chunkCreator.getChunkSize()) { 353 // allocate more index chunks if needed 354 idxChunk = chunkCreator.getChunk(CompactingMemStore.IndexType.CHUNK_MAP); 355 idxBuffer = idxChunk.getData(); 356 idxOffset = ChunkCreator.SIZEOF_CHUNK_HEADER; 357 chunkArray[chunkArrayIdx++] = idxChunk; 358 } 359 // write data chunk id 360 idxOffset = ByteBufferUtils.putInt(idxBuffer, idxOffset, dataJumboChunk.getId()); 361 idxOffset = ByteBufferUtils.putInt(idxBuffer, idxOffset, dataStartOfset); // offset 362 idxOffset = ByteBufferUtils.putInt(idxBuffer, idxOffset, kv.getSerializedSize()); // length 363 idxOffset = ByteBufferUtils.putLong(idxBuffer, idxOffset, kv.getSequenceId()); // seqId 364 365 // Jumbo chunks are working only with one cell per chunk, thus always allocate a new jumbo 366 // data chunk for next cell 367 dataJumboChunk = chunkCreator.getChunk(CompactingMemStore.IndexType.CHUNK_MAP,smallChunkSize); 368 dataBuffer = dataJumboChunk.getData(); 369 dataOffset = ChunkCreator.SIZEOF_CHUNK_HEADER; 370 } 371 372 return new CellChunkMap(CellComparator.getInstance(),chunkArray,0,NUM_OF_CELLS,!asc); 373 } 374}