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.client; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotNull; 023import static org.junit.Assert.assertNull; 024import static org.junit.Assert.assertTrue; 025import static org.mockito.ArgumentMatchers.anyList; 026import static org.mockito.Mockito.spy; 027import static org.mockito.Mockito.times; 028import static org.mockito.Mockito.verify; 029 030import java.io.IOException; 031import java.util.Arrays; 032import org.apache.hadoop.conf.Configuration; 033import org.apache.hadoop.fs.FileSystem; 034import org.apache.hadoop.fs.Path; 035import org.apache.hadoop.hbase.Cell; 036import org.apache.hadoop.hbase.HBaseClassTestRule; 037import org.apache.hadoop.hbase.HBaseTestingUtility; 038import org.apache.hadoop.hbase.HConstants; 039import org.apache.hadoop.hbase.TableName; 040import org.apache.hadoop.hbase.filter.FilterBase; 041import org.apache.hadoop.hbase.io.hfile.BlockCache; 042import org.apache.hadoop.hbase.io.hfile.IndexOnlyLruBlockCache; 043import org.apache.hadoop.hbase.regionserver.RegionScanner; 044import org.apache.hadoop.hbase.regionserver.StoreScanner; 045import org.apache.hadoop.hbase.testclassification.ClientTests; 046import org.apache.hadoop.hbase.testclassification.SmallTests; 047import org.apache.hadoop.hbase.util.Bytes; 048import org.junit.AfterClass; 049import org.junit.Before; 050import org.junit.BeforeClass; 051import org.junit.ClassRule; 052import org.junit.Test; 053import org.junit.experimental.categories.Category; 054 055@Category({ SmallTests.class, ClientTests.class }) 056public class TestClientSideRegionScanner { 057 @ClassRule 058 public static final HBaseClassTestRule CLASS_RULE = 059 HBaseClassTestRule.forClass(TestClientSideRegionScanner.class); 060 061 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 062 private static final TableName TABLE_NAME = TableName.valueOf("test"); 063 private static final byte[] FAM_NAME = Bytes.toBytes("f"); 064 065 private Configuration conf; 066 private Path rootDir; 067 private FileSystem fs; 068 private TableDescriptor htd; 069 private RegionInfo hri; 070 private Scan scan; 071 072 @BeforeClass 073 public static void setUpBeforeClass() throws Exception { 074 TEST_UTIL.startMiniCluster(1); 075 } 076 077 @AfterClass 078 public static void tearDownAfterClass() throws Exception { 079 TEST_UTIL.shutdownMiniCluster(); 080 } 081 082 @Before 083 public void setup() throws IOException { 084 conf = TEST_UTIL.getConfiguration(); 085 rootDir = TEST_UTIL.getDefaultRootDirPath(); 086 fs = TEST_UTIL.getTestFileSystem(); 087 htd = TEST_UTIL.getAdmin().getDescriptor(TableName.META_TABLE_NAME); 088 hri = TEST_UTIL.getAdmin().getRegions(TableName.META_TABLE_NAME).get(0); 089 scan = new Scan(); 090 } 091 092 @Test 093 public void testDefaultBlockCache() throws IOException { 094 Configuration copyConf = new Configuration(conf); 095 ClientSideRegionScanner clientSideRegionScanner = 096 new ClientSideRegionScanner(copyConf, fs, rootDir, htd, hri, scan, null); 097 098 BlockCache blockCache = clientSideRegionScanner.getRegion().getBlockCache(); 099 assertNotNull(blockCache); 100 assertTrue(blockCache instanceof IndexOnlyLruBlockCache); 101 assertTrue(HConstants.HBASE_CLIENT_SCANNER_ONHEAP_BLOCK_CACHE_FIXED_SIZE_DEFAULT 102 == blockCache.getMaxSize()); 103 } 104 105 @Test 106 public void testConfiguredBlockCache() throws IOException { 107 Configuration copyConf = new Configuration(conf); 108 // tiny 1MB fixed cache size 109 long blockCacheFixedSize = 1024 * 1024L; 110 copyConf.setLong(HConstants.HFILE_ONHEAP_BLOCK_CACHE_FIXED_SIZE_KEY, blockCacheFixedSize); 111 ClientSideRegionScanner clientSideRegionScanner = 112 new ClientSideRegionScanner(copyConf, fs, rootDir, htd, hri, scan, null); 113 114 BlockCache blockCache = clientSideRegionScanner.getRegion().getBlockCache(); 115 assertNotNull(blockCache); 116 assertTrue(blockCache instanceof IndexOnlyLruBlockCache); 117 assertTrue(blockCacheFixedSize == blockCache.getMaxSize()); 118 } 119 120 @Test 121 public void testNoBlockCache() throws IOException { 122 Configuration copyConf = new Configuration(conf); 123 copyConf.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0.0f); 124 ClientSideRegionScanner clientSideRegionScanner = 125 new ClientSideRegionScanner(copyConf, fs, rootDir, htd, hri, scan, null); 126 127 BlockCache blockCache = clientSideRegionScanner.getRegion().getBlockCache(); 128 assertNull(blockCache); 129 } 130 131 @Test 132 public void testContinuesToScanIfHasMore() throws IOException { 133 // Conditions for this test to set up RegionScannerImpl to bail on the scan 134 // after a single iteration 135 // 1. Configure preadMaxBytes to something small to trigger scannerContext#returnImmediately 136 // 2. Configure a filter to filter out some rows, in this case rows with values < 5 137 // 3. Configure the filter's hasFilterRow to return true so RegionScannerImpl sets 138 // the limitScope to something with a depth of 0, so we bail on the scan after the first 139 // iteration 140 141 Configuration copyConf = new Configuration(conf); 142 copyConf.setLong(StoreScanner.STORESCANNER_PREAD_MAX_BYTES, 1); 143 Scan scan = new Scan(); 144 scan.setFilter(new FiltersRowsLessThan5()); 145 scan.setLimit(1); 146 147 try (Table table = TEST_UTIL.createTable(TABLE_NAME, FAM_NAME)) { 148 TableDescriptor htd = TEST_UTIL.getAdmin().getDescriptor(TABLE_NAME); 149 RegionInfo hri = TEST_UTIL.getAdmin().getRegions(TABLE_NAME).get(0); 150 151 for (int i = 0; i < 10; ++i) { 152 table.put(createPut(i)); 153 } 154 155 // Flush contents to disk so we can scan the fs 156 TEST_UTIL.getAdmin().flush(TABLE_NAME); 157 158 ClientSideRegionScanner clientSideRegionScanner = 159 new ClientSideRegionScanner(copyConf, fs, rootDir, htd, hri, scan, null); 160 RegionScanner scannerSpy = spy(clientSideRegionScanner.scanner); 161 clientSideRegionScanner.scanner = scannerSpy; 162 Result result = clientSideRegionScanner.next(); 163 164 verify(scannerSpy, times(6)).nextRaw(anyList()); 165 assertNotNull(result); 166 assertEquals(Bytes.toInt(result.getRow()), 5); 167 assertTrue(clientSideRegionScanner.hasMore); 168 169 for (int i = 6; i < 10; ++i) { 170 result = clientSideRegionScanner.next(); 171 verify(scannerSpy, times(i + 1)).nextRaw(anyList()); 172 assertNotNull(result); 173 assertEquals(Bytes.toInt(result.getRow()), i); 174 } 175 176 result = clientSideRegionScanner.next(); 177 assertNull(result); 178 assertFalse(clientSideRegionScanner.hasMore); 179 } 180 } 181 182 private static Put createPut(int rowAsInt) { 183 byte[] row = Bytes.toBytes(rowAsInt); 184 Put put = new Put(row); 185 put.addColumn(FAM_NAME, row, row); 186 return put; 187 } 188 189 private static class FiltersRowsLessThan5 extends FilterBase { 190 191 @Override 192 public boolean filterRowKey(Cell cell) { 193 byte[] rowKey = Arrays.copyOfRange(cell.getRowArray(), cell.getRowOffset(), 194 cell.getRowLength() + cell.getRowOffset()); 195 int intValue = Bytes.toInt(rowKey); 196 return intValue < 5; 197 } 198 199 @Override 200 public boolean hasFilterRow() { 201 return true; 202 } 203 } 204}