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.hfile; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.List; 027import org.apache.hadoop.conf.Configuration; 028import org.apache.hadoop.fs.Path; 029import org.apache.hadoop.hbase.ByteBufferKeyValue; 030import org.apache.hadoop.hbase.Cell; 031import org.apache.hadoop.hbase.HBaseClassTestRule; 032import org.apache.hadoop.hbase.HBaseTestingUtility; 033import org.apache.hadoop.hbase.HConstants; 034import org.apache.hadoop.hbase.HRegionInfo; 035import org.apache.hadoop.hbase.KeyValue; 036import org.apache.hadoop.hbase.PrivateCellUtil; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.client.Durability; 039import org.apache.hadoop.hbase.client.Put; 040import org.apache.hadoop.hbase.client.Scan; 041import org.apache.hadoop.hbase.regionserver.ChunkCreator; 042import org.apache.hadoop.hbase.regionserver.HRegion; 043import org.apache.hadoop.hbase.regionserver.HStore; 044import org.apache.hadoop.hbase.regionserver.InternalScanner; 045import org.apache.hadoop.hbase.regionserver.MemStoreLABImpl; 046import org.apache.hadoop.hbase.testclassification.MediumTests; 047import org.apache.hadoop.hbase.testclassification.RegionServerTests; 048import org.apache.hadoop.hbase.util.Bytes; 049import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper; 050import org.apache.hadoop.hbase.wal.WAL; 051import org.junit.After; 052import org.junit.ClassRule; 053import org.junit.Rule; 054import org.junit.Test; 055import org.junit.experimental.categories.Category; 056import org.junit.rules.TestName; 057import org.slf4j.Logger; 058import org.slf4j.LoggerFactory; 059 060@Category({ RegionServerTests.class, MediumTests.class }) 061public class TestScannerFromBucketCache { 062 063 @ClassRule 064 public static final HBaseClassTestRule CLASS_RULE = 065 HBaseClassTestRule.forClass(TestScannerFromBucketCache.class); 066 067 private static final Logger LOG = LoggerFactory.getLogger(TestScannerFromBucketCache.class); 068 @Rule 069 public TestName name = new TestName(); 070 071 HRegion region = null; 072 private HBaseTestingUtility test_util; 073 public Configuration conf; 074 private final int MAX_VERSIONS = 2; 075 byte[] val = new byte[512 * 1024]; 076 077 // Test names 078 private TableName tableName; 079 080 private void setUp(boolean useBucketCache) throws IOException { 081 test_util = HBaseTestingUtility.createLocalHTU(); 082 conf = test_util.getConfiguration(); 083 if (useBucketCache) { 084 conf.setInt("hbase.bucketcache.size", 400); 085 conf.setStrings(HConstants.BUCKET_CACHE_IOENGINE_KEY, "offheap"); 086 conf.setInt("hbase.bucketcache.writer.threads", 10); 087 conf.setFloat("hfile.block.cache.size", 0.2f); 088 conf.setFloat("hbase.regionserver.global.memstore.size", 0.1f); 089 } 090 tableName = TableName.valueOf(name.getMethodName()); 091 } 092 093 @After 094 public void tearDown() throws Exception { 095 EnvironmentEdgeManagerTestHelper.reset(); 096 LOG.info("Cleaning test directory: " + test_util.getDataTestDir()); 097 test_util.cleanupTestDir(); 098 CacheConfig.clearGlobalInstances(); 099 } 100 101 String getName() { 102 return name.getMethodName(); 103 } 104 105 @Test 106 public void testBasicScanWithLRUCache() throws IOException { 107 setUp(false); 108 byte[] row1 = Bytes.toBytes("row1"); 109 byte[] qf1 = Bytes.toBytes("qualifier1"); 110 byte[] qf2 = Bytes.toBytes("qualifier2"); 111 byte[] fam1 = Bytes.toBytes("lrucache"); 112 113 long ts1 = 1; // System.currentTimeMillis(); 114 long ts2 = ts1 + 1; 115 long ts3 = ts1 + 2; 116 117 // Setting up region 118 String method = this.getName(); 119 this.region = initHRegion(tableName, method, conf, test_util, fam1); 120 try { 121 List<Cell> expected = insertData(row1, qf1, qf2, fam1, ts1, ts2, ts3, false); 122 123 List<Cell> actual = performScan(row1, fam1); 124 // Verify result 125 for (int i = 0; i < expected.size(); i++) { 126 assertFalse(actual.get(i) instanceof ByteBufferKeyValue); 127 assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i))); 128 } 129 // do the scan again and verify. This time it should be from the lru cache 130 actual = performScan(row1, fam1); 131 // Verify result 132 for (int i = 0; i < expected.size(); i++) { 133 assertFalse(actual.get(i) instanceof ByteBufferKeyValue); 134 assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i))); 135 } 136 137 } finally { 138 HBaseTestingUtility.closeRegionAndWAL(this.region); 139 this.region = null; 140 } 141 } 142 143 @Test 144 public void testBasicScanWithOffheapBucketCache() throws IOException { 145 setUp(true); 146 byte[] row1 = Bytes.toBytes("row1offheap"); 147 byte[] qf1 = Bytes.toBytes("qualifier1"); 148 byte[] qf2 = Bytes.toBytes("qualifier2"); 149 byte[] fam1 = Bytes.toBytes("famoffheap"); 150 151 long ts1 = 1; // System.currentTimeMillis(); 152 long ts2 = ts1 + 1; 153 long ts3 = ts1 + 2; 154 155 // Setting up region 156 String method = this.getName(); 157 this.region = initHRegion(tableName, method, conf, test_util, fam1); 158 try { 159 List<Cell> expected = insertData(row1, qf1, qf2, fam1, ts1, ts2, ts3, false); 160 161 List<Cell> actual = performScan(row1, fam1); 162 // Verify result 163 for (int i = 0; i < expected.size(); i++) { 164 assertFalse(actual.get(i) instanceof ByteBufferKeyValue); 165 assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i))); 166 } 167 // Wait for the bucket cache threads to move the data to offheap 168 Thread.sleep(500); 169 // do the scan again and verify. This time it should be from the bucket cache in offheap mode 170 actual = performScan(row1, fam1); 171 // Verify result 172 for (int i = 0; i < expected.size(); i++) { 173 assertTrue(actual.get(i) instanceof ByteBufferKeyValue); 174 assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i))); 175 } 176 177 } catch (InterruptedException e) { 178 } finally { 179 HBaseTestingUtility.closeRegionAndWAL(this.region); 180 this.region = null; 181 } 182 } 183 184 @Test 185 public void testBasicScanWithOffheapBucketCacheWithMBB() throws IOException { 186 setUp(true); 187 byte[] row1 = Bytes.toBytes("row1offheap"); 188 byte[] qf1 = Bytes.toBytes("qualifier1"); 189 byte[] qf2 = Bytes.toBytes("qualifier2"); 190 byte[] fam1 = Bytes.toBytes("famoffheap"); 191 192 long ts1 = 1; // System.currentTimeMillis(); 193 long ts2 = ts1 + 1; 194 long ts3 = ts1 + 2; 195 196 // Setting up region 197 String method = this.getName(); 198 this.region = initHRegion(tableName, method, conf, test_util, fam1); 199 try { 200 List<Cell> expected = insertData(row1, qf1, qf2, fam1, ts1, ts2, ts3, true); 201 202 List<Cell> actual = performScan(row1, fam1); 203 // Verify result 204 for (int i = 0; i < expected.size(); i++) { 205 assertFalse(actual.get(i) instanceof ByteBufferKeyValue); 206 assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i))); 207 } 208 // Wait for the bucket cache threads to move the data to offheap 209 Thread.sleep(500); 210 // do the scan again and verify. This time it should be from the bucket cache in offheap mode 211 // but one of the cell will be copied due to the asSubByteBuff call 212 Scan scan = new Scan(row1); 213 scan.addFamily(fam1); 214 scan.setMaxVersions(10); 215 actual = new ArrayList<>(); 216 InternalScanner scanner = region.getScanner(scan); 217 218 boolean hasNext = scanner.next(actual); 219 assertEquals(false, hasNext); 220 // Verify result 221 for (int i = 0; i < expected.size(); i++) { 222 if (i != 5) { 223 // the last cell fetched will be of type shareable but not offheap because 224 // the MBB is copied to form a single cell 225 assertTrue(actual.get(i) instanceof ByteBufferKeyValue); 226 } 227 } 228 229 } catch (InterruptedException e) { 230 } finally { 231 HBaseTestingUtility.closeRegionAndWAL(this.region); 232 this.region = null; 233 } 234 } 235 236 private List<Cell> insertData(byte[] row1, byte[] qf1, byte[] qf2, byte[] fam1, long ts1, 237 long ts2, long ts3, boolean withVal) throws IOException { 238 // Putting data in Region 239 Put put = null; 240 KeyValue kv13 = null; 241 KeyValue kv12 = null; 242 KeyValue kv11 = null; 243 244 KeyValue kv23 = null; 245 KeyValue kv22 = null; 246 KeyValue kv21 = null; 247 if (!withVal) { 248 kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null); 249 kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null); 250 kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null); 251 252 kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null); 253 kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null); 254 kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null); 255 } else { 256 kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, val); 257 kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, val); 258 kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, val); 259 260 kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, val); 261 kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, val); 262 kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, val); 263 } 264 265 put = new Put(row1); 266 put.add(kv13); 267 put.add(kv12); 268 put.add(kv11); 269 put.add(kv23); 270 put.add(kv22); 271 put.add(kv21); 272 region.put(put); 273 region.flush(true); 274 HStore store = region.getStore(fam1); 275 while (store.getStorefilesCount() <= 0) { 276 try { 277 Thread.sleep(20); 278 } catch (InterruptedException e) { 279 } 280 } 281 282 // Expected 283 List<Cell> expected = new ArrayList<>(); 284 expected.add(kv13); 285 expected.add(kv12); 286 expected.add(kv23); 287 expected.add(kv22); 288 return expected; 289 } 290 291 private List<Cell> performScan(byte[] row1, byte[] fam1) throws IOException { 292 Scan scan = new Scan(row1); 293 scan.addFamily(fam1); 294 scan.setMaxVersions(MAX_VERSIONS); 295 List<Cell> actual = new ArrayList<>(); 296 InternalScanner scanner = region.getScanner(scan); 297 298 boolean hasNext = scanner.next(actual); 299 assertEquals(false, hasNext); 300 return actual; 301 } 302 303 private static HRegion initHRegion(TableName tableName, String callingMethod, Configuration conf, 304 HBaseTestingUtility test_util, byte[]... families) throws IOException { 305 return initHRegion(tableName, null, null, callingMethod, conf, test_util, false, families); 306 } 307 308 private static HRegion initHRegion(TableName tableName, byte[] startKey, byte[] stopKey, 309 String callingMethod, Configuration conf, HBaseTestingUtility test_util, boolean isReadOnly, 310 byte[]... families) throws IOException { 311 ChunkCreator.initialize(MemStoreLABImpl.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null); 312 Path logDir = test_util.getDataTestDirOnTestFS(callingMethod + ".log"); 313 HRegionInfo hri = new HRegionInfo(tableName, startKey, stopKey); 314 final WAL wal = HBaseTestingUtility.createWal(conf, logDir, hri); 315 return initHRegion(tableName, startKey, stopKey, callingMethod, conf, test_util, isReadOnly, 316 Durability.SYNC_WAL, wal, families); 317 } 318 319 /** 320 * @param tableName 321 * @param startKey 322 * @param stopKey 323 * @param callingMethod 324 * @param conf 325 * @param isReadOnly 326 * @param families 327 * @throws IOException 328 * @return A region on which you must call {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} 329 * when done. 330 */ 331 private static HRegion initHRegion(TableName tableName, byte[] startKey, byte[] stopKey, 332 String callingMethod, Configuration conf, HBaseTestingUtility test_util, boolean isReadOnly, 333 Durability durability, WAL wal, byte[]... families) throws IOException { 334 return test_util.createLocalHRegion(tableName, startKey, stopKey, isReadOnly, durability, wal, 335 families); 336 } 337}