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.assertTrue;
023
024import java.io.IOException;
025import java.util.ArrayList;
026import java.util.Collection;
027import java.util.Iterator;
028import java.util.List;
029import java.util.Optional;
030import java.util.concurrent.CountDownLatch;
031import java.util.concurrent.TimeUnit;
032import java.util.concurrent.atomic.AtomicBoolean;
033import java.util.concurrent.atomic.AtomicInteger;
034import java.util.concurrent.atomic.AtomicLong;
035import java.util.concurrent.atomic.AtomicReference;
036import org.apache.hadoop.conf.Configuration;
037import org.apache.hadoop.hbase.Cell;
038import org.apache.hadoop.hbase.HBaseClassTestRule;
039import org.apache.hadoop.hbase.HBaseTestingUtility;
040import org.apache.hadoop.hbase.HConstants;
041import org.apache.hadoop.hbase.ServerName;
042import org.apache.hadoop.hbase.TableName;
043import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
044import org.apache.hadoop.hbase.coprocessor.MultiRowMutationEndpoint;
045import org.apache.hadoop.hbase.coprocessor.ObserverContext;
046import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor;
047import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
048import org.apache.hadoop.hbase.coprocessor.RegionObserver;
049import org.apache.hadoop.hbase.io.hfile.BlockCache;
050import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
051import org.apache.hadoop.hbase.io.hfile.CacheConfig;
052import org.apache.hadoop.hbase.io.hfile.CachedBlock;
053import org.apache.hadoop.hbase.io.hfile.CombinedBlockCache;
054import org.apache.hadoop.hbase.io.hfile.bucket.BucketCache;
055import org.apache.hadoop.hbase.regionserver.HRegion;
056import org.apache.hadoop.hbase.regionserver.HStore;
057import org.apache.hadoop.hbase.regionserver.InternalScanner;
058import org.apache.hadoop.hbase.regionserver.RegionScanner;
059import org.apache.hadoop.hbase.regionserver.ScannerContext;
060import org.apache.hadoop.hbase.testclassification.ClientTests;
061import org.apache.hadoop.hbase.testclassification.LargeTests;
062import org.apache.hadoop.hbase.util.Bytes;
063import org.junit.After;
064import org.junit.AfterClass;
065import org.junit.Before;
066import org.junit.BeforeClass;
067import org.junit.ClassRule;
068import org.junit.Rule;
069import org.junit.Test;
070import org.junit.experimental.categories.Category;
071import org.junit.rules.TestName;
072import org.slf4j.Logger;
073import org.slf4j.LoggerFactory;
074
075@Category({ LargeTests.class, ClientTests.class })
076@SuppressWarnings("deprecation")
077public class TestBlockEvictionFromClient {
078
079  @ClassRule
080  public static final HBaseClassTestRule CLASS_RULE =
081      HBaseClassTestRule.forClass(TestBlockEvictionFromClient.class);
082
083  private static final Logger LOG = LoggerFactory.getLogger(TestBlockEvictionFromClient.class);
084  protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
085  static byte[][] ROWS = new byte[2][];
086  private static int NO_OF_THREADS = 3;
087  private static byte[] ROW = Bytes.toBytes("testRow");
088  private static byte[] ROW1 = Bytes.toBytes("testRow1");
089  private static byte[] ROW2 = Bytes.toBytes("testRow2");
090  private static byte[] ROW3 = Bytes.toBytes("testRow3");
091  private static byte[] FAMILY = Bytes.toBytes("testFamily");
092  private static byte[][] FAMILIES_1 = new byte[1][0];
093  private static byte[] QUALIFIER = Bytes.toBytes("testQualifier");
094  private static byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
095  private static byte[] data = new byte[1000];
096  private static byte[] data2 = Bytes.add(data, data);
097  protected static int SLAVES = 1;
098  private static CountDownLatch latch;
099  private static CountDownLatch getLatch;
100  private static CountDownLatch compactionLatch;
101  private static CountDownLatch exceptionLatch;
102
103  @Rule
104  public TestName name = new TestName();
105
106  /**
107   * @throws java.lang.Exception
108   */
109  @BeforeClass
110  public static void setUpBeforeClass() throws Exception {
111    ROWS[0] = ROW;
112    ROWS[1] = ROW1;
113    Configuration conf = TEST_UTIL.getConfiguration();
114    conf.setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
115        MultiRowMutationEndpoint.class.getName());
116    conf.setBoolean("hbase.table.sanity.checks", true); // enable for below
117                                                        // tests
118    conf.setInt("hbase.regionserver.handler.count", 20);
119    conf.setInt("hbase.bucketcache.size", 400);
120    conf.setStrings(HConstants.BUCKET_CACHE_IOENGINE_KEY, "offheap");
121    conf.setFloat("hfile.block.cache.size", 0.2f);
122    conf.setFloat("hbase.regionserver.global.memstore.size", 0.1f);
123    conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 0);// do not retry
124    conf.setInt(HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD, 5000);
125    FAMILIES_1[0] = FAMILY;
126    TEST_UTIL.startMiniCluster(SLAVES);
127  }
128
129  /**
130   * @throws java.lang.Exception
131   */
132  @AfterClass
133  public static void tearDownAfterClass() throws Exception {
134    TEST_UTIL.shutdownMiniCluster();
135  }
136
137  /**
138   * @throws java.lang.Exception
139   */
140  @Before
141  public void setUp() throws Exception {
142    CustomInnerRegionObserver.waitForGets.set(false);
143    CustomInnerRegionObserver.countOfNext.set(0);
144    CustomInnerRegionObserver.countOfGets.set(0);
145  }
146
147  /**
148   * @throws java.lang.Exception
149   */
150  @After
151  public void tearDown() throws Exception {
152    if (latch != null) {
153      while (latch.getCount() > 0) {
154        latch.countDown();
155      }
156    }
157    if (getLatch != null) {
158      getLatch.countDown();
159    }
160    if (compactionLatch != null) {
161      compactionLatch.countDown();
162    }
163    if (exceptionLatch != null) {
164      exceptionLatch.countDown();
165    }
166    latch = null;
167    getLatch = null;
168    compactionLatch = null;
169    exceptionLatch = null;
170    CustomInnerRegionObserver.throwException.set(false);
171    // Clean up the tables for every test case
172    TableName[] listTableNames = TEST_UTIL.getAdmin().listTableNames();
173    for (TableName tableName : listTableNames) {
174      if (!tableName.isSystemTable()) {
175        TEST_UTIL.getAdmin().disableTable(tableName);
176        TEST_UTIL.getAdmin().deleteTable(tableName);
177      }
178    }
179  }
180
181  @Test
182  public void testBlockEvictionWithParallelScans() throws Exception {
183    Table table = null;
184    try {
185      latch = new CountDownLatch(1);
186      final TableName tableName = TableName.valueOf(name.getMethodName());
187      // Create a table with block size as 1024
188      table = TEST_UTIL.createTable(tableName, FAMILIES_1, 1, 1024,
189          CustomInnerRegionObserver.class.getName());
190      // get the block cache and region
191      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
192      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
193      HRegion region = TEST_UTIL.getRSForFirstRegionInTable(tableName)
194          .getRegion(regionName);
195      HStore store = region.getStores().iterator().next();
196      CacheConfig cacheConf = store.getCacheConfig();
197      cacheConf.setCacheDataOnWrite(true);
198      cacheConf.setEvictOnClose(true);
199      BlockCache cache = cacheConf.getBlockCache();
200
201      // insert data. 2 Rows are added
202      Put put = new Put(ROW);
203      put.addColumn(FAMILY, QUALIFIER, data);
204      table.put(put);
205      put = new Put(ROW1);
206      put.addColumn(FAMILY, QUALIFIER, data);
207      table.put(put);
208      assertTrue(Bytes.equals(table.get(new Get(ROW)).value(), data));
209      // data was in memstore so don't expect any changes
210      // flush the data
211      // Should create one Hfile with 2 blocks
212      region.flush(true);
213      // Load cache
214      // Create three sets of scan
215      ScanThread[] scanThreads = initiateScan(table, false);
216      Thread.sleep(100);
217      checkForBlockEviction(cache, false, false);
218      for (ScanThread thread : scanThreads) {
219        thread.join();
220      }
221      // CustomInnerRegionObserver.sleepTime.set(0);
222      Iterator<CachedBlock> iterator = cache.iterator();
223      iterateBlockCache(cache, iterator);
224      // read the data and expect same blocks, one new hit, no misses
225      assertTrue(Bytes.equals(table.get(new Get(ROW)).value(), data));
226      iterator = cache.iterator();
227      iterateBlockCache(cache, iterator);
228      // Check how this miss is happening
229      // insert a second column, read the row, no new blocks, 3 new hits
230      byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
231      byte[] data2 = Bytes.add(data, data);
232      put = new Put(ROW);
233      put.addColumn(FAMILY, QUALIFIER2, data2);
234      table.put(put);
235      Result r = table.get(new Get(ROW));
236      assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER), data));
237      assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER2), data2));
238      iterator = cache.iterator();
239      iterateBlockCache(cache, iterator);
240      // flush, one new block
241      System.out.println("Flushing cache");
242      region.flush(true);
243      iterator = cache.iterator();
244      iterateBlockCache(cache, iterator);
245      // compact, net minus two blocks, two hits, no misses
246      System.out.println("Compacting");
247      assertEquals(2, store.getStorefilesCount());
248      store.triggerMajorCompaction();
249      region.compact(true);
250      waitForStoreFileCount(store, 1, 10000); // wait 10 seconds max
251      assertEquals(1, store.getStorefilesCount());
252      iterator = cache.iterator();
253      iterateBlockCache(cache, iterator);
254      // read the row, this should be a cache miss because we don't cache data
255      // blocks on compaction
256      r = table.get(new Get(ROW));
257      assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER), data));
258      assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER2), data2));
259      iterator = cache.iterator();
260      iterateBlockCache(cache, iterator);
261    } finally {
262      if (table != null) {
263        table.close();
264      }
265    }
266  }
267
268  @Test
269  public void testParallelGetsAndScans() throws IOException, InterruptedException {
270    Table table = null;
271    try {
272      latch = new CountDownLatch(2);
273      // Check if get() returns blocks on its close() itself
274      getLatch = new CountDownLatch(1);
275      final TableName tableName = TableName.valueOf(name.getMethodName());
276      // Create KV that will give you two blocks
277      // Create a table with block size as 1024
278      table = TEST_UTIL.createTable(tableName, FAMILIES_1, 1, 1024,
279          CustomInnerRegionObserver.class.getName());
280      // get the block cache and region
281      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
282      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
283      HRegion region =
284          TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
285      HStore store = region.getStores().iterator().next();
286      CacheConfig cacheConf = store.getCacheConfig();
287      cacheConf.setCacheDataOnWrite(true);
288      cacheConf.setEvictOnClose(true);
289      BlockCache cache = cacheConf.getBlockCache();
290
291      insertData(table);
292      // flush the data
293      System.out.println("Flushing cache");
294      // Should create one Hfile with 2 blocks
295      region.flush(true);
296      // Create three sets of scan
297      CustomInnerRegionObserver.waitForGets.set(true);
298      ScanThread[] scanThreads = initiateScan(table, false);
299      // Create three sets of gets
300      GetThread[] getThreads = initiateGet(table, false, false);
301      checkForBlockEviction(cache, false, false);
302      CustomInnerRegionObserver.waitForGets.set(false);
303      checkForBlockEviction(cache, false, false);
304      for (GetThread thread : getThreads) {
305        thread.join();
306      }
307      // Verify whether the gets have returned the blocks that it had
308      CustomInnerRegionObserver.waitForGets.set(true);
309      // giving some time for the block to be decremented
310      checkForBlockEviction(cache, true, false);
311      getLatch.countDown();
312      for (ScanThread thread : scanThreads) {
313        thread.join();
314      }
315      System.out.println("Scans should have returned the bloks");
316      // Check with either true or false
317      CustomInnerRegionObserver.waitForGets.set(false);
318      // The scan should also have released the blocks by now
319      checkForBlockEviction(cache, true, true);
320    } finally {
321      if (table != null) {
322        table.close();
323      }
324    }
325  }
326
327  @Test
328  public void testGetWithCellsInDifferentFiles() throws IOException, InterruptedException {
329    Table table = null;
330    try {
331      latch = new CountDownLatch(1);
332      // Check if get() returns blocks on its close() itself
333      getLatch = new CountDownLatch(1);
334      final TableName tableName = TableName.valueOf(name.getMethodName());
335      // Create KV that will give you two blocks
336      // Create a table with block size as 1024
337      table = TEST_UTIL.createTable(tableName, FAMILIES_1, 1, 1024,
338          CustomInnerRegionObserver.class.getName());
339      // get the block cache and region
340      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
341      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
342      HRegion region =
343          TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
344      HStore store = region.getStores().iterator().next();
345      CacheConfig cacheConf = store.getCacheConfig();
346      cacheConf.setCacheDataOnWrite(true);
347      cacheConf.setEvictOnClose(true);
348      BlockCache cache = cacheConf.getBlockCache();
349
350      Put put = new Put(ROW);
351      put.addColumn(FAMILY, QUALIFIER, data);
352      table.put(put);
353      region.flush(true);
354      put = new Put(ROW1);
355      put.addColumn(FAMILY, QUALIFIER, data);
356      table.put(put);
357      region.flush(true);
358      byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
359      put = new Put(ROW);
360      put.addColumn(FAMILY, QUALIFIER2, data2);
361      table.put(put);
362      region.flush(true);
363      // flush the data
364      System.out.println("Flushing cache");
365      // Should create one Hfile with 2 blocks
366      CustomInnerRegionObserver.waitForGets.set(true);
367      // Create three sets of gets
368      GetThread[] getThreads = initiateGet(table, false, false);
369      Thread.sleep(200);
370      CustomInnerRegionObserver.getCdl().get().countDown();
371      for (GetThread thread : getThreads) {
372        thread.join();
373      }
374      // Verify whether the gets have returned the blocks that it had
375      CustomInnerRegionObserver.waitForGets.set(true);
376      // giving some time for the block to be decremented
377      checkForBlockEviction(cache, true, false);
378      getLatch.countDown();
379      System.out.println("Gets should have returned the bloks");
380    } finally {
381      if (table != null) {
382        table.close();
383      }
384    }
385  }
386
387  @Test
388  // TODO : check how block index works here
389  public void testGetsWithMultiColumnsAndExplicitTracker()
390      throws IOException, InterruptedException {
391    Table table = null;
392    try {
393      latch = new CountDownLatch(1);
394      // Check if get() returns blocks on its close() itself
395      getLatch = new CountDownLatch(1);
396      final TableName tableName = TableName.valueOf(name.getMethodName());
397      // Create KV that will give you two blocks
398      // Create a table with block size as 1024
399      table = TEST_UTIL.createTable(tableName, FAMILIES_1, 1, 1024,
400          CustomInnerRegionObserver.class.getName());
401      // get the block cache and region
402      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
403      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
404      HRegion region =
405          TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
406      BlockCache cache = setCacheProperties(region);
407      Put put = new Put(ROW);
408      put.addColumn(FAMILY, QUALIFIER, data);
409      table.put(put);
410      region.flush(true);
411      put = new Put(ROW1);
412      put.addColumn(FAMILY, QUALIFIER, data);
413      table.put(put);
414      region.flush(true);
415      for (int i = 1; i < 10; i++) {
416        put = new Put(ROW);
417        put.addColumn(FAMILY, Bytes.toBytes("testQualifier" + i), data2);
418        table.put(put);
419        if (i % 2 == 0) {
420          region.flush(true);
421        }
422      }
423      byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
424      put = new Put(ROW);
425      put.addColumn(FAMILY, QUALIFIER2, data2);
426      table.put(put);
427      region.flush(true);
428      // flush the data
429      System.out.println("Flushing cache");
430      // Should create one Hfile with 2 blocks
431      CustomInnerRegionObserver.waitForGets.set(true);
432      // Create three sets of gets
433      GetThread[] getThreads = initiateGet(table, true, false);
434      Thread.sleep(200);
435      Iterator<CachedBlock> iterator = cache.iterator();
436      boolean usedBlocksFound = false;
437      int refCount = 0;
438      int noOfBlocksWithRef = 0;
439      while (iterator.hasNext()) {
440        CachedBlock next = iterator.next();
441        BlockCacheKey cacheKey = new BlockCacheKey(next.getFilename(), next.getOffset());
442        if (cache instanceof BucketCache) {
443          refCount = ((BucketCache) cache).getRefCount(cacheKey);
444        } else if (cache instanceof CombinedBlockCache) {
445          refCount = ((CombinedBlockCache) cache).getRefCount(cacheKey);
446        } else {
447          continue;
448        }
449        if (refCount != 0) {
450          // Blocks will be with count 3
451          System.out.println("The refCount is " + refCount);
452          assertEquals(NO_OF_THREADS, refCount);
453          usedBlocksFound = true;
454          noOfBlocksWithRef++;
455        }
456      }
457      assertTrue(usedBlocksFound);
458      // the number of blocks referred
459      assertEquals(10, noOfBlocksWithRef);
460      CustomInnerRegionObserver.getCdl().get().countDown();
461      for (GetThread thread : getThreads) {
462        thread.join();
463      }
464      // Verify whether the gets have returned the blocks that it had
465      CustomInnerRegionObserver.waitForGets.set(true);
466      // giving some time for the block to be decremented
467      checkForBlockEviction(cache, true, false);
468      getLatch.countDown();
469      System.out.println("Gets should have returned the bloks");
470    } finally {
471      if (table != null) {
472        table.close();
473      }
474    }
475  }
476
477  @Test
478  public void testGetWithMultipleColumnFamilies() throws IOException, InterruptedException {
479    Table table = null;
480    try {
481      latch = new CountDownLatch(1);
482      // Check if get() returns blocks on its close() itself
483      getLatch = new CountDownLatch(1);
484      final TableName tableName = TableName.valueOf(name.getMethodName());
485      // Create KV that will give you two blocks
486      // Create a table with block size as 1024
487      byte[][] fams = new byte[10][];
488      fams[0] = FAMILY;
489      for (int i = 1; i < 10; i++) {
490        fams[i] = (Bytes.toBytes("testFamily" + i));
491      }
492      table = TEST_UTIL.createTable(tableName, fams, 1, 1024,
493          CustomInnerRegionObserver.class.getName());
494      // get the block cache and region
495      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
496      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
497      HRegion region =
498          TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
499      BlockCache cache = setCacheProperties(region);
500
501      Put put = new Put(ROW);
502      put.addColumn(FAMILY, QUALIFIER, data);
503      table.put(put);
504      region.flush(true);
505      put = new Put(ROW1);
506      put.addColumn(FAMILY, QUALIFIER, data);
507      table.put(put);
508      region.flush(true);
509      for (int i = 1; i < 10; i++) {
510        put = new Put(ROW);
511        put.addColumn(Bytes.toBytes("testFamily" + i), Bytes.toBytes("testQualifier" + i), data2);
512        table.put(put);
513        if (i % 2 == 0) {
514          region.flush(true);
515        }
516      }
517      region.flush(true);
518      byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
519      put = new Put(ROW);
520      put.addColumn(FAMILY, QUALIFIER2, data2);
521      table.put(put);
522      region.flush(true);
523      // flush the data
524      System.out.println("Flushing cache");
525      // Should create one Hfile with 2 blocks
526      CustomInnerRegionObserver.waitForGets.set(true);
527      // Create three sets of gets
528      GetThread[] getThreads = initiateGet(table, true, true);
529      Thread.sleep(200);
530      Iterator<CachedBlock> iterator = cache.iterator();
531      boolean usedBlocksFound = false;
532      int refCount = 0;
533      int noOfBlocksWithRef = 0;
534      while (iterator.hasNext()) {
535        CachedBlock next = iterator.next();
536        BlockCacheKey cacheKey = new BlockCacheKey(next.getFilename(), next.getOffset());
537        if (cache instanceof BucketCache) {
538          refCount = ((BucketCache) cache).getRefCount(cacheKey);
539        } else if (cache instanceof CombinedBlockCache) {
540          refCount = ((CombinedBlockCache) cache).getRefCount(cacheKey);
541        } else {
542          continue;
543        }
544        if (refCount != 0) {
545          // Blocks will be with count 3
546          System.out.println("The refCount is " + refCount);
547          assertEquals(NO_OF_THREADS, refCount);
548          usedBlocksFound = true;
549          noOfBlocksWithRef++;
550        }
551      }
552      assertTrue(usedBlocksFound);
553      // the number of blocks referred
554      assertEquals(3, noOfBlocksWithRef);
555      CustomInnerRegionObserver.getCdl().get().countDown();
556      for (GetThread thread : getThreads) {
557        thread.join();
558      }
559      // Verify whether the gets have returned the blocks that it had
560      CustomInnerRegionObserver.waitForGets.set(true);
561      // giving some time for the block to be decremented
562      checkForBlockEviction(cache, true, false);
563      getLatch.countDown();
564      System.out.println("Gets should have returned the bloks");
565    } finally {
566      if (table != null) {
567        table.close();
568      }
569    }
570  }
571
572  @Test
573  public void testBlockRefCountAfterSplits() throws IOException, InterruptedException {
574    Table table = null;
575    try {
576      final TableName tableName = TableName.valueOf(name.getMethodName());
577      table = TEST_UTIL.createTable(tableName, FAMILIES_1, 1, 1024);
578      // get the block cache and region
579      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
580      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
581      HRegion region =
582          TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
583      HStore store = region.getStores().iterator().next();
584      CacheConfig cacheConf = store.getCacheConfig();
585      cacheConf.setEvictOnClose(true);
586      BlockCache cache = cacheConf.getBlockCache();
587
588      Put put = new Put(ROW);
589      put.addColumn(FAMILY, QUALIFIER, data);
590      table.put(put);
591      region.flush(true);
592      put = new Put(ROW1);
593      put.addColumn(FAMILY, QUALIFIER, data);
594      table.put(put);
595      region.flush(true);
596      byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
597      put = new Put(ROW2);
598      put.addColumn(FAMILY, QUALIFIER2, data2);
599      table.put(put);
600      put = new Put(ROW3);
601      put.addColumn(FAMILY, QUALIFIER2, data2);
602      table.put(put);
603      region.flush(true);
604      LOG.info("About to SPLIT on " + Bytes.toString(ROW1));
605      TEST_UTIL.getAdmin().split(tableName, ROW1);
606      // Wait for splits
607      Collection<ServerName> regionServers = TEST_UTIL.getAdmin().getRegionServers();
608      Iterator<ServerName> serverItr = regionServers.iterator();
609      serverItr.hasNext();
610      ServerName rs = serverItr.next();
611      List<RegionInfo> onlineRegions = TEST_UTIL.getAdmin().getRegions(rs);
612      while (onlineRegions.size() != 2) {
613        onlineRegions = TEST_UTIL.getAdmin().getRegions(rs);
614        Thread.sleep(100);
615        LOG.info("Waiting on SPLIT to complete...");
616      }
617      region.compact(true);
618      Iterator<CachedBlock> iterator = cache.iterator();
619      // Though the split had created the HalfStorefileReader - the firstkey and lastkey scanners
620      // should be closed inorder to return those blocks
621      iterateBlockCache(cache, iterator);
622    } finally {
623      if (table != null) {
624        table.close();
625      }
626    }
627  }
628
629  @Test
630  public void testMultiGets() throws IOException, InterruptedException {
631    Table table = null;
632    try {
633      latch = new CountDownLatch(2);
634      // Check if get() returns blocks on its close() itself
635      getLatch = new CountDownLatch(1);
636      final TableName tableName = TableName.valueOf(name.getMethodName());
637      // Create KV that will give you two blocks
638      // Create a table with block size as 1024
639      table = TEST_UTIL.createTable(tableName, FAMILIES_1, 1, 1024,
640          CustomInnerRegionObserver.class.getName());
641      // get the block cache and region
642      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
643      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
644      HRegion region =
645          TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
646      HStore store = region.getStores().iterator().next();
647      CacheConfig cacheConf = store.getCacheConfig();
648      cacheConf.setCacheDataOnWrite(true);
649      cacheConf.setEvictOnClose(true);
650      BlockCache cache = cacheConf.getBlockCache();
651
652      Put put = new Put(ROW);
653      put.addColumn(FAMILY, QUALIFIER, data);
654      table.put(put);
655      region.flush(true);
656      put = new Put(ROW1);
657      put.addColumn(FAMILY, QUALIFIER, data);
658      table.put(put);
659      region.flush(true);
660      byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
661      put = new Put(ROW);
662      put.addColumn(FAMILY, QUALIFIER2, data2);
663      table.put(put);
664      region.flush(true);
665      // flush the data
666      System.out.println("Flushing cache");
667      // Should create one Hfile with 2 blocks
668      CustomInnerRegionObserver.waitForGets.set(true);
669      // Create three sets of gets
670      MultiGetThread[] getThreads = initiateMultiGet(table);
671      Thread.sleep(200);
672      int refCount;
673      Iterator<CachedBlock> iterator = cache.iterator();
674      boolean foundNonZeroBlock = false;
675      while (iterator.hasNext()) {
676        CachedBlock next = iterator.next();
677        BlockCacheKey cacheKey = new BlockCacheKey(next.getFilename(), next.getOffset());
678        if (cache instanceof BucketCache) {
679          refCount = ((BucketCache) cache).getRefCount(cacheKey);
680        } else if (cache instanceof CombinedBlockCache) {
681          refCount = ((CombinedBlockCache) cache).getRefCount(cacheKey);
682        } else {
683          continue;
684        }
685        if (refCount != 0) {
686          assertEquals(NO_OF_THREADS, refCount);
687          foundNonZeroBlock = true;
688        }
689      }
690      assertTrue("Should have found nonzero ref count block",foundNonZeroBlock);
691      CustomInnerRegionObserver.getCdl().get().countDown();
692      CustomInnerRegionObserver.getCdl().get().countDown();
693      for (MultiGetThread thread : getThreads) {
694        thread.join();
695      }
696      // Verify whether the gets have returned the blocks that it had
697      CustomInnerRegionObserver.waitForGets.set(true);
698      // giving some time for the block to be decremented
699      iterateBlockCache(cache, iterator);
700      getLatch.countDown();
701      System.out.println("Gets should have returned the bloks");
702    } finally {
703      if (table != null) {
704        table.close();
705      }
706    }
707  }
708  @Test
709  public void testScanWithMultipleColumnFamilies() throws IOException, InterruptedException {
710    Table table = null;
711    try {
712      latch = new CountDownLatch(1);
713      // Check if get() returns blocks on its close() itself
714      final TableName tableName = TableName.valueOf(name.getMethodName());
715      // Create KV that will give you two blocks
716      // Create a table with block size as 1024
717      byte[][] fams = new byte[10][];
718      fams[0] = FAMILY;
719      for (int i = 1; i < 10; i++) {
720        fams[i] = (Bytes.toBytes("testFamily" + i));
721      }
722      table = TEST_UTIL.createTable(tableName, fams, 1, 1024,
723          CustomInnerRegionObserver.class.getName());
724      // get the block cache and region
725      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
726      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
727      HRegion region =
728          TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
729      BlockCache cache = setCacheProperties(region);
730
731      Put put = new Put(ROW);
732      put.addColumn(FAMILY, QUALIFIER, data);
733      table.put(put);
734      region.flush(true);
735      put = new Put(ROW1);
736      put.addColumn(FAMILY, QUALIFIER, data);
737      table.put(put);
738      region.flush(true);
739      for (int i = 1; i < 10; i++) {
740        put = new Put(ROW);
741        put.addColumn(Bytes.toBytes("testFamily" + i), Bytes.toBytes("testQualifier" + i), data2);
742        table.put(put);
743        if (i % 2 == 0) {
744          region.flush(true);
745        }
746      }
747      region.flush(true);
748      byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
749      put = new Put(ROW);
750      put.addColumn(FAMILY, QUALIFIER2, data2);
751      table.put(put);
752      region.flush(true);
753      // flush the data
754      System.out.println("Flushing cache");
755      // Should create one Hfile with 2 blocks
756      // Create three sets of gets
757      ScanThread[] scanThreads = initiateScan(table, true);
758      Thread.sleep(200);
759      Iterator<CachedBlock> iterator = cache.iterator();
760      boolean usedBlocksFound = false;
761      int refCount = 0;
762      int noOfBlocksWithRef = 0;
763      while (iterator.hasNext()) {
764        CachedBlock next = iterator.next();
765        BlockCacheKey cacheKey = new BlockCacheKey(next.getFilename(), next.getOffset());
766        if (cache instanceof BucketCache) {
767          refCount = ((BucketCache) cache).getRefCount(cacheKey);
768        } else if (cache instanceof CombinedBlockCache) {
769          refCount = ((CombinedBlockCache) cache).getRefCount(cacheKey);
770        } else {
771          continue;
772        }
773        if (refCount != 0) {
774          // Blocks will be with count 3
775          System.out.println("The refCount is " + refCount);
776          assertEquals(NO_OF_THREADS, refCount);
777          usedBlocksFound = true;
778          noOfBlocksWithRef++;
779        }
780      }
781      assertTrue(usedBlocksFound);
782      // the number of blocks referred
783      assertEquals(12, noOfBlocksWithRef);
784      CustomInnerRegionObserver.getCdl().get().countDown();
785      for (ScanThread thread : scanThreads) {
786        thread.join();
787      }
788      // giving some time for the block to be decremented
789      checkForBlockEviction(cache, true, false);
790    } finally {
791      if (table != null) {
792        table.close();
793      }
794    }
795  }
796
797  private BlockCache setCacheProperties(HRegion region) {
798    Iterator<HStore> strItr = region.getStores().iterator();
799    BlockCache cache = null;
800    while (strItr.hasNext()) {
801      HStore store = strItr.next();
802      CacheConfig cacheConf = store.getCacheConfig();
803      cacheConf.setCacheDataOnWrite(true);
804      cacheConf.setEvictOnClose(true);
805      // Use the last one
806      cache = cacheConf.getBlockCache();
807    }
808    return cache;
809  }
810
811  @Test
812  public void testParallelGetsAndScanWithWrappedRegionScanner() throws IOException,
813      InterruptedException {
814    Table table = null;
815    try {
816      latch = new CountDownLatch(2);
817      // Check if get() returns blocks on its close() itself
818      getLatch = new CountDownLatch(1);
819      final TableName tableName = TableName.valueOf(name.getMethodName());
820      // Create KV that will give you two blocks
821      // Create a table with block size as 1024
822      table = TEST_UTIL.createTable(tableName, FAMILIES_1, 1, 1024,
823          CustomInnerRegionObserverWrapper.class.getName());
824      // get the block cache and region
825      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
826      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
827      HRegion region =
828          TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
829      HStore store = region.getStores().iterator().next();
830      CacheConfig cacheConf = store.getCacheConfig();
831      cacheConf.setCacheDataOnWrite(true);
832      cacheConf.setEvictOnClose(true);
833      BlockCache cache = cacheConf.getBlockCache();
834
835      // insert data. 2 Rows are added
836      insertData(table);
837      // flush the data
838      System.out.println("Flushing cache");
839      // Should create one Hfile with 2 blocks
840      region.flush(true);
841      // CustomInnerRegionObserver.sleepTime.set(5000);
842      // Create three sets of scan
843      CustomInnerRegionObserver.waitForGets.set(true);
844      ScanThread[] scanThreads = initiateScan(table, false);
845      // Create three sets of gets
846      GetThread[] getThreads = initiateGet(table, false, false);
847      // The block would have been decremented for the scan case as it was
848      // wrapped
849      // before even the postNext hook gets executed.
850      // giving some time for the block to be decremented
851      Thread.sleep(100);
852      CustomInnerRegionObserver.waitForGets.set(false);
853      checkForBlockEviction(cache, false, false);
854      // countdown the latch
855      CustomInnerRegionObserver.getCdl().get().countDown();
856      for (GetThread thread : getThreads) {
857        thread.join();
858      }
859      getLatch.countDown();
860      for (ScanThread thread : scanThreads) {
861        thread.join();
862      }
863    } finally {
864      if (table != null) {
865        table.close();
866      }
867    }
868  }
869
870  @Test
871  public void testScanWithCompaction() throws IOException, InterruptedException {
872    testScanWithCompactionInternals(name.getMethodName(), false);
873  }
874
875  @Test
876  public void testReverseScanWithCompaction() throws IOException, InterruptedException {
877    testScanWithCompactionInternals(name.getMethodName(), true);
878  }
879
880  private void testScanWithCompactionInternals(String tableNameStr, boolean reversed)
881      throws IOException, InterruptedException {
882    Table table = null;
883    try {
884      latch = new CountDownLatch(1);
885      compactionLatch = new CountDownLatch(1);
886      TableName tableName = TableName.valueOf(tableNameStr);
887      // Create a table with block size as 1024
888      table = TEST_UTIL.createTable(tableName, FAMILIES_1, 1, 1024,
889          CustomInnerRegionObserverWrapper.class.getName());
890      // get the block cache and region
891      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
892      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
893      HRegion region =
894          TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
895      HStore store = region.getStores().iterator().next();
896      CacheConfig cacheConf = store.getCacheConfig();
897      cacheConf.setCacheDataOnWrite(true);
898      cacheConf.setEvictOnClose(true);
899      BlockCache cache = cacheConf.getBlockCache();
900
901      // insert data. 2 Rows are added
902      Put put = new Put(ROW);
903      put.addColumn(FAMILY, QUALIFIER, data);
904      table.put(put);
905      put = new Put(ROW1);
906      put.addColumn(FAMILY, QUALIFIER, data);
907      table.put(put);
908      assertTrue(Bytes.equals(table.get(new Get(ROW)).value(), data));
909      // Should create one Hfile with 2 blocks
910      region.flush(true);
911      // read the data and expect same blocks, one new hit, no misses
912      int refCount = 0;
913      // Check how this miss is happening
914      // insert a second column, read the row, no new blocks, 3 new hits
915      byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
916      byte[] data2 = Bytes.add(data, data);
917      put = new Put(ROW);
918      put.addColumn(FAMILY, QUALIFIER2, data2);
919      table.put(put);
920      // flush, one new block
921      System.out.println("Flushing cache");
922      region.flush(true);
923      Iterator<CachedBlock> iterator = cache.iterator();
924      iterateBlockCache(cache, iterator);
925      // Create three sets of scan
926      ScanThread[] scanThreads = initiateScan(table, reversed);
927      Thread.sleep(100);
928      iterator = cache.iterator();
929      boolean usedBlocksFound = false;
930      while (iterator.hasNext()) {
931        CachedBlock next = iterator.next();
932        BlockCacheKey cacheKey = new BlockCacheKey(next.getFilename(), next.getOffset());
933        if (cache instanceof BucketCache) {
934          refCount = ((BucketCache) cache).getRefCount(cacheKey);
935        } else if (cache instanceof CombinedBlockCache) {
936          refCount = ((CombinedBlockCache) cache).getRefCount(cacheKey);
937        } else {
938          continue;
939        }
940        if (refCount != 0) {
941          // Blocks will be with count 3
942          assertEquals(NO_OF_THREADS, refCount);
943          usedBlocksFound = true;
944        }
945      }
946      assertTrue("Blocks with non zero ref count should be found ", usedBlocksFound);
947      usedBlocksFound = false;
948      System.out.println("Compacting");
949      assertEquals(2, store.getStorefilesCount());
950      store.triggerMajorCompaction();
951      region.compact(true);
952      waitForStoreFileCount(store, 1, 10000); // wait 10 seconds max
953      assertEquals(1, store.getStorefilesCount());
954      // Even after compaction is done we will have some blocks that cannot
955      // be evicted this is because the scan is still referencing them
956      iterator = cache.iterator();
957      while (iterator.hasNext()) {
958        CachedBlock next = iterator.next();
959        BlockCacheKey cacheKey = new BlockCacheKey(next.getFilename(), next.getOffset());
960        if (cache instanceof BucketCache) {
961          refCount = ((BucketCache) cache).getRefCount(cacheKey);
962        } else if (cache instanceof CombinedBlockCache) {
963          refCount = ((CombinedBlockCache) cache).getRefCount(cacheKey);
964        } else {
965          continue;
966        }
967        if (refCount != 0) {
968          // Blocks will be with count 3 as they are not yet cleared
969          assertEquals(NO_OF_THREADS, refCount);
970          usedBlocksFound = true;
971        }
972      }
973      assertTrue("Blocks with non zero ref count should be found ", usedBlocksFound);
974      // Should not throw exception
975      compactionLatch.countDown();
976      latch.countDown();
977      for (ScanThread thread : scanThreads) {
978        thread.join();
979      }
980      // by this time all blocks should have been evicted
981      iterator = cache.iterator();
982      iterateBlockCache(cache, iterator);
983      Result r = table.get(new Get(ROW));
984      assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER), data));
985      assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER2), data2));
986      // The gets would be working on new blocks
987      iterator = cache.iterator();
988      iterateBlockCache(cache, iterator);
989    } finally {
990      if (table != null) {
991        table.close();
992      }
993    }
994  }
995
996  @Test
997  public void testBlockEvictionAfterHBASE13082WithCompactionAndFlush()
998      throws IOException, InterruptedException {
999    // do flush and scan in parallel
1000    Table table = null;
1001    try {
1002      latch = new CountDownLatch(1);
1003      compactionLatch = new CountDownLatch(1);
1004      final TableName tableName = TableName.valueOf(name.getMethodName());
1005      // Create a table with block size as 1024
1006      table = TEST_UTIL.createTable(tableName, FAMILIES_1, 1, 1024,
1007          CustomInnerRegionObserverWrapper.class.getName());
1008      // get the block cache and region
1009      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
1010      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
1011      HRegion region =
1012          TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
1013      HStore store = region.getStores().iterator().next();
1014      CacheConfig cacheConf = store.getCacheConfig();
1015      cacheConf.setCacheDataOnWrite(true);
1016      cacheConf.setEvictOnClose(true);
1017      BlockCache cache = cacheConf.getBlockCache();
1018
1019      // insert data. 2 Rows are added
1020      Put put = new Put(ROW);
1021      put.addColumn(FAMILY, QUALIFIER, data);
1022      table.put(put);
1023      put = new Put(ROW1);
1024      put.addColumn(FAMILY, QUALIFIER, data);
1025      table.put(put);
1026      assertTrue(Bytes.equals(table.get(new Get(ROW)).value(), data));
1027      // Should create one Hfile with 2 blocks
1028      region.flush(true);
1029      // read the data and expect same blocks, one new hit, no misses
1030      int refCount = 0;
1031      // Check how this miss is happening
1032      // insert a second column, read the row, no new blocks, 3 new hits
1033      byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
1034      byte[] data2 = Bytes.add(data, data);
1035      put = new Put(ROW);
1036      put.addColumn(FAMILY, QUALIFIER2, data2);
1037      table.put(put);
1038      // flush, one new block
1039      System.out.println("Flushing cache");
1040      region.flush(true);
1041      Iterator<CachedBlock> iterator = cache.iterator();
1042      iterateBlockCache(cache, iterator);
1043      // Create three sets of scan
1044      ScanThread[] scanThreads = initiateScan(table, false);
1045      Thread.sleep(100);
1046      iterator = cache.iterator();
1047      boolean usedBlocksFound = false;
1048      while (iterator.hasNext()) {
1049        CachedBlock next = iterator.next();
1050        BlockCacheKey cacheKey = new BlockCacheKey(next.getFilename(), next.getOffset());
1051        if (cache instanceof BucketCache) {
1052          refCount = ((BucketCache) cache).getRefCount(cacheKey);
1053        } else if (cache instanceof CombinedBlockCache) {
1054          refCount = ((CombinedBlockCache) cache).getRefCount(cacheKey);
1055        } else {
1056          continue;
1057        }
1058        if (refCount != 0) {
1059          // Blocks will be with count 3
1060          assertEquals(NO_OF_THREADS, refCount);
1061          usedBlocksFound = true;
1062        }
1063      }
1064      // Make a put and do a flush
1065      QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
1066      data2 = Bytes.add(data, data);
1067      put = new Put(ROW1);
1068      put.addColumn(FAMILY, QUALIFIER2, data2);
1069      table.put(put);
1070      // flush, one new block
1071      System.out.println("Flushing cache");
1072      region.flush(true);
1073      assertTrue("Blocks with non zero ref count should be found ", usedBlocksFound);
1074      usedBlocksFound = false;
1075      System.out.println("Compacting");
1076      assertEquals(3, store.getStorefilesCount());
1077      store.triggerMajorCompaction();
1078      region.compact(true);
1079      waitForStoreFileCount(store, 1, 10000); // wait 10 seconds max
1080      assertEquals(1, store.getStorefilesCount());
1081      // Even after compaction is done we will have some blocks that cannot
1082      // be evicted this is because the scan is still referencing them
1083      iterator = cache.iterator();
1084      while (iterator.hasNext()) {
1085        CachedBlock next = iterator.next();
1086        BlockCacheKey cacheKey = new BlockCacheKey(next.getFilename(), next.getOffset());
1087        if (cache instanceof BucketCache) {
1088          refCount = ((BucketCache) cache).getRefCount(cacheKey);
1089        } else if (cache instanceof CombinedBlockCache) {
1090          refCount = ((CombinedBlockCache) cache).getRefCount(cacheKey);
1091        } else {
1092          continue;
1093        }
1094        if (refCount != 0) {
1095          // Blocks will be with count 3 as they are not yet cleared
1096          assertEquals(NO_OF_THREADS, refCount);
1097          usedBlocksFound = true;
1098        }
1099      }
1100      assertTrue("Blocks with non zero ref count should be found ", usedBlocksFound);
1101      // Should not throw exception
1102      compactionLatch.countDown();
1103      latch.countDown();
1104      for (ScanThread thread : scanThreads) {
1105        thread.join();
1106      }
1107      // by this time all blocks should have been evicted
1108      iterator = cache.iterator();
1109      // Since a flush and compaction happened after a scan started
1110      // we need to ensure that all the original blocks of the compacted file
1111      // is also removed.
1112      iterateBlockCache(cache, iterator);
1113      Result r = table.get(new Get(ROW));
1114      assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER), data));
1115      assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER2), data2));
1116      // The gets would be working on new blocks
1117      iterator = cache.iterator();
1118      iterateBlockCache(cache, iterator);
1119    } finally {
1120      if (table != null) {
1121        table.close();
1122      }
1123    }
1124  }
1125
1126
1127  @Test
1128  public void testScanWithException() throws IOException, InterruptedException {
1129    Table table = null;
1130    try {
1131      latch = new CountDownLatch(1);
1132      exceptionLatch = new CountDownLatch(1);
1133      final TableName tableName = TableName.valueOf(name.getMethodName());
1134      // Create KV that will give you two blocks
1135      // Create a table with block size as 1024
1136      table = TEST_UTIL.createTable(tableName, FAMILIES_1, 1, 1024,
1137          CustomInnerRegionObserverWrapper.class.getName());
1138      // get the block cache and region
1139      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
1140      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
1141      HRegion region =
1142          TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
1143      HStore store = region.getStores().iterator().next();
1144      CacheConfig cacheConf = store.getCacheConfig();
1145      cacheConf.setCacheDataOnWrite(true);
1146      cacheConf.setEvictOnClose(true);
1147      BlockCache cache = cacheConf.getBlockCache();
1148      // insert data. 2 Rows are added
1149      insertData(table);
1150      // flush the data
1151      System.out.println("Flushing cache");
1152      // Should create one Hfile with 2 blocks
1153      region.flush(true);
1154      // CustomInnerRegionObserver.sleepTime.set(5000);
1155      CustomInnerRegionObserver.throwException.set(true);
1156      ScanThread[] scanThreads = initiateScan(table, false);
1157      // The block would have been decremented for the scan case as it was
1158      // wrapped
1159      // before even the postNext hook gets executed.
1160      // giving some time for the block to be decremented
1161      Thread.sleep(100);
1162      Iterator<CachedBlock> iterator = cache.iterator();
1163      boolean usedBlocksFound = false;
1164      int refCount = 0;
1165      while (iterator.hasNext()) {
1166        CachedBlock next = iterator.next();
1167        BlockCacheKey cacheKey = new BlockCacheKey(next.getFilename(), next.getOffset());
1168        if (cache instanceof BucketCache) {
1169          refCount = ((BucketCache) cache).getRefCount(cacheKey);
1170        } else if (cache instanceof CombinedBlockCache) {
1171          refCount = ((CombinedBlockCache) cache).getRefCount(cacheKey);
1172        } else {
1173          continue;
1174        }
1175        if (refCount != 0) {
1176          // Blocks will be with count 3
1177          assertEquals(NO_OF_THREADS, refCount);
1178          usedBlocksFound = true;
1179        }
1180      }
1181      assertTrue(usedBlocksFound);
1182      exceptionLatch.countDown();
1183      // countdown the latch
1184      CustomInnerRegionObserver.getCdl().get().countDown();
1185      for (ScanThread thread : scanThreads) {
1186        thread.join();
1187      }
1188      iterator = cache.iterator();
1189      usedBlocksFound = false;
1190      refCount = 0;
1191      while (iterator.hasNext()) {
1192        CachedBlock next = iterator.next();
1193        BlockCacheKey cacheKey = new BlockCacheKey(next.getFilename(), next.getOffset());
1194        if (cache instanceof BucketCache) {
1195          refCount = ((BucketCache) cache).getRefCount(cacheKey);
1196        } else if (cache instanceof CombinedBlockCache) {
1197          refCount = ((CombinedBlockCache) cache).getRefCount(cacheKey);
1198        } else {
1199          continue;
1200        }
1201        if (refCount != 0) {
1202          // Blocks will be with count 3
1203          assertEquals(NO_OF_THREADS, refCount);
1204          usedBlocksFound = true;
1205        }
1206      }
1207      assertFalse(usedBlocksFound);
1208      // you should always see 0 ref count. since after HBASE-16604 we always recreate the scanner
1209      assertEquals(0, refCount);
1210    } finally {
1211      if (table != null) {
1212        table.close();
1213      }
1214    }
1215  }
1216
1217  private void iterateBlockCache(BlockCache cache, Iterator<CachedBlock> iterator) {
1218    int refCount;
1219    while (iterator.hasNext()) {
1220      CachedBlock next = iterator.next();
1221      BlockCacheKey cacheKey = new BlockCacheKey(next.getFilename(), next.getOffset());
1222      if (cache instanceof BucketCache) {
1223        refCount = ((BucketCache) cache).getRefCount(cacheKey);
1224      } else if (cache instanceof CombinedBlockCache) {
1225        refCount = ((CombinedBlockCache) cache).getRefCount(cacheKey);
1226      } else {
1227        continue;
1228      }
1229      assertEquals(0, refCount);
1230    }
1231  }
1232
1233  private void insertData(Table table) throws IOException {
1234    Put put = new Put(ROW);
1235    put.addColumn(FAMILY, QUALIFIER, data);
1236    table.put(put);
1237    put = new Put(ROW1);
1238    put.addColumn(FAMILY, QUALIFIER, data);
1239    table.put(put);
1240    byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
1241    put = new Put(ROW);
1242    put.addColumn(FAMILY, QUALIFIER2, data2);
1243    table.put(put);
1244  }
1245
1246  private ScanThread[] initiateScan(Table table, boolean reverse) throws IOException,
1247      InterruptedException {
1248    ScanThread[] scanThreads = new ScanThread[NO_OF_THREADS];
1249    for (int i = 0; i < NO_OF_THREADS; i++) {
1250      scanThreads[i] = new ScanThread(table, reverse);
1251    }
1252    for (ScanThread thread : scanThreads) {
1253      thread.start();
1254    }
1255    return scanThreads;
1256  }
1257
1258  private GetThread[] initiateGet(Table table, boolean tracker, boolean multipleCFs)
1259      throws IOException, InterruptedException {
1260    GetThread[] getThreads = new GetThread[NO_OF_THREADS];
1261    for (int i = 0; i < NO_OF_THREADS; i++) {
1262      getThreads[i] = new GetThread(table, tracker, multipleCFs);
1263    }
1264    for (GetThread thread : getThreads) {
1265      thread.start();
1266    }
1267    return getThreads;
1268  }
1269
1270  private MultiGetThread[] initiateMultiGet(Table table)
1271      throws IOException, InterruptedException {
1272    MultiGetThread[] multiGetThreads = new MultiGetThread[NO_OF_THREADS];
1273    for (int i = 0; i < NO_OF_THREADS; i++) {
1274      multiGetThreads[i] = new MultiGetThread(table);
1275    }
1276    for (MultiGetThread thread : multiGetThreads) {
1277      thread.start();
1278    }
1279    return multiGetThreads;
1280  }
1281
1282  private void checkForBlockEviction(BlockCache cache, boolean getClosed, boolean expectOnlyZero)
1283      throws InterruptedException {
1284    int counter = NO_OF_THREADS;
1285    if (CustomInnerRegionObserver.waitForGets.get()) {
1286      // Because only one row is selected, it has only 2 blocks
1287      counter = counter - 1;
1288      while (CustomInnerRegionObserver.countOfGets.get() < NO_OF_THREADS) {
1289        Thread.sleep(100);
1290      }
1291    } else {
1292      while (CustomInnerRegionObserver.countOfNext.get() < NO_OF_THREADS) {
1293        Thread.sleep(100);
1294      }
1295    }
1296    Iterator<CachedBlock> iterator = cache.iterator();
1297    int refCount = 0;
1298    while (iterator.hasNext()) {
1299      CachedBlock next = iterator.next();
1300      BlockCacheKey cacheKey = new BlockCacheKey(next.getFilename(), next.getOffset());
1301      if (cache instanceof BucketCache) {
1302        refCount = ((BucketCache) cache).getRefCount(cacheKey);
1303      } else if (cache instanceof CombinedBlockCache) {
1304        refCount = ((CombinedBlockCache) cache).getRefCount(cacheKey);
1305      } else {
1306        continue;
1307      }
1308      System.out.println(" the refcount is " + refCount + " block is " + cacheKey);
1309      if (CustomInnerRegionObserver.waitForGets.get()) {
1310        if (expectOnlyZero) {
1311          assertTrue(refCount == 0);
1312        }
1313        if (refCount != 0) {
1314          // Because the scan would have also touched up on these blocks but
1315          // it
1316          // would have touched
1317          // all 3
1318          if (getClosed) {
1319            // If get has closed only the scan's blocks would be available
1320            assertEquals(refCount, CustomInnerRegionObserver.countOfGets.get());
1321          } else {
1322              assertEquals(refCount, CustomInnerRegionObserver.countOfGets.get() + (NO_OF_THREADS));
1323          }
1324        }
1325      } else {
1326        // Because the get would have also touched up on these blocks but it
1327        // would have touched
1328        // upon only 2 additionally
1329        if (expectOnlyZero) {
1330          assertTrue(refCount == 0);
1331        }
1332        if (refCount != 0) {
1333          if (getLatch == null) {
1334            assertEquals(refCount, CustomInnerRegionObserver.countOfNext.get());
1335          } else {
1336            assertEquals(refCount, CustomInnerRegionObserver.countOfNext.get() + (NO_OF_THREADS));
1337          }
1338        }
1339      }
1340    }
1341    CustomInnerRegionObserver.getCdl().get().countDown();
1342  }
1343
1344  private static class MultiGetThread extends Thread {
1345    private final Table table;
1346    private final List<Get> gets = new ArrayList<>();
1347    public MultiGetThread(Table table) {
1348      this.table = table;
1349    }
1350    @Override
1351    public void run() {
1352      gets.add(new Get(ROW));
1353      gets.add(new Get(ROW1));
1354      try {
1355        CustomInnerRegionObserver.getCdl().set(latch);
1356        Result[] r = table.get(gets);
1357        assertTrue(Bytes.equals(r[0].getRow(), ROW));
1358        assertTrue(Bytes.equals(r[1].getRow(), ROW1));
1359      } catch (IOException e) {
1360      }
1361    }
1362  }
1363
1364  private static class GetThread extends Thread {
1365    private final Table table;
1366    private final boolean tracker;
1367    private final boolean multipleCFs;
1368
1369    public GetThread(Table table, boolean tracker, boolean multipleCFs) {
1370      this.table = table;
1371      this.tracker = tracker;
1372      this.multipleCFs = multipleCFs;
1373    }
1374
1375    @Override
1376    public void run() {
1377      try {
1378        initiateGet(table);
1379      } catch (IOException e) {
1380        // do nothing
1381      }
1382    }
1383
1384    private void initiateGet(Table table) throws IOException {
1385      Get get = new Get(ROW);
1386      if (tracker) {
1387        // Change this
1388        if (!multipleCFs) {
1389          get.addColumn(FAMILY, Bytes.toBytes("testQualifier" + 3));
1390          get.addColumn(FAMILY, Bytes.toBytes("testQualifier" + 8));
1391          get.addColumn(FAMILY, Bytes.toBytes("testQualifier" + 9));
1392          // Unknown key
1393          get.addColumn(FAMILY, Bytes.toBytes("testQualifier" + 900));
1394        } else {
1395          get.addColumn(Bytes.toBytes("testFamily" + 3), Bytes.toBytes("testQualifier" + 3));
1396          get.addColumn(Bytes.toBytes("testFamily" + 8), Bytes.toBytes("testQualifier" + 8));
1397          get.addColumn(Bytes.toBytes("testFamily" + 9), Bytes.toBytes("testQualifier" + 9));
1398          // Unknown key
1399          get.addColumn(Bytes.toBytes("testFamily" + 9), Bytes.toBytes("testQualifier" + 900));
1400        }
1401      }
1402      CustomInnerRegionObserver.getCdl().set(latch);
1403      Result r = table.get(get);
1404      System.out.println(r);
1405      if (!tracker) {
1406        assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER), data));
1407        assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER2), data2));
1408      } else {
1409        if (!multipleCFs) {
1410          assertTrue(Bytes.equals(r.getValue(FAMILY, Bytes.toBytes("testQualifier" + 3)), data2));
1411          assertTrue(Bytes.equals(r.getValue(FAMILY, Bytes.toBytes("testQualifier" + 8)), data2));
1412          assertTrue(Bytes.equals(r.getValue(FAMILY, Bytes.toBytes("testQualifier" + 9)), data2));
1413        } else {
1414          assertTrue(Bytes.equals(
1415              r.getValue(Bytes.toBytes("testFamily" + 3), Bytes.toBytes("testQualifier" + 3)),
1416              data2));
1417          assertTrue(Bytes.equals(
1418              r.getValue(Bytes.toBytes("testFamily" + 8), Bytes.toBytes("testQualifier" + 8)),
1419              data2));
1420          assertTrue(Bytes.equals(
1421              r.getValue(Bytes.toBytes("testFamily" + 9), Bytes.toBytes("testQualifier" + 9)),
1422              data2));
1423        }
1424      }
1425    }
1426  }
1427
1428  private static class ScanThread extends Thread {
1429    private final Table table;
1430    private final boolean reverse;
1431
1432    public ScanThread(Table table, boolean reverse) {
1433      this.table = table;
1434      this.reverse = reverse;
1435    }
1436
1437    @Override
1438    public void run() {
1439      try {
1440        initiateScan(table);
1441      } catch (IOException e) {
1442        // do nothing
1443      }
1444    }
1445
1446    private void initiateScan(Table table) throws IOException {
1447      Scan scan = new Scan();
1448      if (reverse) {
1449        scan.setReversed(true);
1450      }
1451      CustomInnerRegionObserver.getCdl().set(latch);
1452      ResultScanner resScanner = table.getScanner(scan);
1453      int i = (reverse ? ROWS.length - 1 : 0);
1454      boolean resultFound = false;
1455      for (Result result : resScanner) {
1456        resultFound = true;
1457        System.out.println(result);
1458        if (!reverse) {
1459          assertTrue(Bytes.equals(result.getRow(), ROWS[i]));
1460          i++;
1461        } else {
1462          assertTrue(Bytes.equals(result.getRow(), ROWS[i]));
1463          i--;
1464        }
1465      }
1466      assertTrue(resultFound);
1467    }
1468  }
1469
1470  private void waitForStoreFileCount(HStore store, int count, int timeout)
1471      throws InterruptedException {
1472    long start = System.currentTimeMillis();
1473    while (start + timeout > System.currentTimeMillis() && store.getStorefilesCount() != count) {
1474      Thread.sleep(100);
1475    }
1476    System.out.println("start=" + start + ", now=" + System.currentTimeMillis() + ", cur=" +
1477        store.getStorefilesCount());
1478    assertEquals(count, store.getStorefilesCount());
1479  }
1480
1481  private static class CustomScanner implements RegionScanner {
1482
1483    private RegionScanner delegate;
1484
1485    public CustomScanner(RegionScanner delegate) {
1486      this.delegate = delegate;
1487    }
1488
1489    @Override
1490    public boolean next(List<Cell> results) throws IOException {
1491      return delegate.next(results);
1492    }
1493
1494    @Override
1495    public boolean next(List<Cell> result, ScannerContext scannerContext) throws IOException {
1496      return delegate.next(result, scannerContext);
1497    }
1498
1499    @Override
1500    public boolean nextRaw(List<Cell> result) throws IOException {
1501      return delegate.nextRaw(result);
1502    }
1503
1504    @Override
1505    public boolean nextRaw(List<Cell> result, ScannerContext context) throws IOException {
1506      boolean nextRaw = delegate.nextRaw(result, context);
1507      if (compactionLatch != null && compactionLatch.getCount() > 0) {
1508        try {
1509          compactionLatch.await();
1510        } catch (InterruptedException ie) {
1511        }
1512      }
1513
1514      if (CustomInnerRegionObserver.throwException.get()) {
1515        if (exceptionLatch.getCount() > 0) {
1516          try {
1517            exceptionLatch.await();
1518          } catch (InterruptedException e) {
1519          }
1520          throw new IOException("throw exception");
1521        }
1522      }
1523      return nextRaw;
1524    }
1525
1526    @Override
1527    public void close() throws IOException {
1528      delegate.close();
1529    }
1530
1531    @Override
1532    public RegionInfo getRegionInfo() {
1533      return delegate.getRegionInfo();
1534    }
1535
1536    @Override
1537    public boolean isFilterDone() throws IOException {
1538      return delegate.isFilterDone();
1539    }
1540
1541    @Override
1542    public boolean reseek(byte[] row) throws IOException {
1543      return false;
1544    }
1545
1546    @Override
1547    public long getMaxResultSize() {
1548      return delegate.getMaxResultSize();
1549    }
1550
1551    @Override
1552    public long getMvccReadPoint() {
1553      return delegate.getMvccReadPoint();
1554    }
1555
1556    @Override
1557    public int getBatch() {
1558      return delegate.getBatch();
1559    }
1560  }
1561
1562  public static class CustomInnerRegionObserverWrapper extends CustomInnerRegionObserver {
1563    @Override
1564    public RegionScanner postScannerOpen(ObserverContext<RegionCoprocessorEnvironment> e,
1565        Scan scan, RegionScanner s) throws IOException {
1566      return new CustomScanner(s);
1567    }
1568  }
1569
1570  public static class CustomInnerRegionObserver implements RegionCoprocessor, RegionObserver {
1571    static final AtomicLong sleepTime = new AtomicLong(0);
1572    static final AtomicBoolean slowDownNext = new AtomicBoolean(false);
1573    static final AtomicInteger countOfNext = new AtomicInteger(0);
1574    static final AtomicInteger countOfGets = new AtomicInteger(0);
1575    static final AtomicBoolean waitForGets = new AtomicBoolean(false);
1576    static final AtomicBoolean throwException = new AtomicBoolean(false);
1577    private static final AtomicReference<CountDownLatch> cdl = new AtomicReference<>(
1578        new CountDownLatch(0));
1579
1580    @Override
1581    public Optional<RegionObserver> getRegionObserver() {
1582      return Optional.of(this);
1583    }
1584
1585    @Override
1586    public boolean postScannerNext(ObserverContext<RegionCoprocessorEnvironment> e,
1587        InternalScanner s, List<Result> results, int limit, boolean hasMore) throws IOException {
1588      slowdownCode(e, false);
1589      if (getLatch != null && getLatch.getCount() > 0) {
1590        try {
1591          getLatch.await();
1592        } catch (InterruptedException e1) {
1593        }
1594      }
1595      return hasMore;
1596    }
1597
1598    @Override
1599    public void postGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get,
1600        List<Cell> results) throws IOException {
1601      slowdownCode(e, true);
1602    }
1603
1604    public static AtomicReference<CountDownLatch> getCdl() {
1605      return cdl;
1606    }
1607
1608    private void slowdownCode(final ObserverContext<RegionCoprocessorEnvironment> e,
1609        boolean isGet) {
1610      CountDownLatch latch = getCdl().get();
1611      try {
1612        System.out.println(latch.getCount() + " is the count " + isGet);
1613        if (latch.getCount() > 0) {
1614          if (isGet) {
1615            countOfGets.incrementAndGet();
1616          } else {
1617            countOfNext.incrementAndGet();
1618          }
1619          LOG.info("Waiting for the counterCountDownLatch");
1620          latch.await(2, TimeUnit.MINUTES); // To help the tests to finish.
1621          if (latch.getCount() > 0) {
1622            throw new RuntimeException("Can't wait more");
1623          }
1624        }
1625      } catch (InterruptedException e1) {
1626        LOG.error(e1.toString(), e1);
1627      }
1628    }
1629  }
1630}