You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by gi...@apache.org on 2021/10/01 20:19:11 UTC
[hbase-site] branch asf-site updated: Published site at
d3c86f4f9071d4eb0919f6baefa8f7cb5c8b7368.
This is an automated email from the ASF dual-hosted git repository.
git-site-role pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/hbase-site.git
The following commit(s) were added to refs/heads/asf-site by this push:
new ce09826 Published site at d3c86f4f9071d4eb0919f6baefa8f7cb5c8b7368.
ce09826 is described below
commit ce0982686164198a87ddbf72d0cb5311cf6fca7d
Author: jenkins <bu...@apache.org>
AuthorDate: Fri Oct 1 20:18:42 2021 +0000
Published site at d3c86f4f9071d4eb0919f6baefa8f7cb5c8b7368.
---
acid-semantics.html | 2 +-
apache_hbase_reference_guide.pdf | 4 +-
book.html | 2 +-
bulk-loads.html | 2 +-
checkstyle-aggregate.html | 16 +-
coc.html | 2 +-
dependencies.html | 2 +-
dependency-convergence.html | 2 +-
dependency-info.html | 2 +-
dependency-management.html | 2 +-
.../hfile/bucket/BucketCache.BucketEntryGroup.html | 18 +-
.../io/hfile/bucket/BucketCache.RAMCache.html | 20 +-
.../io/hfile/bucket/BucketCache.RAMQueueEntry.html | 22 +-
.../hfile/bucket/BucketCache.StatisticsThread.html | 8 +-
.../io/hfile/bucket/BucketCache.WriterThread.html | 12 +-
.../hadoop/hbase/io/hfile/bucket/BucketCache.html | 98 +-
.../hfile/bucket/BucketCache.BucketEntryGroup.html | 2376 ++++++++++----------
.../io/hfile/bucket/BucketCache.RAMCache.html | 2376 ++++++++++----------
.../io/hfile/bucket/BucketCache.RAMQueueEntry.html | 2376 ++++++++++----------
.../hfile/bucket/BucketCache.StatisticsThread.html | 2376 ++++++++++----------
.../io/hfile/bucket/BucketCache.WriterThread.html | 2376 ++++++++++----------
.../hadoop/hbase/io/hfile/bucket/BucketCache.html | 2376 ++++++++++----------
downloads.html | 2 +-
export_control.html | 2 +-
index.html | 2 +-
issue-tracking.html | 2 +-
mail-lists.html | 2 +-
metrics.html | 2 +-
old_news.html | 2 +-
plugin-management.html | 2 +-
plugins.html | 2 +-
poweredbyhbase.html | 2 +-
project-info.html | 2 +-
project-reports.html | 2 +-
project-summary.html | 2 +-
pseudo-distributed.html | 2 +-
replication.html | 2 +-
resources.html | 2 +-
source-repository.html | 2 +-
sponsors.html | 2 +-
supportingprojects.html | 2 +-
team-list.html | 2 +-
42 files changed, 7267 insertions(+), 7243 deletions(-)
diff --git a/acid-semantics.html b/acid-semantics.html
index b617365..f8a9ca6 100644
--- a/acid-semantics.html
+++ b/acid-semantics.html
@@ -456,7 +456,7 @@
<div class="row">
<p>Copyright ©2007–2021
<a href="https://www.apache.org/">The Apache Software Foundation</a>.
-All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-09-29</li>
+All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-10-01</li>
</p>
</div>
<p id="poweredBy" class="pull-right"><a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy"><img class="builtBy" alt="Built by Maven" src="./images/logos/maven-feather.png" /></a>
diff --git a/apache_hbase_reference_guide.pdf b/apache_hbase_reference_guide.pdf
index ff609dc..e44cb83 100644
--- a/apache_hbase_reference_guide.pdf
+++ b/apache_hbase_reference_guide.pdf
@@ -5,8 +5,8 @@
/Author (Apache HBase Team)
/Creator (Asciidoctor PDF 1.5.3, based on Prawn 2.2.2)
/Producer (Apache HBase Team)
-/ModDate (D:20210929200600+00'00')
-/CreationDate (D:20210929201820+00'00')
+/ModDate (D:20211001200559+00'00')
+/CreationDate (D:20211001201649+00'00')
>>
endobj
2 0 obj
diff --git a/book.html b/book.html
index ff7e3b9..67be1d3 100644
--- a/book.html
+++ b/book.html
@@ -46227,7 +46227,7 @@ org/apache/hadoop/hbase/security/access/AccessControlClient.revoke:(Lorg/apache/
<div id="footer">
<div id="footer-text">
Version 3.0.0-alpha-2-SNAPSHOT<br>
-Last updated 2021-09-29 20:06:00 UTC
+Last updated 2021-10-01 20:05:59 UTC
</div>
</div>
<link rel="stylesheet" href="./coderay-asciidoctor.css">
diff --git a/bulk-loads.html b/bulk-loads.html
index 51f3c9e..704c247 100644
--- a/bulk-loads.html
+++ b/bulk-loads.html
@@ -161,7 +161,7 @@
<div class="row">
<p>Copyright ©2007–2021
<a href="https://www.apache.org/">The Apache Software Foundation</a>.
-All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-09-29</li>
+All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-10-01</li>
</p>
</div>
<p id="poweredBy" class="pull-right"><a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy"><img class="builtBy" alt="Built by Maven" src="./images/logos/maven-feather.png" /></a>
diff --git a/checkstyle-aggregate.html b/checkstyle-aggregate.html
index 159a165..f440c24 100644
--- a/checkstyle-aggregate.html
+++ b/checkstyle-aggregate.html
@@ -31307,43 +31307,43 @@
<td>blocks</td>
<td>NeedBraces</td>
<td>'if' construct must use '{}'s.</td>
-<td>1263</td></tr>
+<td>1267</td></tr>
<tr class="a">
<td><img src="images/icon_error_sml.gif" alt="" /> Error</td>
<td>blocks</td>
<td>NeedBraces</td>
<td>'for' construct must use '{}'s.</td>
-<td>1267</td></tr>
+<td>1271</td></tr>
<tr class="b">
<td><img src="images/icon_error_sml.gif" alt="" /> Error</td>
<td>blocks</td>
<td>NeedBraces</td>
<td>'for' construct must use '{}'s.</td>
-<td>1276</td></tr>
+<td>1280</td></tr>
<tr class="a">
<td><img src="images/icon_error_sml.gif" alt="" /> Error</td>
<td>indentation</td>
<td>Indentation</td>
<td>'if' child has incorrect indentation level 10, expected level should be 8.</td>
-<td>1361</td></tr>
+<td>1365</td></tr>
<tr class="b">
<td><img src="images/icon_error_sml.gif" alt="" /> Error</td>
<td>javadoc</td>
<td>NonEmptyAtclauseDescription</td>
<td>At-clause should have a non-empty description.</td>
-<td>1494</td></tr>
+<td>1498</td></tr>
<tr class="a">
<td><img src="images/icon_error_sml.gif" alt="" /> Error</td>
<td>blocks</td>
<td>NeedBraces</td>
<td>'if' construct must use '{}'s.</td>
-<td>1560</td></tr>
+<td>1564</td></tr>
<tr class="b">
<td><img src="images/icon_error_sml.gif" alt="" /> Error</td>
<td>blocks</td>
<td>NeedBraces</td>
<td>'if' construct must use '{}'s.</td>
-<td>1563</td></tr></table></div>
+<td>1567</td></tr></table></div>
<div class="section">
<h3 id="org.apache.hadoop.hbase.io.hfile.bucket.BucketCacheStats.java">org/apache/hadoop/hbase/io/hfile/bucket/BucketCacheStats.java</h3>
<table border="0" class="table table-striped">
@@ -73604,7 +73604,7 @@
<div class="row">
<p>Copyright ©2007–2021
<a href="https://www.apache.org/">The Apache Software Foundation</a>.
-All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-09-29</li>
+All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-10-01</li>
</p>
</div>
<p id="poweredBy" class="pull-right"><a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy"><img class="builtBy" alt="Built by Maven" src="./images/logos/maven-feather.png" /></a>
diff --git a/coc.html b/coc.html
index d41aeaf..5d39094 100644
--- a/coc.html
+++ b/coc.html
@@ -230,7 +230,7 @@ email to <a class="externalLink" href="mailto:private@hbase.apache.org">the priv
<div class="row">
<p>Copyright ©2007–2021
<a href="https://www.apache.org/">The Apache Software Foundation</a>.
-All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-09-29</li>
+All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-10-01</li>
</p>
</div>
<p id="poweredBy" class="pull-right"><a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy"><img class="builtBy" alt="Built by Maven" src="./images/logos/maven-feather.png" /></a>
diff --git a/dependencies.html b/dependencies.html
index 0af9832..ae813c9 100644
--- a/dependencies.html
+++ b/dependencies.html
@@ -302,7 +302,7 @@
<div class="row">
<p>Copyright ©2007–2021
<a href="https://www.apache.org/">The Apache Software Foundation</a>.
-All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-09-29</li>
+All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-10-01</li>
</p>
</div>
<p id="poweredBy" class="pull-right"><a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy"><img class="builtBy" alt="Built by Maven" src="./images/logos/maven-feather.png" /></a>
diff --git a/dependency-convergence.html b/dependency-convergence.html
index 03c663d..04c3115 100644
--- a/dependency-convergence.html
+++ b/dependency-convergence.html
@@ -866,7 +866,7 @@
<div class="row">
<p>Copyright ©2007–2021
<a href="https://www.apache.org/">The Apache Software Foundation</a>.
-All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-09-29</li>
+All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-10-01</li>
</p>
</div>
<p id="poweredBy" class="pull-right"><a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy"><img class="builtBy" alt="Built by Maven" src="./images/logos/maven-feather.png" /></a>
diff --git a/dependency-info.html b/dependency-info.html
index 7110168..7991145 100644
--- a/dependency-info.html
+++ b/dependency-info.html
@@ -183,7 +183,7 @@
<div class="row">
<p>Copyright ©2007–2021
<a href="https://www.apache.org/">The Apache Software Foundation</a>.
-All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-09-29</li>
+All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-10-01</li>
</p>
</div>
<p id="poweredBy" class="pull-right"><a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy"><img class="builtBy" alt="Built by Maven" src="./images/logos/maven-feather.png" /></a>
diff --git a/dependency-management.html b/dependency-management.html
index 496986c..dc498a9 100644
--- a/dependency-management.html
+++ b/dependency-management.html
@@ -1190,7 +1190,7 @@
<div class="row">
<p>Copyright ©2007–2021
<a href="https://www.apache.org/">The Apache Software Foundation</a>.
-All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-09-29</li>
+All rights reserved. <li id="publishDate" class="pull-right">Last Published: 2021-10-01</li>
</p>
</div>
<p id="poweredBy" class="pull-right"><a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy"><img class="builtBy" alt="Built by Maven" src="./images/logos/maven-feather.png" /></a>
diff --git a/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html b/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html
index 7416d8d..618567e 100644
--- a/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html
+++ b/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html
@@ -113,7 +113,7 @@ var activeTableTab = "activeTableTab";
</dl>
<hr>
<br>
-<pre>private class <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1374">BucketCache.BucketEntryGroup</a>
+<pre>private class <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1378">BucketCache.BucketEntryGroup</a>
extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html?is-external=true" title="class or interface in java.lang">Object</a></pre>
<div class="block">Used to group bucket entries into priority buckets. There will be a
BucketEntryGroup for each priority (single, multi, memory). Once bucketed,
@@ -227,7 +227,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>queue</h4>
-<pre>private <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/CachedEntryQueue.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">CachedEntryQueue</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1376">queue</a></pre>
+<pre>private <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/CachedEntryQueue.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">CachedEntryQueue</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1380">queue</a></pre>
</li>
</ul>
<a name="totalSize">
@@ -236,7 +236,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>totalSize</h4>
-<pre>private long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1377">totalSize</a></pre>
+<pre>private long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1381">totalSize</a></pre>
</li>
</ul>
<a name="bucketSize">
@@ -245,7 +245,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockListLast">
<li class="blockList">
<h4>bucketSize</h4>
-<pre>private long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1378">bucketSize</a></pre>
+<pre>private long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1382">bucketSize</a></pre>
</li>
</ul>
</li>
@@ -262,7 +262,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockListLast">
<li class="blockList">
<h4>BucketEntryGroup</h4>
-<pre>public <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1380">BucketEntryGroup</a>(long bytesToFree,
+<pre>public <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1384">BucketEntryGroup</a>(long bytesToFree,
long blockSize,
long bucketSize)</pre>
</li>
@@ -281,7 +281,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>add</h4>
-<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1386">add</a>(<a href="https://docs.oracle.com/javase/8/docs/api/java/util/Map.Entry.html?is-external=true" title="class or interface in java.util">Map.Entry</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a>,<a href="../../../../../../../o [...]
+<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1390">add</a>(<a href="https://docs.oracle.com/javase/8/docs/api/java/util/Map.Entry.html?is-external=true" title="class or interface in java.util">Map.Entry</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a>,<a href="../../../../../../../o [...]
</li>
</ul>
<a name="free-long-">
@@ -290,7 +290,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>free</h4>
-<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1391">free</a>(long toFree)</pre>
+<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1395">free</a>(long toFree)</pre>
</li>
</ul>
<a name="overflow--">
@@ -299,7 +299,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>overflow</h4>
-<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1409">overflow</a>()</pre>
+<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1413">overflow</a>()</pre>
</li>
</ul>
<a name="totalSize--">
@@ -308,7 +308,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockListLast">
<li class="blockList">
<h4>totalSize</h4>
-<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1413">totalSize</a>()</pre>
+<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html#line.1417">totalSize</a>()</pre>
</li>
</ul>
</li>
diff --git a/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html b/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html
index 173061d..4dcb467 100644
--- a/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html
+++ b/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html
@@ -113,7 +113,7 @@ var activeTableTab = "activeTableTab";
</dl>
<hr>
<br>
-<pre>static class <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1635">BucketCache.RAMCache</a>
+<pre>static class <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1639">BucketCache.RAMCache</a>
extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html?is-external=true" title="class or interface in java.lang">Object</a></pre>
<div class="block">Wrapped the delegate ConcurrentMap with maintaining its block's reference count.</div>
</li>
@@ -239,7 +239,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockListLast">
<li class="blockList">
<h4>delegate</h4>
-<pre>final <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html?is-external=true" title="class or interface in java.util.concurrent">ConcurrentHashMap</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a>,<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop. [...]
+<pre>final <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html?is-external=true" title="class or interface in java.util.concurrent">ConcurrentHashMap</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a>,<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop. [...]
<div class="block">Defined the map as <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html?is-external=true" title="class or interface in java.util.concurrent"><code>ConcurrentHashMap</code></a> explicitly here, because in
<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#get-org.apache.hadoop.hbase.io.hfile.BlockCacheKey-"><code>get(BlockCacheKey)</code></a> and
<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#putIfAbsent-org.apache.hadoop.hbase.io.hfile.BlockCacheKey-org.apache.hadoop.hbase.io.hfile.bucket.BucketCache.RAMQueueEntry-"><code>putIfAbsent(BlockCacheKey, BucketCache.RAMQueueEntry)</code></a> , we need to
@@ -263,7 +263,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockListLast">
<li class="blockList">
<h4>RAMCache</h4>
-<pre><a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1635">RAMCache</a>()</pre>
+<pre><a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1639">RAMCache</a>()</pre>
</li>
</ul>
</li>
@@ -280,7 +280,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>containsKey</h4>
-<pre>public boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1647">containsKey</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> key)</pre>
+<pre>public boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1651">containsKey</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> key)</pre>
</li>
</ul>
<a name="get-org.apache.hadoop.hbase.io.hfile.BlockCacheKey-">
@@ -289,7 +289,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>get</h4>
-<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1651">get</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> k [...]
+<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1655">get</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> k [...]
</li>
</ul>
<a name="putIfAbsent-org.apache.hadoop.hbase.io.hfile.BlockCacheKey-org.apache.hadoop.hbase.io.hfile.bucket.BucketCache.RAMQueueEntry-">
@@ -298,7 +298,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>putIfAbsent</h4>
-<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1665">putIfAbsent</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a [...]
+<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1669">putIfAbsent</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a [...]
<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a> entry)</pre>
<div class="block">Return the previous associated value, or null if absent. It has the same meaning as
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html?is-external=true#putIfAbsent-K-V-" title="class or interface in java.util.concurrent"><code>ConcurrentMap.putIfAbsent(Object, Object)</code></a></div>
@@ -310,7 +310,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>remove</h4>
-<pre>public boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1676">remove</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> key)</pre>
+<pre>public boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1680">remove</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> key)</pre>
</li>
</ul>
<a name="remove-org.apache.hadoop.hbase.io.hfile.BlockCacheKey-java.util.function.Consumer-">
@@ -319,7 +319,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>remove</h4>
-<pre>public boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1686">remove</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> key,
+<pre>public boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1690">remove</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> key,
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html?is-external=true" title="class or interface in java.util.function">Consumer</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a>> action)</pre>
<div class="block">Defined an <a href="https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html?is-external=true" title="class or interface in java.util.function"><code>Consumer</code></a> here, because once the removed entry release its reference count,
then it's ByteBuffers may be recycled and accessing it outside this method will be thrown an
@@ -333,7 +333,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>isEmpty</h4>
-<pre>public boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1695">isEmpty</a>()</pre>
+<pre>public boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1699">isEmpty</a>()</pre>
</li>
</ul>
<a name="clear--">
@@ -342,7 +342,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockListLast">
<li class="blockList">
<h4>clear</h4>
-<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1699">clear</a>()</pre>
+<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html#line.1703">clear</a>()</pre>
</li>
</ul>
</li>
diff --git a/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html b/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html
index 78ecb22..0f7b87cf 100644
--- a/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html
+++ b/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html
@@ -113,7 +113,7 @@ var activeTableTab = "activeTableTab";
</dl>
<hr>
<br>
-<pre>static class <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1421">BucketCache.RAMQueueEntry</a>
+<pre>static class <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1425">BucketCache.RAMQueueEntry</a>
extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html?is-external=true" title="class or interface in java.lang">Object</a></pre>
<div class="block">Block Entry stored in the memory with key,data and so on</div>
</li>
@@ -236,7 +236,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>key</h4>
-<pre>private final <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1422">key</a></pre>
+<pre>private final <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1426">key</a></pre>
</li>
</ul>
<a name="data">
@@ -245,7 +245,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>data</h4>
-<pre>private final <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/Cacheable.html" title="interface in org.apache.hadoop.hbase.io.hfile">Cacheable</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1423">data</a></pre>
+<pre>private final <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/Cacheable.html" title="interface in org.apache.hadoop.hbase.io.hfile">Cacheable</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1427">data</a></pre>
</li>
</ul>
<a name="accessCounter">
@@ -254,7 +254,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>accessCounter</h4>
-<pre>private long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1424">accessCounter</a></pre>
+<pre>private long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1428">accessCounter</a></pre>
</li>
</ul>
<a name="inMemory">
@@ -263,7 +263,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockListLast">
<li class="blockList">
<h4>inMemory</h4>
-<pre>private boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1425">inMemory</a></pre>
+<pre>private boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1429">inMemory</a></pre>
</li>
</ul>
</li>
@@ -280,7 +280,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockListLast">
<li class="blockList">
<h4>RAMQueueEntry</h4>
-<pre><a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1427">RAMQueueEntry</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> bck,
+<pre><a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1431">RAMQueueEntry</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> bck,
<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/Cacheable.html" title="interface in org.apache.hadoop.hbase.io.hfile">Cacheable</a> data,
long accessCounter,
boolean inMemory)</pre>
@@ -300,7 +300,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>getData</h4>
-<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/Cacheable.html" title="interface in org.apache.hadoop.hbase.io.hfile">Cacheable</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1434">getData</a>()</pre>
+<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/Cacheable.html" title="interface in org.apache.hadoop.hbase.io.hfile">Cacheable</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1438">getData</a>()</pre>
</li>
</ul>
<a name="getKey--">
@@ -309,7 +309,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>getKey</h4>
-<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1438">getKey</a>()</pre>
+<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1442">getKey</a>()</pre>
</li>
</ul>
<a name="access-long-">
@@ -318,7 +318,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>access</h4>
-<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1442">access</a>(long accessCounter)</pre>
+<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1446">access</a>(long accessCounter)</pre>
</li>
</ul>
<a name="getByteBuffAllocator--">
@@ -327,7 +327,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockList">
<li class="blockList">
<h4>getByteBuffAllocator</h4>
-<pre>private <a href="../../../../../../../org/apache/hadoop/hbase/io/ByteBuffAllocator.html" title="class in org.apache.hadoop.hbase.io">ByteBuffAllocator</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1446">getByteBuffAllocator</a>()</pre>
+<pre>private <a href="../../../../../../../org/apache/hadoop/hbase/io/ByteBuffAllocator.html" title="class in org.apache.hadoop.hbase.io">ByteBuffAllocator</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1450">getByteBuffAllocator</a>()</pre>
</li>
</ul>
<a name="writeToCache-org.apache.hadoop.hbase.io.hfile.bucket.IOEngine-org.apache.hadoop.hbase.io.hfile.bucket.BucketAllocator-java.util.concurrent.atomic.LongAdder-java.util.function.Function-">
@@ -336,7 +336,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html
<ul class="blockListLast">
<li class="blockList">
<h4>writeToCache</h4>
-<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketEntry</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1453">writeToCache</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/IOEngine.html" title="interface in org.apache.hadoop.hbase.io.hfile.bucket">IOEngine</a> ioEngine,
+<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketEntry</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html#line.1457">writeToCache</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/IOEngine.html" title="interface in org.apache.hadoop.hbase.io.hfile.bucket">IOEngine</a> ioEngine,
<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketAllocator.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketAllocator</a> alloc,
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/LongAdder.html?is-external=true" title="class or interface in java.util.concurrent.atomic">LongAdder</a> realCacheSize,
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html?is-external=true" title="class or interface in java.util.function">Function</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketEntry</a>,<a href="../../../../../../../org/apache/hadoop/hbase/io/ByteBuffAllocator.Recycler.html" title="interface in org.apache.hadoop.hbas [...]
diff --git a/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html b/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html
index 259b4b9..3e987a4 100644
--- a/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html
+++ b/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html
@@ -122,7 +122,7 @@ var activeTableTab = "activeTableTab";
</dl>
<hr>
<br>
-<pre>private static class <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.694">BucketCache.StatisticsThread</a>
+<pre>private static class <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.693">BucketCache.StatisticsThread</a>
extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html?is-external=true" title="class or interface in java.lang">Thread</a></pre>
</li>
</ul>
@@ -239,7 +239,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html
<ul class="blockListLast">
<li class="blockList">
<h4>bucketCache</h4>
-<pre>private final <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html#line.695">bucketCache</a></pre>
+<pre>private final <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html#line.694">bucketCache</a></pre>
</li>
</ul>
</li>
@@ -256,7 +256,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html
<ul class="blockListLast">
<li class="blockList">
<h4>StatisticsThread</h4>
-<pre>public <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html#line.697">StatisticsThread</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache</a> bucketCache)</pre>
+<pre>public <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html#line.696">StatisticsThread</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache</a> bucketCache)</pre>
</li>
</ul>
</li>
@@ -273,7 +273,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html
<ul class="blockListLast">
<li class="blockList">
<h4>run</h4>
-<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html#line.704">run</a>()</pre>
+<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html#line.703">run</a>()</pre>
<dl>
<dt><span class="overrideSpecifyLabel">Specified by:</span></dt>
<dd><code><a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html?is-external=true#run--" title="class or interface in java.lang">run</a></code> in interface <code><a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html?is-external=true" title="class or interface in java.lang">Runnable</a></code></dd>
diff --git a/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.WriterThread.html b/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.WriterThread.html
index d3142c1..3357425 100644
--- a/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.WriterThread.html
+++ b/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.WriterThread.html
@@ -122,7 +122,7 @@ var activeTableTab = "activeTableTab";
</dl>
<hr>
<br>
-<pre>class <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.937">BucketCache.WriterThread</a>
+<pre>class <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.936">BucketCache.WriterThread</a>
extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html?is-external=true" title="class or interface in java.lang">Thread</a></pre>
</li>
</ul>
@@ -247,7 +247,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html
<ul class="blockList">
<li class="blockList">
<h4>inputQueue</h4>
-<pre>private final <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingQueue.html?is-external=true" title="class or interface in java.util.concurrent">BlockingQueue</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a>> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.Wri [...]
+<pre>private final <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingQueue.html?is-external=true" title="class or interface in java.util.concurrent">BlockingQueue</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a>> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.Wri [...]
</li>
</ul>
<a name="writerEnabled">
@@ -256,7 +256,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html
<ul class="blockListLast">
<li class="blockList">
<h4>writerEnabled</h4>
-<pre>private volatile boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.WriterThread.html#line.939">writerEnabled</a></pre>
+<pre>private volatile boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.WriterThread.html#line.938">writerEnabled</a></pre>
</li>
</ul>
</li>
@@ -273,7 +273,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html
<ul class="blockListLast">
<li class="blockList">
<h4>WriterThread</h4>
-<pre><a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.WriterThread.html#line.941">WriterThread</a>(<a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingQueue.html?is-external=true" title="class or interface in java.util.concurrent">BlockingQueue</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache [...]
+<pre><a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.WriterThread.html#line.940">WriterThread</a>(<a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingQueue.html?is-external=true" title="class or interface in java.util.concurrent">BlockingQueue</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache [...]
</li>
</ul>
</li>
@@ -290,7 +290,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html
<ul class="blockList">
<li class="blockList">
<h4>disableWriter</h4>
-<pre>void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.WriterThread.html#line.947">disableWriter</a>()</pre>
+<pre>void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.WriterThread.html#line.946">disableWriter</a>()</pre>
</li>
</ul>
<a name="run--">
@@ -299,7 +299,7 @@ extends <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html
<ul class="blockListLast">
<li class="blockList">
<h4>run</h4>
-<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.WriterThread.html#line.952">run</a>()</pre>
+<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.WriterThread.html#line.951">run</a>()</pre>
<dl>
<dt><span class="overrideSpecifyLabel">Specified by:</span></dt>
<dd><code><a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html?is-external=true#run--" title="class or interface in java.lang">run</a></code> in interface <code><a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html?is-external=true" title="class or interface in java.lang">Runnable</a></code></dd>
diff --git a/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html b/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html
index 88ced7f..8756295 100644
--- a/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html
+++ b/devapidocs/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html
@@ -1690,7 +1690,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getBlock</h4>
-<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/Cacheable.html" title="interface in org.apache.hadoop.hbase.io.hfile">Cacheable</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.502">getBlock</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> key,
+<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/Cacheable.html" title="interface in org.apache.hadoop.hbase.io.hfile">Cacheable</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.501">getBlock</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> key,
boolean caching,
boolean repeat,
boolean updateCacheMetrics)</pre>
@@ -1714,7 +1714,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>blockEvicted</h4>
-<pre>void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.561">blockEvicted</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> cacheKey,
+<pre>void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.560">blockEvicted</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> cacheKey,
<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketEntry</a> bucketEntry,
boolean decrementBlockNumber)</pre>
<div class="block">This method is invoked after the bucketEntry is removed from <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#backingMap"><code>backingMap</code></a></div>
@@ -1726,7 +1726,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>freeBucketEntry</h4>
-<pre>void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.574">freeBucketEntry</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketEntry</a> bucketEntry)</pre>
+<pre>void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.573">freeBucketEntry</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketEntry</a> bucketEntry)</pre>
<div class="block">Free the {<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket"><code>BucketEntry</code></a> actually,which could only be invoked when the
<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html#refCnt"><code>BucketEntry.refCnt</code></a> becoming 0.</div>
</li>
@@ -1737,7 +1737,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>evictBlock</h4>
-<pre>public boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.596">evictBlock</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> cacheKey)</pre>
+<pre>public boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.595">evictBlock</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> cacheKey)</pre>
<div class="block">Try to evict the block from <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html" title="interface in org.apache.hadoop.hbase.io.hfile"><code>BlockCache</code></a> by force. We'll call this in few cases:<br>
1. Close an HFile, and clear all cached blocks. <br>
2. Call <a href="../../../../../../../org/apache/hadoop/hbase/client/Admin.html#clearBlockCache-org.apache.hadoop.hbase.TableName-"><code>Admin.clearBlockCache(TableName)</code></a> to clear all blocks for a given table.<br>
@@ -1766,7 +1766,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>doEvictBlock</h4>
-<pre>private boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.609">doEvictBlock</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> cacheKey,
+<pre>private boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.608">doEvictBlock</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> cacheKey,
<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketEntry</a> bucketEntry)</pre>
<div class="block">Evict the <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile"><code>BlockCacheKey</code></a> and <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket"><code>BucketEntry</code></a> from <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#backingMap"><code>backingMap< [...]
<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#ramCache"><code>ramCache</code></a>. <br/>
@@ -1787,7 +1787,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>createRecycler</h4>
-<pre>private <a href="../../../../../../../org/apache/hadoop/hbase/io/ByteBuffAllocator.Recycler.html" title="interface in org.apache.hadoop.hbase.io">ByteBuffAllocator.Recycler</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.648">createRecycler</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketEntry</a> bucketEnt [...]
+<pre>private <a href="../../../../../../../org/apache/hadoop/hbase/io/ByteBuffAllocator.Recycler.html" title="interface in org.apache.hadoop.hbase.io">ByteBuffAllocator.Recycler</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.647">createRecycler</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketEntry</a> bucketEnt [...]
<div class="block"><pre>
Create the <a href="../../../../../../../org/apache/hadoop/hbase/io/ByteBuffAllocator.Recycler.html" title="interface in org.apache.hadoop.hbase.io"><code>ByteBuffAllocator.Recycler</code></a> for <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html#refCnt"><code>BucketEntry.refCnt</code></a>,which would be used as
<a href="../../../../../../../org/apache/hadoop/hbase/nio/RefCnt.html#recycler"><code>RefCnt.recycler</code></a> of <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/HFileBlock.html#buf"><code>HFileBlock.buf</code></a> returned from <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#getBlock-org.apache.hadoop.hbase.io.hfile.BlockCacheKey-boolean-boolean-boolean-"><code>getBlock(org.apache.hadoop.hbase.io.hfile.BlockCacheKey, boolean, bool [...]
@@ -1807,7 +1807,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>evictBlockIfNoRpcReferenced</h4>
-<pre>public boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.658">evictBlockIfNoRpcReferenced</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> blockCacheKey)</pre>
+<pre>public boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.657">evictBlockIfNoRpcReferenced</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> blockCacheKey)</pre>
<div class="block">NOTE: This method is only for test.</div>
</li>
</ul>
@@ -1817,7 +1817,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>evictBucketEntryIfNoRpcReferenced</h4>
-<pre>boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.675">evictBucketEntryIfNoRpcReferenced</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> blockCacheKey,
+<pre>boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.674">evictBucketEntryIfNoRpcReferenced</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> blockCacheKey,
<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketEntry</a> bucketEntry)</pre>
<div class="block">Evict <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile"><code>BlockCacheKey</code></a> and its corresponding <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket"><code>BucketEntry</code></a> only if
<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html#isRpcRef--"><code>BucketEntry.isRpcRef()</code></a> is false. <br/>
@@ -1838,7 +1838,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>removeFromRamCache</h4>
-<pre>protected boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.682">removeFromRamCache</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> cacheKey)</pre>
+<pre>protected boolean <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.681">removeFromRamCache</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> cacheKey)</pre>
</li>
</ul>
<a name="logStats--">
@@ -1847,7 +1847,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>logStats</h4>
-<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.709">logStats</a>()</pre>
+<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.708">logStats</a>()</pre>
</li>
</ul>
<a name="getRealCacheSize--">
@@ -1856,7 +1856,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getRealCacheSize</h4>
-<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.735">getRealCacheSize</a>()</pre>
+<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.734">getRealCacheSize</a>()</pre>
</li>
</ul>
<a name="acceptableSize--">
@@ -1865,7 +1865,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>acceptableSize</h4>
-<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.739">acceptableSize</a>()</pre>
+<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.738">acceptableSize</a>()</pre>
</li>
</ul>
<a name="getPartitionSize-float-">
@@ -1874,7 +1874,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getPartitionSize</h4>
-<pre>long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.743">getPartitionSize</a>(float partitionFactor)</pre>
+<pre>long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.742">getPartitionSize</a>(float partitionFactor)</pre>
</li>
</ul>
<a name="bucketSizesAboveThresholdCount-float-">
@@ -1883,7 +1883,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>bucketSizesAboveThresholdCount</h4>
-<pre>private int <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.750">bucketSizesAboveThresholdCount</a>(float minFactor)</pre>
+<pre>private int <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.749">bucketSizesAboveThresholdCount</a>(float minFactor)</pre>
<div class="block">Return the count of bucketSizeinfos still need free space</div>
</li>
</ul>
@@ -1893,7 +1893,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>freeEntireBuckets</h4>
-<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.772">freeEntireBuckets</a>(int completelyFreeBucketsNeeded)</pre>
+<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.771">freeEntireBuckets</a>(int completelyFreeBucketsNeeded)</pre>
<div class="block">This method will find the buckets that are minimally occupied
and are not reference counted and will free them completely
without any constraint on the access times of the elements,
@@ -1911,7 +1911,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>freeSpace</h4>
-<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.798">freeSpace</a>(<a href="https://docs.oracle.com/javase/8/docs/api/java/lang/String.html?is-external=true" title="class or interface in java.lang">String</a> why)</pre>
+<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.797">freeSpace</a>(<a href="https://docs.oracle.com/javase/8/docs/api/java/lang/String.html?is-external=true" title="class or interface in java.lang">String</a> why)</pre>
<div class="block">Free the space if the used size reaches acceptableSize() or one size block
couldn't be allocated. When freeing the space, we use the LRU algorithm and
ensure there must be some blocks evicted</div>
@@ -1927,7 +1927,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>putIntoBackingMap</h4>
-<pre>protected void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.989">putIntoBackingMap</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> key,
+<pre>protected void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.988">putIntoBackingMap</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> key,
<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketEntry</a> bucketEntry)</pre>
<div class="block">Put the new bucket entry into backingMap. Notice that we are allowed to replace the existing
cache with a new block for the same cache key. there's a corner case: one thread cache a block
@@ -1951,7 +1951,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>doDrain</h4>
-<pre>void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1006">doDrain</a>(<a href="https://docs.oracle.com/javase/8/docs/api/java/util/List.html?is-external=true" title="class or interface in java.util">List</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a>> entries)
+<pre>void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1005">doDrain</a>(<a href="https://docs.oracle.com/javase/8/docs/api/java/util/List.html?is-external=true" title="class or interface in java.util">List</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a>> entries)
throws <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/InterruptedException.html?is-external=true" title="class or interface in java.lang">InterruptedException</a></pre>
<div class="block">Flush the entries in ramCache to IOEngine and add bucket entry to backingMap. Process all that
are passed in even if failure being sure to remove from ramCache else we'll never undo the
@@ -1971,7 +1971,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getRAMQueueEntries</h4>
-<pre>static <a href="https://docs.oracle.com/javase/8/docs/api/java/util/List.html?is-external=true" title="class or interface in java.util">List</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a>> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1113">getRAMQueueEntries</a>(<a hre [...]
+<pre>static <a href="https://docs.oracle.com/javase/8/docs/api/java/util/List.html?is-external=true" title="class or interface in java.util">List</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a>> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1117">getRAMQueueEntries</a>(<a hre [...]
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/List.html?is-external=true" title="class or interface in java.util">List</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketCache.RAMQueueEntry</a>> receptacle)
throws <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/InterruptedException.html?is-external=true" title="class or interface in java.lang">InterruptedException</a></pre>
<div class="block">Blocks until elements available in <code>q</code> then tries to grab as many as possible before
@@ -1994,7 +1994,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>persistToFile</h4>
-<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1128">persistToFile</a>()
+<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1132">persistToFile</a>()
throws <a href="https://docs.oracle.com/javase/8/docs/api/java/io/IOException.html?is-external=true" title="class or interface in java.io">IOException</a></pre>
<dl>
<dt><span class="throwsLabel">Throws:</span></dt>
@@ -2010,7 +2010,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>retrieveFromFile</h4>
-<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1142">retrieveFromFile</a>(int[] bucketSizes)
+<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1146">retrieveFromFile</a>(int[] bucketSizes)
throws <a href="https://docs.oracle.com/javase/8/docs/api/java/io/IOException.html?is-external=true" title="class or interface in java.io">IOException</a></pre>
<dl>
<dt><span class="throwsLabel">Throws:</span></dt>
@@ -2026,7 +2026,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>deleteFileOnClose</h4>
-<pre>private <a href="https://docs.oracle.com/javase/8/docs/api/java/io/FileInputStream.html?is-external=true" title="class or interface in java.io">FileInputStream</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1184">deleteFileOnClose</a>(<a href="https://docs.oracle.com/javase/8/docs/api/java/io/File.html?is-external=true" title="class or interface in java.io">File</a> file)
+<pre>private <a href="https://docs.oracle.com/javase/8/docs/api/java/io/FileInputStream.html?is-external=true" title="class or interface in java.io">FileInputStream</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1188">deleteFileOnClose</a>(<a href="https://docs.oracle.com/javase/8/docs/api/java/io/File.html?is-external=true" title="class or interface in java.io">File</a> file)
throws <a href="https://docs.oracle.com/javase/8/docs/api/java/io/IOException.html?is-external=true" title="class or interface in java.io">IOException</a></pre>
<div class="block">Create an input stream that deletes the file after reading it. Use in try-with-resources to
avoid this pattern where an exception thrown from a finally block may mask earlier exceptions:
@@ -2054,7 +2054,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>verifyCapacityAndClasses</h4>
-<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1209">verifyCapacityAndClasses</a>(long capacitySize,
+<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1213">verifyCapacityAndClasses</a>(long capacitySize,
<a href="https://docs.oracle.com/javase/8/docs/api/java/lang/String.html?is-external=true" title="class or interface in java.lang">String</a> ioclass,
<a href="https://docs.oracle.com/javase/8/docs/api/java/lang/String.html?is-external=true" title="class or interface in java.lang">String</a> mapclass)
throws <a href="https://docs.oracle.com/javase/8/docs/api/java/io/IOException.html?is-external=true" title="class or interface in java.io">IOException</a></pre>
@@ -2070,7 +2070,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>parsePB</h4>
-<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1226">parsePB</a>(org.apache.hadoop.hbase.shaded.protobuf.generated.BucketCacheProtos.BucketCacheEntry proto)
+<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1230">parsePB</a>(org.apache.hadoop.hbase.shaded.protobuf.generated.BucketCacheProtos.BucketCacheEntry proto)
throws <a href="https://docs.oracle.com/javase/8/docs/api/java/io/IOException.html?is-external=true" title="class or interface in java.io">IOException</a></pre>
<dl>
<dt><span class="throwsLabel">Throws:</span></dt>
@@ -2084,7 +2084,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>checkIOErrorIsTolerated</h4>
-<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1244">checkIOErrorIsTolerated</a>()</pre>
+<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1248">checkIOErrorIsTolerated</a>()</pre>
<div class="block">Check whether we tolerate IO error this time. If the duration of IOEngine
throwing errors exceeds ioErrorsDurationTimeTolerated, we will disable the
cache</div>
@@ -2096,7 +2096,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>disableCache</h4>
-<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1262">disableCache</a>()</pre>
+<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1266">disableCache</a>()</pre>
<div class="block">Used to shut down the cache -or- turn it off in the case of something broken.</div>
</li>
</ul>
@@ -2106,7 +2106,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>join</h4>
-<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1275">join</a>()
+<pre>private void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1279">join</a>()
throws <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/InterruptedException.html?is-external=true" title="class or interface in java.lang">InterruptedException</a></pre>
<dl>
<dt><span class="throwsLabel">Throws:</span></dt>
@@ -2120,7 +2120,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>shutdown</h4>
-<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1281">shutdown</a>()</pre>
+<pre>public void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1285">shutdown</a>()</pre>
<div class="block"><span class="descfrmTypeLabel">Description copied from interface: <code><a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html#shutdown--">BlockCache</a></code></span></div>
<div class="block">Shutdown the cache.</div>
<dl>
@@ -2135,7 +2135,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getStats</h4>
-<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/CacheStats.html" title="class in org.apache.hadoop.hbase.io.hfile">CacheStats</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1298">getStats</a>()</pre>
+<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/CacheStats.html" title="class in org.apache.hadoop.hbase.io.hfile">CacheStats</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1302">getStats</a>()</pre>
<div class="block"><span class="descfrmTypeLabel">Description copied from interface: <code><a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html#getStats--">BlockCache</a></code></span></div>
<div class="block">Get the statistics for this block cache.</div>
<dl>
@@ -2152,7 +2152,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getAllocator</h4>
-<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketAllocator.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketAllocator</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1302">getAllocator</a>()</pre>
+<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/bucket/BucketAllocator.html" title="class in org.apache.hadoop.hbase.io.hfile.bucket">BucketAllocator</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1306">getAllocator</a>()</pre>
</li>
</ul>
<a name="heapSize--">
@@ -2161,7 +2161,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>heapSize</h4>
-<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1307">heapSize</a>()</pre>
+<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1311">heapSize</a>()</pre>
<dl>
<dt><span class="overrideSpecifyLabel">Specified by:</span></dt>
<dd><code><a href="../../../../../../../org/apache/hadoop/hbase/io/HeapSize.html#heapSize--">heapSize</a></code> in interface <code><a href="../../../../../../../org/apache/hadoop/hbase/io/HeapSize.html" title="interface in org.apache.hadoop.hbase.io">HeapSize</a></code></dd>
@@ -2177,7 +2177,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>size</h4>
-<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1312">size</a>()</pre>
+<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1316">size</a>()</pre>
<div class="block"><span class="descfrmTypeLabel">Description copied from interface: <code><a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html#size--">BlockCache</a></code></span></div>
<div class="block">Returns the total size of the block cache, in bytes.</div>
<dl>
@@ -2194,7 +2194,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getCurrentDataSize</h4>
-<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1317">getCurrentDataSize</a>()</pre>
+<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1321">getCurrentDataSize</a>()</pre>
<div class="block"><span class="descfrmTypeLabel">Description copied from interface: <code><a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html#getCurrentDataSize--">BlockCache</a></code></span></div>
<div class="block">Returns the occupied size of data blocks, in bytes.</div>
<dl>
@@ -2211,7 +2211,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getFreeSize</h4>
-<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1322">getFreeSize</a>()</pre>
+<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1326">getFreeSize</a>()</pre>
<div class="block"><span class="descfrmTypeLabel">Description copied from interface: <code><a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html#getFreeSize--">BlockCache</a></code></span></div>
<div class="block">Returns the free size of the block cache, in bytes.</div>
<dl>
@@ -2228,7 +2228,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getBlockCount</h4>
-<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1327">getBlockCount</a>()</pre>
+<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1331">getBlockCount</a>()</pre>
<div class="block"><span class="descfrmTypeLabel">Description copied from interface: <code><a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html#getBlockCount--">BlockCache</a></code></span></div>
<div class="block">Returns the number of blocks currently cached in the block cache.</div>
<dl>
@@ -2245,7 +2245,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getDataBlockCount</h4>
-<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1332">getDataBlockCount</a>()</pre>
+<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1336">getDataBlockCount</a>()</pre>
<div class="block"><span class="descfrmTypeLabel">Description copied from interface: <code><a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html#getDataBlockCount--">BlockCache</a></code></span></div>
<div class="block">Returns the number of data blocks currently cached in the block cache.</div>
<dl>
@@ -2262,7 +2262,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getCurrentSize</h4>
-<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1337">getCurrentSize</a>()</pre>
+<pre>public long <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1341">getCurrentSize</a>()</pre>
<div class="block"><span class="descfrmTypeLabel">Description copied from interface: <code><a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html#getCurrentSize--">BlockCache</a></code></span></div>
<div class="block">Returns the occupied size of the block cache, in bytes.</div>
<dl>
@@ -2279,7 +2279,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getAlgorithm</h4>
-<pre>protected <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/String.html?is-external=true" title="class or interface in java.lang">String</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1341">getAlgorithm</a>()</pre>
+<pre>protected <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/String.html?is-external=true" title="class or interface in java.lang">String</a> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1345">getAlgorithm</a>()</pre>
</li>
</ul>
<a name="evictBlocksByHfileName-java.lang.String-">
@@ -2288,7 +2288,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>evictBlocksByHfileName</h4>
-<pre>public int <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1353">evictBlocksByHfileName</a>(<a href="https://docs.oracle.com/javase/8/docs/api/java/lang/String.html?is-external=true" title="class or interface in java.lang">String</a> hfileName)</pre>
+<pre>public int <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1357">evictBlocksByHfileName</a>(<a href="https://docs.oracle.com/javase/8/docs/api/java/lang/String.html?is-external=true" title="class or interface in java.lang">String</a> hfileName)</pre>
<div class="block">Evicts all blocks for a specific HFile.
<p>
This is used for evict-on-close to remove all blocks of a specific HFile.</div>
@@ -2306,7 +2306,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>stopWriterThreads</h4>
-<pre>void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1496">stopWriterThreads</a>()
+<pre>void <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1500">stopWriterThreads</a>()
throws <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/InterruptedException.html?is-external=true" title="class or interface in java.lang">InterruptedException</a></pre>
<div class="block">Only used in test</div>
<dl>
@@ -2321,7 +2321,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>iterator</h4>
-<pre>public <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html?is-external=true" title="class or interface in java.util">Iterator</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/CachedBlock.html" title="interface in org.apache.hadoop.hbase.io.hfile">CachedBlock</a>> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1505">iterator</a>()</pre>
+<pre>public <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html?is-external=true" title="class or interface in java.util">Iterator</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/CachedBlock.html" title="interface in org.apache.hadoop.hbase.io.hfile">CachedBlock</a>> <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1509">iterator</a>()</pre>
<dl>
<dt><span class="overrideSpecifyLabel">Specified by:</span></dt>
<dd><code><a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html?is-external=true#iterator--" title="class or interface in java.lang">iterator</a></code> in interface <code><a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html?is-external=true" title="class or interface in java.lang">Iterable</a><<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/CachedBlock.html" title="interface in org.apache.hadoop.hbase.io.hfile">Cache [...]
@@ -2338,7 +2338,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getBlockCaches</h4>
-<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html" title="interface in org.apache.hadoop.hbase.io.hfile">BlockCache</a>[] <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1596">getBlockCaches</a>()</pre>
+<pre>public <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html" title="interface in org.apache.hadoop.hbase.io.hfile">BlockCache</a>[] <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1600">getBlockCaches</a>()</pre>
<dl>
<dt><span class="overrideSpecifyLabel">Specified by:</span></dt>
<dd><code><a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html#getBlockCaches--">getBlockCaches</a></code> in interface <code><a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCache.html" title="interface in org.apache.hadoop.hbase.io.hfile">BlockCache</a></code></dd>
@@ -2353,7 +2353,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getRpcRefCount</h4>
-<pre>public int <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1600">getRpcRefCount</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> cacheKey)</pre>
+<pre>public int <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1604">getRpcRefCount</a>(<a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockCacheKey.html" title="class in org.apache.hadoop.hbase.io.hfile">BlockCacheKey</a> cacheKey)</pre>
</li>
</ul>
<a name="getAcceptableFactor--">
@@ -2362,7 +2362,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getAcceptableFactor</h4>
-<pre>float <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1608">getAcceptableFactor</a>()</pre>
+<pre>float <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1612">getAcceptableFactor</a>()</pre>
</li>
</ul>
<a name="getMinFactor--">
@@ -2371,7 +2371,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getMinFactor</h4>
-<pre>float <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1612">getMinFactor</a>()</pre>
+<pre>float <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1616">getMinFactor</a>()</pre>
</li>
</ul>
<a name="getExtraFreeFactor--">
@@ -2380,7 +2380,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getExtraFreeFactor</h4>
-<pre>float <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1616">getExtraFreeFactor</a>()</pre>
+<pre>float <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1620">getExtraFreeFactor</a>()</pre>
</li>
</ul>
<a name="getSingleFactor--">
@@ -2389,7 +2389,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getSingleFactor</h4>
-<pre>float <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1620">getSingleFactor</a>()</pre>
+<pre>float <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1624">getSingleFactor</a>()</pre>
</li>
</ul>
<a name="getMultiFactor--">
@@ -2398,7 +2398,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockList">
<li class="blockList">
<h4>getMultiFactor</h4>
-<pre>float <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1624">getMultiFactor</a>()</pre>
+<pre>float <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1628">getMultiFactor</a>()</pre>
</li>
</ul>
<a name="getMemoryFactor--">
@@ -2407,7 +2407,7 @@ implements <a href="../../../../../../../org/apache/hadoop/hbase/io/hfile/BlockC
<ul class="blockListLast">
<li class="blockList">
<h4>getMemoryFactor</h4>
-<pre>float <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1628">getMemoryFactor</a>()</pre>
+<pre>float <a href="../../../../../../../src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.html#line.1632">getMemoryFactor</a>()</pre>
</li>
</ul>
</li>
diff --git a/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html b/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html
index 6e1ac11..5d27f48 100644
--- a/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html
+++ b/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.BucketEntryGroup.html
@@ -494,1226 +494,1230 @@
<span class="sourceLineNo">486</span> } else {<a name="line.486"></a>
<span class="sourceLineNo">487</span> this.blockNumber.increment();<a name="line.487"></a>
<span class="sourceLineNo">488</span> this.heapSize.add(cachedItem.heapSize());<a name="line.488"></a>
-<span class="sourceLineNo">489</span> blocksByHFile.add(cacheKey);<a name="line.489"></a>
-<span class="sourceLineNo">490</span> }<a name="line.490"></a>
-<span class="sourceLineNo">491</span> }<a name="line.491"></a>
-<span class="sourceLineNo">492</span><a name="line.492"></a>
-<span class="sourceLineNo">493</span> /**<a name="line.493"></a>
-<span class="sourceLineNo">494</span> * Get the buffer of the block with the specified key.<a name="line.494"></a>
-<span class="sourceLineNo">495</span> * @param key block's cache key<a name="line.495"></a>
-<span class="sourceLineNo">496</span> * @param caching true if the caller caches blocks on cache misses<a name="line.496"></a>
-<span class="sourceLineNo">497</span> * @param repeat Whether this is a repeat lookup for the same block<a name="line.497"></a>
-<span class="sourceLineNo">498</span> * @param updateCacheMetrics Whether we should update cache metrics or not<a name="line.498"></a>
-<span class="sourceLineNo">499</span> * @return buffer of specified cache key, or null if not in cache<a name="line.499"></a>
-<span class="sourceLineNo">500</span> */<a name="line.500"></a>
-<span class="sourceLineNo">501</span> @Override<a name="line.501"></a>
-<span class="sourceLineNo">502</span> public Cacheable getBlock(BlockCacheKey key, boolean caching, boolean repeat,<a name="line.502"></a>
-<span class="sourceLineNo">503</span> boolean updateCacheMetrics) {<a name="line.503"></a>
-<span class="sourceLineNo">504</span> if (!cacheEnabled) {<a name="line.504"></a>
-<span class="sourceLineNo">505</span> return null;<a name="line.505"></a>
-<span class="sourceLineNo">506</span> }<a name="line.506"></a>
-<span class="sourceLineNo">507</span> RAMQueueEntry re = ramCache.get(key);<a name="line.507"></a>
-<span class="sourceLineNo">508</span> if (re != null) {<a name="line.508"></a>
-<span class="sourceLineNo">509</span> if (updateCacheMetrics) {<a name="line.509"></a>
-<span class="sourceLineNo">510</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.510"></a>
-<span class="sourceLineNo">511</span> }<a name="line.511"></a>
-<span class="sourceLineNo">512</span> re.access(accessCount.incrementAndGet());<a name="line.512"></a>
-<span class="sourceLineNo">513</span> return re.getData();<a name="line.513"></a>
-<span class="sourceLineNo">514</span> }<a name="line.514"></a>
-<span class="sourceLineNo">515</span> BucketEntry bucketEntry = backingMap.get(key);<a name="line.515"></a>
-<span class="sourceLineNo">516</span> if (bucketEntry != null) {<a name="line.516"></a>
-<span class="sourceLineNo">517</span> long start = System.nanoTime();<a name="line.517"></a>
-<span class="sourceLineNo">518</span> ReentrantReadWriteLock lock = offsetLock.getLock(bucketEntry.offset());<a name="line.518"></a>
-<span class="sourceLineNo">519</span> try {<a name="line.519"></a>
-<span class="sourceLineNo">520</span> lock.readLock().lock();<a name="line.520"></a>
-<span class="sourceLineNo">521</span> // We can not read here even if backingMap does contain the given key because its offset<a name="line.521"></a>
-<span class="sourceLineNo">522</span> // maybe changed. If we lock BlockCacheKey instead of offset, then we can only check<a name="line.522"></a>
-<span class="sourceLineNo">523</span> // existence here.<a name="line.523"></a>
-<span class="sourceLineNo">524</span> if (bucketEntry.equals(backingMap.get(key))) {<a name="line.524"></a>
-<span class="sourceLineNo">525</span> // Read the block from IOEngine based on the bucketEntry's offset and length, NOTICE: the<a name="line.525"></a>
-<span class="sourceLineNo">526</span> // block will use the refCnt of bucketEntry, which means if two HFileBlock mapping to<a name="line.526"></a>
-<span class="sourceLineNo">527</span> // the same BucketEntry, then all of the three will share the same refCnt.<a name="line.527"></a>
-<span class="sourceLineNo">528</span> Cacheable cachedBlock = ioEngine.read(bucketEntry);<a name="line.528"></a>
-<span class="sourceLineNo">529</span> if (ioEngine.usesSharedMemory()) {<a name="line.529"></a>
-<span class="sourceLineNo">530</span> // If IOEngine use shared memory, cachedBlock and BucketEntry will share the<a name="line.530"></a>
-<span class="sourceLineNo">531</span> // same RefCnt, do retain here, in order to count the number of RPC references<a name="line.531"></a>
-<span class="sourceLineNo">532</span> cachedBlock.retain();<a name="line.532"></a>
-<span class="sourceLineNo">533</span> }<a name="line.533"></a>
-<span class="sourceLineNo">534</span> // Update the cache statistics.<a name="line.534"></a>
-<span class="sourceLineNo">535</span> if (updateCacheMetrics) {<a name="line.535"></a>
-<span class="sourceLineNo">536</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.536"></a>
-<span class="sourceLineNo">537</span> cacheStats.ioHit(System.nanoTime() - start);<a name="line.537"></a>
-<span class="sourceLineNo">538</span> }<a name="line.538"></a>
-<span class="sourceLineNo">539</span> bucketEntry.access(accessCount.incrementAndGet());<a name="line.539"></a>
-<span class="sourceLineNo">540</span> if (this.ioErrorStartTime > 0) {<a name="line.540"></a>
-<span class="sourceLineNo">541</span> ioErrorStartTime = -1;<a name="line.541"></a>
-<span class="sourceLineNo">542</span> }<a name="line.542"></a>
-<span class="sourceLineNo">543</span> return cachedBlock;<a name="line.543"></a>
-<span class="sourceLineNo">544</span> }<a name="line.544"></a>
-<span class="sourceLineNo">545</span> } catch (IOException ioex) {<a name="line.545"></a>
-<span class="sourceLineNo">546</span> LOG.error("Failed reading block " + key + " from bucket cache", ioex);<a name="line.546"></a>
-<span class="sourceLineNo">547</span> checkIOErrorIsTolerated();<a name="line.547"></a>
-<span class="sourceLineNo">548</span> } finally {<a name="line.548"></a>
-<span class="sourceLineNo">549</span> lock.readLock().unlock();<a name="line.549"></a>
-<span class="sourceLineNo">550</span> }<a name="line.550"></a>
-<span class="sourceLineNo">551</span> }<a name="line.551"></a>
-<span class="sourceLineNo">552</span> if (!repeat && updateCacheMetrics) {<a name="line.552"></a>
-<span class="sourceLineNo">553</span> cacheStats.miss(caching, key.isPrimary(), key.getBlockType());<a name="line.553"></a>
-<span class="sourceLineNo">554</span> }<a name="line.554"></a>
-<span class="sourceLineNo">555</span> return null;<a name="line.555"></a>
-<span class="sourceLineNo">556</span> }<a name="line.556"></a>
-<span class="sourceLineNo">557</span><a name="line.557"></a>
-<span class="sourceLineNo">558</span> /**<a name="line.558"></a>
-<span class="sourceLineNo">559</span> * This method is invoked after the bucketEntry is removed from {@link BucketCache#backingMap}<a name="line.559"></a>
-<span class="sourceLineNo">560</span> */<a name="line.560"></a>
-<span class="sourceLineNo">561</span> void blockEvicted(BlockCacheKey cacheKey, BucketEntry bucketEntry, boolean decrementBlockNumber) {<a name="line.561"></a>
-<span class="sourceLineNo">562</span> bucketEntry.markAsEvicted();<a name="line.562"></a>
-<span class="sourceLineNo">563</span> blocksByHFile.remove(cacheKey);<a name="line.563"></a>
-<span class="sourceLineNo">564</span> if (decrementBlockNumber) {<a name="line.564"></a>
-<span class="sourceLineNo">565</span> this.blockNumber.decrement();<a name="line.565"></a>
-<span class="sourceLineNo">566</span> }<a name="line.566"></a>
-<span class="sourceLineNo">567</span> cacheStats.evicted(bucketEntry.getCachedTime(), cacheKey.isPrimary());<a name="line.567"></a>
-<span class="sourceLineNo">568</span> }<a name="line.568"></a>
-<span class="sourceLineNo">569</span><a name="line.569"></a>
-<span class="sourceLineNo">570</span> /**<a name="line.570"></a>
-<span class="sourceLineNo">571</span> * Free the {{@link BucketEntry} actually,which could only be invoked when the<a name="line.571"></a>
-<span class="sourceLineNo">572</span> * {@link BucketEntry#refCnt} becoming 0.<a name="line.572"></a>
-<span class="sourceLineNo">573</span> */<a name="line.573"></a>
-<span class="sourceLineNo">574</span> void freeBucketEntry(BucketEntry bucketEntry) {<a name="line.574"></a>
-<span class="sourceLineNo">575</span> bucketAllocator.freeBlock(bucketEntry.offset());<a name="line.575"></a>
-<span class="sourceLineNo">576</span> realCacheSize.add(-1 * bucketEntry.getLength());<a name="line.576"></a>
-<span class="sourceLineNo">577</span> }<a name="line.577"></a>
-<span class="sourceLineNo">578</span><a name="line.578"></a>
-<span class="sourceLineNo">579</span> /**<a name="line.579"></a>
-<span class="sourceLineNo">580</span> * Try to evict the block from {@link BlockCache} by force. We'll call this in few cases:<br><a name="line.580"></a>
-<span class="sourceLineNo">581</span> * 1. Close an HFile, and clear all cached blocks. <br><a name="line.581"></a>
-<span class="sourceLineNo">582</span> * 2. Call {@link Admin#clearBlockCache(TableName)} to clear all blocks for a given table.<br><a name="line.582"></a>
-<span class="sourceLineNo">583</span> * <p><a name="line.583"></a>
-<span class="sourceLineNo">584</span> * Firstly, we'll try to remove the block from RAMCache,and then try to evict from backingMap.<a name="line.584"></a>
-<span class="sourceLineNo">585</span> * Here we evict the block from backingMap immediately, but only free the reference from bucket<a name="line.585"></a>
-<span class="sourceLineNo">586</span> * cache by calling {@link BucketEntry#markedAsEvicted}. If there're still some RPC referring this<a name="line.586"></a>
-<span class="sourceLineNo">587</span> * block, block can only be de-allocated when all of them release the block.<a name="line.587"></a>
-<span class="sourceLineNo">588</span> * <p><a name="line.588"></a>
-<span class="sourceLineNo">589</span> * NOTICE: we need to grab the write offset lock firstly before releasing the reference from<a name="line.589"></a>
-<span class="sourceLineNo">590</span> * bucket cache. if we don't, we may read an {@link BucketEntry} with refCnt = 0 when<a name="line.590"></a>
-<span class="sourceLineNo">591</span> * {@link BucketCache#getBlock(BlockCacheKey, boolean, boolean, boolean)}, it's a memory leak.<a name="line.591"></a>
-<span class="sourceLineNo">592</span> * @param cacheKey Block to evict<a name="line.592"></a>
-<span class="sourceLineNo">593</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.593"></a>
-<span class="sourceLineNo">594</span> */<a name="line.594"></a>
-<span class="sourceLineNo">595</span> @Override<a name="line.595"></a>
-<span class="sourceLineNo">596</span> public boolean evictBlock(BlockCacheKey cacheKey) {<a name="line.596"></a>
-<span class="sourceLineNo">597</span> return doEvictBlock(cacheKey, null);<a name="line.597"></a>
-<span class="sourceLineNo">598</span> }<a name="line.598"></a>
-<span class="sourceLineNo">599</span><a name="line.599"></a>
-<span class="sourceLineNo">600</span> /**<a name="line.600"></a>
-<span class="sourceLineNo">601</span> * Evict the {@link BlockCacheKey} and {@link BucketEntry} from {@link BucketCache#backingMap} and<a name="line.601"></a>
-<span class="sourceLineNo">602</span> * {@link BucketCache#ramCache}. <br/><a name="line.602"></a>
-<span class="sourceLineNo">603</span> * NOTE:When Evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.603"></a>
-<span class="sourceLineNo">604</span> * {@link BucketEntry} could be removed.<a name="line.604"></a>
-<span class="sourceLineNo">605</span> * @param cacheKey {@link BlockCacheKey} to evict.<a name="line.605"></a>
-<span class="sourceLineNo">606</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.606"></a>
-<span class="sourceLineNo">607</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.607"></a>
-<span class="sourceLineNo">608</span> */<a name="line.608"></a>
-<span class="sourceLineNo">609</span> private boolean doEvictBlock(BlockCacheKey cacheKey, BucketEntry bucketEntry) {<a name="line.609"></a>
-<span class="sourceLineNo">610</span> if (!cacheEnabled) {<a name="line.610"></a>
-<span class="sourceLineNo">611</span> return false;<a name="line.611"></a>
-<span class="sourceLineNo">612</span> }<a name="line.612"></a>
-<span class="sourceLineNo">613</span> boolean existedInRamCache = removeFromRamCache(cacheKey);<a name="line.613"></a>
-<span class="sourceLineNo">614</span> if (bucketEntry == null) {<a name="line.614"></a>
-<span class="sourceLineNo">615</span> bucketEntry = backingMap.get(cacheKey);<a name="line.615"></a>
-<span class="sourceLineNo">616</span> }<a name="line.616"></a>
-<span class="sourceLineNo">617</span> final BucketEntry bucketEntryToUse = bucketEntry;<a name="line.617"></a>
-<span class="sourceLineNo">618</span><a name="line.618"></a>
-<span class="sourceLineNo">619</span> if (bucketEntryToUse == null) {<a name="line.619"></a>
-<span class="sourceLineNo">620</span> if (existedInRamCache) {<a name="line.620"></a>
-<span class="sourceLineNo">621</span> cacheStats.evicted(0, cacheKey.isPrimary());<a name="line.621"></a>
-<span class="sourceLineNo">622</span> }<a name="line.622"></a>
-<span class="sourceLineNo">623</span> return existedInRamCache;<a name="line.623"></a>
-<span class="sourceLineNo">624</span> } else {<a name="line.624"></a>
-<span class="sourceLineNo">625</span> return bucketEntryToUse.withWriteLock(offsetLock, () -> {<a name="line.625"></a>
-<span class="sourceLineNo">626</span> if (backingMap.remove(cacheKey, bucketEntryToUse)) {<a name="line.626"></a>
-<span class="sourceLineNo">627</span> blockEvicted(cacheKey, bucketEntryToUse, !existedInRamCache);<a name="line.627"></a>
-<span class="sourceLineNo">628</span> return true;<a name="line.628"></a>
-<span class="sourceLineNo">629</span> }<a name="line.629"></a>
-<span class="sourceLineNo">630</span> return false;<a name="line.630"></a>
-<span class="sourceLineNo">631</span> });<a name="line.631"></a>
-<span class="sourceLineNo">632</span> }<a name="line.632"></a>
-<span class="sourceLineNo">633</span> }<a name="line.633"></a>
-<span class="sourceLineNo">634</span><a name="line.634"></a>
-<span class="sourceLineNo">635</span> /**<a name="line.635"></a>
-<span class="sourceLineNo">636</span> * <pre><a name="line.636"></a>
-<span class="sourceLineNo">637</span> * Create the {@link Recycler} for {@link BucketEntry#refCnt},which would be used as<a name="line.637"></a>
-<span class="sourceLineNo">638</span> * {@link RefCnt#recycler} of {@link HFileBlock#buf} returned from {@link BucketCache#getBlock}.<a name="line.638"></a>
-<span class="sourceLineNo">639</span> * NOTE: for {@link BucketCache#getBlock},the {@link RefCnt#recycler} of {@link HFileBlock#buf}<a name="line.639"></a>
-<span class="sourceLineNo">640</span> * from {@link BucketCache#backingMap} and {@link BucketCache#ramCache} are different:<a name="line.640"></a>
-<span class="sourceLineNo">641</span> * 1.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#backingMap},<a name="line.641"></a>
-<span class="sourceLineNo">642</span> * it is the return value of current {@link BucketCache#createRecycler} method.<a name="line.642"></a>
-<span class="sourceLineNo">643</span> *<a name="line.643"></a>
-<span class="sourceLineNo">644</span> * 2.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#ramCache},<a name="line.644"></a>
-<span class="sourceLineNo">645</span> * it is {@link ByteBuffAllocator#putbackBuffer}.<a name="line.645"></a>
-<span class="sourceLineNo">646</span> * </pre><a name="line.646"></a>
-<span class="sourceLineNo">647</span> */<a name="line.647"></a>
-<span class="sourceLineNo">648</span> private Recycler createRecycler(final BucketEntry bucketEntry) {<a name="line.648"></a>
-<span class="sourceLineNo">649</span> return () -> {<a name="line.649"></a>
-<span class="sourceLineNo">650</span> freeBucketEntry(bucketEntry);<a name="line.650"></a>
-<span class="sourceLineNo">651</span> return;<a name="line.651"></a>
-<span class="sourceLineNo">652</span> };<a name="line.652"></a>
-<span class="sourceLineNo">653</span> }<a name="line.653"></a>
-<span class="sourceLineNo">654</span><a name="line.654"></a>
-<span class="sourceLineNo">655</span> /**<a name="line.655"></a>
-<span class="sourceLineNo">656</span> * NOTE: This method is only for test.<a name="line.656"></a>
-<span class="sourceLineNo">657</span> */<a name="line.657"></a>
-<span class="sourceLineNo">658</span> public boolean evictBlockIfNoRpcReferenced(BlockCacheKey blockCacheKey) {<a name="line.658"></a>
-<span class="sourceLineNo">659</span> BucketEntry bucketEntry = backingMap.get(blockCacheKey);<a name="line.659"></a>
-<span class="sourceLineNo">660</span> if (bucketEntry == null) {<a name="line.660"></a>
-<span class="sourceLineNo">661</span> return false;<a name="line.661"></a>
-<span class="sourceLineNo">662</span> }<a name="line.662"></a>
-<span class="sourceLineNo">663</span> return evictBucketEntryIfNoRpcReferenced(blockCacheKey, bucketEntry);<a name="line.663"></a>
-<span class="sourceLineNo">664</span> }<a name="line.664"></a>
-<span class="sourceLineNo">665</span><a name="line.665"></a>
-<span class="sourceLineNo">666</span> /**<a name="line.666"></a>
-<span class="sourceLineNo">667</span> * Evict {@link BlockCacheKey} and its corresponding {@link BucketEntry} only if<a name="line.667"></a>
-<span class="sourceLineNo">668</span> * {@link BucketEntry#isRpcRef} is false. <br/><a name="line.668"></a>
-<span class="sourceLineNo">669</span> * NOTE:When evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.669"></a>
-<span class="sourceLineNo">670</span> * {@link BucketEntry} could be removed.<a name="line.670"></a>
-<span class="sourceLineNo">671</span> * @param blockCacheKey {@link BlockCacheKey} to evict.<a name="line.671"></a>
-<span class="sourceLineNo">672</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.672"></a>
-<span class="sourceLineNo">673</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.673"></a>
-<span class="sourceLineNo">674</span> */<a name="line.674"></a>
-<span class="sourceLineNo">675</span> boolean evictBucketEntryIfNoRpcReferenced(BlockCacheKey blockCacheKey, BucketEntry bucketEntry) {<a name="line.675"></a>
-<span class="sourceLineNo">676</span> if (!bucketEntry.isRpcRef()) {<a name="line.676"></a>
-<span class="sourceLineNo">677</span> return doEvictBlock(blockCacheKey, bucketEntry);<a name="line.677"></a>
-<span class="sourceLineNo">678</span> }<a name="line.678"></a>
-<span class="sourceLineNo">679</span> return false;<a name="line.679"></a>
-<span class="sourceLineNo">680</span> }<a name="line.680"></a>
-<span class="sourceLineNo">681</span><a name="line.681"></a>
-<span class="sourceLineNo">682</span> protected boolean removeFromRamCache(BlockCacheKey cacheKey) {<a name="line.682"></a>
-<span class="sourceLineNo">683</span> return ramCache.remove(cacheKey, re -> {<a name="line.683"></a>
-<span class="sourceLineNo">684</span> if (re != null) {<a name="line.684"></a>
-<span class="sourceLineNo">685</span> this.blockNumber.decrement();<a name="line.685"></a>
-<span class="sourceLineNo">686</span> this.heapSize.add(-1 * re.getData().heapSize());<a name="line.686"></a>
-<span class="sourceLineNo">687</span> }<a name="line.687"></a>
-<span class="sourceLineNo">688</span> });<a name="line.688"></a>
-<span class="sourceLineNo">689</span> }<a name="line.689"></a>
-<span class="sourceLineNo">690</span><a name="line.690"></a>
-<span class="sourceLineNo">691</span> /*<a name="line.691"></a>
-<span class="sourceLineNo">692</span> * Statistics thread. Periodically output cache statistics to the log.<a name="line.692"></a>
-<span class="sourceLineNo">693</span> */<a name="line.693"></a>
-<span class="sourceLineNo">694</span> private static class StatisticsThread extends Thread {<a name="line.694"></a>
-<span class="sourceLineNo">695</span> private final BucketCache bucketCache;<a name="line.695"></a>
-<span class="sourceLineNo">696</span><a name="line.696"></a>
-<span class="sourceLineNo">697</span> public StatisticsThread(BucketCache bucketCache) {<a name="line.697"></a>
-<span class="sourceLineNo">698</span> super("BucketCacheStatsThread");<a name="line.698"></a>
-<span class="sourceLineNo">699</span> setDaemon(true);<a name="line.699"></a>
-<span class="sourceLineNo">700</span> this.bucketCache = bucketCache;<a name="line.700"></a>
-<span class="sourceLineNo">701</span> }<a name="line.701"></a>
-<span class="sourceLineNo">702</span><a name="line.702"></a>
-<span class="sourceLineNo">703</span> @Override<a name="line.703"></a>
-<span class="sourceLineNo">704</span> public void run() {<a name="line.704"></a>
-<span class="sourceLineNo">705</span> bucketCache.logStats();<a name="line.705"></a>
-<span class="sourceLineNo">706</span> }<a name="line.706"></a>
-<span class="sourceLineNo">707</span> }<a name="line.707"></a>
-<span class="sourceLineNo">708</span><a name="line.708"></a>
-<span class="sourceLineNo">709</span> public void logStats() {<a name="line.709"></a>
-<span class="sourceLineNo">710</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.710"></a>
-<span class="sourceLineNo">711</span> long usedSize = bucketAllocator.getUsedSize();<a name="line.711"></a>
-<span class="sourceLineNo">712</span> long freeSize = totalSize - usedSize;<a name="line.712"></a>
-<span class="sourceLineNo">713</span> long cacheSize = getRealCacheSize();<a name="line.713"></a>
-<span class="sourceLineNo">714</span> LOG.info("failedBlockAdditions=" + cacheStats.getFailedInserts() + ", " +<a name="line.714"></a>
-<span class="sourceLineNo">715</span> "totalSize=" + StringUtils.byteDesc(totalSize) + ", " +<a name="line.715"></a>
-<span class="sourceLineNo">716</span> "freeSize=" + StringUtils.byteDesc(freeSize) + ", " +<a name="line.716"></a>
-<span class="sourceLineNo">717</span> "usedSize=" + StringUtils.byteDesc(usedSize) +", " +<a name="line.717"></a>
-<span class="sourceLineNo">718</span> "cacheSize=" + StringUtils.byteDesc(cacheSize) +", " +<a name="line.718"></a>
-<span class="sourceLineNo">719</span> "accesses=" + cacheStats.getRequestCount() + ", " +<a name="line.719"></a>
-<span class="sourceLineNo">720</span> "hits=" + cacheStats.getHitCount() + ", " +<a name="line.720"></a>
-<span class="sourceLineNo">721</span> "IOhitsPerSecond=" + cacheStats.getIOHitsPerSecond() + ", " +<a name="line.721"></a>
-<span class="sourceLineNo">722</span> "IOTimePerHit=" + String.format("%.2f", cacheStats.getIOTimePerHit())+ ", " +<a name="line.722"></a>
-<span class="sourceLineNo">723</span> "hitRatio=" + (cacheStats.getHitCount() == 0 ? "0," :<a name="line.723"></a>
-<span class="sourceLineNo">724</span> (StringUtils.formatPercent(cacheStats.getHitRatio(), 2)+ ", ")) +<a name="line.724"></a>
-<span class="sourceLineNo">725</span> "cachingAccesses=" + cacheStats.getRequestCachingCount() + ", " +<a name="line.725"></a>
-<span class="sourceLineNo">726</span> "cachingHits=" + cacheStats.getHitCachingCount() + ", " +<a name="line.726"></a>
-<span class="sourceLineNo">727</span> "cachingHitsRatio=" +(cacheStats.getHitCachingCount() == 0 ? "0," :<a name="line.727"></a>
-<span class="sourceLineNo">728</span> (StringUtils.formatPercent(cacheStats.getHitCachingRatio(), 2)+ ", ")) +<a name="line.728"></a>
-<span class="sourceLineNo">729</span> "evictions=" + cacheStats.getEvictionCount() + ", " +<a name="line.729"></a>
-<span class="sourceLineNo">730</span> "evicted=" + cacheStats.getEvictedCount() + ", " +<a name="line.730"></a>
-<span class="sourceLineNo">731</span> "evictedPerRun=" + cacheStats.evictedPerEviction());<a name="line.731"></a>
-<span class="sourceLineNo">732</span> cacheStats.reset();<a name="line.732"></a>
-<span class="sourceLineNo">733</span> }<a name="line.733"></a>
-<span class="sourceLineNo">734</span><a name="line.734"></a>
-<span class="sourceLineNo">735</span> public long getRealCacheSize() {<a name="line.735"></a>
-<span class="sourceLineNo">736</span> return this.realCacheSize.sum();<a name="line.736"></a>
-<span class="sourceLineNo">737</span> }<a name="line.737"></a>
-<span class="sourceLineNo">738</span><a name="line.738"></a>
-<span class="sourceLineNo">739</span> public long acceptableSize() {<a name="line.739"></a>
-<span class="sourceLineNo">740</span> return (long) Math.floor(bucketAllocator.getTotalSize() * acceptableFactor);<a name="line.740"></a>
-<span class="sourceLineNo">741</span> }<a name="line.741"></a>
-<span class="sourceLineNo">742</span><a name="line.742"></a>
-<span class="sourceLineNo">743</span> long getPartitionSize(float partitionFactor) {<a name="line.743"></a>
-<span class="sourceLineNo">744</span> return (long) Math.floor(bucketAllocator.getTotalSize() * partitionFactor * minFactor);<a name="line.744"></a>
-<span class="sourceLineNo">745</span> }<a name="line.745"></a>
-<span class="sourceLineNo">746</span><a name="line.746"></a>
-<span class="sourceLineNo">747</span> /**<a name="line.747"></a>
-<span class="sourceLineNo">748</span> * Return the count of bucketSizeinfos still need free space<a name="line.748"></a>
-<span class="sourceLineNo">749</span> */<a name="line.749"></a>
-<span class="sourceLineNo">750</span> private int bucketSizesAboveThresholdCount(float minFactor) {<a name="line.750"></a>
-<span class="sourceLineNo">751</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.751"></a>
-<span class="sourceLineNo">752</span> int fullCount = 0;<a name="line.752"></a>
-<span class="sourceLineNo">753</span> for (int i = 0; i < stats.length; i++) {<a name="line.753"></a>
-<span class="sourceLineNo">754</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.754"></a>
-<span class="sourceLineNo">755</span> freeGoal = Math.max(freeGoal, 1);<a name="line.755"></a>
-<span class="sourceLineNo">756</span> if (stats[i].freeCount() < freeGoal) {<a name="line.756"></a>
-<span class="sourceLineNo">757</span> fullCount++;<a name="line.757"></a>
-<span class="sourceLineNo">758</span> }<a name="line.758"></a>
-<span class="sourceLineNo">759</span> }<a name="line.759"></a>
-<span class="sourceLineNo">760</span> return fullCount;<a name="line.760"></a>
-<span class="sourceLineNo">761</span> }<a name="line.761"></a>
-<span class="sourceLineNo">762</span><a name="line.762"></a>
-<span class="sourceLineNo">763</span> /**<a name="line.763"></a>
-<span class="sourceLineNo">764</span> * This method will find the buckets that are minimally occupied<a name="line.764"></a>
-<span class="sourceLineNo">765</span> * and are not reference counted and will free them completely<a name="line.765"></a>
-<span class="sourceLineNo">766</span> * without any constraint on the access times of the elements,<a name="line.766"></a>
-<span class="sourceLineNo">767</span> * and as a process will completely free at most the number of buckets<a name="line.767"></a>
-<span class="sourceLineNo">768</span> * passed, sometimes it might not due to changing refCounts<a name="line.768"></a>
-<span class="sourceLineNo">769</span> *<a name="line.769"></a>
-<span class="sourceLineNo">770</span> * @param completelyFreeBucketsNeeded number of buckets to free<a name="line.770"></a>
-<span class="sourceLineNo">771</span> **/<a name="line.771"></a>
-<span class="sourceLineNo">772</span> private void freeEntireBuckets(int completelyFreeBucketsNeeded) {<a name="line.772"></a>
-<span class="sourceLineNo">773</span> if (completelyFreeBucketsNeeded != 0) {<a name="line.773"></a>
-<span class="sourceLineNo">774</span> // First we will build a set where the offsets are reference counted, usually<a name="line.774"></a>
-<span class="sourceLineNo">775</span> // this set is small around O(Handler Count) unless something else is wrong<a name="line.775"></a>
-<span class="sourceLineNo">776</span> Set<Integer> inUseBuckets = new HashSet<>();<a name="line.776"></a>
-<span class="sourceLineNo">777</span> backingMap.forEach((k, be) -> {<a name="line.777"></a>
-<span class="sourceLineNo">778</span> if (be.isRpcRef()) {<a name="line.778"></a>
-<span class="sourceLineNo">779</span> inUseBuckets.add(bucketAllocator.getBucketIndex(be.offset()));<a name="line.779"></a>
-<span class="sourceLineNo">780</span> }<a name="line.780"></a>
-<span class="sourceLineNo">781</span> });<a name="line.781"></a>
-<span class="sourceLineNo">782</span> Set<Integer> candidateBuckets =<a name="line.782"></a>
-<span class="sourceLineNo">783</span> bucketAllocator.getLeastFilledBuckets(inUseBuckets, completelyFreeBucketsNeeded);<a name="line.783"></a>
-<span class="sourceLineNo">784</span> for (Map.Entry<BlockCacheKey, BucketEntry> entry : backingMap.entrySet()) {<a name="line.784"></a>
-<span class="sourceLineNo">785</span> if (candidateBuckets.contains(bucketAllocator.getBucketIndex(entry.getValue().offset()))) {<a name="line.785"></a>
-<span class="sourceLineNo">786</span> evictBucketEntryIfNoRpcReferenced(entry.getKey(), entry.getValue());<a name="line.786"></a>
-<span class="sourceLineNo">787</span> }<a name="line.787"></a>
-<span class="sourceLineNo">788</span> }<a name="line.788"></a>
-<span class="sourceLineNo">789</span> }<a name="line.789"></a>
-<span class="sourceLineNo">790</span> }<a name="line.790"></a>
-<span class="sourceLineNo">791</span><a name="line.791"></a>
-<span class="sourceLineNo">792</span> /**<a name="line.792"></a>
-<span class="sourceLineNo">793</span> * Free the space if the used size reaches acceptableSize() or one size block<a name="line.793"></a>
-<span class="sourceLineNo">794</span> * couldn't be allocated. When freeing the space, we use the LRU algorithm and<a name="line.794"></a>
-<span class="sourceLineNo">795</span> * ensure there must be some blocks evicted<a name="line.795"></a>
-<span class="sourceLineNo">796</span> * @param why Why we are being called<a name="line.796"></a>
-<span class="sourceLineNo">797</span> */<a name="line.797"></a>
-<span class="sourceLineNo">798</span> private void freeSpace(final String why) {<a name="line.798"></a>
-<span class="sourceLineNo">799</span> // Ensure only one freeSpace progress at a time<a name="line.799"></a>
-<span class="sourceLineNo">800</span> if (!freeSpaceLock.tryLock()) {<a name="line.800"></a>
-<span class="sourceLineNo">801</span> return;<a name="line.801"></a>
-<span class="sourceLineNo">802</span> }<a name="line.802"></a>
-<span class="sourceLineNo">803</span> try {<a name="line.803"></a>
-<span class="sourceLineNo">804</span> freeInProgress = true;<a name="line.804"></a>
-<span class="sourceLineNo">805</span> long bytesToFreeWithoutExtra = 0;<a name="line.805"></a>
-<span class="sourceLineNo">806</span> // Calculate free byte for each bucketSizeinfo<a name="line.806"></a>
-<span class="sourceLineNo">807</span> StringBuilder msgBuffer = LOG.isDebugEnabled()? new StringBuilder(): null;<a name="line.807"></a>
-<span class="sourceLineNo">808</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.808"></a>
-<span class="sourceLineNo">809</span> long[] bytesToFreeForBucket = new long[stats.length];<a name="line.809"></a>
-<span class="sourceLineNo">810</span> for (int i = 0; i < stats.length; i++) {<a name="line.810"></a>
-<span class="sourceLineNo">811</span> bytesToFreeForBucket[i] = 0;<a name="line.811"></a>
-<span class="sourceLineNo">812</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.812"></a>
-<span class="sourceLineNo">813</span> freeGoal = Math.max(freeGoal, 1);<a name="line.813"></a>
-<span class="sourceLineNo">814</span> if (stats[i].freeCount() < freeGoal) {<a name="line.814"></a>
-<span class="sourceLineNo">815</span> bytesToFreeForBucket[i] = stats[i].itemSize() * (freeGoal - stats[i].freeCount());<a name="line.815"></a>
-<span class="sourceLineNo">816</span> bytesToFreeWithoutExtra += bytesToFreeForBucket[i];<a name="line.816"></a>
-<span class="sourceLineNo">817</span> if (msgBuffer != null) {<a name="line.817"></a>
-<span class="sourceLineNo">818</span> msgBuffer.append("Free for bucketSize(" + stats[i].itemSize() + ")="<a name="line.818"></a>
-<span class="sourceLineNo">819</span> + StringUtils.byteDesc(bytesToFreeForBucket[i]) + ", ");<a name="line.819"></a>
-<span class="sourceLineNo">820</span> }<a name="line.820"></a>
-<span class="sourceLineNo">821</span> }<a name="line.821"></a>
-<span class="sourceLineNo">822</span> }<a name="line.822"></a>
-<span class="sourceLineNo">823</span> if (msgBuffer != null) {<a name="line.823"></a>
-<span class="sourceLineNo">824</span> msgBuffer.append("Free for total=" + StringUtils.byteDesc(bytesToFreeWithoutExtra) + ", ");<a name="line.824"></a>
-<span class="sourceLineNo">825</span> }<a name="line.825"></a>
-<span class="sourceLineNo">826</span><a name="line.826"></a>
-<span class="sourceLineNo">827</span> if (bytesToFreeWithoutExtra <= 0) {<a name="line.827"></a>
-<span class="sourceLineNo">828</span> return;<a name="line.828"></a>
-<span class="sourceLineNo">829</span> }<a name="line.829"></a>
-<span class="sourceLineNo">830</span> long currentSize = bucketAllocator.getUsedSize();<a name="line.830"></a>
-<span class="sourceLineNo">831</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.831"></a>
-<span class="sourceLineNo">832</span> if (LOG.isDebugEnabled() && msgBuffer != null) {<a name="line.832"></a>
-<span class="sourceLineNo">833</span> LOG.debug("Free started because \"" + why + "\"; " + msgBuffer.toString() +<a name="line.833"></a>
-<span class="sourceLineNo">834</span> " of current used=" + StringUtils.byteDesc(currentSize) + ", actual cacheSize=" +<a name="line.834"></a>
-<span class="sourceLineNo">835</span> StringUtils.byteDesc(realCacheSize.sum()) + ", total=" + StringUtils.byteDesc(totalSize));<a name="line.835"></a>
-<span class="sourceLineNo">836</span> }<a name="line.836"></a>
-<span class="sourceLineNo">837</span><a name="line.837"></a>
-<span class="sourceLineNo">838</span> long bytesToFreeWithExtra = (long) Math.floor(bytesToFreeWithoutExtra<a name="line.838"></a>
-<span class="sourceLineNo">839</span> * (1 + extraFreeFactor));<a name="line.839"></a>
-<span class="sourceLineNo">840</span><a name="line.840"></a>
-<span class="sourceLineNo">841</span> // Instantiate priority buckets<a name="line.841"></a>
-<span class="sourceLineNo">842</span> BucketEntryGroup bucketSingle = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.842"></a>
-<span class="sourceLineNo">843</span> blockSize, getPartitionSize(singleFactor));<a name="line.843"></a>
-<span class="sourceLineNo">844</span> BucketEntryGroup bucketMulti = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.844"></a>
-<span class="sourceLineNo">845</span> blockSize, getPartitionSize(multiFactor));<a name="line.845"></a>
-<span class="sourceLineNo">846</span> BucketEntryGroup bucketMemory = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.846"></a>
-<span class="sourceLineNo">847</span> blockSize, getPartitionSize(memoryFactor));<a name="line.847"></a>
-<span class="sourceLineNo">848</span><a name="line.848"></a>
-<span class="sourceLineNo">849</span> // Scan entire map putting bucket entry into appropriate bucket entry<a name="line.849"></a>
-<span class="sourceLineNo">850</span> // group<a name="line.850"></a>
-<span class="sourceLineNo">851</span> for (Map.Entry<BlockCacheKey, BucketEntry> bucketEntryWithKey : backingMap.entrySet()) {<a name="line.851"></a>
-<span class="sourceLineNo">852</span> switch (bucketEntryWithKey.getValue().getPriority()) {<a name="line.852"></a>
-<span class="sourceLineNo">853</span> case SINGLE: {<a name="line.853"></a>
-<span class="sourceLineNo">854</span> bucketSingle.add(bucketEntryWithKey);<a name="line.854"></a>
-<span class="sourceLineNo">855</span> break;<a name="line.855"></a>
-<span class="sourceLineNo">856</span> }<a name="line.856"></a>
-<span class="sourceLineNo">857</span> case MULTI: {<a name="line.857"></a>
-<span class="sourceLineNo">858</span> bucketMulti.add(bucketEntryWithKey);<a name="line.858"></a>
-<span class="sourceLineNo">859</span> break;<a name="line.859"></a>
-<span class="sourceLineNo">860</span> }<a name="line.860"></a>
-<span class="sourceLineNo">861</span> case MEMORY: {<a name="line.861"></a>
-<span class="sourceLineNo">862</span> bucketMemory.add(bucketEntryWithKey);<a name="line.862"></a>
-<span class="sourceLineNo">863</span> break;<a name="line.863"></a>
-<span class="sourceLineNo">864</span> }<a name="line.864"></a>
-<span class="sourceLineNo">865</span> }<a name="line.865"></a>
-<span class="sourceLineNo">866</span> }<a name="line.866"></a>
-<span class="sourceLineNo">867</span><a name="line.867"></a>
-<span class="sourceLineNo">868</span> PriorityQueue<BucketEntryGroup> bucketQueue = new PriorityQueue<>(3,<a name="line.868"></a>
-<span class="sourceLineNo">869</span> Comparator.comparingLong(BucketEntryGroup::overflow));<a name="line.869"></a>
-<span class="sourceLineNo">870</span><a name="line.870"></a>
-<span class="sourceLineNo">871</span> bucketQueue.add(bucketSingle);<a name="line.871"></a>
-<span class="sourceLineNo">872</span> bucketQueue.add(bucketMulti);<a name="line.872"></a>
-<span class="sourceLineNo">873</span> bucketQueue.add(bucketMemory);<a name="line.873"></a>
-<span class="sourceLineNo">874</span><a name="line.874"></a>
-<span class="sourceLineNo">875</span> int remainingBuckets = bucketQueue.size();<a name="line.875"></a>
-<span class="sourceLineNo">876</span> long bytesFreed = 0;<a name="line.876"></a>
-<span class="sourceLineNo">877</span><a name="line.877"></a>
-<span class="sourceLineNo">878</span> BucketEntryGroup bucketGroup;<a name="line.878"></a>
-<span class="sourceLineNo">879</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.879"></a>
-<span class="sourceLineNo">880</span> long overflow = bucketGroup.overflow();<a name="line.880"></a>
-<span class="sourceLineNo">881</span> if (overflow > 0) {<a name="line.881"></a>
-<span class="sourceLineNo">882</span> long bucketBytesToFree = Math.min(overflow,<a name="line.882"></a>
-<span class="sourceLineNo">883</span> (bytesToFreeWithoutExtra - bytesFreed) / remainingBuckets);<a name="line.883"></a>
-<span class="sourceLineNo">884</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.884"></a>
-<span class="sourceLineNo">885</span> }<a name="line.885"></a>
-<span class="sourceLineNo">886</span> remainingBuckets--;<a name="line.886"></a>
-<span class="sourceLineNo">887</span> }<a name="line.887"></a>
-<span class="sourceLineNo">888</span><a name="line.888"></a>
-<span class="sourceLineNo">889</span> // Check and free if there are buckets that still need freeing of space<a name="line.889"></a>
-<span class="sourceLineNo">890</span> if (bucketSizesAboveThresholdCount(minFactor) > 0) {<a name="line.890"></a>
-<span class="sourceLineNo">891</span> bucketQueue.clear();<a name="line.891"></a>
-<span class="sourceLineNo">892</span> remainingBuckets = 3;<a name="line.892"></a>
-<span class="sourceLineNo">893</span><a name="line.893"></a>
-<span class="sourceLineNo">894</span> bucketQueue.add(bucketSingle);<a name="line.894"></a>
-<span class="sourceLineNo">895</span> bucketQueue.add(bucketMulti);<a name="line.895"></a>
-<span class="sourceLineNo">896</span> bucketQueue.add(bucketMemory);<a name="line.896"></a>
-<span class="sourceLineNo">897</span><a name="line.897"></a>
-<span class="sourceLineNo">898</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.898"></a>
-<span class="sourceLineNo">899</span> long bucketBytesToFree = (bytesToFreeWithExtra - bytesFreed) / remainingBuckets;<a name="line.899"></a>
-<span class="sourceLineNo">900</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.900"></a>
-<span class="sourceLineNo">901</span> remainingBuckets--;<a name="line.901"></a>
-<span class="sourceLineNo">902</span> }<a name="line.902"></a>
-<span class="sourceLineNo">903</span> }<a name="line.903"></a>
-<span class="sourceLineNo">904</span><a name="line.904"></a>
-<span class="sourceLineNo">905</span> // Even after the above free we might still need freeing because of the<a name="line.905"></a>
-<span class="sourceLineNo">906</span> // De-fragmentation of the buckets (also called Slab Calcification problem), i.e<a name="line.906"></a>
-<span class="sourceLineNo">907</span> // there might be some buckets where the occupancy is very sparse and thus are not<a name="line.907"></a>
-<span class="sourceLineNo">908</span> // yielding the free for the other bucket sizes, the fix for this to evict some<a name="line.908"></a>
-<span class="sourceLineNo">909</span> // of the buckets, we do this by evicting the buckets that are least fulled<a name="line.909"></a>
-<span class="sourceLineNo">910</span> freeEntireBuckets(DEFAULT_FREE_ENTIRE_BLOCK_FACTOR *<a name="line.910"></a>
-<span class="sourceLineNo">911</span> bucketSizesAboveThresholdCount(1.0f));<a name="line.911"></a>
-<span class="sourceLineNo">912</span><a name="line.912"></a>
-<span class="sourceLineNo">913</span> if (LOG.isDebugEnabled()) {<a name="line.913"></a>
-<span class="sourceLineNo">914</span> long single = bucketSingle.totalSize();<a name="line.914"></a>
-<span class="sourceLineNo">915</span> long multi = bucketMulti.totalSize();<a name="line.915"></a>
-<span class="sourceLineNo">916</span> long memory = bucketMemory.totalSize();<a name="line.916"></a>
-<span class="sourceLineNo">917</span> if (LOG.isDebugEnabled()) {<a name="line.917"></a>
-<span class="sourceLineNo">918</span> LOG.debug("Bucket cache free space completed; " + "freed="<a name="line.918"></a>
-<span class="sourceLineNo">919</span> + StringUtils.byteDesc(bytesFreed) + ", " + "total="<a name="line.919"></a>
-<span class="sourceLineNo">920</span> + StringUtils.byteDesc(totalSize) + ", " + "single="<a name="line.920"></a>
-<span class="sourceLineNo">921</span> + StringUtils.byteDesc(single) + ", " + "multi="<a name="line.921"></a>
-<span class="sourceLineNo">922</span> + StringUtils.byteDesc(multi) + ", " + "memory="<a name="line.922"></a>
-<span class="sourceLineNo">923</span> + StringUtils.byteDesc(memory));<a name="line.923"></a>
-<span class="sourceLineNo">924</span> }<a name="line.924"></a>
-<span class="sourceLineNo">925</span> }<a name="line.925"></a>
-<span class="sourceLineNo">926</span><a name="line.926"></a>
-<span class="sourceLineNo">927</span> } catch (Throwable t) {<a name="line.927"></a>
-<span class="sourceLineNo">928</span> LOG.warn("Failed freeing space", t);<a name="line.928"></a>
-<span class="sourceLineNo">929</span> } finally {<a name="line.929"></a>
-<span class="sourceLineNo">930</span> cacheStats.evict();<a name="line.930"></a>
-<span class="sourceLineNo">931</span> freeInProgress = false;<a name="line.931"></a>
-<span class="sourceLineNo">932</span> freeSpaceLock.unlock();<a name="line.932"></a>
-<span class="sourceLineNo">933</span> }<a name="line.933"></a>
-<span class="sourceLineNo">934</span> }<a name="line.934"></a>
-<span class="sourceLineNo">935</span><a name="line.935"></a>
-<span class="sourceLineNo">936</span> // This handles flushing the RAM cache to IOEngine.<a name="line.936"></a>
-<span class="sourceLineNo">937</span> class WriterThread extends Thread {<a name="line.937"></a>
-<span class="sourceLineNo">938</span> private final BlockingQueue<RAMQueueEntry> inputQueue;<a name="line.938"></a>
-<span class="sourceLineNo">939</span> private volatile boolean writerEnabled = true;<a name="line.939"></a>
-<span class="sourceLineNo">940</span><a name="line.940"></a>
-<span class="sourceLineNo">941</span> WriterThread(BlockingQueue<RAMQueueEntry> queue) {<a name="line.941"></a>
-<span class="sourceLineNo">942</span> super("BucketCacheWriterThread");<a name="line.942"></a>
-<span class="sourceLineNo">943</span> this.inputQueue = queue;<a name="line.943"></a>
-<span class="sourceLineNo">944</span> }<a name="line.944"></a>
-<span class="sourceLineNo">945</span><a name="line.945"></a>
-<span class="sourceLineNo">946</span> // Used for test<a name="line.946"></a>
-<span class="sourceLineNo">947</span> void disableWriter() {<a name="line.947"></a>
-<span class="sourceLineNo">948</span> this.writerEnabled = false;<a name="line.948"></a>
-<span class="sourceLineNo">949</span> }<a name="line.949"></a>
-<span class="sourceLineNo">950</span><a name="line.950"></a>
-<span class="sourceLineNo">951</span> @Override<a name="line.951"></a>
-<span class="sourceLineNo">952</span> public void run() {<a name="line.952"></a>
-<span class="sourceLineNo">953</span> List<RAMQueueEntry> entries = new ArrayList<>();<a name="line.953"></a>
-<span class="sourceLineNo">954</span> try {<a name="line.954"></a>
-<span class="sourceLineNo">955</span> while (cacheEnabled && writerEnabled) {<a name="line.955"></a>
-<span class="sourceLineNo">956</span> try {<a name="line.956"></a>
-<span class="sourceLineNo">957</span> try {<a name="line.957"></a>
-<span class="sourceLineNo">958</span> // Blocks<a name="line.958"></a>
-<span class="sourceLineNo">959</span> entries = getRAMQueueEntries(inputQueue, entries);<a name="line.959"></a>
-<span class="sourceLineNo">960</span> } catch (InterruptedException ie) {<a name="line.960"></a>
-<span class="sourceLineNo">961</span> if (!cacheEnabled || !writerEnabled) {<a name="line.961"></a>
-<span class="sourceLineNo">962</span> break;<a name="line.962"></a>
-<span class="sourceLineNo">963</span> }<a name="line.963"></a>
-<span class="sourceLineNo">964</span> }<a name="line.964"></a>
-<span class="sourceLineNo">965</span> doDrain(entries);<a name="line.965"></a>
-<span class="sourceLineNo">966</span> } catch (Exception ioe) {<a name="line.966"></a>
-<span class="sourceLineNo">967</span> LOG.error("WriterThread encountered error", ioe);<a name="line.967"></a>
-<span class="sourceLineNo">968</span> }<a name="line.968"></a>
-<span class="sourceLineNo">969</span> }<a name="line.969"></a>
-<span class="sourceLineNo">970</span> } catch (Throwable t) {<a name="line.970"></a>
-<span class="sourceLineNo">971</span> LOG.warn("Failed doing drain", t);<a name="line.971"></a>
-<span class="sourceLineNo">972</span> }<a name="line.972"></a>
-<span class="sourceLineNo">973</span> LOG.info(this.getName() + " exiting, cacheEnabled=" + cacheEnabled);<a name="line.973"></a>
-<span class="sourceLineNo">974</span> }<a name="line.974"></a>
-<span class="sourceLineNo">975</span> }<a name="line.975"></a>
-<span class="sourceLineNo">976</span><a name="line.976"></a>
-<span class="sourceLineNo">977</span> /**<a name="line.977"></a>
-<span class="sourceLineNo">978</span> * Put the new bucket entry into backingMap. Notice that we are allowed to replace the existing<a name="line.978"></a>
-<span class="sourceLineNo">979</span> * cache with a new block for the same cache key. there's a corner case: one thread cache a block<a name="line.979"></a>
-<span class="sourceLineNo">980</span> * in ramCache, copy to io-engine and add a bucket entry to backingMap. Caching another new block<a name="line.980"></a>
-<span class="sourceLineNo">981</span> * with the same cache key do the same thing for the same cache key, so if not evict the previous<a name="line.981"></a>
-<span class="sourceLineNo">982</span> * bucket entry, then memory leak happen because the previous bucketEntry is gone but the<a name="line.982"></a>
-<span class="sourceLineNo">983</span> * bucketAllocator do not free its memory.<a name="line.983"></a>
-<span class="sourceLineNo">984</span> * @see BlockCacheUtil#shouldReplaceExistingCacheBlock(BlockCache blockCache,BlockCacheKey<a name="line.984"></a>
-<span class="sourceLineNo">985</span> * cacheKey, Cacheable newBlock)<a name="line.985"></a>
-<span class="sourceLineNo">986</span> * @param key Block cache key<a name="line.986"></a>
-<span class="sourceLineNo">987</span> * @param bucketEntry Bucket entry to put into backingMap.<a name="line.987"></a>
-<span class="sourceLineNo">988</span> */<a name="line.988"></a>
-<span class="sourceLineNo">989</span> protected void putIntoBackingMap(BlockCacheKey key, BucketEntry bucketEntry) {<a name="line.989"></a>
-<span class="sourceLineNo">990</span> BucketEntry previousEntry = backingMap.put(key, bucketEntry);<a name="line.990"></a>
-<span class="sourceLineNo">991</span> if (previousEntry != null && previousEntry != bucketEntry) {<a name="line.991"></a>
-<span class="sourceLineNo">992</span> previousEntry.withWriteLock(offsetLock, () -> {<a name="line.992"></a>
-<span class="sourceLineNo">993</span> blockEvicted(key, previousEntry, false);<a name="line.993"></a>
-<span class="sourceLineNo">994</span> return null;<a name="line.994"></a>
-<span class="sourceLineNo">995</span> });<a name="line.995"></a>
-<span class="sourceLineNo">996</span> }<a name="line.996"></a>
-<span class="sourceLineNo">997</span> }<a name="line.997"></a>
-<span class="sourceLineNo">998</span><a name="line.998"></a>
-<span class="sourceLineNo">999</span> /**<a name="line.999"></a>
-<span class="sourceLineNo">1000</span> * Flush the entries in ramCache to IOEngine and add bucket entry to backingMap. Process all that<a name="line.1000"></a>
-<span class="sourceLineNo">1001</span> * are passed in even if failure being sure to remove from ramCache else we'll never undo the<a name="line.1001"></a>
-<span class="sourceLineNo">1002</span> * references and we'll OOME.<a name="line.1002"></a>
-<span class="sourceLineNo">1003</span> * @param entries Presumes list passed in here will be processed by this invocation only. No<a name="line.1003"></a>
-<span class="sourceLineNo">1004</span> * interference expected.<a name="line.1004"></a>
-<span class="sourceLineNo">1005</span> */<a name="line.1005"></a>
-<span class="sourceLineNo">1006</span> void doDrain(final List<RAMQueueEntry> entries) throws InterruptedException {<a name="line.1006"></a>
-<span class="sourceLineNo">1007</span> if (entries.isEmpty()) {<a name="line.1007"></a>
-<span class="sourceLineNo">1008</span> return;<a name="line.1008"></a>
-<span class="sourceLineNo">1009</span> }<a name="line.1009"></a>
-<span class="sourceLineNo">1010</span> // This method is a little hard to follow. We run through the passed in entries and for each<a name="line.1010"></a>
-<span class="sourceLineNo">1011</span> // successful add, we add a non-null BucketEntry to the below bucketEntries. Later we must<a name="line.1011"></a>
-<span class="sourceLineNo">1012</span> // do cleanup making sure we've cleared ramCache of all entries regardless of whether we<a name="line.1012"></a>
-<span class="sourceLineNo">1013</span> // successfully added the item to the bucketcache; if we don't do the cleanup, we'll OOME by<a name="line.1013"></a>
-<span class="sourceLineNo">1014</span> // filling ramCache. We do the clean up by again running through the passed in entries<a name="line.1014"></a>
-<span class="sourceLineNo">1015</span> // doing extra work when we find a non-null bucketEntries corresponding entry.<a name="line.1015"></a>
-<span class="sourceLineNo">1016</span> final int size = entries.size();<a name="line.1016"></a>
-<span class="sourceLineNo">1017</span> BucketEntry[] bucketEntries = new BucketEntry[size];<a name="line.1017"></a>
-<span class="sourceLineNo">1018</span> // Index updated inside loop if success or if we can't succeed. We retry if cache is full<a name="line.1018"></a>
-<span class="sourceLineNo">1019</span> // when we go to add an entry by going around the loop again without upping the index.<a name="line.1019"></a>
-<span class="sourceLineNo">1020</span> int index = 0;<a name="line.1020"></a>
-<span class="sourceLineNo">1021</span> while (cacheEnabled && index < size) {<a name="line.1021"></a>
-<span class="sourceLineNo">1022</span> RAMQueueEntry re = null;<a name="line.1022"></a>
-<span class="sourceLineNo">1023</span> try {<a name="line.1023"></a>
-<span class="sourceLineNo">1024</span> re = entries.get(index);<a name="line.1024"></a>
-<span class="sourceLineNo">1025</span> if (re == null) {<a name="line.1025"></a>
-<span class="sourceLineNo">1026</span> LOG.warn("Couldn't get entry or changed on us; who else is messing with it?");<a name="line.1026"></a>
-<span class="sourceLineNo">1027</span> index++;<a name="line.1027"></a>
-<span class="sourceLineNo">1028</span> continue;<a name="line.1028"></a>
-<span class="sourceLineNo">1029</span> }<a name="line.1029"></a>
-<span class="sourceLineNo">1030</span> BucketEntry bucketEntry = re.writeToCache(ioEngine, bucketAllocator, realCacheSize,<a name="line.1030"></a>
-<span class="sourceLineNo">1031</span> this::createRecycler);<a name="line.1031"></a>
-<span class="sourceLineNo">1032</span> // Successfully added. Up index and add bucketEntry. Clear io exceptions.<a name="line.1032"></a>
-<span class="sourceLineNo">1033</span> bucketEntries[index] = bucketEntry;<a name="line.1033"></a>
-<span class="sourceLineNo">1034</span> if (ioErrorStartTime > 0) {<a name="line.1034"></a>
-<span class="sourceLineNo">1035</span> ioErrorStartTime = -1;<a name="line.1035"></a>
-<span class="sourceLineNo">1036</span> }<a name="line.1036"></a>
-<span class="sourceLineNo">1037</span> index++;<a name="line.1037"></a>
-<span class="sourceLineNo">1038</span> } catch (BucketAllocatorException fle) {<a name="line.1038"></a>
-<span class="sourceLineNo">1039</span> LOG.warn("Failed allocation for " + (re == null ? "" : re.getKey()) + "; " + fle);<a name="line.1039"></a>
-<span class="sourceLineNo">1040</span> // Presume can't add. Too big? Move index on. Entry will be cleared from ramCache below.<a name="line.1040"></a>
-<span class="sourceLineNo">1041</span> bucketEntries[index] = null;<a name="line.1041"></a>
-<span class="sourceLineNo">1042</span> index++;<a name="line.1042"></a>
-<span class="sourceLineNo">1043</span> } catch (CacheFullException cfe) {<a name="line.1043"></a>
-<span class="sourceLineNo">1044</span> // Cache full when we tried to add. Try freeing space and then retrying (don't up index)<a name="line.1044"></a>
-<span class="sourceLineNo">1045</span> if (!freeInProgress) {<a name="line.1045"></a>
-<span class="sourceLineNo">1046</span> freeSpace("Full!");<a name="line.1046"></a>
-<span class="sourceLineNo">1047</span> } else {<a name="line.1047"></a>
-<span class="sourceLineNo">1048</span> Thread.sleep(50);<a name="line.1048"></a>
-<span class="sourceLineNo">1049</span> }<a name="line.1049"></a>
-<span class="sourceLineNo">1050</span> } catch (IOException ioex) {<a name="line.1050"></a>
-<span class="sourceLineNo">1051</span> // Hopefully transient. Retry. checkIOErrorIsTolerated disables cache if problem.<a name="line.1051"></a>
-<span class="sourceLineNo">1052</span> LOG.error("Failed writing to bucket cache", ioex);<a name="line.1052"></a>
-<span class="sourceLineNo">1053</span> checkIOErrorIsTolerated();<a name="line.1053"></a>
-<span class="sourceLineNo">1054</span> }<a name="line.1054"></a>
-<span class="sourceLineNo">1055</span> }<a name="line.1055"></a>
-<span class="sourceLineNo">1056</span><a name="line.1056"></a>
-<span class="sourceLineNo">1057</span> // Make sure data pages are written on media before we update maps.<a name="line.1057"></a>
-<span class="sourceLineNo">1058</span> try {<a name="line.1058"></a>
-<span class="sourceLineNo">1059</span> ioEngine.sync();<a name="line.1059"></a>
-<span class="sourceLineNo">1060</span> } catch (IOException ioex) {<a name="line.1060"></a>
-<span class="sourceLineNo">1061</span> LOG.error("Failed syncing IO engine", ioex);<a name="line.1061"></a>
-<span class="sourceLineNo">1062</span> checkIOErrorIsTolerated();<a name="line.1062"></a>
-<span class="sourceLineNo">1063</span> // Since we failed sync, free the blocks in bucket allocator<a name="line.1063"></a>
-<span class="sourceLineNo">1064</span> for (int i = 0; i < entries.size(); ++i) {<a name="line.1064"></a>
-<span class="sourceLineNo">1065</span> if (bucketEntries[i] != null) {<a name="line.1065"></a>
-<span class="sourceLineNo">1066</span> bucketAllocator.freeBlock(bucketEntries[i].offset());<a name="line.1066"></a>
-<span class="sourceLineNo">1067</span> bucketEntries[i] = null;<a name="line.1067"></a>
-<span class="sourceLineNo">1068</span> }<a name="line.1068"></a>
-<span class="sourceLineNo">1069</span> }<a name="line.1069"></a>
-<span class="sourceLineNo">1070</span> }<a name="line.1070"></a>
-<span class="sourceLineNo">1071</span><a name="line.1071"></a>
-<span class="sourceLineNo">1072</span> // Now add to backingMap if successfully added to bucket cache. Remove from ramCache if<a name="line.1072"></a>
-<span class="sourceLineNo">1073</span> // success or error.<a name="line.1073"></a>
-<span class="sourceLineNo">1074</span> for (int i = 0; i < size; ++i) {<a name="line.1074"></a>
-<span class="sourceLineNo">1075</span> BlockCacheKey key = entries.get(i).getKey();<a name="line.1075"></a>
-<span class="sourceLineNo">1076</span> // Only add if non-null entry.<a name="line.1076"></a>
-<span class="sourceLineNo">1077</span> if (bucketEntries[i] != null) {<a name="line.1077"></a>
-<span class="sourceLineNo">1078</span> putIntoBackingMap(key, bucketEntries[i]);<a name="line.1078"></a>
-<span class="sourceLineNo">1079</span> }<a name="line.1079"></a>
-<span class="sourceLineNo">1080</span> // Always remove from ramCache even if we failed adding it to the block cache above.<a name="line.1080"></a>
-<span class="sourceLineNo">1081</span> boolean existed = ramCache.remove(key, re -> {<a name="line.1081"></a>
-<span class="sourceLineNo">1082</span> if (re != null) {<a name="line.1082"></a>
-<span class="sourceLineNo">1083</span> heapSize.add(-1 * re.getData().heapSize());<a name="line.1083"></a>
-<span class="sourceLineNo">1084</span> }<a name="line.1084"></a>
-<span class="sourceLineNo">1085</span> });<a name="line.1085"></a>
-<span class="sourceLineNo">1086</span> if (!existed && bucketEntries[i] != null) {<a name="line.1086"></a>
-<span class="sourceLineNo">1087</span> // Block should have already been evicted. Remove it and free space.<a name="line.1087"></a>
-<span class="sourceLineNo">1088</span> final BucketEntry bucketEntry = bucketEntries[i];<a name="line.1088"></a>
-<span class="sourceLineNo">1089</span> bucketEntry.withWriteLock(offsetLock, () -> {<a name="line.1089"></a>
-<span class="sourceLineNo">1090</span> if (backingMap.remove(key, bucketEntry)) {<a name="line.1090"></a>
-<span class="sourceLineNo">1091</span> blockEvicted(key, bucketEntry, false);<a name="line.1091"></a>
-<span class="sourceLineNo">1092</span> }<a name="line.1092"></a>
-<span class="sourceLineNo">1093</span> return null;<a name="line.1093"></a>
-<span class="sourceLineNo">1094</span> });<a name="line.1094"></a>
-<span class="sourceLineNo">1095</span> }<a name="line.1095"></a>
-<span class="sourceLineNo">1096</span> }<a name="line.1096"></a>
-<span class="sourceLineNo">1097</span><a name="line.1097"></a>
-<span class="sourceLineNo">1098</span> long used = bucketAllocator.getUsedSize();<a name="line.1098"></a>
-<span class="sourceLineNo">1099</span> if (used > acceptableSize()) {<a name="line.1099"></a>
-<span class="sourceLineNo">1100</span> freeSpace("Used=" + used + " > acceptable=" + acceptableSize());<a name="line.1100"></a>
-<span class="sourceLineNo">1101</span> }<a name="line.1101"></a>
-<span class="sourceLineNo">1102</span> return;<a name="line.1102"></a>
-<span class="sourceLineNo">1103</span> }<a name="line.1103"></a>
-<span class="sourceLineNo">1104</span><a name="line.1104"></a>
-<span class="sourceLineNo">1105</span> /**<a name="line.1105"></a>
-<span class="sourceLineNo">1106</span> * Blocks until elements available in {@code q} then tries to grab as many as possible before<a name="line.1106"></a>
-<span class="sourceLineNo">1107</span> * returning.<a name="line.1107"></a>
-<span class="sourceLineNo">1108</span> * @param receptacle Where to stash the elements taken from queue. We clear before we use it just<a name="line.1108"></a>
-<span class="sourceLineNo">1109</span> * in case.<a name="line.1109"></a>
-<span class="sourceLineNo">1110</span> * @param q The queue to take from.<a name="line.1110"></a>
-<span class="sourceLineNo">1111</span> * @return {@code receptacle} laden with elements taken from the queue or empty if none found.<a name="line.1111"></a>
-<span class="sourceLineNo">1112</span> */<a name="line.1112"></a>
-<span class="sourceLineNo">1113</span> static List<RAMQueueEntry> getRAMQueueEntries(BlockingQueue<RAMQueueEntry> q,<a name="line.1113"></a>
-<span class="sourceLineNo">1114</span> List<RAMQueueEntry> receptacle) throws InterruptedException {<a name="line.1114"></a>
-<span class="sourceLineNo">1115</span> // Clear sets all entries to null and sets size to 0. We retain allocations. Presume it<a name="line.1115"></a>
-<span class="sourceLineNo">1116</span> // ok even if list grew to accommodate thousands.<a name="line.1116"></a>
-<span class="sourceLineNo">1117</span> receptacle.clear();<a name="line.1117"></a>
-<span class="sourceLineNo">1118</span> receptacle.add(q.take());<a name="line.1118"></a>
-<span class="sourceLineNo">1119</span> q.drainTo(receptacle);<a name="line.1119"></a>
-<span class="sourceLineNo">1120</span> return receptacle;<a name="line.1120"></a>
-<span class="sourceLineNo">1121</span> }<a name="line.1121"></a>
-<span class="sourceLineNo">1122</span><a name="line.1122"></a>
-<span class="sourceLineNo">1123</span> /**<a name="line.1123"></a>
-<span class="sourceLineNo">1124</span> * @see #retrieveFromFile(int[])<a name="line.1124"></a>
-<span class="sourceLineNo">1125</span> */<a name="line.1125"></a>
-<span class="sourceLineNo">1126</span> @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="OBL_UNSATISFIED_OBLIGATION",<a name="line.1126"></a>
-<span class="sourceLineNo">1127</span> justification = "false positive, try-with-resources ensures close is called.")<a name="line.1127"></a>
-<span class="sourceLineNo">1128</span> private void persistToFile() throws IOException {<a name="line.1128"></a>
-<span class="sourceLineNo">1129</span> assert !cacheEnabled;<a name="line.1129"></a>
-<span class="sourceLineNo">1130</span> if (!ioEngine.isPersistent()) {<a name="line.1130"></a>
-<span class="sourceLineNo">1131</span> throw new IOException("Attempt to persist non-persistent cache mappings!");<a name="line.1131"></a>
-<span class="sourceLineNo">1132</span> }<a name="line.1132"></a>
-<span class="sourceLineNo">1133</span> try (FileOutputStream fos = new FileOutputStream(persistencePath, false)) {<a name="line.1133"></a>
-<span class="sourceLineNo">1134</span> fos.write(ProtobufMagic.PB_MAGIC);<a name="line.1134"></a>
-<span class="sourceLineNo">1135</span> BucketProtoUtils.toPB(this).writeDelimitedTo(fos);<a name="line.1135"></a>
+<span class="sourceLineNo">489</span> }<a name="line.489"></a>
+<span class="sourceLineNo">490</span> }<a name="line.490"></a>
+<span class="sourceLineNo">491</span><a name="line.491"></a>
+<span class="sourceLineNo">492</span> /**<a name="line.492"></a>
+<span class="sourceLineNo">493</span> * Get the buffer of the block with the specified key.<a name="line.493"></a>
+<span class="sourceLineNo">494</span> * @param key block's cache key<a name="line.494"></a>
+<span class="sourceLineNo">495</span> * @param caching true if the caller caches blocks on cache misses<a name="line.495"></a>
+<span class="sourceLineNo">496</span> * @param repeat Whether this is a repeat lookup for the same block<a name="line.496"></a>
+<span class="sourceLineNo">497</span> * @param updateCacheMetrics Whether we should update cache metrics or not<a name="line.497"></a>
+<span class="sourceLineNo">498</span> * @return buffer of specified cache key, or null if not in cache<a name="line.498"></a>
+<span class="sourceLineNo">499</span> */<a name="line.499"></a>
+<span class="sourceLineNo">500</span> @Override<a name="line.500"></a>
+<span class="sourceLineNo">501</span> public Cacheable getBlock(BlockCacheKey key, boolean caching, boolean repeat,<a name="line.501"></a>
+<span class="sourceLineNo">502</span> boolean updateCacheMetrics) {<a name="line.502"></a>
+<span class="sourceLineNo">503</span> if (!cacheEnabled) {<a name="line.503"></a>
+<span class="sourceLineNo">504</span> return null;<a name="line.504"></a>
+<span class="sourceLineNo">505</span> }<a name="line.505"></a>
+<span class="sourceLineNo">506</span> RAMQueueEntry re = ramCache.get(key);<a name="line.506"></a>
+<span class="sourceLineNo">507</span> if (re != null) {<a name="line.507"></a>
+<span class="sourceLineNo">508</span> if (updateCacheMetrics) {<a name="line.508"></a>
+<span class="sourceLineNo">509</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.509"></a>
+<span class="sourceLineNo">510</span> }<a name="line.510"></a>
+<span class="sourceLineNo">511</span> re.access(accessCount.incrementAndGet());<a name="line.511"></a>
+<span class="sourceLineNo">512</span> return re.getData();<a name="line.512"></a>
+<span class="sourceLineNo">513</span> }<a name="line.513"></a>
+<span class="sourceLineNo">514</span> BucketEntry bucketEntry = backingMap.get(key);<a name="line.514"></a>
+<span class="sourceLineNo">515</span> if (bucketEntry != null) {<a name="line.515"></a>
+<span class="sourceLineNo">516</span> long start = System.nanoTime();<a name="line.516"></a>
+<span class="sourceLineNo">517</span> ReentrantReadWriteLock lock = offsetLock.getLock(bucketEntry.offset());<a name="line.517"></a>
+<span class="sourceLineNo">518</span> try {<a name="line.518"></a>
+<span class="sourceLineNo">519</span> lock.readLock().lock();<a name="line.519"></a>
+<span class="sourceLineNo">520</span> // We can not read here even if backingMap does contain the given key because its offset<a name="line.520"></a>
+<span class="sourceLineNo">521</span> // maybe changed. If we lock BlockCacheKey instead of offset, then we can only check<a name="line.521"></a>
+<span class="sourceLineNo">522</span> // existence here.<a name="line.522"></a>
+<span class="sourceLineNo">523</span> if (bucketEntry.equals(backingMap.get(key))) {<a name="line.523"></a>
+<span class="sourceLineNo">524</span> // Read the block from IOEngine based on the bucketEntry's offset and length, NOTICE: the<a name="line.524"></a>
+<span class="sourceLineNo">525</span> // block will use the refCnt of bucketEntry, which means if two HFileBlock mapping to<a name="line.525"></a>
+<span class="sourceLineNo">526</span> // the same BucketEntry, then all of the three will share the same refCnt.<a name="line.526"></a>
+<span class="sourceLineNo">527</span> Cacheable cachedBlock = ioEngine.read(bucketEntry);<a name="line.527"></a>
+<span class="sourceLineNo">528</span> if (ioEngine.usesSharedMemory()) {<a name="line.528"></a>
+<span class="sourceLineNo">529</span> // If IOEngine use shared memory, cachedBlock and BucketEntry will share the<a name="line.529"></a>
+<span class="sourceLineNo">530</span> // same RefCnt, do retain here, in order to count the number of RPC references<a name="line.530"></a>
+<span class="sourceLineNo">531</span> cachedBlock.retain();<a name="line.531"></a>
+<span class="sourceLineNo">532</span> }<a name="line.532"></a>
+<span class="sourceLineNo">533</span> // Update the cache statistics.<a name="line.533"></a>
+<span class="sourceLineNo">534</span> if (updateCacheMetrics) {<a name="line.534"></a>
+<span class="sourceLineNo">535</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.535"></a>
+<span class="sourceLineNo">536</span> cacheStats.ioHit(System.nanoTime() - start);<a name="line.536"></a>
+<span class="sourceLineNo">537</span> }<a name="line.537"></a>
+<span class="sourceLineNo">538</span> bucketEntry.access(accessCount.incrementAndGet());<a name="line.538"></a>
+<span class="sourceLineNo">539</span> if (this.ioErrorStartTime > 0) {<a name="line.539"></a>
+<span class="sourceLineNo">540</span> ioErrorStartTime = -1;<a name="line.540"></a>
+<span class="sourceLineNo">541</span> }<a name="line.541"></a>
+<span class="sourceLineNo">542</span> return cachedBlock;<a name="line.542"></a>
+<span class="sourceLineNo">543</span> }<a name="line.543"></a>
+<span class="sourceLineNo">544</span> } catch (IOException ioex) {<a name="line.544"></a>
+<span class="sourceLineNo">545</span> LOG.error("Failed reading block " + key + " from bucket cache", ioex);<a name="line.545"></a>
+<span class="sourceLineNo">546</span> checkIOErrorIsTolerated();<a name="line.546"></a>
+<span class="sourceLineNo">547</span> } finally {<a name="line.547"></a>
+<span class="sourceLineNo">548</span> lock.readLock().unlock();<a name="line.548"></a>
+<span class="sourceLineNo">549</span> }<a name="line.549"></a>
+<span class="sourceLineNo">550</span> }<a name="line.550"></a>
+<span class="sourceLineNo">551</span> if (!repeat && updateCacheMetrics) {<a name="line.551"></a>
+<span class="sourceLineNo">552</span> cacheStats.miss(caching, key.isPrimary(), key.getBlockType());<a name="line.552"></a>
+<span class="sourceLineNo">553</span> }<a name="line.553"></a>
+<span class="sourceLineNo">554</span> return null;<a name="line.554"></a>
+<span class="sourceLineNo">555</span> }<a name="line.555"></a>
+<span class="sourceLineNo">556</span><a name="line.556"></a>
+<span class="sourceLineNo">557</span> /**<a name="line.557"></a>
+<span class="sourceLineNo">558</span> * This method is invoked after the bucketEntry is removed from {@link BucketCache#backingMap}<a name="line.558"></a>
+<span class="sourceLineNo">559</span> */<a name="line.559"></a>
+<span class="sourceLineNo">560</span> void blockEvicted(BlockCacheKey cacheKey, BucketEntry bucketEntry, boolean decrementBlockNumber) {<a name="line.560"></a>
+<span class="sourceLineNo">561</span> bucketEntry.markAsEvicted();<a name="line.561"></a>
+<span class="sourceLineNo">562</span> blocksByHFile.remove(cacheKey);<a name="line.562"></a>
+<span class="sourceLineNo">563</span> if (decrementBlockNumber) {<a name="line.563"></a>
+<span class="sourceLineNo">564</span> this.blockNumber.decrement();<a name="line.564"></a>
+<span class="sourceLineNo">565</span> }<a name="line.565"></a>
+<span class="sourceLineNo">566</span> cacheStats.evicted(bucketEntry.getCachedTime(), cacheKey.isPrimary());<a name="line.566"></a>
+<span class="sourceLineNo">567</span> }<a name="line.567"></a>
+<span class="sourceLineNo">568</span><a name="line.568"></a>
+<span class="sourceLineNo">569</span> /**<a name="line.569"></a>
+<span class="sourceLineNo">570</span> * Free the {{@link BucketEntry} actually,which could only be invoked when the<a name="line.570"></a>
+<span class="sourceLineNo">571</span> * {@link BucketEntry#refCnt} becoming 0.<a name="line.571"></a>
+<span class="sourceLineNo">572</span> */<a name="line.572"></a>
+<span class="sourceLineNo">573</span> void freeBucketEntry(BucketEntry bucketEntry) {<a name="line.573"></a>
+<span class="sourceLineNo">574</span> bucketAllocator.freeBlock(bucketEntry.offset());<a name="line.574"></a>
+<span class="sourceLineNo">575</span> realCacheSize.add(-1 * bucketEntry.getLength());<a name="line.575"></a>
+<span class="sourceLineNo">576</span> }<a name="line.576"></a>
+<span class="sourceLineNo">577</span><a name="line.577"></a>
+<span class="sourceLineNo">578</span> /**<a name="line.578"></a>
+<span class="sourceLineNo">579</span> * Try to evict the block from {@link BlockCache} by force. We'll call this in few cases:<br><a name="line.579"></a>
+<span class="sourceLineNo">580</span> * 1. Close an HFile, and clear all cached blocks. <br><a name="line.580"></a>
+<span class="sourceLineNo">581</span> * 2. Call {@link Admin#clearBlockCache(TableName)} to clear all blocks for a given table.<br><a name="line.581"></a>
+<span class="sourceLineNo">582</span> * <p><a name="line.582"></a>
+<span class="sourceLineNo">583</span> * Firstly, we'll try to remove the block from RAMCache,and then try to evict from backingMap.<a name="line.583"></a>
+<span class="sourceLineNo">584</span> * Here we evict the block from backingMap immediately, but only free the reference from bucket<a name="line.584"></a>
+<span class="sourceLineNo">585</span> * cache by calling {@link BucketEntry#markedAsEvicted}. If there're still some RPC referring this<a name="line.585"></a>
+<span class="sourceLineNo">586</span> * block, block can only be de-allocated when all of them release the block.<a name="line.586"></a>
+<span class="sourceLineNo">587</span> * <p><a name="line.587"></a>
+<span class="sourceLineNo">588</span> * NOTICE: we need to grab the write offset lock firstly before releasing the reference from<a name="line.588"></a>
+<span class="sourceLineNo">589</span> * bucket cache. if we don't, we may read an {@link BucketEntry} with refCnt = 0 when<a name="line.589"></a>
+<span class="sourceLineNo">590</span> * {@link BucketCache#getBlock(BlockCacheKey, boolean, boolean, boolean)}, it's a memory leak.<a name="line.590"></a>
+<span class="sourceLineNo">591</span> * @param cacheKey Block to evict<a name="line.591"></a>
+<span class="sourceLineNo">592</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.592"></a>
+<span class="sourceLineNo">593</span> */<a name="line.593"></a>
+<span class="sourceLineNo">594</span> @Override<a name="line.594"></a>
+<span class="sourceLineNo">595</span> public boolean evictBlock(BlockCacheKey cacheKey) {<a name="line.595"></a>
+<span class="sourceLineNo">596</span> return doEvictBlock(cacheKey, null);<a name="line.596"></a>
+<span class="sourceLineNo">597</span> }<a name="line.597"></a>
+<span class="sourceLineNo">598</span><a name="line.598"></a>
+<span class="sourceLineNo">599</span> /**<a name="line.599"></a>
+<span class="sourceLineNo">600</span> * Evict the {@link BlockCacheKey} and {@link BucketEntry} from {@link BucketCache#backingMap} and<a name="line.600"></a>
+<span class="sourceLineNo">601</span> * {@link BucketCache#ramCache}. <br/><a name="line.601"></a>
+<span class="sourceLineNo">602</span> * NOTE:When Evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.602"></a>
+<span class="sourceLineNo">603</span> * {@link BucketEntry} could be removed.<a name="line.603"></a>
+<span class="sourceLineNo">604</span> * @param cacheKey {@link BlockCacheKey} to evict.<a name="line.604"></a>
+<span class="sourceLineNo">605</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.605"></a>
+<span class="sourceLineNo">606</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.606"></a>
+<span class="sourceLineNo">607</span> */<a name="line.607"></a>
+<span class="sourceLineNo">608</span> private boolean doEvictBlock(BlockCacheKey cacheKey, BucketEntry bucketEntry) {<a name="line.608"></a>
+<span class="sourceLineNo">609</span> if (!cacheEnabled) {<a name="line.609"></a>
+<span class="sourceLineNo">610</span> return false;<a name="line.610"></a>
+<span class="sourceLineNo">611</span> }<a name="line.611"></a>
+<span class="sourceLineNo">612</span> boolean existedInRamCache = removeFromRamCache(cacheKey);<a name="line.612"></a>
+<span class="sourceLineNo">613</span> if (bucketEntry == null) {<a name="line.613"></a>
+<span class="sourceLineNo">614</span> bucketEntry = backingMap.get(cacheKey);<a name="line.614"></a>
+<span class="sourceLineNo">615</span> }<a name="line.615"></a>
+<span class="sourceLineNo">616</span> final BucketEntry bucketEntryToUse = bucketEntry;<a name="line.616"></a>
+<span class="sourceLineNo">617</span><a name="line.617"></a>
+<span class="sourceLineNo">618</span> if (bucketEntryToUse == null) {<a name="line.618"></a>
+<span class="sourceLineNo">619</span> if (existedInRamCache) {<a name="line.619"></a>
+<span class="sourceLineNo">620</span> cacheStats.evicted(0, cacheKey.isPrimary());<a name="line.620"></a>
+<span class="sourceLineNo">621</span> }<a name="line.621"></a>
+<span class="sourceLineNo">622</span> return existedInRamCache;<a name="line.622"></a>
+<span class="sourceLineNo">623</span> } else {<a name="line.623"></a>
+<span class="sourceLineNo">624</span> return bucketEntryToUse.withWriteLock(offsetLock, () -> {<a name="line.624"></a>
+<span class="sourceLineNo">625</span> if (backingMap.remove(cacheKey, bucketEntryToUse)) {<a name="line.625"></a>
+<span class="sourceLineNo">626</span> blockEvicted(cacheKey, bucketEntryToUse, !existedInRamCache);<a name="line.626"></a>
+<span class="sourceLineNo">627</span> return true;<a name="line.627"></a>
+<span class="sourceLineNo">628</span> }<a name="line.628"></a>
+<span class="sourceLineNo">629</span> return false;<a name="line.629"></a>
+<span class="sourceLineNo">630</span> });<a name="line.630"></a>
+<span class="sourceLineNo">631</span> }<a name="line.631"></a>
+<span class="sourceLineNo">632</span> }<a name="line.632"></a>
+<span class="sourceLineNo">633</span><a name="line.633"></a>
+<span class="sourceLineNo">634</span> /**<a name="line.634"></a>
+<span class="sourceLineNo">635</span> * <pre><a name="line.635"></a>
+<span class="sourceLineNo">636</span> * Create the {@link Recycler} for {@link BucketEntry#refCnt},which would be used as<a name="line.636"></a>
+<span class="sourceLineNo">637</span> * {@link RefCnt#recycler} of {@link HFileBlock#buf} returned from {@link BucketCache#getBlock}.<a name="line.637"></a>
+<span class="sourceLineNo">638</span> * NOTE: for {@link BucketCache#getBlock},the {@link RefCnt#recycler} of {@link HFileBlock#buf}<a name="line.638"></a>
+<span class="sourceLineNo">639</span> * from {@link BucketCache#backingMap} and {@link BucketCache#ramCache} are different:<a name="line.639"></a>
+<span class="sourceLineNo">640</span> * 1.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#backingMap},<a name="line.640"></a>
+<span class="sourceLineNo">641</span> * it is the return value of current {@link BucketCache#createRecycler} method.<a name="line.641"></a>
+<span class="sourceLineNo">642</span> *<a name="line.642"></a>
+<span class="sourceLineNo">643</span> * 2.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#ramCache},<a name="line.643"></a>
+<span class="sourceLineNo">644</span> * it is {@link ByteBuffAllocator#putbackBuffer}.<a name="line.644"></a>
+<span class="sourceLineNo">645</span> * </pre><a name="line.645"></a>
+<span class="sourceLineNo">646</span> */<a name="line.646"></a>
+<span class="sourceLineNo">647</span> private Recycler createRecycler(final BucketEntry bucketEntry) {<a name="line.647"></a>
+<span class="sourceLineNo">648</span> return () -> {<a name="line.648"></a>
+<span class="sourceLineNo">649</span> freeBucketEntry(bucketEntry);<a name="line.649"></a>
+<span class="sourceLineNo">650</span> return;<a name="line.650"></a>
+<span class="sourceLineNo">651</span> };<a name="line.651"></a>
+<span class="sourceLineNo">652</span> }<a name="line.652"></a>
+<span class="sourceLineNo">653</span><a name="line.653"></a>
+<span class="sourceLineNo">654</span> /**<a name="line.654"></a>
+<span class="sourceLineNo">655</span> * NOTE: This method is only for test.<a name="line.655"></a>
+<span class="sourceLineNo">656</span> */<a name="line.656"></a>
+<span class="sourceLineNo">657</span> public boolean evictBlockIfNoRpcReferenced(BlockCacheKey blockCacheKey) {<a name="line.657"></a>
+<span class="sourceLineNo">658</span> BucketEntry bucketEntry = backingMap.get(blockCacheKey);<a name="line.658"></a>
+<span class="sourceLineNo">659</span> if (bucketEntry == null) {<a name="line.659"></a>
+<span class="sourceLineNo">660</span> return false;<a name="line.660"></a>
+<span class="sourceLineNo">661</span> }<a name="line.661"></a>
+<span class="sourceLineNo">662</span> return evictBucketEntryIfNoRpcReferenced(blockCacheKey, bucketEntry);<a name="line.662"></a>
+<span class="sourceLineNo">663</span> }<a name="line.663"></a>
+<span class="sourceLineNo">664</span><a name="line.664"></a>
+<span class="sourceLineNo">665</span> /**<a name="line.665"></a>
+<span class="sourceLineNo">666</span> * Evict {@link BlockCacheKey} and its corresponding {@link BucketEntry} only if<a name="line.666"></a>
+<span class="sourceLineNo">667</span> * {@link BucketEntry#isRpcRef} is false. <br/><a name="line.667"></a>
+<span class="sourceLineNo">668</span> * NOTE:When evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.668"></a>
+<span class="sourceLineNo">669</span> * {@link BucketEntry} could be removed.<a name="line.669"></a>
+<span class="sourceLineNo">670</span> * @param blockCacheKey {@link BlockCacheKey} to evict.<a name="line.670"></a>
+<span class="sourceLineNo">671</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.671"></a>
+<span class="sourceLineNo">672</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.672"></a>
+<span class="sourceLineNo">673</span> */<a name="line.673"></a>
+<span class="sourceLineNo">674</span> boolean evictBucketEntryIfNoRpcReferenced(BlockCacheKey blockCacheKey, BucketEntry bucketEntry) {<a name="line.674"></a>
+<span class="sourceLineNo">675</span> if (!bucketEntry.isRpcRef()) {<a name="line.675"></a>
+<span class="sourceLineNo">676</span> return doEvictBlock(blockCacheKey, bucketEntry);<a name="line.676"></a>
+<span class="sourceLineNo">677</span> }<a name="line.677"></a>
+<span class="sourceLineNo">678</span> return false;<a name="line.678"></a>
+<span class="sourceLineNo">679</span> }<a name="line.679"></a>
+<span class="sourceLineNo">680</span><a name="line.680"></a>
+<span class="sourceLineNo">681</span> protected boolean removeFromRamCache(BlockCacheKey cacheKey) {<a name="line.681"></a>
+<span class="sourceLineNo">682</span> return ramCache.remove(cacheKey, re -> {<a name="line.682"></a>
+<span class="sourceLineNo">683</span> if (re != null) {<a name="line.683"></a>
+<span class="sourceLineNo">684</span> this.blockNumber.decrement();<a name="line.684"></a>
+<span class="sourceLineNo">685</span> this.heapSize.add(-1 * re.getData().heapSize());<a name="line.685"></a>
+<span class="sourceLineNo">686</span> }<a name="line.686"></a>
+<span class="sourceLineNo">687</span> });<a name="line.687"></a>
+<span class="sourceLineNo">688</span> }<a name="line.688"></a>
+<span class="sourceLineNo">689</span><a name="line.689"></a>
+<span class="sourceLineNo">690</span> /*<a name="line.690"></a>
+<span class="sourceLineNo">691</span> * Statistics thread. Periodically output cache statistics to the log.<a name="line.691"></a>
+<span class="sourceLineNo">692</span> */<a name="line.692"></a>
+<span class="sourceLineNo">693</span> private static class StatisticsThread extends Thread {<a name="line.693"></a>
+<span class="sourceLineNo">694</span> private final BucketCache bucketCache;<a name="line.694"></a>
+<span class="sourceLineNo">695</span><a name="line.695"></a>
+<span class="sourceLineNo">696</span> public StatisticsThread(BucketCache bucketCache) {<a name="line.696"></a>
+<span class="sourceLineNo">697</span> super("BucketCacheStatsThread");<a name="line.697"></a>
+<span class="sourceLineNo">698</span> setDaemon(true);<a name="line.698"></a>
+<span class="sourceLineNo">699</span> this.bucketCache = bucketCache;<a name="line.699"></a>
+<span class="sourceLineNo">700</span> }<a name="line.700"></a>
+<span class="sourceLineNo">701</span><a name="line.701"></a>
+<span class="sourceLineNo">702</span> @Override<a name="line.702"></a>
+<span class="sourceLineNo">703</span> public void run() {<a name="line.703"></a>
+<span class="sourceLineNo">704</span> bucketCache.logStats();<a name="line.704"></a>
+<span class="sourceLineNo">705</span> }<a name="line.705"></a>
+<span class="sourceLineNo">706</span> }<a name="line.706"></a>
+<span class="sourceLineNo">707</span><a name="line.707"></a>
+<span class="sourceLineNo">708</span> public void logStats() {<a name="line.708"></a>
+<span class="sourceLineNo">709</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.709"></a>
+<span class="sourceLineNo">710</span> long usedSize = bucketAllocator.getUsedSize();<a name="line.710"></a>
+<span class="sourceLineNo">711</span> long freeSize = totalSize - usedSize;<a name="line.711"></a>
+<span class="sourceLineNo">712</span> long cacheSize = getRealCacheSize();<a name="line.712"></a>
+<span class="sourceLineNo">713</span> LOG.info("failedBlockAdditions=" + cacheStats.getFailedInserts() + ", " +<a name="line.713"></a>
+<span class="sourceLineNo">714</span> "totalSize=" + StringUtils.byteDesc(totalSize) + ", " +<a name="line.714"></a>
+<span class="sourceLineNo">715</span> "freeSize=" + StringUtils.byteDesc(freeSize) + ", " +<a name="line.715"></a>
+<span class="sourceLineNo">716</span> "usedSize=" + StringUtils.byteDesc(usedSize) +", " +<a name="line.716"></a>
+<span class="sourceLineNo">717</span> "cacheSize=" + StringUtils.byteDesc(cacheSize) +", " +<a name="line.717"></a>
+<span class="sourceLineNo">718</span> "accesses=" + cacheStats.getRequestCount() + ", " +<a name="line.718"></a>
+<span class="sourceLineNo">719</span> "hits=" + cacheStats.getHitCount() + ", " +<a name="line.719"></a>
+<span class="sourceLineNo">720</span> "IOhitsPerSecond=" + cacheStats.getIOHitsPerSecond() + ", " +<a name="line.720"></a>
+<span class="sourceLineNo">721</span> "IOTimePerHit=" + String.format("%.2f", cacheStats.getIOTimePerHit())+ ", " +<a name="line.721"></a>
+<span class="sourceLineNo">722</span> "hitRatio=" + (cacheStats.getHitCount() == 0 ? "0," :<a name="line.722"></a>
+<span class="sourceLineNo">723</span> (StringUtils.formatPercent(cacheStats.getHitRatio(), 2)+ ", ")) +<a name="line.723"></a>
+<span class="sourceLineNo">724</span> "cachingAccesses=" + cacheStats.getRequestCachingCount() + ", " +<a name="line.724"></a>
+<span class="sourceLineNo">725</span> "cachingHits=" + cacheStats.getHitCachingCount() + ", " +<a name="line.725"></a>
+<span class="sourceLineNo">726</span> "cachingHitsRatio=" +(cacheStats.getHitCachingCount() == 0 ? "0," :<a name="line.726"></a>
+<span class="sourceLineNo">727</span> (StringUtils.formatPercent(cacheStats.getHitCachingRatio(), 2)+ ", ")) +<a name="line.727"></a>
+<span class="sourceLineNo">728</span> "evictions=" + cacheStats.getEvictionCount() + ", " +<a name="line.728"></a>
+<span class="sourceLineNo">729</span> "evicted=" + cacheStats.getEvictedCount() + ", " +<a name="line.729"></a>
+<span class="sourceLineNo">730</span> "evictedPerRun=" + cacheStats.evictedPerEviction());<a name="line.730"></a>
+<span class="sourceLineNo">731</span> cacheStats.reset();<a name="line.731"></a>
+<span class="sourceLineNo">732</span> }<a name="line.732"></a>
+<span class="sourceLineNo">733</span><a name="line.733"></a>
+<span class="sourceLineNo">734</span> public long getRealCacheSize() {<a name="line.734"></a>
+<span class="sourceLineNo">735</span> return this.realCacheSize.sum();<a name="line.735"></a>
+<span class="sourceLineNo">736</span> }<a name="line.736"></a>
+<span class="sourceLineNo">737</span><a name="line.737"></a>
+<span class="sourceLineNo">738</span> public long acceptableSize() {<a name="line.738"></a>
+<span class="sourceLineNo">739</span> return (long) Math.floor(bucketAllocator.getTotalSize() * acceptableFactor);<a name="line.739"></a>
+<span class="sourceLineNo">740</span> }<a name="line.740"></a>
+<span class="sourceLineNo">741</span><a name="line.741"></a>
+<span class="sourceLineNo">742</span> long getPartitionSize(float partitionFactor) {<a name="line.742"></a>
+<span class="sourceLineNo">743</span> return (long) Math.floor(bucketAllocator.getTotalSize() * partitionFactor * minFactor);<a name="line.743"></a>
+<span class="sourceLineNo">744</span> }<a name="line.744"></a>
+<span class="sourceLineNo">745</span><a name="line.745"></a>
+<span class="sourceLineNo">746</span> /**<a name="line.746"></a>
+<span class="sourceLineNo">747</span> * Return the count of bucketSizeinfos still need free space<a name="line.747"></a>
+<span class="sourceLineNo">748</span> */<a name="line.748"></a>
+<span class="sourceLineNo">749</span> private int bucketSizesAboveThresholdCount(float minFactor) {<a name="line.749"></a>
+<span class="sourceLineNo">750</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.750"></a>
+<span class="sourceLineNo">751</span> int fullCount = 0;<a name="line.751"></a>
+<span class="sourceLineNo">752</span> for (int i = 0; i < stats.length; i++) {<a name="line.752"></a>
+<span class="sourceLineNo">753</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.753"></a>
+<span class="sourceLineNo">754</span> freeGoal = Math.max(freeGoal, 1);<a name="line.754"></a>
+<span class="sourceLineNo">755</span> if (stats[i].freeCount() < freeGoal) {<a name="line.755"></a>
+<span class="sourceLineNo">756</span> fullCount++;<a name="line.756"></a>
+<span class="sourceLineNo">757</span> }<a name="line.757"></a>
+<span class="sourceLineNo">758</span> }<a name="line.758"></a>
+<span class="sourceLineNo">759</span> return fullCount;<a name="line.759"></a>
+<span class="sourceLineNo">760</span> }<a name="line.760"></a>
+<span class="sourceLineNo">761</span><a name="line.761"></a>
+<span class="sourceLineNo">762</span> /**<a name="line.762"></a>
+<span class="sourceLineNo">763</span> * This method will find the buckets that are minimally occupied<a name="line.763"></a>
+<span class="sourceLineNo">764</span> * and are not reference counted and will free them completely<a name="line.764"></a>
+<span class="sourceLineNo">765</span> * without any constraint on the access times of the elements,<a name="line.765"></a>
+<span class="sourceLineNo">766</span> * and as a process will completely free at most the number of buckets<a name="line.766"></a>
+<span class="sourceLineNo">767</span> * passed, sometimes it might not due to changing refCounts<a name="line.767"></a>
+<span class="sourceLineNo">768</span> *<a name="line.768"></a>
+<span class="sourceLineNo">769</span> * @param completelyFreeBucketsNeeded number of buckets to free<a name="line.769"></a>
+<span class="sourceLineNo">770</span> **/<a name="line.770"></a>
+<span class="sourceLineNo">771</span> private void freeEntireBuckets(int completelyFreeBucketsNeeded) {<a name="line.771"></a>
+<span class="sourceLineNo">772</span> if (completelyFreeBucketsNeeded != 0) {<a name="line.772"></a>
+<span class="sourceLineNo">773</span> // First we will build a set where the offsets are reference counted, usually<a name="line.773"></a>
+<span class="sourceLineNo">774</span> // this set is small around O(Handler Count) unless something else is wrong<a name="line.774"></a>
+<span class="sourceLineNo">775</span> Set<Integer> inUseBuckets = new HashSet<>();<a name="line.775"></a>
+<span class="sourceLineNo">776</span> backingMap.forEach((k, be) -> {<a name="line.776"></a>
+<span class="sourceLineNo">777</span> if (be.isRpcRef()) {<a name="line.777"></a>
+<span class="sourceLineNo">778</span> inUseBuckets.add(bucketAllocator.getBucketIndex(be.offset()));<a name="line.778"></a>
+<span class="sourceLineNo">779</span> }<a name="line.779"></a>
+<span class="sourceLineNo">780</span> });<a name="line.780"></a>
+<span class="sourceLineNo">781</span> Set<Integer> candidateBuckets =<a name="line.781"></a>
+<span class="sourceLineNo">782</span> bucketAllocator.getLeastFilledBuckets(inUseBuckets, completelyFreeBucketsNeeded);<a name="line.782"></a>
+<span class="sourceLineNo">783</span> for (Map.Entry<BlockCacheKey, BucketEntry> entry : backingMap.entrySet()) {<a name="line.783"></a>
+<span class="sourceLineNo">784</span> if (candidateBuckets.contains(bucketAllocator.getBucketIndex(entry.getValue().offset()))) {<a name="line.784"></a>
+<span class="sourceLineNo">785</span> evictBucketEntryIfNoRpcReferenced(entry.getKey(), entry.getValue());<a name="line.785"></a>
+<span class="sourceLineNo">786</span> }<a name="line.786"></a>
+<span class="sourceLineNo">787</span> }<a name="line.787"></a>
+<span class="sourceLineNo">788</span> }<a name="line.788"></a>
+<span class="sourceLineNo">789</span> }<a name="line.789"></a>
+<span class="sourceLineNo">790</span><a name="line.790"></a>
+<span class="sourceLineNo">791</span> /**<a name="line.791"></a>
+<span class="sourceLineNo">792</span> * Free the space if the used size reaches acceptableSize() or one size block<a name="line.792"></a>
+<span class="sourceLineNo">793</span> * couldn't be allocated. When freeing the space, we use the LRU algorithm and<a name="line.793"></a>
+<span class="sourceLineNo">794</span> * ensure there must be some blocks evicted<a name="line.794"></a>
+<span class="sourceLineNo">795</span> * @param why Why we are being called<a name="line.795"></a>
+<span class="sourceLineNo">796</span> */<a name="line.796"></a>
+<span class="sourceLineNo">797</span> private void freeSpace(final String why) {<a name="line.797"></a>
+<span class="sourceLineNo">798</span> // Ensure only one freeSpace progress at a time<a name="line.798"></a>
+<span class="sourceLineNo">799</span> if (!freeSpaceLock.tryLock()) {<a name="line.799"></a>
+<span class="sourceLineNo">800</span> return;<a name="line.800"></a>
+<span class="sourceLineNo">801</span> }<a name="line.801"></a>
+<span class="sourceLineNo">802</span> try {<a name="line.802"></a>
+<span class="sourceLineNo">803</span> freeInProgress = true;<a name="line.803"></a>
+<span class="sourceLineNo">804</span> long bytesToFreeWithoutExtra = 0;<a name="line.804"></a>
+<span class="sourceLineNo">805</span> // Calculate free byte for each bucketSizeinfo<a name="line.805"></a>
+<span class="sourceLineNo">806</span> StringBuilder msgBuffer = LOG.isDebugEnabled()? new StringBuilder(): null;<a name="line.806"></a>
+<span class="sourceLineNo">807</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.807"></a>
+<span class="sourceLineNo">808</span> long[] bytesToFreeForBucket = new long[stats.length];<a name="line.808"></a>
+<span class="sourceLineNo">809</span> for (int i = 0; i < stats.length; i++) {<a name="line.809"></a>
+<span class="sourceLineNo">810</span> bytesToFreeForBucket[i] = 0;<a name="line.810"></a>
+<span class="sourceLineNo">811</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.811"></a>
+<span class="sourceLineNo">812</span> freeGoal = Math.max(freeGoal, 1);<a name="line.812"></a>
+<span class="sourceLineNo">813</span> if (stats[i].freeCount() < freeGoal) {<a name="line.813"></a>
+<span class="sourceLineNo">814</span> bytesToFreeForBucket[i] = stats[i].itemSize() * (freeGoal - stats[i].freeCount());<a name="line.814"></a>
+<span class="sourceLineNo">815</span> bytesToFreeWithoutExtra += bytesToFreeForBucket[i];<a name="line.815"></a>
+<span class="sourceLineNo">816</span> if (msgBuffer != null) {<a name="line.816"></a>
+<span class="sourceLineNo">817</span> msgBuffer.append("Free for bucketSize(" + stats[i].itemSize() + ")="<a name="line.817"></a>
+<span class="sourceLineNo">818</span> + StringUtils.byteDesc(bytesToFreeForBucket[i]) + ", ");<a name="line.818"></a>
+<span class="sourceLineNo">819</span> }<a name="line.819"></a>
+<span class="sourceLineNo">820</span> }<a name="line.820"></a>
+<span class="sourceLineNo">821</span> }<a name="line.821"></a>
+<span class="sourceLineNo">822</span> if (msgBuffer != null) {<a name="line.822"></a>
+<span class="sourceLineNo">823</span> msgBuffer.append("Free for total=" + StringUtils.byteDesc(bytesToFreeWithoutExtra) + ", ");<a name="line.823"></a>
+<span class="sourceLineNo">824</span> }<a name="line.824"></a>
+<span class="sourceLineNo">825</span><a name="line.825"></a>
+<span class="sourceLineNo">826</span> if (bytesToFreeWithoutExtra <= 0) {<a name="line.826"></a>
+<span class="sourceLineNo">827</span> return;<a name="line.827"></a>
+<span class="sourceLineNo">828</span> }<a name="line.828"></a>
+<span class="sourceLineNo">829</span> long currentSize = bucketAllocator.getUsedSize();<a name="line.829"></a>
+<span class="sourceLineNo">830</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.830"></a>
+<span class="sourceLineNo">831</span> if (LOG.isDebugEnabled() && msgBuffer != null) {<a name="line.831"></a>
+<span class="sourceLineNo">832</span> LOG.debug("Free started because \"" + why + "\"; " + msgBuffer.toString() +<a name="line.832"></a>
+<span class="sourceLineNo">833</span> " of current used=" + StringUtils.byteDesc(currentSize) + ", actual cacheSize=" +<a name="line.833"></a>
+<span class="sourceLineNo">834</span> StringUtils.byteDesc(realCacheSize.sum()) + ", total=" + StringUtils.byteDesc(totalSize));<a name="line.834"></a>
+<span class="sourceLineNo">835</span> }<a name="line.835"></a>
+<span class="sourceLineNo">836</span><a name="line.836"></a>
+<span class="sourceLineNo">837</span> long bytesToFreeWithExtra = (long) Math.floor(bytesToFreeWithoutExtra<a name="line.837"></a>
+<span class="sourceLineNo">838</span> * (1 + extraFreeFactor));<a name="line.838"></a>
+<span class="sourceLineNo">839</span><a name="line.839"></a>
+<span class="sourceLineNo">840</span> // Instantiate priority buckets<a name="line.840"></a>
+<span class="sourceLineNo">841</span> BucketEntryGroup bucketSingle = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.841"></a>
+<span class="sourceLineNo">842</span> blockSize, getPartitionSize(singleFactor));<a name="line.842"></a>
+<span class="sourceLineNo">843</span> BucketEntryGroup bucketMulti = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.843"></a>
+<span class="sourceLineNo">844</span> blockSize, getPartitionSize(multiFactor));<a name="line.844"></a>
+<span class="sourceLineNo">845</span> BucketEntryGroup bucketMemory = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.845"></a>
+<span class="sourceLineNo">846</span> blockSize, getPartitionSize(memoryFactor));<a name="line.846"></a>
+<span class="sourceLineNo">847</span><a name="line.847"></a>
+<span class="sourceLineNo">848</span> // Scan entire map putting bucket entry into appropriate bucket entry<a name="line.848"></a>
+<span class="sourceLineNo">849</span> // group<a name="line.849"></a>
+<span class="sourceLineNo">850</span> for (Map.Entry<BlockCacheKey, BucketEntry> bucketEntryWithKey : backingMap.entrySet()) {<a name="line.850"></a>
+<span class="sourceLineNo">851</span> switch (bucketEntryWithKey.getValue().getPriority()) {<a name="line.851"></a>
+<span class="sourceLineNo">852</span> case SINGLE: {<a name="line.852"></a>
+<span class="sourceLineNo">853</span> bucketSingle.add(bucketEntryWithKey);<a name="line.853"></a>
+<span class="sourceLineNo">854</span> break;<a name="line.854"></a>
+<span class="sourceLineNo">855</span> }<a name="line.855"></a>
+<span class="sourceLineNo">856</span> case MULTI: {<a name="line.856"></a>
+<span class="sourceLineNo">857</span> bucketMulti.add(bucketEntryWithKey);<a name="line.857"></a>
+<span class="sourceLineNo">858</span> break;<a name="line.858"></a>
+<span class="sourceLineNo">859</span> }<a name="line.859"></a>
+<span class="sourceLineNo">860</span> case MEMORY: {<a name="line.860"></a>
+<span class="sourceLineNo">861</span> bucketMemory.add(bucketEntryWithKey);<a name="line.861"></a>
+<span class="sourceLineNo">862</span> break;<a name="line.862"></a>
+<span class="sourceLineNo">863</span> }<a name="line.863"></a>
+<span class="sourceLineNo">864</span> }<a name="line.864"></a>
+<span class="sourceLineNo">865</span> }<a name="line.865"></a>
+<span class="sourceLineNo">866</span><a name="line.866"></a>
+<span class="sourceLineNo">867</span> PriorityQueue<BucketEntryGroup> bucketQueue = new PriorityQueue<>(3,<a name="line.867"></a>
+<span class="sourceLineNo">868</span> Comparator.comparingLong(BucketEntryGroup::overflow));<a name="line.868"></a>
+<span class="sourceLineNo">869</span><a name="line.869"></a>
+<span class="sourceLineNo">870</span> bucketQueue.add(bucketSingle);<a name="line.870"></a>
+<span class="sourceLineNo">871</span> bucketQueue.add(bucketMulti);<a name="line.871"></a>
+<span class="sourceLineNo">872</span> bucketQueue.add(bucketMemory);<a name="line.872"></a>
+<span class="sourceLineNo">873</span><a name="line.873"></a>
+<span class="sourceLineNo">874</span> int remainingBuckets = bucketQueue.size();<a name="line.874"></a>
+<span class="sourceLineNo">875</span> long bytesFreed = 0;<a name="line.875"></a>
+<span class="sourceLineNo">876</span><a name="line.876"></a>
+<span class="sourceLineNo">877</span> BucketEntryGroup bucketGroup;<a name="line.877"></a>
+<span class="sourceLineNo">878</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.878"></a>
+<span class="sourceLineNo">879</span> long overflow = bucketGroup.overflow();<a name="line.879"></a>
+<span class="sourceLineNo">880</span> if (overflow > 0) {<a name="line.880"></a>
+<span class="sourceLineNo">881</span> long bucketBytesToFree = Math.min(overflow,<a name="line.881"></a>
+<span class="sourceLineNo">882</span> (bytesToFreeWithoutExtra - bytesFreed) / remainingBuckets);<a name="line.882"></a>
+<span class="sourceLineNo">883</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.883"></a>
+<span class="sourceLineNo">884</span> }<a name="line.884"></a>
+<span class="sourceLineNo">885</span> remainingBuckets--;<a name="line.885"></a>
+<span class="sourceLineNo">886</span> }<a name="line.886"></a>
+<span class="sourceLineNo">887</span><a name="line.887"></a>
+<span class="sourceLineNo">888</span> // Check and free if there are buckets that still need freeing of space<a name="line.888"></a>
+<span class="sourceLineNo">889</span> if (bucketSizesAboveThresholdCount(minFactor) > 0) {<a name="line.889"></a>
+<span class="sourceLineNo">890</span> bucketQueue.clear();<a name="line.890"></a>
+<span class="sourceLineNo">891</span> remainingBuckets = 3;<a name="line.891"></a>
+<span class="sourceLineNo">892</span><a name="line.892"></a>
+<span class="sourceLineNo">893</span> bucketQueue.add(bucketSingle);<a name="line.893"></a>
+<span class="sourceLineNo">894</span> bucketQueue.add(bucketMulti);<a name="line.894"></a>
+<span class="sourceLineNo">895</span> bucketQueue.add(bucketMemory);<a name="line.895"></a>
+<span class="sourceLineNo">896</span><a name="line.896"></a>
+<span class="sourceLineNo">897</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.897"></a>
+<span class="sourceLineNo">898</span> long bucketBytesToFree = (bytesToFreeWithExtra - bytesFreed) / remainingBuckets;<a name="line.898"></a>
+<span class="sourceLineNo">899</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.899"></a>
+<span class="sourceLineNo">900</span> remainingBuckets--;<a name="line.900"></a>
+<span class="sourceLineNo">901</span> }<a name="line.901"></a>
+<span class="sourceLineNo">902</span> }<a name="line.902"></a>
+<span class="sourceLineNo">903</span><a name="line.903"></a>
+<span class="sourceLineNo">904</span> // Even after the above free we might still need freeing because of the<a name="line.904"></a>
+<span class="sourceLineNo">905</span> // De-fragmentation of the buckets (also called Slab Calcification problem), i.e<a name="line.905"></a>
+<span class="sourceLineNo">906</span> // there might be some buckets where the occupancy is very sparse and thus are not<a name="line.906"></a>
+<span class="sourceLineNo">907</span> // yielding the free for the other bucket sizes, the fix for this to evict some<a name="line.907"></a>
+<span class="sourceLineNo">908</span> // of the buckets, we do this by evicting the buckets that are least fulled<a name="line.908"></a>
+<span class="sourceLineNo">909</span> freeEntireBuckets(DEFAULT_FREE_ENTIRE_BLOCK_FACTOR *<a name="line.909"></a>
+<span class="sourceLineNo">910</span> bucketSizesAboveThresholdCount(1.0f));<a name="line.910"></a>
+<span class="sourceLineNo">911</span><a name="line.911"></a>
+<span class="sourceLineNo">912</span> if (LOG.isDebugEnabled()) {<a name="line.912"></a>
+<span class="sourceLineNo">913</span> long single = bucketSingle.totalSize();<a name="line.913"></a>
+<span class="sourceLineNo">914</span> long multi = bucketMulti.totalSize();<a name="line.914"></a>
+<span class="sourceLineNo">915</span> long memory = bucketMemory.totalSize();<a name="line.915"></a>
+<span class="sourceLineNo">916</span> if (LOG.isDebugEnabled()) {<a name="line.916"></a>
+<span class="sourceLineNo">917</span> LOG.debug("Bucket cache free space completed; " + "freed="<a name="line.917"></a>
+<span class="sourceLineNo">918</span> + StringUtils.byteDesc(bytesFreed) + ", " + "total="<a name="line.918"></a>
+<span class="sourceLineNo">919</span> + StringUtils.byteDesc(totalSize) + ", " + "single="<a name="line.919"></a>
+<span class="sourceLineNo">920</span> + StringUtils.byteDesc(single) + ", " + "multi="<a name="line.920"></a>
+<span class="sourceLineNo">921</span> + StringUtils.byteDesc(multi) + ", " + "memory="<a name="line.921"></a>
+<span class="sourceLineNo">922</span> + StringUtils.byteDesc(memory));<a name="line.922"></a>
+<span class="sourceLineNo">923</span> }<a name="line.923"></a>
+<span class="sourceLineNo">924</span> }<a name="line.924"></a>
+<span class="sourceLineNo">925</span><a name="line.925"></a>
+<span class="sourceLineNo">926</span> } catch (Throwable t) {<a name="line.926"></a>
+<span class="sourceLineNo">927</span> LOG.warn("Failed freeing space", t);<a name="line.927"></a>
+<span class="sourceLineNo">928</span> } finally {<a name="line.928"></a>
+<span class="sourceLineNo">929</span> cacheStats.evict();<a name="line.929"></a>
+<span class="sourceLineNo">930</span> freeInProgress = false;<a name="line.930"></a>
+<span class="sourceLineNo">931</span> freeSpaceLock.unlock();<a name="line.931"></a>
+<span class="sourceLineNo">932</span> }<a name="line.932"></a>
+<span class="sourceLineNo">933</span> }<a name="line.933"></a>
+<span class="sourceLineNo">934</span><a name="line.934"></a>
+<span class="sourceLineNo">935</span> // This handles flushing the RAM cache to IOEngine.<a name="line.935"></a>
+<span class="sourceLineNo">936</span> class WriterThread extends Thread {<a name="line.936"></a>
+<span class="sourceLineNo">937</span> private final BlockingQueue<RAMQueueEntry> inputQueue;<a name="line.937"></a>
+<span class="sourceLineNo">938</span> private volatile boolean writerEnabled = true;<a name="line.938"></a>
+<span class="sourceLineNo">939</span><a name="line.939"></a>
+<span class="sourceLineNo">940</span> WriterThread(BlockingQueue<RAMQueueEntry> queue) {<a name="line.940"></a>
+<span class="sourceLineNo">941</span> super("BucketCacheWriterThread");<a name="line.941"></a>
+<span class="sourceLineNo">942</span> this.inputQueue = queue;<a name="line.942"></a>
+<span class="sourceLineNo">943</span> }<a name="line.943"></a>
+<span class="sourceLineNo">944</span><a name="line.944"></a>
+<span class="sourceLineNo">945</span> // Used for test<a name="line.945"></a>
+<span class="sourceLineNo">946</span> void disableWriter() {<a name="line.946"></a>
+<span class="sourceLineNo">947</span> this.writerEnabled = false;<a name="line.947"></a>
+<span class="sourceLineNo">948</span> }<a name="line.948"></a>
+<span class="sourceLineNo">949</span><a name="line.949"></a>
+<span class="sourceLineNo">950</span> @Override<a name="line.950"></a>
+<span class="sourceLineNo">951</span> public void run() {<a name="line.951"></a>
+<span class="sourceLineNo">952</span> List<RAMQueueEntry> entries = new ArrayList<>();<a name="line.952"></a>
+<span class="sourceLineNo">953</span> try {<a name="line.953"></a>
+<span class="sourceLineNo">954</span> while (cacheEnabled && writerEnabled) {<a name="line.954"></a>
+<span class="sourceLineNo">955</span> try {<a name="line.955"></a>
+<span class="sourceLineNo">956</span> try {<a name="line.956"></a>
+<span class="sourceLineNo">957</span> // Blocks<a name="line.957"></a>
+<span class="sourceLineNo">958</span> entries = getRAMQueueEntries(inputQueue, entries);<a name="line.958"></a>
+<span class="sourceLineNo">959</span> } catch (InterruptedException ie) {<a name="line.959"></a>
+<span class="sourceLineNo">960</span> if (!cacheEnabled || !writerEnabled) {<a name="line.960"></a>
+<span class="sourceLineNo">961</span> break;<a name="line.961"></a>
+<span class="sourceLineNo">962</span> }<a name="line.962"></a>
+<span class="sourceLineNo">963</span> }<a name="line.963"></a>
+<span class="sourceLineNo">964</span> doDrain(entries);<a name="line.964"></a>
+<span class="sourceLineNo">965</span> } catch (Exception ioe) {<a name="line.965"></a>
+<span class="sourceLineNo">966</span> LOG.error("WriterThread encountered error", ioe);<a name="line.966"></a>
+<span class="sourceLineNo">967</span> }<a name="line.967"></a>
+<span class="sourceLineNo">968</span> }<a name="line.968"></a>
+<span class="sourceLineNo">969</span> } catch (Throwable t) {<a name="line.969"></a>
+<span class="sourceLineNo">970</span> LOG.warn("Failed doing drain", t);<a name="line.970"></a>
+<span class="sourceLineNo">971</span> }<a name="line.971"></a>
+<span class="sourceLineNo">972</span> LOG.info(this.getName() + " exiting, cacheEnabled=" + cacheEnabled);<a name="line.972"></a>
+<span class="sourceLineNo">973</span> }<a name="line.973"></a>
+<span class="sourceLineNo">974</span> }<a name="line.974"></a>
+<span class="sourceLineNo">975</span><a name="line.975"></a>
+<span class="sourceLineNo">976</span> /**<a name="line.976"></a>
+<span class="sourceLineNo">977</span> * Put the new bucket entry into backingMap. Notice that we are allowed to replace the existing<a name="line.977"></a>
+<span class="sourceLineNo">978</span> * cache with a new block for the same cache key. there's a corner case: one thread cache a block<a name="line.978"></a>
+<span class="sourceLineNo">979</span> * in ramCache, copy to io-engine and add a bucket entry to backingMap. Caching another new block<a name="line.979"></a>
+<span class="sourceLineNo">980</span> * with the same cache key do the same thing for the same cache key, so if not evict the previous<a name="line.980"></a>
+<span class="sourceLineNo">981</span> * bucket entry, then memory leak happen because the previous bucketEntry is gone but the<a name="line.981"></a>
+<span class="sourceLineNo">982</span> * bucketAllocator do not free its memory.<a name="line.982"></a>
+<span class="sourceLineNo">983</span> * @see BlockCacheUtil#shouldReplaceExistingCacheBlock(BlockCache blockCache,BlockCacheKey<a name="line.983"></a>
+<span class="sourceLineNo">984</span> * cacheKey, Cacheable newBlock)<a name="line.984"></a>
+<span class="sourceLineNo">985</span> * @param key Block cache key<a name="line.985"></a>
+<span class="sourceLineNo">986</span> * @param bucketEntry Bucket entry to put into backingMap.<a name="line.986"></a>
+<span class="sourceLineNo">987</span> */<a name="line.987"></a>
+<span class="sourceLineNo">988</span> protected void putIntoBackingMap(BlockCacheKey key, BucketEntry bucketEntry) {<a name="line.988"></a>
+<span class="sourceLineNo">989</span> BucketEntry previousEntry = backingMap.put(key, bucketEntry);<a name="line.989"></a>
+<span class="sourceLineNo">990</span> if (previousEntry != null && previousEntry != bucketEntry) {<a name="line.990"></a>
+<span class="sourceLineNo">991</span> previousEntry.withWriteLock(offsetLock, () -> {<a name="line.991"></a>
+<span class="sourceLineNo">992</span> blockEvicted(key, previousEntry, false);<a name="line.992"></a>
+<span class="sourceLineNo">993</span> return null;<a name="line.993"></a>
+<span class="sourceLineNo">994</span> });<a name="line.994"></a>
+<span class="sourceLineNo">995</span> }<a name="line.995"></a>
+<span class="sourceLineNo">996</span> }<a name="line.996"></a>
+<span class="sourceLineNo">997</span><a name="line.997"></a>
+<span class="sourceLineNo">998</span> /**<a name="line.998"></a>
+<span class="sourceLineNo">999</span> * Flush the entries in ramCache to IOEngine and add bucket entry to backingMap. Process all that<a name="line.999"></a>
+<span class="sourceLineNo">1000</span> * are passed in even if failure being sure to remove from ramCache else we'll never undo the<a name="line.1000"></a>
+<span class="sourceLineNo">1001</span> * references and we'll OOME.<a name="line.1001"></a>
+<span class="sourceLineNo">1002</span> * @param entries Presumes list passed in here will be processed by this invocation only. No<a name="line.1002"></a>
+<span class="sourceLineNo">1003</span> * interference expected.<a name="line.1003"></a>
+<span class="sourceLineNo">1004</span> */<a name="line.1004"></a>
+<span class="sourceLineNo">1005</span> void doDrain(final List<RAMQueueEntry> entries) throws InterruptedException {<a name="line.1005"></a>
+<span class="sourceLineNo">1006</span> if (entries.isEmpty()) {<a name="line.1006"></a>
+<span class="sourceLineNo">1007</span> return;<a name="line.1007"></a>
+<span class="sourceLineNo">1008</span> }<a name="line.1008"></a>
+<span class="sourceLineNo">1009</span> // This method is a little hard to follow. We run through the passed in entries and for each<a name="line.1009"></a>
+<span class="sourceLineNo">1010</span> // successful add, we add a non-null BucketEntry to the below bucketEntries. Later we must<a name="line.1010"></a>
+<span class="sourceLineNo">1011</span> // do cleanup making sure we've cleared ramCache of all entries regardless of whether we<a name="line.1011"></a>
+<span class="sourceLineNo">1012</span> // successfully added the item to the bucketcache; if we don't do the cleanup, we'll OOME by<a name="line.1012"></a>
+<span class="sourceLineNo">1013</span> // filling ramCache. We do the clean up by again running through the passed in entries<a name="line.1013"></a>
+<span class="sourceLineNo">1014</span> // doing extra work when we find a non-null bucketEntries corresponding entry.<a name="line.1014"></a>
+<span class="sourceLineNo">1015</span> final int size = entries.size();<a name="line.1015"></a>
+<span class="sourceLineNo">1016</span> BucketEntry[] bucketEntries = new BucketEntry[size];<a name="line.1016"></a>
+<span class="sourceLineNo">1017</span> // Index updated inside loop if success or if we can't succeed. We retry if cache is full<a name="line.1017"></a>
+<span class="sourceLineNo">1018</span> // when we go to add an entry by going around the loop again without upping the index.<a name="line.1018"></a>
+<span class="sourceLineNo">1019</span> int index = 0;<a name="line.1019"></a>
+<span class="sourceLineNo">1020</span> while (cacheEnabled && index < size) {<a name="line.1020"></a>
+<span class="sourceLineNo">1021</span> RAMQueueEntry re = null;<a name="line.1021"></a>
+<span class="sourceLineNo">1022</span> try {<a name="line.1022"></a>
+<span class="sourceLineNo">1023</span> re = entries.get(index);<a name="line.1023"></a>
+<span class="sourceLineNo">1024</span> if (re == null) {<a name="line.1024"></a>
+<span class="sourceLineNo">1025</span> LOG.warn("Couldn't get entry or changed on us; who else is messing with it?");<a name="line.1025"></a>
+<span class="sourceLineNo">1026</span> index++;<a name="line.1026"></a>
+<span class="sourceLineNo">1027</span> continue;<a name="line.1027"></a>
+<span class="sourceLineNo">1028</span> }<a name="line.1028"></a>
+<span class="sourceLineNo">1029</span> BlockCacheKey cacheKey = re.getKey();<a name="line.1029"></a>
+<span class="sourceLineNo">1030</span> if (ramCache.containsKey(cacheKey)) {<a name="line.1030"></a>
+<span class="sourceLineNo">1031</span> blocksByHFile.add(cacheKey);<a name="line.1031"></a>
+<span class="sourceLineNo">1032</span> }<a name="line.1032"></a>
+<span class="sourceLineNo">1033</span><a name="line.1033"></a>
+<span class="sourceLineNo">1034</span> BucketEntry bucketEntry = re.writeToCache(ioEngine, bucketAllocator, realCacheSize,<a name="line.1034"></a>
+<span class="sourceLineNo">1035</span> this::createRecycler);<a name="line.1035"></a>
+<span class="sourceLineNo">1036</span> // Successfully added. Up index and add bucketEntry. Clear io exceptions.<a name="line.1036"></a>
+<span class="sourceLineNo">1037</span> bucketEntries[index] = bucketEntry;<a name="line.1037"></a>
+<span class="sourceLineNo">1038</span> if (ioErrorStartTime > 0) {<a name="line.1038"></a>
+<span class="sourceLineNo">1039</span> ioErrorStartTime = -1;<a name="line.1039"></a>
+<span class="sourceLineNo">1040</span> }<a name="line.1040"></a>
+<span class="sourceLineNo">1041</span> index++;<a name="line.1041"></a>
+<span class="sourceLineNo">1042</span> } catch (BucketAllocatorException fle) {<a name="line.1042"></a>
+<span class="sourceLineNo">1043</span> LOG.warn("Failed allocation for " + (re == null ? "" : re.getKey()) + "; " + fle);<a name="line.1043"></a>
+<span class="sourceLineNo">1044</span> // Presume can't add. Too big? Move index on. Entry will be cleared from ramCache below.<a name="line.1044"></a>
+<span class="sourceLineNo">1045</span> bucketEntries[index] = null;<a name="line.1045"></a>
+<span class="sourceLineNo">1046</span> index++;<a name="line.1046"></a>
+<span class="sourceLineNo">1047</span> } catch (CacheFullException cfe) {<a name="line.1047"></a>
+<span class="sourceLineNo">1048</span> // Cache full when we tried to add. Try freeing space and then retrying (don't up index)<a name="line.1048"></a>
+<span class="sourceLineNo">1049</span> if (!freeInProgress) {<a name="line.1049"></a>
+<span class="sourceLineNo">1050</span> freeSpace("Full!");<a name="line.1050"></a>
+<span class="sourceLineNo">1051</span> } else {<a name="line.1051"></a>
+<span class="sourceLineNo">1052</span> Thread.sleep(50);<a name="line.1052"></a>
+<span class="sourceLineNo">1053</span> }<a name="line.1053"></a>
+<span class="sourceLineNo">1054</span> } catch (IOException ioex) {<a name="line.1054"></a>
+<span class="sourceLineNo">1055</span> // Hopefully transient. Retry. checkIOErrorIsTolerated disables cache if problem.<a name="line.1055"></a>
+<span class="sourceLineNo">1056</span> LOG.error("Failed writing to bucket cache", ioex);<a name="line.1056"></a>
+<span class="sourceLineNo">1057</span> checkIOErrorIsTolerated();<a name="line.1057"></a>
+<span class="sourceLineNo">1058</span> }<a name="line.1058"></a>
+<span class="sourceLineNo">1059</span> }<a name="line.1059"></a>
+<span class="sourceLineNo">1060</span><a name="line.1060"></a>
+<span class="sourceLineNo">1061</span> // Make sure data pages are written on media before we update maps.<a name="line.1061"></a>
+<span class="sourceLineNo">1062</span> try {<a name="line.1062"></a>
+<span class="sourceLineNo">1063</span> ioEngine.sync();<a name="line.1063"></a>
+<span class="sourceLineNo">1064</span> } catch (IOException ioex) {<a name="line.1064"></a>
+<span class="sourceLineNo">1065</span> LOG.error("Failed syncing IO engine", ioex);<a name="line.1065"></a>
+<span class="sourceLineNo">1066</span> checkIOErrorIsTolerated();<a name="line.1066"></a>
+<span class="sourceLineNo">1067</span> // Since we failed sync, free the blocks in bucket allocator<a name="line.1067"></a>
+<span class="sourceLineNo">1068</span> for (int i = 0; i < entries.size(); ++i) {<a name="line.1068"></a>
+<span class="sourceLineNo">1069</span> if (bucketEntries[i] != null) {<a name="line.1069"></a>
+<span class="sourceLineNo">1070</span> bucketAllocator.freeBlock(bucketEntries[i].offset());<a name="line.1070"></a>
+<span class="sourceLineNo">1071</span> bucketEntries[i] = null;<a name="line.1071"></a>
+<span class="sourceLineNo">1072</span> }<a name="line.1072"></a>
+<span class="sourceLineNo">1073</span> }<a name="line.1073"></a>
+<span class="sourceLineNo">1074</span> }<a name="line.1074"></a>
+<span class="sourceLineNo">1075</span><a name="line.1075"></a>
+<span class="sourceLineNo">1076</span> // Now add to backingMap if successfully added to bucket cache. Remove from ramCache if<a name="line.1076"></a>
+<span class="sourceLineNo">1077</span> // success or error.<a name="line.1077"></a>
+<span class="sourceLineNo">1078</span> for (int i = 0; i < size; ++i) {<a name="line.1078"></a>
+<span class="sourceLineNo">1079</span> BlockCacheKey key = entries.get(i).getKey();<a name="line.1079"></a>
+<span class="sourceLineNo">1080</span> // Only add if non-null entry.<a name="line.1080"></a>
+<span class="sourceLineNo">1081</span> if (bucketEntries[i] != null) {<a name="line.1081"></a>
+<span class="sourceLineNo">1082</span> putIntoBackingMap(key, bucketEntries[i]);<a name="line.1082"></a>
+<span class="sourceLineNo">1083</span> }<a name="line.1083"></a>
+<span class="sourceLineNo">1084</span> // Always remove from ramCache even if we failed adding it to the block cache above.<a name="line.1084"></a>
+<span class="sourceLineNo">1085</span> boolean existed = ramCache.remove(key, re -> {<a name="line.1085"></a>
+<span class="sourceLineNo">1086</span> if (re != null) {<a name="line.1086"></a>
+<span class="sourceLineNo">1087</span> heapSize.add(-1 * re.getData().heapSize());<a name="line.1087"></a>
+<span class="sourceLineNo">1088</span> }<a name="line.1088"></a>
+<span class="sourceLineNo">1089</span> });<a name="line.1089"></a>
+<span class="sourceLineNo">1090</span> if (!existed && bucketEntries[i] != null) {<a name="line.1090"></a>
+<span class="sourceLineNo">1091</span> // Block should have already been evicted. Remove it and free space.<a name="line.1091"></a>
+<span class="sourceLineNo">1092</span> final BucketEntry bucketEntry = bucketEntries[i];<a name="line.1092"></a>
+<span class="sourceLineNo">1093</span> bucketEntry.withWriteLock(offsetLock, () -> {<a name="line.1093"></a>
+<span class="sourceLineNo">1094</span> if (backingMap.remove(key, bucketEntry)) {<a name="line.1094"></a>
+<span class="sourceLineNo">1095</span> blockEvicted(key, bucketEntry, false);<a name="line.1095"></a>
+<span class="sourceLineNo">1096</span> }<a name="line.1096"></a>
+<span class="sourceLineNo">1097</span> return null;<a name="line.1097"></a>
+<span class="sourceLineNo">1098</span> });<a name="line.1098"></a>
+<span class="sourceLineNo">1099</span> }<a name="line.1099"></a>
+<span class="sourceLineNo">1100</span> }<a name="line.1100"></a>
+<span class="sourceLineNo">1101</span><a name="line.1101"></a>
+<span class="sourceLineNo">1102</span> long used = bucketAllocator.getUsedSize();<a name="line.1102"></a>
+<span class="sourceLineNo">1103</span> if (used > acceptableSize()) {<a name="line.1103"></a>
+<span class="sourceLineNo">1104</span> freeSpace("Used=" + used + " > acceptable=" + acceptableSize());<a name="line.1104"></a>
+<span class="sourceLineNo">1105</span> }<a name="line.1105"></a>
+<span class="sourceLineNo">1106</span> return;<a name="line.1106"></a>
+<span class="sourceLineNo">1107</span> }<a name="line.1107"></a>
+<span class="sourceLineNo">1108</span><a name="line.1108"></a>
+<span class="sourceLineNo">1109</span> /**<a name="line.1109"></a>
+<span class="sourceLineNo">1110</span> * Blocks until elements available in {@code q} then tries to grab as many as possible before<a name="line.1110"></a>
+<span class="sourceLineNo">1111</span> * returning.<a name="line.1111"></a>
+<span class="sourceLineNo">1112</span> * @param receptacle Where to stash the elements taken from queue. We clear before we use it just<a name="line.1112"></a>
+<span class="sourceLineNo">1113</span> * in case.<a name="line.1113"></a>
+<span class="sourceLineNo">1114</span> * @param q The queue to take from.<a name="line.1114"></a>
+<span class="sourceLineNo">1115</span> * @return {@code receptacle} laden with elements taken from the queue or empty if none found.<a name="line.1115"></a>
+<span class="sourceLineNo">1116</span> */<a name="line.1116"></a>
+<span class="sourceLineNo">1117</span> static List<RAMQueueEntry> getRAMQueueEntries(BlockingQueue<RAMQueueEntry> q,<a name="line.1117"></a>
+<span class="sourceLineNo">1118</span> List<RAMQueueEntry> receptacle) throws InterruptedException {<a name="line.1118"></a>
+<span class="sourceLineNo">1119</span> // Clear sets all entries to null and sets size to 0. We retain allocations. Presume it<a name="line.1119"></a>
+<span class="sourceLineNo">1120</span> // ok even if list grew to accommodate thousands.<a name="line.1120"></a>
+<span class="sourceLineNo">1121</span> receptacle.clear();<a name="line.1121"></a>
+<span class="sourceLineNo">1122</span> receptacle.add(q.take());<a name="line.1122"></a>
+<span class="sourceLineNo">1123</span> q.drainTo(receptacle);<a name="line.1123"></a>
+<span class="sourceLineNo">1124</span> return receptacle;<a name="line.1124"></a>
+<span class="sourceLineNo">1125</span> }<a name="line.1125"></a>
+<span class="sourceLineNo">1126</span><a name="line.1126"></a>
+<span class="sourceLineNo">1127</span> /**<a name="line.1127"></a>
+<span class="sourceLineNo">1128</span> * @see #retrieveFromFile(int[])<a name="line.1128"></a>
+<span class="sourceLineNo">1129</span> */<a name="line.1129"></a>
+<span class="sourceLineNo">1130</span> @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="OBL_UNSATISFIED_OBLIGATION",<a name="line.1130"></a>
+<span class="sourceLineNo">1131</span> justification = "false positive, try-with-resources ensures close is called.")<a name="line.1131"></a>
+<span class="sourceLineNo">1132</span> private void persistToFile() throws IOException {<a name="line.1132"></a>
+<span class="sourceLineNo">1133</span> assert !cacheEnabled;<a name="line.1133"></a>
+<span class="sourceLineNo">1134</span> if (!ioEngine.isPersistent()) {<a name="line.1134"></a>
+<span class="sourceLineNo">1135</span> throw new IOException("Attempt to persist non-persistent cache mappings!");<a name="line.1135"></a>
<span class="sourceLineNo">1136</span> }<a name="line.1136"></a>
-<span class="sourceLineNo">1137</span> }<a name="line.1137"></a>
-<span class="sourceLineNo">1138</span><a name="line.1138"></a>
-<span class="sourceLineNo">1139</span> /**<a name="line.1139"></a>
-<span class="sourceLineNo">1140</span> * @see #persistToFile()<a name="line.1140"></a>
-<span class="sourceLineNo">1141</span> */<a name="line.1141"></a>
-<span class="sourceLineNo">1142</span> private void retrieveFromFile(int[] bucketSizes) throws IOException {<a name="line.1142"></a>
-<span class="sourceLineNo">1143</span> File persistenceFile = new File(persistencePath);<a name="line.1143"></a>
-<span class="sourceLineNo">1144</span> if (!persistenceFile.exists()) {<a name="line.1144"></a>
-<span class="sourceLineNo">1145</span> return;<a name="line.1145"></a>
-<span class="sourceLineNo">1146</span> }<a name="line.1146"></a>
-<span class="sourceLineNo">1147</span> assert !cacheEnabled;<a name="line.1147"></a>
-<span class="sourceLineNo">1148</span><a name="line.1148"></a>
-<span class="sourceLineNo">1149</span> try (FileInputStream in = deleteFileOnClose(persistenceFile)) {<a name="line.1149"></a>
-<span class="sourceLineNo">1150</span> int pblen = ProtobufMagic.lengthOfPBMagic();<a name="line.1150"></a>
-<span class="sourceLineNo">1151</span> byte[] pbuf = new byte[pblen];<a name="line.1151"></a>
-<span class="sourceLineNo">1152</span> int read = in.read(pbuf);<a name="line.1152"></a>
-<span class="sourceLineNo">1153</span> if (read != pblen) {<a name="line.1153"></a>
-<span class="sourceLineNo">1154</span> throw new IOException("Incorrect number of bytes read while checking for protobuf magic "<a name="line.1154"></a>
-<span class="sourceLineNo">1155</span> + "number. Requested=" + pblen + ", Received= " + read + ", File=" + persistencePath);<a name="line.1155"></a>
-<span class="sourceLineNo">1156</span> }<a name="line.1156"></a>
-<span class="sourceLineNo">1157</span> if (! ProtobufMagic.isPBMagicPrefix(pbuf)) {<a name="line.1157"></a>
-<span class="sourceLineNo">1158</span> // In 3.0 we have enough flexibility to dump the old cache data.<a name="line.1158"></a>
-<span class="sourceLineNo">1159</span> // TODO: In 2.x line, this might need to be filled in to support reading the old format<a name="line.1159"></a>
-<span class="sourceLineNo">1160</span> throw new IOException("Persistence file does not start with protobuf magic number. " +<a name="line.1160"></a>
-<span class="sourceLineNo">1161</span> persistencePath);<a name="line.1161"></a>
-<span class="sourceLineNo">1162</span> }<a name="line.1162"></a>
-<span class="sourceLineNo">1163</span> parsePB(BucketCacheProtos.BucketCacheEntry.parseDelimitedFrom(in));<a name="line.1163"></a>
-<span class="sourceLineNo">1164</span> bucketAllocator = new BucketAllocator(cacheCapacity, bucketSizes, backingMap, realCacheSize);<a name="line.1164"></a>
-<span class="sourceLineNo">1165</span> blockNumber.add(backingMap.size());<a name="line.1165"></a>
-<span class="sourceLineNo">1166</span> }<a name="line.1166"></a>
-<span class="sourceLineNo">1167</span> }<a name="line.1167"></a>
-<span class="sourceLineNo">1168</span><a name="line.1168"></a>
-<span class="sourceLineNo">1169</span> /**<a name="line.1169"></a>
-<span class="sourceLineNo">1170</span> * Create an input stream that deletes the file after reading it. Use in try-with-resources to<a name="line.1170"></a>
-<span class="sourceLineNo">1171</span> * avoid this pattern where an exception thrown from a finally block may mask earlier exceptions:<a name="line.1171"></a>
-<span class="sourceLineNo">1172</span> * <pre><a name="line.1172"></a>
-<span class="sourceLineNo">1173</span> * File f = ...<a name="line.1173"></a>
-<span class="sourceLineNo">1174</span> * try (FileInputStream fis = new FileInputStream(f)) {<a name="line.1174"></a>
-<span class="sourceLineNo">1175</span> * // use the input stream<a name="line.1175"></a>
-<span class="sourceLineNo">1176</span> * } finally {<a name="line.1176"></a>
-<span class="sourceLineNo">1177</span> * if (!f.delete()) throw new IOException("failed to delete");<a name="line.1177"></a>
-<span class="sourceLineNo">1178</span> * }<a name="line.1178"></a>
-<span class="sourceLineNo">1179</span> * </pre><a name="line.1179"></a>
-<span class="sourceLineNo">1180</span> * @param file the file to read and delete<a name="line.1180"></a>
-<span class="sourceLineNo">1181</span> * @return a FileInputStream for the given file<a name="line.1181"></a>
-<span class="sourceLineNo">1182</span> * @throws IOException if there is a problem creating the stream<a name="line.1182"></a>
-<span class="sourceLineNo">1183</span> */<a name="line.1183"></a>
-<span class="sourceLineNo">1184</span> private FileInputStream deleteFileOnClose(final File file) throws IOException {<a name="line.1184"></a>
-<span class="sourceLineNo">1185</span> return new FileInputStream(file) {<a name="line.1185"></a>
-<span class="sourceLineNo">1186</span> private File myFile;<a name="line.1186"></a>
-<span class="sourceLineNo">1187</span> private FileInputStream init(File file) {<a name="line.1187"></a>
-<span class="sourceLineNo">1188</span> myFile = file;<a name="line.1188"></a>
-<span class="sourceLineNo">1189</span> return this;<a name="line.1189"></a>
-<span class="sourceLineNo">1190</span> }<a name="line.1190"></a>
-<span class="sourceLineNo">1191</span> @Override<a name="line.1191"></a>
-<span class="sourceLineNo">1192</span> public void close() throws IOException {<a name="line.1192"></a>
-<span class="sourceLineNo">1193</span> // close() will be called during try-with-resources and it will be<a name="line.1193"></a>
-<span class="sourceLineNo">1194</span> // called by finalizer thread during GC. To avoid double-free resource,<a name="line.1194"></a>
-<span class="sourceLineNo">1195</span> // set myFile to null after the first call.<a name="line.1195"></a>
-<span class="sourceLineNo">1196</span> if (myFile == null) {<a name="line.1196"></a>
-<span class="sourceLineNo">1197</span> return;<a name="line.1197"></a>
-<span class="sourceLineNo">1198</span> }<a name="line.1198"></a>
-<span class="sourceLineNo">1199</span><a name="line.1199"></a>
-<span class="sourceLineNo">1200</span> super.close();<a name="line.1200"></a>
-<span class="sourceLineNo">1201</span> if (!myFile.delete()) {<a name="line.1201"></a>
-<span class="sourceLineNo">1202</span> throw new IOException("Failed deleting persistence file " + myFile.getAbsolutePath());<a name="line.1202"></a>
-<span class="sourceLineNo">1203</span> }<a name="line.1203"></a>
-<span class="sourceLineNo">1204</span> myFile = null;<a name="line.1204"></a>
-<span class="sourceLineNo">1205</span> }<a name="line.1205"></a>
-<span class="sourceLineNo">1206</span> }.init(file);<a name="line.1206"></a>
-<span class="sourceLineNo">1207</span> }<a name="line.1207"></a>
-<span class="sourceLineNo">1208</span><a name="line.1208"></a>
-<span class="sourceLineNo">1209</span> private void verifyCapacityAndClasses(long capacitySize, String ioclass, String mapclass)<a name="line.1209"></a>
-<span class="sourceLineNo">1210</span> throws IOException {<a name="line.1210"></a>
-<span class="sourceLineNo">1211</span> if (capacitySize != cacheCapacity) {<a name="line.1211"></a>
-<span class="sourceLineNo">1212</span> throw new IOException("Mismatched cache capacity:"<a name="line.1212"></a>
-<span class="sourceLineNo">1213</span> + StringUtils.byteDesc(capacitySize) + ", expected: "<a name="line.1213"></a>
-<span class="sourceLineNo">1214</span> + StringUtils.byteDesc(cacheCapacity));<a name="line.1214"></a>
-<span class="sourceLineNo">1215</span> }<a name="line.1215"></a>
-<span class="sourceLineNo">1216</span> if (!ioEngine.getClass().getName().equals(ioclass)) {<a name="line.1216"></a>
-<span class="sourceLineNo">1217</span> throw new IOException("Class name for IO engine mismatch: " + ioclass<a name="line.1217"></a>
-<span class="sourceLineNo">1218</span> + ", expected:" + ioEngine.getClass().getName());<a name="line.1218"></a>
+<span class="sourceLineNo">1137</span> try (FileOutputStream fos = new FileOutputStream(persistencePath, false)) {<a name="line.1137"></a>
+<span class="sourceLineNo">1138</span> fos.write(ProtobufMagic.PB_MAGIC);<a name="line.1138"></a>
+<span class="sourceLineNo">1139</span> BucketProtoUtils.toPB(this).writeDelimitedTo(fos);<a name="line.1139"></a>
+<span class="sourceLineNo">1140</span> }<a name="line.1140"></a>
+<span class="sourceLineNo">1141</span> }<a name="line.1141"></a>
+<span class="sourceLineNo">1142</span><a name="line.1142"></a>
+<span class="sourceLineNo">1143</span> /**<a name="line.1143"></a>
+<span class="sourceLineNo">1144</span> * @see #persistToFile()<a name="line.1144"></a>
+<span class="sourceLineNo">1145</span> */<a name="line.1145"></a>
+<span class="sourceLineNo">1146</span> private void retrieveFromFile(int[] bucketSizes) throws IOException {<a name="line.1146"></a>
+<span class="sourceLineNo">1147</span> File persistenceFile = new File(persistencePath);<a name="line.1147"></a>
+<span class="sourceLineNo">1148</span> if (!persistenceFile.exists()) {<a name="line.1148"></a>
+<span class="sourceLineNo">1149</span> return;<a name="line.1149"></a>
+<span class="sourceLineNo">1150</span> }<a name="line.1150"></a>
+<span class="sourceLineNo">1151</span> assert !cacheEnabled;<a name="line.1151"></a>
+<span class="sourceLineNo">1152</span><a name="line.1152"></a>
+<span class="sourceLineNo">1153</span> try (FileInputStream in = deleteFileOnClose(persistenceFile)) {<a name="line.1153"></a>
+<span class="sourceLineNo">1154</span> int pblen = ProtobufMagic.lengthOfPBMagic();<a name="line.1154"></a>
+<span class="sourceLineNo">1155</span> byte[] pbuf = new byte[pblen];<a name="line.1155"></a>
+<span class="sourceLineNo">1156</span> int read = in.read(pbuf);<a name="line.1156"></a>
+<span class="sourceLineNo">1157</span> if (read != pblen) {<a name="line.1157"></a>
+<span class="sourceLineNo">1158</span> throw new IOException("Incorrect number of bytes read while checking for protobuf magic "<a name="line.1158"></a>
+<span class="sourceLineNo">1159</span> + "number. Requested=" + pblen + ", Received= " + read + ", File=" + persistencePath);<a name="line.1159"></a>
+<span class="sourceLineNo">1160</span> }<a name="line.1160"></a>
+<span class="sourceLineNo">1161</span> if (! ProtobufMagic.isPBMagicPrefix(pbuf)) {<a name="line.1161"></a>
+<span class="sourceLineNo">1162</span> // In 3.0 we have enough flexibility to dump the old cache data.<a name="line.1162"></a>
+<span class="sourceLineNo">1163</span> // TODO: In 2.x line, this might need to be filled in to support reading the old format<a name="line.1163"></a>
+<span class="sourceLineNo">1164</span> throw new IOException("Persistence file does not start with protobuf magic number. " +<a name="line.1164"></a>
+<span class="sourceLineNo">1165</span> persistencePath);<a name="line.1165"></a>
+<span class="sourceLineNo">1166</span> }<a name="line.1166"></a>
+<span class="sourceLineNo">1167</span> parsePB(BucketCacheProtos.BucketCacheEntry.parseDelimitedFrom(in));<a name="line.1167"></a>
+<span class="sourceLineNo">1168</span> bucketAllocator = new BucketAllocator(cacheCapacity, bucketSizes, backingMap, realCacheSize);<a name="line.1168"></a>
+<span class="sourceLineNo">1169</span> blockNumber.add(backingMap.size());<a name="line.1169"></a>
+<span class="sourceLineNo">1170</span> }<a name="line.1170"></a>
+<span class="sourceLineNo">1171</span> }<a name="line.1171"></a>
+<span class="sourceLineNo">1172</span><a name="line.1172"></a>
+<span class="sourceLineNo">1173</span> /**<a name="line.1173"></a>
+<span class="sourceLineNo">1174</span> * Create an input stream that deletes the file after reading it. Use in try-with-resources to<a name="line.1174"></a>
+<span class="sourceLineNo">1175</span> * avoid this pattern where an exception thrown from a finally block may mask earlier exceptions:<a name="line.1175"></a>
+<span class="sourceLineNo">1176</span> * <pre><a name="line.1176"></a>
+<span class="sourceLineNo">1177</span> * File f = ...<a name="line.1177"></a>
+<span class="sourceLineNo">1178</span> * try (FileInputStream fis = new FileInputStream(f)) {<a name="line.1178"></a>
+<span class="sourceLineNo">1179</span> * // use the input stream<a name="line.1179"></a>
+<span class="sourceLineNo">1180</span> * } finally {<a name="line.1180"></a>
+<span class="sourceLineNo">1181</span> * if (!f.delete()) throw new IOException("failed to delete");<a name="line.1181"></a>
+<span class="sourceLineNo">1182</span> * }<a name="line.1182"></a>
+<span class="sourceLineNo">1183</span> * </pre><a name="line.1183"></a>
+<span class="sourceLineNo">1184</span> * @param file the file to read and delete<a name="line.1184"></a>
+<span class="sourceLineNo">1185</span> * @return a FileInputStream for the given file<a name="line.1185"></a>
+<span class="sourceLineNo">1186</span> * @throws IOException if there is a problem creating the stream<a name="line.1186"></a>
+<span class="sourceLineNo">1187</span> */<a name="line.1187"></a>
+<span class="sourceLineNo">1188</span> private FileInputStream deleteFileOnClose(final File file) throws IOException {<a name="line.1188"></a>
+<span class="sourceLineNo">1189</span> return new FileInputStream(file) {<a name="line.1189"></a>
+<span class="sourceLineNo">1190</span> private File myFile;<a name="line.1190"></a>
+<span class="sourceLineNo">1191</span> private FileInputStream init(File file) {<a name="line.1191"></a>
+<span class="sourceLineNo">1192</span> myFile = file;<a name="line.1192"></a>
+<span class="sourceLineNo">1193</span> return this;<a name="line.1193"></a>
+<span class="sourceLineNo">1194</span> }<a name="line.1194"></a>
+<span class="sourceLineNo">1195</span> @Override<a name="line.1195"></a>
+<span class="sourceLineNo">1196</span> public void close() throws IOException {<a name="line.1196"></a>
+<span class="sourceLineNo">1197</span> // close() will be called during try-with-resources and it will be<a name="line.1197"></a>
+<span class="sourceLineNo">1198</span> // called by finalizer thread during GC. To avoid double-free resource,<a name="line.1198"></a>
+<span class="sourceLineNo">1199</span> // set myFile to null after the first call.<a name="line.1199"></a>
+<span class="sourceLineNo">1200</span> if (myFile == null) {<a name="line.1200"></a>
+<span class="sourceLineNo">1201</span> return;<a name="line.1201"></a>
+<span class="sourceLineNo">1202</span> }<a name="line.1202"></a>
+<span class="sourceLineNo">1203</span><a name="line.1203"></a>
+<span class="sourceLineNo">1204</span> super.close();<a name="line.1204"></a>
+<span class="sourceLineNo">1205</span> if (!myFile.delete()) {<a name="line.1205"></a>
+<span class="sourceLineNo">1206</span> throw new IOException("Failed deleting persistence file " + myFile.getAbsolutePath());<a name="line.1206"></a>
+<span class="sourceLineNo">1207</span> }<a name="line.1207"></a>
+<span class="sourceLineNo">1208</span> myFile = null;<a name="line.1208"></a>
+<span class="sourceLineNo">1209</span> }<a name="line.1209"></a>
+<span class="sourceLineNo">1210</span> }.init(file);<a name="line.1210"></a>
+<span class="sourceLineNo">1211</span> }<a name="line.1211"></a>
+<span class="sourceLineNo">1212</span><a name="line.1212"></a>
+<span class="sourceLineNo">1213</span> private void verifyCapacityAndClasses(long capacitySize, String ioclass, String mapclass)<a name="line.1213"></a>
+<span class="sourceLineNo">1214</span> throws IOException {<a name="line.1214"></a>
+<span class="sourceLineNo">1215</span> if (capacitySize != cacheCapacity) {<a name="line.1215"></a>
+<span class="sourceLineNo">1216</span> throw new IOException("Mismatched cache capacity:"<a name="line.1216"></a>
+<span class="sourceLineNo">1217</span> + StringUtils.byteDesc(capacitySize) + ", expected: "<a name="line.1217"></a>
+<span class="sourceLineNo">1218</span> + StringUtils.byteDesc(cacheCapacity));<a name="line.1218"></a>
<span class="sourceLineNo">1219</span> }<a name="line.1219"></a>
-<span class="sourceLineNo">1220</span> if (!backingMap.getClass().getName().equals(mapclass)) {<a name="line.1220"></a>
-<span class="sourceLineNo">1221</span> throw new IOException("Class name for cache map mismatch: " + mapclass<a name="line.1221"></a>
-<span class="sourceLineNo">1222</span> + ", expected:" + backingMap.getClass().getName());<a name="line.1222"></a>
+<span class="sourceLineNo">1220</span> if (!ioEngine.getClass().getName().equals(ioclass)) {<a name="line.1220"></a>
+<span class="sourceLineNo">1221</span> throw new IOException("Class name for IO engine mismatch: " + ioclass<a name="line.1221"></a>
+<span class="sourceLineNo">1222</span> + ", expected:" + ioEngine.getClass().getName());<a name="line.1222"></a>
<span class="sourceLineNo">1223</span> }<a name="line.1223"></a>
-<span class="sourceLineNo">1224</span> }<a name="line.1224"></a>
-<span class="sourceLineNo">1225</span><a name="line.1225"></a>
-<span class="sourceLineNo">1226</span> private void parsePB(BucketCacheProtos.BucketCacheEntry proto) throws IOException {<a name="line.1226"></a>
-<span class="sourceLineNo">1227</span> if (proto.hasChecksum()) {<a name="line.1227"></a>
-<span class="sourceLineNo">1228</span> ((PersistentIOEngine) ioEngine).verifyFileIntegrity(proto.getChecksum().toByteArray(),<a name="line.1228"></a>
-<span class="sourceLineNo">1229</span> algorithm);<a name="line.1229"></a>
-<span class="sourceLineNo">1230</span> } else {<a name="line.1230"></a>
-<span class="sourceLineNo">1231</span> // if has not checksum, it means the persistence file is old format<a name="line.1231"></a>
-<span class="sourceLineNo">1232</span> LOG.info("Persistent file is old format, it does not support verifying file integrity!");<a name="line.1232"></a>
-<span class="sourceLineNo">1233</span> }<a name="line.1233"></a>
-<span class="sourceLineNo">1234</span> verifyCapacityAndClasses(proto.getCacheCapacity(), proto.getIoClass(), proto.getMapClass());<a name="line.1234"></a>
-<span class="sourceLineNo">1235</span> backingMap = BucketProtoUtils.fromPB(proto.getDeserializersMap(), proto.getBackingMap(),<a name="line.1235"></a>
-<span class="sourceLineNo">1236</span> this::createRecycler);<a name="line.1236"></a>
-<span class="sourceLineNo">1237</span> }<a name="line.1237"></a>
-<span class="sourceLineNo">1238</span><a name="line.1238"></a>
-<span class="sourceLineNo">1239</span> /**<a name="line.1239"></a>
-<span class="sourceLineNo">1240</span> * Check whether we tolerate IO error this time. If the duration of IOEngine<a name="line.1240"></a>
-<span class="sourceLineNo">1241</span> * throwing errors exceeds ioErrorsDurationTimeTolerated, we will disable the<a name="line.1241"></a>
-<span class="sourceLineNo">1242</span> * cache<a name="line.1242"></a>
-<span class="sourceLineNo">1243</span> */<a name="line.1243"></a>
-<span class="sourceLineNo">1244</span> private void checkIOErrorIsTolerated() {<a name="line.1244"></a>
-<span class="sourceLineNo">1245</span> long now = EnvironmentEdgeManager.currentTime();<a name="line.1245"></a>
-<span class="sourceLineNo">1246</span> // Do a single read to a local variable to avoid timing issue - HBASE-24454<a name="line.1246"></a>
-<span class="sourceLineNo">1247</span> long ioErrorStartTimeTmp = this.ioErrorStartTime;<a name="line.1247"></a>
-<span class="sourceLineNo">1248</span> if (ioErrorStartTimeTmp > 0) {<a name="line.1248"></a>
-<span class="sourceLineNo">1249</span> if (cacheEnabled && (now - ioErrorStartTimeTmp) > this.ioErrorsTolerationDuration) {<a name="line.1249"></a>
-<span class="sourceLineNo">1250</span> LOG.error("IO errors duration time has exceeded " + ioErrorsTolerationDuration +<a name="line.1250"></a>
-<span class="sourceLineNo">1251</span> "ms, disabling cache, please check your IOEngine");<a name="line.1251"></a>
-<span class="sourceLineNo">1252</span> disableCache();<a name="line.1252"></a>
-<span class="sourceLineNo">1253</span> }<a name="line.1253"></a>
-<span class="sourceLineNo">1254</span> } else {<a name="line.1254"></a>
-<span class="sourceLineNo">1255</span> this.ioErrorStartTime = now;<a name="line.1255"></a>
-<span class="sourceLineNo">1256</span> }<a name="line.1256"></a>
-<span class="sourceLineNo">1257</span> }<a name="line.1257"></a>
-<span class="sourceLineNo">1258</span><a name="line.1258"></a>
-<span class="sourceLineNo">1259</span> /**<a name="line.1259"></a>
-<span class="sourceLineNo">1260</span> * Used to shut down the cache -or- turn it off in the case of something broken.<a name="line.1260"></a>
-<span class="sourceLineNo">1261</span> */<a name="line.1261"></a>
-<span class="sourceLineNo">1262</span> private void disableCache() {<a name="line.1262"></a>
-<span class="sourceLineNo">1263</span> if (!cacheEnabled) return;<a name="line.1263"></a>
-<span class="sourceLineNo">1264</span> cacheEnabled = false;<a name="line.1264"></a>
-<span class="sourceLineNo">1265</span> ioEngine.shutdown();<a name="line.1265"></a>
-<span class="sourceLineNo">1266</span> this.scheduleThreadPool.shutdown();<a name="line.1266"></a>
-<span class="sourceLineNo">1267</span> for (int i = 0; i < writerThreads.length; ++i) writerThreads[i].interrupt();<a name="line.1267"></a>
-<span class="sourceLineNo">1268</span> this.ramCache.clear();<a name="line.1268"></a>
-<span class="sourceLineNo">1269</span> if (!ioEngine.isPersistent() || persistencePath == null) {<a name="line.1269"></a>
-<span class="sourceLineNo">1270</span> // If persistent ioengine and a path, we will serialize out the backingMap.<a name="line.1270"></a>
-<span class="sourceLineNo">1271</span> this.backingMap.clear();<a name="line.1271"></a>
-<span class="sourceLineNo">1272</span> }<a name="line.1272"></a>
-<span class="sourceLineNo">1273</span> }<a name="line.1273"></a>
-<span class="sourceLineNo">1274</span><a name="line.1274"></a>
-<span class="sourceLineNo">1275</span> private void join() throws InterruptedException {<a name="line.1275"></a>
-<span class="sourceLineNo">1276</span> for (int i = 0; i < writerThreads.length; ++i)<a name="line.1276"></a>
-<span class="sourceLineNo">1277</span> writerThreads[i].join();<a name="line.1277"></a>
-<span class="sourceLineNo">1278</span> }<a name="line.1278"></a>
-<span class="sourceLineNo">1279</span><a name="line.1279"></a>
-<span class="sourceLineNo">1280</span> @Override<a name="line.1280"></a>
-<span class="sourceLineNo">1281</span> public void shutdown() {<a name="line.1281"></a>
-<span class="sourceLineNo">1282</span> disableCache();<a name="line.1282"></a>
-<span class="sourceLineNo">1283</span> LOG.info("Shutdown bucket cache: IO persistent=" + ioEngine.isPersistent()<a name="line.1283"></a>
-<span class="sourceLineNo">1284</span> + "; path to write=" + persistencePath);<a name="line.1284"></a>
-<span class="sourceLineNo">1285</span> if (ioEngine.isPersistent() && persistencePath != null) {<a name="line.1285"></a>
-<span class="sourceLineNo">1286</span> try {<a name="line.1286"></a>
-<span class="sourceLineNo">1287</span> join();<a name="line.1287"></a>
-<span class="sourceLineNo">1288</span> persistToFile();<a name="line.1288"></a>
-<span class="sourceLineNo">1289</span> } catch (IOException ex) {<a name="line.1289"></a>
-<span class="sourceLineNo">1290</span> LOG.error("Unable to persist data on exit: " + ex.toString(), ex);<a name="line.1290"></a>
-<span class="sourceLineNo">1291</span> } catch (InterruptedException e) {<a name="line.1291"></a>
-<span class="sourceLineNo">1292</span> LOG.warn("Failed to persist data on exit", e);<a name="line.1292"></a>
-<span class="sourceLineNo">1293</span> }<a name="line.1293"></a>
-<span class="sourceLineNo">1294</span> }<a name="line.1294"></a>
-<span class="sourceLineNo">1295</span> }<a name="line.1295"></a>
-<span class="sourceLineNo">1296</span><a name="line.1296"></a>
-<span class="sourceLineNo">1297</span> @Override<a name="line.1297"></a>
-<span class="sourceLineNo">1298</span> public CacheStats getStats() {<a name="line.1298"></a>
-<span class="sourceLineNo">1299</span> return cacheStats;<a name="line.1299"></a>
-<span class="sourceLineNo">1300</span> }<a name="line.1300"></a>
-<span class="sourceLineNo">1301</span><a name="line.1301"></a>
-<span class="sourceLineNo">1302</span> public BucketAllocator getAllocator() {<a name="line.1302"></a>
-<span class="sourceLineNo">1303</span> return this.bucketAllocator;<a name="line.1303"></a>
+<span class="sourceLineNo">1224</span> if (!backingMap.getClass().getName().equals(mapclass)) {<a name="line.1224"></a>
+<span class="sourceLineNo">1225</span> throw new IOException("Class name for cache map mismatch: " + mapclass<a name="line.1225"></a>
+<span class="sourceLineNo">1226</span> + ", expected:" + backingMap.getClass().getName());<a name="line.1226"></a>
+<span class="sourceLineNo">1227</span> }<a name="line.1227"></a>
+<span class="sourceLineNo">1228</span> }<a name="line.1228"></a>
+<span class="sourceLineNo">1229</span><a name="line.1229"></a>
+<span class="sourceLineNo">1230</span> private void parsePB(BucketCacheProtos.BucketCacheEntry proto) throws IOException {<a name="line.1230"></a>
+<span class="sourceLineNo">1231</span> if (proto.hasChecksum()) {<a name="line.1231"></a>
+<span class="sourceLineNo">1232</span> ((PersistentIOEngine) ioEngine).verifyFileIntegrity(proto.getChecksum().toByteArray(),<a name="line.1232"></a>
+<span class="sourceLineNo">1233</span> algorithm);<a name="line.1233"></a>
+<span class="sourceLineNo">1234</span> } else {<a name="line.1234"></a>
+<span class="sourceLineNo">1235</span> // if has not checksum, it means the persistence file is old format<a name="line.1235"></a>
+<span class="sourceLineNo">1236</span> LOG.info("Persistent file is old format, it does not support verifying file integrity!");<a name="line.1236"></a>
+<span class="sourceLineNo">1237</span> }<a name="line.1237"></a>
+<span class="sourceLineNo">1238</span> verifyCapacityAndClasses(proto.getCacheCapacity(), proto.getIoClass(), proto.getMapClass());<a name="line.1238"></a>
+<span class="sourceLineNo">1239</span> backingMap = BucketProtoUtils.fromPB(proto.getDeserializersMap(), proto.getBackingMap(),<a name="line.1239"></a>
+<span class="sourceLineNo">1240</span> this::createRecycler);<a name="line.1240"></a>
+<span class="sourceLineNo">1241</span> }<a name="line.1241"></a>
+<span class="sourceLineNo">1242</span><a name="line.1242"></a>
+<span class="sourceLineNo">1243</span> /**<a name="line.1243"></a>
+<span class="sourceLineNo">1244</span> * Check whether we tolerate IO error this time. If the duration of IOEngine<a name="line.1244"></a>
+<span class="sourceLineNo">1245</span> * throwing errors exceeds ioErrorsDurationTimeTolerated, we will disable the<a name="line.1245"></a>
+<span class="sourceLineNo">1246</span> * cache<a name="line.1246"></a>
+<span class="sourceLineNo">1247</span> */<a name="line.1247"></a>
+<span class="sourceLineNo">1248</span> private void checkIOErrorIsTolerated() {<a name="line.1248"></a>
+<span class="sourceLineNo">1249</span> long now = EnvironmentEdgeManager.currentTime();<a name="line.1249"></a>
+<span class="sourceLineNo">1250</span> // Do a single read to a local variable to avoid timing issue - HBASE-24454<a name="line.1250"></a>
+<span class="sourceLineNo">1251</span> long ioErrorStartTimeTmp = this.ioErrorStartTime;<a name="line.1251"></a>
+<span class="sourceLineNo">1252</span> if (ioErrorStartTimeTmp > 0) {<a name="line.1252"></a>
+<span class="sourceLineNo">1253</span> if (cacheEnabled && (now - ioErrorStartTimeTmp) > this.ioErrorsTolerationDuration) {<a name="line.1253"></a>
+<span class="sourceLineNo">1254</span> LOG.error("IO errors duration time has exceeded " + ioErrorsTolerationDuration +<a name="line.1254"></a>
+<span class="sourceLineNo">1255</span> "ms, disabling cache, please check your IOEngine");<a name="line.1255"></a>
+<span class="sourceLineNo">1256</span> disableCache();<a name="line.1256"></a>
+<span class="sourceLineNo">1257</span> }<a name="line.1257"></a>
+<span class="sourceLineNo">1258</span> } else {<a name="line.1258"></a>
+<span class="sourceLineNo">1259</span> this.ioErrorStartTime = now;<a name="line.1259"></a>
+<span class="sourceLineNo">1260</span> }<a name="line.1260"></a>
+<span class="sourceLineNo">1261</span> }<a name="line.1261"></a>
+<span class="sourceLineNo">1262</span><a name="line.1262"></a>
+<span class="sourceLineNo">1263</span> /**<a name="line.1263"></a>
+<span class="sourceLineNo">1264</span> * Used to shut down the cache -or- turn it off in the case of something broken.<a name="line.1264"></a>
+<span class="sourceLineNo">1265</span> */<a name="line.1265"></a>
+<span class="sourceLineNo">1266</span> private void disableCache() {<a name="line.1266"></a>
+<span class="sourceLineNo">1267</span> if (!cacheEnabled) return;<a name="line.1267"></a>
+<span class="sourceLineNo">1268</span> cacheEnabled = false;<a name="line.1268"></a>
+<span class="sourceLineNo">1269</span> ioEngine.shutdown();<a name="line.1269"></a>
+<span class="sourceLineNo">1270</span> this.scheduleThreadPool.shutdown();<a name="line.1270"></a>
+<span class="sourceLineNo">1271</span> for (int i = 0; i < writerThreads.length; ++i) writerThreads[i].interrupt();<a name="line.1271"></a>
+<span class="sourceLineNo">1272</span> this.ramCache.clear();<a name="line.1272"></a>
+<span class="sourceLineNo">1273</span> if (!ioEngine.isPersistent() || persistencePath == null) {<a name="line.1273"></a>
+<span class="sourceLineNo">1274</span> // If persistent ioengine and a path, we will serialize out the backingMap.<a name="line.1274"></a>
+<span class="sourceLineNo">1275</span> this.backingMap.clear();<a name="line.1275"></a>
+<span class="sourceLineNo">1276</span> }<a name="line.1276"></a>
+<span class="sourceLineNo">1277</span> }<a name="line.1277"></a>
+<span class="sourceLineNo">1278</span><a name="line.1278"></a>
+<span class="sourceLineNo">1279</span> private void join() throws InterruptedException {<a name="line.1279"></a>
+<span class="sourceLineNo">1280</span> for (int i = 0; i < writerThreads.length; ++i)<a name="line.1280"></a>
+<span class="sourceLineNo">1281</span> writerThreads[i].join();<a name="line.1281"></a>
+<span class="sourceLineNo">1282</span> }<a name="line.1282"></a>
+<span class="sourceLineNo">1283</span><a name="line.1283"></a>
+<span class="sourceLineNo">1284</span> @Override<a name="line.1284"></a>
+<span class="sourceLineNo">1285</span> public void shutdown() {<a name="line.1285"></a>
+<span class="sourceLineNo">1286</span> disableCache();<a name="line.1286"></a>
+<span class="sourceLineNo">1287</span> LOG.info("Shutdown bucket cache: IO persistent=" + ioEngine.isPersistent()<a name="line.1287"></a>
+<span class="sourceLineNo">1288</span> + "; path to write=" + persistencePath);<a name="line.1288"></a>
+<span class="sourceLineNo">1289</span> if (ioEngine.isPersistent() && persistencePath != null) {<a name="line.1289"></a>
+<span class="sourceLineNo">1290</span> try {<a name="line.1290"></a>
+<span class="sourceLineNo">1291</span> join();<a name="line.1291"></a>
+<span class="sourceLineNo">1292</span> persistToFile();<a name="line.1292"></a>
+<span class="sourceLineNo">1293</span> } catch (IOException ex) {<a name="line.1293"></a>
+<span class="sourceLineNo">1294</span> LOG.error("Unable to persist data on exit: " + ex.toString(), ex);<a name="line.1294"></a>
+<span class="sourceLineNo">1295</span> } catch (InterruptedException e) {<a name="line.1295"></a>
+<span class="sourceLineNo">1296</span> LOG.warn("Failed to persist data on exit", e);<a name="line.1296"></a>
+<span class="sourceLineNo">1297</span> }<a name="line.1297"></a>
+<span class="sourceLineNo">1298</span> }<a name="line.1298"></a>
+<span class="sourceLineNo">1299</span> }<a name="line.1299"></a>
+<span class="sourceLineNo">1300</span><a name="line.1300"></a>
+<span class="sourceLineNo">1301</span> @Override<a name="line.1301"></a>
+<span class="sourceLineNo">1302</span> public CacheStats getStats() {<a name="line.1302"></a>
+<span class="sourceLineNo">1303</span> return cacheStats;<a name="line.1303"></a>
<span class="sourceLineNo">1304</span> }<a name="line.1304"></a>
<span class="sourceLineNo">1305</span><a name="line.1305"></a>
-<span class="sourceLineNo">1306</span> @Override<a name="line.1306"></a>
-<span class="sourceLineNo">1307</span> public long heapSize() {<a name="line.1307"></a>
-<span class="sourceLineNo">1308</span> return this.heapSize.sum();<a name="line.1308"></a>
-<span class="sourceLineNo">1309</span> }<a name="line.1309"></a>
-<span class="sourceLineNo">1310</span><a name="line.1310"></a>
-<span class="sourceLineNo">1311</span> @Override<a name="line.1311"></a>
-<span class="sourceLineNo">1312</span> public long size() {<a name="line.1312"></a>
-<span class="sourceLineNo">1313</span> return this.realCacheSize.sum();<a name="line.1313"></a>
-<span class="sourceLineNo">1314</span> }<a name="line.1314"></a>
-<span class="sourceLineNo">1315</span><a name="line.1315"></a>
-<span class="sourceLineNo">1316</span> @Override<a name="line.1316"></a>
-<span class="sourceLineNo">1317</span> public long getCurrentDataSize() {<a name="line.1317"></a>
-<span class="sourceLineNo">1318</span> return size();<a name="line.1318"></a>
-<span class="sourceLineNo">1319</span> }<a name="line.1319"></a>
-<span class="sourceLineNo">1320</span><a name="line.1320"></a>
-<span class="sourceLineNo">1321</span> @Override<a name="line.1321"></a>
-<span class="sourceLineNo">1322</span> public long getFreeSize() {<a name="line.1322"></a>
-<span class="sourceLineNo">1323</span> return this.bucketAllocator.getFreeSize();<a name="line.1323"></a>
-<span class="sourceLineNo">1324</span> }<a name="line.1324"></a>
-<span class="sourceLineNo">1325</span><a name="line.1325"></a>
-<span class="sourceLineNo">1326</span> @Override<a name="line.1326"></a>
-<span class="sourceLineNo">1327</span> public long getBlockCount() {<a name="line.1327"></a>
-<span class="sourceLineNo">1328</span> return this.blockNumber.sum();<a name="line.1328"></a>
-<span class="sourceLineNo">1329</span> }<a name="line.1329"></a>
-<span class="sourceLineNo">1330</span><a name="line.1330"></a>
-<span class="sourceLineNo">1331</span> @Override<a name="line.1331"></a>
-<span class="sourceLineNo">1332</span> public long getDataBlockCount() {<a name="line.1332"></a>
-<span class="sourceLineNo">1333</span> return getBlockCount();<a name="line.1333"></a>
-<span class="sourceLineNo">1334</span> }<a name="line.1334"></a>
-<span class="sourceLineNo">1335</span><a name="line.1335"></a>
-<span class="sourceLineNo">1336</span> @Override<a name="line.1336"></a>
-<span class="sourceLineNo">1337</span> public long getCurrentSize() {<a name="line.1337"></a>
-<span class="sourceLineNo">1338</span> return this.bucketAllocator.getUsedSize();<a name="line.1338"></a>
-<span class="sourceLineNo">1339</span> }<a name="line.1339"></a>
-<span class="sourceLineNo">1340</span><a name="line.1340"></a>
-<span class="sourceLineNo">1341</span> protected String getAlgorithm() {<a name="line.1341"></a>
-<span class="sourceLineNo">1342</span> return algorithm;<a name="line.1342"></a>
+<span class="sourceLineNo">1306</span> public BucketAllocator getAllocator() {<a name="line.1306"></a>
+<span class="sourceLineNo">1307</span> return this.bucketAllocator;<a name="line.1307"></a>
+<span class="sourceLineNo">1308</span> }<a name="line.1308"></a>
+<span class="sourceLineNo">1309</span><a name="line.1309"></a>
+<span class="sourceLineNo">1310</span> @Override<a name="line.1310"></a>
+<span class="sourceLineNo">1311</span> public long heapSize() {<a name="line.1311"></a>
+<span class="sourceLineNo">1312</span> return this.heapSize.sum();<a name="line.1312"></a>
+<span class="sourceLineNo">1313</span> }<a name="line.1313"></a>
+<span class="sourceLineNo">1314</span><a name="line.1314"></a>
+<span class="sourceLineNo">1315</span> @Override<a name="line.1315"></a>
+<span class="sourceLineNo">1316</span> public long size() {<a name="line.1316"></a>
+<span class="sourceLineNo">1317</span> return this.realCacheSize.sum();<a name="line.1317"></a>
+<span class="sourceLineNo">1318</span> }<a name="line.1318"></a>
+<span class="sourceLineNo">1319</span><a name="line.1319"></a>
+<span class="sourceLineNo">1320</span> @Override<a name="line.1320"></a>
+<span class="sourceLineNo">1321</span> public long getCurrentDataSize() {<a name="line.1321"></a>
+<span class="sourceLineNo">1322</span> return size();<a name="line.1322"></a>
+<span class="sourceLineNo">1323</span> }<a name="line.1323"></a>
+<span class="sourceLineNo">1324</span><a name="line.1324"></a>
+<span class="sourceLineNo">1325</span> @Override<a name="line.1325"></a>
+<span class="sourceLineNo">1326</span> public long getFreeSize() {<a name="line.1326"></a>
+<span class="sourceLineNo">1327</span> return this.bucketAllocator.getFreeSize();<a name="line.1327"></a>
+<span class="sourceLineNo">1328</span> }<a name="line.1328"></a>
+<span class="sourceLineNo">1329</span><a name="line.1329"></a>
+<span class="sourceLineNo">1330</span> @Override<a name="line.1330"></a>
+<span class="sourceLineNo">1331</span> public long getBlockCount() {<a name="line.1331"></a>
+<span class="sourceLineNo">1332</span> return this.blockNumber.sum();<a name="line.1332"></a>
+<span class="sourceLineNo">1333</span> }<a name="line.1333"></a>
+<span class="sourceLineNo">1334</span><a name="line.1334"></a>
+<span class="sourceLineNo">1335</span> @Override<a name="line.1335"></a>
+<span class="sourceLineNo">1336</span> public long getDataBlockCount() {<a name="line.1336"></a>
+<span class="sourceLineNo">1337</span> return getBlockCount();<a name="line.1337"></a>
+<span class="sourceLineNo">1338</span> }<a name="line.1338"></a>
+<span class="sourceLineNo">1339</span><a name="line.1339"></a>
+<span class="sourceLineNo">1340</span> @Override<a name="line.1340"></a>
+<span class="sourceLineNo">1341</span> public long getCurrentSize() {<a name="line.1341"></a>
+<span class="sourceLineNo">1342</span> return this.bucketAllocator.getUsedSize();<a name="line.1342"></a>
<span class="sourceLineNo">1343</span> }<a name="line.1343"></a>
<span class="sourceLineNo">1344</span><a name="line.1344"></a>
-<span class="sourceLineNo">1345</span> /**<a name="line.1345"></a>
-<span class="sourceLineNo">1346</span> * Evicts all blocks for a specific HFile.<a name="line.1346"></a>
-<span class="sourceLineNo">1347</span> * <p><a name="line.1347"></a>
-<span class="sourceLineNo">1348</span> * This is used for evict-on-close to remove all blocks of a specific HFile.<a name="line.1348"></a>
-<span class="sourceLineNo">1349</span> *<a name="line.1349"></a>
-<span class="sourceLineNo">1350</span> * @return the number of blocks evicted<a name="line.1350"></a>
-<span class="sourceLineNo">1351</span> */<a name="line.1351"></a>
-<span class="sourceLineNo">1352</span> @Override<a name="line.1352"></a>
-<span class="sourceLineNo">1353</span> public int evictBlocksByHfileName(String hfileName) {<a name="line.1353"></a>
-<span class="sourceLineNo">1354</span> Set<BlockCacheKey> keySet = blocksByHFile.subSet(<a name="line.1354"></a>
-<span class="sourceLineNo">1355</span> new BlockCacheKey(hfileName, Long.MIN_VALUE), true,<a name="line.1355"></a>
-<span class="sourceLineNo">1356</span> new BlockCacheKey(hfileName, Long.MAX_VALUE), true);<a name="line.1356"></a>
-<span class="sourceLineNo">1357</span><a name="line.1357"></a>
-<span class="sourceLineNo">1358</span> int numEvicted = 0;<a name="line.1358"></a>
-<span class="sourceLineNo">1359</span> for (BlockCacheKey key : keySet) {<a name="line.1359"></a>
-<span class="sourceLineNo">1360</span> if (evictBlock(key)) {<a name="line.1360"></a>
-<span class="sourceLineNo">1361</span> ++numEvicted;<a name="line.1361"></a>
-<span class="sourceLineNo">1362</span> }<a name="line.1362"></a>
-<span class="sourceLineNo">1363</span> }<a name="line.1363"></a>
-<span class="sourceLineNo">1364</span><a name="line.1364"></a>
-<span class="sourceLineNo">1365</span> return numEvicted;<a name="line.1365"></a>
-<span class="sourceLineNo">1366</span> }<a name="line.1366"></a>
-<span class="sourceLineNo">1367</span><a name="line.1367"></a>
-<span class="sourceLineNo">1368</span> /**<a name="line.1368"></a>
-<span class="sourceLineNo">1369</span> * Used to group bucket entries into priority buckets. There will be a<a name="line.1369"></a>
-<span class="sourceLineNo">1370</span> * BucketEntryGroup for each priority (single, multi, memory). Once bucketed,<a name="line.1370"></a>
-<span class="sourceLineNo">1371</span> * the eviction algorithm takes the appropriate number of elements out of each<a name="line.1371"></a>
-<span class="sourceLineNo">1372</span> * according to configuration parameters and their relative sizes.<a name="line.1372"></a>
-<span class="sourceLineNo">1373</span> */<a name="line.1373"></a>
-<span class="sourceLineNo">1374</span> private class BucketEntryGroup {<a name="line.1374"></a>
-<span class="sourceLineNo">1375</span><a name="line.1375"></a>
-<span class="sourceLineNo">1376</span> private CachedEntryQueue queue;<a name="line.1376"></a>
-<span class="sourceLineNo">1377</span> private long totalSize = 0;<a name="line.1377"></a>
-<span class="sourceLineNo">1378</span> private long bucketSize;<a name="line.1378"></a>
+<span class="sourceLineNo">1345</span> protected String getAlgorithm() {<a name="line.1345"></a>
+<span class="sourceLineNo">1346</span> return algorithm;<a name="line.1346"></a>
+<span class="sourceLineNo">1347</span> }<a name="line.1347"></a>
+<span class="sourceLineNo">1348</span><a name="line.1348"></a>
+<span class="sourceLineNo">1349</span> /**<a name="line.1349"></a>
+<span class="sourceLineNo">1350</span> * Evicts all blocks for a specific HFile.<a name="line.1350"></a>
+<span class="sourceLineNo">1351</span> * <p><a name="line.1351"></a>
+<span class="sourceLineNo">1352</span> * This is used for evict-on-close to remove all blocks of a specific HFile.<a name="line.1352"></a>
+<span class="sourceLineNo">1353</span> *<a name="line.1353"></a>
+<span class="sourceLineNo">1354</span> * @return the number of blocks evicted<a name="line.1354"></a>
+<span class="sourceLineNo">1355</span> */<a name="line.1355"></a>
+<span class="sourceLineNo">1356</span> @Override<a name="line.1356"></a>
+<span class="sourceLineNo">1357</span> public int evictBlocksByHfileName(String hfileName) {<a name="line.1357"></a>
+<span class="sourceLineNo">1358</span> Set<BlockCacheKey> keySet = blocksByHFile.subSet(<a name="line.1358"></a>
+<span class="sourceLineNo">1359</span> new BlockCacheKey(hfileName, Long.MIN_VALUE), true,<a name="line.1359"></a>
+<span class="sourceLineNo">1360</span> new BlockCacheKey(hfileName, Long.MAX_VALUE), true);<a name="line.1360"></a>
+<span class="sourceLineNo">1361</span><a name="line.1361"></a>
+<span class="sourceLineNo">1362</span> int numEvicted = 0;<a name="line.1362"></a>
+<span class="sourceLineNo">1363</span> for (BlockCacheKey key : keySet) {<a name="line.1363"></a>
+<span class="sourceLineNo">1364</span> if (evictBlock(key)) {<a name="line.1364"></a>
+<span class="sourceLineNo">1365</span> ++numEvicted;<a name="line.1365"></a>
+<span class="sourceLineNo">1366</span> }<a name="line.1366"></a>
+<span class="sourceLineNo">1367</span> }<a name="line.1367"></a>
+<span class="sourceLineNo">1368</span><a name="line.1368"></a>
+<span class="sourceLineNo">1369</span> return numEvicted;<a name="line.1369"></a>
+<span class="sourceLineNo">1370</span> }<a name="line.1370"></a>
+<span class="sourceLineNo">1371</span><a name="line.1371"></a>
+<span class="sourceLineNo">1372</span> /**<a name="line.1372"></a>
+<span class="sourceLineNo">1373</span> * Used to group bucket entries into priority buckets. There will be a<a name="line.1373"></a>
+<span class="sourceLineNo">1374</span> * BucketEntryGroup for each priority (single, multi, memory). Once bucketed,<a name="line.1374"></a>
+<span class="sourceLineNo">1375</span> * the eviction algorithm takes the appropriate number of elements out of each<a name="line.1375"></a>
+<span class="sourceLineNo">1376</span> * according to configuration parameters and their relative sizes.<a name="line.1376"></a>
+<span class="sourceLineNo">1377</span> */<a name="line.1377"></a>
+<span class="sourceLineNo">1378</span> private class BucketEntryGroup {<a name="line.1378"></a>
<span class="sourceLineNo">1379</span><a name="line.1379"></a>
-<span class="sourceLineNo">1380</span> public BucketEntryGroup(long bytesToFree, long blockSize, long bucketSize) {<a name="line.1380"></a>
-<span class="sourceLineNo">1381</span> this.bucketSize = bucketSize;<a name="line.1381"></a>
-<span class="sourceLineNo">1382</span> queue = new CachedEntryQueue(bytesToFree, blockSize);<a name="line.1382"></a>
-<span class="sourceLineNo">1383</span> totalSize = 0;<a name="line.1383"></a>
-<span class="sourceLineNo">1384</span> }<a name="line.1384"></a>
-<span class="sourceLineNo">1385</span><a name="line.1385"></a>
-<span class="sourceLineNo">1386</span> public void add(Map.Entry<BlockCacheKey, BucketEntry> block) {<a name="line.1386"></a>
-<span class="sourceLineNo">1387</span> totalSize += block.getValue().getLength();<a name="line.1387"></a>
-<span class="sourceLineNo">1388</span> queue.add(block);<a name="line.1388"></a>
-<span class="sourceLineNo">1389</span> }<a name="line.1389"></a>
-<span class="sourceLineNo">1390</span><a name="line.1390"></a>
-<span class="sourceLineNo">1391</span> public long free(long toFree) {<a name="line.1391"></a>
-<span class="sourceLineNo">1392</span> Map.Entry<BlockCacheKey, BucketEntry> entry;<a name="line.1392"></a>
-<span class="sourceLineNo">1393</span> long freedBytes = 0;<a name="line.1393"></a>
-<span class="sourceLineNo">1394</span> // TODO avoid a cycling siutation. We find no block which is not in use and so no way to free<a name="line.1394"></a>
-<span class="sourceLineNo">1395</span> // What to do then? Caching attempt fail? Need some changes in cacheBlock API?<a name="line.1395"></a>
-<span class="sourceLineNo">1396</span> while ((entry = queue.pollLast()) != null) {<a name="line.1396"></a>
-<span class="sourceLineNo">1397</span> BlockCacheKey blockCacheKey = entry.getKey();<a name="line.1397"></a>
-<span class="sourceLineNo">1398</span> BucketEntry be = entry.getValue();<a name="line.1398"></a>
-<span class="sourceLineNo">1399</span> if (evictBucketEntryIfNoRpcReferenced(blockCacheKey, be)) {<a name="line.1399"></a>
-<span class="sourceLineNo">1400</span> freedBytes += be.getLength();<a name="line.1400"></a>
-<span class="sourceLineNo">1401</span> }<a name="line.1401"></a>
-<span class="sourceLineNo">1402</span> if (freedBytes >= toFree) {<a name="line.1402"></a>
-<span class="sourceLineNo">1403</span> return freedBytes;<a name="line.1403"></a>
-<span class="sourceLineNo">1404</span> }<a name="line.1404"></a>
-<span class="sourceLineNo">1405</span> }<a name="line.1405"></a>
-<span class="sourceLineNo">1406</span> return freedBytes;<a name="line.1406"></a>
-<span class="sourceLineNo">1407</span> }<a name="line.1407"></a>
-<span class="sourceLineNo">1408</span><a name="line.1408"></a>
-<span class="sourceLineNo">1409</span> public long overflow() {<a name="line.1409"></a>
-<span class="sourceLineNo">1410</span> return totalSize - bucketSize;<a name="line.1410"></a>
+<span class="sourceLineNo">1380</span> private CachedEntryQueue queue;<a name="line.1380"></a>
+<span class="sourceLineNo">1381</span> private long totalSize = 0;<a name="line.1381"></a>
+<span class="sourceLineNo">1382</span> private long bucketSize;<a name="line.1382"></a>
+<span class="sourceLineNo">1383</span><a name="line.1383"></a>
+<span class="sourceLineNo">1384</span> public BucketEntryGroup(long bytesToFree, long blockSize, long bucketSize) {<a name="line.1384"></a>
+<span class="sourceLineNo">1385</span> this.bucketSize = bucketSize;<a name="line.1385"></a>
+<span class="sourceLineNo">1386</span> queue = new CachedEntryQueue(bytesToFree, blockSize);<a name="line.1386"></a>
+<span class="sourceLineNo">1387</span> totalSize = 0;<a name="line.1387"></a>
+<span class="sourceLineNo">1388</span> }<a name="line.1388"></a>
+<span class="sourceLineNo">1389</span><a name="line.1389"></a>
+<span class="sourceLineNo">1390</span> public void add(Map.Entry<BlockCacheKey, BucketEntry> block) {<a name="line.1390"></a>
+<span class="sourceLineNo">1391</span> totalSize += block.getValue().getLength();<a name="line.1391"></a>
+<span class="sourceLineNo">1392</span> queue.add(block);<a name="line.1392"></a>
+<span class="sourceLineNo">1393</span> }<a name="line.1393"></a>
+<span class="sourceLineNo">1394</span><a name="line.1394"></a>
+<span class="sourceLineNo">1395</span> public long free(long toFree) {<a name="line.1395"></a>
+<span class="sourceLineNo">1396</span> Map.Entry<BlockCacheKey, BucketEntry> entry;<a name="line.1396"></a>
+<span class="sourceLineNo">1397</span> long freedBytes = 0;<a name="line.1397"></a>
+<span class="sourceLineNo">1398</span> // TODO avoid a cycling siutation. We find no block which is not in use and so no way to free<a name="line.1398"></a>
+<span class="sourceLineNo">1399</span> // What to do then? Caching attempt fail? Need some changes in cacheBlock API?<a name="line.1399"></a>
+<span class="sourceLineNo">1400</span> while ((entry = queue.pollLast()) != null) {<a name="line.1400"></a>
+<span class="sourceLineNo">1401</span> BlockCacheKey blockCacheKey = entry.getKey();<a name="line.1401"></a>
+<span class="sourceLineNo">1402</span> BucketEntry be = entry.getValue();<a name="line.1402"></a>
+<span class="sourceLineNo">1403</span> if (evictBucketEntryIfNoRpcReferenced(blockCacheKey, be)) {<a name="line.1403"></a>
+<span class="sourceLineNo">1404</span> freedBytes += be.getLength();<a name="line.1404"></a>
+<span class="sourceLineNo">1405</span> }<a name="line.1405"></a>
+<span class="sourceLineNo">1406</span> if (freedBytes >= toFree) {<a name="line.1406"></a>
+<span class="sourceLineNo">1407</span> return freedBytes;<a name="line.1407"></a>
+<span class="sourceLineNo">1408</span> }<a name="line.1408"></a>
+<span class="sourceLineNo">1409</span> }<a name="line.1409"></a>
+<span class="sourceLineNo">1410</span> return freedBytes;<a name="line.1410"></a>
<span class="sourceLineNo">1411</span> }<a name="line.1411"></a>
<span class="sourceLineNo">1412</span><a name="line.1412"></a>
-<span class="sourceLineNo">1413</span> public long totalSize() {<a name="line.1413"></a>
-<span class="sourceLineNo">1414</span> return totalSize;<a name="line.1414"></a>
+<span class="sourceLineNo">1413</span> public long overflow() {<a name="line.1413"></a>
+<span class="sourceLineNo">1414</span> return totalSize - bucketSize;<a name="line.1414"></a>
<span class="sourceLineNo">1415</span> }<a name="line.1415"></a>
-<span class="sourceLineNo">1416</span> }<a name="line.1416"></a>
-<span class="sourceLineNo">1417</span><a name="line.1417"></a>
-<span class="sourceLineNo">1418</span> /**<a name="line.1418"></a>
-<span class="sourceLineNo">1419</span> * Block Entry stored in the memory with key,data and so on<a name="line.1419"></a>
-<span class="sourceLineNo">1420</span> */<a name="line.1420"></a>
-<span class="sourceLineNo">1421</span> static class RAMQueueEntry {<a name="line.1421"></a>
-<span class="sourceLineNo">1422</span> private final BlockCacheKey key;<a name="line.1422"></a>
-<span class="sourceLineNo">1423</span> private final Cacheable data;<a name="line.1423"></a>
-<span class="sourceLineNo">1424</span> private long accessCounter;<a name="line.1424"></a>
-<span class="sourceLineNo">1425</span> private boolean inMemory;<a name="line.1425"></a>
-<span class="sourceLineNo">1426</span><a name="line.1426"></a>
-<span class="sourceLineNo">1427</span> RAMQueueEntry(BlockCacheKey bck, Cacheable data, long accessCounter, boolean inMemory) {<a name="line.1427"></a>
-<span class="sourceLineNo">1428</span> this.key = bck;<a name="line.1428"></a>
-<span class="sourceLineNo">1429</span> this.data = data;<a name="line.1429"></a>
-<span class="sourceLineNo">1430</span> this.accessCounter = accessCounter;<a name="line.1430"></a>
-<span class="sourceLineNo">1431</span> this.inMemory = inMemory;<a name="line.1431"></a>
-<span class="sourceLineNo">1432</span> }<a name="line.1432"></a>
-<span class="sourceLineNo">1433</span><a name="line.1433"></a>
-<span class="sourceLineNo">1434</span> public Cacheable getData() {<a name="line.1434"></a>
-<span class="sourceLineNo">1435</span> return data;<a name="line.1435"></a>
+<span class="sourceLineNo">1416</span><a name="line.1416"></a>
+<span class="sourceLineNo">1417</span> public long totalSize() {<a name="line.1417"></a>
+<span class="sourceLineNo">1418</span> return totalSize;<a name="line.1418"></a>
+<span class="sourceLineNo">1419</span> }<a name="line.1419"></a>
+<span class="sourceLineNo">1420</span> }<a name="line.1420"></a>
+<span class="sourceLineNo">1421</span><a name="line.1421"></a>
+<span class="sourceLineNo">1422</span> /**<a name="line.1422"></a>
+<span class="sourceLineNo">1423</span> * Block Entry stored in the memory with key,data and so on<a name="line.1423"></a>
+<span class="sourceLineNo">1424</span> */<a name="line.1424"></a>
+<span class="sourceLineNo">1425</span> static class RAMQueueEntry {<a name="line.1425"></a>
+<span class="sourceLineNo">1426</span> private final BlockCacheKey key;<a name="line.1426"></a>
+<span class="sourceLineNo">1427</span> private final Cacheable data;<a name="line.1427"></a>
+<span class="sourceLineNo">1428</span> private long accessCounter;<a name="line.1428"></a>
+<span class="sourceLineNo">1429</span> private boolean inMemory;<a name="line.1429"></a>
+<span class="sourceLineNo">1430</span><a name="line.1430"></a>
+<span class="sourceLineNo">1431</span> RAMQueueEntry(BlockCacheKey bck, Cacheable data, long accessCounter, boolean inMemory) {<a name="line.1431"></a>
+<span class="sourceLineNo">1432</span> this.key = bck;<a name="line.1432"></a>
+<span class="sourceLineNo">1433</span> this.data = data;<a name="line.1433"></a>
+<span class="sourceLineNo">1434</span> this.accessCounter = accessCounter;<a name="line.1434"></a>
+<span class="sourceLineNo">1435</span> this.inMemory = inMemory;<a name="line.1435"></a>
<span class="sourceLineNo">1436</span> }<a name="line.1436"></a>
<span class="sourceLineNo">1437</span><a name="line.1437"></a>
-<span class="sourceLineNo">1438</span> public BlockCacheKey getKey() {<a name="line.1438"></a>
-<span class="sourceLineNo">1439</span> return key;<a name="line.1439"></a>
+<span class="sourceLineNo">1438</span> public Cacheable getData() {<a name="line.1438"></a>
+<span class="sourceLineNo">1439</span> return data;<a name="line.1439"></a>
<span class="sourceLineNo">1440</span> }<a name="line.1440"></a>
<span class="sourceLineNo">1441</span><a name="line.1441"></a>
-<span class="sourceLineNo">1442</span> public void access(long accessCounter) {<a name="line.1442"></a>
-<span class="sourceLineNo">1443</span> this.accessCounter = accessCounter;<a name="line.1443"></a>
+<span class="sourceLineNo">1442</span> public BlockCacheKey getKey() {<a name="line.1442"></a>
+<span class="sourceLineNo">1443</span> return key;<a name="line.1443"></a>
<span class="sourceLineNo">1444</span> }<a name="line.1444"></a>
<span class="sourceLineNo">1445</span><a name="line.1445"></a>
-<span class="sourceLineNo">1446</span> private ByteBuffAllocator getByteBuffAllocator() {<a name="line.1446"></a>
-<span class="sourceLineNo">1447</span> if (data instanceof HFileBlock) {<a name="line.1447"></a>
-<span class="sourceLineNo">1448</span> return ((HFileBlock) data).getByteBuffAllocator();<a name="line.1448"></a>
-<span class="sourceLineNo">1449</span> }<a name="line.1449"></a>
-<span class="sourceLineNo">1450</span> return ByteBuffAllocator.HEAP;<a name="line.1450"></a>
-<span class="sourceLineNo">1451</span> }<a name="line.1451"></a>
-<span class="sourceLineNo">1452</span><a name="line.1452"></a>
-<span class="sourceLineNo">1453</span> public BucketEntry writeToCache(final IOEngine ioEngine, final BucketAllocator alloc,<a name="line.1453"></a>
-<span class="sourceLineNo">1454</span> final LongAdder realCacheSize, Function<BucketEntry, Recycler> createRecycler)<a name="line.1454"></a>
-<span class="sourceLineNo">1455</span> throws IOException {<a name="line.1455"></a>
-<span class="sourceLineNo">1456</span> int len = data.getSerializedLength();<a name="line.1456"></a>
-<span class="sourceLineNo">1457</span> // This cacheable thing can't be serialized<a name="line.1457"></a>
-<span class="sourceLineNo">1458</span> if (len == 0) {<a name="line.1458"></a>
-<span class="sourceLineNo">1459</span> return null;<a name="line.1459"></a>
-<span class="sourceLineNo">1460</span> }<a name="line.1460"></a>
-<span class="sourceLineNo">1461</span> long offset = alloc.allocateBlock(len);<a name="line.1461"></a>
-<span class="sourceLineNo">1462</span> boolean succ = false;<a name="line.1462"></a>
-<span class="sourceLineNo">1463</span> BucketEntry bucketEntry = null;<a name="line.1463"></a>
-<span class="sourceLineNo">1464</span> try {<a name="line.1464"></a>
-<span class="sourceLineNo">1465</span> bucketEntry = new BucketEntry(offset, len, accessCounter, inMemory, createRecycler,<a name="line.1465"></a>
-<span class="sourceLineNo">1466</span> getByteBuffAllocator());<a name="line.1466"></a>
-<span class="sourceLineNo">1467</span> bucketEntry.setDeserializerReference(data.getDeserializer());<a name="line.1467"></a>
-<span class="sourceLineNo">1468</span> if (data instanceof HFileBlock) {<a name="line.1468"></a>
-<span class="sourceLineNo">1469</span> // If an instance of HFileBlock, save on some allocations.<a name="line.1469"></a>
-<span class="sourceLineNo">1470</span> HFileBlock block = (HFileBlock) data;<a name="line.1470"></a>
-<span class="sourceLineNo">1471</span> ByteBuff sliceBuf = block.getBufferReadOnly();<a name="line.1471"></a>
-<span class="sourceLineNo">1472</span> ByteBuffer metadata = block.getMetaData();<a name="line.1472"></a>
-<span class="sourceLineNo">1473</span> ioEngine.write(sliceBuf, offset);<a name="line.1473"></a>
-<span class="sourceLineNo">1474</span> ioEngine.write(metadata, offset + len - metadata.limit());<a name="line.1474"></a>
-<span class="sourceLineNo">1475</span> } else {<a name="line.1475"></a>
-<span class="sourceLineNo">1476</span> // Only used for testing.<a name="line.1476"></a>
-<span class="sourceLineNo">1477</span> ByteBuffer bb = ByteBuffer.allocate(len);<a name="line.1477"></a>
-<span class="sourceLineNo">1478</span> data.serialize(bb, true);<a name="line.1478"></a>
-<span class="sourceLineNo">1479</span> ioEngine.write(bb, offset);<a name="line.1479"></a>
-<span class="sourceLineNo">1480</span> }<a name="line.1480"></a>
-<span class="sourceLineNo">1481</span> succ = true;<a name="line.1481"></a>
-<span class="sourceLineNo">1482</span> } finally {<a name="line.1482"></a>
-<span class="sourceLineNo">1483</span> if (!succ) {<a name="line.1483"></a>
-<span class="sourceLineNo">1484</span> alloc.freeBlock(offset);<a name="line.1484"></a>
-<span class="sourceLineNo">1485</span> }<a name="line.1485"></a>
-<span class="sourceLineNo">1486</span> }<a name="line.1486"></a>
-<span class="sourceLineNo">1487</span> realCacheSize.add(len);<a name="line.1487"></a>
-<span class="sourceLineNo">1488</span> return bucketEntry;<a name="line.1488"></a>
-<span class="sourceLineNo">1489</span> }<a name="line.1489"></a>
-<span class="sourceLineNo">1490</span> }<a name="line.1490"></a>
-<span class="sourceLineNo">1491</span><a name="line.1491"></a>
-<span class="sourceLineNo">1492</span> /**<a name="line.1492"></a>
-<span class="sourceLineNo">1493</span> * Only used in test<a name="line.1493"></a>
-<span class="sourceLineNo">1494</span> * @throws InterruptedException<a name="line.1494"></a>
-<span class="sourceLineNo">1495</span> */<a name="line.1495"></a>
-<span class="sourceLineNo">1496</span> void stopWriterThreads() throws InterruptedException {<a name="line.1496"></a>
-<span class="sourceLineNo">1497</span> for (WriterThread writerThread : writerThreads) {<a name="line.1497"></a>
-<span class="sourceLineNo">1498</span> writerThread.disableWriter();<a name="line.1498"></a>
-<span class="sourceLineNo">1499</span> writerThread.interrupt();<a name="line.1499"></a>
-<span class="sourceLineNo">1500</span> writerThread.join();<a name="line.1500"></a>
-<span class="sourceLineNo">1501</span> }<a name="line.1501"></a>
-<span class="sourceLineNo">1502</span> }<a name="line.1502"></a>
-<span class="sourceLineNo">1503</span><a name="line.1503"></a>
-<span class="sourceLineNo">1504</span> @Override<a name="line.1504"></a>
-<span class="sourceLineNo">1505</span> public Iterator<CachedBlock> iterator() {<a name="line.1505"></a>
-<span class="sourceLineNo">1506</span> // Don't bother with ramcache since stuff is in here only a little while.<a name="line.1506"></a>
-<span class="sourceLineNo">1507</span> final Iterator<Map.Entry<BlockCacheKey, BucketEntry>> i =<a name="line.1507"></a>
-<span class="sourceLineNo">1508</span> this.backingMap.entrySet().iterator();<a name="line.1508"></a>
-<span class="sourceLineNo">1509</span> return new Iterator<CachedBlock>() {<a name="line.1509"></a>
-<span class="sourceLineNo">1510</span> private final long now = System.nanoTime();<a name="line.1510"></a>
-<span class="sourceLineNo">1511</span><a name="line.1511"></a>
-<span class="sourceLineNo">1512</span> @Override<a name="line.1512"></a>
-<span class="sourceLineNo">1513</span> public boolean hasNext() {<a name="line.1513"></a>
-<span class="sourceLineNo">1514</span> return i.hasNext();<a name="line.1514"></a>
-<span class="sourceLineNo">1515</span> }<a name="line.1515"></a>
-<span class="sourceLineNo">1516</span><a name="line.1516"></a>
-<span class="sourceLineNo">1517</span> @Override<a name="line.1517"></a>
-<span class="sourceLineNo">1518</span> public CachedBlock next() {<a name="line.1518"></a>
-<span class="sourceLineNo">1519</span> final Map.Entry<BlockCacheKey, BucketEntry> e = i.next();<a name="line.1519"></a>
-<span class="sourceLineNo">1520</span> return new CachedBlock() {<a name="line.1520"></a>
-<span class="sourceLineNo">1521</span> @Override<a name="line.1521"></a>
-<span class="sourceLineNo">1522</span> public String toString() {<a name="line.1522"></a>
-<span class="sourceLineNo">1523</span> return BlockCacheUtil.toString(this, now);<a name="line.1523"></a>
-<span class="sourceLineNo">1524</span> }<a name="line.1524"></a>
-<span class="sourceLineNo">1525</span><a name="line.1525"></a>
-<span class="sourceLineNo">1526</span> @Override<a name="line.1526"></a>
-<span class="sourceLineNo">1527</span> public BlockPriority getBlockPriority() {<a name="line.1527"></a>
-<span class="sourceLineNo">1528</span> return e.getValue().getPriority();<a name="line.1528"></a>
-<span class="sourceLineNo">1529</span> }<a name="line.1529"></a>
-<span class="sourceLineNo">1530</span><a name="line.1530"></a>
-<span class="sourceLineNo">1531</span> @Override<a name="line.1531"></a>
-<span class="sourceLineNo">1532</span> public BlockType getBlockType() {<a name="line.1532"></a>
-<span class="sourceLineNo">1533</span> // Not held by BucketEntry. Could add it if wanted on BucketEntry creation.<a name="line.1533"></a>
-<span class="sourceLineNo">1534</span> return null;<a name="line.1534"></a>
-<span class="sourceLineNo">1535</span> }<a name="line.1535"></a>
-<span class="sourceLineNo">1536</span><a name="line.1536"></a>
-<span class="sourceLineNo">1537</span> @Override<a name="line.1537"></a>
-<span class="sourceLineNo">1538</span> public long getOffset() {<a name="line.1538"></a>
-<span class="sourceLineNo">1539</span> return e.getKey().getOffset();<a name="line.1539"></a>
-<span class="sourceLineNo">1540</span> }<a name="line.1540"></a>
-<span class="sourceLineNo">1541</span><a name="line.1541"></a>
-<span class="sourceLineNo">1542</span> @Override<a name="line.1542"></a>
-<span class="sourceLineNo">1543</span> public long getSize() {<a name="line.1543"></a>
-<span class="sourceLineNo">1544</span> return e.getValue().getLength();<a name="line.1544"></a>
-<span class="sourceLineNo">1545</span> }<a name="line.1545"></a>
-<span class="sourceLineNo">1546</span><a name="line.1546"></a>
-<span class="sourceLineNo">1547</span> @Override<a name="line.1547"></a>
-<span class="sourceLineNo">1548</span> public long getCachedTime() {<a name="line.1548"></a>
-<span class="sourceLineNo">1549</span> return e.getValue().getCachedTime();<a name="line.1549"></a>
-<span class="sourceLineNo">1550</span> }<a name="line.1550"></a>
-<span class="sourceLineNo">1551</span><a name="line.1551"></a>
-<span class="sourceLineNo">1552</span> @Override<a name="line.1552"></a>
-<span class="sourceLineNo">1553</span> public String getFilename() {<a name="line.1553"></a>
-<span class="sourceLineNo">1554</span> return e.getKey().getHfileName();<a name="line.1554"></a>
-<span class="sourceLineNo">1555</span> }<a name="line.1555"></a>
-<span class="sourceLineNo">1556</span><a name="line.1556"></a>
-<span class="sourceLineNo">1557</span> @Override<a name="line.1557"></a>
-<span class="sourceLineNo">1558</span> public int compareTo(CachedBlock other) {<a name="line.1558"></a>
-<span class="sourceLineNo">1559</span> int diff = this.getFilename().compareTo(other.getFilename());<a name="line.1559"></a>
-<span class="sourceLineNo">1560</span> if (diff != 0) return diff;<a name="line.1560"></a>
-<span class="sourceLineNo">1561</span><a name="line.1561"></a>
-<span class="sourceLineNo">1562</span> diff = Long.compare(this.getOffset(), other.getOffset());<a name="line.1562"></a>
-<span class="sourceLineNo">1563</span> if (diff != 0) return diff;<a name="line.1563"></a>
-<span class="sourceLineNo">1564</span> if (other.getCachedTime() < 0 || this.getCachedTime() < 0) {<a name="line.1564"></a>
-<span class="sourceLineNo">1565</span> throw new IllegalStateException("" + this.getCachedTime() + ", " +<a name="line.1565"></a>
-<span class="sourceLineNo">1566</span> other.getCachedTime());<a name="line.1566"></a>
-<span class="sourceLineNo">1567</span> }<a name="line.1567"></a>
-<span class="sourceLineNo">1568</span> return Long.compare(other.getCachedTime(), this.getCachedTime());<a name="line.1568"></a>
-<span class="sourceLineNo">1569</span> }<a name="line.1569"></a>
-<span class="sourceLineNo">1570</span><a name="line.1570"></a>
-<span class="sourceLineNo">1571</span> @Override<a name="line.1571"></a>
-<span class="sourceLineNo">1572</span> public int hashCode() {<a name="line.1572"></a>
-<span class="sourceLineNo">1573</span> return e.getKey().hashCode();<a name="line.1573"></a>
-<span class="sourceLineNo">1574</span> }<a name="line.1574"></a>
-<span class="sourceLineNo">1575</span><a name="line.1575"></a>
-<span class="sourceLineNo">1576</span> @Override<a name="line.1576"></a>
-<span class="sourceLineNo">1577</span> public boolean equals(Object obj) {<a name="line.1577"></a>
-<span class="sourceLineNo">1578</span> if (obj instanceof CachedBlock) {<a name="line.1578"></a>
-<span class="sourceLineNo">1579</span> CachedBlock cb = (CachedBlock)obj;<a name="line.1579"></a>
-<span class="sourceLineNo">1580</span> return compareTo(cb) == 0;<a name="line.1580"></a>
-<span class="sourceLineNo">1581</span> } else {<a name="line.1581"></a>
-<span class="sourceLineNo">1582</span> return false;<a name="line.1582"></a>
-<span class="sourceLineNo">1583</span> }<a name="line.1583"></a>
-<span class="sourceLineNo">1584</span> }<a name="line.1584"></a>
-<span class="sourceLineNo">1585</span> };<a name="line.1585"></a>
-<span class="sourceLineNo">1586</span> }<a name="line.1586"></a>
-<span class="sourceLineNo">1587</span><a name="line.1587"></a>
-<span class="sourceLineNo">1588</span> @Override<a name="line.1588"></a>
-<span class="sourceLineNo">1589</span> public void remove() {<a name="line.1589"></a>
-<span class="sourceLineNo">1590</span> throw new UnsupportedOperationException();<a name="line.1590"></a>
-<span class="sourceLineNo">1591</span> }<a name="line.1591"></a>
-<span class="sourceLineNo">1592</span> };<a name="line.1592"></a>
-<span class="sourceLineNo">1593</span> }<a name="line.1593"></a>
-<span class="sourceLineNo">1594</span><a name="line.1594"></a>
-<span class="sourceLineNo">1595</span> @Override<a name="line.1595"></a>
-<span class="sourceLineNo">1596</span> public BlockCache[] getBlockCaches() {<a name="line.1596"></a>
-<span class="sourceLineNo">1597</span> return null;<a name="line.1597"></a>
-<span class="sourceLineNo">1598</span> }<a name="line.1598"></a>
-<span class="sourceLineNo">1599</span><a name="line.1599"></a>
-<span class="sourceLineNo">1600</span> public int getRpcRefCount(BlockCacheKey cacheKey) {<a name="line.1600"></a>
-<span class="sourceLineNo">1601</span> BucketEntry bucketEntry = backingMap.get(cacheKey);<a name="line.1601"></a>
-<span class="sourceLineNo">1602</span> if (bucketEntry != null) {<a name="line.1602"></a>
-<span class="sourceLineNo">1603</span> return bucketEntry.refCnt() - (bucketEntry.markedAsEvicted.get() ? 0 : 1);<a name="line.1603"></a>
-<span class="sourceLineNo">1604</span> }<a name="line.1604"></a>
-<span class="sourceLineNo">1605</span> return 0;<a name="line.1605"></a>
-<span class="sourceLineNo">1606</span> }<a name="line.1606"></a>
-<span class="sourceLineNo">1607</span><a name="line.1607"></a>
-<span class="sourceLineNo">1608</span> float getAcceptableFactor() {<a name="line.1608"></a>
-<span class="sourceLineNo">1609</span> return acceptableFactor;<a name="line.1609"></a>
+<span class="sourceLineNo">1446</span> public void access(long accessCounter) {<a name="line.1446"></a>
+<span class="sourceLineNo">1447</span> this.accessCounter = accessCounter;<a name="line.1447"></a>
+<span class="sourceLineNo">1448</span> }<a name="line.1448"></a>
+<span class="sourceLineNo">1449</span><a name="line.1449"></a>
+<span class="sourceLineNo">1450</span> private ByteBuffAllocator getByteBuffAllocator() {<a name="line.1450"></a>
+<span class="sourceLineNo">1451</span> if (data instanceof HFileBlock) {<a name="line.1451"></a>
+<span class="sourceLineNo">1452</span> return ((HFileBlock) data).getByteBuffAllocator();<a name="line.1452"></a>
+<span class="sourceLineNo">1453</span> }<a name="line.1453"></a>
+<span class="sourceLineNo">1454</span> return ByteBuffAllocator.HEAP;<a name="line.1454"></a>
+<span class="sourceLineNo">1455</span> }<a name="line.1455"></a>
+<span class="sourceLineNo">1456</span><a name="line.1456"></a>
+<span class="sourceLineNo">1457</span> public BucketEntry writeToCache(final IOEngine ioEngine, final BucketAllocator alloc,<a name="line.1457"></a>
+<span class="sourceLineNo">1458</span> final LongAdder realCacheSize, Function<BucketEntry, Recycler> createRecycler)<a name="line.1458"></a>
+<span class="sourceLineNo">1459</span> throws IOException {<a name="line.1459"></a>
+<span class="sourceLineNo">1460</span> int len = data.getSerializedLength();<a name="line.1460"></a>
+<span class="sourceLineNo">1461</span> // This cacheable thing can't be serialized<a name="line.1461"></a>
+<span class="sourceLineNo">1462</span> if (len == 0) {<a name="line.1462"></a>
+<span class="sourceLineNo">1463</span> return null;<a name="line.1463"></a>
+<span class="sourceLineNo">1464</span> }<a name="line.1464"></a>
+<span class="sourceLineNo">1465</span> long offset = alloc.allocateBlock(len);<a name="line.1465"></a>
+<span class="sourceLineNo">1466</span> boolean succ = false;<a name="line.1466"></a>
+<span class="sourceLineNo">1467</span> BucketEntry bucketEntry = null;<a name="line.1467"></a>
+<span class="sourceLineNo">1468</span> try {<a name="line.1468"></a>
+<span class="sourceLineNo">1469</span> bucketEntry = new BucketEntry(offset, len, accessCounter, inMemory, createRecycler,<a name="line.1469"></a>
+<span class="sourceLineNo">1470</span> getByteBuffAllocator());<a name="line.1470"></a>
+<span class="sourceLineNo">1471</span> bucketEntry.setDeserializerReference(data.getDeserializer());<a name="line.1471"></a>
+<span class="sourceLineNo">1472</span> if (data instanceof HFileBlock) {<a name="line.1472"></a>
+<span class="sourceLineNo">1473</span> // If an instance of HFileBlock, save on some allocations.<a name="line.1473"></a>
+<span class="sourceLineNo">1474</span> HFileBlock block = (HFileBlock) data;<a name="line.1474"></a>
+<span class="sourceLineNo">1475</span> ByteBuff sliceBuf = block.getBufferReadOnly();<a name="line.1475"></a>
+<span class="sourceLineNo">1476</span> ByteBuffer metadata = block.getMetaData();<a name="line.1476"></a>
+<span class="sourceLineNo">1477</span> ioEngine.write(sliceBuf, offset);<a name="line.1477"></a>
+<span class="sourceLineNo">1478</span> ioEngine.write(metadata, offset + len - metadata.limit());<a name="line.1478"></a>
+<span class="sourceLineNo">1479</span> } else {<a name="line.1479"></a>
+<span class="sourceLineNo">1480</span> // Only used for testing.<a name="line.1480"></a>
+<span class="sourceLineNo">1481</span> ByteBuffer bb = ByteBuffer.allocate(len);<a name="line.1481"></a>
+<span class="sourceLineNo">1482</span> data.serialize(bb, true);<a name="line.1482"></a>
+<span class="sourceLineNo">1483</span> ioEngine.write(bb, offset);<a name="line.1483"></a>
+<span class="sourceLineNo">1484</span> }<a name="line.1484"></a>
+<span class="sourceLineNo">1485</span> succ = true;<a name="line.1485"></a>
+<span class="sourceLineNo">1486</span> } finally {<a name="line.1486"></a>
+<span class="sourceLineNo">1487</span> if (!succ) {<a name="line.1487"></a>
+<span class="sourceLineNo">1488</span> alloc.freeBlock(offset);<a name="line.1488"></a>
+<span class="sourceLineNo">1489</span> }<a name="line.1489"></a>
+<span class="sourceLineNo">1490</span> }<a name="line.1490"></a>
+<span class="sourceLineNo">1491</span> realCacheSize.add(len);<a name="line.1491"></a>
+<span class="sourceLineNo">1492</span> return bucketEntry;<a name="line.1492"></a>
+<span class="sourceLineNo">1493</span> }<a name="line.1493"></a>
+<span class="sourceLineNo">1494</span> }<a name="line.1494"></a>
+<span class="sourceLineNo">1495</span><a name="line.1495"></a>
+<span class="sourceLineNo">1496</span> /**<a name="line.1496"></a>
+<span class="sourceLineNo">1497</span> * Only used in test<a name="line.1497"></a>
+<span class="sourceLineNo">1498</span> * @throws InterruptedException<a name="line.1498"></a>
+<span class="sourceLineNo">1499</span> */<a name="line.1499"></a>
+<span class="sourceLineNo">1500</span> void stopWriterThreads() throws InterruptedException {<a name="line.1500"></a>
+<span class="sourceLineNo">1501</span> for (WriterThread writerThread : writerThreads) {<a name="line.1501"></a>
+<span class="sourceLineNo">1502</span> writerThread.disableWriter();<a name="line.1502"></a>
+<span class="sourceLineNo">1503</span> writerThread.interrupt();<a name="line.1503"></a>
+<span class="sourceLineNo">1504</span> writerThread.join();<a name="line.1504"></a>
+<span class="sourceLineNo">1505</span> }<a name="line.1505"></a>
+<span class="sourceLineNo">1506</span> }<a name="line.1506"></a>
+<span class="sourceLineNo">1507</span><a name="line.1507"></a>
+<span class="sourceLineNo">1508</span> @Override<a name="line.1508"></a>
+<span class="sourceLineNo">1509</span> public Iterator<CachedBlock> iterator() {<a name="line.1509"></a>
+<span class="sourceLineNo">1510</span> // Don't bother with ramcache since stuff is in here only a little while.<a name="line.1510"></a>
+<span class="sourceLineNo">1511</span> final Iterator<Map.Entry<BlockCacheKey, BucketEntry>> i =<a name="line.1511"></a>
+<span class="sourceLineNo">1512</span> this.backingMap.entrySet().iterator();<a name="line.1512"></a>
+<span class="sourceLineNo">1513</span> return new Iterator<CachedBlock>() {<a name="line.1513"></a>
+<span class="sourceLineNo">1514</span> private final long now = System.nanoTime();<a name="line.1514"></a>
+<span class="sourceLineNo">1515</span><a name="line.1515"></a>
+<span class="sourceLineNo">1516</span> @Override<a name="line.1516"></a>
+<span class="sourceLineNo">1517</span> public boolean hasNext() {<a name="line.1517"></a>
+<span class="sourceLineNo">1518</span> return i.hasNext();<a name="line.1518"></a>
+<span class="sourceLineNo">1519</span> }<a name="line.1519"></a>
+<span class="sourceLineNo">1520</span><a name="line.1520"></a>
+<span class="sourceLineNo">1521</span> @Override<a name="line.1521"></a>
+<span class="sourceLineNo">1522</span> public CachedBlock next() {<a name="line.1522"></a>
+<span class="sourceLineNo">1523</span> final Map.Entry<BlockCacheKey, BucketEntry> e = i.next();<a name="line.1523"></a>
+<span class="sourceLineNo">1524</span> return new CachedBlock() {<a name="line.1524"></a>
+<span class="sourceLineNo">1525</span> @Override<a name="line.1525"></a>
+<span class="sourceLineNo">1526</span> public String toString() {<a name="line.1526"></a>
+<span class="sourceLineNo">1527</span> return BlockCacheUtil.toString(this, now);<a name="line.1527"></a>
+<span class="sourceLineNo">1528</span> }<a name="line.1528"></a>
+<span class="sourceLineNo">1529</span><a name="line.1529"></a>
+<span class="sourceLineNo">1530</span> @Override<a name="line.1530"></a>
+<span class="sourceLineNo">1531</span> public BlockPriority getBlockPriority() {<a name="line.1531"></a>
+<span class="sourceLineNo">1532</span> return e.getValue().getPriority();<a name="line.1532"></a>
+<span class="sourceLineNo">1533</span> }<a name="line.1533"></a>
+<span class="sourceLineNo">1534</span><a name="line.1534"></a>
+<span class="sourceLineNo">1535</span> @Override<a name="line.1535"></a>
+<span class="sourceLineNo">1536</span> public BlockType getBlockType() {<a name="line.1536"></a>
+<span class="sourceLineNo">1537</span> // Not held by BucketEntry. Could add it if wanted on BucketEntry creation.<a name="line.1537"></a>
+<span class="sourceLineNo">1538</span> return null;<a name="line.1538"></a>
+<span class="sourceLineNo">1539</span> }<a name="line.1539"></a>
+<span class="sourceLineNo">1540</span><a name="line.1540"></a>
+<span class="sourceLineNo">1541</span> @Override<a name="line.1541"></a>
+<span class="sourceLineNo">1542</span> public long getOffset() {<a name="line.1542"></a>
+<span class="sourceLineNo">1543</span> return e.getKey().getOffset();<a name="line.1543"></a>
+<span class="sourceLineNo">1544</span> }<a name="line.1544"></a>
+<span class="sourceLineNo">1545</span><a name="line.1545"></a>
+<span class="sourceLineNo">1546</span> @Override<a name="line.1546"></a>
+<span class="sourceLineNo">1547</span> public long getSize() {<a name="line.1547"></a>
+<span class="sourceLineNo">1548</span> return e.getValue().getLength();<a name="line.1548"></a>
+<span class="sourceLineNo">1549</span> }<a name="line.1549"></a>
+<span class="sourceLineNo">1550</span><a name="line.1550"></a>
+<span class="sourceLineNo">1551</span> @Override<a name="line.1551"></a>
+<span class="sourceLineNo">1552</span> public long getCachedTime() {<a name="line.1552"></a>
+<span class="sourceLineNo">1553</span> return e.getValue().getCachedTime();<a name="line.1553"></a>
+<span class="sourceLineNo">1554</span> }<a name="line.1554"></a>
+<span class="sourceLineNo">1555</span><a name="line.1555"></a>
+<span class="sourceLineNo">1556</span> @Override<a name="line.1556"></a>
+<span class="sourceLineNo">1557</span> public String getFilename() {<a name="line.1557"></a>
+<span class="sourceLineNo">1558</span> return e.getKey().getHfileName();<a name="line.1558"></a>
+<span class="sourceLineNo">1559</span> }<a name="line.1559"></a>
+<span class="sourceLineNo">1560</span><a name="line.1560"></a>
+<span class="sourceLineNo">1561</span> @Override<a name="line.1561"></a>
+<span class="sourceLineNo">1562</span> public int compareTo(CachedBlock other) {<a name="line.1562"></a>
+<span class="sourceLineNo">1563</span> int diff = this.getFilename().compareTo(other.getFilename());<a name="line.1563"></a>
+<span class="sourceLineNo">1564</span> if (diff != 0) return diff;<a name="line.1564"></a>
+<span class="sourceLineNo">1565</span><a name="line.1565"></a>
+<span class="sourceLineNo">1566</span> diff = Long.compare(this.getOffset(), other.getOffset());<a name="line.1566"></a>
+<span class="sourceLineNo">1567</span> if (diff != 0) return diff;<a name="line.1567"></a>
+<span class="sourceLineNo">1568</span> if (other.getCachedTime() < 0 || this.getCachedTime() < 0) {<a name="line.1568"></a>
+<span class="sourceLineNo">1569</span> throw new IllegalStateException("" + this.getCachedTime() + ", " +<a name="line.1569"></a>
+<span class="sourceLineNo">1570</span> other.getCachedTime());<a name="line.1570"></a>
+<span class="sourceLineNo">1571</span> }<a name="line.1571"></a>
+<span class="sourceLineNo">1572</span> return Long.compare(other.getCachedTime(), this.getCachedTime());<a name="line.1572"></a>
+<span class="sourceLineNo">1573</span> }<a name="line.1573"></a>
+<span class="sourceLineNo">1574</span><a name="line.1574"></a>
+<span class="sourceLineNo">1575</span> @Override<a name="line.1575"></a>
+<span class="sourceLineNo">1576</span> public int hashCode() {<a name="line.1576"></a>
+<span class="sourceLineNo">1577</span> return e.getKey().hashCode();<a name="line.1577"></a>
+<span class="sourceLineNo">1578</span> }<a name="line.1578"></a>
+<span class="sourceLineNo">1579</span><a name="line.1579"></a>
+<span class="sourceLineNo">1580</span> @Override<a name="line.1580"></a>
+<span class="sourceLineNo">1581</span> public boolean equals(Object obj) {<a name="line.1581"></a>
+<span class="sourceLineNo">1582</span> if (obj instanceof CachedBlock) {<a name="line.1582"></a>
+<span class="sourceLineNo">1583</span> CachedBlock cb = (CachedBlock)obj;<a name="line.1583"></a>
+<span class="sourceLineNo">1584</span> return compareTo(cb) == 0;<a name="line.1584"></a>
+<span class="sourceLineNo">1585</span> } else {<a name="line.1585"></a>
+<span class="sourceLineNo">1586</span> return false;<a name="line.1586"></a>
+<span class="sourceLineNo">1587</span> }<a name="line.1587"></a>
+<span class="sourceLineNo">1588</span> }<a name="line.1588"></a>
+<span class="sourceLineNo">1589</span> };<a name="line.1589"></a>
+<span class="sourceLineNo">1590</span> }<a name="line.1590"></a>
+<span class="sourceLineNo">1591</span><a name="line.1591"></a>
+<span class="sourceLineNo">1592</span> @Override<a name="line.1592"></a>
+<span class="sourceLineNo">1593</span> public void remove() {<a name="line.1593"></a>
+<span class="sourceLineNo">1594</span> throw new UnsupportedOperationException();<a name="line.1594"></a>
+<span class="sourceLineNo">1595</span> }<a name="line.1595"></a>
+<span class="sourceLineNo">1596</span> };<a name="line.1596"></a>
+<span class="sourceLineNo">1597</span> }<a name="line.1597"></a>
+<span class="sourceLineNo">1598</span><a name="line.1598"></a>
+<span class="sourceLineNo">1599</span> @Override<a name="line.1599"></a>
+<span class="sourceLineNo">1600</span> public BlockCache[] getBlockCaches() {<a name="line.1600"></a>
+<span class="sourceLineNo">1601</span> return null;<a name="line.1601"></a>
+<span class="sourceLineNo">1602</span> }<a name="line.1602"></a>
+<span class="sourceLineNo">1603</span><a name="line.1603"></a>
+<span class="sourceLineNo">1604</span> public int getRpcRefCount(BlockCacheKey cacheKey) {<a name="line.1604"></a>
+<span class="sourceLineNo">1605</span> BucketEntry bucketEntry = backingMap.get(cacheKey);<a name="line.1605"></a>
+<span class="sourceLineNo">1606</span> if (bucketEntry != null) {<a name="line.1606"></a>
+<span class="sourceLineNo">1607</span> return bucketEntry.refCnt() - (bucketEntry.markedAsEvicted.get() ? 0 : 1);<a name="line.1607"></a>
+<span class="sourceLineNo">1608</span> }<a name="line.1608"></a>
+<span class="sourceLineNo">1609</span> return 0;<a name="line.1609"></a>
<span class="sourceLineNo">1610</span> }<a name="line.1610"></a>
<span class="sourceLineNo">1611</span><a name="line.1611"></a>
-<span class="sourceLineNo">1612</span> float getMinFactor() {<a name="line.1612"></a>
-<span class="sourceLineNo">1613</span> return minFactor;<a name="line.1613"></a>
+<span class="sourceLineNo">1612</span> float getAcceptableFactor() {<a name="line.1612"></a>
+<span class="sourceLineNo">1613</span> return acceptableFactor;<a name="line.1613"></a>
<span class="sourceLineNo">1614</span> }<a name="line.1614"></a>
<span class="sourceLineNo">1615</span><a name="line.1615"></a>
-<span class="sourceLineNo">1616</span> float getExtraFreeFactor() {<a name="line.1616"></a>
-<span class="sourceLineNo">1617</span> return extraFreeFactor;<a name="line.1617"></a>
+<span class="sourceLineNo">1616</span> float getMinFactor() {<a name="line.1616"></a>
+<span class="sourceLineNo">1617</span> return minFactor;<a name="line.1617"></a>
<span class="sourceLineNo">1618</span> }<a name="line.1618"></a>
<span class="sourceLineNo">1619</span><a name="line.1619"></a>
-<span class="sourceLineNo">1620</span> float getSingleFactor() {<a name="line.1620"></a>
-<span class="sourceLineNo">1621</span> return singleFactor;<a name="line.1621"></a>
+<span class="sourceLineNo">1620</span> float getExtraFreeFactor() {<a name="line.1620"></a>
+<span class="sourceLineNo">1621</span> return extraFreeFactor;<a name="line.1621"></a>
<span class="sourceLineNo">1622</span> }<a name="line.1622"></a>
<span class="sourceLineNo">1623</span><a name="line.1623"></a>
-<span class="sourceLineNo">1624</span> float getMultiFactor() {<a name="line.1624"></a>
-<span class="sourceLineNo">1625</span> return multiFactor;<a name="line.1625"></a>
+<span class="sourceLineNo">1624</span> float getSingleFactor() {<a name="line.1624"></a>
+<span class="sourceLineNo">1625</span> return singleFactor;<a name="line.1625"></a>
<span class="sourceLineNo">1626</span> }<a name="line.1626"></a>
<span class="sourceLineNo">1627</span><a name="line.1627"></a>
-<span class="sourceLineNo">1628</span> float getMemoryFactor() {<a name="line.1628"></a>
-<span class="sourceLineNo">1629</span> return memoryFactor;<a name="line.1629"></a>
+<span class="sourceLineNo">1628</span> float getMultiFactor() {<a name="line.1628"></a>
+<span class="sourceLineNo">1629</span> return multiFactor;<a name="line.1629"></a>
<span class="sourceLineNo">1630</span> }<a name="line.1630"></a>
<span class="sourceLineNo">1631</span><a name="line.1631"></a>
-<span class="sourceLineNo">1632</span> /**<a name="line.1632"></a>
-<span class="sourceLineNo">1633</span> * Wrapped the delegate ConcurrentMap with maintaining its block's reference count.<a name="line.1633"></a>
-<span class="sourceLineNo">1634</span> */<a name="line.1634"></a>
-<span class="sourceLineNo">1635</span> static class RAMCache {<a name="line.1635"></a>
-<span class="sourceLineNo">1636</span> /**<a name="line.1636"></a>
-<span class="sourceLineNo">1637</span> * Defined the map as {@link ConcurrentHashMap} explicitly here, because in<a name="line.1637"></a>
-<span class="sourceLineNo">1638</span> * {@link RAMCache#get(BlockCacheKey)} and<a name="line.1638"></a>
-<span class="sourceLineNo">1639</span> * {@link RAMCache#putIfAbsent(BlockCacheKey, BucketCache.RAMQueueEntry)} , we need to<a name="line.1639"></a>
-<span class="sourceLineNo">1640</span> * guarantee the atomicity of map#computeIfPresent(key, func) and map#putIfAbsent(key, func).<a name="line.1640"></a>
-<span class="sourceLineNo">1641</span> * Besides, the func method can execute exactly once only when the key is present(or absent)<a name="line.1641"></a>
-<span class="sourceLineNo">1642</span> * and under the lock context. Otherwise, the reference count of block will be messed up.<a name="line.1642"></a>
-<span class="sourceLineNo">1643</span> * Notice that the {@link java.util.concurrent.ConcurrentSkipListMap} can not guarantee that.<a name="line.1643"></a>
-<span class="sourceLineNo">1644</span> */<a name="line.1644"></a>
-<span class="sourceLineNo">1645</span> final ConcurrentHashMap<BlockCacheKey, RAMQueueEntry> delegate = new ConcurrentHashMap<>();<a name="line.1645"></a>
-<span class="sourceLineNo">1646</span><a name="line.1646"></a>
-<span class="sourceLineNo">1647</span> public boolean containsKey(BlockCacheKey key) {<a name="line.1647"></a>
-<span class="sourceLineNo">1648</span> return delegate.containsKey(key);<a name="line.1648"></a>
-<span class="sourceLineNo">1649</span> }<a name="line.1649"></a>
+<span class="sourceLineNo">1632</span> float getMemoryFactor() {<a name="line.1632"></a>
+<span class="sourceLineNo">1633</span> return memoryFactor;<a name="line.1633"></a>
+<span class="sourceLineNo">1634</span> }<a name="line.1634"></a>
+<span class="sourceLineNo">1635</span><a name="line.1635"></a>
+<span class="sourceLineNo">1636</span> /**<a name="line.1636"></a>
+<span class="sourceLineNo">1637</span> * Wrapped the delegate ConcurrentMap with maintaining its block's reference count.<a name="line.1637"></a>
+<span class="sourceLineNo">1638</span> */<a name="line.1638"></a>
+<span class="sourceLineNo">1639</span> static class RAMCache {<a name="line.1639"></a>
+<span class="sourceLineNo">1640</span> /**<a name="line.1640"></a>
+<span class="sourceLineNo">1641</span> * Defined the map as {@link ConcurrentHashMap} explicitly here, because in<a name="line.1641"></a>
+<span class="sourceLineNo">1642</span> * {@link RAMCache#get(BlockCacheKey)} and<a name="line.1642"></a>
+<span class="sourceLineNo">1643</span> * {@link RAMCache#putIfAbsent(BlockCacheKey, BucketCache.RAMQueueEntry)} , we need to<a name="line.1643"></a>
+<span class="sourceLineNo">1644</span> * guarantee the atomicity of map#computeIfPresent(key, func) and map#putIfAbsent(key, func).<a name="line.1644"></a>
+<span class="sourceLineNo">1645</span> * Besides, the func method can execute exactly once only when the key is present(or absent)<a name="line.1645"></a>
+<span class="sourceLineNo">1646</span> * and under the lock context. Otherwise, the reference count of block will be messed up.<a name="line.1646"></a>
+<span class="sourceLineNo">1647</span> * Notice that the {@link java.util.concurrent.ConcurrentSkipListMap} can not guarantee that.<a name="line.1647"></a>
+<span class="sourceLineNo">1648</span> */<a name="line.1648"></a>
+<span class="sourceLineNo">1649</span> final ConcurrentHashMap<BlockCacheKey, RAMQueueEntry> delegate = new ConcurrentHashMap<>();<a name="line.1649"></a>
<span class="sourceLineNo">1650</span><a name="line.1650"></a>
-<span class="sourceLineNo">1651</span> public RAMQueueEntry get(BlockCacheKey key) {<a name="line.1651"></a>
-<span class="sourceLineNo">1652</span> return delegate.computeIfPresent(key, (k, re) -> {<a name="line.1652"></a>
-<span class="sourceLineNo">1653</span> // It'll be referenced by RPC, so retain atomically here. if the get and retain is not<a name="line.1653"></a>
-<span class="sourceLineNo">1654</span> // atomic, another thread may remove and release the block, when retaining in this thread we<a name="line.1654"></a>
-<span class="sourceLineNo">1655</span> // may retain a block with refCnt=0 which is disallowed. (see HBASE-22422)<a name="line.1655"></a>
-<span class="sourceLineNo">1656</span> re.getData().retain();<a name="line.1656"></a>
-<span class="sourceLineNo">1657</span> return re;<a name="line.1657"></a>
-<span class="sourceLineNo">1658</span> });<a name="line.1658"></a>
-<span class="sourceLineNo">1659</span> }<a name="line.1659"></a>
-<span class="sourceLineNo">1660</span><a name="line.1660"></a>
-<span class="sourceLineNo">1661</span> /**<a name="line.1661"></a>
-<span class="sourceLineNo">1662</span> * Return the previous associated value, or null if absent. It has the same meaning as<a name="line.1662"></a>
-<span class="sourceLineNo">1663</span> * {@link ConcurrentMap#putIfAbsent(Object, Object)}<a name="line.1663"></a>
-<span class="sourceLineNo">1664</span> */<a name="line.1664"></a>
-<span class="sourceLineNo">1665</span> public RAMQueueEntry putIfAbsent(BlockCacheKey key, RAMQueueEntry entry) {<a name="line.1665"></a>
-<span class="sourceLineNo">1666</span> AtomicBoolean absent = new AtomicBoolean(false);<a name="line.1666"></a>
-<span class="sourceLineNo">1667</span> RAMQueueEntry re = delegate.computeIfAbsent(key, k -> {<a name="line.1667"></a>
-<span class="sourceLineNo">1668</span> // The RAMCache reference to this entry, so reference count should be increment.<a name="line.1668"></a>
-<span class="sourceLineNo">1669</span> entry.getData().retain();<a name="line.1669"></a>
-<span class="sourceLineNo">1670</span> absent.set(true);<a name="line.1670"></a>
-<span class="sourceLineNo">1671</span> return entry;<a name="line.1671"></a>
-<span class="sourceLineNo">1672</span> });<a name="line.1672"></a>
-<span class="sourceLineNo">1673</span> return absent.get() ? null : re;<a name="line.1673"></a>
-<span class="sourceLineNo">1674</span> }<a name="line.1674"></a>
-<span class="sourceLineNo">1675</span><a name="line.1675"></a>
-<span class="sourceLineNo">1676</span> public boolean remove(BlockCacheKey key) {<a name="line.1676"></a>
-<span class="sourceLineNo">1677</span> return remove(key, re->{});<a name="line.1677"></a>
+<span class="sourceLineNo">1651</span> public boolean containsKey(BlockCacheKey key) {<a name="line.1651"></a>
+<span class="sourceLineNo">1652</span> return delegate.containsKey(key);<a name="line.1652"></a>
+<span class="sourceLineNo">1653</span> }<a name="line.1653"></a>
+<span class="sourceLineNo">1654</span><a name="line.1654"></a>
+<span class="sourceLineNo">1655</span> public RAMQueueEntry get(BlockCacheKey key) {<a name="line.1655"></a>
+<span class="sourceLineNo">1656</span> return delegate.computeIfPresent(key, (k, re) -> {<a name="line.1656"></a>
+<span class="sourceLineNo">1657</span> // It'll be referenced by RPC, so retain atomically here. if the get and retain is not<a name="line.1657"></a>
+<span class="sourceLineNo">1658</span> // atomic, another thread may remove and release the block, when retaining in this thread we<a name="line.1658"></a>
+<span class="sourceLineNo">1659</span> // may retain a block with refCnt=0 which is disallowed. (see HBASE-22422)<a name="line.1659"></a>
+<span class="sourceLineNo">1660</span> re.getData().retain();<a name="line.1660"></a>
+<span class="sourceLineNo">1661</span> return re;<a name="line.1661"></a>
+<span class="sourceLineNo">1662</span> });<a name="line.1662"></a>
+<span class="sourceLineNo">1663</span> }<a name="line.1663"></a>
+<span class="sourceLineNo">1664</span><a name="line.1664"></a>
+<span class="sourceLineNo">1665</span> /**<a name="line.1665"></a>
+<span class="sourceLineNo">1666</span> * Return the previous associated value, or null if absent. It has the same meaning as<a name="line.1666"></a>
+<span class="sourceLineNo">1667</span> * {@link ConcurrentMap#putIfAbsent(Object, Object)}<a name="line.1667"></a>
+<span class="sourceLineNo">1668</span> */<a name="line.1668"></a>
+<span class="sourceLineNo">1669</span> public RAMQueueEntry putIfAbsent(BlockCacheKey key, RAMQueueEntry entry) {<a name="line.1669"></a>
+<span class="sourceLineNo">1670</span> AtomicBoolean absent = new AtomicBoolean(false);<a name="line.1670"></a>
+<span class="sourceLineNo">1671</span> RAMQueueEntry re = delegate.computeIfAbsent(key, k -> {<a name="line.1671"></a>
+<span class="sourceLineNo">1672</span> // The RAMCache reference to this entry, so reference count should be increment.<a name="line.1672"></a>
+<span class="sourceLineNo">1673</span> entry.getData().retain();<a name="line.1673"></a>
+<span class="sourceLineNo">1674</span> absent.set(true);<a name="line.1674"></a>
+<span class="sourceLineNo">1675</span> return entry;<a name="line.1675"></a>
+<span class="sourceLineNo">1676</span> });<a name="line.1676"></a>
+<span class="sourceLineNo">1677</span> return absent.get() ? null : re;<a name="line.1677"></a>
<span class="sourceLineNo">1678</span> }<a name="line.1678"></a>
<span class="sourceLineNo">1679</span><a name="line.1679"></a>
-<span class="sourceLineNo">1680</span> /**<a name="line.1680"></a>
-<span class="sourceLineNo">1681</span> * Defined an {@link Consumer} here, because once the removed entry release its reference count,<a name="line.1681"></a>
-<span class="sourceLineNo">1682</span> * then it's ByteBuffers may be recycled and accessing it outside this method will be thrown an<a name="line.1682"></a>
-<span class="sourceLineNo">1683</span> * exception. the consumer will access entry to remove before release its reference count.<a name="line.1683"></a>
-<span class="sourceLineNo">1684</span> * Notice, don't change its reference count in the {@link Consumer}<a name="line.1684"></a>
-<span class="sourceLineNo">1685</span> */<a name="line.1685"></a>
-<span class="sourceLineNo">1686</span> public boolean remove(BlockCacheKey key, Consumer<RAMQueueEntry> action) {<a name="line.1686"></a>
-<span class="sourceLineNo">1687</span> RAMQueueEntry previous = delegate.remove(key);<a name="line.1687"></a>
-<span class="sourceLineNo">1688</span> action.accept(previous);<a name="line.1688"></a>
-<span class="sourceLineNo">1689</span> if (previous != null) {<a name="line.1689"></a>
-<span class="sourceLineNo">1690</span> previous.getData().release();<a name="line.1690"></a>
-<span class="sourceLineNo">1691</span> }<a name="line.1691"></a>
-<span class="sourceLineNo">1692</span> return previous != null;<a name="line.1692"></a>
-<span class="sourceLineNo">1693</span> }<a name="line.1693"></a>
-<span class="sourceLineNo">1694</span><a name="line.1694"></a>
-<span class="sourceLineNo">1695</span> public boolean isEmpty() {<a name="line.1695"></a>
-<span class="sourceLineNo">1696</span> return delegate.isEmpty();<a name="line.1696"></a>
+<span class="sourceLineNo">1680</span> public boolean remove(BlockCacheKey key) {<a name="line.1680"></a>
+<span class="sourceLineNo">1681</span> return remove(key, re->{});<a name="line.1681"></a>
+<span class="sourceLineNo">1682</span> }<a name="line.1682"></a>
+<span class="sourceLineNo">1683</span><a name="line.1683"></a>
+<span class="sourceLineNo">1684</span> /**<a name="line.1684"></a>
+<span class="sourceLineNo">1685</span> * Defined an {@link Consumer} here, because once the removed entry release its reference count,<a name="line.1685"></a>
+<span class="sourceLineNo">1686</span> * then it's ByteBuffers may be recycled and accessing it outside this method will be thrown an<a name="line.1686"></a>
+<span class="sourceLineNo">1687</span> * exception. the consumer will access entry to remove before release its reference count.<a name="line.1687"></a>
+<span class="sourceLineNo">1688</span> * Notice, don't change its reference count in the {@link Consumer}<a name="line.1688"></a>
+<span class="sourceLineNo">1689</span> */<a name="line.1689"></a>
+<span class="sourceLineNo">1690</span> public boolean remove(BlockCacheKey key, Consumer<RAMQueueEntry> action) {<a name="line.1690"></a>
+<span class="sourceLineNo">1691</span> RAMQueueEntry previous = delegate.remove(key);<a name="line.1691"></a>
+<span class="sourceLineNo">1692</span> action.accept(previous);<a name="line.1692"></a>
+<span class="sourceLineNo">1693</span> if (previous != null) {<a name="line.1693"></a>
+<span class="sourceLineNo">1694</span> previous.getData().release();<a name="line.1694"></a>
+<span class="sourceLineNo">1695</span> }<a name="line.1695"></a>
+<span class="sourceLineNo">1696</span> return previous != null;<a name="line.1696"></a>
<span class="sourceLineNo">1697</span> }<a name="line.1697"></a>
<span class="sourceLineNo">1698</span><a name="line.1698"></a>
-<span class="sourceLineNo">1699</span> public void clear() {<a name="line.1699"></a>
-<span class="sourceLineNo">1700</span> Iterator<Map.Entry<BlockCacheKey, RAMQueueEntry>> it = delegate.entrySet().iterator();<a name="line.1700"></a>
-<span class="sourceLineNo">1701</span> while (it.hasNext()) {<a name="line.1701"></a>
-<span class="sourceLineNo">1702</span> RAMQueueEntry re = it.next().getValue();<a name="line.1702"></a>
-<span class="sourceLineNo">1703</span> it.remove();<a name="line.1703"></a>
-<span class="sourceLineNo">1704</span> re.getData().release();<a name="line.1704"></a>
-<span class="sourceLineNo">1705</span> }<a name="line.1705"></a>
-<span class="sourceLineNo">1706</span> }<a name="line.1706"></a>
-<span class="sourceLineNo">1707</span> }<a name="line.1707"></a>
-<span class="sourceLineNo">1708</span>}<a name="line.1708"></a>
+<span class="sourceLineNo">1699</span> public boolean isEmpty() {<a name="line.1699"></a>
+<span class="sourceLineNo">1700</span> return delegate.isEmpty();<a name="line.1700"></a>
+<span class="sourceLineNo">1701</span> }<a name="line.1701"></a>
+<span class="sourceLineNo">1702</span><a name="line.1702"></a>
+<span class="sourceLineNo">1703</span> public void clear() {<a name="line.1703"></a>
+<span class="sourceLineNo">1704</span> Iterator<Map.Entry<BlockCacheKey, RAMQueueEntry>> it = delegate.entrySet().iterator();<a name="line.1704"></a>
+<span class="sourceLineNo">1705</span> while (it.hasNext()) {<a name="line.1705"></a>
+<span class="sourceLineNo">1706</span> RAMQueueEntry re = it.next().getValue();<a name="line.1706"></a>
+<span class="sourceLineNo">1707</span> it.remove();<a name="line.1707"></a>
+<span class="sourceLineNo">1708</span> re.getData().release();<a name="line.1708"></a>
+<span class="sourceLineNo">1709</span> }<a name="line.1709"></a>
+<span class="sourceLineNo">1710</span> }<a name="line.1710"></a>
+<span class="sourceLineNo">1711</span> }<a name="line.1711"></a>
+<span class="sourceLineNo">1712</span>}<a name="line.1712"></a>
diff --git a/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html b/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html
index 6e1ac11..5d27f48 100644
--- a/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html
+++ b/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMCache.html
@@ -494,1226 +494,1230 @@
<span class="sourceLineNo">486</span> } else {<a name="line.486"></a>
<span class="sourceLineNo">487</span> this.blockNumber.increment();<a name="line.487"></a>
<span class="sourceLineNo">488</span> this.heapSize.add(cachedItem.heapSize());<a name="line.488"></a>
-<span class="sourceLineNo">489</span> blocksByHFile.add(cacheKey);<a name="line.489"></a>
-<span class="sourceLineNo">490</span> }<a name="line.490"></a>
-<span class="sourceLineNo">491</span> }<a name="line.491"></a>
-<span class="sourceLineNo">492</span><a name="line.492"></a>
-<span class="sourceLineNo">493</span> /**<a name="line.493"></a>
-<span class="sourceLineNo">494</span> * Get the buffer of the block with the specified key.<a name="line.494"></a>
-<span class="sourceLineNo">495</span> * @param key block's cache key<a name="line.495"></a>
-<span class="sourceLineNo">496</span> * @param caching true if the caller caches blocks on cache misses<a name="line.496"></a>
-<span class="sourceLineNo">497</span> * @param repeat Whether this is a repeat lookup for the same block<a name="line.497"></a>
-<span class="sourceLineNo">498</span> * @param updateCacheMetrics Whether we should update cache metrics or not<a name="line.498"></a>
-<span class="sourceLineNo">499</span> * @return buffer of specified cache key, or null if not in cache<a name="line.499"></a>
-<span class="sourceLineNo">500</span> */<a name="line.500"></a>
-<span class="sourceLineNo">501</span> @Override<a name="line.501"></a>
-<span class="sourceLineNo">502</span> public Cacheable getBlock(BlockCacheKey key, boolean caching, boolean repeat,<a name="line.502"></a>
-<span class="sourceLineNo">503</span> boolean updateCacheMetrics) {<a name="line.503"></a>
-<span class="sourceLineNo">504</span> if (!cacheEnabled) {<a name="line.504"></a>
-<span class="sourceLineNo">505</span> return null;<a name="line.505"></a>
-<span class="sourceLineNo">506</span> }<a name="line.506"></a>
-<span class="sourceLineNo">507</span> RAMQueueEntry re = ramCache.get(key);<a name="line.507"></a>
-<span class="sourceLineNo">508</span> if (re != null) {<a name="line.508"></a>
-<span class="sourceLineNo">509</span> if (updateCacheMetrics) {<a name="line.509"></a>
-<span class="sourceLineNo">510</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.510"></a>
-<span class="sourceLineNo">511</span> }<a name="line.511"></a>
-<span class="sourceLineNo">512</span> re.access(accessCount.incrementAndGet());<a name="line.512"></a>
-<span class="sourceLineNo">513</span> return re.getData();<a name="line.513"></a>
-<span class="sourceLineNo">514</span> }<a name="line.514"></a>
-<span class="sourceLineNo">515</span> BucketEntry bucketEntry = backingMap.get(key);<a name="line.515"></a>
-<span class="sourceLineNo">516</span> if (bucketEntry != null) {<a name="line.516"></a>
-<span class="sourceLineNo">517</span> long start = System.nanoTime();<a name="line.517"></a>
-<span class="sourceLineNo">518</span> ReentrantReadWriteLock lock = offsetLock.getLock(bucketEntry.offset());<a name="line.518"></a>
-<span class="sourceLineNo">519</span> try {<a name="line.519"></a>
-<span class="sourceLineNo">520</span> lock.readLock().lock();<a name="line.520"></a>
-<span class="sourceLineNo">521</span> // We can not read here even if backingMap does contain the given key because its offset<a name="line.521"></a>
-<span class="sourceLineNo">522</span> // maybe changed. If we lock BlockCacheKey instead of offset, then we can only check<a name="line.522"></a>
-<span class="sourceLineNo">523</span> // existence here.<a name="line.523"></a>
-<span class="sourceLineNo">524</span> if (bucketEntry.equals(backingMap.get(key))) {<a name="line.524"></a>
-<span class="sourceLineNo">525</span> // Read the block from IOEngine based on the bucketEntry's offset and length, NOTICE: the<a name="line.525"></a>
-<span class="sourceLineNo">526</span> // block will use the refCnt of bucketEntry, which means if two HFileBlock mapping to<a name="line.526"></a>
-<span class="sourceLineNo">527</span> // the same BucketEntry, then all of the three will share the same refCnt.<a name="line.527"></a>
-<span class="sourceLineNo">528</span> Cacheable cachedBlock = ioEngine.read(bucketEntry);<a name="line.528"></a>
-<span class="sourceLineNo">529</span> if (ioEngine.usesSharedMemory()) {<a name="line.529"></a>
-<span class="sourceLineNo">530</span> // If IOEngine use shared memory, cachedBlock and BucketEntry will share the<a name="line.530"></a>
-<span class="sourceLineNo">531</span> // same RefCnt, do retain here, in order to count the number of RPC references<a name="line.531"></a>
-<span class="sourceLineNo">532</span> cachedBlock.retain();<a name="line.532"></a>
-<span class="sourceLineNo">533</span> }<a name="line.533"></a>
-<span class="sourceLineNo">534</span> // Update the cache statistics.<a name="line.534"></a>
-<span class="sourceLineNo">535</span> if (updateCacheMetrics) {<a name="line.535"></a>
-<span class="sourceLineNo">536</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.536"></a>
-<span class="sourceLineNo">537</span> cacheStats.ioHit(System.nanoTime() - start);<a name="line.537"></a>
-<span class="sourceLineNo">538</span> }<a name="line.538"></a>
-<span class="sourceLineNo">539</span> bucketEntry.access(accessCount.incrementAndGet());<a name="line.539"></a>
-<span class="sourceLineNo">540</span> if (this.ioErrorStartTime > 0) {<a name="line.540"></a>
-<span class="sourceLineNo">541</span> ioErrorStartTime = -1;<a name="line.541"></a>
-<span class="sourceLineNo">542</span> }<a name="line.542"></a>
-<span class="sourceLineNo">543</span> return cachedBlock;<a name="line.543"></a>
-<span class="sourceLineNo">544</span> }<a name="line.544"></a>
-<span class="sourceLineNo">545</span> } catch (IOException ioex) {<a name="line.545"></a>
-<span class="sourceLineNo">546</span> LOG.error("Failed reading block " + key + " from bucket cache", ioex);<a name="line.546"></a>
-<span class="sourceLineNo">547</span> checkIOErrorIsTolerated();<a name="line.547"></a>
-<span class="sourceLineNo">548</span> } finally {<a name="line.548"></a>
-<span class="sourceLineNo">549</span> lock.readLock().unlock();<a name="line.549"></a>
-<span class="sourceLineNo">550</span> }<a name="line.550"></a>
-<span class="sourceLineNo">551</span> }<a name="line.551"></a>
-<span class="sourceLineNo">552</span> if (!repeat && updateCacheMetrics) {<a name="line.552"></a>
-<span class="sourceLineNo">553</span> cacheStats.miss(caching, key.isPrimary(), key.getBlockType());<a name="line.553"></a>
-<span class="sourceLineNo">554</span> }<a name="line.554"></a>
-<span class="sourceLineNo">555</span> return null;<a name="line.555"></a>
-<span class="sourceLineNo">556</span> }<a name="line.556"></a>
-<span class="sourceLineNo">557</span><a name="line.557"></a>
-<span class="sourceLineNo">558</span> /**<a name="line.558"></a>
-<span class="sourceLineNo">559</span> * This method is invoked after the bucketEntry is removed from {@link BucketCache#backingMap}<a name="line.559"></a>
-<span class="sourceLineNo">560</span> */<a name="line.560"></a>
-<span class="sourceLineNo">561</span> void blockEvicted(BlockCacheKey cacheKey, BucketEntry bucketEntry, boolean decrementBlockNumber) {<a name="line.561"></a>
-<span class="sourceLineNo">562</span> bucketEntry.markAsEvicted();<a name="line.562"></a>
-<span class="sourceLineNo">563</span> blocksByHFile.remove(cacheKey);<a name="line.563"></a>
-<span class="sourceLineNo">564</span> if (decrementBlockNumber) {<a name="line.564"></a>
-<span class="sourceLineNo">565</span> this.blockNumber.decrement();<a name="line.565"></a>
-<span class="sourceLineNo">566</span> }<a name="line.566"></a>
-<span class="sourceLineNo">567</span> cacheStats.evicted(bucketEntry.getCachedTime(), cacheKey.isPrimary());<a name="line.567"></a>
-<span class="sourceLineNo">568</span> }<a name="line.568"></a>
-<span class="sourceLineNo">569</span><a name="line.569"></a>
-<span class="sourceLineNo">570</span> /**<a name="line.570"></a>
-<span class="sourceLineNo">571</span> * Free the {{@link BucketEntry} actually,which could only be invoked when the<a name="line.571"></a>
-<span class="sourceLineNo">572</span> * {@link BucketEntry#refCnt} becoming 0.<a name="line.572"></a>
-<span class="sourceLineNo">573</span> */<a name="line.573"></a>
-<span class="sourceLineNo">574</span> void freeBucketEntry(BucketEntry bucketEntry) {<a name="line.574"></a>
-<span class="sourceLineNo">575</span> bucketAllocator.freeBlock(bucketEntry.offset());<a name="line.575"></a>
-<span class="sourceLineNo">576</span> realCacheSize.add(-1 * bucketEntry.getLength());<a name="line.576"></a>
-<span class="sourceLineNo">577</span> }<a name="line.577"></a>
-<span class="sourceLineNo">578</span><a name="line.578"></a>
-<span class="sourceLineNo">579</span> /**<a name="line.579"></a>
-<span class="sourceLineNo">580</span> * Try to evict the block from {@link BlockCache} by force. We'll call this in few cases:<br><a name="line.580"></a>
-<span class="sourceLineNo">581</span> * 1. Close an HFile, and clear all cached blocks. <br><a name="line.581"></a>
-<span class="sourceLineNo">582</span> * 2. Call {@link Admin#clearBlockCache(TableName)} to clear all blocks for a given table.<br><a name="line.582"></a>
-<span class="sourceLineNo">583</span> * <p><a name="line.583"></a>
-<span class="sourceLineNo">584</span> * Firstly, we'll try to remove the block from RAMCache,and then try to evict from backingMap.<a name="line.584"></a>
-<span class="sourceLineNo">585</span> * Here we evict the block from backingMap immediately, but only free the reference from bucket<a name="line.585"></a>
-<span class="sourceLineNo">586</span> * cache by calling {@link BucketEntry#markedAsEvicted}. If there're still some RPC referring this<a name="line.586"></a>
-<span class="sourceLineNo">587</span> * block, block can only be de-allocated when all of them release the block.<a name="line.587"></a>
-<span class="sourceLineNo">588</span> * <p><a name="line.588"></a>
-<span class="sourceLineNo">589</span> * NOTICE: we need to grab the write offset lock firstly before releasing the reference from<a name="line.589"></a>
-<span class="sourceLineNo">590</span> * bucket cache. if we don't, we may read an {@link BucketEntry} with refCnt = 0 when<a name="line.590"></a>
-<span class="sourceLineNo">591</span> * {@link BucketCache#getBlock(BlockCacheKey, boolean, boolean, boolean)}, it's a memory leak.<a name="line.591"></a>
-<span class="sourceLineNo">592</span> * @param cacheKey Block to evict<a name="line.592"></a>
-<span class="sourceLineNo">593</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.593"></a>
-<span class="sourceLineNo">594</span> */<a name="line.594"></a>
-<span class="sourceLineNo">595</span> @Override<a name="line.595"></a>
-<span class="sourceLineNo">596</span> public boolean evictBlock(BlockCacheKey cacheKey) {<a name="line.596"></a>
-<span class="sourceLineNo">597</span> return doEvictBlock(cacheKey, null);<a name="line.597"></a>
-<span class="sourceLineNo">598</span> }<a name="line.598"></a>
-<span class="sourceLineNo">599</span><a name="line.599"></a>
-<span class="sourceLineNo">600</span> /**<a name="line.600"></a>
-<span class="sourceLineNo">601</span> * Evict the {@link BlockCacheKey} and {@link BucketEntry} from {@link BucketCache#backingMap} and<a name="line.601"></a>
-<span class="sourceLineNo">602</span> * {@link BucketCache#ramCache}. <br/><a name="line.602"></a>
-<span class="sourceLineNo">603</span> * NOTE:When Evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.603"></a>
-<span class="sourceLineNo">604</span> * {@link BucketEntry} could be removed.<a name="line.604"></a>
-<span class="sourceLineNo">605</span> * @param cacheKey {@link BlockCacheKey} to evict.<a name="line.605"></a>
-<span class="sourceLineNo">606</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.606"></a>
-<span class="sourceLineNo">607</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.607"></a>
-<span class="sourceLineNo">608</span> */<a name="line.608"></a>
-<span class="sourceLineNo">609</span> private boolean doEvictBlock(BlockCacheKey cacheKey, BucketEntry bucketEntry) {<a name="line.609"></a>
-<span class="sourceLineNo">610</span> if (!cacheEnabled) {<a name="line.610"></a>
-<span class="sourceLineNo">611</span> return false;<a name="line.611"></a>
-<span class="sourceLineNo">612</span> }<a name="line.612"></a>
-<span class="sourceLineNo">613</span> boolean existedInRamCache = removeFromRamCache(cacheKey);<a name="line.613"></a>
-<span class="sourceLineNo">614</span> if (bucketEntry == null) {<a name="line.614"></a>
-<span class="sourceLineNo">615</span> bucketEntry = backingMap.get(cacheKey);<a name="line.615"></a>
-<span class="sourceLineNo">616</span> }<a name="line.616"></a>
-<span class="sourceLineNo">617</span> final BucketEntry bucketEntryToUse = bucketEntry;<a name="line.617"></a>
-<span class="sourceLineNo">618</span><a name="line.618"></a>
-<span class="sourceLineNo">619</span> if (bucketEntryToUse == null) {<a name="line.619"></a>
-<span class="sourceLineNo">620</span> if (existedInRamCache) {<a name="line.620"></a>
-<span class="sourceLineNo">621</span> cacheStats.evicted(0, cacheKey.isPrimary());<a name="line.621"></a>
-<span class="sourceLineNo">622</span> }<a name="line.622"></a>
-<span class="sourceLineNo">623</span> return existedInRamCache;<a name="line.623"></a>
-<span class="sourceLineNo">624</span> } else {<a name="line.624"></a>
-<span class="sourceLineNo">625</span> return bucketEntryToUse.withWriteLock(offsetLock, () -> {<a name="line.625"></a>
-<span class="sourceLineNo">626</span> if (backingMap.remove(cacheKey, bucketEntryToUse)) {<a name="line.626"></a>
-<span class="sourceLineNo">627</span> blockEvicted(cacheKey, bucketEntryToUse, !existedInRamCache);<a name="line.627"></a>
-<span class="sourceLineNo">628</span> return true;<a name="line.628"></a>
-<span class="sourceLineNo">629</span> }<a name="line.629"></a>
-<span class="sourceLineNo">630</span> return false;<a name="line.630"></a>
-<span class="sourceLineNo">631</span> });<a name="line.631"></a>
-<span class="sourceLineNo">632</span> }<a name="line.632"></a>
-<span class="sourceLineNo">633</span> }<a name="line.633"></a>
-<span class="sourceLineNo">634</span><a name="line.634"></a>
-<span class="sourceLineNo">635</span> /**<a name="line.635"></a>
-<span class="sourceLineNo">636</span> * <pre><a name="line.636"></a>
-<span class="sourceLineNo">637</span> * Create the {@link Recycler} for {@link BucketEntry#refCnt},which would be used as<a name="line.637"></a>
-<span class="sourceLineNo">638</span> * {@link RefCnt#recycler} of {@link HFileBlock#buf} returned from {@link BucketCache#getBlock}.<a name="line.638"></a>
-<span class="sourceLineNo">639</span> * NOTE: for {@link BucketCache#getBlock},the {@link RefCnt#recycler} of {@link HFileBlock#buf}<a name="line.639"></a>
-<span class="sourceLineNo">640</span> * from {@link BucketCache#backingMap} and {@link BucketCache#ramCache} are different:<a name="line.640"></a>
-<span class="sourceLineNo">641</span> * 1.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#backingMap},<a name="line.641"></a>
-<span class="sourceLineNo">642</span> * it is the return value of current {@link BucketCache#createRecycler} method.<a name="line.642"></a>
-<span class="sourceLineNo">643</span> *<a name="line.643"></a>
-<span class="sourceLineNo">644</span> * 2.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#ramCache},<a name="line.644"></a>
-<span class="sourceLineNo">645</span> * it is {@link ByteBuffAllocator#putbackBuffer}.<a name="line.645"></a>
-<span class="sourceLineNo">646</span> * </pre><a name="line.646"></a>
-<span class="sourceLineNo">647</span> */<a name="line.647"></a>
-<span class="sourceLineNo">648</span> private Recycler createRecycler(final BucketEntry bucketEntry) {<a name="line.648"></a>
-<span class="sourceLineNo">649</span> return () -> {<a name="line.649"></a>
-<span class="sourceLineNo">650</span> freeBucketEntry(bucketEntry);<a name="line.650"></a>
-<span class="sourceLineNo">651</span> return;<a name="line.651"></a>
-<span class="sourceLineNo">652</span> };<a name="line.652"></a>
-<span class="sourceLineNo">653</span> }<a name="line.653"></a>
-<span class="sourceLineNo">654</span><a name="line.654"></a>
-<span class="sourceLineNo">655</span> /**<a name="line.655"></a>
-<span class="sourceLineNo">656</span> * NOTE: This method is only for test.<a name="line.656"></a>
-<span class="sourceLineNo">657</span> */<a name="line.657"></a>
-<span class="sourceLineNo">658</span> public boolean evictBlockIfNoRpcReferenced(BlockCacheKey blockCacheKey) {<a name="line.658"></a>
-<span class="sourceLineNo">659</span> BucketEntry bucketEntry = backingMap.get(blockCacheKey);<a name="line.659"></a>
-<span class="sourceLineNo">660</span> if (bucketEntry == null) {<a name="line.660"></a>
-<span class="sourceLineNo">661</span> return false;<a name="line.661"></a>
-<span class="sourceLineNo">662</span> }<a name="line.662"></a>
-<span class="sourceLineNo">663</span> return evictBucketEntryIfNoRpcReferenced(blockCacheKey, bucketEntry);<a name="line.663"></a>
-<span class="sourceLineNo">664</span> }<a name="line.664"></a>
-<span class="sourceLineNo">665</span><a name="line.665"></a>
-<span class="sourceLineNo">666</span> /**<a name="line.666"></a>
-<span class="sourceLineNo">667</span> * Evict {@link BlockCacheKey} and its corresponding {@link BucketEntry} only if<a name="line.667"></a>
-<span class="sourceLineNo">668</span> * {@link BucketEntry#isRpcRef} is false. <br/><a name="line.668"></a>
-<span class="sourceLineNo">669</span> * NOTE:When evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.669"></a>
-<span class="sourceLineNo">670</span> * {@link BucketEntry} could be removed.<a name="line.670"></a>
-<span class="sourceLineNo">671</span> * @param blockCacheKey {@link BlockCacheKey} to evict.<a name="line.671"></a>
-<span class="sourceLineNo">672</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.672"></a>
-<span class="sourceLineNo">673</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.673"></a>
-<span class="sourceLineNo">674</span> */<a name="line.674"></a>
-<span class="sourceLineNo">675</span> boolean evictBucketEntryIfNoRpcReferenced(BlockCacheKey blockCacheKey, BucketEntry bucketEntry) {<a name="line.675"></a>
-<span class="sourceLineNo">676</span> if (!bucketEntry.isRpcRef()) {<a name="line.676"></a>
-<span class="sourceLineNo">677</span> return doEvictBlock(blockCacheKey, bucketEntry);<a name="line.677"></a>
-<span class="sourceLineNo">678</span> }<a name="line.678"></a>
-<span class="sourceLineNo">679</span> return false;<a name="line.679"></a>
-<span class="sourceLineNo">680</span> }<a name="line.680"></a>
-<span class="sourceLineNo">681</span><a name="line.681"></a>
-<span class="sourceLineNo">682</span> protected boolean removeFromRamCache(BlockCacheKey cacheKey) {<a name="line.682"></a>
-<span class="sourceLineNo">683</span> return ramCache.remove(cacheKey, re -> {<a name="line.683"></a>
-<span class="sourceLineNo">684</span> if (re != null) {<a name="line.684"></a>
-<span class="sourceLineNo">685</span> this.blockNumber.decrement();<a name="line.685"></a>
-<span class="sourceLineNo">686</span> this.heapSize.add(-1 * re.getData().heapSize());<a name="line.686"></a>
-<span class="sourceLineNo">687</span> }<a name="line.687"></a>
-<span class="sourceLineNo">688</span> });<a name="line.688"></a>
-<span class="sourceLineNo">689</span> }<a name="line.689"></a>
-<span class="sourceLineNo">690</span><a name="line.690"></a>
-<span class="sourceLineNo">691</span> /*<a name="line.691"></a>
-<span class="sourceLineNo">692</span> * Statistics thread. Periodically output cache statistics to the log.<a name="line.692"></a>
-<span class="sourceLineNo">693</span> */<a name="line.693"></a>
-<span class="sourceLineNo">694</span> private static class StatisticsThread extends Thread {<a name="line.694"></a>
-<span class="sourceLineNo">695</span> private final BucketCache bucketCache;<a name="line.695"></a>
-<span class="sourceLineNo">696</span><a name="line.696"></a>
-<span class="sourceLineNo">697</span> public StatisticsThread(BucketCache bucketCache) {<a name="line.697"></a>
-<span class="sourceLineNo">698</span> super("BucketCacheStatsThread");<a name="line.698"></a>
-<span class="sourceLineNo">699</span> setDaemon(true);<a name="line.699"></a>
-<span class="sourceLineNo">700</span> this.bucketCache = bucketCache;<a name="line.700"></a>
-<span class="sourceLineNo">701</span> }<a name="line.701"></a>
-<span class="sourceLineNo">702</span><a name="line.702"></a>
-<span class="sourceLineNo">703</span> @Override<a name="line.703"></a>
-<span class="sourceLineNo">704</span> public void run() {<a name="line.704"></a>
-<span class="sourceLineNo">705</span> bucketCache.logStats();<a name="line.705"></a>
-<span class="sourceLineNo">706</span> }<a name="line.706"></a>
-<span class="sourceLineNo">707</span> }<a name="line.707"></a>
-<span class="sourceLineNo">708</span><a name="line.708"></a>
-<span class="sourceLineNo">709</span> public void logStats() {<a name="line.709"></a>
-<span class="sourceLineNo">710</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.710"></a>
-<span class="sourceLineNo">711</span> long usedSize = bucketAllocator.getUsedSize();<a name="line.711"></a>
-<span class="sourceLineNo">712</span> long freeSize = totalSize - usedSize;<a name="line.712"></a>
-<span class="sourceLineNo">713</span> long cacheSize = getRealCacheSize();<a name="line.713"></a>
-<span class="sourceLineNo">714</span> LOG.info("failedBlockAdditions=" + cacheStats.getFailedInserts() + ", " +<a name="line.714"></a>
-<span class="sourceLineNo">715</span> "totalSize=" + StringUtils.byteDesc(totalSize) + ", " +<a name="line.715"></a>
-<span class="sourceLineNo">716</span> "freeSize=" + StringUtils.byteDesc(freeSize) + ", " +<a name="line.716"></a>
-<span class="sourceLineNo">717</span> "usedSize=" + StringUtils.byteDesc(usedSize) +", " +<a name="line.717"></a>
-<span class="sourceLineNo">718</span> "cacheSize=" + StringUtils.byteDesc(cacheSize) +", " +<a name="line.718"></a>
-<span class="sourceLineNo">719</span> "accesses=" + cacheStats.getRequestCount() + ", " +<a name="line.719"></a>
-<span class="sourceLineNo">720</span> "hits=" + cacheStats.getHitCount() + ", " +<a name="line.720"></a>
-<span class="sourceLineNo">721</span> "IOhitsPerSecond=" + cacheStats.getIOHitsPerSecond() + ", " +<a name="line.721"></a>
-<span class="sourceLineNo">722</span> "IOTimePerHit=" + String.format("%.2f", cacheStats.getIOTimePerHit())+ ", " +<a name="line.722"></a>
-<span class="sourceLineNo">723</span> "hitRatio=" + (cacheStats.getHitCount() == 0 ? "0," :<a name="line.723"></a>
-<span class="sourceLineNo">724</span> (StringUtils.formatPercent(cacheStats.getHitRatio(), 2)+ ", ")) +<a name="line.724"></a>
-<span class="sourceLineNo">725</span> "cachingAccesses=" + cacheStats.getRequestCachingCount() + ", " +<a name="line.725"></a>
-<span class="sourceLineNo">726</span> "cachingHits=" + cacheStats.getHitCachingCount() + ", " +<a name="line.726"></a>
-<span class="sourceLineNo">727</span> "cachingHitsRatio=" +(cacheStats.getHitCachingCount() == 0 ? "0," :<a name="line.727"></a>
-<span class="sourceLineNo">728</span> (StringUtils.formatPercent(cacheStats.getHitCachingRatio(), 2)+ ", ")) +<a name="line.728"></a>
-<span class="sourceLineNo">729</span> "evictions=" + cacheStats.getEvictionCount() + ", " +<a name="line.729"></a>
-<span class="sourceLineNo">730</span> "evicted=" + cacheStats.getEvictedCount() + ", " +<a name="line.730"></a>
-<span class="sourceLineNo">731</span> "evictedPerRun=" + cacheStats.evictedPerEviction());<a name="line.731"></a>
-<span class="sourceLineNo">732</span> cacheStats.reset();<a name="line.732"></a>
-<span class="sourceLineNo">733</span> }<a name="line.733"></a>
-<span class="sourceLineNo">734</span><a name="line.734"></a>
-<span class="sourceLineNo">735</span> public long getRealCacheSize() {<a name="line.735"></a>
-<span class="sourceLineNo">736</span> return this.realCacheSize.sum();<a name="line.736"></a>
-<span class="sourceLineNo">737</span> }<a name="line.737"></a>
-<span class="sourceLineNo">738</span><a name="line.738"></a>
-<span class="sourceLineNo">739</span> public long acceptableSize() {<a name="line.739"></a>
-<span class="sourceLineNo">740</span> return (long) Math.floor(bucketAllocator.getTotalSize() * acceptableFactor);<a name="line.740"></a>
-<span class="sourceLineNo">741</span> }<a name="line.741"></a>
-<span class="sourceLineNo">742</span><a name="line.742"></a>
-<span class="sourceLineNo">743</span> long getPartitionSize(float partitionFactor) {<a name="line.743"></a>
-<span class="sourceLineNo">744</span> return (long) Math.floor(bucketAllocator.getTotalSize() * partitionFactor * minFactor);<a name="line.744"></a>
-<span class="sourceLineNo">745</span> }<a name="line.745"></a>
-<span class="sourceLineNo">746</span><a name="line.746"></a>
-<span class="sourceLineNo">747</span> /**<a name="line.747"></a>
-<span class="sourceLineNo">748</span> * Return the count of bucketSizeinfos still need free space<a name="line.748"></a>
-<span class="sourceLineNo">749</span> */<a name="line.749"></a>
-<span class="sourceLineNo">750</span> private int bucketSizesAboveThresholdCount(float minFactor) {<a name="line.750"></a>
-<span class="sourceLineNo">751</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.751"></a>
-<span class="sourceLineNo">752</span> int fullCount = 0;<a name="line.752"></a>
-<span class="sourceLineNo">753</span> for (int i = 0; i < stats.length; i++) {<a name="line.753"></a>
-<span class="sourceLineNo">754</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.754"></a>
-<span class="sourceLineNo">755</span> freeGoal = Math.max(freeGoal, 1);<a name="line.755"></a>
-<span class="sourceLineNo">756</span> if (stats[i].freeCount() < freeGoal) {<a name="line.756"></a>
-<span class="sourceLineNo">757</span> fullCount++;<a name="line.757"></a>
-<span class="sourceLineNo">758</span> }<a name="line.758"></a>
-<span class="sourceLineNo">759</span> }<a name="line.759"></a>
-<span class="sourceLineNo">760</span> return fullCount;<a name="line.760"></a>
-<span class="sourceLineNo">761</span> }<a name="line.761"></a>
-<span class="sourceLineNo">762</span><a name="line.762"></a>
-<span class="sourceLineNo">763</span> /**<a name="line.763"></a>
-<span class="sourceLineNo">764</span> * This method will find the buckets that are minimally occupied<a name="line.764"></a>
-<span class="sourceLineNo">765</span> * and are not reference counted and will free them completely<a name="line.765"></a>
-<span class="sourceLineNo">766</span> * without any constraint on the access times of the elements,<a name="line.766"></a>
-<span class="sourceLineNo">767</span> * and as a process will completely free at most the number of buckets<a name="line.767"></a>
-<span class="sourceLineNo">768</span> * passed, sometimes it might not due to changing refCounts<a name="line.768"></a>
-<span class="sourceLineNo">769</span> *<a name="line.769"></a>
-<span class="sourceLineNo">770</span> * @param completelyFreeBucketsNeeded number of buckets to free<a name="line.770"></a>
-<span class="sourceLineNo">771</span> **/<a name="line.771"></a>
-<span class="sourceLineNo">772</span> private void freeEntireBuckets(int completelyFreeBucketsNeeded) {<a name="line.772"></a>
-<span class="sourceLineNo">773</span> if (completelyFreeBucketsNeeded != 0) {<a name="line.773"></a>
-<span class="sourceLineNo">774</span> // First we will build a set where the offsets are reference counted, usually<a name="line.774"></a>
-<span class="sourceLineNo">775</span> // this set is small around O(Handler Count) unless something else is wrong<a name="line.775"></a>
-<span class="sourceLineNo">776</span> Set<Integer> inUseBuckets = new HashSet<>();<a name="line.776"></a>
-<span class="sourceLineNo">777</span> backingMap.forEach((k, be) -> {<a name="line.777"></a>
-<span class="sourceLineNo">778</span> if (be.isRpcRef()) {<a name="line.778"></a>
-<span class="sourceLineNo">779</span> inUseBuckets.add(bucketAllocator.getBucketIndex(be.offset()));<a name="line.779"></a>
-<span class="sourceLineNo">780</span> }<a name="line.780"></a>
-<span class="sourceLineNo">781</span> });<a name="line.781"></a>
-<span class="sourceLineNo">782</span> Set<Integer> candidateBuckets =<a name="line.782"></a>
-<span class="sourceLineNo">783</span> bucketAllocator.getLeastFilledBuckets(inUseBuckets, completelyFreeBucketsNeeded);<a name="line.783"></a>
-<span class="sourceLineNo">784</span> for (Map.Entry<BlockCacheKey, BucketEntry> entry : backingMap.entrySet()) {<a name="line.784"></a>
-<span class="sourceLineNo">785</span> if (candidateBuckets.contains(bucketAllocator.getBucketIndex(entry.getValue().offset()))) {<a name="line.785"></a>
-<span class="sourceLineNo">786</span> evictBucketEntryIfNoRpcReferenced(entry.getKey(), entry.getValue());<a name="line.786"></a>
-<span class="sourceLineNo">787</span> }<a name="line.787"></a>
-<span class="sourceLineNo">788</span> }<a name="line.788"></a>
-<span class="sourceLineNo">789</span> }<a name="line.789"></a>
-<span class="sourceLineNo">790</span> }<a name="line.790"></a>
-<span class="sourceLineNo">791</span><a name="line.791"></a>
-<span class="sourceLineNo">792</span> /**<a name="line.792"></a>
-<span class="sourceLineNo">793</span> * Free the space if the used size reaches acceptableSize() or one size block<a name="line.793"></a>
-<span class="sourceLineNo">794</span> * couldn't be allocated. When freeing the space, we use the LRU algorithm and<a name="line.794"></a>
-<span class="sourceLineNo">795</span> * ensure there must be some blocks evicted<a name="line.795"></a>
-<span class="sourceLineNo">796</span> * @param why Why we are being called<a name="line.796"></a>
-<span class="sourceLineNo">797</span> */<a name="line.797"></a>
-<span class="sourceLineNo">798</span> private void freeSpace(final String why) {<a name="line.798"></a>
-<span class="sourceLineNo">799</span> // Ensure only one freeSpace progress at a time<a name="line.799"></a>
-<span class="sourceLineNo">800</span> if (!freeSpaceLock.tryLock()) {<a name="line.800"></a>
-<span class="sourceLineNo">801</span> return;<a name="line.801"></a>
-<span class="sourceLineNo">802</span> }<a name="line.802"></a>
-<span class="sourceLineNo">803</span> try {<a name="line.803"></a>
-<span class="sourceLineNo">804</span> freeInProgress = true;<a name="line.804"></a>
-<span class="sourceLineNo">805</span> long bytesToFreeWithoutExtra = 0;<a name="line.805"></a>
-<span class="sourceLineNo">806</span> // Calculate free byte for each bucketSizeinfo<a name="line.806"></a>
-<span class="sourceLineNo">807</span> StringBuilder msgBuffer = LOG.isDebugEnabled()? new StringBuilder(): null;<a name="line.807"></a>
-<span class="sourceLineNo">808</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.808"></a>
-<span class="sourceLineNo">809</span> long[] bytesToFreeForBucket = new long[stats.length];<a name="line.809"></a>
-<span class="sourceLineNo">810</span> for (int i = 0; i < stats.length; i++) {<a name="line.810"></a>
-<span class="sourceLineNo">811</span> bytesToFreeForBucket[i] = 0;<a name="line.811"></a>
-<span class="sourceLineNo">812</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.812"></a>
-<span class="sourceLineNo">813</span> freeGoal = Math.max(freeGoal, 1);<a name="line.813"></a>
-<span class="sourceLineNo">814</span> if (stats[i].freeCount() < freeGoal) {<a name="line.814"></a>
-<span class="sourceLineNo">815</span> bytesToFreeForBucket[i] = stats[i].itemSize() * (freeGoal - stats[i].freeCount());<a name="line.815"></a>
-<span class="sourceLineNo">816</span> bytesToFreeWithoutExtra += bytesToFreeForBucket[i];<a name="line.816"></a>
-<span class="sourceLineNo">817</span> if (msgBuffer != null) {<a name="line.817"></a>
-<span class="sourceLineNo">818</span> msgBuffer.append("Free for bucketSize(" + stats[i].itemSize() + ")="<a name="line.818"></a>
-<span class="sourceLineNo">819</span> + StringUtils.byteDesc(bytesToFreeForBucket[i]) + ", ");<a name="line.819"></a>
-<span class="sourceLineNo">820</span> }<a name="line.820"></a>
-<span class="sourceLineNo">821</span> }<a name="line.821"></a>
-<span class="sourceLineNo">822</span> }<a name="line.822"></a>
-<span class="sourceLineNo">823</span> if (msgBuffer != null) {<a name="line.823"></a>
-<span class="sourceLineNo">824</span> msgBuffer.append("Free for total=" + StringUtils.byteDesc(bytesToFreeWithoutExtra) + ", ");<a name="line.824"></a>
-<span class="sourceLineNo">825</span> }<a name="line.825"></a>
-<span class="sourceLineNo">826</span><a name="line.826"></a>
-<span class="sourceLineNo">827</span> if (bytesToFreeWithoutExtra <= 0) {<a name="line.827"></a>
-<span class="sourceLineNo">828</span> return;<a name="line.828"></a>
-<span class="sourceLineNo">829</span> }<a name="line.829"></a>
-<span class="sourceLineNo">830</span> long currentSize = bucketAllocator.getUsedSize();<a name="line.830"></a>
-<span class="sourceLineNo">831</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.831"></a>
-<span class="sourceLineNo">832</span> if (LOG.isDebugEnabled() && msgBuffer != null) {<a name="line.832"></a>
-<span class="sourceLineNo">833</span> LOG.debug("Free started because \"" + why + "\"; " + msgBuffer.toString() +<a name="line.833"></a>
-<span class="sourceLineNo">834</span> " of current used=" + StringUtils.byteDesc(currentSize) + ", actual cacheSize=" +<a name="line.834"></a>
-<span class="sourceLineNo">835</span> StringUtils.byteDesc(realCacheSize.sum()) + ", total=" + StringUtils.byteDesc(totalSize));<a name="line.835"></a>
-<span class="sourceLineNo">836</span> }<a name="line.836"></a>
-<span class="sourceLineNo">837</span><a name="line.837"></a>
-<span class="sourceLineNo">838</span> long bytesToFreeWithExtra = (long) Math.floor(bytesToFreeWithoutExtra<a name="line.838"></a>
-<span class="sourceLineNo">839</span> * (1 + extraFreeFactor));<a name="line.839"></a>
-<span class="sourceLineNo">840</span><a name="line.840"></a>
-<span class="sourceLineNo">841</span> // Instantiate priority buckets<a name="line.841"></a>
-<span class="sourceLineNo">842</span> BucketEntryGroup bucketSingle = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.842"></a>
-<span class="sourceLineNo">843</span> blockSize, getPartitionSize(singleFactor));<a name="line.843"></a>
-<span class="sourceLineNo">844</span> BucketEntryGroup bucketMulti = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.844"></a>
-<span class="sourceLineNo">845</span> blockSize, getPartitionSize(multiFactor));<a name="line.845"></a>
-<span class="sourceLineNo">846</span> BucketEntryGroup bucketMemory = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.846"></a>
-<span class="sourceLineNo">847</span> blockSize, getPartitionSize(memoryFactor));<a name="line.847"></a>
-<span class="sourceLineNo">848</span><a name="line.848"></a>
-<span class="sourceLineNo">849</span> // Scan entire map putting bucket entry into appropriate bucket entry<a name="line.849"></a>
-<span class="sourceLineNo">850</span> // group<a name="line.850"></a>
-<span class="sourceLineNo">851</span> for (Map.Entry<BlockCacheKey, BucketEntry> bucketEntryWithKey : backingMap.entrySet()) {<a name="line.851"></a>
-<span class="sourceLineNo">852</span> switch (bucketEntryWithKey.getValue().getPriority()) {<a name="line.852"></a>
-<span class="sourceLineNo">853</span> case SINGLE: {<a name="line.853"></a>
-<span class="sourceLineNo">854</span> bucketSingle.add(bucketEntryWithKey);<a name="line.854"></a>
-<span class="sourceLineNo">855</span> break;<a name="line.855"></a>
-<span class="sourceLineNo">856</span> }<a name="line.856"></a>
-<span class="sourceLineNo">857</span> case MULTI: {<a name="line.857"></a>
-<span class="sourceLineNo">858</span> bucketMulti.add(bucketEntryWithKey);<a name="line.858"></a>
-<span class="sourceLineNo">859</span> break;<a name="line.859"></a>
-<span class="sourceLineNo">860</span> }<a name="line.860"></a>
-<span class="sourceLineNo">861</span> case MEMORY: {<a name="line.861"></a>
-<span class="sourceLineNo">862</span> bucketMemory.add(bucketEntryWithKey);<a name="line.862"></a>
-<span class="sourceLineNo">863</span> break;<a name="line.863"></a>
-<span class="sourceLineNo">864</span> }<a name="line.864"></a>
-<span class="sourceLineNo">865</span> }<a name="line.865"></a>
-<span class="sourceLineNo">866</span> }<a name="line.866"></a>
-<span class="sourceLineNo">867</span><a name="line.867"></a>
-<span class="sourceLineNo">868</span> PriorityQueue<BucketEntryGroup> bucketQueue = new PriorityQueue<>(3,<a name="line.868"></a>
-<span class="sourceLineNo">869</span> Comparator.comparingLong(BucketEntryGroup::overflow));<a name="line.869"></a>
-<span class="sourceLineNo">870</span><a name="line.870"></a>
-<span class="sourceLineNo">871</span> bucketQueue.add(bucketSingle);<a name="line.871"></a>
-<span class="sourceLineNo">872</span> bucketQueue.add(bucketMulti);<a name="line.872"></a>
-<span class="sourceLineNo">873</span> bucketQueue.add(bucketMemory);<a name="line.873"></a>
-<span class="sourceLineNo">874</span><a name="line.874"></a>
-<span class="sourceLineNo">875</span> int remainingBuckets = bucketQueue.size();<a name="line.875"></a>
-<span class="sourceLineNo">876</span> long bytesFreed = 0;<a name="line.876"></a>
-<span class="sourceLineNo">877</span><a name="line.877"></a>
-<span class="sourceLineNo">878</span> BucketEntryGroup bucketGroup;<a name="line.878"></a>
-<span class="sourceLineNo">879</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.879"></a>
-<span class="sourceLineNo">880</span> long overflow = bucketGroup.overflow();<a name="line.880"></a>
-<span class="sourceLineNo">881</span> if (overflow > 0) {<a name="line.881"></a>
-<span class="sourceLineNo">882</span> long bucketBytesToFree = Math.min(overflow,<a name="line.882"></a>
-<span class="sourceLineNo">883</span> (bytesToFreeWithoutExtra - bytesFreed) / remainingBuckets);<a name="line.883"></a>
-<span class="sourceLineNo">884</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.884"></a>
-<span class="sourceLineNo">885</span> }<a name="line.885"></a>
-<span class="sourceLineNo">886</span> remainingBuckets--;<a name="line.886"></a>
-<span class="sourceLineNo">887</span> }<a name="line.887"></a>
-<span class="sourceLineNo">888</span><a name="line.888"></a>
-<span class="sourceLineNo">889</span> // Check and free if there are buckets that still need freeing of space<a name="line.889"></a>
-<span class="sourceLineNo">890</span> if (bucketSizesAboveThresholdCount(minFactor) > 0) {<a name="line.890"></a>
-<span class="sourceLineNo">891</span> bucketQueue.clear();<a name="line.891"></a>
-<span class="sourceLineNo">892</span> remainingBuckets = 3;<a name="line.892"></a>
-<span class="sourceLineNo">893</span><a name="line.893"></a>
-<span class="sourceLineNo">894</span> bucketQueue.add(bucketSingle);<a name="line.894"></a>
-<span class="sourceLineNo">895</span> bucketQueue.add(bucketMulti);<a name="line.895"></a>
-<span class="sourceLineNo">896</span> bucketQueue.add(bucketMemory);<a name="line.896"></a>
-<span class="sourceLineNo">897</span><a name="line.897"></a>
-<span class="sourceLineNo">898</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.898"></a>
-<span class="sourceLineNo">899</span> long bucketBytesToFree = (bytesToFreeWithExtra - bytesFreed) / remainingBuckets;<a name="line.899"></a>
-<span class="sourceLineNo">900</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.900"></a>
-<span class="sourceLineNo">901</span> remainingBuckets--;<a name="line.901"></a>
-<span class="sourceLineNo">902</span> }<a name="line.902"></a>
-<span class="sourceLineNo">903</span> }<a name="line.903"></a>
-<span class="sourceLineNo">904</span><a name="line.904"></a>
-<span class="sourceLineNo">905</span> // Even after the above free we might still need freeing because of the<a name="line.905"></a>
-<span class="sourceLineNo">906</span> // De-fragmentation of the buckets (also called Slab Calcification problem), i.e<a name="line.906"></a>
-<span class="sourceLineNo">907</span> // there might be some buckets where the occupancy is very sparse and thus are not<a name="line.907"></a>
-<span class="sourceLineNo">908</span> // yielding the free for the other bucket sizes, the fix for this to evict some<a name="line.908"></a>
-<span class="sourceLineNo">909</span> // of the buckets, we do this by evicting the buckets that are least fulled<a name="line.909"></a>
-<span class="sourceLineNo">910</span> freeEntireBuckets(DEFAULT_FREE_ENTIRE_BLOCK_FACTOR *<a name="line.910"></a>
-<span class="sourceLineNo">911</span> bucketSizesAboveThresholdCount(1.0f));<a name="line.911"></a>
-<span class="sourceLineNo">912</span><a name="line.912"></a>
-<span class="sourceLineNo">913</span> if (LOG.isDebugEnabled()) {<a name="line.913"></a>
-<span class="sourceLineNo">914</span> long single = bucketSingle.totalSize();<a name="line.914"></a>
-<span class="sourceLineNo">915</span> long multi = bucketMulti.totalSize();<a name="line.915"></a>
-<span class="sourceLineNo">916</span> long memory = bucketMemory.totalSize();<a name="line.916"></a>
-<span class="sourceLineNo">917</span> if (LOG.isDebugEnabled()) {<a name="line.917"></a>
-<span class="sourceLineNo">918</span> LOG.debug("Bucket cache free space completed; " + "freed="<a name="line.918"></a>
-<span class="sourceLineNo">919</span> + StringUtils.byteDesc(bytesFreed) + ", " + "total="<a name="line.919"></a>
-<span class="sourceLineNo">920</span> + StringUtils.byteDesc(totalSize) + ", " + "single="<a name="line.920"></a>
-<span class="sourceLineNo">921</span> + StringUtils.byteDesc(single) + ", " + "multi="<a name="line.921"></a>
-<span class="sourceLineNo">922</span> + StringUtils.byteDesc(multi) + ", " + "memory="<a name="line.922"></a>
-<span class="sourceLineNo">923</span> + StringUtils.byteDesc(memory));<a name="line.923"></a>
-<span class="sourceLineNo">924</span> }<a name="line.924"></a>
-<span class="sourceLineNo">925</span> }<a name="line.925"></a>
-<span class="sourceLineNo">926</span><a name="line.926"></a>
-<span class="sourceLineNo">927</span> } catch (Throwable t) {<a name="line.927"></a>
-<span class="sourceLineNo">928</span> LOG.warn("Failed freeing space", t);<a name="line.928"></a>
-<span class="sourceLineNo">929</span> } finally {<a name="line.929"></a>
-<span class="sourceLineNo">930</span> cacheStats.evict();<a name="line.930"></a>
-<span class="sourceLineNo">931</span> freeInProgress = false;<a name="line.931"></a>
-<span class="sourceLineNo">932</span> freeSpaceLock.unlock();<a name="line.932"></a>
-<span class="sourceLineNo">933</span> }<a name="line.933"></a>
-<span class="sourceLineNo">934</span> }<a name="line.934"></a>
-<span class="sourceLineNo">935</span><a name="line.935"></a>
-<span class="sourceLineNo">936</span> // This handles flushing the RAM cache to IOEngine.<a name="line.936"></a>
-<span class="sourceLineNo">937</span> class WriterThread extends Thread {<a name="line.937"></a>
-<span class="sourceLineNo">938</span> private final BlockingQueue<RAMQueueEntry> inputQueue;<a name="line.938"></a>
-<span class="sourceLineNo">939</span> private volatile boolean writerEnabled = true;<a name="line.939"></a>
-<span class="sourceLineNo">940</span><a name="line.940"></a>
-<span class="sourceLineNo">941</span> WriterThread(BlockingQueue<RAMQueueEntry> queue) {<a name="line.941"></a>
-<span class="sourceLineNo">942</span> super("BucketCacheWriterThread");<a name="line.942"></a>
-<span class="sourceLineNo">943</span> this.inputQueue = queue;<a name="line.943"></a>
-<span class="sourceLineNo">944</span> }<a name="line.944"></a>
-<span class="sourceLineNo">945</span><a name="line.945"></a>
-<span class="sourceLineNo">946</span> // Used for test<a name="line.946"></a>
-<span class="sourceLineNo">947</span> void disableWriter() {<a name="line.947"></a>
-<span class="sourceLineNo">948</span> this.writerEnabled = false;<a name="line.948"></a>
-<span class="sourceLineNo">949</span> }<a name="line.949"></a>
-<span class="sourceLineNo">950</span><a name="line.950"></a>
-<span class="sourceLineNo">951</span> @Override<a name="line.951"></a>
-<span class="sourceLineNo">952</span> public void run() {<a name="line.952"></a>
-<span class="sourceLineNo">953</span> List<RAMQueueEntry> entries = new ArrayList<>();<a name="line.953"></a>
-<span class="sourceLineNo">954</span> try {<a name="line.954"></a>
-<span class="sourceLineNo">955</span> while (cacheEnabled && writerEnabled) {<a name="line.955"></a>
-<span class="sourceLineNo">956</span> try {<a name="line.956"></a>
-<span class="sourceLineNo">957</span> try {<a name="line.957"></a>
-<span class="sourceLineNo">958</span> // Blocks<a name="line.958"></a>
-<span class="sourceLineNo">959</span> entries = getRAMQueueEntries(inputQueue, entries);<a name="line.959"></a>
-<span class="sourceLineNo">960</span> } catch (InterruptedException ie) {<a name="line.960"></a>
-<span class="sourceLineNo">961</span> if (!cacheEnabled || !writerEnabled) {<a name="line.961"></a>
-<span class="sourceLineNo">962</span> break;<a name="line.962"></a>
-<span class="sourceLineNo">963</span> }<a name="line.963"></a>
-<span class="sourceLineNo">964</span> }<a name="line.964"></a>
-<span class="sourceLineNo">965</span> doDrain(entries);<a name="line.965"></a>
-<span class="sourceLineNo">966</span> } catch (Exception ioe) {<a name="line.966"></a>
-<span class="sourceLineNo">967</span> LOG.error("WriterThread encountered error", ioe);<a name="line.967"></a>
-<span class="sourceLineNo">968</span> }<a name="line.968"></a>
-<span class="sourceLineNo">969</span> }<a name="line.969"></a>
-<span class="sourceLineNo">970</span> } catch (Throwable t) {<a name="line.970"></a>
-<span class="sourceLineNo">971</span> LOG.warn("Failed doing drain", t);<a name="line.971"></a>
-<span class="sourceLineNo">972</span> }<a name="line.972"></a>
-<span class="sourceLineNo">973</span> LOG.info(this.getName() + " exiting, cacheEnabled=" + cacheEnabled);<a name="line.973"></a>
-<span class="sourceLineNo">974</span> }<a name="line.974"></a>
-<span class="sourceLineNo">975</span> }<a name="line.975"></a>
-<span class="sourceLineNo">976</span><a name="line.976"></a>
-<span class="sourceLineNo">977</span> /**<a name="line.977"></a>
-<span class="sourceLineNo">978</span> * Put the new bucket entry into backingMap. Notice that we are allowed to replace the existing<a name="line.978"></a>
-<span class="sourceLineNo">979</span> * cache with a new block for the same cache key. there's a corner case: one thread cache a block<a name="line.979"></a>
-<span class="sourceLineNo">980</span> * in ramCache, copy to io-engine and add a bucket entry to backingMap. Caching another new block<a name="line.980"></a>
-<span class="sourceLineNo">981</span> * with the same cache key do the same thing for the same cache key, so if not evict the previous<a name="line.981"></a>
-<span class="sourceLineNo">982</span> * bucket entry, then memory leak happen because the previous bucketEntry is gone but the<a name="line.982"></a>
-<span class="sourceLineNo">983</span> * bucketAllocator do not free its memory.<a name="line.983"></a>
-<span class="sourceLineNo">984</span> * @see BlockCacheUtil#shouldReplaceExistingCacheBlock(BlockCache blockCache,BlockCacheKey<a name="line.984"></a>
-<span class="sourceLineNo">985</span> * cacheKey, Cacheable newBlock)<a name="line.985"></a>
-<span class="sourceLineNo">986</span> * @param key Block cache key<a name="line.986"></a>
-<span class="sourceLineNo">987</span> * @param bucketEntry Bucket entry to put into backingMap.<a name="line.987"></a>
-<span class="sourceLineNo">988</span> */<a name="line.988"></a>
-<span class="sourceLineNo">989</span> protected void putIntoBackingMap(BlockCacheKey key, BucketEntry bucketEntry) {<a name="line.989"></a>
-<span class="sourceLineNo">990</span> BucketEntry previousEntry = backingMap.put(key, bucketEntry);<a name="line.990"></a>
-<span class="sourceLineNo">991</span> if (previousEntry != null && previousEntry != bucketEntry) {<a name="line.991"></a>
-<span class="sourceLineNo">992</span> previousEntry.withWriteLock(offsetLock, () -> {<a name="line.992"></a>
-<span class="sourceLineNo">993</span> blockEvicted(key, previousEntry, false);<a name="line.993"></a>
-<span class="sourceLineNo">994</span> return null;<a name="line.994"></a>
-<span class="sourceLineNo">995</span> });<a name="line.995"></a>
-<span class="sourceLineNo">996</span> }<a name="line.996"></a>
-<span class="sourceLineNo">997</span> }<a name="line.997"></a>
-<span class="sourceLineNo">998</span><a name="line.998"></a>
-<span class="sourceLineNo">999</span> /**<a name="line.999"></a>
-<span class="sourceLineNo">1000</span> * Flush the entries in ramCache to IOEngine and add bucket entry to backingMap. Process all that<a name="line.1000"></a>
-<span class="sourceLineNo">1001</span> * are passed in even if failure being sure to remove from ramCache else we'll never undo the<a name="line.1001"></a>
-<span class="sourceLineNo">1002</span> * references and we'll OOME.<a name="line.1002"></a>
-<span class="sourceLineNo">1003</span> * @param entries Presumes list passed in here will be processed by this invocation only. No<a name="line.1003"></a>
-<span class="sourceLineNo">1004</span> * interference expected.<a name="line.1004"></a>
-<span class="sourceLineNo">1005</span> */<a name="line.1005"></a>
-<span class="sourceLineNo">1006</span> void doDrain(final List<RAMQueueEntry> entries) throws InterruptedException {<a name="line.1006"></a>
-<span class="sourceLineNo">1007</span> if (entries.isEmpty()) {<a name="line.1007"></a>
-<span class="sourceLineNo">1008</span> return;<a name="line.1008"></a>
-<span class="sourceLineNo">1009</span> }<a name="line.1009"></a>
-<span class="sourceLineNo">1010</span> // This method is a little hard to follow. We run through the passed in entries and for each<a name="line.1010"></a>
-<span class="sourceLineNo">1011</span> // successful add, we add a non-null BucketEntry to the below bucketEntries. Later we must<a name="line.1011"></a>
-<span class="sourceLineNo">1012</span> // do cleanup making sure we've cleared ramCache of all entries regardless of whether we<a name="line.1012"></a>
-<span class="sourceLineNo">1013</span> // successfully added the item to the bucketcache; if we don't do the cleanup, we'll OOME by<a name="line.1013"></a>
-<span class="sourceLineNo">1014</span> // filling ramCache. We do the clean up by again running through the passed in entries<a name="line.1014"></a>
-<span class="sourceLineNo">1015</span> // doing extra work when we find a non-null bucketEntries corresponding entry.<a name="line.1015"></a>
-<span class="sourceLineNo">1016</span> final int size = entries.size();<a name="line.1016"></a>
-<span class="sourceLineNo">1017</span> BucketEntry[] bucketEntries = new BucketEntry[size];<a name="line.1017"></a>
-<span class="sourceLineNo">1018</span> // Index updated inside loop if success or if we can't succeed. We retry if cache is full<a name="line.1018"></a>
-<span class="sourceLineNo">1019</span> // when we go to add an entry by going around the loop again without upping the index.<a name="line.1019"></a>
-<span class="sourceLineNo">1020</span> int index = 0;<a name="line.1020"></a>
-<span class="sourceLineNo">1021</span> while (cacheEnabled && index < size) {<a name="line.1021"></a>
-<span class="sourceLineNo">1022</span> RAMQueueEntry re = null;<a name="line.1022"></a>
-<span class="sourceLineNo">1023</span> try {<a name="line.1023"></a>
-<span class="sourceLineNo">1024</span> re = entries.get(index);<a name="line.1024"></a>
-<span class="sourceLineNo">1025</span> if (re == null) {<a name="line.1025"></a>
-<span class="sourceLineNo">1026</span> LOG.warn("Couldn't get entry or changed on us; who else is messing with it?");<a name="line.1026"></a>
-<span class="sourceLineNo">1027</span> index++;<a name="line.1027"></a>
-<span class="sourceLineNo">1028</span> continue;<a name="line.1028"></a>
-<span class="sourceLineNo">1029</span> }<a name="line.1029"></a>
-<span class="sourceLineNo">1030</span> BucketEntry bucketEntry = re.writeToCache(ioEngine, bucketAllocator, realCacheSize,<a name="line.1030"></a>
-<span class="sourceLineNo">1031</span> this::createRecycler);<a name="line.1031"></a>
-<span class="sourceLineNo">1032</span> // Successfully added. Up index and add bucketEntry. Clear io exceptions.<a name="line.1032"></a>
-<span class="sourceLineNo">1033</span> bucketEntries[index] = bucketEntry;<a name="line.1033"></a>
-<span class="sourceLineNo">1034</span> if (ioErrorStartTime > 0) {<a name="line.1034"></a>
-<span class="sourceLineNo">1035</span> ioErrorStartTime = -1;<a name="line.1035"></a>
-<span class="sourceLineNo">1036</span> }<a name="line.1036"></a>
-<span class="sourceLineNo">1037</span> index++;<a name="line.1037"></a>
-<span class="sourceLineNo">1038</span> } catch (BucketAllocatorException fle) {<a name="line.1038"></a>
-<span class="sourceLineNo">1039</span> LOG.warn("Failed allocation for " + (re == null ? "" : re.getKey()) + "; " + fle);<a name="line.1039"></a>
-<span class="sourceLineNo">1040</span> // Presume can't add. Too big? Move index on. Entry will be cleared from ramCache below.<a name="line.1040"></a>
-<span class="sourceLineNo">1041</span> bucketEntries[index] = null;<a name="line.1041"></a>
-<span class="sourceLineNo">1042</span> index++;<a name="line.1042"></a>
-<span class="sourceLineNo">1043</span> } catch (CacheFullException cfe) {<a name="line.1043"></a>
-<span class="sourceLineNo">1044</span> // Cache full when we tried to add. Try freeing space and then retrying (don't up index)<a name="line.1044"></a>
-<span class="sourceLineNo">1045</span> if (!freeInProgress) {<a name="line.1045"></a>
-<span class="sourceLineNo">1046</span> freeSpace("Full!");<a name="line.1046"></a>
-<span class="sourceLineNo">1047</span> } else {<a name="line.1047"></a>
-<span class="sourceLineNo">1048</span> Thread.sleep(50);<a name="line.1048"></a>
-<span class="sourceLineNo">1049</span> }<a name="line.1049"></a>
-<span class="sourceLineNo">1050</span> } catch (IOException ioex) {<a name="line.1050"></a>
-<span class="sourceLineNo">1051</span> // Hopefully transient. Retry. checkIOErrorIsTolerated disables cache if problem.<a name="line.1051"></a>
-<span class="sourceLineNo">1052</span> LOG.error("Failed writing to bucket cache", ioex);<a name="line.1052"></a>
-<span class="sourceLineNo">1053</span> checkIOErrorIsTolerated();<a name="line.1053"></a>
-<span class="sourceLineNo">1054</span> }<a name="line.1054"></a>
-<span class="sourceLineNo">1055</span> }<a name="line.1055"></a>
-<span class="sourceLineNo">1056</span><a name="line.1056"></a>
-<span class="sourceLineNo">1057</span> // Make sure data pages are written on media before we update maps.<a name="line.1057"></a>
-<span class="sourceLineNo">1058</span> try {<a name="line.1058"></a>
-<span class="sourceLineNo">1059</span> ioEngine.sync();<a name="line.1059"></a>
-<span class="sourceLineNo">1060</span> } catch (IOException ioex) {<a name="line.1060"></a>
-<span class="sourceLineNo">1061</span> LOG.error("Failed syncing IO engine", ioex);<a name="line.1061"></a>
-<span class="sourceLineNo">1062</span> checkIOErrorIsTolerated();<a name="line.1062"></a>
-<span class="sourceLineNo">1063</span> // Since we failed sync, free the blocks in bucket allocator<a name="line.1063"></a>
-<span class="sourceLineNo">1064</span> for (int i = 0; i < entries.size(); ++i) {<a name="line.1064"></a>
-<span class="sourceLineNo">1065</span> if (bucketEntries[i] != null) {<a name="line.1065"></a>
-<span class="sourceLineNo">1066</span> bucketAllocator.freeBlock(bucketEntries[i].offset());<a name="line.1066"></a>
-<span class="sourceLineNo">1067</span> bucketEntries[i] = null;<a name="line.1067"></a>
-<span class="sourceLineNo">1068</span> }<a name="line.1068"></a>
-<span class="sourceLineNo">1069</span> }<a name="line.1069"></a>
-<span class="sourceLineNo">1070</span> }<a name="line.1070"></a>
-<span class="sourceLineNo">1071</span><a name="line.1071"></a>
-<span class="sourceLineNo">1072</span> // Now add to backingMap if successfully added to bucket cache. Remove from ramCache if<a name="line.1072"></a>
-<span class="sourceLineNo">1073</span> // success or error.<a name="line.1073"></a>
-<span class="sourceLineNo">1074</span> for (int i = 0; i < size; ++i) {<a name="line.1074"></a>
-<span class="sourceLineNo">1075</span> BlockCacheKey key = entries.get(i).getKey();<a name="line.1075"></a>
-<span class="sourceLineNo">1076</span> // Only add if non-null entry.<a name="line.1076"></a>
-<span class="sourceLineNo">1077</span> if (bucketEntries[i] != null) {<a name="line.1077"></a>
-<span class="sourceLineNo">1078</span> putIntoBackingMap(key, bucketEntries[i]);<a name="line.1078"></a>
-<span class="sourceLineNo">1079</span> }<a name="line.1079"></a>
-<span class="sourceLineNo">1080</span> // Always remove from ramCache even if we failed adding it to the block cache above.<a name="line.1080"></a>
-<span class="sourceLineNo">1081</span> boolean existed = ramCache.remove(key, re -> {<a name="line.1081"></a>
-<span class="sourceLineNo">1082</span> if (re != null) {<a name="line.1082"></a>
-<span class="sourceLineNo">1083</span> heapSize.add(-1 * re.getData().heapSize());<a name="line.1083"></a>
-<span class="sourceLineNo">1084</span> }<a name="line.1084"></a>
-<span class="sourceLineNo">1085</span> });<a name="line.1085"></a>
-<span class="sourceLineNo">1086</span> if (!existed && bucketEntries[i] != null) {<a name="line.1086"></a>
-<span class="sourceLineNo">1087</span> // Block should have already been evicted. Remove it and free space.<a name="line.1087"></a>
-<span class="sourceLineNo">1088</span> final BucketEntry bucketEntry = bucketEntries[i];<a name="line.1088"></a>
-<span class="sourceLineNo">1089</span> bucketEntry.withWriteLock(offsetLock, () -> {<a name="line.1089"></a>
-<span class="sourceLineNo">1090</span> if (backingMap.remove(key, bucketEntry)) {<a name="line.1090"></a>
-<span class="sourceLineNo">1091</span> blockEvicted(key, bucketEntry, false);<a name="line.1091"></a>
-<span class="sourceLineNo">1092</span> }<a name="line.1092"></a>
-<span class="sourceLineNo">1093</span> return null;<a name="line.1093"></a>
-<span class="sourceLineNo">1094</span> });<a name="line.1094"></a>
-<span class="sourceLineNo">1095</span> }<a name="line.1095"></a>
-<span class="sourceLineNo">1096</span> }<a name="line.1096"></a>
-<span class="sourceLineNo">1097</span><a name="line.1097"></a>
-<span class="sourceLineNo">1098</span> long used = bucketAllocator.getUsedSize();<a name="line.1098"></a>
-<span class="sourceLineNo">1099</span> if (used > acceptableSize()) {<a name="line.1099"></a>
-<span class="sourceLineNo">1100</span> freeSpace("Used=" + used + " > acceptable=" + acceptableSize());<a name="line.1100"></a>
-<span class="sourceLineNo">1101</span> }<a name="line.1101"></a>
-<span class="sourceLineNo">1102</span> return;<a name="line.1102"></a>
-<span class="sourceLineNo">1103</span> }<a name="line.1103"></a>
-<span class="sourceLineNo">1104</span><a name="line.1104"></a>
-<span class="sourceLineNo">1105</span> /**<a name="line.1105"></a>
-<span class="sourceLineNo">1106</span> * Blocks until elements available in {@code q} then tries to grab as many as possible before<a name="line.1106"></a>
-<span class="sourceLineNo">1107</span> * returning.<a name="line.1107"></a>
-<span class="sourceLineNo">1108</span> * @param receptacle Where to stash the elements taken from queue. We clear before we use it just<a name="line.1108"></a>
-<span class="sourceLineNo">1109</span> * in case.<a name="line.1109"></a>
-<span class="sourceLineNo">1110</span> * @param q The queue to take from.<a name="line.1110"></a>
-<span class="sourceLineNo">1111</span> * @return {@code receptacle} laden with elements taken from the queue or empty if none found.<a name="line.1111"></a>
-<span class="sourceLineNo">1112</span> */<a name="line.1112"></a>
-<span class="sourceLineNo">1113</span> static List<RAMQueueEntry> getRAMQueueEntries(BlockingQueue<RAMQueueEntry> q,<a name="line.1113"></a>
-<span class="sourceLineNo">1114</span> List<RAMQueueEntry> receptacle) throws InterruptedException {<a name="line.1114"></a>
-<span class="sourceLineNo">1115</span> // Clear sets all entries to null and sets size to 0. We retain allocations. Presume it<a name="line.1115"></a>
-<span class="sourceLineNo">1116</span> // ok even if list grew to accommodate thousands.<a name="line.1116"></a>
-<span class="sourceLineNo">1117</span> receptacle.clear();<a name="line.1117"></a>
-<span class="sourceLineNo">1118</span> receptacle.add(q.take());<a name="line.1118"></a>
-<span class="sourceLineNo">1119</span> q.drainTo(receptacle);<a name="line.1119"></a>
-<span class="sourceLineNo">1120</span> return receptacle;<a name="line.1120"></a>
-<span class="sourceLineNo">1121</span> }<a name="line.1121"></a>
-<span class="sourceLineNo">1122</span><a name="line.1122"></a>
-<span class="sourceLineNo">1123</span> /**<a name="line.1123"></a>
-<span class="sourceLineNo">1124</span> * @see #retrieveFromFile(int[])<a name="line.1124"></a>
-<span class="sourceLineNo">1125</span> */<a name="line.1125"></a>
-<span class="sourceLineNo">1126</span> @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="OBL_UNSATISFIED_OBLIGATION",<a name="line.1126"></a>
-<span class="sourceLineNo">1127</span> justification = "false positive, try-with-resources ensures close is called.")<a name="line.1127"></a>
-<span class="sourceLineNo">1128</span> private void persistToFile() throws IOException {<a name="line.1128"></a>
-<span class="sourceLineNo">1129</span> assert !cacheEnabled;<a name="line.1129"></a>
-<span class="sourceLineNo">1130</span> if (!ioEngine.isPersistent()) {<a name="line.1130"></a>
-<span class="sourceLineNo">1131</span> throw new IOException("Attempt to persist non-persistent cache mappings!");<a name="line.1131"></a>
-<span class="sourceLineNo">1132</span> }<a name="line.1132"></a>
-<span class="sourceLineNo">1133</span> try (FileOutputStream fos = new FileOutputStream(persistencePath, false)) {<a name="line.1133"></a>
-<span class="sourceLineNo">1134</span> fos.write(ProtobufMagic.PB_MAGIC);<a name="line.1134"></a>
-<span class="sourceLineNo">1135</span> BucketProtoUtils.toPB(this).writeDelimitedTo(fos);<a name="line.1135"></a>
+<span class="sourceLineNo">489</span> }<a name="line.489"></a>
+<span class="sourceLineNo">490</span> }<a name="line.490"></a>
+<span class="sourceLineNo">491</span><a name="line.491"></a>
+<span class="sourceLineNo">492</span> /**<a name="line.492"></a>
+<span class="sourceLineNo">493</span> * Get the buffer of the block with the specified key.<a name="line.493"></a>
+<span class="sourceLineNo">494</span> * @param key block's cache key<a name="line.494"></a>
+<span class="sourceLineNo">495</span> * @param caching true if the caller caches blocks on cache misses<a name="line.495"></a>
+<span class="sourceLineNo">496</span> * @param repeat Whether this is a repeat lookup for the same block<a name="line.496"></a>
+<span class="sourceLineNo">497</span> * @param updateCacheMetrics Whether we should update cache metrics or not<a name="line.497"></a>
+<span class="sourceLineNo">498</span> * @return buffer of specified cache key, or null if not in cache<a name="line.498"></a>
+<span class="sourceLineNo">499</span> */<a name="line.499"></a>
+<span class="sourceLineNo">500</span> @Override<a name="line.500"></a>
+<span class="sourceLineNo">501</span> public Cacheable getBlock(BlockCacheKey key, boolean caching, boolean repeat,<a name="line.501"></a>
+<span class="sourceLineNo">502</span> boolean updateCacheMetrics) {<a name="line.502"></a>
+<span class="sourceLineNo">503</span> if (!cacheEnabled) {<a name="line.503"></a>
+<span class="sourceLineNo">504</span> return null;<a name="line.504"></a>
+<span class="sourceLineNo">505</span> }<a name="line.505"></a>
+<span class="sourceLineNo">506</span> RAMQueueEntry re = ramCache.get(key);<a name="line.506"></a>
+<span class="sourceLineNo">507</span> if (re != null) {<a name="line.507"></a>
+<span class="sourceLineNo">508</span> if (updateCacheMetrics) {<a name="line.508"></a>
+<span class="sourceLineNo">509</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.509"></a>
+<span class="sourceLineNo">510</span> }<a name="line.510"></a>
+<span class="sourceLineNo">511</span> re.access(accessCount.incrementAndGet());<a name="line.511"></a>
+<span class="sourceLineNo">512</span> return re.getData();<a name="line.512"></a>
+<span class="sourceLineNo">513</span> }<a name="line.513"></a>
+<span class="sourceLineNo">514</span> BucketEntry bucketEntry = backingMap.get(key);<a name="line.514"></a>
+<span class="sourceLineNo">515</span> if (bucketEntry != null) {<a name="line.515"></a>
+<span class="sourceLineNo">516</span> long start = System.nanoTime();<a name="line.516"></a>
+<span class="sourceLineNo">517</span> ReentrantReadWriteLock lock = offsetLock.getLock(bucketEntry.offset());<a name="line.517"></a>
+<span class="sourceLineNo">518</span> try {<a name="line.518"></a>
+<span class="sourceLineNo">519</span> lock.readLock().lock();<a name="line.519"></a>
+<span class="sourceLineNo">520</span> // We can not read here even if backingMap does contain the given key because its offset<a name="line.520"></a>
+<span class="sourceLineNo">521</span> // maybe changed. If we lock BlockCacheKey instead of offset, then we can only check<a name="line.521"></a>
+<span class="sourceLineNo">522</span> // existence here.<a name="line.522"></a>
+<span class="sourceLineNo">523</span> if (bucketEntry.equals(backingMap.get(key))) {<a name="line.523"></a>
+<span class="sourceLineNo">524</span> // Read the block from IOEngine based on the bucketEntry's offset and length, NOTICE: the<a name="line.524"></a>
+<span class="sourceLineNo">525</span> // block will use the refCnt of bucketEntry, which means if two HFileBlock mapping to<a name="line.525"></a>
+<span class="sourceLineNo">526</span> // the same BucketEntry, then all of the three will share the same refCnt.<a name="line.526"></a>
+<span class="sourceLineNo">527</span> Cacheable cachedBlock = ioEngine.read(bucketEntry);<a name="line.527"></a>
+<span class="sourceLineNo">528</span> if (ioEngine.usesSharedMemory()) {<a name="line.528"></a>
+<span class="sourceLineNo">529</span> // If IOEngine use shared memory, cachedBlock and BucketEntry will share the<a name="line.529"></a>
+<span class="sourceLineNo">530</span> // same RefCnt, do retain here, in order to count the number of RPC references<a name="line.530"></a>
+<span class="sourceLineNo">531</span> cachedBlock.retain();<a name="line.531"></a>
+<span class="sourceLineNo">532</span> }<a name="line.532"></a>
+<span class="sourceLineNo">533</span> // Update the cache statistics.<a name="line.533"></a>
+<span class="sourceLineNo">534</span> if (updateCacheMetrics) {<a name="line.534"></a>
+<span class="sourceLineNo">535</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.535"></a>
+<span class="sourceLineNo">536</span> cacheStats.ioHit(System.nanoTime() - start);<a name="line.536"></a>
+<span class="sourceLineNo">537</span> }<a name="line.537"></a>
+<span class="sourceLineNo">538</span> bucketEntry.access(accessCount.incrementAndGet());<a name="line.538"></a>
+<span class="sourceLineNo">539</span> if (this.ioErrorStartTime > 0) {<a name="line.539"></a>
+<span class="sourceLineNo">540</span> ioErrorStartTime = -1;<a name="line.540"></a>
+<span class="sourceLineNo">541</span> }<a name="line.541"></a>
+<span class="sourceLineNo">542</span> return cachedBlock;<a name="line.542"></a>
+<span class="sourceLineNo">543</span> }<a name="line.543"></a>
+<span class="sourceLineNo">544</span> } catch (IOException ioex) {<a name="line.544"></a>
+<span class="sourceLineNo">545</span> LOG.error("Failed reading block " + key + " from bucket cache", ioex);<a name="line.545"></a>
+<span class="sourceLineNo">546</span> checkIOErrorIsTolerated();<a name="line.546"></a>
+<span class="sourceLineNo">547</span> } finally {<a name="line.547"></a>
+<span class="sourceLineNo">548</span> lock.readLock().unlock();<a name="line.548"></a>
+<span class="sourceLineNo">549</span> }<a name="line.549"></a>
+<span class="sourceLineNo">550</span> }<a name="line.550"></a>
+<span class="sourceLineNo">551</span> if (!repeat && updateCacheMetrics) {<a name="line.551"></a>
+<span class="sourceLineNo">552</span> cacheStats.miss(caching, key.isPrimary(), key.getBlockType());<a name="line.552"></a>
+<span class="sourceLineNo">553</span> }<a name="line.553"></a>
+<span class="sourceLineNo">554</span> return null;<a name="line.554"></a>
+<span class="sourceLineNo">555</span> }<a name="line.555"></a>
+<span class="sourceLineNo">556</span><a name="line.556"></a>
+<span class="sourceLineNo">557</span> /**<a name="line.557"></a>
+<span class="sourceLineNo">558</span> * This method is invoked after the bucketEntry is removed from {@link BucketCache#backingMap}<a name="line.558"></a>
+<span class="sourceLineNo">559</span> */<a name="line.559"></a>
+<span class="sourceLineNo">560</span> void blockEvicted(BlockCacheKey cacheKey, BucketEntry bucketEntry, boolean decrementBlockNumber) {<a name="line.560"></a>
+<span class="sourceLineNo">561</span> bucketEntry.markAsEvicted();<a name="line.561"></a>
+<span class="sourceLineNo">562</span> blocksByHFile.remove(cacheKey);<a name="line.562"></a>
+<span class="sourceLineNo">563</span> if (decrementBlockNumber) {<a name="line.563"></a>
+<span class="sourceLineNo">564</span> this.blockNumber.decrement();<a name="line.564"></a>
+<span class="sourceLineNo">565</span> }<a name="line.565"></a>
+<span class="sourceLineNo">566</span> cacheStats.evicted(bucketEntry.getCachedTime(), cacheKey.isPrimary());<a name="line.566"></a>
+<span class="sourceLineNo">567</span> }<a name="line.567"></a>
+<span class="sourceLineNo">568</span><a name="line.568"></a>
+<span class="sourceLineNo">569</span> /**<a name="line.569"></a>
+<span class="sourceLineNo">570</span> * Free the {{@link BucketEntry} actually,which could only be invoked when the<a name="line.570"></a>
+<span class="sourceLineNo">571</span> * {@link BucketEntry#refCnt} becoming 0.<a name="line.571"></a>
+<span class="sourceLineNo">572</span> */<a name="line.572"></a>
+<span class="sourceLineNo">573</span> void freeBucketEntry(BucketEntry bucketEntry) {<a name="line.573"></a>
+<span class="sourceLineNo">574</span> bucketAllocator.freeBlock(bucketEntry.offset());<a name="line.574"></a>
+<span class="sourceLineNo">575</span> realCacheSize.add(-1 * bucketEntry.getLength());<a name="line.575"></a>
+<span class="sourceLineNo">576</span> }<a name="line.576"></a>
+<span class="sourceLineNo">577</span><a name="line.577"></a>
+<span class="sourceLineNo">578</span> /**<a name="line.578"></a>
+<span class="sourceLineNo">579</span> * Try to evict the block from {@link BlockCache} by force. We'll call this in few cases:<br><a name="line.579"></a>
+<span class="sourceLineNo">580</span> * 1. Close an HFile, and clear all cached blocks. <br><a name="line.580"></a>
+<span class="sourceLineNo">581</span> * 2. Call {@link Admin#clearBlockCache(TableName)} to clear all blocks for a given table.<br><a name="line.581"></a>
+<span class="sourceLineNo">582</span> * <p><a name="line.582"></a>
+<span class="sourceLineNo">583</span> * Firstly, we'll try to remove the block from RAMCache,and then try to evict from backingMap.<a name="line.583"></a>
+<span class="sourceLineNo">584</span> * Here we evict the block from backingMap immediately, but only free the reference from bucket<a name="line.584"></a>
+<span class="sourceLineNo">585</span> * cache by calling {@link BucketEntry#markedAsEvicted}. If there're still some RPC referring this<a name="line.585"></a>
+<span class="sourceLineNo">586</span> * block, block can only be de-allocated when all of them release the block.<a name="line.586"></a>
+<span class="sourceLineNo">587</span> * <p><a name="line.587"></a>
+<span class="sourceLineNo">588</span> * NOTICE: we need to grab the write offset lock firstly before releasing the reference from<a name="line.588"></a>
+<span class="sourceLineNo">589</span> * bucket cache. if we don't, we may read an {@link BucketEntry} with refCnt = 0 when<a name="line.589"></a>
+<span class="sourceLineNo">590</span> * {@link BucketCache#getBlock(BlockCacheKey, boolean, boolean, boolean)}, it's a memory leak.<a name="line.590"></a>
+<span class="sourceLineNo">591</span> * @param cacheKey Block to evict<a name="line.591"></a>
+<span class="sourceLineNo">592</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.592"></a>
+<span class="sourceLineNo">593</span> */<a name="line.593"></a>
+<span class="sourceLineNo">594</span> @Override<a name="line.594"></a>
+<span class="sourceLineNo">595</span> public boolean evictBlock(BlockCacheKey cacheKey) {<a name="line.595"></a>
+<span class="sourceLineNo">596</span> return doEvictBlock(cacheKey, null);<a name="line.596"></a>
+<span class="sourceLineNo">597</span> }<a name="line.597"></a>
+<span class="sourceLineNo">598</span><a name="line.598"></a>
+<span class="sourceLineNo">599</span> /**<a name="line.599"></a>
+<span class="sourceLineNo">600</span> * Evict the {@link BlockCacheKey} and {@link BucketEntry} from {@link BucketCache#backingMap} and<a name="line.600"></a>
+<span class="sourceLineNo">601</span> * {@link BucketCache#ramCache}. <br/><a name="line.601"></a>
+<span class="sourceLineNo">602</span> * NOTE:When Evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.602"></a>
+<span class="sourceLineNo">603</span> * {@link BucketEntry} could be removed.<a name="line.603"></a>
+<span class="sourceLineNo">604</span> * @param cacheKey {@link BlockCacheKey} to evict.<a name="line.604"></a>
+<span class="sourceLineNo">605</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.605"></a>
+<span class="sourceLineNo">606</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.606"></a>
+<span class="sourceLineNo">607</span> */<a name="line.607"></a>
+<span class="sourceLineNo">608</span> private boolean doEvictBlock(BlockCacheKey cacheKey, BucketEntry bucketEntry) {<a name="line.608"></a>
+<span class="sourceLineNo">609</span> if (!cacheEnabled) {<a name="line.609"></a>
+<span class="sourceLineNo">610</span> return false;<a name="line.610"></a>
+<span class="sourceLineNo">611</span> }<a name="line.611"></a>
+<span class="sourceLineNo">612</span> boolean existedInRamCache = removeFromRamCache(cacheKey);<a name="line.612"></a>
+<span class="sourceLineNo">613</span> if (bucketEntry == null) {<a name="line.613"></a>
+<span class="sourceLineNo">614</span> bucketEntry = backingMap.get(cacheKey);<a name="line.614"></a>
+<span class="sourceLineNo">615</span> }<a name="line.615"></a>
+<span class="sourceLineNo">616</span> final BucketEntry bucketEntryToUse = bucketEntry;<a name="line.616"></a>
+<span class="sourceLineNo">617</span><a name="line.617"></a>
+<span class="sourceLineNo">618</span> if (bucketEntryToUse == null) {<a name="line.618"></a>
+<span class="sourceLineNo">619</span> if (existedInRamCache) {<a name="line.619"></a>
+<span class="sourceLineNo">620</span> cacheStats.evicted(0, cacheKey.isPrimary());<a name="line.620"></a>
+<span class="sourceLineNo">621</span> }<a name="line.621"></a>
+<span class="sourceLineNo">622</span> return existedInRamCache;<a name="line.622"></a>
+<span class="sourceLineNo">623</span> } else {<a name="line.623"></a>
+<span class="sourceLineNo">624</span> return bucketEntryToUse.withWriteLock(offsetLock, () -> {<a name="line.624"></a>
+<span class="sourceLineNo">625</span> if (backingMap.remove(cacheKey, bucketEntryToUse)) {<a name="line.625"></a>
+<span class="sourceLineNo">626</span> blockEvicted(cacheKey, bucketEntryToUse, !existedInRamCache);<a name="line.626"></a>
+<span class="sourceLineNo">627</span> return true;<a name="line.627"></a>
+<span class="sourceLineNo">628</span> }<a name="line.628"></a>
+<span class="sourceLineNo">629</span> return false;<a name="line.629"></a>
+<span class="sourceLineNo">630</span> });<a name="line.630"></a>
+<span class="sourceLineNo">631</span> }<a name="line.631"></a>
+<span class="sourceLineNo">632</span> }<a name="line.632"></a>
+<span class="sourceLineNo">633</span><a name="line.633"></a>
+<span class="sourceLineNo">634</span> /**<a name="line.634"></a>
+<span class="sourceLineNo">635</span> * <pre><a name="line.635"></a>
+<span class="sourceLineNo">636</span> * Create the {@link Recycler} for {@link BucketEntry#refCnt},which would be used as<a name="line.636"></a>
+<span class="sourceLineNo">637</span> * {@link RefCnt#recycler} of {@link HFileBlock#buf} returned from {@link BucketCache#getBlock}.<a name="line.637"></a>
+<span class="sourceLineNo">638</span> * NOTE: for {@link BucketCache#getBlock},the {@link RefCnt#recycler} of {@link HFileBlock#buf}<a name="line.638"></a>
+<span class="sourceLineNo">639</span> * from {@link BucketCache#backingMap} and {@link BucketCache#ramCache} are different:<a name="line.639"></a>
+<span class="sourceLineNo">640</span> * 1.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#backingMap},<a name="line.640"></a>
+<span class="sourceLineNo">641</span> * it is the return value of current {@link BucketCache#createRecycler} method.<a name="line.641"></a>
+<span class="sourceLineNo">642</span> *<a name="line.642"></a>
+<span class="sourceLineNo">643</span> * 2.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#ramCache},<a name="line.643"></a>
+<span class="sourceLineNo">644</span> * it is {@link ByteBuffAllocator#putbackBuffer}.<a name="line.644"></a>
+<span class="sourceLineNo">645</span> * </pre><a name="line.645"></a>
+<span class="sourceLineNo">646</span> */<a name="line.646"></a>
+<span class="sourceLineNo">647</span> private Recycler createRecycler(final BucketEntry bucketEntry) {<a name="line.647"></a>
+<span class="sourceLineNo">648</span> return () -> {<a name="line.648"></a>
+<span class="sourceLineNo">649</span> freeBucketEntry(bucketEntry);<a name="line.649"></a>
+<span class="sourceLineNo">650</span> return;<a name="line.650"></a>
+<span class="sourceLineNo">651</span> };<a name="line.651"></a>
+<span class="sourceLineNo">652</span> }<a name="line.652"></a>
+<span class="sourceLineNo">653</span><a name="line.653"></a>
+<span class="sourceLineNo">654</span> /**<a name="line.654"></a>
+<span class="sourceLineNo">655</span> * NOTE: This method is only for test.<a name="line.655"></a>
+<span class="sourceLineNo">656</span> */<a name="line.656"></a>
+<span class="sourceLineNo">657</span> public boolean evictBlockIfNoRpcReferenced(BlockCacheKey blockCacheKey) {<a name="line.657"></a>
+<span class="sourceLineNo">658</span> BucketEntry bucketEntry = backingMap.get(blockCacheKey);<a name="line.658"></a>
+<span class="sourceLineNo">659</span> if (bucketEntry == null) {<a name="line.659"></a>
+<span class="sourceLineNo">660</span> return false;<a name="line.660"></a>
+<span class="sourceLineNo">661</span> }<a name="line.661"></a>
+<span class="sourceLineNo">662</span> return evictBucketEntryIfNoRpcReferenced(blockCacheKey, bucketEntry);<a name="line.662"></a>
+<span class="sourceLineNo">663</span> }<a name="line.663"></a>
+<span class="sourceLineNo">664</span><a name="line.664"></a>
+<span class="sourceLineNo">665</span> /**<a name="line.665"></a>
+<span class="sourceLineNo">666</span> * Evict {@link BlockCacheKey} and its corresponding {@link BucketEntry} only if<a name="line.666"></a>
+<span class="sourceLineNo">667</span> * {@link BucketEntry#isRpcRef} is false. <br/><a name="line.667"></a>
+<span class="sourceLineNo">668</span> * NOTE:When evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.668"></a>
+<span class="sourceLineNo">669</span> * {@link BucketEntry} could be removed.<a name="line.669"></a>
+<span class="sourceLineNo">670</span> * @param blockCacheKey {@link BlockCacheKey} to evict.<a name="line.670"></a>
+<span class="sourceLineNo">671</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.671"></a>
+<span class="sourceLineNo">672</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.672"></a>
+<span class="sourceLineNo">673</span> */<a name="line.673"></a>
+<span class="sourceLineNo">674</span> boolean evictBucketEntryIfNoRpcReferenced(BlockCacheKey blockCacheKey, BucketEntry bucketEntry) {<a name="line.674"></a>
+<span class="sourceLineNo">675</span> if (!bucketEntry.isRpcRef()) {<a name="line.675"></a>
+<span class="sourceLineNo">676</span> return doEvictBlock(blockCacheKey, bucketEntry);<a name="line.676"></a>
+<span class="sourceLineNo">677</span> }<a name="line.677"></a>
+<span class="sourceLineNo">678</span> return false;<a name="line.678"></a>
+<span class="sourceLineNo">679</span> }<a name="line.679"></a>
+<span class="sourceLineNo">680</span><a name="line.680"></a>
+<span class="sourceLineNo">681</span> protected boolean removeFromRamCache(BlockCacheKey cacheKey) {<a name="line.681"></a>
+<span class="sourceLineNo">682</span> return ramCache.remove(cacheKey, re -> {<a name="line.682"></a>
+<span class="sourceLineNo">683</span> if (re != null) {<a name="line.683"></a>
+<span class="sourceLineNo">684</span> this.blockNumber.decrement();<a name="line.684"></a>
+<span class="sourceLineNo">685</span> this.heapSize.add(-1 * re.getData().heapSize());<a name="line.685"></a>
+<span class="sourceLineNo">686</span> }<a name="line.686"></a>
+<span class="sourceLineNo">687</span> });<a name="line.687"></a>
+<span class="sourceLineNo">688</span> }<a name="line.688"></a>
+<span class="sourceLineNo">689</span><a name="line.689"></a>
+<span class="sourceLineNo">690</span> /*<a name="line.690"></a>
+<span class="sourceLineNo">691</span> * Statistics thread. Periodically output cache statistics to the log.<a name="line.691"></a>
+<span class="sourceLineNo">692</span> */<a name="line.692"></a>
+<span class="sourceLineNo">693</span> private static class StatisticsThread extends Thread {<a name="line.693"></a>
+<span class="sourceLineNo">694</span> private final BucketCache bucketCache;<a name="line.694"></a>
+<span class="sourceLineNo">695</span><a name="line.695"></a>
+<span class="sourceLineNo">696</span> public StatisticsThread(BucketCache bucketCache) {<a name="line.696"></a>
+<span class="sourceLineNo">697</span> super("BucketCacheStatsThread");<a name="line.697"></a>
+<span class="sourceLineNo">698</span> setDaemon(true);<a name="line.698"></a>
+<span class="sourceLineNo">699</span> this.bucketCache = bucketCache;<a name="line.699"></a>
+<span class="sourceLineNo">700</span> }<a name="line.700"></a>
+<span class="sourceLineNo">701</span><a name="line.701"></a>
+<span class="sourceLineNo">702</span> @Override<a name="line.702"></a>
+<span class="sourceLineNo">703</span> public void run() {<a name="line.703"></a>
+<span class="sourceLineNo">704</span> bucketCache.logStats();<a name="line.704"></a>
+<span class="sourceLineNo">705</span> }<a name="line.705"></a>
+<span class="sourceLineNo">706</span> }<a name="line.706"></a>
+<span class="sourceLineNo">707</span><a name="line.707"></a>
+<span class="sourceLineNo">708</span> public void logStats() {<a name="line.708"></a>
+<span class="sourceLineNo">709</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.709"></a>
+<span class="sourceLineNo">710</span> long usedSize = bucketAllocator.getUsedSize();<a name="line.710"></a>
+<span class="sourceLineNo">711</span> long freeSize = totalSize - usedSize;<a name="line.711"></a>
+<span class="sourceLineNo">712</span> long cacheSize = getRealCacheSize();<a name="line.712"></a>
+<span class="sourceLineNo">713</span> LOG.info("failedBlockAdditions=" + cacheStats.getFailedInserts() + ", " +<a name="line.713"></a>
+<span class="sourceLineNo">714</span> "totalSize=" + StringUtils.byteDesc(totalSize) + ", " +<a name="line.714"></a>
+<span class="sourceLineNo">715</span> "freeSize=" + StringUtils.byteDesc(freeSize) + ", " +<a name="line.715"></a>
+<span class="sourceLineNo">716</span> "usedSize=" + StringUtils.byteDesc(usedSize) +", " +<a name="line.716"></a>
+<span class="sourceLineNo">717</span> "cacheSize=" + StringUtils.byteDesc(cacheSize) +", " +<a name="line.717"></a>
+<span class="sourceLineNo">718</span> "accesses=" + cacheStats.getRequestCount() + ", " +<a name="line.718"></a>
+<span class="sourceLineNo">719</span> "hits=" + cacheStats.getHitCount() + ", " +<a name="line.719"></a>
+<span class="sourceLineNo">720</span> "IOhitsPerSecond=" + cacheStats.getIOHitsPerSecond() + ", " +<a name="line.720"></a>
+<span class="sourceLineNo">721</span> "IOTimePerHit=" + String.format("%.2f", cacheStats.getIOTimePerHit())+ ", " +<a name="line.721"></a>
+<span class="sourceLineNo">722</span> "hitRatio=" + (cacheStats.getHitCount() == 0 ? "0," :<a name="line.722"></a>
+<span class="sourceLineNo">723</span> (StringUtils.formatPercent(cacheStats.getHitRatio(), 2)+ ", ")) +<a name="line.723"></a>
+<span class="sourceLineNo">724</span> "cachingAccesses=" + cacheStats.getRequestCachingCount() + ", " +<a name="line.724"></a>
+<span class="sourceLineNo">725</span> "cachingHits=" + cacheStats.getHitCachingCount() + ", " +<a name="line.725"></a>
+<span class="sourceLineNo">726</span> "cachingHitsRatio=" +(cacheStats.getHitCachingCount() == 0 ? "0," :<a name="line.726"></a>
+<span class="sourceLineNo">727</span> (StringUtils.formatPercent(cacheStats.getHitCachingRatio(), 2)+ ", ")) +<a name="line.727"></a>
+<span class="sourceLineNo">728</span> "evictions=" + cacheStats.getEvictionCount() + ", " +<a name="line.728"></a>
+<span class="sourceLineNo">729</span> "evicted=" + cacheStats.getEvictedCount() + ", " +<a name="line.729"></a>
+<span class="sourceLineNo">730</span> "evictedPerRun=" + cacheStats.evictedPerEviction());<a name="line.730"></a>
+<span class="sourceLineNo">731</span> cacheStats.reset();<a name="line.731"></a>
+<span class="sourceLineNo">732</span> }<a name="line.732"></a>
+<span class="sourceLineNo">733</span><a name="line.733"></a>
+<span class="sourceLineNo">734</span> public long getRealCacheSize() {<a name="line.734"></a>
+<span class="sourceLineNo">735</span> return this.realCacheSize.sum();<a name="line.735"></a>
+<span class="sourceLineNo">736</span> }<a name="line.736"></a>
+<span class="sourceLineNo">737</span><a name="line.737"></a>
+<span class="sourceLineNo">738</span> public long acceptableSize() {<a name="line.738"></a>
+<span class="sourceLineNo">739</span> return (long) Math.floor(bucketAllocator.getTotalSize() * acceptableFactor);<a name="line.739"></a>
+<span class="sourceLineNo">740</span> }<a name="line.740"></a>
+<span class="sourceLineNo">741</span><a name="line.741"></a>
+<span class="sourceLineNo">742</span> long getPartitionSize(float partitionFactor) {<a name="line.742"></a>
+<span class="sourceLineNo">743</span> return (long) Math.floor(bucketAllocator.getTotalSize() * partitionFactor * minFactor);<a name="line.743"></a>
+<span class="sourceLineNo">744</span> }<a name="line.744"></a>
+<span class="sourceLineNo">745</span><a name="line.745"></a>
+<span class="sourceLineNo">746</span> /**<a name="line.746"></a>
+<span class="sourceLineNo">747</span> * Return the count of bucketSizeinfos still need free space<a name="line.747"></a>
+<span class="sourceLineNo">748</span> */<a name="line.748"></a>
+<span class="sourceLineNo">749</span> private int bucketSizesAboveThresholdCount(float minFactor) {<a name="line.749"></a>
+<span class="sourceLineNo">750</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.750"></a>
+<span class="sourceLineNo">751</span> int fullCount = 0;<a name="line.751"></a>
+<span class="sourceLineNo">752</span> for (int i = 0; i < stats.length; i++) {<a name="line.752"></a>
+<span class="sourceLineNo">753</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.753"></a>
+<span class="sourceLineNo">754</span> freeGoal = Math.max(freeGoal, 1);<a name="line.754"></a>
+<span class="sourceLineNo">755</span> if (stats[i].freeCount() < freeGoal) {<a name="line.755"></a>
+<span class="sourceLineNo">756</span> fullCount++;<a name="line.756"></a>
+<span class="sourceLineNo">757</span> }<a name="line.757"></a>
+<span class="sourceLineNo">758</span> }<a name="line.758"></a>
+<span class="sourceLineNo">759</span> return fullCount;<a name="line.759"></a>
+<span class="sourceLineNo">760</span> }<a name="line.760"></a>
+<span class="sourceLineNo">761</span><a name="line.761"></a>
+<span class="sourceLineNo">762</span> /**<a name="line.762"></a>
+<span class="sourceLineNo">763</span> * This method will find the buckets that are minimally occupied<a name="line.763"></a>
+<span class="sourceLineNo">764</span> * and are not reference counted and will free them completely<a name="line.764"></a>
+<span class="sourceLineNo">765</span> * without any constraint on the access times of the elements,<a name="line.765"></a>
+<span class="sourceLineNo">766</span> * and as a process will completely free at most the number of buckets<a name="line.766"></a>
+<span class="sourceLineNo">767</span> * passed, sometimes it might not due to changing refCounts<a name="line.767"></a>
+<span class="sourceLineNo">768</span> *<a name="line.768"></a>
+<span class="sourceLineNo">769</span> * @param completelyFreeBucketsNeeded number of buckets to free<a name="line.769"></a>
+<span class="sourceLineNo">770</span> **/<a name="line.770"></a>
+<span class="sourceLineNo">771</span> private void freeEntireBuckets(int completelyFreeBucketsNeeded) {<a name="line.771"></a>
+<span class="sourceLineNo">772</span> if (completelyFreeBucketsNeeded != 0) {<a name="line.772"></a>
+<span class="sourceLineNo">773</span> // First we will build a set where the offsets are reference counted, usually<a name="line.773"></a>
+<span class="sourceLineNo">774</span> // this set is small around O(Handler Count) unless something else is wrong<a name="line.774"></a>
+<span class="sourceLineNo">775</span> Set<Integer> inUseBuckets = new HashSet<>();<a name="line.775"></a>
+<span class="sourceLineNo">776</span> backingMap.forEach((k, be) -> {<a name="line.776"></a>
+<span class="sourceLineNo">777</span> if (be.isRpcRef()) {<a name="line.777"></a>
+<span class="sourceLineNo">778</span> inUseBuckets.add(bucketAllocator.getBucketIndex(be.offset()));<a name="line.778"></a>
+<span class="sourceLineNo">779</span> }<a name="line.779"></a>
+<span class="sourceLineNo">780</span> });<a name="line.780"></a>
+<span class="sourceLineNo">781</span> Set<Integer> candidateBuckets =<a name="line.781"></a>
+<span class="sourceLineNo">782</span> bucketAllocator.getLeastFilledBuckets(inUseBuckets, completelyFreeBucketsNeeded);<a name="line.782"></a>
+<span class="sourceLineNo">783</span> for (Map.Entry<BlockCacheKey, BucketEntry> entry : backingMap.entrySet()) {<a name="line.783"></a>
+<span class="sourceLineNo">784</span> if (candidateBuckets.contains(bucketAllocator.getBucketIndex(entry.getValue().offset()))) {<a name="line.784"></a>
+<span class="sourceLineNo">785</span> evictBucketEntryIfNoRpcReferenced(entry.getKey(), entry.getValue());<a name="line.785"></a>
+<span class="sourceLineNo">786</span> }<a name="line.786"></a>
+<span class="sourceLineNo">787</span> }<a name="line.787"></a>
+<span class="sourceLineNo">788</span> }<a name="line.788"></a>
+<span class="sourceLineNo">789</span> }<a name="line.789"></a>
+<span class="sourceLineNo">790</span><a name="line.790"></a>
+<span class="sourceLineNo">791</span> /**<a name="line.791"></a>
+<span class="sourceLineNo">792</span> * Free the space if the used size reaches acceptableSize() or one size block<a name="line.792"></a>
+<span class="sourceLineNo">793</span> * couldn't be allocated. When freeing the space, we use the LRU algorithm and<a name="line.793"></a>
+<span class="sourceLineNo">794</span> * ensure there must be some blocks evicted<a name="line.794"></a>
+<span class="sourceLineNo">795</span> * @param why Why we are being called<a name="line.795"></a>
+<span class="sourceLineNo">796</span> */<a name="line.796"></a>
+<span class="sourceLineNo">797</span> private void freeSpace(final String why) {<a name="line.797"></a>
+<span class="sourceLineNo">798</span> // Ensure only one freeSpace progress at a time<a name="line.798"></a>
+<span class="sourceLineNo">799</span> if (!freeSpaceLock.tryLock()) {<a name="line.799"></a>
+<span class="sourceLineNo">800</span> return;<a name="line.800"></a>
+<span class="sourceLineNo">801</span> }<a name="line.801"></a>
+<span class="sourceLineNo">802</span> try {<a name="line.802"></a>
+<span class="sourceLineNo">803</span> freeInProgress = true;<a name="line.803"></a>
+<span class="sourceLineNo">804</span> long bytesToFreeWithoutExtra = 0;<a name="line.804"></a>
+<span class="sourceLineNo">805</span> // Calculate free byte for each bucketSizeinfo<a name="line.805"></a>
+<span class="sourceLineNo">806</span> StringBuilder msgBuffer = LOG.isDebugEnabled()? new StringBuilder(): null;<a name="line.806"></a>
+<span class="sourceLineNo">807</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.807"></a>
+<span class="sourceLineNo">808</span> long[] bytesToFreeForBucket = new long[stats.length];<a name="line.808"></a>
+<span class="sourceLineNo">809</span> for (int i = 0; i < stats.length; i++) {<a name="line.809"></a>
+<span class="sourceLineNo">810</span> bytesToFreeForBucket[i] = 0;<a name="line.810"></a>
+<span class="sourceLineNo">811</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.811"></a>
+<span class="sourceLineNo">812</span> freeGoal = Math.max(freeGoal, 1);<a name="line.812"></a>
+<span class="sourceLineNo">813</span> if (stats[i].freeCount() < freeGoal) {<a name="line.813"></a>
+<span class="sourceLineNo">814</span> bytesToFreeForBucket[i] = stats[i].itemSize() * (freeGoal - stats[i].freeCount());<a name="line.814"></a>
+<span class="sourceLineNo">815</span> bytesToFreeWithoutExtra += bytesToFreeForBucket[i];<a name="line.815"></a>
+<span class="sourceLineNo">816</span> if (msgBuffer != null) {<a name="line.816"></a>
+<span class="sourceLineNo">817</span> msgBuffer.append("Free for bucketSize(" + stats[i].itemSize() + ")="<a name="line.817"></a>
+<span class="sourceLineNo">818</span> + StringUtils.byteDesc(bytesToFreeForBucket[i]) + ", ");<a name="line.818"></a>
+<span class="sourceLineNo">819</span> }<a name="line.819"></a>
+<span class="sourceLineNo">820</span> }<a name="line.820"></a>
+<span class="sourceLineNo">821</span> }<a name="line.821"></a>
+<span class="sourceLineNo">822</span> if (msgBuffer != null) {<a name="line.822"></a>
+<span class="sourceLineNo">823</span> msgBuffer.append("Free for total=" + StringUtils.byteDesc(bytesToFreeWithoutExtra) + ", ");<a name="line.823"></a>
+<span class="sourceLineNo">824</span> }<a name="line.824"></a>
+<span class="sourceLineNo">825</span><a name="line.825"></a>
+<span class="sourceLineNo">826</span> if (bytesToFreeWithoutExtra <= 0) {<a name="line.826"></a>
+<span class="sourceLineNo">827</span> return;<a name="line.827"></a>
+<span class="sourceLineNo">828</span> }<a name="line.828"></a>
+<span class="sourceLineNo">829</span> long currentSize = bucketAllocator.getUsedSize();<a name="line.829"></a>
+<span class="sourceLineNo">830</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.830"></a>
+<span class="sourceLineNo">831</span> if (LOG.isDebugEnabled() && msgBuffer != null) {<a name="line.831"></a>
+<span class="sourceLineNo">832</span> LOG.debug("Free started because \"" + why + "\"; " + msgBuffer.toString() +<a name="line.832"></a>
+<span class="sourceLineNo">833</span> " of current used=" + StringUtils.byteDesc(currentSize) + ", actual cacheSize=" +<a name="line.833"></a>
+<span class="sourceLineNo">834</span> StringUtils.byteDesc(realCacheSize.sum()) + ", total=" + StringUtils.byteDesc(totalSize));<a name="line.834"></a>
+<span class="sourceLineNo">835</span> }<a name="line.835"></a>
+<span class="sourceLineNo">836</span><a name="line.836"></a>
+<span class="sourceLineNo">837</span> long bytesToFreeWithExtra = (long) Math.floor(bytesToFreeWithoutExtra<a name="line.837"></a>
+<span class="sourceLineNo">838</span> * (1 + extraFreeFactor));<a name="line.838"></a>
+<span class="sourceLineNo">839</span><a name="line.839"></a>
+<span class="sourceLineNo">840</span> // Instantiate priority buckets<a name="line.840"></a>
+<span class="sourceLineNo">841</span> BucketEntryGroup bucketSingle = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.841"></a>
+<span class="sourceLineNo">842</span> blockSize, getPartitionSize(singleFactor));<a name="line.842"></a>
+<span class="sourceLineNo">843</span> BucketEntryGroup bucketMulti = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.843"></a>
+<span class="sourceLineNo">844</span> blockSize, getPartitionSize(multiFactor));<a name="line.844"></a>
+<span class="sourceLineNo">845</span> BucketEntryGroup bucketMemory = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.845"></a>
+<span class="sourceLineNo">846</span> blockSize, getPartitionSize(memoryFactor));<a name="line.846"></a>
+<span class="sourceLineNo">847</span><a name="line.847"></a>
+<span class="sourceLineNo">848</span> // Scan entire map putting bucket entry into appropriate bucket entry<a name="line.848"></a>
+<span class="sourceLineNo">849</span> // group<a name="line.849"></a>
+<span class="sourceLineNo">850</span> for (Map.Entry<BlockCacheKey, BucketEntry> bucketEntryWithKey : backingMap.entrySet()) {<a name="line.850"></a>
+<span class="sourceLineNo">851</span> switch (bucketEntryWithKey.getValue().getPriority()) {<a name="line.851"></a>
+<span class="sourceLineNo">852</span> case SINGLE: {<a name="line.852"></a>
+<span class="sourceLineNo">853</span> bucketSingle.add(bucketEntryWithKey);<a name="line.853"></a>
+<span class="sourceLineNo">854</span> break;<a name="line.854"></a>
+<span class="sourceLineNo">855</span> }<a name="line.855"></a>
+<span class="sourceLineNo">856</span> case MULTI: {<a name="line.856"></a>
+<span class="sourceLineNo">857</span> bucketMulti.add(bucketEntryWithKey);<a name="line.857"></a>
+<span class="sourceLineNo">858</span> break;<a name="line.858"></a>
+<span class="sourceLineNo">859</span> }<a name="line.859"></a>
+<span class="sourceLineNo">860</span> case MEMORY: {<a name="line.860"></a>
+<span class="sourceLineNo">861</span> bucketMemory.add(bucketEntryWithKey);<a name="line.861"></a>
+<span class="sourceLineNo">862</span> break;<a name="line.862"></a>
+<span class="sourceLineNo">863</span> }<a name="line.863"></a>
+<span class="sourceLineNo">864</span> }<a name="line.864"></a>
+<span class="sourceLineNo">865</span> }<a name="line.865"></a>
+<span class="sourceLineNo">866</span><a name="line.866"></a>
+<span class="sourceLineNo">867</span> PriorityQueue<BucketEntryGroup> bucketQueue = new PriorityQueue<>(3,<a name="line.867"></a>
+<span class="sourceLineNo">868</span> Comparator.comparingLong(BucketEntryGroup::overflow));<a name="line.868"></a>
+<span class="sourceLineNo">869</span><a name="line.869"></a>
+<span class="sourceLineNo">870</span> bucketQueue.add(bucketSingle);<a name="line.870"></a>
+<span class="sourceLineNo">871</span> bucketQueue.add(bucketMulti);<a name="line.871"></a>
+<span class="sourceLineNo">872</span> bucketQueue.add(bucketMemory);<a name="line.872"></a>
+<span class="sourceLineNo">873</span><a name="line.873"></a>
+<span class="sourceLineNo">874</span> int remainingBuckets = bucketQueue.size();<a name="line.874"></a>
+<span class="sourceLineNo">875</span> long bytesFreed = 0;<a name="line.875"></a>
+<span class="sourceLineNo">876</span><a name="line.876"></a>
+<span class="sourceLineNo">877</span> BucketEntryGroup bucketGroup;<a name="line.877"></a>
+<span class="sourceLineNo">878</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.878"></a>
+<span class="sourceLineNo">879</span> long overflow = bucketGroup.overflow();<a name="line.879"></a>
+<span class="sourceLineNo">880</span> if (overflow > 0) {<a name="line.880"></a>
+<span class="sourceLineNo">881</span> long bucketBytesToFree = Math.min(overflow,<a name="line.881"></a>
+<span class="sourceLineNo">882</span> (bytesToFreeWithoutExtra - bytesFreed) / remainingBuckets);<a name="line.882"></a>
+<span class="sourceLineNo">883</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.883"></a>
+<span class="sourceLineNo">884</span> }<a name="line.884"></a>
+<span class="sourceLineNo">885</span> remainingBuckets--;<a name="line.885"></a>
+<span class="sourceLineNo">886</span> }<a name="line.886"></a>
+<span class="sourceLineNo">887</span><a name="line.887"></a>
+<span class="sourceLineNo">888</span> // Check and free if there are buckets that still need freeing of space<a name="line.888"></a>
+<span class="sourceLineNo">889</span> if (bucketSizesAboveThresholdCount(minFactor) > 0) {<a name="line.889"></a>
+<span class="sourceLineNo">890</span> bucketQueue.clear();<a name="line.890"></a>
+<span class="sourceLineNo">891</span> remainingBuckets = 3;<a name="line.891"></a>
+<span class="sourceLineNo">892</span><a name="line.892"></a>
+<span class="sourceLineNo">893</span> bucketQueue.add(bucketSingle);<a name="line.893"></a>
+<span class="sourceLineNo">894</span> bucketQueue.add(bucketMulti);<a name="line.894"></a>
+<span class="sourceLineNo">895</span> bucketQueue.add(bucketMemory);<a name="line.895"></a>
+<span class="sourceLineNo">896</span><a name="line.896"></a>
+<span class="sourceLineNo">897</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.897"></a>
+<span class="sourceLineNo">898</span> long bucketBytesToFree = (bytesToFreeWithExtra - bytesFreed) / remainingBuckets;<a name="line.898"></a>
+<span class="sourceLineNo">899</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.899"></a>
+<span class="sourceLineNo">900</span> remainingBuckets--;<a name="line.900"></a>
+<span class="sourceLineNo">901</span> }<a name="line.901"></a>
+<span class="sourceLineNo">902</span> }<a name="line.902"></a>
+<span class="sourceLineNo">903</span><a name="line.903"></a>
+<span class="sourceLineNo">904</span> // Even after the above free we might still need freeing because of the<a name="line.904"></a>
+<span class="sourceLineNo">905</span> // De-fragmentation of the buckets (also called Slab Calcification problem), i.e<a name="line.905"></a>
+<span class="sourceLineNo">906</span> // there might be some buckets where the occupancy is very sparse and thus are not<a name="line.906"></a>
+<span class="sourceLineNo">907</span> // yielding the free for the other bucket sizes, the fix for this to evict some<a name="line.907"></a>
+<span class="sourceLineNo">908</span> // of the buckets, we do this by evicting the buckets that are least fulled<a name="line.908"></a>
+<span class="sourceLineNo">909</span> freeEntireBuckets(DEFAULT_FREE_ENTIRE_BLOCK_FACTOR *<a name="line.909"></a>
+<span class="sourceLineNo">910</span> bucketSizesAboveThresholdCount(1.0f));<a name="line.910"></a>
+<span class="sourceLineNo">911</span><a name="line.911"></a>
+<span class="sourceLineNo">912</span> if (LOG.isDebugEnabled()) {<a name="line.912"></a>
+<span class="sourceLineNo">913</span> long single = bucketSingle.totalSize();<a name="line.913"></a>
+<span class="sourceLineNo">914</span> long multi = bucketMulti.totalSize();<a name="line.914"></a>
+<span class="sourceLineNo">915</span> long memory = bucketMemory.totalSize();<a name="line.915"></a>
+<span class="sourceLineNo">916</span> if (LOG.isDebugEnabled()) {<a name="line.916"></a>
+<span class="sourceLineNo">917</span> LOG.debug("Bucket cache free space completed; " + "freed="<a name="line.917"></a>
+<span class="sourceLineNo">918</span> + StringUtils.byteDesc(bytesFreed) + ", " + "total="<a name="line.918"></a>
+<span class="sourceLineNo">919</span> + StringUtils.byteDesc(totalSize) + ", " + "single="<a name="line.919"></a>
+<span class="sourceLineNo">920</span> + StringUtils.byteDesc(single) + ", " + "multi="<a name="line.920"></a>
+<span class="sourceLineNo">921</span> + StringUtils.byteDesc(multi) + ", " + "memory="<a name="line.921"></a>
+<span class="sourceLineNo">922</span> + StringUtils.byteDesc(memory));<a name="line.922"></a>
+<span class="sourceLineNo">923</span> }<a name="line.923"></a>
+<span class="sourceLineNo">924</span> }<a name="line.924"></a>
+<span class="sourceLineNo">925</span><a name="line.925"></a>
+<span class="sourceLineNo">926</span> } catch (Throwable t) {<a name="line.926"></a>
+<span class="sourceLineNo">927</span> LOG.warn("Failed freeing space", t);<a name="line.927"></a>
+<span class="sourceLineNo">928</span> } finally {<a name="line.928"></a>
+<span class="sourceLineNo">929</span> cacheStats.evict();<a name="line.929"></a>
+<span class="sourceLineNo">930</span> freeInProgress = false;<a name="line.930"></a>
+<span class="sourceLineNo">931</span> freeSpaceLock.unlock();<a name="line.931"></a>
+<span class="sourceLineNo">932</span> }<a name="line.932"></a>
+<span class="sourceLineNo">933</span> }<a name="line.933"></a>
+<span class="sourceLineNo">934</span><a name="line.934"></a>
+<span class="sourceLineNo">935</span> // This handles flushing the RAM cache to IOEngine.<a name="line.935"></a>
+<span class="sourceLineNo">936</span> class WriterThread extends Thread {<a name="line.936"></a>
+<span class="sourceLineNo">937</span> private final BlockingQueue<RAMQueueEntry> inputQueue;<a name="line.937"></a>
+<span class="sourceLineNo">938</span> private volatile boolean writerEnabled = true;<a name="line.938"></a>
+<span class="sourceLineNo">939</span><a name="line.939"></a>
+<span class="sourceLineNo">940</span> WriterThread(BlockingQueue<RAMQueueEntry> queue) {<a name="line.940"></a>
+<span class="sourceLineNo">941</span> super("BucketCacheWriterThread");<a name="line.941"></a>
+<span class="sourceLineNo">942</span> this.inputQueue = queue;<a name="line.942"></a>
+<span class="sourceLineNo">943</span> }<a name="line.943"></a>
+<span class="sourceLineNo">944</span><a name="line.944"></a>
+<span class="sourceLineNo">945</span> // Used for test<a name="line.945"></a>
+<span class="sourceLineNo">946</span> void disableWriter() {<a name="line.946"></a>
+<span class="sourceLineNo">947</span> this.writerEnabled = false;<a name="line.947"></a>
+<span class="sourceLineNo">948</span> }<a name="line.948"></a>
+<span class="sourceLineNo">949</span><a name="line.949"></a>
+<span class="sourceLineNo">950</span> @Override<a name="line.950"></a>
+<span class="sourceLineNo">951</span> public void run() {<a name="line.951"></a>
+<span class="sourceLineNo">952</span> List<RAMQueueEntry> entries = new ArrayList<>();<a name="line.952"></a>
+<span class="sourceLineNo">953</span> try {<a name="line.953"></a>
+<span class="sourceLineNo">954</span> while (cacheEnabled && writerEnabled) {<a name="line.954"></a>
+<span class="sourceLineNo">955</span> try {<a name="line.955"></a>
+<span class="sourceLineNo">956</span> try {<a name="line.956"></a>
+<span class="sourceLineNo">957</span> // Blocks<a name="line.957"></a>
+<span class="sourceLineNo">958</span> entries = getRAMQueueEntries(inputQueue, entries);<a name="line.958"></a>
+<span class="sourceLineNo">959</span> } catch (InterruptedException ie) {<a name="line.959"></a>
+<span class="sourceLineNo">960</span> if (!cacheEnabled || !writerEnabled) {<a name="line.960"></a>
+<span class="sourceLineNo">961</span> break;<a name="line.961"></a>
+<span class="sourceLineNo">962</span> }<a name="line.962"></a>
+<span class="sourceLineNo">963</span> }<a name="line.963"></a>
+<span class="sourceLineNo">964</span> doDrain(entries);<a name="line.964"></a>
+<span class="sourceLineNo">965</span> } catch (Exception ioe) {<a name="line.965"></a>
+<span class="sourceLineNo">966</span> LOG.error("WriterThread encountered error", ioe);<a name="line.966"></a>
+<span class="sourceLineNo">967</span> }<a name="line.967"></a>
+<span class="sourceLineNo">968</span> }<a name="line.968"></a>
+<span class="sourceLineNo">969</span> } catch (Throwable t) {<a name="line.969"></a>
+<span class="sourceLineNo">970</span> LOG.warn("Failed doing drain", t);<a name="line.970"></a>
+<span class="sourceLineNo">971</span> }<a name="line.971"></a>
+<span class="sourceLineNo">972</span> LOG.info(this.getName() + " exiting, cacheEnabled=" + cacheEnabled);<a name="line.972"></a>
+<span class="sourceLineNo">973</span> }<a name="line.973"></a>
+<span class="sourceLineNo">974</span> }<a name="line.974"></a>
+<span class="sourceLineNo">975</span><a name="line.975"></a>
+<span class="sourceLineNo">976</span> /**<a name="line.976"></a>
+<span class="sourceLineNo">977</span> * Put the new bucket entry into backingMap. Notice that we are allowed to replace the existing<a name="line.977"></a>
+<span class="sourceLineNo">978</span> * cache with a new block for the same cache key. there's a corner case: one thread cache a block<a name="line.978"></a>
+<span class="sourceLineNo">979</span> * in ramCache, copy to io-engine and add a bucket entry to backingMap. Caching another new block<a name="line.979"></a>
+<span class="sourceLineNo">980</span> * with the same cache key do the same thing for the same cache key, so if not evict the previous<a name="line.980"></a>
+<span class="sourceLineNo">981</span> * bucket entry, then memory leak happen because the previous bucketEntry is gone but the<a name="line.981"></a>
+<span class="sourceLineNo">982</span> * bucketAllocator do not free its memory.<a name="line.982"></a>
+<span class="sourceLineNo">983</span> * @see BlockCacheUtil#shouldReplaceExistingCacheBlock(BlockCache blockCache,BlockCacheKey<a name="line.983"></a>
+<span class="sourceLineNo">984</span> * cacheKey, Cacheable newBlock)<a name="line.984"></a>
+<span class="sourceLineNo">985</span> * @param key Block cache key<a name="line.985"></a>
+<span class="sourceLineNo">986</span> * @param bucketEntry Bucket entry to put into backingMap.<a name="line.986"></a>
+<span class="sourceLineNo">987</span> */<a name="line.987"></a>
+<span class="sourceLineNo">988</span> protected void putIntoBackingMap(BlockCacheKey key, BucketEntry bucketEntry) {<a name="line.988"></a>
+<span class="sourceLineNo">989</span> BucketEntry previousEntry = backingMap.put(key, bucketEntry);<a name="line.989"></a>
+<span class="sourceLineNo">990</span> if (previousEntry != null && previousEntry != bucketEntry) {<a name="line.990"></a>
+<span class="sourceLineNo">991</span> previousEntry.withWriteLock(offsetLock, () -> {<a name="line.991"></a>
+<span class="sourceLineNo">992</span> blockEvicted(key, previousEntry, false);<a name="line.992"></a>
+<span class="sourceLineNo">993</span> return null;<a name="line.993"></a>
+<span class="sourceLineNo">994</span> });<a name="line.994"></a>
+<span class="sourceLineNo">995</span> }<a name="line.995"></a>
+<span class="sourceLineNo">996</span> }<a name="line.996"></a>
+<span class="sourceLineNo">997</span><a name="line.997"></a>
+<span class="sourceLineNo">998</span> /**<a name="line.998"></a>
+<span class="sourceLineNo">999</span> * Flush the entries in ramCache to IOEngine and add bucket entry to backingMap. Process all that<a name="line.999"></a>
+<span class="sourceLineNo">1000</span> * are passed in even if failure being sure to remove from ramCache else we'll never undo the<a name="line.1000"></a>
+<span class="sourceLineNo">1001</span> * references and we'll OOME.<a name="line.1001"></a>
+<span class="sourceLineNo">1002</span> * @param entries Presumes list passed in here will be processed by this invocation only. No<a name="line.1002"></a>
+<span class="sourceLineNo">1003</span> * interference expected.<a name="line.1003"></a>
+<span class="sourceLineNo">1004</span> */<a name="line.1004"></a>
+<span class="sourceLineNo">1005</span> void doDrain(final List<RAMQueueEntry> entries) throws InterruptedException {<a name="line.1005"></a>
+<span class="sourceLineNo">1006</span> if (entries.isEmpty()) {<a name="line.1006"></a>
+<span class="sourceLineNo">1007</span> return;<a name="line.1007"></a>
+<span class="sourceLineNo">1008</span> }<a name="line.1008"></a>
+<span class="sourceLineNo">1009</span> // This method is a little hard to follow. We run through the passed in entries and for each<a name="line.1009"></a>
+<span class="sourceLineNo">1010</span> // successful add, we add a non-null BucketEntry to the below bucketEntries. Later we must<a name="line.1010"></a>
+<span class="sourceLineNo">1011</span> // do cleanup making sure we've cleared ramCache of all entries regardless of whether we<a name="line.1011"></a>
+<span class="sourceLineNo">1012</span> // successfully added the item to the bucketcache; if we don't do the cleanup, we'll OOME by<a name="line.1012"></a>
+<span class="sourceLineNo">1013</span> // filling ramCache. We do the clean up by again running through the passed in entries<a name="line.1013"></a>
+<span class="sourceLineNo">1014</span> // doing extra work when we find a non-null bucketEntries corresponding entry.<a name="line.1014"></a>
+<span class="sourceLineNo">1015</span> final int size = entries.size();<a name="line.1015"></a>
+<span class="sourceLineNo">1016</span> BucketEntry[] bucketEntries = new BucketEntry[size];<a name="line.1016"></a>
+<span class="sourceLineNo">1017</span> // Index updated inside loop if success or if we can't succeed. We retry if cache is full<a name="line.1017"></a>
+<span class="sourceLineNo">1018</span> // when we go to add an entry by going around the loop again without upping the index.<a name="line.1018"></a>
+<span class="sourceLineNo">1019</span> int index = 0;<a name="line.1019"></a>
+<span class="sourceLineNo">1020</span> while (cacheEnabled && index < size) {<a name="line.1020"></a>
+<span class="sourceLineNo">1021</span> RAMQueueEntry re = null;<a name="line.1021"></a>
+<span class="sourceLineNo">1022</span> try {<a name="line.1022"></a>
+<span class="sourceLineNo">1023</span> re = entries.get(index);<a name="line.1023"></a>
+<span class="sourceLineNo">1024</span> if (re == null) {<a name="line.1024"></a>
+<span class="sourceLineNo">1025</span> LOG.warn("Couldn't get entry or changed on us; who else is messing with it?");<a name="line.1025"></a>
+<span class="sourceLineNo">1026</span> index++;<a name="line.1026"></a>
+<span class="sourceLineNo">1027</span> continue;<a name="line.1027"></a>
+<span class="sourceLineNo">1028</span> }<a name="line.1028"></a>
+<span class="sourceLineNo">1029</span> BlockCacheKey cacheKey = re.getKey();<a name="line.1029"></a>
+<span class="sourceLineNo">1030</span> if (ramCache.containsKey(cacheKey)) {<a name="line.1030"></a>
+<span class="sourceLineNo">1031</span> blocksByHFile.add(cacheKey);<a name="line.1031"></a>
+<span class="sourceLineNo">1032</span> }<a name="line.1032"></a>
+<span class="sourceLineNo">1033</span><a name="line.1033"></a>
+<span class="sourceLineNo">1034</span> BucketEntry bucketEntry = re.writeToCache(ioEngine, bucketAllocator, realCacheSize,<a name="line.1034"></a>
+<span class="sourceLineNo">1035</span> this::createRecycler);<a name="line.1035"></a>
+<span class="sourceLineNo">1036</span> // Successfully added. Up index and add bucketEntry. Clear io exceptions.<a name="line.1036"></a>
+<span class="sourceLineNo">1037</span> bucketEntries[index] = bucketEntry;<a name="line.1037"></a>
+<span class="sourceLineNo">1038</span> if (ioErrorStartTime > 0) {<a name="line.1038"></a>
+<span class="sourceLineNo">1039</span> ioErrorStartTime = -1;<a name="line.1039"></a>
+<span class="sourceLineNo">1040</span> }<a name="line.1040"></a>
+<span class="sourceLineNo">1041</span> index++;<a name="line.1041"></a>
+<span class="sourceLineNo">1042</span> } catch (BucketAllocatorException fle) {<a name="line.1042"></a>
+<span class="sourceLineNo">1043</span> LOG.warn("Failed allocation for " + (re == null ? "" : re.getKey()) + "; " + fle);<a name="line.1043"></a>
+<span class="sourceLineNo">1044</span> // Presume can't add. Too big? Move index on. Entry will be cleared from ramCache below.<a name="line.1044"></a>
+<span class="sourceLineNo">1045</span> bucketEntries[index] = null;<a name="line.1045"></a>
+<span class="sourceLineNo">1046</span> index++;<a name="line.1046"></a>
+<span class="sourceLineNo">1047</span> } catch (CacheFullException cfe) {<a name="line.1047"></a>
+<span class="sourceLineNo">1048</span> // Cache full when we tried to add. Try freeing space and then retrying (don't up index)<a name="line.1048"></a>
+<span class="sourceLineNo">1049</span> if (!freeInProgress) {<a name="line.1049"></a>
+<span class="sourceLineNo">1050</span> freeSpace("Full!");<a name="line.1050"></a>
+<span class="sourceLineNo">1051</span> } else {<a name="line.1051"></a>
+<span class="sourceLineNo">1052</span> Thread.sleep(50);<a name="line.1052"></a>
+<span class="sourceLineNo">1053</span> }<a name="line.1053"></a>
+<span class="sourceLineNo">1054</span> } catch (IOException ioex) {<a name="line.1054"></a>
+<span class="sourceLineNo">1055</span> // Hopefully transient. Retry. checkIOErrorIsTolerated disables cache if problem.<a name="line.1055"></a>
+<span class="sourceLineNo">1056</span> LOG.error("Failed writing to bucket cache", ioex);<a name="line.1056"></a>
+<span class="sourceLineNo">1057</span> checkIOErrorIsTolerated();<a name="line.1057"></a>
+<span class="sourceLineNo">1058</span> }<a name="line.1058"></a>
+<span class="sourceLineNo">1059</span> }<a name="line.1059"></a>
+<span class="sourceLineNo">1060</span><a name="line.1060"></a>
+<span class="sourceLineNo">1061</span> // Make sure data pages are written on media before we update maps.<a name="line.1061"></a>
+<span class="sourceLineNo">1062</span> try {<a name="line.1062"></a>
+<span class="sourceLineNo">1063</span> ioEngine.sync();<a name="line.1063"></a>
+<span class="sourceLineNo">1064</span> } catch (IOException ioex) {<a name="line.1064"></a>
+<span class="sourceLineNo">1065</span> LOG.error("Failed syncing IO engine", ioex);<a name="line.1065"></a>
+<span class="sourceLineNo">1066</span> checkIOErrorIsTolerated();<a name="line.1066"></a>
+<span class="sourceLineNo">1067</span> // Since we failed sync, free the blocks in bucket allocator<a name="line.1067"></a>
+<span class="sourceLineNo">1068</span> for (int i = 0; i < entries.size(); ++i) {<a name="line.1068"></a>
+<span class="sourceLineNo">1069</span> if (bucketEntries[i] != null) {<a name="line.1069"></a>
+<span class="sourceLineNo">1070</span> bucketAllocator.freeBlock(bucketEntries[i].offset());<a name="line.1070"></a>
+<span class="sourceLineNo">1071</span> bucketEntries[i] = null;<a name="line.1071"></a>
+<span class="sourceLineNo">1072</span> }<a name="line.1072"></a>
+<span class="sourceLineNo">1073</span> }<a name="line.1073"></a>
+<span class="sourceLineNo">1074</span> }<a name="line.1074"></a>
+<span class="sourceLineNo">1075</span><a name="line.1075"></a>
+<span class="sourceLineNo">1076</span> // Now add to backingMap if successfully added to bucket cache. Remove from ramCache if<a name="line.1076"></a>
+<span class="sourceLineNo">1077</span> // success or error.<a name="line.1077"></a>
+<span class="sourceLineNo">1078</span> for (int i = 0; i < size; ++i) {<a name="line.1078"></a>
+<span class="sourceLineNo">1079</span> BlockCacheKey key = entries.get(i).getKey();<a name="line.1079"></a>
+<span class="sourceLineNo">1080</span> // Only add if non-null entry.<a name="line.1080"></a>
+<span class="sourceLineNo">1081</span> if (bucketEntries[i] != null) {<a name="line.1081"></a>
+<span class="sourceLineNo">1082</span> putIntoBackingMap(key, bucketEntries[i]);<a name="line.1082"></a>
+<span class="sourceLineNo">1083</span> }<a name="line.1083"></a>
+<span class="sourceLineNo">1084</span> // Always remove from ramCache even if we failed adding it to the block cache above.<a name="line.1084"></a>
+<span class="sourceLineNo">1085</span> boolean existed = ramCache.remove(key, re -> {<a name="line.1085"></a>
+<span class="sourceLineNo">1086</span> if (re != null) {<a name="line.1086"></a>
+<span class="sourceLineNo">1087</span> heapSize.add(-1 * re.getData().heapSize());<a name="line.1087"></a>
+<span class="sourceLineNo">1088</span> }<a name="line.1088"></a>
+<span class="sourceLineNo">1089</span> });<a name="line.1089"></a>
+<span class="sourceLineNo">1090</span> if (!existed && bucketEntries[i] != null) {<a name="line.1090"></a>
+<span class="sourceLineNo">1091</span> // Block should have already been evicted. Remove it and free space.<a name="line.1091"></a>
+<span class="sourceLineNo">1092</span> final BucketEntry bucketEntry = bucketEntries[i];<a name="line.1092"></a>
+<span class="sourceLineNo">1093</span> bucketEntry.withWriteLock(offsetLock, () -> {<a name="line.1093"></a>
+<span class="sourceLineNo">1094</span> if (backingMap.remove(key, bucketEntry)) {<a name="line.1094"></a>
+<span class="sourceLineNo">1095</span> blockEvicted(key, bucketEntry, false);<a name="line.1095"></a>
+<span class="sourceLineNo">1096</span> }<a name="line.1096"></a>
+<span class="sourceLineNo">1097</span> return null;<a name="line.1097"></a>
+<span class="sourceLineNo">1098</span> });<a name="line.1098"></a>
+<span class="sourceLineNo">1099</span> }<a name="line.1099"></a>
+<span class="sourceLineNo">1100</span> }<a name="line.1100"></a>
+<span class="sourceLineNo">1101</span><a name="line.1101"></a>
+<span class="sourceLineNo">1102</span> long used = bucketAllocator.getUsedSize();<a name="line.1102"></a>
+<span class="sourceLineNo">1103</span> if (used > acceptableSize()) {<a name="line.1103"></a>
+<span class="sourceLineNo">1104</span> freeSpace("Used=" + used + " > acceptable=" + acceptableSize());<a name="line.1104"></a>
+<span class="sourceLineNo">1105</span> }<a name="line.1105"></a>
+<span class="sourceLineNo">1106</span> return;<a name="line.1106"></a>
+<span class="sourceLineNo">1107</span> }<a name="line.1107"></a>
+<span class="sourceLineNo">1108</span><a name="line.1108"></a>
+<span class="sourceLineNo">1109</span> /**<a name="line.1109"></a>
+<span class="sourceLineNo">1110</span> * Blocks until elements available in {@code q} then tries to grab as many as possible before<a name="line.1110"></a>
+<span class="sourceLineNo">1111</span> * returning.<a name="line.1111"></a>
+<span class="sourceLineNo">1112</span> * @param receptacle Where to stash the elements taken from queue. We clear before we use it just<a name="line.1112"></a>
+<span class="sourceLineNo">1113</span> * in case.<a name="line.1113"></a>
+<span class="sourceLineNo">1114</span> * @param q The queue to take from.<a name="line.1114"></a>
+<span class="sourceLineNo">1115</span> * @return {@code receptacle} laden with elements taken from the queue or empty if none found.<a name="line.1115"></a>
+<span class="sourceLineNo">1116</span> */<a name="line.1116"></a>
+<span class="sourceLineNo">1117</span> static List<RAMQueueEntry> getRAMQueueEntries(BlockingQueue<RAMQueueEntry> q,<a name="line.1117"></a>
+<span class="sourceLineNo">1118</span> List<RAMQueueEntry> receptacle) throws InterruptedException {<a name="line.1118"></a>
+<span class="sourceLineNo">1119</span> // Clear sets all entries to null and sets size to 0. We retain allocations. Presume it<a name="line.1119"></a>
+<span class="sourceLineNo">1120</span> // ok even if list grew to accommodate thousands.<a name="line.1120"></a>
+<span class="sourceLineNo">1121</span> receptacle.clear();<a name="line.1121"></a>
+<span class="sourceLineNo">1122</span> receptacle.add(q.take());<a name="line.1122"></a>
+<span class="sourceLineNo">1123</span> q.drainTo(receptacle);<a name="line.1123"></a>
+<span class="sourceLineNo">1124</span> return receptacle;<a name="line.1124"></a>
+<span class="sourceLineNo">1125</span> }<a name="line.1125"></a>
+<span class="sourceLineNo">1126</span><a name="line.1126"></a>
+<span class="sourceLineNo">1127</span> /**<a name="line.1127"></a>
+<span class="sourceLineNo">1128</span> * @see #retrieveFromFile(int[])<a name="line.1128"></a>
+<span class="sourceLineNo">1129</span> */<a name="line.1129"></a>
+<span class="sourceLineNo">1130</span> @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="OBL_UNSATISFIED_OBLIGATION",<a name="line.1130"></a>
+<span class="sourceLineNo">1131</span> justification = "false positive, try-with-resources ensures close is called.")<a name="line.1131"></a>
+<span class="sourceLineNo">1132</span> private void persistToFile() throws IOException {<a name="line.1132"></a>
+<span class="sourceLineNo">1133</span> assert !cacheEnabled;<a name="line.1133"></a>
+<span class="sourceLineNo">1134</span> if (!ioEngine.isPersistent()) {<a name="line.1134"></a>
+<span class="sourceLineNo">1135</span> throw new IOException("Attempt to persist non-persistent cache mappings!");<a name="line.1135"></a>
<span class="sourceLineNo">1136</span> }<a name="line.1136"></a>
-<span class="sourceLineNo">1137</span> }<a name="line.1137"></a>
-<span class="sourceLineNo">1138</span><a name="line.1138"></a>
-<span class="sourceLineNo">1139</span> /**<a name="line.1139"></a>
-<span class="sourceLineNo">1140</span> * @see #persistToFile()<a name="line.1140"></a>
-<span class="sourceLineNo">1141</span> */<a name="line.1141"></a>
-<span class="sourceLineNo">1142</span> private void retrieveFromFile(int[] bucketSizes) throws IOException {<a name="line.1142"></a>
-<span class="sourceLineNo">1143</span> File persistenceFile = new File(persistencePath);<a name="line.1143"></a>
-<span class="sourceLineNo">1144</span> if (!persistenceFile.exists()) {<a name="line.1144"></a>
-<span class="sourceLineNo">1145</span> return;<a name="line.1145"></a>
-<span class="sourceLineNo">1146</span> }<a name="line.1146"></a>
-<span class="sourceLineNo">1147</span> assert !cacheEnabled;<a name="line.1147"></a>
-<span class="sourceLineNo">1148</span><a name="line.1148"></a>
-<span class="sourceLineNo">1149</span> try (FileInputStream in = deleteFileOnClose(persistenceFile)) {<a name="line.1149"></a>
-<span class="sourceLineNo">1150</span> int pblen = ProtobufMagic.lengthOfPBMagic();<a name="line.1150"></a>
-<span class="sourceLineNo">1151</span> byte[] pbuf = new byte[pblen];<a name="line.1151"></a>
-<span class="sourceLineNo">1152</span> int read = in.read(pbuf);<a name="line.1152"></a>
-<span class="sourceLineNo">1153</span> if (read != pblen) {<a name="line.1153"></a>
-<span class="sourceLineNo">1154</span> throw new IOException("Incorrect number of bytes read while checking for protobuf magic "<a name="line.1154"></a>
-<span class="sourceLineNo">1155</span> + "number. Requested=" + pblen + ", Received= " + read + ", File=" + persistencePath);<a name="line.1155"></a>
-<span class="sourceLineNo">1156</span> }<a name="line.1156"></a>
-<span class="sourceLineNo">1157</span> if (! ProtobufMagic.isPBMagicPrefix(pbuf)) {<a name="line.1157"></a>
-<span class="sourceLineNo">1158</span> // In 3.0 we have enough flexibility to dump the old cache data.<a name="line.1158"></a>
-<span class="sourceLineNo">1159</span> // TODO: In 2.x line, this might need to be filled in to support reading the old format<a name="line.1159"></a>
-<span class="sourceLineNo">1160</span> throw new IOException("Persistence file does not start with protobuf magic number. " +<a name="line.1160"></a>
-<span class="sourceLineNo">1161</span> persistencePath);<a name="line.1161"></a>
-<span class="sourceLineNo">1162</span> }<a name="line.1162"></a>
-<span class="sourceLineNo">1163</span> parsePB(BucketCacheProtos.BucketCacheEntry.parseDelimitedFrom(in));<a name="line.1163"></a>
-<span class="sourceLineNo">1164</span> bucketAllocator = new BucketAllocator(cacheCapacity, bucketSizes, backingMap, realCacheSize);<a name="line.1164"></a>
-<span class="sourceLineNo">1165</span> blockNumber.add(backingMap.size());<a name="line.1165"></a>
-<span class="sourceLineNo">1166</span> }<a name="line.1166"></a>
-<span class="sourceLineNo">1167</span> }<a name="line.1167"></a>
-<span class="sourceLineNo">1168</span><a name="line.1168"></a>
-<span class="sourceLineNo">1169</span> /**<a name="line.1169"></a>
-<span class="sourceLineNo">1170</span> * Create an input stream that deletes the file after reading it. Use in try-with-resources to<a name="line.1170"></a>
-<span class="sourceLineNo">1171</span> * avoid this pattern where an exception thrown from a finally block may mask earlier exceptions:<a name="line.1171"></a>
-<span class="sourceLineNo">1172</span> * <pre><a name="line.1172"></a>
-<span class="sourceLineNo">1173</span> * File f = ...<a name="line.1173"></a>
-<span class="sourceLineNo">1174</span> * try (FileInputStream fis = new FileInputStream(f)) {<a name="line.1174"></a>
-<span class="sourceLineNo">1175</span> * // use the input stream<a name="line.1175"></a>
-<span class="sourceLineNo">1176</span> * } finally {<a name="line.1176"></a>
-<span class="sourceLineNo">1177</span> * if (!f.delete()) throw new IOException("failed to delete");<a name="line.1177"></a>
-<span class="sourceLineNo">1178</span> * }<a name="line.1178"></a>
-<span class="sourceLineNo">1179</span> * </pre><a name="line.1179"></a>
-<span class="sourceLineNo">1180</span> * @param file the file to read and delete<a name="line.1180"></a>
-<span class="sourceLineNo">1181</span> * @return a FileInputStream for the given file<a name="line.1181"></a>
-<span class="sourceLineNo">1182</span> * @throws IOException if there is a problem creating the stream<a name="line.1182"></a>
-<span class="sourceLineNo">1183</span> */<a name="line.1183"></a>
-<span class="sourceLineNo">1184</span> private FileInputStream deleteFileOnClose(final File file) throws IOException {<a name="line.1184"></a>
-<span class="sourceLineNo">1185</span> return new FileInputStream(file) {<a name="line.1185"></a>
-<span class="sourceLineNo">1186</span> private File myFile;<a name="line.1186"></a>
-<span class="sourceLineNo">1187</span> private FileInputStream init(File file) {<a name="line.1187"></a>
-<span class="sourceLineNo">1188</span> myFile = file;<a name="line.1188"></a>
-<span class="sourceLineNo">1189</span> return this;<a name="line.1189"></a>
-<span class="sourceLineNo">1190</span> }<a name="line.1190"></a>
-<span class="sourceLineNo">1191</span> @Override<a name="line.1191"></a>
-<span class="sourceLineNo">1192</span> public void close() throws IOException {<a name="line.1192"></a>
-<span class="sourceLineNo">1193</span> // close() will be called during try-with-resources and it will be<a name="line.1193"></a>
-<span class="sourceLineNo">1194</span> // called by finalizer thread during GC. To avoid double-free resource,<a name="line.1194"></a>
-<span class="sourceLineNo">1195</span> // set myFile to null after the first call.<a name="line.1195"></a>
-<span class="sourceLineNo">1196</span> if (myFile == null) {<a name="line.1196"></a>
-<span class="sourceLineNo">1197</span> return;<a name="line.1197"></a>
-<span class="sourceLineNo">1198</span> }<a name="line.1198"></a>
-<span class="sourceLineNo">1199</span><a name="line.1199"></a>
-<span class="sourceLineNo">1200</span> super.close();<a name="line.1200"></a>
-<span class="sourceLineNo">1201</span> if (!myFile.delete()) {<a name="line.1201"></a>
-<span class="sourceLineNo">1202</span> throw new IOException("Failed deleting persistence file " + myFile.getAbsolutePath());<a name="line.1202"></a>
-<span class="sourceLineNo">1203</span> }<a name="line.1203"></a>
-<span class="sourceLineNo">1204</span> myFile = null;<a name="line.1204"></a>
-<span class="sourceLineNo">1205</span> }<a name="line.1205"></a>
-<span class="sourceLineNo">1206</span> }.init(file);<a name="line.1206"></a>
-<span class="sourceLineNo">1207</span> }<a name="line.1207"></a>
-<span class="sourceLineNo">1208</span><a name="line.1208"></a>
-<span class="sourceLineNo">1209</span> private void verifyCapacityAndClasses(long capacitySize, String ioclass, String mapclass)<a name="line.1209"></a>
-<span class="sourceLineNo">1210</span> throws IOException {<a name="line.1210"></a>
-<span class="sourceLineNo">1211</span> if (capacitySize != cacheCapacity) {<a name="line.1211"></a>
-<span class="sourceLineNo">1212</span> throw new IOException("Mismatched cache capacity:"<a name="line.1212"></a>
-<span class="sourceLineNo">1213</span> + StringUtils.byteDesc(capacitySize) + ", expected: "<a name="line.1213"></a>
-<span class="sourceLineNo">1214</span> + StringUtils.byteDesc(cacheCapacity));<a name="line.1214"></a>
-<span class="sourceLineNo">1215</span> }<a name="line.1215"></a>
-<span class="sourceLineNo">1216</span> if (!ioEngine.getClass().getName().equals(ioclass)) {<a name="line.1216"></a>
-<span class="sourceLineNo">1217</span> throw new IOException("Class name for IO engine mismatch: " + ioclass<a name="line.1217"></a>
-<span class="sourceLineNo">1218</span> + ", expected:" + ioEngine.getClass().getName());<a name="line.1218"></a>
+<span class="sourceLineNo">1137</span> try (FileOutputStream fos = new FileOutputStream(persistencePath, false)) {<a name="line.1137"></a>
+<span class="sourceLineNo">1138</span> fos.write(ProtobufMagic.PB_MAGIC);<a name="line.1138"></a>
+<span class="sourceLineNo">1139</span> BucketProtoUtils.toPB(this).writeDelimitedTo(fos);<a name="line.1139"></a>
+<span class="sourceLineNo">1140</span> }<a name="line.1140"></a>
+<span class="sourceLineNo">1141</span> }<a name="line.1141"></a>
+<span class="sourceLineNo">1142</span><a name="line.1142"></a>
+<span class="sourceLineNo">1143</span> /**<a name="line.1143"></a>
+<span class="sourceLineNo">1144</span> * @see #persistToFile()<a name="line.1144"></a>
+<span class="sourceLineNo">1145</span> */<a name="line.1145"></a>
+<span class="sourceLineNo">1146</span> private void retrieveFromFile(int[] bucketSizes) throws IOException {<a name="line.1146"></a>
+<span class="sourceLineNo">1147</span> File persistenceFile = new File(persistencePath);<a name="line.1147"></a>
+<span class="sourceLineNo">1148</span> if (!persistenceFile.exists()) {<a name="line.1148"></a>
+<span class="sourceLineNo">1149</span> return;<a name="line.1149"></a>
+<span class="sourceLineNo">1150</span> }<a name="line.1150"></a>
+<span class="sourceLineNo">1151</span> assert !cacheEnabled;<a name="line.1151"></a>
+<span class="sourceLineNo">1152</span><a name="line.1152"></a>
+<span class="sourceLineNo">1153</span> try (FileInputStream in = deleteFileOnClose(persistenceFile)) {<a name="line.1153"></a>
+<span class="sourceLineNo">1154</span> int pblen = ProtobufMagic.lengthOfPBMagic();<a name="line.1154"></a>
+<span class="sourceLineNo">1155</span> byte[] pbuf = new byte[pblen];<a name="line.1155"></a>
+<span class="sourceLineNo">1156</span> int read = in.read(pbuf);<a name="line.1156"></a>
+<span class="sourceLineNo">1157</span> if (read != pblen) {<a name="line.1157"></a>
+<span class="sourceLineNo">1158</span> throw new IOException("Incorrect number of bytes read while checking for protobuf magic "<a name="line.1158"></a>
+<span class="sourceLineNo">1159</span> + "number. Requested=" + pblen + ", Received= " + read + ", File=" + persistencePath);<a name="line.1159"></a>
+<span class="sourceLineNo">1160</span> }<a name="line.1160"></a>
+<span class="sourceLineNo">1161</span> if (! ProtobufMagic.isPBMagicPrefix(pbuf)) {<a name="line.1161"></a>
+<span class="sourceLineNo">1162</span> // In 3.0 we have enough flexibility to dump the old cache data.<a name="line.1162"></a>
+<span class="sourceLineNo">1163</span> // TODO: In 2.x line, this might need to be filled in to support reading the old format<a name="line.1163"></a>
+<span class="sourceLineNo">1164</span> throw new IOException("Persistence file does not start with protobuf magic number. " +<a name="line.1164"></a>
+<span class="sourceLineNo">1165</span> persistencePath);<a name="line.1165"></a>
+<span class="sourceLineNo">1166</span> }<a name="line.1166"></a>
+<span class="sourceLineNo">1167</span> parsePB(BucketCacheProtos.BucketCacheEntry.parseDelimitedFrom(in));<a name="line.1167"></a>
+<span class="sourceLineNo">1168</span> bucketAllocator = new BucketAllocator(cacheCapacity, bucketSizes, backingMap, realCacheSize);<a name="line.1168"></a>
+<span class="sourceLineNo">1169</span> blockNumber.add(backingMap.size());<a name="line.1169"></a>
+<span class="sourceLineNo">1170</span> }<a name="line.1170"></a>
+<span class="sourceLineNo">1171</span> }<a name="line.1171"></a>
+<span class="sourceLineNo">1172</span><a name="line.1172"></a>
+<span class="sourceLineNo">1173</span> /**<a name="line.1173"></a>
+<span class="sourceLineNo">1174</span> * Create an input stream that deletes the file after reading it. Use in try-with-resources to<a name="line.1174"></a>
+<span class="sourceLineNo">1175</span> * avoid this pattern where an exception thrown from a finally block may mask earlier exceptions:<a name="line.1175"></a>
+<span class="sourceLineNo">1176</span> * <pre><a name="line.1176"></a>
+<span class="sourceLineNo">1177</span> * File f = ...<a name="line.1177"></a>
+<span class="sourceLineNo">1178</span> * try (FileInputStream fis = new FileInputStream(f)) {<a name="line.1178"></a>
+<span class="sourceLineNo">1179</span> * // use the input stream<a name="line.1179"></a>
+<span class="sourceLineNo">1180</span> * } finally {<a name="line.1180"></a>
+<span class="sourceLineNo">1181</span> * if (!f.delete()) throw new IOException("failed to delete");<a name="line.1181"></a>
+<span class="sourceLineNo">1182</span> * }<a name="line.1182"></a>
+<span class="sourceLineNo">1183</span> * </pre><a name="line.1183"></a>
+<span class="sourceLineNo">1184</span> * @param file the file to read and delete<a name="line.1184"></a>
+<span class="sourceLineNo">1185</span> * @return a FileInputStream for the given file<a name="line.1185"></a>
+<span class="sourceLineNo">1186</span> * @throws IOException if there is a problem creating the stream<a name="line.1186"></a>
+<span class="sourceLineNo">1187</span> */<a name="line.1187"></a>
+<span class="sourceLineNo">1188</span> private FileInputStream deleteFileOnClose(final File file) throws IOException {<a name="line.1188"></a>
+<span class="sourceLineNo">1189</span> return new FileInputStream(file) {<a name="line.1189"></a>
+<span class="sourceLineNo">1190</span> private File myFile;<a name="line.1190"></a>
+<span class="sourceLineNo">1191</span> private FileInputStream init(File file) {<a name="line.1191"></a>
+<span class="sourceLineNo">1192</span> myFile = file;<a name="line.1192"></a>
+<span class="sourceLineNo">1193</span> return this;<a name="line.1193"></a>
+<span class="sourceLineNo">1194</span> }<a name="line.1194"></a>
+<span class="sourceLineNo">1195</span> @Override<a name="line.1195"></a>
+<span class="sourceLineNo">1196</span> public void close() throws IOException {<a name="line.1196"></a>
+<span class="sourceLineNo">1197</span> // close() will be called during try-with-resources and it will be<a name="line.1197"></a>
+<span class="sourceLineNo">1198</span> // called by finalizer thread during GC. To avoid double-free resource,<a name="line.1198"></a>
+<span class="sourceLineNo">1199</span> // set myFile to null after the first call.<a name="line.1199"></a>
+<span class="sourceLineNo">1200</span> if (myFile == null) {<a name="line.1200"></a>
+<span class="sourceLineNo">1201</span> return;<a name="line.1201"></a>
+<span class="sourceLineNo">1202</span> }<a name="line.1202"></a>
+<span class="sourceLineNo">1203</span><a name="line.1203"></a>
+<span class="sourceLineNo">1204</span> super.close();<a name="line.1204"></a>
+<span class="sourceLineNo">1205</span> if (!myFile.delete()) {<a name="line.1205"></a>
+<span class="sourceLineNo">1206</span> throw new IOException("Failed deleting persistence file " + myFile.getAbsolutePath());<a name="line.1206"></a>
+<span class="sourceLineNo">1207</span> }<a name="line.1207"></a>
+<span class="sourceLineNo">1208</span> myFile = null;<a name="line.1208"></a>
+<span class="sourceLineNo">1209</span> }<a name="line.1209"></a>
+<span class="sourceLineNo">1210</span> }.init(file);<a name="line.1210"></a>
+<span class="sourceLineNo">1211</span> }<a name="line.1211"></a>
+<span class="sourceLineNo">1212</span><a name="line.1212"></a>
+<span class="sourceLineNo">1213</span> private void verifyCapacityAndClasses(long capacitySize, String ioclass, String mapclass)<a name="line.1213"></a>
+<span class="sourceLineNo">1214</span> throws IOException {<a name="line.1214"></a>
+<span class="sourceLineNo">1215</span> if (capacitySize != cacheCapacity) {<a name="line.1215"></a>
+<span class="sourceLineNo">1216</span> throw new IOException("Mismatched cache capacity:"<a name="line.1216"></a>
+<span class="sourceLineNo">1217</span> + StringUtils.byteDesc(capacitySize) + ", expected: "<a name="line.1217"></a>
+<span class="sourceLineNo">1218</span> + StringUtils.byteDesc(cacheCapacity));<a name="line.1218"></a>
<span class="sourceLineNo">1219</span> }<a name="line.1219"></a>
-<span class="sourceLineNo">1220</span> if (!backingMap.getClass().getName().equals(mapclass)) {<a name="line.1220"></a>
-<span class="sourceLineNo">1221</span> throw new IOException("Class name for cache map mismatch: " + mapclass<a name="line.1221"></a>
-<span class="sourceLineNo">1222</span> + ", expected:" + backingMap.getClass().getName());<a name="line.1222"></a>
+<span class="sourceLineNo">1220</span> if (!ioEngine.getClass().getName().equals(ioclass)) {<a name="line.1220"></a>
+<span class="sourceLineNo">1221</span> throw new IOException("Class name for IO engine mismatch: " + ioclass<a name="line.1221"></a>
+<span class="sourceLineNo">1222</span> + ", expected:" + ioEngine.getClass().getName());<a name="line.1222"></a>
<span class="sourceLineNo">1223</span> }<a name="line.1223"></a>
-<span class="sourceLineNo">1224</span> }<a name="line.1224"></a>
-<span class="sourceLineNo">1225</span><a name="line.1225"></a>
-<span class="sourceLineNo">1226</span> private void parsePB(BucketCacheProtos.BucketCacheEntry proto) throws IOException {<a name="line.1226"></a>
-<span class="sourceLineNo">1227</span> if (proto.hasChecksum()) {<a name="line.1227"></a>
-<span class="sourceLineNo">1228</span> ((PersistentIOEngine) ioEngine).verifyFileIntegrity(proto.getChecksum().toByteArray(),<a name="line.1228"></a>
-<span class="sourceLineNo">1229</span> algorithm);<a name="line.1229"></a>
-<span class="sourceLineNo">1230</span> } else {<a name="line.1230"></a>
-<span class="sourceLineNo">1231</span> // if has not checksum, it means the persistence file is old format<a name="line.1231"></a>
-<span class="sourceLineNo">1232</span> LOG.info("Persistent file is old format, it does not support verifying file integrity!");<a name="line.1232"></a>
-<span class="sourceLineNo">1233</span> }<a name="line.1233"></a>
-<span class="sourceLineNo">1234</span> verifyCapacityAndClasses(proto.getCacheCapacity(), proto.getIoClass(), proto.getMapClass());<a name="line.1234"></a>
-<span class="sourceLineNo">1235</span> backingMap = BucketProtoUtils.fromPB(proto.getDeserializersMap(), proto.getBackingMap(),<a name="line.1235"></a>
-<span class="sourceLineNo">1236</span> this::createRecycler);<a name="line.1236"></a>
-<span class="sourceLineNo">1237</span> }<a name="line.1237"></a>
-<span class="sourceLineNo">1238</span><a name="line.1238"></a>
-<span class="sourceLineNo">1239</span> /**<a name="line.1239"></a>
-<span class="sourceLineNo">1240</span> * Check whether we tolerate IO error this time. If the duration of IOEngine<a name="line.1240"></a>
-<span class="sourceLineNo">1241</span> * throwing errors exceeds ioErrorsDurationTimeTolerated, we will disable the<a name="line.1241"></a>
-<span class="sourceLineNo">1242</span> * cache<a name="line.1242"></a>
-<span class="sourceLineNo">1243</span> */<a name="line.1243"></a>
-<span class="sourceLineNo">1244</span> private void checkIOErrorIsTolerated() {<a name="line.1244"></a>
-<span class="sourceLineNo">1245</span> long now = EnvironmentEdgeManager.currentTime();<a name="line.1245"></a>
-<span class="sourceLineNo">1246</span> // Do a single read to a local variable to avoid timing issue - HBASE-24454<a name="line.1246"></a>
-<span class="sourceLineNo">1247</span> long ioErrorStartTimeTmp = this.ioErrorStartTime;<a name="line.1247"></a>
-<span class="sourceLineNo">1248</span> if (ioErrorStartTimeTmp > 0) {<a name="line.1248"></a>
-<span class="sourceLineNo">1249</span> if (cacheEnabled && (now - ioErrorStartTimeTmp) > this.ioErrorsTolerationDuration) {<a name="line.1249"></a>
-<span class="sourceLineNo">1250</span> LOG.error("IO errors duration time has exceeded " + ioErrorsTolerationDuration +<a name="line.1250"></a>
-<span class="sourceLineNo">1251</span> "ms, disabling cache, please check your IOEngine");<a name="line.1251"></a>
-<span class="sourceLineNo">1252</span> disableCache();<a name="line.1252"></a>
-<span class="sourceLineNo">1253</span> }<a name="line.1253"></a>
-<span class="sourceLineNo">1254</span> } else {<a name="line.1254"></a>
-<span class="sourceLineNo">1255</span> this.ioErrorStartTime = now;<a name="line.1255"></a>
-<span class="sourceLineNo">1256</span> }<a name="line.1256"></a>
-<span class="sourceLineNo">1257</span> }<a name="line.1257"></a>
-<span class="sourceLineNo">1258</span><a name="line.1258"></a>
-<span class="sourceLineNo">1259</span> /**<a name="line.1259"></a>
-<span class="sourceLineNo">1260</span> * Used to shut down the cache -or- turn it off in the case of something broken.<a name="line.1260"></a>
-<span class="sourceLineNo">1261</span> */<a name="line.1261"></a>
-<span class="sourceLineNo">1262</span> private void disableCache() {<a name="line.1262"></a>
-<span class="sourceLineNo">1263</span> if (!cacheEnabled) return;<a name="line.1263"></a>
-<span class="sourceLineNo">1264</span> cacheEnabled = false;<a name="line.1264"></a>
-<span class="sourceLineNo">1265</span> ioEngine.shutdown();<a name="line.1265"></a>
-<span class="sourceLineNo">1266</span> this.scheduleThreadPool.shutdown();<a name="line.1266"></a>
-<span class="sourceLineNo">1267</span> for (int i = 0; i < writerThreads.length; ++i) writerThreads[i].interrupt();<a name="line.1267"></a>
-<span class="sourceLineNo">1268</span> this.ramCache.clear();<a name="line.1268"></a>
-<span class="sourceLineNo">1269</span> if (!ioEngine.isPersistent() || persistencePath == null) {<a name="line.1269"></a>
-<span class="sourceLineNo">1270</span> // If persistent ioengine and a path, we will serialize out the backingMap.<a name="line.1270"></a>
-<span class="sourceLineNo">1271</span> this.backingMap.clear();<a name="line.1271"></a>
-<span class="sourceLineNo">1272</span> }<a name="line.1272"></a>
-<span class="sourceLineNo">1273</span> }<a name="line.1273"></a>
-<span class="sourceLineNo">1274</span><a name="line.1274"></a>
-<span class="sourceLineNo">1275</span> private void join() throws InterruptedException {<a name="line.1275"></a>
-<span class="sourceLineNo">1276</span> for (int i = 0; i < writerThreads.length; ++i)<a name="line.1276"></a>
-<span class="sourceLineNo">1277</span> writerThreads[i].join();<a name="line.1277"></a>
-<span class="sourceLineNo">1278</span> }<a name="line.1278"></a>
-<span class="sourceLineNo">1279</span><a name="line.1279"></a>
-<span class="sourceLineNo">1280</span> @Override<a name="line.1280"></a>
-<span class="sourceLineNo">1281</span> public void shutdown() {<a name="line.1281"></a>
-<span class="sourceLineNo">1282</span> disableCache();<a name="line.1282"></a>
-<span class="sourceLineNo">1283</span> LOG.info("Shutdown bucket cache: IO persistent=" + ioEngine.isPersistent()<a name="line.1283"></a>
-<span class="sourceLineNo">1284</span> + "; path to write=" + persistencePath);<a name="line.1284"></a>
-<span class="sourceLineNo">1285</span> if (ioEngine.isPersistent() && persistencePath != null) {<a name="line.1285"></a>
-<span class="sourceLineNo">1286</span> try {<a name="line.1286"></a>
-<span class="sourceLineNo">1287</span> join();<a name="line.1287"></a>
-<span class="sourceLineNo">1288</span> persistToFile();<a name="line.1288"></a>
-<span class="sourceLineNo">1289</span> } catch (IOException ex) {<a name="line.1289"></a>
-<span class="sourceLineNo">1290</span> LOG.error("Unable to persist data on exit: " + ex.toString(), ex);<a name="line.1290"></a>
-<span class="sourceLineNo">1291</span> } catch (InterruptedException e) {<a name="line.1291"></a>
-<span class="sourceLineNo">1292</span> LOG.warn("Failed to persist data on exit", e);<a name="line.1292"></a>
-<span class="sourceLineNo">1293</span> }<a name="line.1293"></a>
-<span class="sourceLineNo">1294</span> }<a name="line.1294"></a>
-<span class="sourceLineNo">1295</span> }<a name="line.1295"></a>
-<span class="sourceLineNo">1296</span><a name="line.1296"></a>
-<span class="sourceLineNo">1297</span> @Override<a name="line.1297"></a>
-<span class="sourceLineNo">1298</span> public CacheStats getStats() {<a name="line.1298"></a>
-<span class="sourceLineNo">1299</span> return cacheStats;<a name="line.1299"></a>
-<span class="sourceLineNo">1300</span> }<a name="line.1300"></a>
-<span class="sourceLineNo">1301</span><a name="line.1301"></a>
-<span class="sourceLineNo">1302</span> public BucketAllocator getAllocator() {<a name="line.1302"></a>
-<span class="sourceLineNo">1303</span> return this.bucketAllocator;<a name="line.1303"></a>
+<span class="sourceLineNo">1224</span> if (!backingMap.getClass().getName().equals(mapclass)) {<a name="line.1224"></a>
+<span class="sourceLineNo">1225</span> throw new IOException("Class name for cache map mismatch: " + mapclass<a name="line.1225"></a>
+<span class="sourceLineNo">1226</span> + ", expected:" + backingMap.getClass().getName());<a name="line.1226"></a>
+<span class="sourceLineNo">1227</span> }<a name="line.1227"></a>
+<span class="sourceLineNo">1228</span> }<a name="line.1228"></a>
+<span class="sourceLineNo">1229</span><a name="line.1229"></a>
+<span class="sourceLineNo">1230</span> private void parsePB(BucketCacheProtos.BucketCacheEntry proto) throws IOException {<a name="line.1230"></a>
+<span class="sourceLineNo">1231</span> if (proto.hasChecksum()) {<a name="line.1231"></a>
+<span class="sourceLineNo">1232</span> ((PersistentIOEngine) ioEngine).verifyFileIntegrity(proto.getChecksum().toByteArray(),<a name="line.1232"></a>
+<span class="sourceLineNo">1233</span> algorithm);<a name="line.1233"></a>
+<span class="sourceLineNo">1234</span> } else {<a name="line.1234"></a>
+<span class="sourceLineNo">1235</span> // if has not checksum, it means the persistence file is old format<a name="line.1235"></a>
+<span class="sourceLineNo">1236</span> LOG.info("Persistent file is old format, it does not support verifying file integrity!");<a name="line.1236"></a>
+<span class="sourceLineNo">1237</span> }<a name="line.1237"></a>
+<span class="sourceLineNo">1238</span> verifyCapacityAndClasses(proto.getCacheCapacity(), proto.getIoClass(), proto.getMapClass());<a name="line.1238"></a>
+<span class="sourceLineNo">1239</span> backingMap = BucketProtoUtils.fromPB(proto.getDeserializersMap(), proto.getBackingMap(),<a name="line.1239"></a>
+<span class="sourceLineNo">1240</span> this::createRecycler);<a name="line.1240"></a>
+<span class="sourceLineNo">1241</span> }<a name="line.1241"></a>
+<span class="sourceLineNo">1242</span><a name="line.1242"></a>
+<span class="sourceLineNo">1243</span> /**<a name="line.1243"></a>
+<span class="sourceLineNo">1244</span> * Check whether we tolerate IO error this time. If the duration of IOEngine<a name="line.1244"></a>
+<span class="sourceLineNo">1245</span> * throwing errors exceeds ioErrorsDurationTimeTolerated, we will disable the<a name="line.1245"></a>
+<span class="sourceLineNo">1246</span> * cache<a name="line.1246"></a>
+<span class="sourceLineNo">1247</span> */<a name="line.1247"></a>
+<span class="sourceLineNo">1248</span> private void checkIOErrorIsTolerated() {<a name="line.1248"></a>
+<span class="sourceLineNo">1249</span> long now = EnvironmentEdgeManager.currentTime();<a name="line.1249"></a>
+<span class="sourceLineNo">1250</span> // Do a single read to a local variable to avoid timing issue - HBASE-24454<a name="line.1250"></a>
+<span class="sourceLineNo">1251</span> long ioErrorStartTimeTmp = this.ioErrorStartTime;<a name="line.1251"></a>
+<span class="sourceLineNo">1252</span> if (ioErrorStartTimeTmp > 0) {<a name="line.1252"></a>
+<span class="sourceLineNo">1253</span> if (cacheEnabled && (now - ioErrorStartTimeTmp) > this.ioErrorsTolerationDuration) {<a name="line.1253"></a>
+<span class="sourceLineNo">1254</span> LOG.error("IO errors duration time has exceeded " + ioErrorsTolerationDuration +<a name="line.1254"></a>
+<span class="sourceLineNo">1255</span> "ms, disabling cache, please check your IOEngine");<a name="line.1255"></a>
+<span class="sourceLineNo">1256</span> disableCache();<a name="line.1256"></a>
+<span class="sourceLineNo">1257</span> }<a name="line.1257"></a>
+<span class="sourceLineNo">1258</span> } else {<a name="line.1258"></a>
+<span class="sourceLineNo">1259</span> this.ioErrorStartTime = now;<a name="line.1259"></a>
+<span class="sourceLineNo">1260</span> }<a name="line.1260"></a>
+<span class="sourceLineNo">1261</span> }<a name="line.1261"></a>
+<span class="sourceLineNo">1262</span><a name="line.1262"></a>
+<span class="sourceLineNo">1263</span> /**<a name="line.1263"></a>
+<span class="sourceLineNo">1264</span> * Used to shut down the cache -or- turn it off in the case of something broken.<a name="line.1264"></a>
+<span class="sourceLineNo">1265</span> */<a name="line.1265"></a>
+<span class="sourceLineNo">1266</span> private void disableCache() {<a name="line.1266"></a>
+<span class="sourceLineNo">1267</span> if (!cacheEnabled) return;<a name="line.1267"></a>
+<span class="sourceLineNo">1268</span> cacheEnabled = false;<a name="line.1268"></a>
+<span class="sourceLineNo">1269</span> ioEngine.shutdown();<a name="line.1269"></a>
+<span class="sourceLineNo">1270</span> this.scheduleThreadPool.shutdown();<a name="line.1270"></a>
+<span class="sourceLineNo">1271</span> for (int i = 0; i < writerThreads.length; ++i) writerThreads[i].interrupt();<a name="line.1271"></a>
+<span class="sourceLineNo">1272</span> this.ramCache.clear();<a name="line.1272"></a>
+<span class="sourceLineNo">1273</span> if (!ioEngine.isPersistent() || persistencePath == null) {<a name="line.1273"></a>
+<span class="sourceLineNo">1274</span> // If persistent ioengine and a path, we will serialize out the backingMap.<a name="line.1274"></a>
+<span class="sourceLineNo">1275</span> this.backingMap.clear();<a name="line.1275"></a>
+<span class="sourceLineNo">1276</span> }<a name="line.1276"></a>
+<span class="sourceLineNo">1277</span> }<a name="line.1277"></a>
+<span class="sourceLineNo">1278</span><a name="line.1278"></a>
+<span class="sourceLineNo">1279</span> private void join() throws InterruptedException {<a name="line.1279"></a>
+<span class="sourceLineNo">1280</span> for (int i = 0; i < writerThreads.length; ++i)<a name="line.1280"></a>
+<span class="sourceLineNo">1281</span> writerThreads[i].join();<a name="line.1281"></a>
+<span class="sourceLineNo">1282</span> }<a name="line.1282"></a>
+<span class="sourceLineNo">1283</span><a name="line.1283"></a>
+<span class="sourceLineNo">1284</span> @Override<a name="line.1284"></a>
+<span class="sourceLineNo">1285</span> public void shutdown() {<a name="line.1285"></a>
+<span class="sourceLineNo">1286</span> disableCache();<a name="line.1286"></a>
+<span class="sourceLineNo">1287</span> LOG.info("Shutdown bucket cache: IO persistent=" + ioEngine.isPersistent()<a name="line.1287"></a>
+<span class="sourceLineNo">1288</span> + "; path to write=" + persistencePath);<a name="line.1288"></a>
+<span class="sourceLineNo">1289</span> if (ioEngine.isPersistent() && persistencePath != null) {<a name="line.1289"></a>
+<span class="sourceLineNo">1290</span> try {<a name="line.1290"></a>
+<span class="sourceLineNo">1291</span> join();<a name="line.1291"></a>
+<span class="sourceLineNo">1292</span> persistToFile();<a name="line.1292"></a>
+<span class="sourceLineNo">1293</span> } catch (IOException ex) {<a name="line.1293"></a>
+<span class="sourceLineNo">1294</span> LOG.error("Unable to persist data on exit: " + ex.toString(), ex);<a name="line.1294"></a>
+<span class="sourceLineNo">1295</span> } catch (InterruptedException e) {<a name="line.1295"></a>
+<span class="sourceLineNo">1296</span> LOG.warn("Failed to persist data on exit", e);<a name="line.1296"></a>
+<span class="sourceLineNo">1297</span> }<a name="line.1297"></a>
+<span class="sourceLineNo">1298</span> }<a name="line.1298"></a>
+<span class="sourceLineNo">1299</span> }<a name="line.1299"></a>
+<span class="sourceLineNo">1300</span><a name="line.1300"></a>
+<span class="sourceLineNo">1301</span> @Override<a name="line.1301"></a>
+<span class="sourceLineNo">1302</span> public CacheStats getStats() {<a name="line.1302"></a>
+<span class="sourceLineNo">1303</span> return cacheStats;<a name="line.1303"></a>
<span class="sourceLineNo">1304</span> }<a name="line.1304"></a>
<span class="sourceLineNo">1305</span><a name="line.1305"></a>
-<span class="sourceLineNo">1306</span> @Override<a name="line.1306"></a>
-<span class="sourceLineNo">1307</span> public long heapSize() {<a name="line.1307"></a>
-<span class="sourceLineNo">1308</span> return this.heapSize.sum();<a name="line.1308"></a>
-<span class="sourceLineNo">1309</span> }<a name="line.1309"></a>
-<span class="sourceLineNo">1310</span><a name="line.1310"></a>
-<span class="sourceLineNo">1311</span> @Override<a name="line.1311"></a>
-<span class="sourceLineNo">1312</span> public long size() {<a name="line.1312"></a>
-<span class="sourceLineNo">1313</span> return this.realCacheSize.sum();<a name="line.1313"></a>
-<span class="sourceLineNo">1314</span> }<a name="line.1314"></a>
-<span class="sourceLineNo">1315</span><a name="line.1315"></a>
-<span class="sourceLineNo">1316</span> @Override<a name="line.1316"></a>
-<span class="sourceLineNo">1317</span> public long getCurrentDataSize() {<a name="line.1317"></a>
-<span class="sourceLineNo">1318</span> return size();<a name="line.1318"></a>
-<span class="sourceLineNo">1319</span> }<a name="line.1319"></a>
-<span class="sourceLineNo">1320</span><a name="line.1320"></a>
-<span class="sourceLineNo">1321</span> @Override<a name="line.1321"></a>
-<span class="sourceLineNo">1322</span> public long getFreeSize() {<a name="line.1322"></a>
-<span class="sourceLineNo">1323</span> return this.bucketAllocator.getFreeSize();<a name="line.1323"></a>
-<span class="sourceLineNo">1324</span> }<a name="line.1324"></a>
-<span class="sourceLineNo">1325</span><a name="line.1325"></a>
-<span class="sourceLineNo">1326</span> @Override<a name="line.1326"></a>
-<span class="sourceLineNo">1327</span> public long getBlockCount() {<a name="line.1327"></a>
-<span class="sourceLineNo">1328</span> return this.blockNumber.sum();<a name="line.1328"></a>
-<span class="sourceLineNo">1329</span> }<a name="line.1329"></a>
-<span class="sourceLineNo">1330</span><a name="line.1330"></a>
-<span class="sourceLineNo">1331</span> @Override<a name="line.1331"></a>
-<span class="sourceLineNo">1332</span> public long getDataBlockCount() {<a name="line.1332"></a>
-<span class="sourceLineNo">1333</span> return getBlockCount();<a name="line.1333"></a>
-<span class="sourceLineNo">1334</span> }<a name="line.1334"></a>
-<span class="sourceLineNo">1335</span><a name="line.1335"></a>
-<span class="sourceLineNo">1336</span> @Override<a name="line.1336"></a>
-<span class="sourceLineNo">1337</span> public long getCurrentSize() {<a name="line.1337"></a>
-<span class="sourceLineNo">1338</span> return this.bucketAllocator.getUsedSize();<a name="line.1338"></a>
-<span class="sourceLineNo">1339</span> }<a name="line.1339"></a>
-<span class="sourceLineNo">1340</span><a name="line.1340"></a>
-<span class="sourceLineNo">1341</span> protected String getAlgorithm() {<a name="line.1341"></a>
-<span class="sourceLineNo">1342</span> return algorithm;<a name="line.1342"></a>
+<span class="sourceLineNo">1306</span> public BucketAllocator getAllocator() {<a name="line.1306"></a>
+<span class="sourceLineNo">1307</span> return this.bucketAllocator;<a name="line.1307"></a>
+<span class="sourceLineNo">1308</span> }<a name="line.1308"></a>
+<span class="sourceLineNo">1309</span><a name="line.1309"></a>
+<span class="sourceLineNo">1310</span> @Override<a name="line.1310"></a>
+<span class="sourceLineNo">1311</span> public long heapSize() {<a name="line.1311"></a>
+<span class="sourceLineNo">1312</span> return this.heapSize.sum();<a name="line.1312"></a>
+<span class="sourceLineNo">1313</span> }<a name="line.1313"></a>
+<span class="sourceLineNo">1314</span><a name="line.1314"></a>
+<span class="sourceLineNo">1315</span> @Override<a name="line.1315"></a>
+<span class="sourceLineNo">1316</span> public long size() {<a name="line.1316"></a>
+<span class="sourceLineNo">1317</span> return this.realCacheSize.sum();<a name="line.1317"></a>
+<span class="sourceLineNo">1318</span> }<a name="line.1318"></a>
+<span class="sourceLineNo">1319</span><a name="line.1319"></a>
+<span class="sourceLineNo">1320</span> @Override<a name="line.1320"></a>
+<span class="sourceLineNo">1321</span> public long getCurrentDataSize() {<a name="line.1321"></a>
+<span class="sourceLineNo">1322</span> return size();<a name="line.1322"></a>
+<span class="sourceLineNo">1323</span> }<a name="line.1323"></a>
+<span class="sourceLineNo">1324</span><a name="line.1324"></a>
+<span class="sourceLineNo">1325</span> @Override<a name="line.1325"></a>
+<span class="sourceLineNo">1326</span> public long getFreeSize() {<a name="line.1326"></a>
+<span class="sourceLineNo">1327</span> return this.bucketAllocator.getFreeSize();<a name="line.1327"></a>
+<span class="sourceLineNo">1328</span> }<a name="line.1328"></a>
+<span class="sourceLineNo">1329</span><a name="line.1329"></a>
+<span class="sourceLineNo">1330</span> @Override<a name="line.1330"></a>
+<span class="sourceLineNo">1331</span> public long getBlockCount() {<a name="line.1331"></a>
+<span class="sourceLineNo">1332</span> return this.blockNumber.sum();<a name="line.1332"></a>
+<span class="sourceLineNo">1333</span> }<a name="line.1333"></a>
+<span class="sourceLineNo">1334</span><a name="line.1334"></a>
+<span class="sourceLineNo">1335</span> @Override<a name="line.1335"></a>
+<span class="sourceLineNo">1336</span> public long getDataBlockCount() {<a name="line.1336"></a>
+<span class="sourceLineNo">1337</span> return getBlockCount();<a name="line.1337"></a>
+<span class="sourceLineNo">1338</span> }<a name="line.1338"></a>
+<span class="sourceLineNo">1339</span><a name="line.1339"></a>
+<span class="sourceLineNo">1340</span> @Override<a name="line.1340"></a>
+<span class="sourceLineNo">1341</span> public long getCurrentSize() {<a name="line.1341"></a>
+<span class="sourceLineNo">1342</span> return this.bucketAllocator.getUsedSize();<a name="line.1342"></a>
<span class="sourceLineNo">1343</span> }<a name="line.1343"></a>
<span class="sourceLineNo">1344</span><a name="line.1344"></a>
-<span class="sourceLineNo">1345</span> /**<a name="line.1345"></a>
-<span class="sourceLineNo">1346</span> * Evicts all blocks for a specific HFile.<a name="line.1346"></a>
-<span class="sourceLineNo">1347</span> * <p><a name="line.1347"></a>
-<span class="sourceLineNo">1348</span> * This is used for evict-on-close to remove all blocks of a specific HFile.<a name="line.1348"></a>
-<span class="sourceLineNo">1349</span> *<a name="line.1349"></a>
-<span class="sourceLineNo">1350</span> * @return the number of blocks evicted<a name="line.1350"></a>
-<span class="sourceLineNo">1351</span> */<a name="line.1351"></a>
-<span class="sourceLineNo">1352</span> @Override<a name="line.1352"></a>
-<span class="sourceLineNo">1353</span> public int evictBlocksByHfileName(String hfileName) {<a name="line.1353"></a>
-<span class="sourceLineNo">1354</span> Set<BlockCacheKey> keySet = blocksByHFile.subSet(<a name="line.1354"></a>
-<span class="sourceLineNo">1355</span> new BlockCacheKey(hfileName, Long.MIN_VALUE), true,<a name="line.1355"></a>
-<span class="sourceLineNo">1356</span> new BlockCacheKey(hfileName, Long.MAX_VALUE), true);<a name="line.1356"></a>
-<span class="sourceLineNo">1357</span><a name="line.1357"></a>
-<span class="sourceLineNo">1358</span> int numEvicted = 0;<a name="line.1358"></a>
-<span class="sourceLineNo">1359</span> for (BlockCacheKey key : keySet) {<a name="line.1359"></a>
-<span class="sourceLineNo">1360</span> if (evictBlock(key)) {<a name="line.1360"></a>
-<span class="sourceLineNo">1361</span> ++numEvicted;<a name="line.1361"></a>
-<span class="sourceLineNo">1362</span> }<a name="line.1362"></a>
-<span class="sourceLineNo">1363</span> }<a name="line.1363"></a>
-<span class="sourceLineNo">1364</span><a name="line.1364"></a>
-<span class="sourceLineNo">1365</span> return numEvicted;<a name="line.1365"></a>
-<span class="sourceLineNo">1366</span> }<a name="line.1366"></a>
-<span class="sourceLineNo">1367</span><a name="line.1367"></a>
-<span class="sourceLineNo">1368</span> /**<a name="line.1368"></a>
-<span class="sourceLineNo">1369</span> * Used to group bucket entries into priority buckets. There will be a<a name="line.1369"></a>
-<span class="sourceLineNo">1370</span> * BucketEntryGroup for each priority (single, multi, memory). Once bucketed,<a name="line.1370"></a>
-<span class="sourceLineNo">1371</span> * the eviction algorithm takes the appropriate number of elements out of each<a name="line.1371"></a>
-<span class="sourceLineNo">1372</span> * according to configuration parameters and their relative sizes.<a name="line.1372"></a>
-<span class="sourceLineNo">1373</span> */<a name="line.1373"></a>
-<span class="sourceLineNo">1374</span> private class BucketEntryGroup {<a name="line.1374"></a>
-<span class="sourceLineNo">1375</span><a name="line.1375"></a>
-<span class="sourceLineNo">1376</span> private CachedEntryQueue queue;<a name="line.1376"></a>
-<span class="sourceLineNo">1377</span> private long totalSize = 0;<a name="line.1377"></a>
-<span class="sourceLineNo">1378</span> private long bucketSize;<a name="line.1378"></a>
+<span class="sourceLineNo">1345</span> protected String getAlgorithm() {<a name="line.1345"></a>
+<span class="sourceLineNo">1346</span> return algorithm;<a name="line.1346"></a>
+<span class="sourceLineNo">1347</span> }<a name="line.1347"></a>
+<span class="sourceLineNo">1348</span><a name="line.1348"></a>
+<span class="sourceLineNo">1349</span> /**<a name="line.1349"></a>
+<span class="sourceLineNo">1350</span> * Evicts all blocks for a specific HFile.<a name="line.1350"></a>
+<span class="sourceLineNo">1351</span> * <p><a name="line.1351"></a>
+<span class="sourceLineNo">1352</span> * This is used for evict-on-close to remove all blocks of a specific HFile.<a name="line.1352"></a>
+<span class="sourceLineNo">1353</span> *<a name="line.1353"></a>
+<span class="sourceLineNo">1354</span> * @return the number of blocks evicted<a name="line.1354"></a>
+<span class="sourceLineNo">1355</span> */<a name="line.1355"></a>
+<span class="sourceLineNo">1356</span> @Override<a name="line.1356"></a>
+<span class="sourceLineNo">1357</span> public int evictBlocksByHfileName(String hfileName) {<a name="line.1357"></a>
+<span class="sourceLineNo">1358</span> Set<BlockCacheKey> keySet = blocksByHFile.subSet(<a name="line.1358"></a>
+<span class="sourceLineNo">1359</span> new BlockCacheKey(hfileName, Long.MIN_VALUE), true,<a name="line.1359"></a>
+<span class="sourceLineNo">1360</span> new BlockCacheKey(hfileName, Long.MAX_VALUE), true);<a name="line.1360"></a>
+<span class="sourceLineNo">1361</span><a name="line.1361"></a>
+<span class="sourceLineNo">1362</span> int numEvicted = 0;<a name="line.1362"></a>
+<span class="sourceLineNo">1363</span> for (BlockCacheKey key : keySet) {<a name="line.1363"></a>
+<span class="sourceLineNo">1364</span> if (evictBlock(key)) {<a name="line.1364"></a>
+<span class="sourceLineNo">1365</span> ++numEvicted;<a name="line.1365"></a>
+<span class="sourceLineNo">1366</span> }<a name="line.1366"></a>
+<span class="sourceLineNo">1367</span> }<a name="line.1367"></a>
+<span class="sourceLineNo">1368</span><a name="line.1368"></a>
+<span class="sourceLineNo">1369</span> return numEvicted;<a name="line.1369"></a>
+<span class="sourceLineNo">1370</span> }<a name="line.1370"></a>
+<span class="sourceLineNo">1371</span><a name="line.1371"></a>
+<span class="sourceLineNo">1372</span> /**<a name="line.1372"></a>
+<span class="sourceLineNo">1373</span> * Used to group bucket entries into priority buckets. There will be a<a name="line.1373"></a>
+<span class="sourceLineNo">1374</span> * BucketEntryGroup for each priority (single, multi, memory). Once bucketed,<a name="line.1374"></a>
+<span class="sourceLineNo">1375</span> * the eviction algorithm takes the appropriate number of elements out of each<a name="line.1375"></a>
+<span class="sourceLineNo">1376</span> * according to configuration parameters and their relative sizes.<a name="line.1376"></a>
+<span class="sourceLineNo">1377</span> */<a name="line.1377"></a>
+<span class="sourceLineNo">1378</span> private class BucketEntryGroup {<a name="line.1378"></a>
<span class="sourceLineNo">1379</span><a name="line.1379"></a>
-<span class="sourceLineNo">1380</span> public BucketEntryGroup(long bytesToFree, long blockSize, long bucketSize) {<a name="line.1380"></a>
-<span class="sourceLineNo">1381</span> this.bucketSize = bucketSize;<a name="line.1381"></a>
-<span class="sourceLineNo">1382</span> queue = new CachedEntryQueue(bytesToFree, blockSize);<a name="line.1382"></a>
-<span class="sourceLineNo">1383</span> totalSize = 0;<a name="line.1383"></a>
-<span class="sourceLineNo">1384</span> }<a name="line.1384"></a>
-<span class="sourceLineNo">1385</span><a name="line.1385"></a>
-<span class="sourceLineNo">1386</span> public void add(Map.Entry<BlockCacheKey, BucketEntry> block) {<a name="line.1386"></a>
-<span class="sourceLineNo">1387</span> totalSize += block.getValue().getLength();<a name="line.1387"></a>
-<span class="sourceLineNo">1388</span> queue.add(block);<a name="line.1388"></a>
-<span class="sourceLineNo">1389</span> }<a name="line.1389"></a>
-<span class="sourceLineNo">1390</span><a name="line.1390"></a>
-<span class="sourceLineNo">1391</span> public long free(long toFree) {<a name="line.1391"></a>
-<span class="sourceLineNo">1392</span> Map.Entry<BlockCacheKey, BucketEntry> entry;<a name="line.1392"></a>
-<span class="sourceLineNo">1393</span> long freedBytes = 0;<a name="line.1393"></a>
-<span class="sourceLineNo">1394</span> // TODO avoid a cycling siutation. We find no block which is not in use and so no way to free<a name="line.1394"></a>
-<span class="sourceLineNo">1395</span> // What to do then? Caching attempt fail? Need some changes in cacheBlock API?<a name="line.1395"></a>
-<span class="sourceLineNo">1396</span> while ((entry = queue.pollLast()) != null) {<a name="line.1396"></a>
-<span class="sourceLineNo">1397</span> BlockCacheKey blockCacheKey = entry.getKey();<a name="line.1397"></a>
-<span class="sourceLineNo">1398</span> BucketEntry be = entry.getValue();<a name="line.1398"></a>
-<span class="sourceLineNo">1399</span> if (evictBucketEntryIfNoRpcReferenced(blockCacheKey, be)) {<a name="line.1399"></a>
-<span class="sourceLineNo">1400</span> freedBytes += be.getLength();<a name="line.1400"></a>
-<span class="sourceLineNo">1401</span> }<a name="line.1401"></a>
-<span class="sourceLineNo">1402</span> if (freedBytes >= toFree) {<a name="line.1402"></a>
-<span class="sourceLineNo">1403</span> return freedBytes;<a name="line.1403"></a>
-<span class="sourceLineNo">1404</span> }<a name="line.1404"></a>
-<span class="sourceLineNo">1405</span> }<a name="line.1405"></a>
-<span class="sourceLineNo">1406</span> return freedBytes;<a name="line.1406"></a>
-<span class="sourceLineNo">1407</span> }<a name="line.1407"></a>
-<span class="sourceLineNo">1408</span><a name="line.1408"></a>
-<span class="sourceLineNo">1409</span> public long overflow() {<a name="line.1409"></a>
-<span class="sourceLineNo">1410</span> return totalSize - bucketSize;<a name="line.1410"></a>
+<span class="sourceLineNo">1380</span> private CachedEntryQueue queue;<a name="line.1380"></a>
+<span class="sourceLineNo">1381</span> private long totalSize = 0;<a name="line.1381"></a>
+<span class="sourceLineNo">1382</span> private long bucketSize;<a name="line.1382"></a>
+<span class="sourceLineNo">1383</span><a name="line.1383"></a>
+<span class="sourceLineNo">1384</span> public BucketEntryGroup(long bytesToFree, long blockSize, long bucketSize) {<a name="line.1384"></a>
+<span class="sourceLineNo">1385</span> this.bucketSize = bucketSize;<a name="line.1385"></a>
+<span class="sourceLineNo">1386</span> queue = new CachedEntryQueue(bytesToFree, blockSize);<a name="line.1386"></a>
+<span class="sourceLineNo">1387</span> totalSize = 0;<a name="line.1387"></a>
+<span class="sourceLineNo">1388</span> }<a name="line.1388"></a>
+<span class="sourceLineNo">1389</span><a name="line.1389"></a>
+<span class="sourceLineNo">1390</span> public void add(Map.Entry<BlockCacheKey, BucketEntry> block) {<a name="line.1390"></a>
+<span class="sourceLineNo">1391</span> totalSize += block.getValue().getLength();<a name="line.1391"></a>
+<span class="sourceLineNo">1392</span> queue.add(block);<a name="line.1392"></a>
+<span class="sourceLineNo">1393</span> }<a name="line.1393"></a>
+<span class="sourceLineNo">1394</span><a name="line.1394"></a>
+<span class="sourceLineNo">1395</span> public long free(long toFree) {<a name="line.1395"></a>
+<span class="sourceLineNo">1396</span> Map.Entry<BlockCacheKey, BucketEntry> entry;<a name="line.1396"></a>
+<span class="sourceLineNo">1397</span> long freedBytes = 0;<a name="line.1397"></a>
+<span class="sourceLineNo">1398</span> // TODO avoid a cycling siutation. We find no block which is not in use and so no way to free<a name="line.1398"></a>
+<span class="sourceLineNo">1399</span> // What to do then? Caching attempt fail? Need some changes in cacheBlock API?<a name="line.1399"></a>
+<span class="sourceLineNo">1400</span> while ((entry = queue.pollLast()) != null) {<a name="line.1400"></a>
+<span class="sourceLineNo">1401</span> BlockCacheKey blockCacheKey = entry.getKey();<a name="line.1401"></a>
+<span class="sourceLineNo">1402</span> BucketEntry be = entry.getValue();<a name="line.1402"></a>
+<span class="sourceLineNo">1403</span> if (evictBucketEntryIfNoRpcReferenced(blockCacheKey, be)) {<a name="line.1403"></a>
+<span class="sourceLineNo">1404</span> freedBytes += be.getLength();<a name="line.1404"></a>
+<span class="sourceLineNo">1405</span> }<a name="line.1405"></a>
+<span class="sourceLineNo">1406</span> if (freedBytes >= toFree) {<a name="line.1406"></a>
+<span class="sourceLineNo">1407</span> return freedBytes;<a name="line.1407"></a>
+<span class="sourceLineNo">1408</span> }<a name="line.1408"></a>
+<span class="sourceLineNo">1409</span> }<a name="line.1409"></a>
+<span class="sourceLineNo">1410</span> return freedBytes;<a name="line.1410"></a>
<span class="sourceLineNo">1411</span> }<a name="line.1411"></a>
<span class="sourceLineNo">1412</span><a name="line.1412"></a>
-<span class="sourceLineNo">1413</span> public long totalSize() {<a name="line.1413"></a>
-<span class="sourceLineNo">1414</span> return totalSize;<a name="line.1414"></a>
+<span class="sourceLineNo">1413</span> public long overflow() {<a name="line.1413"></a>
+<span class="sourceLineNo">1414</span> return totalSize - bucketSize;<a name="line.1414"></a>
<span class="sourceLineNo">1415</span> }<a name="line.1415"></a>
-<span class="sourceLineNo">1416</span> }<a name="line.1416"></a>
-<span class="sourceLineNo">1417</span><a name="line.1417"></a>
-<span class="sourceLineNo">1418</span> /**<a name="line.1418"></a>
-<span class="sourceLineNo">1419</span> * Block Entry stored in the memory with key,data and so on<a name="line.1419"></a>
-<span class="sourceLineNo">1420</span> */<a name="line.1420"></a>
-<span class="sourceLineNo">1421</span> static class RAMQueueEntry {<a name="line.1421"></a>
-<span class="sourceLineNo">1422</span> private final BlockCacheKey key;<a name="line.1422"></a>
-<span class="sourceLineNo">1423</span> private final Cacheable data;<a name="line.1423"></a>
-<span class="sourceLineNo">1424</span> private long accessCounter;<a name="line.1424"></a>
-<span class="sourceLineNo">1425</span> private boolean inMemory;<a name="line.1425"></a>
-<span class="sourceLineNo">1426</span><a name="line.1426"></a>
-<span class="sourceLineNo">1427</span> RAMQueueEntry(BlockCacheKey bck, Cacheable data, long accessCounter, boolean inMemory) {<a name="line.1427"></a>
-<span class="sourceLineNo">1428</span> this.key = bck;<a name="line.1428"></a>
-<span class="sourceLineNo">1429</span> this.data = data;<a name="line.1429"></a>
-<span class="sourceLineNo">1430</span> this.accessCounter = accessCounter;<a name="line.1430"></a>
-<span class="sourceLineNo">1431</span> this.inMemory = inMemory;<a name="line.1431"></a>
-<span class="sourceLineNo">1432</span> }<a name="line.1432"></a>
-<span class="sourceLineNo">1433</span><a name="line.1433"></a>
-<span class="sourceLineNo">1434</span> public Cacheable getData() {<a name="line.1434"></a>
-<span class="sourceLineNo">1435</span> return data;<a name="line.1435"></a>
+<span class="sourceLineNo">1416</span><a name="line.1416"></a>
+<span class="sourceLineNo">1417</span> public long totalSize() {<a name="line.1417"></a>
+<span class="sourceLineNo">1418</span> return totalSize;<a name="line.1418"></a>
+<span class="sourceLineNo">1419</span> }<a name="line.1419"></a>
+<span class="sourceLineNo">1420</span> }<a name="line.1420"></a>
+<span class="sourceLineNo">1421</span><a name="line.1421"></a>
+<span class="sourceLineNo">1422</span> /**<a name="line.1422"></a>
+<span class="sourceLineNo">1423</span> * Block Entry stored in the memory with key,data and so on<a name="line.1423"></a>
+<span class="sourceLineNo">1424</span> */<a name="line.1424"></a>
+<span class="sourceLineNo">1425</span> static class RAMQueueEntry {<a name="line.1425"></a>
+<span class="sourceLineNo">1426</span> private final BlockCacheKey key;<a name="line.1426"></a>
+<span class="sourceLineNo">1427</span> private final Cacheable data;<a name="line.1427"></a>
+<span class="sourceLineNo">1428</span> private long accessCounter;<a name="line.1428"></a>
+<span class="sourceLineNo">1429</span> private boolean inMemory;<a name="line.1429"></a>
+<span class="sourceLineNo">1430</span><a name="line.1430"></a>
+<span class="sourceLineNo">1431</span> RAMQueueEntry(BlockCacheKey bck, Cacheable data, long accessCounter, boolean inMemory) {<a name="line.1431"></a>
+<span class="sourceLineNo">1432</span> this.key = bck;<a name="line.1432"></a>
+<span class="sourceLineNo">1433</span> this.data = data;<a name="line.1433"></a>
+<span class="sourceLineNo">1434</span> this.accessCounter = accessCounter;<a name="line.1434"></a>
+<span class="sourceLineNo">1435</span> this.inMemory = inMemory;<a name="line.1435"></a>
<span class="sourceLineNo">1436</span> }<a name="line.1436"></a>
<span class="sourceLineNo">1437</span><a name="line.1437"></a>
-<span class="sourceLineNo">1438</span> public BlockCacheKey getKey() {<a name="line.1438"></a>
-<span class="sourceLineNo">1439</span> return key;<a name="line.1439"></a>
+<span class="sourceLineNo">1438</span> public Cacheable getData() {<a name="line.1438"></a>
+<span class="sourceLineNo">1439</span> return data;<a name="line.1439"></a>
<span class="sourceLineNo">1440</span> }<a name="line.1440"></a>
<span class="sourceLineNo">1441</span><a name="line.1441"></a>
-<span class="sourceLineNo">1442</span> public void access(long accessCounter) {<a name="line.1442"></a>
-<span class="sourceLineNo">1443</span> this.accessCounter = accessCounter;<a name="line.1443"></a>
+<span class="sourceLineNo">1442</span> public BlockCacheKey getKey() {<a name="line.1442"></a>
+<span class="sourceLineNo">1443</span> return key;<a name="line.1443"></a>
<span class="sourceLineNo">1444</span> }<a name="line.1444"></a>
<span class="sourceLineNo">1445</span><a name="line.1445"></a>
-<span class="sourceLineNo">1446</span> private ByteBuffAllocator getByteBuffAllocator() {<a name="line.1446"></a>
-<span class="sourceLineNo">1447</span> if (data instanceof HFileBlock) {<a name="line.1447"></a>
-<span class="sourceLineNo">1448</span> return ((HFileBlock) data).getByteBuffAllocator();<a name="line.1448"></a>
-<span class="sourceLineNo">1449</span> }<a name="line.1449"></a>
-<span class="sourceLineNo">1450</span> return ByteBuffAllocator.HEAP;<a name="line.1450"></a>
-<span class="sourceLineNo">1451</span> }<a name="line.1451"></a>
-<span class="sourceLineNo">1452</span><a name="line.1452"></a>
-<span class="sourceLineNo">1453</span> public BucketEntry writeToCache(final IOEngine ioEngine, final BucketAllocator alloc,<a name="line.1453"></a>
-<span class="sourceLineNo">1454</span> final LongAdder realCacheSize, Function<BucketEntry, Recycler> createRecycler)<a name="line.1454"></a>
-<span class="sourceLineNo">1455</span> throws IOException {<a name="line.1455"></a>
-<span class="sourceLineNo">1456</span> int len = data.getSerializedLength();<a name="line.1456"></a>
-<span class="sourceLineNo">1457</span> // This cacheable thing can't be serialized<a name="line.1457"></a>
-<span class="sourceLineNo">1458</span> if (len == 0) {<a name="line.1458"></a>
-<span class="sourceLineNo">1459</span> return null;<a name="line.1459"></a>
-<span class="sourceLineNo">1460</span> }<a name="line.1460"></a>
-<span class="sourceLineNo">1461</span> long offset = alloc.allocateBlock(len);<a name="line.1461"></a>
-<span class="sourceLineNo">1462</span> boolean succ = false;<a name="line.1462"></a>
-<span class="sourceLineNo">1463</span> BucketEntry bucketEntry = null;<a name="line.1463"></a>
-<span class="sourceLineNo">1464</span> try {<a name="line.1464"></a>
-<span class="sourceLineNo">1465</span> bucketEntry = new BucketEntry(offset, len, accessCounter, inMemory, createRecycler,<a name="line.1465"></a>
-<span class="sourceLineNo">1466</span> getByteBuffAllocator());<a name="line.1466"></a>
-<span class="sourceLineNo">1467</span> bucketEntry.setDeserializerReference(data.getDeserializer());<a name="line.1467"></a>
-<span class="sourceLineNo">1468</span> if (data instanceof HFileBlock) {<a name="line.1468"></a>
-<span class="sourceLineNo">1469</span> // If an instance of HFileBlock, save on some allocations.<a name="line.1469"></a>
-<span class="sourceLineNo">1470</span> HFileBlock block = (HFileBlock) data;<a name="line.1470"></a>
-<span class="sourceLineNo">1471</span> ByteBuff sliceBuf = block.getBufferReadOnly();<a name="line.1471"></a>
-<span class="sourceLineNo">1472</span> ByteBuffer metadata = block.getMetaData();<a name="line.1472"></a>
-<span class="sourceLineNo">1473</span> ioEngine.write(sliceBuf, offset);<a name="line.1473"></a>
-<span class="sourceLineNo">1474</span> ioEngine.write(metadata, offset + len - metadata.limit());<a name="line.1474"></a>
-<span class="sourceLineNo">1475</span> } else {<a name="line.1475"></a>
-<span class="sourceLineNo">1476</span> // Only used for testing.<a name="line.1476"></a>
-<span class="sourceLineNo">1477</span> ByteBuffer bb = ByteBuffer.allocate(len);<a name="line.1477"></a>
-<span class="sourceLineNo">1478</span> data.serialize(bb, true);<a name="line.1478"></a>
-<span class="sourceLineNo">1479</span> ioEngine.write(bb, offset);<a name="line.1479"></a>
-<span class="sourceLineNo">1480</span> }<a name="line.1480"></a>
-<span class="sourceLineNo">1481</span> succ = true;<a name="line.1481"></a>
-<span class="sourceLineNo">1482</span> } finally {<a name="line.1482"></a>
-<span class="sourceLineNo">1483</span> if (!succ) {<a name="line.1483"></a>
-<span class="sourceLineNo">1484</span> alloc.freeBlock(offset);<a name="line.1484"></a>
-<span class="sourceLineNo">1485</span> }<a name="line.1485"></a>
-<span class="sourceLineNo">1486</span> }<a name="line.1486"></a>
-<span class="sourceLineNo">1487</span> realCacheSize.add(len);<a name="line.1487"></a>
-<span class="sourceLineNo">1488</span> return bucketEntry;<a name="line.1488"></a>
-<span class="sourceLineNo">1489</span> }<a name="line.1489"></a>
-<span class="sourceLineNo">1490</span> }<a name="line.1490"></a>
-<span class="sourceLineNo">1491</span><a name="line.1491"></a>
-<span class="sourceLineNo">1492</span> /**<a name="line.1492"></a>
-<span class="sourceLineNo">1493</span> * Only used in test<a name="line.1493"></a>
-<span class="sourceLineNo">1494</span> * @throws InterruptedException<a name="line.1494"></a>
-<span class="sourceLineNo">1495</span> */<a name="line.1495"></a>
-<span class="sourceLineNo">1496</span> void stopWriterThreads() throws InterruptedException {<a name="line.1496"></a>
-<span class="sourceLineNo">1497</span> for (WriterThread writerThread : writerThreads) {<a name="line.1497"></a>
-<span class="sourceLineNo">1498</span> writerThread.disableWriter();<a name="line.1498"></a>
-<span class="sourceLineNo">1499</span> writerThread.interrupt();<a name="line.1499"></a>
-<span class="sourceLineNo">1500</span> writerThread.join();<a name="line.1500"></a>
-<span class="sourceLineNo">1501</span> }<a name="line.1501"></a>
-<span class="sourceLineNo">1502</span> }<a name="line.1502"></a>
-<span class="sourceLineNo">1503</span><a name="line.1503"></a>
-<span class="sourceLineNo">1504</span> @Override<a name="line.1504"></a>
-<span class="sourceLineNo">1505</span> public Iterator<CachedBlock> iterator() {<a name="line.1505"></a>
-<span class="sourceLineNo">1506</span> // Don't bother with ramcache since stuff is in here only a little while.<a name="line.1506"></a>
-<span class="sourceLineNo">1507</span> final Iterator<Map.Entry<BlockCacheKey, BucketEntry>> i =<a name="line.1507"></a>
-<span class="sourceLineNo">1508</span> this.backingMap.entrySet().iterator();<a name="line.1508"></a>
-<span class="sourceLineNo">1509</span> return new Iterator<CachedBlock>() {<a name="line.1509"></a>
-<span class="sourceLineNo">1510</span> private final long now = System.nanoTime();<a name="line.1510"></a>
-<span class="sourceLineNo">1511</span><a name="line.1511"></a>
-<span class="sourceLineNo">1512</span> @Override<a name="line.1512"></a>
-<span class="sourceLineNo">1513</span> public boolean hasNext() {<a name="line.1513"></a>
-<span class="sourceLineNo">1514</span> return i.hasNext();<a name="line.1514"></a>
-<span class="sourceLineNo">1515</span> }<a name="line.1515"></a>
-<span class="sourceLineNo">1516</span><a name="line.1516"></a>
-<span class="sourceLineNo">1517</span> @Override<a name="line.1517"></a>
-<span class="sourceLineNo">1518</span> public CachedBlock next() {<a name="line.1518"></a>
-<span class="sourceLineNo">1519</span> final Map.Entry<BlockCacheKey, BucketEntry> e = i.next();<a name="line.1519"></a>
-<span class="sourceLineNo">1520</span> return new CachedBlock() {<a name="line.1520"></a>
-<span class="sourceLineNo">1521</span> @Override<a name="line.1521"></a>
-<span class="sourceLineNo">1522</span> public String toString() {<a name="line.1522"></a>
-<span class="sourceLineNo">1523</span> return BlockCacheUtil.toString(this, now);<a name="line.1523"></a>
-<span class="sourceLineNo">1524</span> }<a name="line.1524"></a>
-<span class="sourceLineNo">1525</span><a name="line.1525"></a>
-<span class="sourceLineNo">1526</span> @Override<a name="line.1526"></a>
-<span class="sourceLineNo">1527</span> public BlockPriority getBlockPriority() {<a name="line.1527"></a>
-<span class="sourceLineNo">1528</span> return e.getValue().getPriority();<a name="line.1528"></a>
-<span class="sourceLineNo">1529</span> }<a name="line.1529"></a>
-<span class="sourceLineNo">1530</span><a name="line.1530"></a>
-<span class="sourceLineNo">1531</span> @Override<a name="line.1531"></a>
-<span class="sourceLineNo">1532</span> public BlockType getBlockType() {<a name="line.1532"></a>
-<span class="sourceLineNo">1533</span> // Not held by BucketEntry. Could add it if wanted on BucketEntry creation.<a name="line.1533"></a>
-<span class="sourceLineNo">1534</span> return null;<a name="line.1534"></a>
-<span class="sourceLineNo">1535</span> }<a name="line.1535"></a>
-<span class="sourceLineNo">1536</span><a name="line.1536"></a>
-<span class="sourceLineNo">1537</span> @Override<a name="line.1537"></a>
-<span class="sourceLineNo">1538</span> public long getOffset() {<a name="line.1538"></a>
-<span class="sourceLineNo">1539</span> return e.getKey().getOffset();<a name="line.1539"></a>
-<span class="sourceLineNo">1540</span> }<a name="line.1540"></a>
-<span class="sourceLineNo">1541</span><a name="line.1541"></a>
-<span class="sourceLineNo">1542</span> @Override<a name="line.1542"></a>
-<span class="sourceLineNo">1543</span> public long getSize() {<a name="line.1543"></a>
-<span class="sourceLineNo">1544</span> return e.getValue().getLength();<a name="line.1544"></a>
-<span class="sourceLineNo">1545</span> }<a name="line.1545"></a>
-<span class="sourceLineNo">1546</span><a name="line.1546"></a>
-<span class="sourceLineNo">1547</span> @Override<a name="line.1547"></a>
-<span class="sourceLineNo">1548</span> public long getCachedTime() {<a name="line.1548"></a>
-<span class="sourceLineNo">1549</span> return e.getValue().getCachedTime();<a name="line.1549"></a>
-<span class="sourceLineNo">1550</span> }<a name="line.1550"></a>
-<span class="sourceLineNo">1551</span><a name="line.1551"></a>
-<span class="sourceLineNo">1552</span> @Override<a name="line.1552"></a>
-<span class="sourceLineNo">1553</span> public String getFilename() {<a name="line.1553"></a>
-<span class="sourceLineNo">1554</span> return e.getKey().getHfileName();<a name="line.1554"></a>
-<span class="sourceLineNo">1555</span> }<a name="line.1555"></a>
-<span class="sourceLineNo">1556</span><a name="line.1556"></a>
-<span class="sourceLineNo">1557</span> @Override<a name="line.1557"></a>
-<span class="sourceLineNo">1558</span> public int compareTo(CachedBlock other) {<a name="line.1558"></a>
-<span class="sourceLineNo">1559</span> int diff = this.getFilename().compareTo(other.getFilename());<a name="line.1559"></a>
-<span class="sourceLineNo">1560</span> if (diff != 0) return diff;<a name="line.1560"></a>
-<span class="sourceLineNo">1561</span><a name="line.1561"></a>
-<span class="sourceLineNo">1562</span> diff = Long.compare(this.getOffset(), other.getOffset());<a name="line.1562"></a>
-<span class="sourceLineNo">1563</span> if (diff != 0) return diff;<a name="line.1563"></a>
-<span class="sourceLineNo">1564</span> if (other.getCachedTime() < 0 || this.getCachedTime() < 0) {<a name="line.1564"></a>
-<span class="sourceLineNo">1565</span> throw new IllegalStateException("" + this.getCachedTime() + ", " +<a name="line.1565"></a>
-<span class="sourceLineNo">1566</span> other.getCachedTime());<a name="line.1566"></a>
-<span class="sourceLineNo">1567</span> }<a name="line.1567"></a>
-<span class="sourceLineNo">1568</span> return Long.compare(other.getCachedTime(), this.getCachedTime());<a name="line.1568"></a>
-<span class="sourceLineNo">1569</span> }<a name="line.1569"></a>
-<span class="sourceLineNo">1570</span><a name="line.1570"></a>
-<span class="sourceLineNo">1571</span> @Override<a name="line.1571"></a>
-<span class="sourceLineNo">1572</span> public int hashCode() {<a name="line.1572"></a>
-<span class="sourceLineNo">1573</span> return e.getKey().hashCode();<a name="line.1573"></a>
-<span class="sourceLineNo">1574</span> }<a name="line.1574"></a>
-<span class="sourceLineNo">1575</span><a name="line.1575"></a>
-<span class="sourceLineNo">1576</span> @Override<a name="line.1576"></a>
-<span class="sourceLineNo">1577</span> public boolean equals(Object obj) {<a name="line.1577"></a>
-<span class="sourceLineNo">1578</span> if (obj instanceof CachedBlock) {<a name="line.1578"></a>
-<span class="sourceLineNo">1579</span> CachedBlock cb = (CachedBlock)obj;<a name="line.1579"></a>
-<span class="sourceLineNo">1580</span> return compareTo(cb) == 0;<a name="line.1580"></a>
-<span class="sourceLineNo">1581</span> } else {<a name="line.1581"></a>
-<span class="sourceLineNo">1582</span> return false;<a name="line.1582"></a>
-<span class="sourceLineNo">1583</span> }<a name="line.1583"></a>
-<span class="sourceLineNo">1584</span> }<a name="line.1584"></a>
-<span class="sourceLineNo">1585</span> };<a name="line.1585"></a>
-<span class="sourceLineNo">1586</span> }<a name="line.1586"></a>
-<span class="sourceLineNo">1587</span><a name="line.1587"></a>
-<span class="sourceLineNo">1588</span> @Override<a name="line.1588"></a>
-<span class="sourceLineNo">1589</span> public void remove() {<a name="line.1589"></a>
-<span class="sourceLineNo">1590</span> throw new UnsupportedOperationException();<a name="line.1590"></a>
-<span class="sourceLineNo">1591</span> }<a name="line.1591"></a>
-<span class="sourceLineNo">1592</span> };<a name="line.1592"></a>
-<span class="sourceLineNo">1593</span> }<a name="line.1593"></a>
-<span class="sourceLineNo">1594</span><a name="line.1594"></a>
-<span class="sourceLineNo">1595</span> @Override<a name="line.1595"></a>
-<span class="sourceLineNo">1596</span> public BlockCache[] getBlockCaches() {<a name="line.1596"></a>
-<span class="sourceLineNo">1597</span> return null;<a name="line.1597"></a>
-<span class="sourceLineNo">1598</span> }<a name="line.1598"></a>
-<span class="sourceLineNo">1599</span><a name="line.1599"></a>
-<span class="sourceLineNo">1600</span> public int getRpcRefCount(BlockCacheKey cacheKey) {<a name="line.1600"></a>
-<span class="sourceLineNo">1601</span> BucketEntry bucketEntry = backingMap.get(cacheKey);<a name="line.1601"></a>
-<span class="sourceLineNo">1602</span> if (bucketEntry != null) {<a name="line.1602"></a>
-<span class="sourceLineNo">1603</span> return bucketEntry.refCnt() - (bucketEntry.markedAsEvicted.get() ? 0 : 1);<a name="line.1603"></a>
-<span class="sourceLineNo">1604</span> }<a name="line.1604"></a>
-<span class="sourceLineNo">1605</span> return 0;<a name="line.1605"></a>
-<span class="sourceLineNo">1606</span> }<a name="line.1606"></a>
-<span class="sourceLineNo">1607</span><a name="line.1607"></a>
-<span class="sourceLineNo">1608</span> float getAcceptableFactor() {<a name="line.1608"></a>
-<span class="sourceLineNo">1609</span> return acceptableFactor;<a name="line.1609"></a>
+<span class="sourceLineNo">1446</span> public void access(long accessCounter) {<a name="line.1446"></a>
+<span class="sourceLineNo">1447</span> this.accessCounter = accessCounter;<a name="line.1447"></a>
+<span class="sourceLineNo">1448</span> }<a name="line.1448"></a>
+<span class="sourceLineNo">1449</span><a name="line.1449"></a>
+<span class="sourceLineNo">1450</span> private ByteBuffAllocator getByteBuffAllocator() {<a name="line.1450"></a>
+<span class="sourceLineNo">1451</span> if (data instanceof HFileBlock) {<a name="line.1451"></a>
+<span class="sourceLineNo">1452</span> return ((HFileBlock) data).getByteBuffAllocator();<a name="line.1452"></a>
+<span class="sourceLineNo">1453</span> }<a name="line.1453"></a>
+<span class="sourceLineNo">1454</span> return ByteBuffAllocator.HEAP;<a name="line.1454"></a>
+<span class="sourceLineNo">1455</span> }<a name="line.1455"></a>
+<span class="sourceLineNo">1456</span><a name="line.1456"></a>
+<span class="sourceLineNo">1457</span> public BucketEntry writeToCache(final IOEngine ioEngine, final BucketAllocator alloc,<a name="line.1457"></a>
+<span class="sourceLineNo">1458</span> final LongAdder realCacheSize, Function<BucketEntry, Recycler> createRecycler)<a name="line.1458"></a>
+<span class="sourceLineNo">1459</span> throws IOException {<a name="line.1459"></a>
+<span class="sourceLineNo">1460</span> int len = data.getSerializedLength();<a name="line.1460"></a>
+<span class="sourceLineNo">1461</span> // This cacheable thing can't be serialized<a name="line.1461"></a>
+<span class="sourceLineNo">1462</span> if (len == 0) {<a name="line.1462"></a>
+<span class="sourceLineNo">1463</span> return null;<a name="line.1463"></a>
+<span class="sourceLineNo">1464</span> }<a name="line.1464"></a>
+<span class="sourceLineNo">1465</span> long offset = alloc.allocateBlock(len);<a name="line.1465"></a>
+<span class="sourceLineNo">1466</span> boolean succ = false;<a name="line.1466"></a>
+<span class="sourceLineNo">1467</span> BucketEntry bucketEntry = null;<a name="line.1467"></a>
+<span class="sourceLineNo">1468</span> try {<a name="line.1468"></a>
+<span class="sourceLineNo">1469</span> bucketEntry = new BucketEntry(offset, len, accessCounter, inMemory, createRecycler,<a name="line.1469"></a>
+<span class="sourceLineNo">1470</span> getByteBuffAllocator());<a name="line.1470"></a>
+<span class="sourceLineNo">1471</span> bucketEntry.setDeserializerReference(data.getDeserializer());<a name="line.1471"></a>
+<span class="sourceLineNo">1472</span> if (data instanceof HFileBlock) {<a name="line.1472"></a>
+<span class="sourceLineNo">1473</span> // If an instance of HFileBlock, save on some allocations.<a name="line.1473"></a>
+<span class="sourceLineNo">1474</span> HFileBlock block = (HFileBlock) data;<a name="line.1474"></a>
+<span class="sourceLineNo">1475</span> ByteBuff sliceBuf = block.getBufferReadOnly();<a name="line.1475"></a>
+<span class="sourceLineNo">1476</span> ByteBuffer metadata = block.getMetaData();<a name="line.1476"></a>
+<span class="sourceLineNo">1477</span> ioEngine.write(sliceBuf, offset);<a name="line.1477"></a>
+<span class="sourceLineNo">1478</span> ioEngine.write(metadata, offset + len - metadata.limit());<a name="line.1478"></a>
+<span class="sourceLineNo">1479</span> } else {<a name="line.1479"></a>
+<span class="sourceLineNo">1480</span> // Only used for testing.<a name="line.1480"></a>
+<span class="sourceLineNo">1481</span> ByteBuffer bb = ByteBuffer.allocate(len);<a name="line.1481"></a>
+<span class="sourceLineNo">1482</span> data.serialize(bb, true);<a name="line.1482"></a>
+<span class="sourceLineNo">1483</span> ioEngine.write(bb, offset);<a name="line.1483"></a>
+<span class="sourceLineNo">1484</span> }<a name="line.1484"></a>
+<span class="sourceLineNo">1485</span> succ = true;<a name="line.1485"></a>
+<span class="sourceLineNo">1486</span> } finally {<a name="line.1486"></a>
+<span class="sourceLineNo">1487</span> if (!succ) {<a name="line.1487"></a>
+<span class="sourceLineNo">1488</span> alloc.freeBlock(offset);<a name="line.1488"></a>
+<span class="sourceLineNo">1489</span> }<a name="line.1489"></a>
+<span class="sourceLineNo">1490</span> }<a name="line.1490"></a>
+<span class="sourceLineNo">1491</span> realCacheSize.add(len);<a name="line.1491"></a>
+<span class="sourceLineNo">1492</span> return bucketEntry;<a name="line.1492"></a>
+<span class="sourceLineNo">1493</span> }<a name="line.1493"></a>
+<span class="sourceLineNo">1494</span> }<a name="line.1494"></a>
+<span class="sourceLineNo">1495</span><a name="line.1495"></a>
+<span class="sourceLineNo">1496</span> /**<a name="line.1496"></a>
+<span class="sourceLineNo">1497</span> * Only used in test<a name="line.1497"></a>
+<span class="sourceLineNo">1498</span> * @throws InterruptedException<a name="line.1498"></a>
+<span class="sourceLineNo">1499</span> */<a name="line.1499"></a>
+<span class="sourceLineNo">1500</span> void stopWriterThreads() throws InterruptedException {<a name="line.1500"></a>
+<span class="sourceLineNo">1501</span> for (WriterThread writerThread : writerThreads) {<a name="line.1501"></a>
+<span class="sourceLineNo">1502</span> writerThread.disableWriter();<a name="line.1502"></a>
+<span class="sourceLineNo">1503</span> writerThread.interrupt();<a name="line.1503"></a>
+<span class="sourceLineNo">1504</span> writerThread.join();<a name="line.1504"></a>
+<span class="sourceLineNo">1505</span> }<a name="line.1505"></a>
+<span class="sourceLineNo">1506</span> }<a name="line.1506"></a>
+<span class="sourceLineNo">1507</span><a name="line.1507"></a>
+<span class="sourceLineNo">1508</span> @Override<a name="line.1508"></a>
+<span class="sourceLineNo">1509</span> public Iterator<CachedBlock> iterator() {<a name="line.1509"></a>
+<span class="sourceLineNo">1510</span> // Don't bother with ramcache since stuff is in here only a little while.<a name="line.1510"></a>
+<span class="sourceLineNo">1511</span> final Iterator<Map.Entry<BlockCacheKey, BucketEntry>> i =<a name="line.1511"></a>
+<span class="sourceLineNo">1512</span> this.backingMap.entrySet().iterator();<a name="line.1512"></a>
+<span class="sourceLineNo">1513</span> return new Iterator<CachedBlock>() {<a name="line.1513"></a>
+<span class="sourceLineNo">1514</span> private final long now = System.nanoTime();<a name="line.1514"></a>
+<span class="sourceLineNo">1515</span><a name="line.1515"></a>
+<span class="sourceLineNo">1516</span> @Override<a name="line.1516"></a>
+<span class="sourceLineNo">1517</span> public boolean hasNext() {<a name="line.1517"></a>
+<span class="sourceLineNo">1518</span> return i.hasNext();<a name="line.1518"></a>
+<span class="sourceLineNo">1519</span> }<a name="line.1519"></a>
+<span class="sourceLineNo">1520</span><a name="line.1520"></a>
+<span class="sourceLineNo">1521</span> @Override<a name="line.1521"></a>
+<span class="sourceLineNo">1522</span> public CachedBlock next() {<a name="line.1522"></a>
+<span class="sourceLineNo">1523</span> final Map.Entry<BlockCacheKey, BucketEntry> e = i.next();<a name="line.1523"></a>
+<span class="sourceLineNo">1524</span> return new CachedBlock() {<a name="line.1524"></a>
+<span class="sourceLineNo">1525</span> @Override<a name="line.1525"></a>
+<span class="sourceLineNo">1526</span> public String toString() {<a name="line.1526"></a>
+<span class="sourceLineNo">1527</span> return BlockCacheUtil.toString(this, now);<a name="line.1527"></a>
+<span class="sourceLineNo">1528</span> }<a name="line.1528"></a>
+<span class="sourceLineNo">1529</span><a name="line.1529"></a>
+<span class="sourceLineNo">1530</span> @Override<a name="line.1530"></a>
+<span class="sourceLineNo">1531</span> public BlockPriority getBlockPriority() {<a name="line.1531"></a>
+<span class="sourceLineNo">1532</span> return e.getValue().getPriority();<a name="line.1532"></a>
+<span class="sourceLineNo">1533</span> }<a name="line.1533"></a>
+<span class="sourceLineNo">1534</span><a name="line.1534"></a>
+<span class="sourceLineNo">1535</span> @Override<a name="line.1535"></a>
+<span class="sourceLineNo">1536</span> public BlockType getBlockType() {<a name="line.1536"></a>
+<span class="sourceLineNo">1537</span> // Not held by BucketEntry. Could add it if wanted on BucketEntry creation.<a name="line.1537"></a>
+<span class="sourceLineNo">1538</span> return null;<a name="line.1538"></a>
+<span class="sourceLineNo">1539</span> }<a name="line.1539"></a>
+<span class="sourceLineNo">1540</span><a name="line.1540"></a>
+<span class="sourceLineNo">1541</span> @Override<a name="line.1541"></a>
+<span class="sourceLineNo">1542</span> public long getOffset() {<a name="line.1542"></a>
+<span class="sourceLineNo">1543</span> return e.getKey().getOffset();<a name="line.1543"></a>
+<span class="sourceLineNo">1544</span> }<a name="line.1544"></a>
+<span class="sourceLineNo">1545</span><a name="line.1545"></a>
+<span class="sourceLineNo">1546</span> @Override<a name="line.1546"></a>
+<span class="sourceLineNo">1547</span> public long getSize() {<a name="line.1547"></a>
+<span class="sourceLineNo">1548</span> return e.getValue().getLength();<a name="line.1548"></a>
+<span class="sourceLineNo">1549</span> }<a name="line.1549"></a>
+<span class="sourceLineNo">1550</span><a name="line.1550"></a>
+<span class="sourceLineNo">1551</span> @Override<a name="line.1551"></a>
+<span class="sourceLineNo">1552</span> public long getCachedTime() {<a name="line.1552"></a>
+<span class="sourceLineNo">1553</span> return e.getValue().getCachedTime();<a name="line.1553"></a>
+<span class="sourceLineNo">1554</span> }<a name="line.1554"></a>
+<span class="sourceLineNo">1555</span><a name="line.1555"></a>
+<span class="sourceLineNo">1556</span> @Override<a name="line.1556"></a>
+<span class="sourceLineNo">1557</span> public String getFilename() {<a name="line.1557"></a>
+<span class="sourceLineNo">1558</span> return e.getKey().getHfileName();<a name="line.1558"></a>
+<span class="sourceLineNo">1559</span> }<a name="line.1559"></a>
+<span class="sourceLineNo">1560</span><a name="line.1560"></a>
+<span class="sourceLineNo">1561</span> @Override<a name="line.1561"></a>
+<span class="sourceLineNo">1562</span> public int compareTo(CachedBlock other) {<a name="line.1562"></a>
+<span class="sourceLineNo">1563</span> int diff = this.getFilename().compareTo(other.getFilename());<a name="line.1563"></a>
+<span class="sourceLineNo">1564</span> if (diff != 0) return diff;<a name="line.1564"></a>
+<span class="sourceLineNo">1565</span><a name="line.1565"></a>
+<span class="sourceLineNo">1566</span> diff = Long.compare(this.getOffset(), other.getOffset());<a name="line.1566"></a>
+<span class="sourceLineNo">1567</span> if (diff != 0) return diff;<a name="line.1567"></a>
+<span class="sourceLineNo">1568</span> if (other.getCachedTime() < 0 || this.getCachedTime() < 0) {<a name="line.1568"></a>
+<span class="sourceLineNo">1569</span> throw new IllegalStateException("" + this.getCachedTime() + ", " +<a name="line.1569"></a>
+<span class="sourceLineNo">1570</span> other.getCachedTime());<a name="line.1570"></a>
+<span class="sourceLineNo">1571</span> }<a name="line.1571"></a>
+<span class="sourceLineNo">1572</span> return Long.compare(other.getCachedTime(), this.getCachedTime());<a name="line.1572"></a>
+<span class="sourceLineNo">1573</span> }<a name="line.1573"></a>
+<span class="sourceLineNo">1574</span><a name="line.1574"></a>
+<span class="sourceLineNo">1575</span> @Override<a name="line.1575"></a>
+<span class="sourceLineNo">1576</span> public int hashCode() {<a name="line.1576"></a>
+<span class="sourceLineNo">1577</span> return e.getKey().hashCode();<a name="line.1577"></a>
+<span class="sourceLineNo">1578</span> }<a name="line.1578"></a>
+<span class="sourceLineNo">1579</span><a name="line.1579"></a>
+<span class="sourceLineNo">1580</span> @Override<a name="line.1580"></a>
+<span class="sourceLineNo">1581</span> public boolean equals(Object obj) {<a name="line.1581"></a>
+<span class="sourceLineNo">1582</span> if (obj instanceof CachedBlock) {<a name="line.1582"></a>
+<span class="sourceLineNo">1583</span> CachedBlock cb = (CachedBlock)obj;<a name="line.1583"></a>
+<span class="sourceLineNo">1584</span> return compareTo(cb) == 0;<a name="line.1584"></a>
+<span class="sourceLineNo">1585</span> } else {<a name="line.1585"></a>
+<span class="sourceLineNo">1586</span> return false;<a name="line.1586"></a>
+<span class="sourceLineNo">1587</span> }<a name="line.1587"></a>
+<span class="sourceLineNo">1588</span> }<a name="line.1588"></a>
+<span class="sourceLineNo">1589</span> };<a name="line.1589"></a>
+<span class="sourceLineNo">1590</span> }<a name="line.1590"></a>
+<span class="sourceLineNo">1591</span><a name="line.1591"></a>
+<span class="sourceLineNo">1592</span> @Override<a name="line.1592"></a>
+<span class="sourceLineNo">1593</span> public void remove() {<a name="line.1593"></a>
+<span class="sourceLineNo">1594</span> throw new UnsupportedOperationException();<a name="line.1594"></a>
+<span class="sourceLineNo">1595</span> }<a name="line.1595"></a>
+<span class="sourceLineNo">1596</span> };<a name="line.1596"></a>
+<span class="sourceLineNo">1597</span> }<a name="line.1597"></a>
+<span class="sourceLineNo">1598</span><a name="line.1598"></a>
+<span class="sourceLineNo">1599</span> @Override<a name="line.1599"></a>
+<span class="sourceLineNo">1600</span> public BlockCache[] getBlockCaches() {<a name="line.1600"></a>
+<span class="sourceLineNo">1601</span> return null;<a name="line.1601"></a>
+<span class="sourceLineNo">1602</span> }<a name="line.1602"></a>
+<span class="sourceLineNo">1603</span><a name="line.1603"></a>
+<span class="sourceLineNo">1604</span> public int getRpcRefCount(BlockCacheKey cacheKey) {<a name="line.1604"></a>
+<span class="sourceLineNo">1605</span> BucketEntry bucketEntry = backingMap.get(cacheKey);<a name="line.1605"></a>
+<span class="sourceLineNo">1606</span> if (bucketEntry != null) {<a name="line.1606"></a>
+<span class="sourceLineNo">1607</span> return bucketEntry.refCnt() - (bucketEntry.markedAsEvicted.get() ? 0 : 1);<a name="line.1607"></a>
+<span class="sourceLineNo">1608</span> }<a name="line.1608"></a>
+<span class="sourceLineNo">1609</span> return 0;<a name="line.1609"></a>
<span class="sourceLineNo">1610</span> }<a name="line.1610"></a>
<span class="sourceLineNo">1611</span><a name="line.1611"></a>
-<span class="sourceLineNo">1612</span> float getMinFactor() {<a name="line.1612"></a>
-<span class="sourceLineNo">1613</span> return minFactor;<a name="line.1613"></a>
+<span class="sourceLineNo">1612</span> float getAcceptableFactor() {<a name="line.1612"></a>
+<span class="sourceLineNo">1613</span> return acceptableFactor;<a name="line.1613"></a>
<span class="sourceLineNo">1614</span> }<a name="line.1614"></a>
<span class="sourceLineNo">1615</span><a name="line.1615"></a>
-<span class="sourceLineNo">1616</span> float getExtraFreeFactor() {<a name="line.1616"></a>
-<span class="sourceLineNo">1617</span> return extraFreeFactor;<a name="line.1617"></a>
+<span class="sourceLineNo">1616</span> float getMinFactor() {<a name="line.1616"></a>
+<span class="sourceLineNo">1617</span> return minFactor;<a name="line.1617"></a>
<span class="sourceLineNo">1618</span> }<a name="line.1618"></a>
<span class="sourceLineNo">1619</span><a name="line.1619"></a>
-<span class="sourceLineNo">1620</span> float getSingleFactor() {<a name="line.1620"></a>
-<span class="sourceLineNo">1621</span> return singleFactor;<a name="line.1621"></a>
+<span class="sourceLineNo">1620</span> float getExtraFreeFactor() {<a name="line.1620"></a>
+<span class="sourceLineNo">1621</span> return extraFreeFactor;<a name="line.1621"></a>
<span class="sourceLineNo">1622</span> }<a name="line.1622"></a>
<span class="sourceLineNo">1623</span><a name="line.1623"></a>
-<span class="sourceLineNo">1624</span> float getMultiFactor() {<a name="line.1624"></a>
-<span class="sourceLineNo">1625</span> return multiFactor;<a name="line.1625"></a>
+<span class="sourceLineNo">1624</span> float getSingleFactor() {<a name="line.1624"></a>
+<span class="sourceLineNo">1625</span> return singleFactor;<a name="line.1625"></a>
<span class="sourceLineNo">1626</span> }<a name="line.1626"></a>
<span class="sourceLineNo">1627</span><a name="line.1627"></a>
-<span class="sourceLineNo">1628</span> float getMemoryFactor() {<a name="line.1628"></a>
-<span class="sourceLineNo">1629</span> return memoryFactor;<a name="line.1629"></a>
+<span class="sourceLineNo">1628</span> float getMultiFactor() {<a name="line.1628"></a>
+<span class="sourceLineNo">1629</span> return multiFactor;<a name="line.1629"></a>
<span class="sourceLineNo">1630</span> }<a name="line.1630"></a>
<span class="sourceLineNo">1631</span><a name="line.1631"></a>
-<span class="sourceLineNo">1632</span> /**<a name="line.1632"></a>
-<span class="sourceLineNo">1633</span> * Wrapped the delegate ConcurrentMap with maintaining its block's reference count.<a name="line.1633"></a>
-<span class="sourceLineNo">1634</span> */<a name="line.1634"></a>
-<span class="sourceLineNo">1635</span> static class RAMCache {<a name="line.1635"></a>
-<span class="sourceLineNo">1636</span> /**<a name="line.1636"></a>
-<span class="sourceLineNo">1637</span> * Defined the map as {@link ConcurrentHashMap} explicitly here, because in<a name="line.1637"></a>
-<span class="sourceLineNo">1638</span> * {@link RAMCache#get(BlockCacheKey)} and<a name="line.1638"></a>
-<span class="sourceLineNo">1639</span> * {@link RAMCache#putIfAbsent(BlockCacheKey, BucketCache.RAMQueueEntry)} , we need to<a name="line.1639"></a>
-<span class="sourceLineNo">1640</span> * guarantee the atomicity of map#computeIfPresent(key, func) and map#putIfAbsent(key, func).<a name="line.1640"></a>
-<span class="sourceLineNo">1641</span> * Besides, the func method can execute exactly once only when the key is present(or absent)<a name="line.1641"></a>
-<span class="sourceLineNo">1642</span> * and under the lock context. Otherwise, the reference count of block will be messed up.<a name="line.1642"></a>
-<span class="sourceLineNo">1643</span> * Notice that the {@link java.util.concurrent.ConcurrentSkipListMap} can not guarantee that.<a name="line.1643"></a>
-<span class="sourceLineNo">1644</span> */<a name="line.1644"></a>
-<span class="sourceLineNo">1645</span> final ConcurrentHashMap<BlockCacheKey, RAMQueueEntry> delegate = new ConcurrentHashMap<>();<a name="line.1645"></a>
-<span class="sourceLineNo">1646</span><a name="line.1646"></a>
-<span class="sourceLineNo">1647</span> public boolean containsKey(BlockCacheKey key) {<a name="line.1647"></a>
-<span class="sourceLineNo">1648</span> return delegate.containsKey(key);<a name="line.1648"></a>
-<span class="sourceLineNo">1649</span> }<a name="line.1649"></a>
+<span class="sourceLineNo">1632</span> float getMemoryFactor() {<a name="line.1632"></a>
+<span class="sourceLineNo">1633</span> return memoryFactor;<a name="line.1633"></a>
+<span class="sourceLineNo">1634</span> }<a name="line.1634"></a>
+<span class="sourceLineNo">1635</span><a name="line.1635"></a>
+<span class="sourceLineNo">1636</span> /**<a name="line.1636"></a>
+<span class="sourceLineNo">1637</span> * Wrapped the delegate ConcurrentMap with maintaining its block's reference count.<a name="line.1637"></a>
+<span class="sourceLineNo">1638</span> */<a name="line.1638"></a>
+<span class="sourceLineNo">1639</span> static class RAMCache {<a name="line.1639"></a>
+<span class="sourceLineNo">1640</span> /**<a name="line.1640"></a>
+<span class="sourceLineNo">1641</span> * Defined the map as {@link ConcurrentHashMap} explicitly here, because in<a name="line.1641"></a>
+<span class="sourceLineNo">1642</span> * {@link RAMCache#get(BlockCacheKey)} and<a name="line.1642"></a>
+<span class="sourceLineNo">1643</span> * {@link RAMCache#putIfAbsent(BlockCacheKey, BucketCache.RAMQueueEntry)} , we need to<a name="line.1643"></a>
+<span class="sourceLineNo">1644</span> * guarantee the atomicity of map#computeIfPresent(key, func) and map#putIfAbsent(key, func).<a name="line.1644"></a>
+<span class="sourceLineNo">1645</span> * Besides, the func method can execute exactly once only when the key is present(or absent)<a name="line.1645"></a>
+<span class="sourceLineNo">1646</span> * and under the lock context. Otherwise, the reference count of block will be messed up.<a name="line.1646"></a>
+<span class="sourceLineNo">1647</span> * Notice that the {@link java.util.concurrent.ConcurrentSkipListMap} can not guarantee that.<a name="line.1647"></a>
+<span class="sourceLineNo">1648</span> */<a name="line.1648"></a>
+<span class="sourceLineNo">1649</span> final ConcurrentHashMap<BlockCacheKey, RAMQueueEntry> delegate = new ConcurrentHashMap<>();<a name="line.1649"></a>
<span class="sourceLineNo">1650</span><a name="line.1650"></a>
-<span class="sourceLineNo">1651</span> public RAMQueueEntry get(BlockCacheKey key) {<a name="line.1651"></a>
-<span class="sourceLineNo">1652</span> return delegate.computeIfPresent(key, (k, re) -> {<a name="line.1652"></a>
-<span class="sourceLineNo">1653</span> // It'll be referenced by RPC, so retain atomically here. if the get and retain is not<a name="line.1653"></a>
-<span class="sourceLineNo">1654</span> // atomic, another thread may remove and release the block, when retaining in this thread we<a name="line.1654"></a>
-<span class="sourceLineNo">1655</span> // may retain a block with refCnt=0 which is disallowed. (see HBASE-22422)<a name="line.1655"></a>
-<span class="sourceLineNo">1656</span> re.getData().retain();<a name="line.1656"></a>
-<span class="sourceLineNo">1657</span> return re;<a name="line.1657"></a>
-<span class="sourceLineNo">1658</span> });<a name="line.1658"></a>
-<span class="sourceLineNo">1659</span> }<a name="line.1659"></a>
-<span class="sourceLineNo">1660</span><a name="line.1660"></a>
-<span class="sourceLineNo">1661</span> /**<a name="line.1661"></a>
-<span class="sourceLineNo">1662</span> * Return the previous associated value, or null if absent. It has the same meaning as<a name="line.1662"></a>
-<span class="sourceLineNo">1663</span> * {@link ConcurrentMap#putIfAbsent(Object, Object)}<a name="line.1663"></a>
-<span class="sourceLineNo">1664</span> */<a name="line.1664"></a>
-<span class="sourceLineNo">1665</span> public RAMQueueEntry putIfAbsent(BlockCacheKey key, RAMQueueEntry entry) {<a name="line.1665"></a>
-<span class="sourceLineNo">1666</span> AtomicBoolean absent = new AtomicBoolean(false);<a name="line.1666"></a>
-<span class="sourceLineNo">1667</span> RAMQueueEntry re = delegate.computeIfAbsent(key, k -> {<a name="line.1667"></a>
-<span class="sourceLineNo">1668</span> // The RAMCache reference to this entry, so reference count should be increment.<a name="line.1668"></a>
-<span class="sourceLineNo">1669</span> entry.getData().retain();<a name="line.1669"></a>
-<span class="sourceLineNo">1670</span> absent.set(true);<a name="line.1670"></a>
-<span class="sourceLineNo">1671</span> return entry;<a name="line.1671"></a>
-<span class="sourceLineNo">1672</span> });<a name="line.1672"></a>
-<span class="sourceLineNo">1673</span> return absent.get() ? null : re;<a name="line.1673"></a>
-<span class="sourceLineNo">1674</span> }<a name="line.1674"></a>
-<span class="sourceLineNo">1675</span><a name="line.1675"></a>
-<span class="sourceLineNo">1676</span> public boolean remove(BlockCacheKey key) {<a name="line.1676"></a>
-<span class="sourceLineNo">1677</span> return remove(key, re->{});<a name="line.1677"></a>
+<span class="sourceLineNo">1651</span> public boolean containsKey(BlockCacheKey key) {<a name="line.1651"></a>
+<span class="sourceLineNo">1652</span> return delegate.containsKey(key);<a name="line.1652"></a>
+<span class="sourceLineNo">1653</span> }<a name="line.1653"></a>
+<span class="sourceLineNo">1654</span><a name="line.1654"></a>
+<span class="sourceLineNo">1655</span> public RAMQueueEntry get(BlockCacheKey key) {<a name="line.1655"></a>
+<span class="sourceLineNo">1656</span> return delegate.computeIfPresent(key, (k, re) -> {<a name="line.1656"></a>
+<span class="sourceLineNo">1657</span> // It'll be referenced by RPC, so retain atomically here. if the get and retain is not<a name="line.1657"></a>
+<span class="sourceLineNo">1658</span> // atomic, another thread may remove and release the block, when retaining in this thread we<a name="line.1658"></a>
+<span class="sourceLineNo">1659</span> // may retain a block with refCnt=0 which is disallowed. (see HBASE-22422)<a name="line.1659"></a>
+<span class="sourceLineNo">1660</span> re.getData().retain();<a name="line.1660"></a>
+<span class="sourceLineNo">1661</span> return re;<a name="line.1661"></a>
+<span class="sourceLineNo">1662</span> });<a name="line.1662"></a>
+<span class="sourceLineNo">1663</span> }<a name="line.1663"></a>
+<span class="sourceLineNo">1664</span><a name="line.1664"></a>
+<span class="sourceLineNo">1665</span> /**<a name="line.1665"></a>
+<span class="sourceLineNo">1666</span> * Return the previous associated value, or null if absent. It has the same meaning as<a name="line.1666"></a>
+<span class="sourceLineNo">1667</span> * {@link ConcurrentMap#putIfAbsent(Object, Object)}<a name="line.1667"></a>
+<span class="sourceLineNo">1668</span> */<a name="line.1668"></a>
+<span class="sourceLineNo">1669</span> public RAMQueueEntry putIfAbsent(BlockCacheKey key, RAMQueueEntry entry) {<a name="line.1669"></a>
+<span class="sourceLineNo">1670</span> AtomicBoolean absent = new AtomicBoolean(false);<a name="line.1670"></a>
+<span class="sourceLineNo">1671</span> RAMQueueEntry re = delegate.computeIfAbsent(key, k -> {<a name="line.1671"></a>
+<span class="sourceLineNo">1672</span> // The RAMCache reference to this entry, so reference count should be increment.<a name="line.1672"></a>
+<span class="sourceLineNo">1673</span> entry.getData().retain();<a name="line.1673"></a>
+<span class="sourceLineNo">1674</span> absent.set(true);<a name="line.1674"></a>
+<span class="sourceLineNo">1675</span> return entry;<a name="line.1675"></a>
+<span class="sourceLineNo">1676</span> });<a name="line.1676"></a>
+<span class="sourceLineNo">1677</span> return absent.get() ? null : re;<a name="line.1677"></a>
<span class="sourceLineNo">1678</span> }<a name="line.1678"></a>
<span class="sourceLineNo">1679</span><a name="line.1679"></a>
-<span class="sourceLineNo">1680</span> /**<a name="line.1680"></a>
-<span class="sourceLineNo">1681</span> * Defined an {@link Consumer} here, because once the removed entry release its reference count,<a name="line.1681"></a>
-<span class="sourceLineNo">1682</span> * then it's ByteBuffers may be recycled and accessing it outside this method will be thrown an<a name="line.1682"></a>
-<span class="sourceLineNo">1683</span> * exception. the consumer will access entry to remove before release its reference count.<a name="line.1683"></a>
-<span class="sourceLineNo">1684</span> * Notice, don't change its reference count in the {@link Consumer}<a name="line.1684"></a>
-<span class="sourceLineNo">1685</span> */<a name="line.1685"></a>
-<span class="sourceLineNo">1686</span> public boolean remove(BlockCacheKey key, Consumer<RAMQueueEntry> action) {<a name="line.1686"></a>
-<span class="sourceLineNo">1687</span> RAMQueueEntry previous = delegate.remove(key);<a name="line.1687"></a>
-<span class="sourceLineNo">1688</span> action.accept(previous);<a name="line.1688"></a>
-<span class="sourceLineNo">1689</span> if (previous != null) {<a name="line.1689"></a>
-<span class="sourceLineNo">1690</span> previous.getData().release();<a name="line.1690"></a>
-<span class="sourceLineNo">1691</span> }<a name="line.1691"></a>
-<span class="sourceLineNo">1692</span> return previous != null;<a name="line.1692"></a>
-<span class="sourceLineNo">1693</span> }<a name="line.1693"></a>
-<span class="sourceLineNo">1694</span><a name="line.1694"></a>
-<span class="sourceLineNo">1695</span> public boolean isEmpty() {<a name="line.1695"></a>
-<span class="sourceLineNo">1696</span> return delegate.isEmpty();<a name="line.1696"></a>
+<span class="sourceLineNo">1680</span> public boolean remove(BlockCacheKey key) {<a name="line.1680"></a>
+<span class="sourceLineNo">1681</span> return remove(key, re->{});<a name="line.1681"></a>
+<span class="sourceLineNo">1682</span> }<a name="line.1682"></a>
+<span class="sourceLineNo">1683</span><a name="line.1683"></a>
+<span class="sourceLineNo">1684</span> /**<a name="line.1684"></a>
+<span class="sourceLineNo">1685</span> * Defined an {@link Consumer} here, because once the removed entry release its reference count,<a name="line.1685"></a>
+<span class="sourceLineNo">1686</span> * then it's ByteBuffers may be recycled and accessing it outside this method will be thrown an<a name="line.1686"></a>
+<span class="sourceLineNo">1687</span> * exception. the consumer will access entry to remove before release its reference count.<a name="line.1687"></a>
+<span class="sourceLineNo">1688</span> * Notice, don't change its reference count in the {@link Consumer}<a name="line.1688"></a>
+<span class="sourceLineNo">1689</span> */<a name="line.1689"></a>
+<span class="sourceLineNo">1690</span> public boolean remove(BlockCacheKey key, Consumer<RAMQueueEntry> action) {<a name="line.1690"></a>
+<span class="sourceLineNo">1691</span> RAMQueueEntry previous = delegate.remove(key);<a name="line.1691"></a>
+<span class="sourceLineNo">1692</span> action.accept(previous);<a name="line.1692"></a>
+<span class="sourceLineNo">1693</span> if (previous != null) {<a name="line.1693"></a>
+<span class="sourceLineNo">1694</span> previous.getData().release();<a name="line.1694"></a>
+<span class="sourceLineNo">1695</span> }<a name="line.1695"></a>
+<span class="sourceLineNo">1696</span> return previous != null;<a name="line.1696"></a>
<span class="sourceLineNo">1697</span> }<a name="line.1697"></a>
<span class="sourceLineNo">1698</span><a name="line.1698"></a>
-<span class="sourceLineNo">1699</span> public void clear() {<a name="line.1699"></a>
-<span class="sourceLineNo">1700</span> Iterator<Map.Entry<BlockCacheKey, RAMQueueEntry>> it = delegate.entrySet().iterator();<a name="line.1700"></a>
-<span class="sourceLineNo">1701</span> while (it.hasNext()) {<a name="line.1701"></a>
-<span class="sourceLineNo">1702</span> RAMQueueEntry re = it.next().getValue();<a name="line.1702"></a>
-<span class="sourceLineNo">1703</span> it.remove();<a name="line.1703"></a>
-<span class="sourceLineNo">1704</span> re.getData().release();<a name="line.1704"></a>
-<span class="sourceLineNo">1705</span> }<a name="line.1705"></a>
-<span class="sourceLineNo">1706</span> }<a name="line.1706"></a>
-<span class="sourceLineNo">1707</span> }<a name="line.1707"></a>
-<span class="sourceLineNo">1708</span>}<a name="line.1708"></a>
+<span class="sourceLineNo">1699</span> public boolean isEmpty() {<a name="line.1699"></a>
+<span class="sourceLineNo">1700</span> return delegate.isEmpty();<a name="line.1700"></a>
+<span class="sourceLineNo">1701</span> }<a name="line.1701"></a>
+<span class="sourceLineNo">1702</span><a name="line.1702"></a>
+<span class="sourceLineNo">1703</span> public void clear() {<a name="line.1703"></a>
+<span class="sourceLineNo">1704</span> Iterator<Map.Entry<BlockCacheKey, RAMQueueEntry>> it = delegate.entrySet().iterator();<a name="line.1704"></a>
+<span class="sourceLineNo">1705</span> while (it.hasNext()) {<a name="line.1705"></a>
+<span class="sourceLineNo">1706</span> RAMQueueEntry re = it.next().getValue();<a name="line.1706"></a>
+<span class="sourceLineNo">1707</span> it.remove();<a name="line.1707"></a>
+<span class="sourceLineNo">1708</span> re.getData().release();<a name="line.1708"></a>
+<span class="sourceLineNo">1709</span> }<a name="line.1709"></a>
+<span class="sourceLineNo">1710</span> }<a name="line.1710"></a>
+<span class="sourceLineNo">1711</span> }<a name="line.1711"></a>
+<span class="sourceLineNo">1712</span>}<a name="line.1712"></a>
diff --git a/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html b/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html
index 6e1ac11..5d27f48 100644
--- a/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html
+++ b/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.RAMQueueEntry.html
@@ -494,1226 +494,1230 @@
<span class="sourceLineNo">486</span> } else {<a name="line.486"></a>
<span class="sourceLineNo">487</span> this.blockNumber.increment();<a name="line.487"></a>
<span class="sourceLineNo">488</span> this.heapSize.add(cachedItem.heapSize());<a name="line.488"></a>
-<span class="sourceLineNo">489</span> blocksByHFile.add(cacheKey);<a name="line.489"></a>
-<span class="sourceLineNo">490</span> }<a name="line.490"></a>
-<span class="sourceLineNo">491</span> }<a name="line.491"></a>
-<span class="sourceLineNo">492</span><a name="line.492"></a>
-<span class="sourceLineNo">493</span> /**<a name="line.493"></a>
-<span class="sourceLineNo">494</span> * Get the buffer of the block with the specified key.<a name="line.494"></a>
-<span class="sourceLineNo">495</span> * @param key block's cache key<a name="line.495"></a>
-<span class="sourceLineNo">496</span> * @param caching true if the caller caches blocks on cache misses<a name="line.496"></a>
-<span class="sourceLineNo">497</span> * @param repeat Whether this is a repeat lookup for the same block<a name="line.497"></a>
-<span class="sourceLineNo">498</span> * @param updateCacheMetrics Whether we should update cache metrics or not<a name="line.498"></a>
-<span class="sourceLineNo">499</span> * @return buffer of specified cache key, or null if not in cache<a name="line.499"></a>
-<span class="sourceLineNo">500</span> */<a name="line.500"></a>
-<span class="sourceLineNo">501</span> @Override<a name="line.501"></a>
-<span class="sourceLineNo">502</span> public Cacheable getBlock(BlockCacheKey key, boolean caching, boolean repeat,<a name="line.502"></a>
-<span class="sourceLineNo">503</span> boolean updateCacheMetrics) {<a name="line.503"></a>
-<span class="sourceLineNo">504</span> if (!cacheEnabled) {<a name="line.504"></a>
-<span class="sourceLineNo">505</span> return null;<a name="line.505"></a>
-<span class="sourceLineNo">506</span> }<a name="line.506"></a>
-<span class="sourceLineNo">507</span> RAMQueueEntry re = ramCache.get(key);<a name="line.507"></a>
-<span class="sourceLineNo">508</span> if (re != null) {<a name="line.508"></a>
-<span class="sourceLineNo">509</span> if (updateCacheMetrics) {<a name="line.509"></a>
-<span class="sourceLineNo">510</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.510"></a>
-<span class="sourceLineNo">511</span> }<a name="line.511"></a>
-<span class="sourceLineNo">512</span> re.access(accessCount.incrementAndGet());<a name="line.512"></a>
-<span class="sourceLineNo">513</span> return re.getData();<a name="line.513"></a>
-<span class="sourceLineNo">514</span> }<a name="line.514"></a>
-<span class="sourceLineNo">515</span> BucketEntry bucketEntry = backingMap.get(key);<a name="line.515"></a>
-<span class="sourceLineNo">516</span> if (bucketEntry != null) {<a name="line.516"></a>
-<span class="sourceLineNo">517</span> long start = System.nanoTime();<a name="line.517"></a>
-<span class="sourceLineNo">518</span> ReentrantReadWriteLock lock = offsetLock.getLock(bucketEntry.offset());<a name="line.518"></a>
-<span class="sourceLineNo">519</span> try {<a name="line.519"></a>
-<span class="sourceLineNo">520</span> lock.readLock().lock();<a name="line.520"></a>
-<span class="sourceLineNo">521</span> // We can not read here even if backingMap does contain the given key because its offset<a name="line.521"></a>
-<span class="sourceLineNo">522</span> // maybe changed. If we lock BlockCacheKey instead of offset, then we can only check<a name="line.522"></a>
-<span class="sourceLineNo">523</span> // existence here.<a name="line.523"></a>
-<span class="sourceLineNo">524</span> if (bucketEntry.equals(backingMap.get(key))) {<a name="line.524"></a>
-<span class="sourceLineNo">525</span> // Read the block from IOEngine based on the bucketEntry's offset and length, NOTICE: the<a name="line.525"></a>
-<span class="sourceLineNo">526</span> // block will use the refCnt of bucketEntry, which means if two HFileBlock mapping to<a name="line.526"></a>
-<span class="sourceLineNo">527</span> // the same BucketEntry, then all of the three will share the same refCnt.<a name="line.527"></a>
-<span class="sourceLineNo">528</span> Cacheable cachedBlock = ioEngine.read(bucketEntry);<a name="line.528"></a>
-<span class="sourceLineNo">529</span> if (ioEngine.usesSharedMemory()) {<a name="line.529"></a>
-<span class="sourceLineNo">530</span> // If IOEngine use shared memory, cachedBlock and BucketEntry will share the<a name="line.530"></a>
-<span class="sourceLineNo">531</span> // same RefCnt, do retain here, in order to count the number of RPC references<a name="line.531"></a>
-<span class="sourceLineNo">532</span> cachedBlock.retain();<a name="line.532"></a>
-<span class="sourceLineNo">533</span> }<a name="line.533"></a>
-<span class="sourceLineNo">534</span> // Update the cache statistics.<a name="line.534"></a>
-<span class="sourceLineNo">535</span> if (updateCacheMetrics) {<a name="line.535"></a>
-<span class="sourceLineNo">536</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.536"></a>
-<span class="sourceLineNo">537</span> cacheStats.ioHit(System.nanoTime() - start);<a name="line.537"></a>
-<span class="sourceLineNo">538</span> }<a name="line.538"></a>
-<span class="sourceLineNo">539</span> bucketEntry.access(accessCount.incrementAndGet());<a name="line.539"></a>
-<span class="sourceLineNo">540</span> if (this.ioErrorStartTime > 0) {<a name="line.540"></a>
-<span class="sourceLineNo">541</span> ioErrorStartTime = -1;<a name="line.541"></a>
-<span class="sourceLineNo">542</span> }<a name="line.542"></a>
-<span class="sourceLineNo">543</span> return cachedBlock;<a name="line.543"></a>
-<span class="sourceLineNo">544</span> }<a name="line.544"></a>
-<span class="sourceLineNo">545</span> } catch (IOException ioex) {<a name="line.545"></a>
-<span class="sourceLineNo">546</span> LOG.error("Failed reading block " + key + " from bucket cache", ioex);<a name="line.546"></a>
-<span class="sourceLineNo">547</span> checkIOErrorIsTolerated();<a name="line.547"></a>
-<span class="sourceLineNo">548</span> } finally {<a name="line.548"></a>
-<span class="sourceLineNo">549</span> lock.readLock().unlock();<a name="line.549"></a>
-<span class="sourceLineNo">550</span> }<a name="line.550"></a>
-<span class="sourceLineNo">551</span> }<a name="line.551"></a>
-<span class="sourceLineNo">552</span> if (!repeat && updateCacheMetrics) {<a name="line.552"></a>
-<span class="sourceLineNo">553</span> cacheStats.miss(caching, key.isPrimary(), key.getBlockType());<a name="line.553"></a>
-<span class="sourceLineNo">554</span> }<a name="line.554"></a>
-<span class="sourceLineNo">555</span> return null;<a name="line.555"></a>
-<span class="sourceLineNo">556</span> }<a name="line.556"></a>
-<span class="sourceLineNo">557</span><a name="line.557"></a>
-<span class="sourceLineNo">558</span> /**<a name="line.558"></a>
-<span class="sourceLineNo">559</span> * This method is invoked after the bucketEntry is removed from {@link BucketCache#backingMap}<a name="line.559"></a>
-<span class="sourceLineNo">560</span> */<a name="line.560"></a>
-<span class="sourceLineNo">561</span> void blockEvicted(BlockCacheKey cacheKey, BucketEntry bucketEntry, boolean decrementBlockNumber) {<a name="line.561"></a>
-<span class="sourceLineNo">562</span> bucketEntry.markAsEvicted();<a name="line.562"></a>
-<span class="sourceLineNo">563</span> blocksByHFile.remove(cacheKey);<a name="line.563"></a>
-<span class="sourceLineNo">564</span> if (decrementBlockNumber) {<a name="line.564"></a>
-<span class="sourceLineNo">565</span> this.blockNumber.decrement();<a name="line.565"></a>
-<span class="sourceLineNo">566</span> }<a name="line.566"></a>
-<span class="sourceLineNo">567</span> cacheStats.evicted(bucketEntry.getCachedTime(), cacheKey.isPrimary());<a name="line.567"></a>
-<span class="sourceLineNo">568</span> }<a name="line.568"></a>
-<span class="sourceLineNo">569</span><a name="line.569"></a>
-<span class="sourceLineNo">570</span> /**<a name="line.570"></a>
-<span class="sourceLineNo">571</span> * Free the {{@link BucketEntry} actually,which could only be invoked when the<a name="line.571"></a>
-<span class="sourceLineNo">572</span> * {@link BucketEntry#refCnt} becoming 0.<a name="line.572"></a>
-<span class="sourceLineNo">573</span> */<a name="line.573"></a>
-<span class="sourceLineNo">574</span> void freeBucketEntry(BucketEntry bucketEntry) {<a name="line.574"></a>
-<span class="sourceLineNo">575</span> bucketAllocator.freeBlock(bucketEntry.offset());<a name="line.575"></a>
-<span class="sourceLineNo">576</span> realCacheSize.add(-1 * bucketEntry.getLength());<a name="line.576"></a>
-<span class="sourceLineNo">577</span> }<a name="line.577"></a>
-<span class="sourceLineNo">578</span><a name="line.578"></a>
-<span class="sourceLineNo">579</span> /**<a name="line.579"></a>
-<span class="sourceLineNo">580</span> * Try to evict the block from {@link BlockCache} by force. We'll call this in few cases:<br><a name="line.580"></a>
-<span class="sourceLineNo">581</span> * 1. Close an HFile, and clear all cached blocks. <br><a name="line.581"></a>
-<span class="sourceLineNo">582</span> * 2. Call {@link Admin#clearBlockCache(TableName)} to clear all blocks for a given table.<br><a name="line.582"></a>
-<span class="sourceLineNo">583</span> * <p><a name="line.583"></a>
-<span class="sourceLineNo">584</span> * Firstly, we'll try to remove the block from RAMCache,and then try to evict from backingMap.<a name="line.584"></a>
-<span class="sourceLineNo">585</span> * Here we evict the block from backingMap immediately, but only free the reference from bucket<a name="line.585"></a>
-<span class="sourceLineNo">586</span> * cache by calling {@link BucketEntry#markedAsEvicted}. If there're still some RPC referring this<a name="line.586"></a>
-<span class="sourceLineNo">587</span> * block, block can only be de-allocated when all of them release the block.<a name="line.587"></a>
-<span class="sourceLineNo">588</span> * <p><a name="line.588"></a>
-<span class="sourceLineNo">589</span> * NOTICE: we need to grab the write offset lock firstly before releasing the reference from<a name="line.589"></a>
-<span class="sourceLineNo">590</span> * bucket cache. if we don't, we may read an {@link BucketEntry} with refCnt = 0 when<a name="line.590"></a>
-<span class="sourceLineNo">591</span> * {@link BucketCache#getBlock(BlockCacheKey, boolean, boolean, boolean)}, it's a memory leak.<a name="line.591"></a>
-<span class="sourceLineNo">592</span> * @param cacheKey Block to evict<a name="line.592"></a>
-<span class="sourceLineNo">593</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.593"></a>
-<span class="sourceLineNo">594</span> */<a name="line.594"></a>
-<span class="sourceLineNo">595</span> @Override<a name="line.595"></a>
-<span class="sourceLineNo">596</span> public boolean evictBlock(BlockCacheKey cacheKey) {<a name="line.596"></a>
-<span class="sourceLineNo">597</span> return doEvictBlock(cacheKey, null);<a name="line.597"></a>
-<span class="sourceLineNo">598</span> }<a name="line.598"></a>
-<span class="sourceLineNo">599</span><a name="line.599"></a>
-<span class="sourceLineNo">600</span> /**<a name="line.600"></a>
-<span class="sourceLineNo">601</span> * Evict the {@link BlockCacheKey} and {@link BucketEntry} from {@link BucketCache#backingMap} and<a name="line.601"></a>
-<span class="sourceLineNo">602</span> * {@link BucketCache#ramCache}. <br/><a name="line.602"></a>
-<span class="sourceLineNo">603</span> * NOTE:When Evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.603"></a>
-<span class="sourceLineNo">604</span> * {@link BucketEntry} could be removed.<a name="line.604"></a>
-<span class="sourceLineNo">605</span> * @param cacheKey {@link BlockCacheKey} to evict.<a name="line.605"></a>
-<span class="sourceLineNo">606</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.606"></a>
-<span class="sourceLineNo">607</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.607"></a>
-<span class="sourceLineNo">608</span> */<a name="line.608"></a>
-<span class="sourceLineNo">609</span> private boolean doEvictBlock(BlockCacheKey cacheKey, BucketEntry bucketEntry) {<a name="line.609"></a>
-<span class="sourceLineNo">610</span> if (!cacheEnabled) {<a name="line.610"></a>
-<span class="sourceLineNo">611</span> return false;<a name="line.611"></a>
-<span class="sourceLineNo">612</span> }<a name="line.612"></a>
-<span class="sourceLineNo">613</span> boolean existedInRamCache = removeFromRamCache(cacheKey);<a name="line.613"></a>
-<span class="sourceLineNo">614</span> if (bucketEntry == null) {<a name="line.614"></a>
-<span class="sourceLineNo">615</span> bucketEntry = backingMap.get(cacheKey);<a name="line.615"></a>
-<span class="sourceLineNo">616</span> }<a name="line.616"></a>
-<span class="sourceLineNo">617</span> final BucketEntry bucketEntryToUse = bucketEntry;<a name="line.617"></a>
-<span class="sourceLineNo">618</span><a name="line.618"></a>
-<span class="sourceLineNo">619</span> if (bucketEntryToUse == null) {<a name="line.619"></a>
-<span class="sourceLineNo">620</span> if (existedInRamCache) {<a name="line.620"></a>
-<span class="sourceLineNo">621</span> cacheStats.evicted(0, cacheKey.isPrimary());<a name="line.621"></a>
-<span class="sourceLineNo">622</span> }<a name="line.622"></a>
-<span class="sourceLineNo">623</span> return existedInRamCache;<a name="line.623"></a>
-<span class="sourceLineNo">624</span> } else {<a name="line.624"></a>
-<span class="sourceLineNo">625</span> return bucketEntryToUse.withWriteLock(offsetLock, () -> {<a name="line.625"></a>
-<span class="sourceLineNo">626</span> if (backingMap.remove(cacheKey, bucketEntryToUse)) {<a name="line.626"></a>
-<span class="sourceLineNo">627</span> blockEvicted(cacheKey, bucketEntryToUse, !existedInRamCache);<a name="line.627"></a>
-<span class="sourceLineNo">628</span> return true;<a name="line.628"></a>
-<span class="sourceLineNo">629</span> }<a name="line.629"></a>
-<span class="sourceLineNo">630</span> return false;<a name="line.630"></a>
-<span class="sourceLineNo">631</span> });<a name="line.631"></a>
-<span class="sourceLineNo">632</span> }<a name="line.632"></a>
-<span class="sourceLineNo">633</span> }<a name="line.633"></a>
-<span class="sourceLineNo">634</span><a name="line.634"></a>
-<span class="sourceLineNo">635</span> /**<a name="line.635"></a>
-<span class="sourceLineNo">636</span> * <pre><a name="line.636"></a>
-<span class="sourceLineNo">637</span> * Create the {@link Recycler} for {@link BucketEntry#refCnt},which would be used as<a name="line.637"></a>
-<span class="sourceLineNo">638</span> * {@link RefCnt#recycler} of {@link HFileBlock#buf} returned from {@link BucketCache#getBlock}.<a name="line.638"></a>
-<span class="sourceLineNo">639</span> * NOTE: for {@link BucketCache#getBlock},the {@link RefCnt#recycler} of {@link HFileBlock#buf}<a name="line.639"></a>
-<span class="sourceLineNo">640</span> * from {@link BucketCache#backingMap} and {@link BucketCache#ramCache} are different:<a name="line.640"></a>
-<span class="sourceLineNo">641</span> * 1.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#backingMap},<a name="line.641"></a>
-<span class="sourceLineNo">642</span> * it is the return value of current {@link BucketCache#createRecycler} method.<a name="line.642"></a>
-<span class="sourceLineNo">643</span> *<a name="line.643"></a>
-<span class="sourceLineNo">644</span> * 2.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#ramCache},<a name="line.644"></a>
-<span class="sourceLineNo">645</span> * it is {@link ByteBuffAllocator#putbackBuffer}.<a name="line.645"></a>
-<span class="sourceLineNo">646</span> * </pre><a name="line.646"></a>
-<span class="sourceLineNo">647</span> */<a name="line.647"></a>
-<span class="sourceLineNo">648</span> private Recycler createRecycler(final BucketEntry bucketEntry) {<a name="line.648"></a>
-<span class="sourceLineNo">649</span> return () -> {<a name="line.649"></a>
-<span class="sourceLineNo">650</span> freeBucketEntry(bucketEntry);<a name="line.650"></a>
-<span class="sourceLineNo">651</span> return;<a name="line.651"></a>
-<span class="sourceLineNo">652</span> };<a name="line.652"></a>
-<span class="sourceLineNo">653</span> }<a name="line.653"></a>
-<span class="sourceLineNo">654</span><a name="line.654"></a>
-<span class="sourceLineNo">655</span> /**<a name="line.655"></a>
-<span class="sourceLineNo">656</span> * NOTE: This method is only for test.<a name="line.656"></a>
-<span class="sourceLineNo">657</span> */<a name="line.657"></a>
-<span class="sourceLineNo">658</span> public boolean evictBlockIfNoRpcReferenced(BlockCacheKey blockCacheKey) {<a name="line.658"></a>
-<span class="sourceLineNo">659</span> BucketEntry bucketEntry = backingMap.get(blockCacheKey);<a name="line.659"></a>
-<span class="sourceLineNo">660</span> if (bucketEntry == null) {<a name="line.660"></a>
-<span class="sourceLineNo">661</span> return false;<a name="line.661"></a>
-<span class="sourceLineNo">662</span> }<a name="line.662"></a>
-<span class="sourceLineNo">663</span> return evictBucketEntryIfNoRpcReferenced(blockCacheKey, bucketEntry);<a name="line.663"></a>
-<span class="sourceLineNo">664</span> }<a name="line.664"></a>
-<span class="sourceLineNo">665</span><a name="line.665"></a>
-<span class="sourceLineNo">666</span> /**<a name="line.666"></a>
-<span class="sourceLineNo">667</span> * Evict {@link BlockCacheKey} and its corresponding {@link BucketEntry} only if<a name="line.667"></a>
-<span class="sourceLineNo">668</span> * {@link BucketEntry#isRpcRef} is false. <br/><a name="line.668"></a>
-<span class="sourceLineNo">669</span> * NOTE:When evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.669"></a>
-<span class="sourceLineNo">670</span> * {@link BucketEntry} could be removed.<a name="line.670"></a>
-<span class="sourceLineNo">671</span> * @param blockCacheKey {@link BlockCacheKey} to evict.<a name="line.671"></a>
-<span class="sourceLineNo">672</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.672"></a>
-<span class="sourceLineNo">673</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.673"></a>
-<span class="sourceLineNo">674</span> */<a name="line.674"></a>
-<span class="sourceLineNo">675</span> boolean evictBucketEntryIfNoRpcReferenced(BlockCacheKey blockCacheKey, BucketEntry bucketEntry) {<a name="line.675"></a>
-<span class="sourceLineNo">676</span> if (!bucketEntry.isRpcRef()) {<a name="line.676"></a>
-<span class="sourceLineNo">677</span> return doEvictBlock(blockCacheKey, bucketEntry);<a name="line.677"></a>
-<span class="sourceLineNo">678</span> }<a name="line.678"></a>
-<span class="sourceLineNo">679</span> return false;<a name="line.679"></a>
-<span class="sourceLineNo">680</span> }<a name="line.680"></a>
-<span class="sourceLineNo">681</span><a name="line.681"></a>
-<span class="sourceLineNo">682</span> protected boolean removeFromRamCache(BlockCacheKey cacheKey) {<a name="line.682"></a>
-<span class="sourceLineNo">683</span> return ramCache.remove(cacheKey, re -> {<a name="line.683"></a>
-<span class="sourceLineNo">684</span> if (re != null) {<a name="line.684"></a>
-<span class="sourceLineNo">685</span> this.blockNumber.decrement();<a name="line.685"></a>
-<span class="sourceLineNo">686</span> this.heapSize.add(-1 * re.getData().heapSize());<a name="line.686"></a>
-<span class="sourceLineNo">687</span> }<a name="line.687"></a>
-<span class="sourceLineNo">688</span> });<a name="line.688"></a>
-<span class="sourceLineNo">689</span> }<a name="line.689"></a>
-<span class="sourceLineNo">690</span><a name="line.690"></a>
-<span class="sourceLineNo">691</span> /*<a name="line.691"></a>
-<span class="sourceLineNo">692</span> * Statistics thread. Periodically output cache statistics to the log.<a name="line.692"></a>
-<span class="sourceLineNo">693</span> */<a name="line.693"></a>
-<span class="sourceLineNo">694</span> private static class StatisticsThread extends Thread {<a name="line.694"></a>
-<span class="sourceLineNo">695</span> private final BucketCache bucketCache;<a name="line.695"></a>
-<span class="sourceLineNo">696</span><a name="line.696"></a>
-<span class="sourceLineNo">697</span> public StatisticsThread(BucketCache bucketCache) {<a name="line.697"></a>
-<span class="sourceLineNo">698</span> super("BucketCacheStatsThread");<a name="line.698"></a>
-<span class="sourceLineNo">699</span> setDaemon(true);<a name="line.699"></a>
-<span class="sourceLineNo">700</span> this.bucketCache = bucketCache;<a name="line.700"></a>
-<span class="sourceLineNo">701</span> }<a name="line.701"></a>
-<span class="sourceLineNo">702</span><a name="line.702"></a>
-<span class="sourceLineNo">703</span> @Override<a name="line.703"></a>
-<span class="sourceLineNo">704</span> public void run() {<a name="line.704"></a>
-<span class="sourceLineNo">705</span> bucketCache.logStats();<a name="line.705"></a>
-<span class="sourceLineNo">706</span> }<a name="line.706"></a>
-<span class="sourceLineNo">707</span> }<a name="line.707"></a>
-<span class="sourceLineNo">708</span><a name="line.708"></a>
-<span class="sourceLineNo">709</span> public void logStats() {<a name="line.709"></a>
-<span class="sourceLineNo">710</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.710"></a>
-<span class="sourceLineNo">711</span> long usedSize = bucketAllocator.getUsedSize();<a name="line.711"></a>
-<span class="sourceLineNo">712</span> long freeSize = totalSize - usedSize;<a name="line.712"></a>
-<span class="sourceLineNo">713</span> long cacheSize = getRealCacheSize();<a name="line.713"></a>
-<span class="sourceLineNo">714</span> LOG.info("failedBlockAdditions=" + cacheStats.getFailedInserts() + ", " +<a name="line.714"></a>
-<span class="sourceLineNo">715</span> "totalSize=" + StringUtils.byteDesc(totalSize) + ", " +<a name="line.715"></a>
-<span class="sourceLineNo">716</span> "freeSize=" + StringUtils.byteDesc(freeSize) + ", " +<a name="line.716"></a>
-<span class="sourceLineNo">717</span> "usedSize=" + StringUtils.byteDesc(usedSize) +", " +<a name="line.717"></a>
-<span class="sourceLineNo">718</span> "cacheSize=" + StringUtils.byteDesc(cacheSize) +", " +<a name="line.718"></a>
-<span class="sourceLineNo">719</span> "accesses=" + cacheStats.getRequestCount() + ", " +<a name="line.719"></a>
-<span class="sourceLineNo">720</span> "hits=" + cacheStats.getHitCount() + ", " +<a name="line.720"></a>
-<span class="sourceLineNo">721</span> "IOhitsPerSecond=" + cacheStats.getIOHitsPerSecond() + ", " +<a name="line.721"></a>
-<span class="sourceLineNo">722</span> "IOTimePerHit=" + String.format("%.2f", cacheStats.getIOTimePerHit())+ ", " +<a name="line.722"></a>
-<span class="sourceLineNo">723</span> "hitRatio=" + (cacheStats.getHitCount() == 0 ? "0," :<a name="line.723"></a>
-<span class="sourceLineNo">724</span> (StringUtils.formatPercent(cacheStats.getHitRatio(), 2)+ ", ")) +<a name="line.724"></a>
-<span class="sourceLineNo">725</span> "cachingAccesses=" + cacheStats.getRequestCachingCount() + ", " +<a name="line.725"></a>
-<span class="sourceLineNo">726</span> "cachingHits=" + cacheStats.getHitCachingCount() + ", " +<a name="line.726"></a>
-<span class="sourceLineNo">727</span> "cachingHitsRatio=" +(cacheStats.getHitCachingCount() == 0 ? "0," :<a name="line.727"></a>
-<span class="sourceLineNo">728</span> (StringUtils.formatPercent(cacheStats.getHitCachingRatio(), 2)+ ", ")) +<a name="line.728"></a>
-<span class="sourceLineNo">729</span> "evictions=" + cacheStats.getEvictionCount() + ", " +<a name="line.729"></a>
-<span class="sourceLineNo">730</span> "evicted=" + cacheStats.getEvictedCount() + ", " +<a name="line.730"></a>
-<span class="sourceLineNo">731</span> "evictedPerRun=" + cacheStats.evictedPerEviction());<a name="line.731"></a>
-<span class="sourceLineNo">732</span> cacheStats.reset();<a name="line.732"></a>
-<span class="sourceLineNo">733</span> }<a name="line.733"></a>
-<span class="sourceLineNo">734</span><a name="line.734"></a>
-<span class="sourceLineNo">735</span> public long getRealCacheSize() {<a name="line.735"></a>
-<span class="sourceLineNo">736</span> return this.realCacheSize.sum();<a name="line.736"></a>
-<span class="sourceLineNo">737</span> }<a name="line.737"></a>
-<span class="sourceLineNo">738</span><a name="line.738"></a>
-<span class="sourceLineNo">739</span> public long acceptableSize() {<a name="line.739"></a>
-<span class="sourceLineNo">740</span> return (long) Math.floor(bucketAllocator.getTotalSize() * acceptableFactor);<a name="line.740"></a>
-<span class="sourceLineNo">741</span> }<a name="line.741"></a>
-<span class="sourceLineNo">742</span><a name="line.742"></a>
-<span class="sourceLineNo">743</span> long getPartitionSize(float partitionFactor) {<a name="line.743"></a>
-<span class="sourceLineNo">744</span> return (long) Math.floor(bucketAllocator.getTotalSize() * partitionFactor * minFactor);<a name="line.744"></a>
-<span class="sourceLineNo">745</span> }<a name="line.745"></a>
-<span class="sourceLineNo">746</span><a name="line.746"></a>
-<span class="sourceLineNo">747</span> /**<a name="line.747"></a>
-<span class="sourceLineNo">748</span> * Return the count of bucketSizeinfos still need free space<a name="line.748"></a>
-<span class="sourceLineNo">749</span> */<a name="line.749"></a>
-<span class="sourceLineNo">750</span> private int bucketSizesAboveThresholdCount(float minFactor) {<a name="line.750"></a>
-<span class="sourceLineNo">751</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.751"></a>
-<span class="sourceLineNo">752</span> int fullCount = 0;<a name="line.752"></a>
-<span class="sourceLineNo">753</span> for (int i = 0; i < stats.length; i++) {<a name="line.753"></a>
-<span class="sourceLineNo">754</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.754"></a>
-<span class="sourceLineNo">755</span> freeGoal = Math.max(freeGoal, 1);<a name="line.755"></a>
-<span class="sourceLineNo">756</span> if (stats[i].freeCount() < freeGoal) {<a name="line.756"></a>
-<span class="sourceLineNo">757</span> fullCount++;<a name="line.757"></a>
-<span class="sourceLineNo">758</span> }<a name="line.758"></a>
-<span class="sourceLineNo">759</span> }<a name="line.759"></a>
-<span class="sourceLineNo">760</span> return fullCount;<a name="line.760"></a>
-<span class="sourceLineNo">761</span> }<a name="line.761"></a>
-<span class="sourceLineNo">762</span><a name="line.762"></a>
-<span class="sourceLineNo">763</span> /**<a name="line.763"></a>
-<span class="sourceLineNo">764</span> * This method will find the buckets that are minimally occupied<a name="line.764"></a>
-<span class="sourceLineNo">765</span> * and are not reference counted and will free them completely<a name="line.765"></a>
-<span class="sourceLineNo">766</span> * without any constraint on the access times of the elements,<a name="line.766"></a>
-<span class="sourceLineNo">767</span> * and as a process will completely free at most the number of buckets<a name="line.767"></a>
-<span class="sourceLineNo">768</span> * passed, sometimes it might not due to changing refCounts<a name="line.768"></a>
-<span class="sourceLineNo">769</span> *<a name="line.769"></a>
-<span class="sourceLineNo">770</span> * @param completelyFreeBucketsNeeded number of buckets to free<a name="line.770"></a>
-<span class="sourceLineNo">771</span> **/<a name="line.771"></a>
-<span class="sourceLineNo">772</span> private void freeEntireBuckets(int completelyFreeBucketsNeeded) {<a name="line.772"></a>
-<span class="sourceLineNo">773</span> if (completelyFreeBucketsNeeded != 0) {<a name="line.773"></a>
-<span class="sourceLineNo">774</span> // First we will build a set where the offsets are reference counted, usually<a name="line.774"></a>
-<span class="sourceLineNo">775</span> // this set is small around O(Handler Count) unless something else is wrong<a name="line.775"></a>
-<span class="sourceLineNo">776</span> Set<Integer> inUseBuckets = new HashSet<>();<a name="line.776"></a>
-<span class="sourceLineNo">777</span> backingMap.forEach((k, be) -> {<a name="line.777"></a>
-<span class="sourceLineNo">778</span> if (be.isRpcRef()) {<a name="line.778"></a>
-<span class="sourceLineNo">779</span> inUseBuckets.add(bucketAllocator.getBucketIndex(be.offset()));<a name="line.779"></a>
-<span class="sourceLineNo">780</span> }<a name="line.780"></a>
-<span class="sourceLineNo">781</span> });<a name="line.781"></a>
-<span class="sourceLineNo">782</span> Set<Integer> candidateBuckets =<a name="line.782"></a>
-<span class="sourceLineNo">783</span> bucketAllocator.getLeastFilledBuckets(inUseBuckets, completelyFreeBucketsNeeded);<a name="line.783"></a>
-<span class="sourceLineNo">784</span> for (Map.Entry<BlockCacheKey, BucketEntry> entry : backingMap.entrySet()) {<a name="line.784"></a>
-<span class="sourceLineNo">785</span> if (candidateBuckets.contains(bucketAllocator.getBucketIndex(entry.getValue().offset()))) {<a name="line.785"></a>
-<span class="sourceLineNo">786</span> evictBucketEntryIfNoRpcReferenced(entry.getKey(), entry.getValue());<a name="line.786"></a>
-<span class="sourceLineNo">787</span> }<a name="line.787"></a>
-<span class="sourceLineNo">788</span> }<a name="line.788"></a>
-<span class="sourceLineNo">789</span> }<a name="line.789"></a>
-<span class="sourceLineNo">790</span> }<a name="line.790"></a>
-<span class="sourceLineNo">791</span><a name="line.791"></a>
-<span class="sourceLineNo">792</span> /**<a name="line.792"></a>
-<span class="sourceLineNo">793</span> * Free the space if the used size reaches acceptableSize() or one size block<a name="line.793"></a>
-<span class="sourceLineNo">794</span> * couldn't be allocated. When freeing the space, we use the LRU algorithm and<a name="line.794"></a>
-<span class="sourceLineNo">795</span> * ensure there must be some blocks evicted<a name="line.795"></a>
-<span class="sourceLineNo">796</span> * @param why Why we are being called<a name="line.796"></a>
-<span class="sourceLineNo">797</span> */<a name="line.797"></a>
-<span class="sourceLineNo">798</span> private void freeSpace(final String why) {<a name="line.798"></a>
-<span class="sourceLineNo">799</span> // Ensure only one freeSpace progress at a time<a name="line.799"></a>
-<span class="sourceLineNo">800</span> if (!freeSpaceLock.tryLock()) {<a name="line.800"></a>
-<span class="sourceLineNo">801</span> return;<a name="line.801"></a>
-<span class="sourceLineNo">802</span> }<a name="line.802"></a>
-<span class="sourceLineNo">803</span> try {<a name="line.803"></a>
-<span class="sourceLineNo">804</span> freeInProgress = true;<a name="line.804"></a>
-<span class="sourceLineNo">805</span> long bytesToFreeWithoutExtra = 0;<a name="line.805"></a>
-<span class="sourceLineNo">806</span> // Calculate free byte for each bucketSizeinfo<a name="line.806"></a>
-<span class="sourceLineNo">807</span> StringBuilder msgBuffer = LOG.isDebugEnabled()? new StringBuilder(): null;<a name="line.807"></a>
-<span class="sourceLineNo">808</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.808"></a>
-<span class="sourceLineNo">809</span> long[] bytesToFreeForBucket = new long[stats.length];<a name="line.809"></a>
-<span class="sourceLineNo">810</span> for (int i = 0; i < stats.length; i++) {<a name="line.810"></a>
-<span class="sourceLineNo">811</span> bytesToFreeForBucket[i] = 0;<a name="line.811"></a>
-<span class="sourceLineNo">812</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.812"></a>
-<span class="sourceLineNo">813</span> freeGoal = Math.max(freeGoal, 1);<a name="line.813"></a>
-<span class="sourceLineNo">814</span> if (stats[i].freeCount() < freeGoal) {<a name="line.814"></a>
-<span class="sourceLineNo">815</span> bytesToFreeForBucket[i] = stats[i].itemSize() * (freeGoal - stats[i].freeCount());<a name="line.815"></a>
-<span class="sourceLineNo">816</span> bytesToFreeWithoutExtra += bytesToFreeForBucket[i];<a name="line.816"></a>
-<span class="sourceLineNo">817</span> if (msgBuffer != null) {<a name="line.817"></a>
-<span class="sourceLineNo">818</span> msgBuffer.append("Free for bucketSize(" + stats[i].itemSize() + ")="<a name="line.818"></a>
-<span class="sourceLineNo">819</span> + StringUtils.byteDesc(bytesToFreeForBucket[i]) + ", ");<a name="line.819"></a>
-<span class="sourceLineNo">820</span> }<a name="line.820"></a>
-<span class="sourceLineNo">821</span> }<a name="line.821"></a>
-<span class="sourceLineNo">822</span> }<a name="line.822"></a>
-<span class="sourceLineNo">823</span> if (msgBuffer != null) {<a name="line.823"></a>
-<span class="sourceLineNo">824</span> msgBuffer.append("Free for total=" + StringUtils.byteDesc(bytesToFreeWithoutExtra) + ", ");<a name="line.824"></a>
-<span class="sourceLineNo">825</span> }<a name="line.825"></a>
-<span class="sourceLineNo">826</span><a name="line.826"></a>
-<span class="sourceLineNo">827</span> if (bytesToFreeWithoutExtra <= 0) {<a name="line.827"></a>
-<span class="sourceLineNo">828</span> return;<a name="line.828"></a>
-<span class="sourceLineNo">829</span> }<a name="line.829"></a>
-<span class="sourceLineNo">830</span> long currentSize = bucketAllocator.getUsedSize();<a name="line.830"></a>
-<span class="sourceLineNo">831</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.831"></a>
-<span class="sourceLineNo">832</span> if (LOG.isDebugEnabled() && msgBuffer != null) {<a name="line.832"></a>
-<span class="sourceLineNo">833</span> LOG.debug("Free started because \"" + why + "\"; " + msgBuffer.toString() +<a name="line.833"></a>
-<span class="sourceLineNo">834</span> " of current used=" + StringUtils.byteDesc(currentSize) + ", actual cacheSize=" +<a name="line.834"></a>
-<span class="sourceLineNo">835</span> StringUtils.byteDesc(realCacheSize.sum()) + ", total=" + StringUtils.byteDesc(totalSize));<a name="line.835"></a>
-<span class="sourceLineNo">836</span> }<a name="line.836"></a>
-<span class="sourceLineNo">837</span><a name="line.837"></a>
-<span class="sourceLineNo">838</span> long bytesToFreeWithExtra = (long) Math.floor(bytesToFreeWithoutExtra<a name="line.838"></a>
-<span class="sourceLineNo">839</span> * (1 + extraFreeFactor));<a name="line.839"></a>
-<span class="sourceLineNo">840</span><a name="line.840"></a>
-<span class="sourceLineNo">841</span> // Instantiate priority buckets<a name="line.841"></a>
-<span class="sourceLineNo">842</span> BucketEntryGroup bucketSingle = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.842"></a>
-<span class="sourceLineNo">843</span> blockSize, getPartitionSize(singleFactor));<a name="line.843"></a>
-<span class="sourceLineNo">844</span> BucketEntryGroup bucketMulti = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.844"></a>
-<span class="sourceLineNo">845</span> blockSize, getPartitionSize(multiFactor));<a name="line.845"></a>
-<span class="sourceLineNo">846</span> BucketEntryGroup bucketMemory = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.846"></a>
-<span class="sourceLineNo">847</span> blockSize, getPartitionSize(memoryFactor));<a name="line.847"></a>
-<span class="sourceLineNo">848</span><a name="line.848"></a>
-<span class="sourceLineNo">849</span> // Scan entire map putting bucket entry into appropriate bucket entry<a name="line.849"></a>
-<span class="sourceLineNo">850</span> // group<a name="line.850"></a>
-<span class="sourceLineNo">851</span> for (Map.Entry<BlockCacheKey, BucketEntry> bucketEntryWithKey : backingMap.entrySet()) {<a name="line.851"></a>
-<span class="sourceLineNo">852</span> switch (bucketEntryWithKey.getValue().getPriority()) {<a name="line.852"></a>
-<span class="sourceLineNo">853</span> case SINGLE: {<a name="line.853"></a>
-<span class="sourceLineNo">854</span> bucketSingle.add(bucketEntryWithKey);<a name="line.854"></a>
-<span class="sourceLineNo">855</span> break;<a name="line.855"></a>
-<span class="sourceLineNo">856</span> }<a name="line.856"></a>
-<span class="sourceLineNo">857</span> case MULTI: {<a name="line.857"></a>
-<span class="sourceLineNo">858</span> bucketMulti.add(bucketEntryWithKey);<a name="line.858"></a>
-<span class="sourceLineNo">859</span> break;<a name="line.859"></a>
-<span class="sourceLineNo">860</span> }<a name="line.860"></a>
-<span class="sourceLineNo">861</span> case MEMORY: {<a name="line.861"></a>
-<span class="sourceLineNo">862</span> bucketMemory.add(bucketEntryWithKey);<a name="line.862"></a>
-<span class="sourceLineNo">863</span> break;<a name="line.863"></a>
-<span class="sourceLineNo">864</span> }<a name="line.864"></a>
-<span class="sourceLineNo">865</span> }<a name="line.865"></a>
-<span class="sourceLineNo">866</span> }<a name="line.866"></a>
-<span class="sourceLineNo">867</span><a name="line.867"></a>
-<span class="sourceLineNo">868</span> PriorityQueue<BucketEntryGroup> bucketQueue = new PriorityQueue<>(3,<a name="line.868"></a>
-<span class="sourceLineNo">869</span> Comparator.comparingLong(BucketEntryGroup::overflow));<a name="line.869"></a>
-<span class="sourceLineNo">870</span><a name="line.870"></a>
-<span class="sourceLineNo">871</span> bucketQueue.add(bucketSingle);<a name="line.871"></a>
-<span class="sourceLineNo">872</span> bucketQueue.add(bucketMulti);<a name="line.872"></a>
-<span class="sourceLineNo">873</span> bucketQueue.add(bucketMemory);<a name="line.873"></a>
-<span class="sourceLineNo">874</span><a name="line.874"></a>
-<span class="sourceLineNo">875</span> int remainingBuckets = bucketQueue.size();<a name="line.875"></a>
-<span class="sourceLineNo">876</span> long bytesFreed = 0;<a name="line.876"></a>
-<span class="sourceLineNo">877</span><a name="line.877"></a>
-<span class="sourceLineNo">878</span> BucketEntryGroup bucketGroup;<a name="line.878"></a>
-<span class="sourceLineNo">879</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.879"></a>
-<span class="sourceLineNo">880</span> long overflow = bucketGroup.overflow();<a name="line.880"></a>
-<span class="sourceLineNo">881</span> if (overflow > 0) {<a name="line.881"></a>
-<span class="sourceLineNo">882</span> long bucketBytesToFree = Math.min(overflow,<a name="line.882"></a>
-<span class="sourceLineNo">883</span> (bytesToFreeWithoutExtra - bytesFreed) / remainingBuckets);<a name="line.883"></a>
-<span class="sourceLineNo">884</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.884"></a>
-<span class="sourceLineNo">885</span> }<a name="line.885"></a>
-<span class="sourceLineNo">886</span> remainingBuckets--;<a name="line.886"></a>
-<span class="sourceLineNo">887</span> }<a name="line.887"></a>
-<span class="sourceLineNo">888</span><a name="line.888"></a>
-<span class="sourceLineNo">889</span> // Check and free if there are buckets that still need freeing of space<a name="line.889"></a>
-<span class="sourceLineNo">890</span> if (bucketSizesAboveThresholdCount(minFactor) > 0) {<a name="line.890"></a>
-<span class="sourceLineNo">891</span> bucketQueue.clear();<a name="line.891"></a>
-<span class="sourceLineNo">892</span> remainingBuckets = 3;<a name="line.892"></a>
-<span class="sourceLineNo">893</span><a name="line.893"></a>
-<span class="sourceLineNo">894</span> bucketQueue.add(bucketSingle);<a name="line.894"></a>
-<span class="sourceLineNo">895</span> bucketQueue.add(bucketMulti);<a name="line.895"></a>
-<span class="sourceLineNo">896</span> bucketQueue.add(bucketMemory);<a name="line.896"></a>
-<span class="sourceLineNo">897</span><a name="line.897"></a>
-<span class="sourceLineNo">898</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.898"></a>
-<span class="sourceLineNo">899</span> long bucketBytesToFree = (bytesToFreeWithExtra - bytesFreed) / remainingBuckets;<a name="line.899"></a>
-<span class="sourceLineNo">900</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.900"></a>
-<span class="sourceLineNo">901</span> remainingBuckets--;<a name="line.901"></a>
-<span class="sourceLineNo">902</span> }<a name="line.902"></a>
-<span class="sourceLineNo">903</span> }<a name="line.903"></a>
-<span class="sourceLineNo">904</span><a name="line.904"></a>
-<span class="sourceLineNo">905</span> // Even after the above free we might still need freeing because of the<a name="line.905"></a>
-<span class="sourceLineNo">906</span> // De-fragmentation of the buckets (also called Slab Calcification problem), i.e<a name="line.906"></a>
-<span class="sourceLineNo">907</span> // there might be some buckets where the occupancy is very sparse and thus are not<a name="line.907"></a>
-<span class="sourceLineNo">908</span> // yielding the free for the other bucket sizes, the fix for this to evict some<a name="line.908"></a>
-<span class="sourceLineNo">909</span> // of the buckets, we do this by evicting the buckets that are least fulled<a name="line.909"></a>
-<span class="sourceLineNo">910</span> freeEntireBuckets(DEFAULT_FREE_ENTIRE_BLOCK_FACTOR *<a name="line.910"></a>
-<span class="sourceLineNo">911</span> bucketSizesAboveThresholdCount(1.0f));<a name="line.911"></a>
-<span class="sourceLineNo">912</span><a name="line.912"></a>
-<span class="sourceLineNo">913</span> if (LOG.isDebugEnabled()) {<a name="line.913"></a>
-<span class="sourceLineNo">914</span> long single = bucketSingle.totalSize();<a name="line.914"></a>
-<span class="sourceLineNo">915</span> long multi = bucketMulti.totalSize();<a name="line.915"></a>
-<span class="sourceLineNo">916</span> long memory = bucketMemory.totalSize();<a name="line.916"></a>
-<span class="sourceLineNo">917</span> if (LOG.isDebugEnabled()) {<a name="line.917"></a>
-<span class="sourceLineNo">918</span> LOG.debug("Bucket cache free space completed; " + "freed="<a name="line.918"></a>
-<span class="sourceLineNo">919</span> + StringUtils.byteDesc(bytesFreed) + ", " + "total="<a name="line.919"></a>
-<span class="sourceLineNo">920</span> + StringUtils.byteDesc(totalSize) + ", " + "single="<a name="line.920"></a>
-<span class="sourceLineNo">921</span> + StringUtils.byteDesc(single) + ", " + "multi="<a name="line.921"></a>
-<span class="sourceLineNo">922</span> + StringUtils.byteDesc(multi) + ", " + "memory="<a name="line.922"></a>
-<span class="sourceLineNo">923</span> + StringUtils.byteDesc(memory));<a name="line.923"></a>
-<span class="sourceLineNo">924</span> }<a name="line.924"></a>
-<span class="sourceLineNo">925</span> }<a name="line.925"></a>
-<span class="sourceLineNo">926</span><a name="line.926"></a>
-<span class="sourceLineNo">927</span> } catch (Throwable t) {<a name="line.927"></a>
-<span class="sourceLineNo">928</span> LOG.warn("Failed freeing space", t);<a name="line.928"></a>
-<span class="sourceLineNo">929</span> } finally {<a name="line.929"></a>
-<span class="sourceLineNo">930</span> cacheStats.evict();<a name="line.930"></a>
-<span class="sourceLineNo">931</span> freeInProgress = false;<a name="line.931"></a>
-<span class="sourceLineNo">932</span> freeSpaceLock.unlock();<a name="line.932"></a>
-<span class="sourceLineNo">933</span> }<a name="line.933"></a>
-<span class="sourceLineNo">934</span> }<a name="line.934"></a>
-<span class="sourceLineNo">935</span><a name="line.935"></a>
-<span class="sourceLineNo">936</span> // This handles flushing the RAM cache to IOEngine.<a name="line.936"></a>
-<span class="sourceLineNo">937</span> class WriterThread extends Thread {<a name="line.937"></a>
-<span class="sourceLineNo">938</span> private final BlockingQueue<RAMQueueEntry> inputQueue;<a name="line.938"></a>
-<span class="sourceLineNo">939</span> private volatile boolean writerEnabled = true;<a name="line.939"></a>
-<span class="sourceLineNo">940</span><a name="line.940"></a>
-<span class="sourceLineNo">941</span> WriterThread(BlockingQueue<RAMQueueEntry> queue) {<a name="line.941"></a>
-<span class="sourceLineNo">942</span> super("BucketCacheWriterThread");<a name="line.942"></a>
-<span class="sourceLineNo">943</span> this.inputQueue = queue;<a name="line.943"></a>
-<span class="sourceLineNo">944</span> }<a name="line.944"></a>
-<span class="sourceLineNo">945</span><a name="line.945"></a>
-<span class="sourceLineNo">946</span> // Used for test<a name="line.946"></a>
-<span class="sourceLineNo">947</span> void disableWriter() {<a name="line.947"></a>
-<span class="sourceLineNo">948</span> this.writerEnabled = false;<a name="line.948"></a>
-<span class="sourceLineNo">949</span> }<a name="line.949"></a>
-<span class="sourceLineNo">950</span><a name="line.950"></a>
-<span class="sourceLineNo">951</span> @Override<a name="line.951"></a>
-<span class="sourceLineNo">952</span> public void run() {<a name="line.952"></a>
-<span class="sourceLineNo">953</span> List<RAMQueueEntry> entries = new ArrayList<>();<a name="line.953"></a>
-<span class="sourceLineNo">954</span> try {<a name="line.954"></a>
-<span class="sourceLineNo">955</span> while (cacheEnabled && writerEnabled) {<a name="line.955"></a>
-<span class="sourceLineNo">956</span> try {<a name="line.956"></a>
-<span class="sourceLineNo">957</span> try {<a name="line.957"></a>
-<span class="sourceLineNo">958</span> // Blocks<a name="line.958"></a>
-<span class="sourceLineNo">959</span> entries = getRAMQueueEntries(inputQueue, entries);<a name="line.959"></a>
-<span class="sourceLineNo">960</span> } catch (InterruptedException ie) {<a name="line.960"></a>
-<span class="sourceLineNo">961</span> if (!cacheEnabled || !writerEnabled) {<a name="line.961"></a>
-<span class="sourceLineNo">962</span> break;<a name="line.962"></a>
-<span class="sourceLineNo">963</span> }<a name="line.963"></a>
-<span class="sourceLineNo">964</span> }<a name="line.964"></a>
-<span class="sourceLineNo">965</span> doDrain(entries);<a name="line.965"></a>
-<span class="sourceLineNo">966</span> } catch (Exception ioe) {<a name="line.966"></a>
-<span class="sourceLineNo">967</span> LOG.error("WriterThread encountered error", ioe);<a name="line.967"></a>
-<span class="sourceLineNo">968</span> }<a name="line.968"></a>
-<span class="sourceLineNo">969</span> }<a name="line.969"></a>
-<span class="sourceLineNo">970</span> } catch (Throwable t) {<a name="line.970"></a>
-<span class="sourceLineNo">971</span> LOG.warn("Failed doing drain", t);<a name="line.971"></a>
-<span class="sourceLineNo">972</span> }<a name="line.972"></a>
-<span class="sourceLineNo">973</span> LOG.info(this.getName() + " exiting, cacheEnabled=" + cacheEnabled);<a name="line.973"></a>
-<span class="sourceLineNo">974</span> }<a name="line.974"></a>
-<span class="sourceLineNo">975</span> }<a name="line.975"></a>
-<span class="sourceLineNo">976</span><a name="line.976"></a>
-<span class="sourceLineNo">977</span> /**<a name="line.977"></a>
-<span class="sourceLineNo">978</span> * Put the new bucket entry into backingMap. Notice that we are allowed to replace the existing<a name="line.978"></a>
-<span class="sourceLineNo">979</span> * cache with a new block for the same cache key. there's a corner case: one thread cache a block<a name="line.979"></a>
-<span class="sourceLineNo">980</span> * in ramCache, copy to io-engine and add a bucket entry to backingMap. Caching another new block<a name="line.980"></a>
-<span class="sourceLineNo">981</span> * with the same cache key do the same thing for the same cache key, so if not evict the previous<a name="line.981"></a>
-<span class="sourceLineNo">982</span> * bucket entry, then memory leak happen because the previous bucketEntry is gone but the<a name="line.982"></a>
-<span class="sourceLineNo">983</span> * bucketAllocator do not free its memory.<a name="line.983"></a>
-<span class="sourceLineNo">984</span> * @see BlockCacheUtil#shouldReplaceExistingCacheBlock(BlockCache blockCache,BlockCacheKey<a name="line.984"></a>
-<span class="sourceLineNo">985</span> * cacheKey, Cacheable newBlock)<a name="line.985"></a>
-<span class="sourceLineNo">986</span> * @param key Block cache key<a name="line.986"></a>
-<span class="sourceLineNo">987</span> * @param bucketEntry Bucket entry to put into backingMap.<a name="line.987"></a>
-<span class="sourceLineNo">988</span> */<a name="line.988"></a>
-<span class="sourceLineNo">989</span> protected void putIntoBackingMap(BlockCacheKey key, BucketEntry bucketEntry) {<a name="line.989"></a>
-<span class="sourceLineNo">990</span> BucketEntry previousEntry = backingMap.put(key, bucketEntry);<a name="line.990"></a>
-<span class="sourceLineNo">991</span> if (previousEntry != null && previousEntry != bucketEntry) {<a name="line.991"></a>
-<span class="sourceLineNo">992</span> previousEntry.withWriteLock(offsetLock, () -> {<a name="line.992"></a>
-<span class="sourceLineNo">993</span> blockEvicted(key, previousEntry, false);<a name="line.993"></a>
-<span class="sourceLineNo">994</span> return null;<a name="line.994"></a>
-<span class="sourceLineNo">995</span> });<a name="line.995"></a>
-<span class="sourceLineNo">996</span> }<a name="line.996"></a>
-<span class="sourceLineNo">997</span> }<a name="line.997"></a>
-<span class="sourceLineNo">998</span><a name="line.998"></a>
-<span class="sourceLineNo">999</span> /**<a name="line.999"></a>
-<span class="sourceLineNo">1000</span> * Flush the entries in ramCache to IOEngine and add bucket entry to backingMap. Process all that<a name="line.1000"></a>
-<span class="sourceLineNo">1001</span> * are passed in even if failure being sure to remove from ramCache else we'll never undo the<a name="line.1001"></a>
-<span class="sourceLineNo">1002</span> * references and we'll OOME.<a name="line.1002"></a>
-<span class="sourceLineNo">1003</span> * @param entries Presumes list passed in here will be processed by this invocation only. No<a name="line.1003"></a>
-<span class="sourceLineNo">1004</span> * interference expected.<a name="line.1004"></a>
-<span class="sourceLineNo">1005</span> */<a name="line.1005"></a>
-<span class="sourceLineNo">1006</span> void doDrain(final List<RAMQueueEntry> entries) throws InterruptedException {<a name="line.1006"></a>
-<span class="sourceLineNo">1007</span> if (entries.isEmpty()) {<a name="line.1007"></a>
-<span class="sourceLineNo">1008</span> return;<a name="line.1008"></a>
-<span class="sourceLineNo">1009</span> }<a name="line.1009"></a>
-<span class="sourceLineNo">1010</span> // This method is a little hard to follow. We run through the passed in entries and for each<a name="line.1010"></a>
-<span class="sourceLineNo">1011</span> // successful add, we add a non-null BucketEntry to the below bucketEntries. Later we must<a name="line.1011"></a>
-<span class="sourceLineNo">1012</span> // do cleanup making sure we've cleared ramCache of all entries regardless of whether we<a name="line.1012"></a>
-<span class="sourceLineNo">1013</span> // successfully added the item to the bucketcache; if we don't do the cleanup, we'll OOME by<a name="line.1013"></a>
-<span class="sourceLineNo">1014</span> // filling ramCache. We do the clean up by again running through the passed in entries<a name="line.1014"></a>
-<span class="sourceLineNo">1015</span> // doing extra work when we find a non-null bucketEntries corresponding entry.<a name="line.1015"></a>
-<span class="sourceLineNo">1016</span> final int size = entries.size();<a name="line.1016"></a>
-<span class="sourceLineNo">1017</span> BucketEntry[] bucketEntries = new BucketEntry[size];<a name="line.1017"></a>
-<span class="sourceLineNo">1018</span> // Index updated inside loop if success or if we can't succeed. We retry if cache is full<a name="line.1018"></a>
-<span class="sourceLineNo">1019</span> // when we go to add an entry by going around the loop again without upping the index.<a name="line.1019"></a>
-<span class="sourceLineNo">1020</span> int index = 0;<a name="line.1020"></a>
-<span class="sourceLineNo">1021</span> while (cacheEnabled && index < size) {<a name="line.1021"></a>
-<span class="sourceLineNo">1022</span> RAMQueueEntry re = null;<a name="line.1022"></a>
-<span class="sourceLineNo">1023</span> try {<a name="line.1023"></a>
-<span class="sourceLineNo">1024</span> re = entries.get(index);<a name="line.1024"></a>
-<span class="sourceLineNo">1025</span> if (re == null) {<a name="line.1025"></a>
-<span class="sourceLineNo">1026</span> LOG.warn("Couldn't get entry or changed on us; who else is messing with it?");<a name="line.1026"></a>
-<span class="sourceLineNo">1027</span> index++;<a name="line.1027"></a>
-<span class="sourceLineNo">1028</span> continue;<a name="line.1028"></a>
-<span class="sourceLineNo">1029</span> }<a name="line.1029"></a>
-<span class="sourceLineNo">1030</span> BucketEntry bucketEntry = re.writeToCache(ioEngine, bucketAllocator, realCacheSize,<a name="line.1030"></a>
-<span class="sourceLineNo">1031</span> this::createRecycler);<a name="line.1031"></a>
-<span class="sourceLineNo">1032</span> // Successfully added. Up index and add bucketEntry. Clear io exceptions.<a name="line.1032"></a>
-<span class="sourceLineNo">1033</span> bucketEntries[index] = bucketEntry;<a name="line.1033"></a>
-<span class="sourceLineNo">1034</span> if (ioErrorStartTime > 0) {<a name="line.1034"></a>
-<span class="sourceLineNo">1035</span> ioErrorStartTime = -1;<a name="line.1035"></a>
-<span class="sourceLineNo">1036</span> }<a name="line.1036"></a>
-<span class="sourceLineNo">1037</span> index++;<a name="line.1037"></a>
-<span class="sourceLineNo">1038</span> } catch (BucketAllocatorException fle) {<a name="line.1038"></a>
-<span class="sourceLineNo">1039</span> LOG.warn("Failed allocation for " + (re == null ? "" : re.getKey()) + "; " + fle);<a name="line.1039"></a>
-<span class="sourceLineNo">1040</span> // Presume can't add. Too big? Move index on. Entry will be cleared from ramCache below.<a name="line.1040"></a>
-<span class="sourceLineNo">1041</span> bucketEntries[index] = null;<a name="line.1041"></a>
-<span class="sourceLineNo">1042</span> index++;<a name="line.1042"></a>
-<span class="sourceLineNo">1043</span> } catch (CacheFullException cfe) {<a name="line.1043"></a>
-<span class="sourceLineNo">1044</span> // Cache full when we tried to add. Try freeing space and then retrying (don't up index)<a name="line.1044"></a>
-<span class="sourceLineNo">1045</span> if (!freeInProgress) {<a name="line.1045"></a>
-<span class="sourceLineNo">1046</span> freeSpace("Full!");<a name="line.1046"></a>
-<span class="sourceLineNo">1047</span> } else {<a name="line.1047"></a>
-<span class="sourceLineNo">1048</span> Thread.sleep(50);<a name="line.1048"></a>
-<span class="sourceLineNo">1049</span> }<a name="line.1049"></a>
-<span class="sourceLineNo">1050</span> } catch (IOException ioex) {<a name="line.1050"></a>
-<span class="sourceLineNo">1051</span> // Hopefully transient. Retry. checkIOErrorIsTolerated disables cache if problem.<a name="line.1051"></a>
-<span class="sourceLineNo">1052</span> LOG.error("Failed writing to bucket cache", ioex);<a name="line.1052"></a>
-<span class="sourceLineNo">1053</span> checkIOErrorIsTolerated();<a name="line.1053"></a>
-<span class="sourceLineNo">1054</span> }<a name="line.1054"></a>
-<span class="sourceLineNo">1055</span> }<a name="line.1055"></a>
-<span class="sourceLineNo">1056</span><a name="line.1056"></a>
-<span class="sourceLineNo">1057</span> // Make sure data pages are written on media before we update maps.<a name="line.1057"></a>
-<span class="sourceLineNo">1058</span> try {<a name="line.1058"></a>
-<span class="sourceLineNo">1059</span> ioEngine.sync();<a name="line.1059"></a>
-<span class="sourceLineNo">1060</span> } catch (IOException ioex) {<a name="line.1060"></a>
-<span class="sourceLineNo">1061</span> LOG.error("Failed syncing IO engine", ioex);<a name="line.1061"></a>
-<span class="sourceLineNo">1062</span> checkIOErrorIsTolerated();<a name="line.1062"></a>
-<span class="sourceLineNo">1063</span> // Since we failed sync, free the blocks in bucket allocator<a name="line.1063"></a>
-<span class="sourceLineNo">1064</span> for (int i = 0; i < entries.size(); ++i) {<a name="line.1064"></a>
-<span class="sourceLineNo">1065</span> if (bucketEntries[i] != null) {<a name="line.1065"></a>
-<span class="sourceLineNo">1066</span> bucketAllocator.freeBlock(bucketEntries[i].offset());<a name="line.1066"></a>
-<span class="sourceLineNo">1067</span> bucketEntries[i] = null;<a name="line.1067"></a>
-<span class="sourceLineNo">1068</span> }<a name="line.1068"></a>
-<span class="sourceLineNo">1069</span> }<a name="line.1069"></a>
-<span class="sourceLineNo">1070</span> }<a name="line.1070"></a>
-<span class="sourceLineNo">1071</span><a name="line.1071"></a>
-<span class="sourceLineNo">1072</span> // Now add to backingMap if successfully added to bucket cache. Remove from ramCache if<a name="line.1072"></a>
-<span class="sourceLineNo">1073</span> // success or error.<a name="line.1073"></a>
-<span class="sourceLineNo">1074</span> for (int i = 0; i < size; ++i) {<a name="line.1074"></a>
-<span class="sourceLineNo">1075</span> BlockCacheKey key = entries.get(i).getKey();<a name="line.1075"></a>
-<span class="sourceLineNo">1076</span> // Only add if non-null entry.<a name="line.1076"></a>
-<span class="sourceLineNo">1077</span> if (bucketEntries[i] != null) {<a name="line.1077"></a>
-<span class="sourceLineNo">1078</span> putIntoBackingMap(key, bucketEntries[i]);<a name="line.1078"></a>
-<span class="sourceLineNo">1079</span> }<a name="line.1079"></a>
-<span class="sourceLineNo">1080</span> // Always remove from ramCache even if we failed adding it to the block cache above.<a name="line.1080"></a>
-<span class="sourceLineNo">1081</span> boolean existed = ramCache.remove(key, re -> {<a name="line.1081"></a>
-<span class="sourceLineNo">1082</span> if (re != null) {<a name="line.1082"></a>
-<span class="sourceLineNo">1083</span> heapSize.add(-1 * re.getData().heapSize());<a name="line.1083"></a>
-<span class="sourceLineNo">1084</span> }<a name="line.1084"></a>
-<span class="sourceLineNo">1085</span> });<a name="line.1085"></a>
-<span class="sourceLineNo">1086</span> if (!existed && bucketEntries[i] != null) {<a name="line.1086"></a>
-<span class="sourceLineNo">1087</span> // Block should have already been evicted. Remove it and free space.<a name="line.1087"></a>
-<span class="sourceLineNo">1088</span> final BucketEntry bucketEntry = bucketEntries[i];<a name="line.1088"></a>
-<span class="sourceLineNo">1089</span> bucketEntry.withWriteLock(offsetLock, () -> {<a name="line.1089"></a>
-<span class="sourceLineNo">1090</span> if (backingMap.remove(key, bucketEntry)) {<a name="line.1090"></a>
-<span class="sourceLineNo">1091</span> blockEvicted(key, bucketEntry, false);<a name="line.1091"></a>
-<span class="sourceLineNo">1092</span> }<a name="line.1092"></a>
-<span class="sourceLineNo">1093</span> return null;<a name="line.1093"></a>
-<span class="sourceLineNo">1094</span> });<a name="line.1094"></a>
-<span class="sourceLineNo">1095</span> }<a name="line.1095"></a>
-<span class="sourceLineNo">1096</span> }<a name="line.1096"></a>
-<span class="sourceLineNo">1097</span><a name="line.1097"></a>
-<span class="sourceLineNo">1098</span> long used = bucketAllocator.getUsedSize();<a name="line.1098"></a>
-<span class="sourceLineNo">1099</span> if (used > acceptableSize()) {<a name="line.1099"></a>
-<span class="sourceLineNo">1100</span> freeSpace("Used=" + used + " > acceptable=" + acceptableSize());<a name="line.1100"></a>
-<span class="sourceLineNo">1101</span> }<a name="line.1101"></a>
-<span class="sourceLineNo">1102</span> return;<a name="line.1102"></a>
-<span class="sourceLineNo">1103</span> }<a name="line.1103"></a>
-<span class="sourceLineNo">1104</span><a name="line.1104"></a>
-<span class="sourceLineNo">1105</span> /**<a name="line.1105"></a>
-<span class="sourceLineNo">1106</span> * Blocks until elements available in {@code q} then tries to grab as many as possible before<a name="line.1106"></a>
-<span class="sourceLineNo">1107</span> * returning.<a name="line.1107"></a>
-<span class="sourceLineNo">1108</span> * @param receptacle Where to stash the elements taken from queue. We clear before we use it just<a name="line.1108"></a>
-<span class="sourceLineNo">1109</span> * in case.<a name="line.1109"></a>
-<span class="sourceLineNo">1110</span> * @param q The queue to take from.<a name="line.1110"></a>
-<span class="sourceLineNo">1111</span> * @return {@code receptacle} laden with elements taken from the queue or empty if none found.<a name="line.1111"></a>
-<span class="sourceLineNo">1112</span> */<a name="line.1112"></a>
-<span class="sourceLineNo">1113</span> static List<RAMQueueEntry> getRAMQueueEntries(BlockingQueue<RAMQueueEntry> q,<a name="line.1113"></a>
-<span class="sourceLineNo">1114</span> List<RAMQueueEntry> receptacle) throws InterruptedException {<a name="line.1114"></a>
-<span class="sourceLineNo">1115</span> // Clear sets all entries to null and sets size to 0. We retain allocations. Presume it<a name="line.1115"></a>
-<span class="sourceLineNo">1116</span> // ok even if list grew to accommodate thousands.<a name="line.1116"></a>
-<span class="sourceLineNo">1117</span> receptacle.clear();<a name="line.1117"></a>
-<span class="sourceLineNo">1118</span> receptacle.add(q.take());<a name="line.1118"></a>
-<span class="sourceLineNo">1119</span> q.drainTo(receptacle);<a name="line.1119"></a>
-<span class="sourceLineNo">1120</span> return receptacle;<a name="line.1120"></a>
-<span class="sourceLineNo">1121</span> }<a name="line.1121"></a>
-<span class="sourceLineNo">1122</span><a name="line.1122"></a>
-<span class="sourceLineNo">1123</span> /**<a name="line.1123"></a>
-<span class="sourceLineNo">1124</span> * @see #retrieveFromFile(int[])<a name="line.1124"></a>
-<span class="sourceLineNo">1125</span> */<a name="line.1125"></a>
-<span class="sourceLineNo">1126</span> @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="OBL_UNSATISFIED_OBLIGATION",<a name="line.1126"></a>
-<span class="sourceLineNo">1127</span> justification = "false positive, try-with-resources ensures close is called.")<a name="line.1127"></a>
-<span class="sourceLineNo">1128</span> private void persistToFile() throws IOException {<a name="line.1128"></a>
-<span class="sourceLineNo">1129</span> assert !cacheEnabled;<a name="line.1129"></a>
-<span class="sourceLineNo">1130</span> if (!ioEngine.isPersistent()) {<a name="line.1130"></a>
-<span class="sourceLineNo">1131</span> throw new IOException("Attempt to persist non-persistent cache mappings!");<a name="line.1131"></a>
-<span class="sourceLineNo">1132</span> }<a name="line.1132"></a>
-<span class="sourceLineNo">1133</span> try (FileOutputStream fos = new FileOutputStream(persistencePath, false)) {<a name="line.1133"></a>
-<span class="sourceLineNo">1134</span> fos.write(ProtobufMagic.PB_MAGIC);<a name="line.1134"></a>
-<span class="sourceLineNo">1135</span> BucketProtoUtils.toPB(this).writeDelimitedTo(fos);<a name="line.1135"></a>
+<span class="sourceLineNo">489</span> }<a name="line.489"></a>
+<span class="sourceLineNo">490</span> }<a name="line.490"></a>
+<span class="sourceLineNo">491</span><a name="line.491"></a>
+<span class="sourceLineNo">492</span> /**<a name="line.492"></a>
+<span class="sourceLineNo">493</span> * Get the buffer of the block with the specified key.<a name="line.493"></a>
+<span class="sourceLineNo">494</span> * @param key block's cache key<a name="line.494"></a>
+<span class="sourceLineNo">495</span> * @param caching true if the caller caches blocks on cache misses<a name="line.495"></a>
+<span class="sourceLineNo">496</span> * @param repeat Whether this is a repeat lookup for the same block<a name="line.496"></a>
+<span class="sourceLineNo">497</span> * @param updateCacheMetrics Whether we should update cache metrics or not<a name="line.497"></a>
+<span class="sourceLineNo">498</span> * @return buffer of specified cache key, or null if not in cache<a name="line.498"></a>
+<span class="sourceLineNo">499</span> */<a name="line.499"></a>
+<span class="sourceLineNo">500</span> @Override<a name="line.500"></a>
+<span class="sourceLineNo">501</span> public Cacheable getBlock(BlockCacheKey key, boolean caching, boolean repeat,<a name="line.501"></a>
+<span class="sourceLineNo">502</span> boolean updateCacheMetrics) {<a name="line.502"></a>
+<span class="sourceLineNo">503</span> if (!cacheEnabled) {<a name="line.503"></a>
+<span class="sourceLineNo">504</span> return null;<a name="line.504"></a>
+<span class="sourceLineNo">505</span> }<a name="line.505"></a>
+<span class="sourceLineNo">506</span> RAMQueueEntry re = ramCache.get(key);<a name="line.506"></a>
+<span class="sourceLineNo">507</span> if (re != null) {<a name="line.507"></a>
+<span class="sourceLineNo">508</span> if (updateCacheMetrics) {<a name="line.508"></a>
+<span class="sourceLineNo">509</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.509"></a>
+<span class="sourceLineNo">510</span> }<a name="line.510"></a>
+<span class="sourceLineNo">511</span> re.access(accessCount.incrementAndGet());<a name="line.511"></a>
+<span class="sourceLineNo">512</span> return re.getData();<a name="line.512"></a>
+<span class="sourceLineNo">513</span> }<a name="line.513"></a>
+<span class="sourceLineNo">514</span> BucketEntry bucketEntry = backingMap.get(key);<a name="line.514"></a>
+<span class="sourceLineNo">515</span> if (bucketEntry != null) {<a name="line.515"></a>
+<span class="sourceLineNo">516</span> long start = System.nanoTime();<a name="line.516"></a>
+<span class="sourceLineNo">517</span> ReentrantReadWriteLock lock = offsetLock.getLock(bucketEntry.offset());<a name="line.517"></a>
+<span class="sourceLineNo">518</span> try {<a name="line.518"></a>
+<span class="sourceLineNo">519</span> lock.readLock().lock();<a name="line.519"></a>
+<span class="sourceLineNo">520</span> // We can not read here even if backingMap does contain the given key because its offset<a name="line.520"></a>
+<span class="sourceLineNo">521</span> // maybe changed. If we lock BlockCacheKey instead of offset, then we can only check<a name="line.521"></a>
+<span class="sourceLineNo">522</span> // existence here.<a name="line.522"></a>
+<span class="sourceLineNo">523</span> if (bucketEntry.equals(backingMap.get(key))) {<a name="line.523"></a>
+<span class="sourceLineNo">524</span> // Read the block from IOEngine based on the bucketEntry's offset and length, NOTICE: the<a name="line.524"></a>
+<span class="sourceLineNo">525</span> // block will use the refCnt of bucketEntry, which means if two HFileBlock mapping to<a name="line.525"></a>
+<span class="sourceLineNo">526</span> // the same BucketEntry, then all of the three will share the same refCnt.<a name="line.526"></a>
+<span class="sourceLineNo">527</span> Cacheable cachedBlock = ioEngine.read(bucketEntry);<a name="line.527"></a>
+<span class="sourceLineNo">528</span> if (ioEngine.usesSharedMemory()) {<a name="line.528"></a>
+<span class="sourceLineNo">529</span> // If IOEngine use shared memory, cachedBlock and BucketEntry will share the<a name="line.529"></a>
+<span class="sourceLineNo">530</span> // same RefCnt, do retain here, in order to count the number of RPC references<a name="line.530"></a>
+<span class="sourceLineNo">531</span> cachedBlock.retain();<a name="line.531"></a>
+<span class="sourceLineNo">532</span> }<a name="line.532"></a>
+<span class="sourceLineNo">533</span> // Update the cache statistics.<a name="line.533"></a>
+<span class="sourceLineNo">534</span> if (updateCacheMetrics) {<a name="line.534"></a>
+<span class="sourceLineNo">535</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.535"></a>
+<span class="sourceLineNo">536</span> cacheStats.ioHit(System.nanoTime() - start);<a name="line.536"></a>
+<span class="sourceLineNo">537</span> }<a name="line.537"></a>
+<span class="sourceLineNo">538</span> bucketEntry.access(accessCount.incrementAndGet());<a name="line.538"></a>
+<span class="sourceLineNo">539</span> if (this.ioErrorStartTime > 0) {<a name="line.539"></a>
+<span class="sourceLineNo">540</span> ioErrorStartTime = -1;<a name="line.540"></a>
+<span class="sourceLineNo">541</span> }<a name="line.541"></a>
+<span class="sourceLineNo">542</span> return cachedBlock;<a name="line.542"></a>
+<span class="sourceLineNo">543</span> }<a name="line.543"></a>
+<span class="sourceLineNo">544</span> } catch (IOException ioex) {<a name="line.544"></a>
+<span class="sourceLineNo">545</span> LOG.error("Failed reading block " + key + " from bucket cache", ioex);<a name="line.545"></a>
+<span class="sourceLineNo">546</span> checkIOErrorIsTolerated();<a name="line.546"></a>
+<span class="sourceLineNo">547</span> } finally {<a name="line.547"></a>
+<span class="sourceLineNo">548</span> lock.readLock().unlock();<a name="line.548"></a>
+<span class="sourceLineNo">549</span> }<a name="line.549"></a>
+<span class="sourceLineNo">550</span> }<a name="line.550"></a>
+<span class="sourceLineNo">551</span> if (!repeat && updateCacheMetrics) {<a name="line.551"></a>
+<span class="sourceLineNo">552</span> cacheStats.miss(caching, key.isPrimary(), key.getBlockType());<a name="line.552"></a>
+<span class="sourceLineNo">553</span> }<a name="line.553"></a>
+<span class="sourceLineNo">554</span> return null;<a name="line.554"></a>
+<span class="sourceLineNo">555</span> }<a name="line.555"></a>
+<span class="sourceLineNo">556</span><a name="line.556"></a>
+<span class="sourceLineNo">557</span> /**<a name="line.557"></a>
+<span class="sourceLineNo">558</span> * This method is invoked after the bucketEntry is removed from {@link BucketCache#backingMap}<a name="line.558"></a>
+<span class="sourceLineNo">559</span> */<a name="line.559"></a>
+<span class="sourceLineNo">560</span> void blockEvicted(BlockCacheKey cacheKey, BucketEntry bucketEntry, boolean decrementBlockNumber) {<a name="line.560"></a>
+<span class="sourceLineNo">561</span> bucketEntry.markAsEvicted();<a name="line.561"></a>
+<span class="sourceLineNo">562</span> blocksByHFile.remove(cacheKey);<a name="line.562"></a>
+<span class="sourceLineNo">563</span> if (decrementBlockNumber) {<a name="line.563"></a>
+<span class="sourceLineNo">564</span> this.blockNumber.decrement();<a name="line.564"></a>
+<span class="sourceLineNo">565</span> }<a name="line.565"></a>
+<span class="sourceLineNo">566</span> cacheStats.evicted(bucketEntry.getCachedTime(), cacheKey.isPrimary());<a name="line.566"></a>
+<span class="sourceLineNo">567</span> }<a name="line.567"></a>
+<span class="sourceLineNo">568</span><a name="line.568"></a>
+<span class="sourceLineNo">569</span> /**<a name="line.569"></a>
+<span class="sourceLineNo">570</span> * Free the {{@link BucketEntry} actually,which could only be invoked when the<a name="line.570"></a>
+<span class="sourceLineNo">571</span> * {@link BucketEntry#refCnt} becoming 0.<a name="line.571"></a>
+<span class="sourceLineNo">572</span> */<a name="line.572"></a>
+<span class="sourceLineNo">573</span> void freeBucketEntry(BucketEntry bucketEntry) {<a name="line.573"></a>
+<span class="sourceLineNo">574</span> bucketAllocator.freeBlock(bucketEntry.offset());<a name="line.574"></a>
+<span class="sourceLineNo">575</span> realCacheSize.add(-1 * bucketEntry.getLength());<a name="line.575"></a>
+<span class="sourceLineNo">576</span> }<a name="line.576"></a>
+<span class="sourceLineNo">577</span><a name="line.577"></a>
+<span class="sourceLineNo">578</span> /**<a name="line.578"></a>
+<span class="sourceLineNo">579</span> * Try to evict the block from {@link BlockCache} by force. We'll call this in few cases:<br><a name="line.579"></a>
+<span class="sourceLineNo">580</span> * 1. Close an HFile, and clear all cached blocks. <br><a name="line.580"></a>
+<span class="sourceLineNo">581</span> * 2. Call {@link Admin#clearBlockCache(TableName)} to clear all blocks for a given table.<br><a name="line.581"></a>
+<span class="sourceLineNo">582</span> * <p><a name="line.582"></a>
+<span class="sourceLineNo">583</span> * Firstly, we'll try to remove the block from RAMCache,and then try to evict from backingMap.<a name="line.583"></a>
+<span class="sourceLineNo">584</span> * Here we evict the block from backingMap immediately, but only free the reference from bucket<a name="line.584"></a>
+<span class="sourceLineNo">585</span> * cache by calling {@link BucketEntry#markedAsEvicted}. If there're still some RPC referring this<a name="line.585"></a>
+<span class="sourceLineNo">586</span> * block, block can only be de-allocated when all of them release the block.<a name="line.586"></a>
+<span class="sourceLineNo">587</span> * <p><a name="line.587"></a>
+<span class="sourceLineNo">588</span> * NOTICE: we need to grab the write offset lock firstly before releasing the reference from<a name="line.588"></a>
+<span class="sourceLineNo">589</span> * bucket cache. if we don't, we may read an {@link BucketEntry} with refCnt = 0 when<a name="line.589"></a>
+<span class="sourceLineNo">590</span> * {@link BucketCache#getBlock(BlockCacheKey, boolean, boolean, boolean)}, it's a memory leak.<a name="line.590"></a>
+<span class="sourceLineNo">591</span> * @param cacheKey Block to evict<a name="line.591"></a>
+<span class="sourceLineNo">592</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.592"></a>
+<span class="sourceLineNo">593</span> */<a name="line.593"></a>
+<span class="sourceLineNo">594</span> @Override<a name="line.594"></a>
+<span class="sourceLineNo">595</span> public boolean evictBlock(BlockCacheKey cacheKey) {<a name="line.595"></a>
+<span class="sourceLineNo">596</span> return doEvictBlock(cacheKey, null);<a name="line.596"></a>
+<span class="sourceLineNo">597</span> }<a name="line.597"></a>
+<span class="sourceLineNo">598</span><a name="line.598"></a>
+<span class="sourceLineNo">599</span> /**<a name="line.599"></a>
+<span class="sourceLineNo">600</span> * Evict the {@link BlockCacheKey} and {@link BucketEntry} from {@link BucketCache#backingMap} and<a name="line.600"></a>
+<span class="sourceLineNo">601</span> * {@link BucketCache#ramCache}. <br/><a name="line.601"></a>
+<span class="sourceLineNo">602</span> * NOTE:When Evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.602"></a>
+<span class="sourceLineNo">603</span> * {@link BucketEntry} could be removed.<a name="line.603"></a>
+<span class="sourceLineNo">604</span> * @param cacheKey {@link BlockCacheKey} to evict.<a name="line.604"></a>
+<span class="sourceLineNo">605</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.605"></a>
+<span class="sourceLineNo">606</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.606"></a>
+<span class="sourceLineNo">607</span> */<a name="line.607"></a>
+<span class="sourceLineNo">608</span> private boolean doEvictBlock(BlockCacheKey cacheKey, BucketEntry bucketEntry) {<a name="line.608"></a>
+<span class="sourceLineNo">609</span> if (!cacheEnabled) {<a name="line.609"></a>
+<span class="sourceLineNo">610</span> return false;<a name="line.610"></a>
+<span class="sourceLineNo">611</span> }<a name="line.611"></a>
+<span class="sourceLineNo">612</span> boolean existedInRamCache = removeFromRamCache(cacheKey);<a name="line.612"></a>
+<span class="sourceLineNo">613</span> if (bucketEntry == null) {<a name="line.613"></a>
+<span class="sourceLineNo">614</span> bucketEntry = backingMap.get(cacheKey);<a name="line.614"></a>
+<span class="sourceLineNo">615</span> }<a name="line.615"></a>
+<span class="sourceLineNo">616</span> final BucketEntry bucketEntryToUse = bucketEntry;<a name="line.616"></a>
+<span class="sourceLineNo">617</span><a name="line.617"></a>
+<span class="sourceLineNo">618</span> if (bucketEntryToUse == null) {<a name="line.618"></a>
+<span class="sourceLineNo">619</span> if (existedInRamCache) {<a name="line.619"></a>
+<span class="sourceLineNo">620</span> cacheStats.evicted(0, cacheKey.isPrimary());<a name="line.620"></a>
+<span class="sourceLineNo">621</span> }<a name="line.621"></a>
+<span class="sourceLineNo">622</span> return existedInRamCache;<a name="line.622"></a>
+<span class="sourceLineNo">623</span> } else {<a name="line.623"></a>
+<span class="sourceLineNo">624</span> return bucketEntryToUse.withWriteLock(offsetLock, () -> {<a name="line.624"></a>
+<span class="sourceLineNo">625</span> if (backingMap.remove(cacheKey, bucketEntryToUse)) {<a name="line.625"></a>
+<span class="sourceLineNo">626</span> blockEvicted(cacheKey, bucketEntryToUse, !existedInRamCache);<a name="line.626"></a>
+<span class="sourceLineNo">627</span> return true;<a name="line.627"></a>
+<span class="sourceLineNo">628</span> }<a name="line.628"></a>
+<span class="sourceLineNo">629</span> return false;<a name="line.629"></a>
+<span class="sourceLineNo">630</span> });<a name="line.630"></a>
+<span class="sourceLineNo">631</span> }<a name="line.631"></a>
+<span class="sourceLineNo">632</span> }<a name="line.632"></a>
+<span class="sourceLineNo">633</span><a name="line.633"></a>
+<span class="sourceLineNo">634</span> /**<a name="line.634"></a>
+<span class="sourceLineNo">635</span> * <pre><a name="line.635"></a>
+<span class="sourceLineNo">636</span> * Create the {@link Recycler} for {@link BucketEntry#refCnt},which would be used as<a name="line.636"></a>
+<span class="sourceLineNo">637</span> * {@link RefCnt#recycler} of {@link HFileBlock#buf} returned from {@link BucketCache#getBlock}.<a name="line.637"></a>
+<span class="sourceLineNo">638</span> * NOTE: for {@link BucketCache#getBlock},the {@link RefCnt#recycler} of {@link HFileBlock#buf}<a name="line.638"></a>
+<span class="sourceLineNo">639</span> * from {@link BucketCache#backingMap} and {@link BucketCache#ramCache} are different:<a name="line.639"></a>
+<span class="sourceLineNo">640</span> * 1.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#backingMap},<a name="line.640"></a>
+<span class="sourceLineNo">641</span> * it is the return value of current {@link BucketCache#createRecycler} method.<a name="line.641"></a>
+<span class="sourceLineNo">642</span> *<a name="line.642"></a>
+<span class="sourceLineNo">643</span> * 2.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#ramCache},<a name="line.643"></a>
+<span class="sourceLineNo">644</span> * it is {@link ByteBuffAllocator#putbackBuffer}.<a name="line.644"></a>
+<span class="sourceLineNo">645</span> * </pre><a name="line.645"></a>
+<span class="sourceLineNo">646</span> */<a name="line.646"></a>
+<span class="sourceLineNo">647</span> private Recycler createRecycler(final BucketEntry bucketEntry) {<a name="line.647"></a>
+<span class="sourceLineNo">648</span> return () -> {<a name="line.648"></a>
+<span class="sourceLineNo">649</span> freeBucketEntry(bucketEntry);<a name="line.649"></a>
+<span class="sourceLineNo">650</span> return;<a name="line.650"></a>
+<span class="sourceLineNo">651</span> };<a name="line.651"></a>
+<span class="sourceLineNo">652</span> }<a name="line.652"></a>
+<span class="sourceLineNo">653</span><a name="line.653"></a>
+<span class="sourceLineNo">654</span> /**<a name="line.654"></a>
+<span class="sourceLineNo">655</span> * NOTE: This method is only for test.<a name="line.655"></a>
+<span class="sourceLineNo">656</span> */<a name="line.656"></a>
+<span class="sourceLineNo">657</span> public boolean evictBlockIfNoRpcReferenced(BlockCacheKey blockCacheKey) {<a name="line.657"></a>
+<span class="sourceLineNo">658</span> BucketEntry bucketEntry = backingMap.get(blockCacheKey);<a name="line.658"></a>
+<span class="sourceLineNo">659</span> if (bucketEntry == null) {<a name="line.659"></a>
+<span class="sourceLineNo">660</span> return false;<a name="line.660"></a>
+<span class="sourceLineNo">661</span> }<a name="line.661"></a>
+<span class="sourceLineNo">662</span> return evictBucketEntryIfNoRpcReferenced(blockCacheKey, bucketEntry);<a name="line.662"></a>
+<span class="sourceLineNo">663</span> }<a name="line.663"></a>
+<span class="sourceLineNo">664</span><a name="line.664"></a>
+<span class="sourceLineNo">665</span> /**<a name="line.665"></a>
+<span class="sourceLineNo">666</span> * Evict {@link BlockCacheKey} and its corresponding {@link BucketEntry} only if<a name="line.666"></a>
+<span class="sourceLineNo">667</span> * {@link BucketEntry#isRpcRef} is false. <br/><a name="line.667"></a>
+<span class="sourceLineNo">668</span> * NOTE:When evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.668"></a>
+<span class="sourceLineNo">669</span> * {@link BucketEntry} could be removed.<a name="line.669"></a>
+<span class="sourceLineNo">670</span> * @param blockCacheKey {@link BlockCacheKey} to evict.<a name="line.670"></a>
+<span class="sourceLineNo">671</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.671"></a>
+<span class="sourceLineNo">672</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.672"></a>
+<span class="sourceLineNo">673</span> */<a name="line.673"></a>
+<span class="sourceLineNo">674</span> boolean evictBucketEntryIfNoRpcReferenced(BlockCacheKey blockCacheKey, BucketEntry bucketEntry) {<a name="line.674"></a>
+<span class="sourceLineNo">675</span> if (!bucketEntry.isRpcRef()) {<a name="line.675"></a>
+<span class="sourceLineNo">676</span> return doEvictBlock(blockCacheKey, bucketEntry);<a name="line.676"></a>
+<span class="sourceLineNo">677</span> }<a name="line.677"></a>
+<span class="sourceLineNo">678</span> return false;<a name="line.678"></a>
+<span class="sourceLineNo">679</span> }<a name="line.679"></a>
+<span class="sourceLineNo">680</span><a name="line.680"></a>
+<span class="sourceLineNo">681</span> protected boolean removeFromRamCache(BlockCacheKey cacheKey) {<a name="line.681"></a>
+<span class="sourceLineNo">682</span> return ramCache.remove(cacheKey, re -> {<a name="line.682"></a>
+<span class="sourceLineNo">683</span> if (re != null) {<a name="line.683"></a>
+<span class="sourceLineNo">684</span> this.blockNumber.decrement();<a name="line.684"></a>
+<span class="sourceLineNo">685</span> this.heapSize.add(-1 * re.getData().heapSize());<a name="line.685"></a>
+<span class="sourceLineNo">686</span> }<a name="line.686"></a>
+<span class="sourceLineNo">687</span> });<a name="line.687"></a>
+<span class="sourceLineNo">688</span> }<a name="line.688"></a>
+<span class="sourceLineNo">689</span><a name="line.689"></a>
+<span class="sourceLineNo">690</span> /*<a name="line.690"></a>
+<span class="sourceLineNo">691</span> * Statistics thread. Periodically output cache statistics to the log.<a name="line.691"></a>
+<span class="sourceLineNo">692</span> */<a name="line.692"></a>
+<span class="sourceLineNo">693</span> private static class StatisticsThread extends Thread {<a name="line.693"></a>
+<span class="sourceLineNo">694</span> private final BucketCache bucketCache;<a name="line.694"></a>
+<span class="sourceLineNo">695</span><a name="line.695"></a>
+<span class="sourceLineNo">696</span> public StatisticsThread(BucketCache bucketCache) {<a name="line.696"></a>
+<span class="sourceLineNo">697</span> super("BucketCacheStatsThread");<a name="line.697"></a>
+<span class="sourceLineNo">698</span> setDaemon(true);<a name="line.698"></a>
+<span class="sourceLineNo">699</span> this.bucketCache = bucketCache;<a name="line.699"></a>
+<span class="sourceLineNo">700</span> }<a name="line.700"></a>
+<span class="sourceLineNo">701</span><a name="line.701"></a>
+<span class="sourceLineNo">702</span> @Override<a name="line.702"></a>
+<span class="sourceLineNo">703</span> public void run() {<a name="line.703"></a>
+<span class="sourceLineNo">704</span> bucketCache.logStats();<a name="line.704"></a>
+<span class="sourceLineNo">705</span> }<a name="line.705"></a>
+<span class="sourceLineNo">706</span> }<a name="line.706"></a>
+<span class="sourceLineNo">707</span><a name="line.707"></a>
+<span class="sourceLineNo">708</span> public void logStats() {<a name="line.708"></a>
+<span class="sourceLineNo">709</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.709"></a>
+<span class="sourceLineNo">710</span> long usedSize = bucketAllocator.getUsedSize();<a name="line.710"></a>
+<span class="sourceLineNo">711</span> long freeSize = totalSize - usedSize;<a name="line.711"></a>
+<span class="sourceLineNo">712</span> long cacheSize = getRealCacheSize();<a name="line.712"></a>
+<span class="sourceLineNo">713</span> LOG.info("failedBlockAdditions=" + cacheStats.getFailedInserts() + ", " +<a name="line.713"></a>
+<span class="sourceLineNo">714</span> "totalSize=" + StringUtils.byteDesc(totalSize) + ", " +<a name="line.714"></a>
+<span class="sourceLineNo">715</span> "freeSize=" + StringUtils.byteDesc(freeSize) + ", " +<a name="line.715"></a>
+<span class="sourceLineNo">716</span> "usedSize=" + StringUtils.byteDesc(usedSize) +", " +<a name="line.716"></a>
+<span class="sourceLineNo">717</span> "cacheSize=" + StringUtils.byteDesc(cacheSize) +", " +<a name="line.717"></a>
+<span class="sourceLineNo">718</span> "accesses=" + cacheStats.getRequestCount() + ", " +<a name="line.718"></a>
+<span class="sourceLineNo">719</span> "hits=" + cacheStats.getHitCount() + ", " +<a name="line.719"></a>
+<span class="sourceLineNo">720</span> "IOhitsPerSecond=" + cacheStats.getIOHitsPerSecond() + ", " +<a name="line.720"></a>
+<span class="sourceLineNo">721</span> "IOTimePerHit=" + String.format("%.2f", cacheStats.getIOTimePerHit())+ ", " +<a name="line.721"></a>
+<span class="sourceLineNo">722</span> "hitRatio=" + (cacheStats.getHitCount() == 0 ? "0," :<a name="line.722"></a>
+<span class="sourceLineNo">723</span> (StringUtils.formatPercent(cacheStats.getHitRatio(), 2)+ ", ")) +<a name="line.723"></a>
+<span class="sourceLineNo">724</span> "cachingAccesses=" + cacheStats.getRequestCachingCount() + ", " +<a name="line.724"></a>
+<span class="sourceLineNo">725</span> "cachingHits=" + cacheStats.getHitCachingCount() + ", " +<a name="line.725"></a>
+<span class="sourceLineNo">726</span> "cachingHitsRatio=" +(cacheStats.getHitCachingCount() == 0 ? "0," :<a name="line.726"></a>
+<span class="sourceLineNo">727</span> (StringUtils.formatPercent(cacheStats.getHitCachingRatio(), 2)+ ", ")) +<a name="line.727"></a>
+<span class="sourceLineNo">728</span> "evictions=" + cacheStats.getEvictionCount() + ", " +<a name="line.728"></a>
+<span class="sourceLineNo">729</span> "evicted=" + cacheStats.getEvictedCount() + ", " +<a name="line.729"></a>
+<span class="sourceLineNo">730</span> "evictedPerRun=" + cacheStats.evictedPerEviction());<a name="line.730"></a>
+<span class="sourceLineNo">731</span> cacheStats.reset();<a name="line.731"></a>
+<span class="sourceLineNo">732</span> }<a name="line.732"></a>
+<span class="sourceLineNo">733</span><a name="line.733"></a>
+<span class="sourceLineNo">734</span> public long getRealCacheSize() {<a name="line.734"></a>
+<span class="sourceLineNo">735</span> return this.realCacheSize.sum();<a name="line.735"></a>
+<span class="sourceLineNo">736</span> }<a name="line.736"></a>
+<span class="sourceLineNo">737</span><a name="line.737"></a>
+<span class="sourceLineNo">738</span> public long acceptableSize() {<a name="line.738"></a>
+<span class="sourceLineNo">739</span> return (long) Math.floor(bucketAllocator.getTotalSize() * acceptableFactor);<a name="line.739"></a>
+<span class="sourceLineNo">740</span> }<a name="line.740"></a>
+<span class="sourceLineNo">741</span><a name="line.741"></a>
+<span class="sourceLineNo">742</span> long getPartitionSize(float partitionFactor) {<a name="line.742"></a>
+<span class="sourceLineNo">743</span> return (long) Math.floor(bucketAllocator.getTotalSize() * partitionFactor * minFactor);<a name="line.743"></a>
+<span class="sourceLineNo">744</span> }<a name="line.744"></a>
+<span class="sourceLineNo">745</span><a name="line.745"></a>
+<span class="sourceLineNo">746</span> /**<a name="line.746"></a>
+<span class="sourceLineNo">747</span> * Return the count of bucketSizeinfos still need free space<a name="line.747"></a>
+<span class="sourceLineNo">748</span> */<a name="line.748"></a>
+<span class="sourceLineNo">749</span> private int bucketSizesAboveThresholdCount(float minFactor) {<a name="line.749"></a>
+<span class="sourceLineNo">750</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.750"></a>
+<span class="sourceLineNo">751</span> int fullCount = 0;<a name="line.751"></a>
+<span class="sourceLineNo">752</span> for (int i = 0; i < stats.length; i++) {<a name="line.752"></a>
+<span class="sourceLineNo">753</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.753"></a>
+<span class="sourceLineNo">754</span> freeGoal = Math.max(freeGoal, 1);<a name="line.754"></a>
+<span class="sourceLineNo">755</span> if (stats[i].freeCount() < freeGoal) {<a name="line.755"></a>
+<span class="sourceLineNo">756</span> fullCount++;<a name="line.756"></a>
+<span class="sourceLineNo">757</span> }<a name="line.757"></a>
+<span class="sourceLineNo">758</span> }<a name="line.758"></a>
+<span class="sourceLineNo">759</span> return fullCount;<a name="line.759"></a>
+<span class="sourceLineNo">760</span> }<a name="line.760"></a>
+<span class="sourceLineNo">761</span><a name="line.761"></a>
+<span class="sourceLineNo">762</span> /**<a name="line.762"></a>
+<span class="sourceLineNo">763</span> * This method will find the buckets that are minimally occupied<a name="line.763"></a>
+<span class="sourceLineNo">764</span> * and are not reference counted and will free them completely<a name="line.764"></a>
+<span class="sourceLineNo">765</span> * without any constraint on the access times of the elements,<a name="line.765"></a>
+<span class="sourceLineNo">766</span> * and as a process will completely free at most the number of buckets<a name="line.766"></a>
+<span class="sourceLineNo">767</span> * passed, sometimes it might not due to changing refCounts<a name="line.767"></a>
+<span class="sourceLineNo">768</span> *<a name="line.768"></a>
+<span class="sourceLineNo">769</span> * @param completelyFreeBucketsNeeded number of buckets to free<a name="line.769"></a>
+<span class="sourceLineNo">770</span> **/<a name="line.770"></a>
+<span class="sourceLineNo">771</span> private void freeEntireBuckets(int completelyFreeBucketsNeeded) {<a name="line.771"></a>
+<span class="sourceLineNo">772</span> if (completelyFreeBucketsNeeded != 0) {<a name="line.772"></a>
+<span class="sourceLineNo">773</span> // First we will build a set where the offsets are reference counted, usually<a name="line.773"></a>
+<span class="sourceLineNo">774</span> // this set is small around O(Handler Count) unless something else is wrong<a name="line.774"></a>
+<span class="sourceLineNo">775</span> Set<Integer> inUseBuckets = new HashSet<>();<a name="line.775"></a>
+<span class="sourceLineNo">776</span> backingMap.forEach((k, be) -> {<a name="line.776"></a>
+<span class="sourceLineNo">777</span> if (be.isRpcRef()) {<a name="line.777"></a>
+<span class="sourceLineNo">778</span> inUseBuckets.add(bucketAllocator.getBucketIndex(be.offset()));<a name="line.778"></a>
+<span class="sourceLineNo">779</span> }<a name="line.779"></a>
+<span class="sourceLineNo">780</span> });<a name="line.780"></a>
+<span class="sourceLineNo">781</span> Set<Integer> candidateBuckets =<a name="line.781"></a>
+<span class="sourceLineNo">782</span> bucketAllocator.getLeastFilledBuckets(inUseBuckets, completelyFreeBucketsNeeded);<a name="line.782"></a>
+<span class="sourceLineNo">783</span> for (Map.Entry<BlockCacheKey, BucketEntry> entry : backingMap.entrySet()) {<a name="line.783"></a>
+<span class="sourceLineNo">784</span> if (candidateBuckets.contains(bucketAllocator.getBucketIndex(entry.getValue().offset()))) {<a name="line.784"></a>
+<span class="sourceLineNo">785</span> evictBucketEntryIfNoRpcReferenced(entry.getKey(), entry.getValue());<a name="line.785"></a>
+<span class="sourceLineNo">786</span> }<a name="line.786"></a>
+<span class="sourceLineNo">787</span> }<a name="line.787"></a>
+<span class="sourceLineNo">788</span> }<a name="line.788"></a>
+<span class="sourceLineNo">789</span> }<a name="line.789"></a>
+<span class="sourceLineNo">790</span><a name="line.790"></a>
+<span class="sourceLineNo">791</span> /**<a name="line.791"></a>
+<span class="sourceLineNo">792</span> * Free the space if the used size reaches acceptableSize() or one size block<a name="line.792"></a>
+<span class="sourceLineNo">793</span> * couldn't be allocated. When freeing the space, we use the LRU algorithm and<a name="line.793"></a>
+<span class="sourceLineNo">794</span> * ensure there must be some blocks evicted<a name="line.794"></a>
+<span class="sourceLineNo">795</span> * @param why Why we are being called<a name="line.795"></a>
+<span class="sourceLineNo">796</span> */<a name="line.796"></a>
+<span class="sourceLineNo">797</span> private void freeSpace(final String why) {<a name="line.797"></a>
+<span class="sourceLineNo">798</span> // Ensure only one freeSpace progress at a time<a name="line.798"></a>
+<span class="sourceLineNo">799</span> if (!freeSpaceLock.tryLock()) {<a name="line.799"></a>
+<span class="sourceLineNo">800</span> return;<a name="line.800"></a>
+<span class="sourceLineNo">801</span> }<a name="line.801"></a>
+<span class="sourceLineNo">802</span> try {<a name="line.802"></a>
+<span class="sourceLineNo">803</span> freeInProgress = true;<a name="line.803"></a>
+<span class="sourceLineNo">804</span> long bytesToFreeWithoutExtra = 0;<a name="line.804"></a>
+<span class="sourceLineNo">805</span> // Calculate free byte for each bucketSizeinfo<a name="line.805"></a>
+<span class="sourceLineNo">806</span> StringBuilder msgBuffer = LOG.isDebugEnabled()? new StringBuilder(): null;<a name="line.806"></a>
+<span class="sourceLineNo">807</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.807"></a>
+<span class="sourceLineNo">808</span> long[] bytesToFreeForBucket = new long[stats.length];<a name="line.808"></a>
+<span class="sourceLineNo">809</span> for (int i = 0; i < stats.length; i++) {<a name="line.809"></a>
+<span class="sourceLineNo">810</span> bytesToFreeForBucket[i] = 0;<a name="line.810"></a>
+<span class="sourceLineNo">811</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.811"></a>
+<span class="sourceLineNo">812</span> freeGoal = Math.max(freeGoal, 1);<a name="line.812"></a>
+<span class="sourceLineNo">813</span> if (stats[i].freeCount() < freeGoal) {<a name="line.813"></a>
+<span class="sourceLineNo">814</span> bytesToFreeForBucket[i] = stats[i].itemSize() * (freeGoal - stats[i].freeCount());<a name="line.814"></a>
+<span class="sourceLineNo">815</span> bytesToFreeWithoutExtra += bytesToFreeForBucket[i];<a name="line.815"></a>
+<span class="sourceLineNo">816</span> if (msgBuffer != null) {<a name="line.816"></a>
+<span class="sourceLineNo">817</span> msgBuffer.append("Free for bucketSize(" + stats[i].itemSize() + ")="<a name="line.817"></a>
+<span class="sourceLineNo">818</span> + StringUtils.byteDesc(bytesToFreeForBucket[i]) + ", ");<a name="line.818"></a>
+<span class="sourceLineNo">819</span> }<a name="line.819"></a>
+<span class="sourceLineNo">820</span> }<a name="line.820"></a>
+<span class="sourceLineNo">821</span> }<a name="line.821"></a>
+<span class="sourceLineNo">822</span> if (msgBuffer != null) {<a name="line.822"></a>
+<span class="sourceLineNo">823</span> msgBuffer.append("Free for total=" + StringUtils.byteDesc(bytesToFreeWithoutExtra) + ", ");<a name="line.823"></a>
+<span class="sourceLineNo">824</span> }<a name="line.824"></a>
+<span class="sourceLineNo">825</span><a name="line.825"></a>
+<span class="sourceLineNo">826</span> if (bytesToFreeWithoutExtra <= 0) {<a name="line.826"></a>
+<span class="sourceLineNo">827</span> return;<a name="line.827"></a>
+<span class="sourceLineNo">828</span> }<a name="line.828"></a>
+<span class="sourceLineNo">829</span> long currentSize = bucketAllocator.getUsedSize();<a name="line.829"></a>
+<span class="sourceLineNo">830</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.830"></a>
+<span class="sourceLineNo">831</span> if (LOG.isDebugEnabled() && msgBuffer != null) {<a name="line.831"></a>
+<span class="sourceLineNo">832</span> LOG.debug("Free started because \"" + why + "\"; " + msgBuffer.toString() +<a name="line.832"></a>
+<span class="sourceLineNo">833</span> " of current used=" + StringUtils.byteDesc(currentSize) + ", actual cacheSize=" +<a name="line.833"></a>
+<span class="sourceLineNo">834</span> StringUtils.byteDesc(realCacheSize.sum()) + ", total=" + StringUtils.byteDesc(totalSize));<a name="line.834"></a>
+<span class="sourceLineNo">835</span> }<a name="line.835"></a>
+<span class="sourceLineNo">836</span><a name="line.836"></a>
+<span class="sourceLineNo">837</span> long bytesToFreeWithExtra = (long) Math.floor(bytesToFreeWithoutExtra<a name="line.837"></a>
+<span class="sourceLineNo">838</span> * (1 + extraFreeFactor));<a name="line.838"></a>
+<span class="sourceLineNo">839</span><a name="line.839"></a>
+<span class="sourceLineNo">840</span> // Instantiate priority buckets<a name="line.840"></a>
+<span class="sourceLineNo">841</span> BucketEntryGroup bucketSingle = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.841"></a>
+<span class="sourceLineNo">842</span> blockSize, getPartitionSize(singleFactor));<a name="line.842"></a>
+<span class="sourceLineNo">843</span> BucketEntryGroup bucketMulti = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.843"></a>
+<span class="sourceLineNo">844</span> blockSize, getPartitionSize(multiFactor));<a name="line.844"></a>
+<span class="sourceLineNo">845</span> BucketEntryGroup bucketMemory = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.845"></a>
+<span class="sourceLineNo">846</span> blockSize, getPartitionSize(memoryFactor));<a name="line.846"></a>
+<span class="sourceLineNo">847</span><a name="line.847"></a>
+<span class="sourceLineNo">848</span> // Scan entire map putting bucket entry into appropriate bucket entry<a name="line.848"></a>
+<span class="sourceLineNo">849</span> // group<a name="line.849"></a>
+<span class="sourceLineNo">850</span> for (Map.Entry<BlockCacheKey, BucketEntry> bucketEntryWithKey : backingMap.entrySet()) {<a name="line.850"></a>
+<span class="sourceLineNo">851</span> switch (bucketEntryWithKey.getValue().getPriority()) {<a name="line.851"></a>
+<span class="sourceLineNo">852</span> case SINGLE: {<a name="line.852"></a>
+<span class="sourceLineNo">853</span> bucketSingle.add(bucketEntryWithKey);<a name="line.853"></a>
+<span class="sourceLineNo">854</span> break;<a name="line.854"></a>
+<span class="sourceLineNo">855</span> }<a name="line.855"></a>
+<span class="sourceLineNo">856</span> case MULTI: {<a name="line.856"></a>
+<span class="sourceLineNo">857</span> bucketMulti.add(bucketEntryWithKey);<a name="line.857"></a>
+<span class="sourceLineNo">858</span> break;<a name="line.858"></a>
+<span class="sourceLineNo">859</span> }<a name="line.859"></a>
+<span class="sourceLineNo">860</span> case MEMORY: {<a name="line.860"></a>
+<span class="sourceLineNo">861</span> bucketMemory.add(bucketEntryWithKey);<a name="line.861"></a>
+<span class="sourceLineNo">862</span> break;<a name="line.862"></a>
+<span class="sourceLineNo">863</span> }<a name="line.863"></a>
+<span class="sourceLineNo">864</span> }<a name="line.864"></a>
+<span class="sourceLineNo">865</span> }<a name="line.865"></a>
+<span class="sourceLineNo">866</span><a name="line.866"></a>
+<span class="sourceLineNo">867</span> PriorityQueue<BucketEntryGroup> bucketQueue = new PriorityQueue<>(3,<a name="line.867"></a>
+<span class="sourceLineNo">868</span> Comparator.comparingLong(BucketEntryGroup::overflow));<a name="line.868"></a>
+<span class="sourceLineNo">869</span><a name="line.869"></a>
+<span class="sourceLineNo">870</span> bucketQueue.add(bucketSingle);<a name="line.870"></a>
+<span class="sourceLineNo">871</span> bucketQueue.add(bucketMulti);<a name="line.871"></a>
+<span class="sourceLineNo">872</span> bucketQueue.add(bucketMemory);<a name="line.872"></a>
+<span class="sourceLineNo">873</span><a name="line.873"></a>
+<span class="sourceLineNo">874</span> int remainingBuckets = bucketQueue.size();<a name="line.874"></a>
+<span class="sourceLineNo">875</span> long bytesFreed = 0;<a name="line.875"></a>
+<span class="sourceLineNo">876</span><a name="line.876"></a>
+<span class="sourceLineNo">877</span> BucketEntryGroup bucketGroup;<a name="line.877"></a>
+<span class="sourceLineNo">878</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.878"></a>
+<span class="sourceLineNo">879</span> long overflow = bucketGroup.overflow();<a name="line.879"></a>
+<span class="sourceLineNo">880</span> if (overflow > 0) {<a name="line.880"></a>
+<span class="sourceLineNo">881</span> long bucketBytesToFree = Math.min(overflow,<a name="line.881"></a>
+<span class="sourceLineNo">882</span> (bytesToFreeWithoutExtra - bytesFreed) / remainingBuckets);<a name="line.882"></a>
+<span class="sourceLineNo">883</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.883"></a>
+<span class="sourceLineNo">884</span> }<a name="line.884"></a>
+<span class="sourceLineNo">885</span> remainingBuckets--;<a name="line.885"></a>
+<span class="sourceLineNo">886</span> }<a name="line.886"></a>
+<span class="sourceLineNo">887</span><a name="line.887"></a>
+<span class="sourceLineNo">888</span> // Check and free if there are buckets that still need freeing of space<a name="line.888"></a>
+<span class="sourceLineNo">889</span> if (bucketSizesAboveThresholdCount(minFactor) > 0) {<a name="line.889"></a>
+<span class="sourceLineNo">890</span> bucketQueue.clear();<a name="line.890"></a>
+<span class="sourceLineNo">891</span> remainingBuckets = 3;<a name="line.891"></a>
+<span class="sourceLineNo">892</span><a name="line.892"></a>
+<span class="sourceLineNo">893</span> bucketQueue.add(bucketSingle);<a name="line.893"></a>
+<span class="sourceLineNo">894</span> bucketQueue.add(bucketMulti);<a name="line.894"></a>
+<span class="sourceLineNo">895</span> bucketQueue.add(bucketMemory);<a name="line.895"></a>
+<span class="sourceLineNo">896</span><a name="line.896"></a>
+<span class="sourceLineNo">897</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.897"></a>
+<span class="sourceLineNo">898</span> long bucketBytesToFree = (bytesToFreeWithExtra - bytesFreed) / remainingBuckets;<a name="line.898"></a>
+<span class="sourceLineNo">899</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.899"></a>
+<span class="sourceLineNo">900</span> remainingBuckets--;<a name="line.900"></a>
+<span class="sourceLineNo">901</span> }<a name="line.901"></a>
+<span class="sourceLineNo">902</span> }<a name="line.902"></a>
+<span class="sourceLineNo">903</span><a name="line.903"></a>
+<span class="sourceLineNo">904</span> // Even after the above free we might still need freeing because of the<a name="line.904"></a>
+<span class="sourceLineNo">905</span> // De-fragmentation of the buckets (also called Slab Calcification problem), i.e<a name="line.905"></a>
+<span class="sourceLineNo">906</span> // there might be some buckets where the occupancy is very sparse and thus are not<a name="line.906"></a>
+<span class="sourceLineNo">907</span> // yielding the free for the other bucket sizes, the fix for this to evict some<a name="line.907"></a>
+<span class="sourceLineNo">908</span> // of the buckets, we do this by evicting the buckets that are least fulled<a name="line.908"></a>
+<span class="sourceLineNo">909</span> freeEntireBuckets(DEFAULT_FREE_ENTIRE_BLOCK_FACTOR *<a name="line.909"></a>
+<span class="sourceLineNo">910</span> bucketSizesAboveThresholdCount(1.0f));<a name="line.910"></a>
+<span class="sourceLineNo">911</span><a name="line.911"></a>
+<span class="sourceLineNo">912</span> if (LOG.isDebugEnabled()) {<a name="line.912"></a>
+<span class="sourceLineNo">913</span> long single = bucketSingle.totalSize();<a name="line.913"></a>
+<span class="sourceLineNo">914</span> long multi = bucketMulti.totalSize();<a name="line.914"></a>
+<span class="sourceLineNo">915</span> long memory = bucketMemory.totalSize();<a name="line.915"></a>
+<span class="sourceLineNo">916</span> if (LOG.isDebugEnabled()) {<a name="line.916"></a>
+<span class="sourceLineNo">917</span> LOG.debug("Bucket cache free space completed; " + "freed="<a name="line.917"></a>
+<span class="sourceLineNo">918</span> + StringUtils.byteDesc(bytesFreed) + ", " + "total="<a name="line.918"></a>
+<span class="sourceLineNo">919</span> + StringUtils.byteDesc(totalSize) + ", " + "single="<a name="line.919"></a>
+<span class="sourceLineNo">920</span> + StringUtils.byteDesc(single) + ", " + "multi="<a name="line.920"></a>
+<span class="sourceLineNo">921</span> + StringUtils.byteDesc(multi) + ", " + "memory="<a name="line.921"></a>
+<span class="sourceLineNo">922</span> + StringUtils.byteDesc(memory));<a name="line.922"></a>
+<span class="sourceLineNo">923</span> }<a name="line.923"></a>
+<span class="sourceLineNo">924</span> }<a name="line.924"></a>
+<span class="sourceLineNo">925</span><a name="line.925"></a>
+<span class="sourceLineNo">926</span> } catch (Throwable t) {<a name="line.926"></a>
+<span class="sourceLineNo">927</span> LOG.warn("Failed freeing space", t);<a name="line.927"></a>
+<span class="sourceLineNo">928</span> } finally {<a name="line.928"></a>
+<span class="sourceLineNo">929</span> cacheStats.evict();<a name="line.929"></a>
+<span class="sourceLineNo">930</span> freeInProgress = false;<a name="line.930"></a>
+<span class="sourceLineNo">931</span> freeSpaceLock.unlock();<a name="line.931"></a>
+<span class="sourceLineNo">932</span> }<a name="line.932"></a>
+<span class="sourceLineNo">933</span> }<a name="line.933"></a>
+<span class="sourceLineNo">934</span><a name="line.934"></a>
+<span class="sourceLineNo">935</span> // This handles flushing the RAM cache to IOEngine.<a name="line.935"></a>
+<span class="sourceLineNo">936</span> class WriterThread extends Thread {<a name="line.936"></a>
+<span class="sourceLineNo">937</span> private final BlockingQueue<RAMQueueEntry> inputQueue;<a name="line.937"></a>
+<span class="sourceLineNo">938</span> private volatile boolean writerEnabled = true;<a name="line.938"></a>
+<span class="sourceLineNo">939</span><a name="line.939"></a>
+<span class="sourceLineNo">940</span> WriterThread(BlockingQueue<RAMQueueEntry> queue) {<a name="line.940"></a>
+<span class="sourceLineNo">941</span> super("BucketCacheWriterThread");<a name="line.941"></a>
+<span class="sourceLineNo">942</span> this.inputQueue = queue;<a name="line.942"></a>
+<span class="sourceLineNo">943</span> }<a name="line.943"></a>
+<span class="sourceLineNo">944</span><a name="line.944"></a>
+<span class="sourceLineNo">945</span> // Used for test<a name="line.945"></a>
+<span class="sourceLineNo">946</span> void disableWriter() {<a name="line.946"></a>
+<span class="sourceLineNo">947</span> this.writerEnabled = false;<a name="line.947"></a>
+<span class="sourceLineNo">948</span> }<a name="line.948"></a>
+<span class="sourceLineNo">949</span><a name="line.949"></a>
+<span class="sourceLineNo">950</span> @Override<a name="line.950"></a>
+<span class="sourceLineNo">951</span> public void run() {<a name="line.951"></a>
+<span class="sourceLineNo">952</span> List<RAMQueueEntry> entries = new ArrayList<>();<a name="line.952"></a>
+<span class="sourceLineNo">953</span> try {<a name="line.953"></a>
+<span class="sourceLineNo">954</span> while (cacheEnabled && writerEnabled) {<a name="line.954"></a>
+<span class="sourceLineNo">955</span> try {<a name="line.955"></a>
+<span class="sourceLineNo">956</span> try {<a name="line.956"></a>
+<span class="sourceLineNo">957</span> // Blocks<a name="line.957"></a>
+<span class="sourceLineNo">958</span> entries = getRAMQueueEntries(inputQueue, entries);<a name="line.958"></a>
+<span class="sourceLineNo">959</span> } catch (InterruptedException ie) {<a name="line.959"></a>
+<span class="sourceLineNo">960</span> if (!cacheEnabled || !writerEnabled) {<a name="line.960"></a>
+<span class="sourceLineNo">961</span> break;<a name="line.961"></a>
+<span class="sourceLineNo">962</span> }<a name="line.962"></a>
+<span class="sourceLineNo">963</span> }<a name="line.963"></a>
+<span class="sourceLineNo">964</span> doDrain(entries);<a name="line.964"></a>
+<span class="sourceLineNo">965</span> } catch (Exception ioe) {<a name="line.965"></a>
+<span class="sourceLineNo">966</span> LOG.error("WriterThread encountered error", ioe);<a name="line.966"></a>
+<span class="sourceLineNo">967</span> }<a name="line.967"></a>
+<span class="sourceLineNo">968</span> }<a name="line.968"></a>
+<span class="sourceLineNo">969</span> } catch (Throwable t) {<a name="line.969"></a>
+<span class="sourceLineNo">970</span> LOG.warn("Failed doing drain", t);<a name="line.970"></a>
+<span class="sourceLineNo">971</span> }<a name="line.971"></a>
+<span class="sourceLineNo">972</span> LOG.info(this.getName() + " exiting, cacheEnabled=" + cacheEnabled);<a name="line.972"></a>
+<span class="sourceLineNo">973</span> }<a name="line.973"></a>
+<span class="sourceLineNo">974</span> }<a name="line.974"></a>
+<span class="sourceLineNo">975</span><a name="line.975"></a>
+<span class="sourceLineNo">976</span> /**<a name="line.976"></a>
+<span class="sourceLineNo">977</span> * Put the new bucket entry into backingMap. Notice that we are allowed to replace the existing<a name="line.977"></a>
+<span class="sourceLineNo">978</span> * cache with a new block for the same cache key. there's a corner case: one thread cache a block<a name="line.978"></a>
+<span class="sourceLineNo">979</span> * in ramCache, copy to io-engine and add a bucket entry to backingMap. Caching another new block<a name="line.979"></a>
+<span class="sourceLineNo">980</span> * with the same cache key do the same thing for the same cache key, so if not evict the previous<a name="line.980"></a>
+<span class="sourceLineNo">981</span> * bucket entry, then memory leak happen because the previous bucketEntry is gone but the<a name="line.981"></a>
+<span class="sourceLineNo">982</span> * bucketAllocator do not free its memory.<a name="line.982"></a>
+<span class="sourceLineNo">983</span> * @see BlockCacheUtil#shouldReplaceExistingCacheBlock(BlockCache blockCache,BlockCacheKey<a name="line.983"></a>
+<span class="sourceLineNo">984</span> * cacheKey, Cacheable newBlock)<a name="line.984"></a>
+<span class="sourceLineNo">985</span> * @param key Block cache key<a name="line.985"></a>
+<span class="sourceLineNo">986</span> * @param bucketEntry Bucket entry to put into backingMap.<a name="line.986"></a>
+<span class="sourceLineNo">987</span> */<a name="line.987"></a>
+<span class="sourceLineNo">988</span> protected void putIntoBackingMap(BlockCacheKey key, BucketEntry bucketEntry) {<a name="line.988"></a>
+<span class="sourceLineNo">989</span> BucketEntry previousEntry = backingMap.put(key, bucketEntry);<a name="line.989"></a>
+<span class="sourceLineNo">990</span> if (previousEntry != null && previousEntry != bucketEntry) {<a name="line.990"></a>
+<span class="sourceLineNo">991</span> previousEntry.withWriteLock(offsetLock, () -> {<a name="line.991"></a>
+<span class="sourceLineNo">992</span> blockEvicted(key, previousEntry, false);<a name="line.992"></a>
+<span class="sourceLineNo">993</span> return null;<a name="line.993"></a>
+<span class="sourceLineNo">994</span> });<a name="line.994"></a>
+<span class="sourceLineNo">995</span> }<a name="line.995"></a>
+<span class="sourceLineNo">996</span> }<a name="line.996"></a>
+<span class="sourceLineNo">997</span><a name="line.997"></a>
+<span class="sourceLineNo">998</span> /**<a name="line.998"></a>
+<span class="sourceLineNo">999</span> * Flush the entries in ramCache to IOEngine and add bucket entry to backingMap. Process all that<a name="line.999"></a>
+<span class="sourceLineNo">1000</span> * are passed in even if failure being sure to remove from ramCache else we'll never undo the<a name="line.1000"></a>
+<span class="sourceLineNo">1001</span> * references and we'll OOME.<a name="line.1001"></a>
+<span class="sourceLineNo">1002</span> * @param entries Presumes list passed in here will be processed by this invocation only. No<a name="line.1002"></a>
+<span class="sourceLineNo">1003</span> * interference expected.<a name="line.1003"></a>
+<span class="sourceLineNo">1004</span> */<a name="line.1004"></a>
+<span class="sourceLineNo">1005</span> void doDrain(final List<RAMQueueEntry> entries) throws InterruptedException {<a name="line.1005"></a>
+<span class="sourceLineNo">1006</span> if (entries.isEmpty()) {<a name="line.1006"></a>
+<span class="sourceLineNo">1007</span> return;<a name="line.1007"></a>
+<span class="sourceLineNo">1008</span> }<a name="line.1008"></a>
+<span class="sourceLineNo">1009</span> // This method is a little hard to follow. We run through the passed in entries and for each<a name="line.1009"></a>
+<span class="sourceLineNo">1010</span> // successful add, we add a non-null BucketEntry to the below bucketEntries. Later we must<a name="line.1010"></a>
+<span class="sourceLineNo">1011</span> // do cleanup making sure we've cleared ramCache of all entries regardless of whether we<a name="line.1011"></a>
+<span class="sourceLineNo">1012</span> // successfully added the item to the bucketcache; if we don't do the cleanup, we'll OOME by<a name="line.1012"></a>
+<span class="sourceLineNo">1013</span> // filling ramCache. We do the clean up by again running through the passed in entries<a name="line.1013"></a>
+<span class="sourceLineNo">1014</span> // doing extra work when we find a non-null bucketEntries corresponding entry.<a name="line.1014"></a>
+<span class="sourceLineNo">1015</span> final int size = entries.size();<a name="line.1015"></a>
+<span class="sourceLineNo">1016</span> BucketEntry[] bucketEntries = new BucketEntry[size];<a name="line.1016"></a>
+<span class="sourceLineNo">1017</span> // Index updated inside loop if success or if we can't succeed. We retry if cache is full<a name="line.1017"></a>
+<span class="sourceLineNo">1018</span> // when we go to add an entry by going around the loop again without upping the index.<a name="line.1018"></a>
+<span class="sourceLineNo">1019</span> int index = 0;<a name="line.1019"></a>
+<span class="sourceLineNo">1020</span> while (cacheEnabled && index < size) {<a name="line.1020"></a>
+<span class="sourceLineNo">1021</span> RAMQueueEntry re = null;<a name="line.1021"></a>
+<span class="sourceLineNo">1022</span> try {<a name="line.1022"></a>
+<span class="sourceLineNo">1023</span> re = entries.get(index);<a name="line.1023"></a>
+<span class="sourceLineNo">1024</span> if (re == null) {<a name="line.1024"></a>
+<span class="sourceLineNo">1025</span> LOG.warn("Couldn't get entry or changed on us; who else is messing with it?");<a name="line.1025"></a>
+<span class="sourceLineNo">1026</span> index++;<a name="line.1026"></a>
+<span class="sourceLineNo">1027</span> continue;<a name="line.1027"></a>
+<span class="sourceLineNo">1028</span> }<a name="line.1028"></a>
+<span class="sourceLineNo">1029</span> BlockCacheKey cacheKey = re.getKey();<a name="line.1029"></a>
+<span class="sourceLineNo">1030</span> if (ramCache.containsKey(cacheKey)) {<a name="line.1030"></a>
+<span class="sourceLineNo">1031</span> blocksByHFile.add(cacheKey);<a name="line.1031"></a>
+<span class="sourceLineNo">1032</span> }<a name="line.1032"></a>
+<span class="sourceLineNo">1033</span><a name="line.1033"></a>
+<span class="sourceLineNo">1034</span> BucketEntry bucketEntry = re.writeToCache(ioEngine, bucketAllocator, realCacheSize,<a name="line.1034"></a>
+<span class="sourceLineNo">1035</span> this::createRecycler);<a name="line.1035"></a>
+<span class="sourceLineNo">1036</span> // Successfully added. Up index and add bucketEntry. Clear io exceptions.<a name="line.1036"></a>
+<span class="sourceLineNo">1037</span> bucketEntries[index] = bucketEntry;<a name="line.1037"></a>
+<span class="sourceLineNo">1038</span> if (ioErrorStartTime > 0) {<a name="line.1038"></a>
+<span class="sourceLineNo">1039</span> ioErrorStartTime = -1;<a name="line.1039"></a>
+<span class="sourceLineNo">1040</span> }<a name="line.1040"></a>
+<span class="sourceLineNo">1041</span> index++;<a name="line.1041"></a>
+<span class="sourceLineNo">1042</span> } catch (BucketAllocatorException fle) {<a name="line.1042"></a>
+<span class="sourceLineNo">1043</span> LOG.warn("Failed allocation for " + (re == null ? "" : re.getKey()) + "; " + fle);<a name="line.1043"></a>
+<span class="sourceLineNo">1044</span> // Presume can't add. Too big? Move index on. Entry will be cleared from ramCache below.<a name="line.1044"></a>
+<span class="sourceLineNo">1045</span> bucketEntries[index] = null;<a name="line.1045"></a>
+<span class="sourceLineNo">1046</span> index++;<a name="line.1046"></a>
+<span class="sourceLineNo">1047</span> } catch (CacheFullException cfe) {<a name="line.1047"></a>
+<span class="sourceLineNo">1048</span> // Cache full when we tried to add. Try freeing space and then retrying (don't up index)<a name="line.1048"></a>
+<span class="sourceLineNo">1049</span> if (!freeInProgress) {<a name="line.1049"></a>
+<span class="sourceLineNo">1050</span> freeSpace("Full!");<a name="line.1050"></a>
+<span class="sourceLineNo">1051</span> } else {<a name="line.1051"></a>
+<span class="sourceLineNo">1052</span> Thread.sleep(50);<a name="line.1052"></a>
+<span class="sourceLineNo">1053</span> }<a name="line.1053"></a>
+<span class="sourceLineNo">1054</span> } catch (IOException ioex) {<a name="line.1054"></a>
+<span class="sourceLineNo">1055</span> // Hopefully transient. Retry. checkIOErrorIsTolerated disables cache if problem.<a name="line.1055"></a>
+<span class="sourceLineNo">1056</span> LOG.error("Failed writing to bucket cache", ioex);<a name="line.1056"></a>
+<span class="sourceLineNo">1057</span> checkIOErrorIsTolerated();<a name="line.1057"></a>
+<span class="sourceLineNo">1058</span> }<a name="line.1058"></a>
+<span class="sourceLineNo">1059</span> }<a name="line.1059"></a>
+<span class="sourceLineNo">1060</span><a name="line.1060"></a>
+<span class="sourceLineNo">1061</span> // Make sure data pages are written on media before we update maps.<a name="line.1061"></a>
+<span class="sourceLineNo">1062</span> try {<a name="line.1062"></a>
+<span class="sourceLineNo">1063</span> ioEngine.sync();<a name="line.1063"></a>
+<span class="sourceLineNo">1064</span> } catch (IOException ioex) {<a name="line.1064"></a>
+<span class="sourceLineNo">1065</span> LOG.error("Failed syncing IO engine", ioex);<a name="line.1065"></a>
+<span class="sourceLineNo">1066</span> checkIOErrorIsTolerated();<a name="line.1066"></a>
+<span class="sourceLineNo">1067</span> // Since we failed sync, free the blocks in bucket allocator<a name="line.1067"></a>
+<span class="sourceLineNo">1068</span> for (int i = 0; i < entries.size(); ++i) {<a name="line.1068"></a>
+<span class="sourceLineNo">1069</span> if (bucketEntries[i] != null) {<a name="line.1069"></a>
+<span class="sourceLineNo">1070</span> bucketAllocator.freeBlock(bucketEntries[i].offset());<a name="line.1070"></a>
+<span class="sourceLineNo">1071</span> bucketEntries[i] = null;<a name="line.1071"></a>
+<span class="sourceLineNo">1072</span> }<a name="line.1072"></a>
+<span class="sourceLineNo">1073</span> }<a name="line.1073"></a>
+<span class="sourceLineNo">1074</span> }<a name="line.1074"></a>
+<span class="sourceLineNo">1075</span><a name="line.1075"></a>
+<span class="sourceLineNo">1076</span> // Now add to backingMap if successfully added to bucket cache. Remove from ramCache if<a name="line.1076"></a>
+<span class="sourceLineNo">1077</span> // success or error.<a name="line.1077"></a>
+<span class="sourceLineNo">1078</span> for (int i = 0; i < size; ++i) {<a name="line.1078"></a>
+<span class="sourceLineNo">1079</span> BlockCacheKey key = entries.get(i).getKey();<a name="line.1079"></a>
+<span class="sourceLineNo">1080</span> // Only add if non-null entry.<a name="line.1080"></a>
+<span class="sourceLineNo">1081</span> if (bucketEntries[i] != null) {<a name="line.1081"></a>
+<span class="sourceLineNo">1082</span> putIntoBackingMap(key, bucketEntries[i]);<a name="line.1082"></a>
+<span class="sourceLineNo">1083</span> }<a name="line.1083"></a>
+<span class="sourceLineNo">1084</span> // Always remove from ramCache even if we failed adding it to the block cache above.<a name="line.1084"></a>
+<span class="sourceLineNo">1085</span> boolean existed = ramCache.remove(key, re -> {<a name="line.1085"></a>
+<span class="sourceLineNo">1086</span> if (re != null) {<a name="line.1086"></a>
+<span class="sourceLineNo">1087</span> heapSize.add(-1 * re.getData().heapSize());<a name="line.1087"></a>
+<span class="sourceLineNo">1088</span> }<a name="line.1088"></a>
+<span class="sourceLineNo">1089</span> });<a name="line.1089"></a>
+<span class="sourceLineNo">1090</span> if (!existed && bucketEntries[i] != null) {<a name="line.1090"></a>
+<span class="sourceLineNo">1091</span> // Block should have already been evicted. Remove it and free space.<a name="line.1091"></a>
+<span class="sourceLineNo">1092</span> final BucketEntry bucketEntry = bucketEntries[i];<a name="line.1092"></a>
+<span class="sourceLineNo">1093</span> bucketEntry.withWriteLock(offsetLock, () -> {<a name="line.1093"></a>
+<span class="sourceLineNo">1094</span> if (backingMap.remove(key, bucketEntry)) {<a name="line.1094"></a>
+<span class="sourceLineNo">1095</span> blockEvicted(key, bucketEntry, false);<a name="line.1095"></a>
+<span class="sourceLineNo">1096</span> }<a name="line.1096"></a>
+<span class="sourceLineNo">1097</span> return null;<a name="line.1097"></a>
+<span class="sourceLineNo">1098</span> });<a name="line.1098"></a>
+<span class="sourceLineNo">1099</span> }<a name="line.1099"></a>
+<span class="sourceLineNo">1100</span> }<a name="line.1100"></a>
+<span class="sourceLineNo">1101</span><a name="line.1101"></a>
+<span class="sourceLineNo">1102</span> long used = bucketAllocator.getUsedSize();<a name="line.1102"></a>
+<span class="sourceLineNo">1103</span> if (used > acceptableSize()) {<a name="line.1103"></a>
+<span class="sourceLineNo">1104</span> freeSpace("Used=" + used + " > acceptable=" + acceptableSize());<a name="line.1104"></a>
+<span class="sourceLineNo">1105</span> }<a name="line.1105"></a>
+<span class="sourceLineNo">1106</span> return;<a name="line.1106"></a>
+<span class="sourceLineNo">1107</span> }<a name="line.1107"></a>
+<span class="sourceLineNo">1108</span><a name="line.1108"></a>
+<span class="sourceLineNo">1109</span> /**<a name="line.1109"></a>
+<span class="sourceLineNo">1110</span> * Blocks until elements available in {@code q} then tries to grab as many as possible before<a name="line.1110"></a>
+<span class="sourceLineNo">1111</span> * returning.<a name="line.1111"></a>
+<span class="sourceLineNo">1112</span> * @param receptacle Where to stash the elements taken from queue. We clear before we use it just<a name="line.1112"></a>
+<span class="sourceLineNo">1113</span> * in case.<a name="line.1113"></a>
+<span class="sourceLineNo">1114</span> * @param q The queue to take from.<a name="line.1114"></a>
+<span class="sourceLineNo">1115</span> * @return {@code receptacle} laden with elements taken from the queue or empty if none found.<a name="line.1115"></a>
+<span class="sourceLineNo">1116</span> */<a name="line.1116"></a>
+<span class="sourceLineNo">1117</span> static List<RAMQueueEntry> getRAMQueueEntries(BlockingQueue<RAMQueueEntry> q,<a name="line.1117"></a>
+<span class="sourceLineNo">1118</span> List<RAMQueueEntry> receptacle) throws InterruptedException {<a name="line.1118"></a>
+<span class="sourceLineNo">1119</span> // Clear sets all entries to null and sets size to 0. We retain allocations. Presume it<a name="line.1119"></a>
+<span class="sourceLineNo">1120</span> // ok even if list grew to accommodate thousands.<a name="line.1120"></a>
+<span class="sourceLineNo">1121</span> receptacle.clear();<a name="line.1121"></a>
+<span class="sourceLineNo">1122</span> receptacle.add(q.take());<a name="line.1122"></a>
+<span class="sourceLineNo">1123</span> q.drainTo(receptacle);<a name="line.1123"></a>
+<span class="sourceLineNo">1124</span> return receptacle;<a name="line.1124"></a>
+<span class="sourceLineNo">1125</span> }<a name="line.1125"></a>
+<span class="sourceLineNo">1126</span><a name="line.1126"></a>
+<span class="sourceLineNo">1127</span> /**<a name="line.1127"></a>
+<span class="sourceLineNo">1128</span> * @see #retrieveFromFile(int[])<a name="line.1128"></a>
+<span class="sourceLineNo">1129</span> */<a name="line.1129"></a>
+<span class="sourceLineNo">1130</span> @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="OBL_UNSATISFIED_OBLIGATION",<a name="line.1130"></a>
+<span class="sourceLineNo">1131</span> justification = "false positive, try-with-resources ensures close is called.")<a name="line.1131"></a>
+<span class="sourceLineNo">1132</span> private void persistToFile() throws IOException {<a name="line.1132"></a>
+<span class="sourceLineNo">1133</span> assert !cacheEnabled;<a name="line.1133"></a>
+<span class="sourceLineNo">1134</span> if (!ioEngine.isPersistent()) {<a name="line.1134"></a>
+<span class="sourceLineNo">1135</span> throw new IOException("Attempt to persist non-persistent cache mappings!");<a name="line.1135"></a>
<span class="sourceLineNo">1136</span> }<a name="line.1136"></a>
-<span class="sourceLineNo">1137</span> }<a name="line.1137"></a>
-<span class="sourceLineNo">1138</span><a name="line.1138"></a>
-<span class="sourceLineNo">1139</span> /**<a name="line.1139"></a>
-<span class="sourceLineNo">1140</span> * @see #persistToFile()<a name="line.1140"></a>
-<span class="sourceLineNo">1141</span> */<a name="line.1141"></a>
-<span class="sourceLineNo">1142</span> private void retrieveFromFile(int[] bucketSizes) throws IOException {<a name="line.1142"></a>
-<span class="sourceLineNo">1143</span> File persistenceFile = new File(persistencePath);<a name="line.1143"></a>
-<span class="sourceLineNo">1144</span> if (!persistenceFile.exists()) {<a name="line.1144"></a>
-<span class="sourceLineNo">1145</span> return;<a name="line.1145"></a>
-<span class="sourceLineNo">1146</span> }<a name="line.1146"></a>
-<span class="sourceLineNo">1147</span> assert !cacheEnabled;<a name="line.1147"></a>
-<span class="sourceLineNo">1148</span><a name="line.1148"></a>
-<span class="sourceLineNo">1149</span> try (FileInputStream in = deleteFileOnClose(persistenceFile)) {<a name="line.1149"></a>
-<span class="sourceLineNo">1150</span> int pblen = ProtobufMagic.lengthOfPBMagic();<a name="line.1150"></a>
-<span class="sourceLineNo">1151</span> byte[] pbuf = new byte[pblen];<a name="line.1151"></a>
-<span class="sourceLineNo">1152</span> int read = in.read(pbuf);<a name="line.1152"></a>
-<span class="sourceLineNo">1153</span> if (read != pblen) {<a name="line.1153"></a>
-<span class="sourceLineNo">1154</span> throw new IOException("Incorrect number of bytes read while checking for protobuf magic "<a name="line.1154"></a>
-<span class="sourceLineNo">1155</span> + "number. Requested=" + pblen + ", Received= " + read + ", File=" + persistencePath);<a name="line.1155"></a>
-<span class="sourceLineNo">1156</span> }<a name="line.1156"></a>
-<span class="sourceLineNo">1157</span> if (! ProtobufMagic.isPBMagicPrefix(pbuf)) {<a name="line.1157"></a>
-<span class="sourceLineNo">1158</span> // In 3.0 we have enough flexibility to dump the old cache data.<a name="line.1158"></a>
-<span class="sourceLineNo">1159</span> // TODO: In 2.x line, this might need to be filled in to support reading the old format<a name="line.1159"></a>
-<span class="sourceLineNo">1160</span> throw new IOException("Persistence file does not start with protobuf magic number. " +<a name="line.1160"></a>
-<span class="sourceLineNo">1161</span> persistencePath);<a name="line.1161"></a>
-<span class="sourceLineNo">1162</span> }<a name="line.1162"></a>
-<span class="sourceLineNo">1163</span> parsePB(BucketCacheProtos.BucketCacheEntry.parseDelimitedFrom(in));<a name="line.1163"></a>
-<span class="sourceLineNo">1164</span> bucketAllocator = new BucketAllocator(cacheCapacity, bucketSizes, backingMap, realCacheSize);<a name="line.1164"></a>
-<span class="sourceLineNo">1165</span> blockNumber.add(backingMap.size());<a name="line.1165"></a>
-<span class="sourceLineNo">1166</span> }<a name="line.1166"></a>
-<span class="sourceLineNo">1167</span> }<a name="line.1167"></a>
-<span class="sourceLineNo">1168</span><a name="line.1168"></a>
-<span class="sourceLineNo">1169</span> /**<a name="line.1169"></a>
-<span class="sourceLineNo">1170</span> * Create an input stream that deletes the file after reading it. Use in try-with-resources to<a name="line.1170"></a>
-<span class="sourceLineNo">1171</span> * avoid this pattern where an exception thrown from a finally block may mask earlier exceptions:<a name="line.1171"></a>
-<span class="sourceLineNo">1172</span> * <pre><a name="line.1172"></a>
-<span class="sourceLineNo">1173</span> * File f = ...<a name="line.1173"></a>
-<span class="sourceLineNo">1174</span> * try (FileInputStream fis = new FileInputStream(f)) {<a name="line.1174"></a>
-<span class="sourceLineNo">1175</span> * // use the input stream<a name="line.1175"></a>
-<span class="sourceLineNo">1176</span> * } finally {<a name="line.1176"></a>
-<span class="sourceLineNo">1177</span> * if (!f.delete()) throw new IOException("failed to delete");<a name="line.1177"></a>
-<span class="sourceLineNo">1178</span> * }<a name="line.1178"></a>
-<span class="sourceLineNo">1179</span> * </pre><a name="line.1179"></a>
-<span class="sourceLineNo">1180</span> * @param file the file to read and delete<a name="line.1180"></a>
-<span class="sourceLineNo">1181</span> * @return a FileInputStream for the given file<a name="line.1181"></a>
-<span class="sourceLineNo">1182</span> * @throws IOException if there is a problem creating the stream<a name="line.1182"></a>
-<span class="sourceLineNo">1183</span> */<a name="line.1183"></a>
-<span class="sourceLineNo">1184</span> private FileInputStream deleteFileOnClose(final File file) throws IOException {<a name="line.1184"></a>
-<span class="sourceLineNo">1185</span> return new FileInputStream(file) {<a name="line.1185"></a>
-<span class="sourceLineNo">1186</span> private File myFile;<a name="line.1186"></a>
-<span class="sourceLineNo">1187</span> private FileInputStream init(File file) {<a name="line.1187"></a>
-<span class="sourceLineNo">1188</span> myFile = file;<a name="line.1188"></a>
-<span class="sourceLineNo">1189</span> return this;<a name="line.1189"></a>
-<span class="sourceLineNo">1190</span> }<a name="line.1190"></a>
-<span class="sourceLineNo">1191</span> @Override<a name="line.1191"></a>
-<span class="sourceLineNo">1192</span> public void close() throws IOException {<a name="line.1192"></a>
-<span class="sourceLineNo">1193</span> // close() will be called during try-with-resources and it will be<a name="line.1193"></a>
-<span class="sourceLineNo">1194</span> // called by finalizer thread during GC. To avoid double-free resource,<a name="line.1194"></a>
-<span class="sourceLineNo">1195</span> // set myFile to null after the first call.<a name="line.1195"></a>
-<span class="sourceLineNo">1196</span> if (myFile == null) {<a name="line.1196"></a>
-<span class="sourceLineNo">1197</span> return;<a name="line.1197"></a>
-<span class="sourceLineNo">1198</span> }<a name="line.1198"></a>
-<span class="sourceLineNo">1199</span><a name="line.1199"></a>
-<span class="sourceLineNo">1200</span> super.close();<a name="line.1200"></a>
-<span class="sourceLineNo">1201</span> if (!myFile.delete()) {<a name="line.1201"></a>
-<span class="sourceLineNo">1202</span> throw new IOException("Failed deleting persistence file " + myFile.getAbsolutePath());<a name="line.1202"></a>
-<span class="sourceLineNo">1203</span> }<a name="line.1203"></a>
-<span class="sourceLineNo">1204</span> myFile = null;<a name="line.1204"></a>
-<span class="sourceLineNo">1205</span> }<a name="line.1205"></a>
-<span class="sourceLineNo">1206</span> }.init(file);<a name="line.1206"></a>
-<span class="sourceLineNo">1207</span> }<a name="line.1207"></a>
-<span class="sourceLineNo">1208</span><a name="line.1208"></a>
-<span class="sourceLineNo">1209</span> private void verifyCapacityAndClasses(long capacitySize, String ioclass, String mapclass)<a name="line.1209"></a>
-<span class="sourceLineNo">1210</span> throws IOException {<a name="line.1210"></a>
-<span class="sourceLineNo">1211</span> if (capacitySize != cacheCapacity) {<a name="line.1211"></a>
-<span class="sourceLineNo">1212</span> throw new IOException("Mismatched cache capacity:"<a name="line.1212"></a>
-<span class="sourceLineNo">1213</span> + StringUtils.byteDesc(capacitySize) + ", expected: "<a name="line.1213"></a>
-<span class="sourceLineNo">1214</span> + StringUtils.byteDesc(cacheCapacity));<a name="line.1214"></a>
-<span class="sourceLineNo">1215</span> }<a name="line.1215"></a>
-<span class="sourceLineNo">1216</span> if (!ioEngine.getClass().getName().equals(ioclass)) {<a name="line.1216"></a>
-<span class="sourceLineNo">1217</span> throw new IOException("Class name for IO engine mismatch: " + ioclass<a name="line.1217"></a>
-<span class="sourceLineNo">1218</span> + ", expected:" + ioEngine.getClass().getName());<a name="line.1218"></a>
+<span class="sourceLineNo">1137</span> try (FileOutputStream fos = new FileOutputStream(persistencePath, false)) {<a name="line.1137"></a>
+<span class="sourceLineNo">1138</span> fos.write(ProtobufMagic.PB_MAGIC);<a name="line.1138"></a>
+<span class="sourceLineNo">1139</span> BucketProtoUtils.toPB(this).writeDelimitedTo(fos);<a name="line.1139"></a>
+<span class="sourceLineNo">1140</span> }<a name="line.1140"></a>
+<span class="sourceLineNo">1141</span> }<a name="line.1141"></a>
+<span class="sourceLineNo">1142</span><a name="line.1142"></a>
+<span class="sourceLineNo">1143</span> /**<a name="line.1143"></a>
+<span class="sourceLineNo">1144</span> * @see #persistToFile()<a name="line.1144"></a>
+<span class="sourceLineNo">1145</span> */<a name="line.1145"></a>
+<span class="sourceLineNo">1146</span> private void retrieveFromFile(int[] bucketSizes) throws IOException {<a name="line.1146"></a>
+<span class="sourceLineNo">1147</span> File persistenceFile = new File(persistencePath);<a name="line.1147"></a>
+<span class="sourceLineNo">1148</span> if (!persistenceFile.exists()) {<a name="line.1148"></a>
+<span class="sourceLineNo">1149</span> return;<a name="line.1149"></a>
+<span class="sourceLineNo">1150</span> }<a name="line.1150"></a>
+<span class="sourceLineNo">1151</span> assert !cacheEnabled;<a name="line.1151"></a>
+<span class="sourceLineNo">1152</span><a name="line.1152"></a>
+<span class="sourceLineNo">1153</span> try (FileInputStream in = deleteFileOnClose(persistenceFile)) {<a name="line.1153"></a>
+<span class="sourceLineNo">1154</span> int pblen = ProtobufMagic.lengthOfPBMagic();<a name="line.1154"></a>
+<span class="sourceLineNo">1155</span> byte[] pbuf = new byte[pblen];<a name="line.1155"></a>
+<span class="sourceLineNo">1156</span> int read = in.read(pbuf);<a name="line.1156"></a>
+<span class="sourceLineNo">1157</span> if (read != pblen) {<a name="line.1157"></a>
+<span class="sourceLineNo">1158</span> throw new IOException("Incorrect number of bytes read while checking for protobuf magic "<a name="line.1158"></a>
+<span class="sourceLineNo">1159</span> + "number. Requested=" + pblen + ", Received= " + read + ", File=" + persistencePath);<a name="line.1159"></a>
+<span class="sourceLineNo">1160</span> }<a name="line.1160"></a>
+<span class="sourceLineNo">1161</span> if (! ProtobufMagic.isPBMagicPrefix(pbuf)) {<a name="line.1161"></a>
+<span class="sourceLineNo">1162</span> // In 3.0 we have enough flexibility to dump the old cache data.<a name="line.1162"></a>
+<span class="sourceLineNo">1163</span> // TODO: In 2.x line, this might need to be filled in to support reading the old format<a name="line.1163"></a>
+<span class="sourceLineNo">1164</span> throw new IOException("Persistence file does not start with protobuf magic number. " +<a name="line.1164"></a>
+<span class="sourceLineNo">1165</span> persistencePath);<a name="line.1165"></a>
+<span class="sourceLineNo">1166</span> }<a name="line.1166"></a>
+<span class="sourceLineNo">1167</span> parsePB(BucketCacheProtos.BucketCacheEntry.parseDelimitedFrom(in));<a name="line.1167"></a>
+<span class="sourceLineNo">1168</span> bucketAllocator = new BucketAllocator(cacheCapacity, bucketSizes, backingMap, realCacheSize);<a name="line.1168"></a>
+<span class="sourceLineNo">1169</span> blockNumber.add(backingMap.size());<a name="line.1169"></a>
+<span class="sourceLineNo">1170</span> }<a name="line.1170"></a>
+<span class="sourceLineNo">1171</span> }<a name="line.1171"></a>
+<span class="sourceLineNo">1172</span><a name="line.1172"></a>
+<span class="sourceLineNo">1173</span> /**<a name="line.1173"></a>
+<span class="sourceLineNo">1174</span> * Create an input stream that deletes the file after reading it. Use in try-with-resources to<a name="line.1174"></a>
+<span class="sourceLineNo">1175</span> * avoid this pattern where an exception thrown from a finally block may mask earlier exceptions:<a name="line.1175"></a>
+<span class="sourceLineNo">1176</span> * <pre><a name="line.1176"></a>
+<span class="sourceLineNo">1177</span> * File f = ...<a name="line.1177"></a>
+<span class="sourceLineNo">1178</span> * try (FileInputStream fis = new FileInputStream(f)) {<a name="line.1178"></a>
+<span class="sourceLineNo">1179</span> * // use the input stream<a name="line.1179"></a>
+<span class="sourceLineNo">1180</span> * } finally {<a name="line.1180"></a>
+<span class="sourceLineNo">1181</span> * if (!f.delete()) throw new IOException("failed to delete");<a name="line.1181"></a>
+<span class="sourceLineNo">1182</span> * }<a name="line.1182"></a>
+<span class="sourceLineNo">1183</span> * </pre><a name="line.1183"></a>
+<span class="sourceLineNo">1184</span> * @param file the file to read and delete<a name="line.1184"></a>
+<span class="sourceLineNo">1185</span> * @return a FileInputStream for the given file<a name="line.1185"></a>
+<span class="sourceLineNo">1186</span> * @throws IOException if there is a problem creating the stream<a name="line.1186"></a>
+<span class="sourceLineNo">1187</span> */<a name="line.1187"></a>
+<span class="sourceLineNo">1188</span> private FileInputStream deleteFileOnClose(final File file) throws IOException {<a name="line.1188"></a>
+<span class="sourceLineNo">1189</span> return new FileInputStream(file) {<a name="line.1189"></a>
+<span class="sourceLineNo">1190</span> private File myFile;<a name="line.1190"></a>
+<span class="sourceLineNo">1191</span> private FileInputStream init(File file) {<a name="line.1191"></a>
+<span class="sourceLineNo">1192</span> myFile = file;<a name="line.1192"></a>
+<span class="sourceLineNo">1193</span> return this;<a name="line.1193"></a>
+<span class="sourceLineNo">1194</span> }<a name="line.1194"></a>
+<span class="sourceLineNo">1195</span> @Override<a name="line.1195"></a>
+<span class="sourceLineNo">1196</span> public void close() throws IOException {<a name="line.1196"></a>
+<span class="sourceLineNo">1197</span> // close() will be called during try-with-resources and it will be<a name="line.1197"></a>
+<span class="sourceLineNo">1198</span> // called by finalizer thread during GC. To avoid double-free resource,<a name="line.1198"></a>
+<span class="sourceLineNo">1199</span> // set myFile to null after the first call.<a name="line.1199"></a>
+<span class="sourceLineNo">1200</span> if (myFile == null) {<a name="line.1200"></a>
+<span class="sourceLineNo">1201</span> return;<a name="line.1201"></a>
+<span class="sourceLineNo">1202</span> }<a name="line.1202"></a>
+<span class="sourceLineNo">1203</span><a name="line.1203"></a>
+<span class="sourceLineNo">1204</span> super.close();<a name="line.1204"></a>
+<span class="sourceLineNo">1205</span> if (!myFile.delete()) {<a name="line.1205"></a>
+<span class="sourceLineNo">1206</span> throw new IOException("Failed deleting persistence file " + myFile.getAbsolutePath());<a name="line.1206"></a>
+<span class="sourceLineNo">1207</span> }<a name="line.1207"></a>
+<span class="sourceLineNo">1208</span> myFile = null;<a name="line.1208"></a>
+<span class="sourceLineNo">1209</span> }<a name="line.1209"></a>
+<span class="sourceLineNo">1210</span> }.init(file);<a name="line.1210"></a>
+<span class="sourceLineNo">1211</span> }<a name="line.1211"></a>
+<span class="sourceLineNo">1212</span><a name="line.1212"></a>
+<span class="sourceLineNo">1213</span> private void verifyCapacityAndClasses(long capacitySize, String ioclass, String mapclass)<a name="line.1213"></a>
+<span class="sourceLineNo">1214</span> throws IOException {<a name="line.1214"></a>
+<span class="sourceLineNo">1215</span> if (capacitySize != cacheCapacity) {<a name="line.1215"></a>
+<span class="sourceLineNo">1216</span> throw new IOException("Mismatched cache capacity:"<a name="line.1216"></a>
+<span class="sourceLineNo">1217</span> + StringUtils.byteDesc(capacitySize) + ", expected: "<a name="line.1217"></a>
+<span class="sourceLineNo">1218</span> + StringUtils.byteDesc(cacheCapacity));<a name="line.1218"></a>
<span class="sourceLineNo">1219</span> }<a name="line.1219"></a>
-<span class="sourceLineNo">1220</span> if (!backingMap.getClass().getName().equals(mapclass)) {<a name="line.1220"></a>
-<span class="sourceLineNo">1221</span> throw new IOException("Class name for cache map mismatch: " + mapclass<a name="line.1221"></a>
-<span class="sourceLineNo">1222</span> + ", expected:" + backingMap.getClass().getName());<a name="line.1222"></a>
+<span class="sourceLineNo">1220</span> if (!ioEngine.getClass().getName().equals(ioclass)) {<a name="line.1220"></a>
+<span class="sourceLineNo">1221</span> throw new IOException("Class name for IO engine mismatch: " + ioclass<a name="line.1221"></a>
+<span class="sourceLineNo">1222</span> + ", expected:" + ioEngine.getClass().getName());<a name="line.1222"></a>
<span class="sourceLineNo">1223</span> }<a name="line.1223"></a>
-<span class="sourceLineNo">1224</span> }<a name="line.1224"></a>
-<span class="sourceLineNo">1225</span><a name="line.1225"></a>
-<span class="sourceLineNo">1226</span> private void parsePB(BucketCacheProtos.BucketCacheEntry proto) throws IOException {<a name="line.1226"></a>
-<span class="sourceLineNo">1227</span> if (proto.hasChecksum()) {<a name="line.1227"></a>
-<span class="sourceLineNo">1228</span> ((PersistentIOEngine) ioEngine).verifyFileIntegrity(proto.getChecksum().toByteArray(),<a name="line.1228"></a>
-<span class="sourceLineNo">1229</span> algorithm);<a name="line.1229"></a>
-<span class="sourceLineNo">1230</span> } else {<a name="line.1230"></a>
-<span class="sourceLineNo">1231</span> // if has not checksum, it means the persistence file is old format<a name="line.1231"></a>
-<span class="sourceLineNo">1232</span> LOG.info("Persistent file is old format, it does not support verifying file integrity!");<a name="line.1232"></a>
-<span class="sourceLineNo">1233</span> }<a name="line.1233"></a>
-<span class="sourceLineNo">1234</span> verifyCapacityAndClasses(proto.getCacheCapacity(), proto.getIoClass(), proto.getMapClass());<a name="line.1234"></a>
-<span class="sourceLineNo">1235</span> backingMap = BucketProtoUtils.fromPB(proto.getDeserializersMap(), proto.getBackingMap(),<a name="line.1235"></a>
-<span class="sourceLineNo">1236</span> this::createRecycler);<a name="line.1236"></a>
-<span class="sourceLineNo">1237</span> }<a name="line.1237"></a>
-<span class="sourceLineNo">1238</span><a name="line.1238"></a>
-<span class="sourceLineNo">1239</span> /**<a name="line.1239"></a>
-<span class="sourceLineNo">1240</span> * Check whether we tolerate IO error this time. If the duration of IOEngine<a name="line.1240"></a>
-<span class="sourceLineNo">1241</span> * throwing errors exceeds ioErrorsDurationTimeTolerated, we will disable the<a name="line.1241"></a>
-<span class="sourceLineNo">1242</span> * cache<a name="line.1242"></a>
-<span class="sourceLineNo">1243</span> */<a name="line.1243"></a>
-<span class="sourceLineNo">1244</span> private void checkIOErrorIsTolerated() {<a name="line.1244"></a>
-<span class="sourceLineNo">1245</span> long now = EnvironmentEdgeManager.currentTime();<a name="line.1245"></a>
-<span class="sourceLineNo">1246</span> // Do a single read to a local variable to avoid timing issue - HBASE-24454<a name="line.1246"></a>
-<span class="sourceLineNo">1247</span> long ioErrorStartTimeTmp = this.ioErrorStartTime;<a name="line.1247"></a>
-<span class="sourceLineNo">1248</span> if (ioErrorStartTimeTmp > 0) {<a name="line.1248"></a>
-<span class="sourceLineNo">1249</span> if (cacheEnabled && (now - ioErrorStartTimeTmp) > this.ioErrorsTolerationDuration) {<a name="line.1249"></a>
-<span class="sourceLineNo">1250</span> LOG.error("IO errors duration time has exceeded " + ioErrorsTolerationDuration +<a name="line.1250"></a>
-<span class="sourceLineNo">1251</span> "ms, disabling cache, please check your IOEngine");<a name="line.1251"></a>
-<span class="sourceLineNo">1252</span> disableCache();<a name="line.1252"></a>
-<span class="sourceLineNo">1253</span> }<a name="line.1253"></a>
-<span class="sourceLineNo">1254</span> } else {<a name="line.1254"></a>
-<span class="sourceLineNo">1255</span> this.ioErrorStartTime = now;<a name="line.1255"></a>
-<span class="sourceLineNo">1256</span> }<a name="line.1256"></a>
-<span class="sourceLineNo">1257</span> }<a name="line.1257"></a>
-<span class="sourceLineNo">1258</span><a name="line.1258"></a>
-<span class="sourceLineNo">1259</span> /**<a name="line.1259"></a>
-<span class="sourceLineNo">1260</span> * Used to shut down the cache -or- turn it off in the case of something broken.<a name="line.1260"></a>
-<span class="sourceLineNo">1261</span> */<a name="line.1261"></a>
-<span class="sourceLineNo">1262</span> private void disableCache() {<a name="line.1262"></a>
-<span class="sourceLineNo">1263</span> if (!cacheEnabled) return;<a name="line.1263"></a>
-<span class="sourceLineNo">1264</span> cacheEnabled = false;<a name="line.1264"></a>
-<span class="sourceLineNo">1265</span> ioEngine.shutdown();<a name="line.1265"></a>
-<span class="sourceLineNo">1266</span> this.scheduleThreadPool.shutdown();<a name="line.1266"></a>
-<span class="sourceLineNo">1267</span> for (int i = 0; i < writerThreads.length; ++i) writerThreads[i].interrupt();<a name="line.1267"></a>
-<span class="sourceLineNo">1268</span> this.ramCache.clear();<a name="line.1268"></a>
-<span class="sourceLineNo">1269</span> if (!ioEngine.isPersistent() || persistencePath == null) {<a name="line.1269"></a>
-<span class="sourceLineNo">1270</span> // If persistent ioengine and a path, we will serialize out the backingMap.<a name="line.1270"></a>
-<span class="sourceLineNo">1271</span> this.backingMap.clear();<a name="line.1271"></a>
-<span class="sourceLineNo">1272</span> }<a name="line.1272"></a>
-<span class="sourceLineNo">1273</span> }<a name="line.1273"></a>
-<span class="sourceLineNo">1274</span><a name="line.1274"></a>
-<span class="sourceLineNo">1275</span> private void join() throws InterruptedException {<a name="line.1275"></a>
-<span class="sourceLineNo">1276</span> for (int i = 0; i < writerThreads.length; ++i)<a name="line.1276"></a>
-<span class="sourceLineNo">1277</span> writerThreads[i].join();<a name="line.1277"></a>
-<span class="sourceLineNo">1278</span> }<a name="line.1278"></a>
-<span class="sourceLineNo">1279</span><a name="line.1279"></a>
-<span class="sourceLineNo">1280</span> @Override<a name="line.1280"></a>
-<span class="sourceLineNo">1281</span> public void shutdown() {<a name="line.1281"></a>
-<span class="sourceLineNo">1282</span> disableCache();<a name="line.1282"></a>
-<span class="sourceLineNo">1283</span> LOG.info("Shutdown bucket cache: IO persistent=" + ioEngine.isPersistent()<a name="line.1283"></a>
-<span class="sourceLineNo">1284</span> + "; path to write=" + persistencePath);<a name="line.1284"></a>
-<span class="sourceLineNo">1285</span> if (ioEngine.isPersistent() && persistencePath != null) {<a name="line.1285"></a>
-<span class="sourceLineNo">1286</span> try {<a name="line.1286"></a>
-<span class="sourceLineNo">1287</span> join();<a name="line.1287"></a>
-<span class="sourceLineNo">1288</span> persistToFile();<a name="line.1288"></a>
-<span class="sourceLineNo">1289</span> } catch (IOException ex) {<a name="line.1289"></a>
-<span class="sourceLineNo">1290</span> LOG.error("Unable to persist data on exit: " + ex.toString(), ex);<a name="line.1290"></a>
-<span class="sourceLineNo">1291</span> } catch (InterruptedException e) {<a name="line.1291"></a>
-<span class="sourceLineNo">1292</span> LOG.warn("Failed to persist data on exit", e);<a name="line.1292"></a>
-<span class="sourceLineNo">1293</span> }<a name="line.1293"></a>
-<span class="sourceLineNo">1294</span> }<a name="line.1294"></a>
-<span class="sourceLineNo">1295</span> }<a name="line.1295"></a>
-<span class="sourceLineNo">1296</span><a name="line.1296"></a>
-<span class="sourceLineNo">1297</span> @Override<a name="line.1297"></a>
-<span class="sourceLineNo">1298</span> public CacheStats getStats() {<a name="line.1298"></a>
-<span class="sourceLineNo">1299</span> return cacheStats;<a name="line.1299"></a>
-<span class="sourceLineNo">1300</span> }<a name="line.1300"></a>
-<span class="sourceLineNo">1301</span><a name="line.1301"></a>
-<span class="sourceLineNo">1302</span> public BucketAllocator getAllocator() {<a name="line.1302"></a>
-<span class="sourceLineNo">1303</span> return this.bucketAllocator;<a name="line.1303"></a>
+<span class="sourceLineNo">1224</span> if (!backingMap.getClass().getName().equals(mapclass)) {<a name="line.1224"></a>
+<span class="sourceLineNo">1225</span> throw new IOException("Class name for cache map mismatch: " + mapclass<a name="line.1225"></a>
+<span class="sourceLineNo">1226</span> + ", expected:" + backingMap.getClass().getName());<a name="line.1226"></a>
+<span class="sourceLineNo">1227</span> }<a name="line.1227"></a>
+<span class="sourceLineNo">1228</span> }<a name="line.1228"></a>
+<span class="sourceLineNo">1229</span><a name="line.1229"></a>
+<span class="sourceLineNo">1230</span> private void parsePB(BucketCacheProtos.BucketCacheEntry proto) throws IOException {<a name="line.1230"></a>
+<span class="sourceLineNo">1231</span> if (proto.hasChecksum()) {<a name="line.1231"></a>
+<span class="sourceLineNo">1232</span> ((PersistentIOEngine) ioEngine).verifyFileIntegrity(proto.getChecksum().toByteArray(),<a name="line.1232"></a>
+<span class="sourceLineNo">1233</span> algorithm);<a name="line.1233"></a>
+<span class="sourceLineNo">1234</span> } else {<a name="line.1234"></a>
+<span class="sourceLineNo">1235</span> // if has not checksum, it means the persistence file is old format<a name="line.1235"></a>
+<span class="sourceLineNo">1236</span> LOG.info("Persistent file is old format, it does not support verifying file integrity!");<a name="line.1236"></a>
+<span class="sourceLineNo">1237</span> }<a name="line.1237"></a>
+<span class="sourceLineNo">1238</span> verifyCapacityAndClasses(proto.getCacheCapacity(), proto.getIoClass(), proto.getMapClass());<a name="line.1238"></a>
+<span class="sourceLineNo">1239</span> backingMap = BucketProtoUtils.fromPB(proto.getDeserializersMap(), proto.getBackingMap(),<a name="line.1239"></a>
+<span class="sourceLineNo">1240</span> this::createRecycler);<a name="line.1240"></a>
+<span class="sourceLineNo">1241</span> }<a name="line.1241"></a>
+<span class="sourceLineNo">1242</span><a name="line.1242"></a>
+<span class="sourceLineNo">1243</span> /**<a name="line.1243"></a>
+<span class="sourceLineNo">1244</span> * Check whether we tolerate IO error this time. If the duration of IOEngine<a name="line.1244"></a>
+<span class="sourceLineNo">1245</span> * throwing errors exceeds ioErrorsDurationTimeTolerated, we will disable the<a name="line.1245"></a>
+<span class="sourceLineNo">1246</span> * cache<a name="line.1246"></a>
+<span class="sourceLineNo">1247</span> */<a name="line.1247"></a>
+<span class="sourceLineNo">1248</span> private void checkIOErrorIsTolerated() {<a name="line.1248"></a>
+<span class="sourceLineNo">1249</span> long now = EnvironmentEdgeManager.currentTime();<a name="line.1249"></a>
+<span class="sourceLineNo">1250</span> // Do a single read to a local variable to avoid timing issue - HBASE-24454<a name="line.1250"></a>
+<span class="sourceLineNo">1251</span> long ioErrorStartTimeTmp = this.ioErrorStartTime;<a name="line.1251"></a>
+<span class="sourceLineNo">1252</span> if (ioErrorStartTimeTmp > 0) {<a name="line.1252"></a>
+<span class="sourceLineNo">1253</span> if (cacheEnabled && (now - ioErrorStartTimeTmp) > this.ioErrorsTolerationDuration) {<a name="line.1253"></a>
+<span class="sourceLineNo">1254</span> LOG.error("IO errors duration time has exceeded " + ioErrorsTolerationDuration +<a name="line.1254"></a>
+<span class="sourceLineNo">1255</span> "ms, disabling cache, please check your IOEngine");<a name="line.1255"></a>
+<span class="sourceLineNo">1256</span> disableCache();<a name="line.1256"></a>
+<span class="sourceLineNo">1257</span> }<a name="line.1257"></a>
+<span class="sourceLineNo">1258</span> } else {<a name="line.1258"></a>
+<span class="sourceLineNo">1259</span> this.ioErrorStartTime = now;<a name="line.1259"></a>
+<span class="sourceLineNo">1260</span> }<a name="line.1260"></a>
+<span class="sourceLineNo">1261</span> }<a name="line.1261"></a>
+<span class="sourceLineNo">1262</span><a name="line.1262"></a>
+<span class="sourceLineNo">1263</span> /**<a name="line.1263"></a>
+<span class="sourceLineNo">1264</span> * Used to shut down the cache -or- turn it off in the case of something broken.<a name="line.1264"></a>
+<span class="sourceLineNo">1265</span> */<a name="line.1265"></a>
+<span class="sourceLineNo">1266</span> private void disableCache() {<a name="line.1266"></a>
+<span class="sourceLineNo">1267</span> if (!cacheEnabled) return;<a name="line.1267"></a>
+<span class="sourceLineNo">1268</span> cacheEnabled = false;<a name="line.1268"></a>
+<span class="sourceLineNo">1269</span> ioEngine.shutdown();<a name="line.1269"></a>
+<span class="sourceLineNo">1270</span> this.scheduleThreadPool.shutdown();<a name="line.1270"></a>
+<span class="sourceLineNo">1271</span> for (int i = 0; i < writerThreads.length; ++i) writerThreads[i].interrupt();<a name="line.1271"></a>
+<span class="sourceLineNo">1272</span> this.ramCache.clear();<a name="line.1272"></a>
+<span class="sourceLineNo">1273</span> if (!ioEngine.isPersistent() || persistencePath == null) {<a name="line.1273"></a>
+<span class="sourceLineNo">1274</span> // If persistent ioengine and a path, we will serialize out the backingMap.<a name="line.1274"></a>
+<span class="sourceLineNo">1275</span> this.backingMap.clear();<a name="line.1275"></a>
+<span class="sourceLineNo">1276</span> }<a name="line.1276"></a>
+<span class="sourceLineNo">1277</span> }<a name="line.1277"></a>
+<span class="sourceLineNo">1278</span><a name="line.1278"></a>
+<span class="sourceLineNo">1279</span> private void join() throws InterruptedException {<a name="line.1279"></a>
+<span class="sourceLineNo">1280</span> for (int i = 0; i < writerThreads.length; ++i)<a name="line.1280"></a>
+<span class="sourceLineNo">1281</span> writerThreads[i].join();<a name="line.1281"></a>
+<span class="sourceLineNo">1282</span> }<a name="line.1282"></a>
+<span class="sourceLineNo">1283</span><a name="line.1283"></a>
+<span class="sourceLineNo">1284</span> @Override<a name="line.1284"></a>
+<span class="sourceLineNo">1285</span> public void shutdown() {<a name="line.1285"></a>
+<span class="sourceLineNo">1286</span> disableCache();<a name="line.1286"></a>
+<span class="sourceLineNo">1287</span> LOG.info("Shutdown bucket cache: IO persistent=" + ioEngine.isPersistent()<a name="line.1287"></a>
+<span class="sourceLineNo">1288</span> + "; path to write=" + persistencePath);<a name="line.1288"></a>
+<span class="sourceLineNo">1289</span> if (ioEngine.isPersistent() && persistencePath != null) {<a name="line.1289"></a>
+<span class="sourceLineNo">1290</span> try {<a name="line.1290"></a>
+<span class="sourceLineNo">1291</span> join();<a name="line.1291"></a>
+<span class="sourceLineNo">1292</span> persistToFile();<a name="line.1292"></a>
+<span class="sourceLineNo">1293</span> } catch (IOException ex) {<a name="line.1293"></a>
+<span class="sourceLineNo">1294</span> LOG.error("Unable to persist data on exit: " + ex.toString(), ex);<a name="line.1294"></a>
+<span class="sourceLineNo">1295</span> } catch (InterruptedException e) {<a name="line.1295"></a>
+<span class="sourceLineNo">1296</span> LOG.warn("Failed to persist data on exit", e);<a name="line.1296"></a>
+<span class="sourceLineNo">1297</span> }<a name="line.1297"></a>
+<span class="sourceLineNo">1298</span> }<a name="line.1298"></a>
+<span class="sourceLineNo">1299</span> }<a name="line.1299"></a>
+<span class="sourceLineNo">1300</span><a name="line.1300"></a>
+<span class="sourceLineNo">1301</span> @Override<a name="line.1301"></a>
+<span class="sourceLineNo">1302</span> public CacheStats getStats() {<a name="line.1302"></a>
+<span class="sourceLineNo">1303</span> return cacheStats;<a name="line.1303"></a>
<span class="sourceLineNo">1304</span> }<a name="line.1304"></a>
<span class="sourceLineNo">1305</span><a name="line.1305"></a>
-<span class="sourceLineNo">1306</span> @Override<a name="line.1306"></a>
-<span class="sourceLineNo">1307</span> public long heapSize() {<a name="line.1307"></a>
-<span class="sourceLineNo">1308</span> return this.heapSize.sum();<a name="line.1308"></a>
-<span class="sourceLineNo">1309</span> }<a name="line.1309"></a>
-<span class="sourceLineNo">1310</span><a name="line.1310"></a>
-<span class="sourceLineNo">1311</span> @Override<a name="line.1311"></a>
-<span class="sourceLineNo">1312</span> public long size() {<a name="line.1312"></a>
-<span class="sourceLineNo">1313</span> return this.realCacheSize.sum();<a name="line.1313"></a>
-<span class="sourceLineNo">1314</span> }<a name="line.1314"></a>
-<span class="sourceLineNo">1315</span><a name="line.1315"></a>
-<span class="sourceLineNo">1316</span> @Override<a name="line.1316"></a>
-<span class="sourceLineNo">1317</span> public long getCurrentDataSize() {<a name="line.1317"></a>
-<span class="sourceLineNo">1318</span> return size();<a name="line.1318"></a>
-<span class="sourceLineNo">1319</span> }<a name="line.1319"></a>
-<span class="sourceLineNo">1320</span><a name="line.1320"></a>
-<span class="sourceLineNo">1321</span> @Override<a name="line.1321"></a>
-<span class="sourceLineNo">1322</span> public long getFreeSize() {<a name="line.1322"></a>
-<span class="sourceLineNo">1323</span> return this.bucketAllocator.getFreeSize();<a name="line.1323"></a>
-<span class="sourceLineNo">1324</span> }<a name="line.1324"></a>
-<span class="sourceLineNo">1325</span><a name="line.1325"></a>
-<span class="sourceLineNo">1326</span> @Override<a name="line.1326"></a>
-<span class="sourceLineNo">1327</span> public long getBlockCount() {<a name="line.1327"></a>
-<span class="sourceLineNo">1328</span> return this.blockNumber.sum();<a name="line.1328"></a>
-<span class="sourceLineNo">1329</span> }<a name="line.1329"></a>
-<span class="sourceLineNo">1330</span><a name="line.1330"></a>
-<span class="sourceLineNo">1331</span> @Override<a name="line.1331"></a>
-<span class="sourceLineNo">1332</span> public long getDataBlockCount() {<a name="line.1332"></a>
-<span class="sourceLineNo">1333</span> return getBlockCount();<a name="line.1333"></a>
-<span class="sourceLineNo">1334</span> }<a name="line.1334"></a>
-<span class="sourceLineNo">1335</span><a name="line.1335"></a>
-<span class="sourceLineNo">1336</span> @Override<a name="line.1336"></a>
-<span class="sourceLineNo">1337</span> public long getCurrentSize() {<a name="line.1337"></a>
-<span class="sourceLineNo">1338</span> return this.bucketAllocator.getUsedSize();<a name="line.1338"></a>
-<span class="sourceLineNo">1339</span> }<a name="line.1339"></a>
-<span class="sourceLineNo">1340</span><a name="line.1340"></a>
-<span class="sourceLineNo">1341</span> protected String getAlgorithm() {<a name="line.1341"></a>
-<span class="sourceLineNo">1342</span> return algorithm;<a name="line.1342"></a>
+<span class="sourceLineNo">1306</span> public BucketAllocator getAllocator() {<a name="line.1306"></a>
+<span class="sourceLineNo">1307</span> return this.bucketAllocator;<a name="line.1307"></a>
+<span class="sourceLineNo">1308</span> }<a name="line.1308"></a>
+<span class="sourceLineNo">1309</span><a name="line.1309"></a>
+<span class="sourceLineNo">1310</span> @Override<a name="line.1310"></a>
+<span class="sourceLineNo">1311</span> public long heapSize() {<a name="line.1311"></a>
+<span class="sourceLineNo">1312</span> return this.heapSize.sum();<a name="line.1312"></a>
+<span class="sourceLineNo">1313</span> }<a name="line.1313"></a>
+<span class="sourceLineNo">1314</span><a name="line.1314"></a>
+<span class="sourceLineNo">1315</span> @Override<a name="line.1315"></a>
+<span class="sourceLineNo">1316</span> public long size() {<a name="line.1316"></a>
+<span class="sourceLineNo">1317</span> return this.realCacheSize.sum();<a name="line.1317"></a>
+<span class="sourceLineNo">1318</span> }<a name="line.1318"></a>
+<span class="sourceLineNo">1319</span><a name="line.1319"></a>
+<span class="sourceLineNo">1320</span> @Override<a name="line.1320"></a>
+<span class="sourceLineNo">1321</span> public long getCurrentDataSize() {<a name="line.1321"></a>
+<span class="sourceLineNo">1322</span> return size();<a name="line.1322"></a>
+<span class="sourceLineNo">1323</span> }<a name="line.1323"></a>
+<span class="sourceLineNo">1324</span><a name="line.1324"></a>
+<span class="sourceLineNo">1325</span> @Override<a name="line.1325"></a>
+<span class="sourceLineNo">1326</span> public long getFreeSize() {<a name="line.1326"></a>
+<span class="sourceLineNo">1327</span> return this.bucketAllocator.getFreeSize();<a name="line.1327"></a>
+<span class="sourceLineNo">1328</span> }<a name="line.1328"></a>
+<span class="sourceLineNo">1329</span><a name="line.1329"></a>
+<span class="sourceLineNo">1330</span> @Override<a name="line.1330"></a>
+<span class="sourceLineNo">1331</span> public long getBlockCount() {<a name="line.1331"></a>
+<span class="sourceLineNo">1332</span> return this.blockNumber.sum();<a name="line.1332"></a>
+<span class="sourceLineNo">1333</span> }<a name="line.1333"></a>
+<span class="sourceLineNo">1334</span><a name="line.1334"></a>
+<span class="sourceLineNo">1335</span> @Override<a name="line.1335"></a>
+<span class="sourceLineNo">1336</span> public long getDataBlockCount() {<a name="line.1336"></a>
+<span class="sourceLineNo">1337</span> return getBlockCount();<a name="line.1337"></a>
+<span class="sourceLineNo">1338</span> }<a name="line.1338"></a>
+<span class="sourceLineNo">1339</span><a name="line.1339"></a>
+<span class="sourceLineNo">1340</span> @Override<a name="line.1340"></a>
+<span class="sourceLineNo">1341</span> public long getCurrentSize() {<a name="line.1341"></a>
+<span class="sourceLineNo">1342</span> return this.bucketAllocator.getUsedSize();<a name="line.1342"></a>
<span class="sourceLineNo">1343</span> }<a name="line.1343"></a>
<span class="sourceLineNo">1344</span><a name="line.1344"></a>
-<span class="sourceLineNo">1345</span> /**<a name="line.1345"></a>
-<span class="sourceLineNo">1346</span> * Evicts all blocks for a specific HFile.<a name="line.1346"></a>
-<span class="sourceLineNo">1347</span> * <p><a name="line.1347"></a>
-<span class="sourceLineNo">1348</span> * This is used for evict-on-close to remove all blocks of a specific HFile.<a name="line.1348"></a>
-<span class="sourceLineNo">1349</span> *<a name="line.1349"></a>
-<span class="sourceLineNo">1350</span> * @return the number of blocks evicted<a name="line.1350"></a>
-<span class="sourceLineNo">1351</span> */<a name="line.1351"></a>
-<span class="sourceLineNo">1352</span> @Override<a name="line.1352"></a>
-<span class="sourceLineNo">1353</span> public int evictBlocksByHfileName(String hfileName) {<a name="line.1353"></a>
-<span class="sourceLineNo">1354</span> Set<BlockCacheKey> keySet = blocksByHFile.subSet(<a name="line.1354"></a>
-<span class="sourceLineNo">1355</span> new BlockCacheKey(hfileName, Long.MIN_VALUE), true,<a name="line.1355"></a>
-<span class="sourceLineNo">1356</span> new BlockCacheKey(hfileName, Long.MAX_VALUE), true);<a name="line.1356"></a>
-<span class="sourceLineNo">1357</span><a name="line.1357"></a>
-<span class="sourceLineNo">1358</span> int numEvicted = 0;<a name="line.1358"></a>
-<span class="sourceLineNo">1359</span> for (BlockCacheKey key : keySet) {<a name="line.1359"></a>
-<span class="sourceLineNo">1360</span> if (evictBlock(key)) {<a name="line.1360"></a>
-<span class="sourceLineNo">1361</span> ++numEvicted;<a name="line.1361"></a>
-<span class="sourceLineNo">1362</span> }<a name="line.1362"></a>
-<span class="sourceLineNo">1363</span> }<a name="line.1363"></a>
-<span class="sourceLineNo">1364</span><a name="line.1364"></a>
-<span class="sourceLineNo">1365</span> return numEvicted;<a name="line.1365"></a>
-<span class="sourceLineNo">1366</span> }<a name="line.1366"></a>
-<span class="sourceLineNo">1367</span><a name="line.1367"></a>
-<span class="sourceLineNo">1368</span> /**<a name="line.1368"></a>
-<span class="sourceLineNo">1369</span> * Used to group bucket entries into priority buckets. There will be a<a name="line.1369"></a>
-<span class="sourceLineNo">1370</span> * BucketEntryGroup for each priority (single, multi, memory). Once bucketed,<a name="line.1370"></a>
-<span class="sourceLineNo">1371</span> * the eviction algorithm takes the appropriate number of elements out of each<a name="line.1371"></a>
-<span class="sourceLineNo">1372</span> * according to configuration parameters and their relative sizes.<a name="line.1372"></a>
-<span class="sourceLineNo">1373</span> */<a name="line.1373"></a>
-<span class="sourceLineNo">1374</span> private class BucketEntryGroup {<a name="line.1374"></a>
-<span class="sourceLineNo">1375</span><a name="line.1375"></a>
-<span class="sourceLineNo">1376</span> private CachedEntryQueue queue;<a name="line.1376"></a>
-<span class="sourceLineNo">1377</span> private long totalSize = 0;<a name="line.1377"></a>
-<span class="sourceLineNo">1378</span> private long bucketSize;<a name="line.1378"></a>
+<span class="sourceLineNo">1345</span> protected String getAlgorithm() {<a name="line.1345"></a>
+<span class="sourceLineNo">1346</span> return algorithm;<a name="line.1346"></a>
+<span class="sourceLineNo">1347</span> }<a name="line.1347"></a>
+<span class="sourceLineNo">1348</span><a name="line.1348"></a>
+<span class="sourceLineNo">1349</span> /**<a name="line.1349"></a>
+<span class="sourceLineNo">1350</span> * Evicts all blocks for a specific HFile.<a name="line.1350"></a>
+<span class="sourceLineNo">1351</span> * <p><a name="line.1351"></a>
+<span class="sourceLineNo">1352</span> * This is used for evict-on-close to remove all blocks of a specific HFile.<a name="line.1352"></a>
+<span class="sourceLineNo">1353</span> *<a name="line.1353"></a>
+<span class="sourceLineNo">1354</span> * @return the number of blocks evicted<a name="line.1354"></a>
+<span class="sourceLineNo">1355</span> */<a name="line.1355"></a>
+<span class="sourceLineNo">1356</span> @Override<a name="line.1356"></a>
+<span class="sourceLineNo">1357</span> public int evictBlocksByHfileName(String hfileName) {<a name="line.1357"></a>
+<span class="sourceLineNo">1358</span> Set<BlockCacheKey> keySet = blocksByHFile.subSet(<a name="line.1358"></a>
+<span class="sourceLineNo">1359</span> new BlockCacheKey(hfileName, Long.MIN_VALUE), true,<a name="line.1359"></a>
+<span class="sourceLineNo">1360</span> new BlockCacheKey(hfileName, Long.MAX_VALUE), true);<a name="line.1360"></a>
+<span class="sourceLineNo">1361</span><a name="line.1361"></a>
+<span class="sourceLineNo">1362</span> int numEvicted = 0;<a name="line.1362"></a>
+<span class="sourceLineNo">1363</span> for (BlockCacheKey key : keySet) {<a name="line.1363"></a>
+<span class="sourceLineNo">1364</span> if (evictBlock(key)) {<a name="line.1364"></a>
+<span class="sourceLineNo">1365</span> ++numEvicted;<a name="line.1365"></a>
+<span class="sourceLineNo">1366</span> }<a name="line.1366"></a>
+<span class="sourceLineNo">1367</span> }<a name="line.1367"></a>
+<span class="sourceLineNo">1368</span><a name="line.1368"></a>
+<span class="sourceLineNo">1369</span> return numEvicted;<a name="line.1369"></a>
+<span class="sourceLineNo">1370</span> }<a name="line.1370"></a>
+<span class="sourceLineNo">1371</span><a name="line.1371"></a>
+<span class="sourceLineNo">1372</span> /**<a name="line.1372"></a>
+<span class="sourceLineNo">1373</span> * Used to group bucket entries into priority buckets. There will be a<a name="line.1373"></a>
+<span class="sourceLineNo">1374</span> * BucketEntryGroup for each priority (single, multi, memory). Once bucketed,<a name="line.1374"></a>
+<span class="sourceLineNo">1375</span> * the eviction algorithm takes the appropriate number of elements out of each<a name="line.1375"></a>
+<span class="sourceLineNo">1376</span> * according to configuration parameters and their relative sizes.<a name="line.1376"></a>
+<span class="sourceLineNo">1377</span> */<a name="line.1377"></a>
+<span class="sourceLineNo">1378</span> private class BucketEntryGroup {<a name="line.1378"></a>
<span class="sourceLineNo">1379</span><a name="line.1379"></a>
-<span class="sourceLineNo">1380</span> public BucketEntryGroup(long bytesToFree, long blockSize, long bucketSize) {<a name="line.1380"></a>
-<span class="sourceLineNo">1381</span> this.bucketSize = bucketSize;<a name="line.1381"></a>
-<span class="sourceLineNo">1382</span> queue = new CachedEntryQueue(bytesToFree, blockSize);<a name="line.1382"></a>
-<span class="sourceLineNo">1383</span> totalSize = 0;<a name="line.1383"></a>
-<span class="sourceLineNo">1384</span> }<a name="line.1384"></a>
-<span class="sourceLineNo">1385</span><a name="line.1385"></a>
-<span class="sourceLineNo">1386</span> public void add(Map.Entry<BlockCacheKey, BucketEntry> block) {<a name="line.1386"></a>
-<span class="sourceLineNo">1387</span> totalSize += block.getValue().getLength();<a name="line.1387"></a>
-<span class="sourceLineNo">1388</span> queue.add(block);<a name="line.1388"></a>
-<span class="sourceLineNo">1389</span> }<a name="line.1389"></a>
-<span class="sourceLineNo">1390</span><a name="line.1390"></a>
-<span class="sourceLineNo">1391</span> public long free(long toFree) {<a name="line.1391"></a>
-<span class="sourceLineNo">1392</span> Map.Entry<BlockCacheKey, BucketEntry> entry;<a name="line.1392"></a>
-<span class="sourceLineNo">1393</span> long freedBytes = 0;<a name="line.1393"></a>
-<span class="sourceLineNo">1394</span> // TODO avoid a cycling siutation. We find no block which is not in use and so no way to free<a name="line.1394"></a>
-<span class="sourceLineNo">1395</span> // What to do then? Caching attempt fail? Need some changes in cacheBlock API?<a name="line.1395"></a>
-<span class="sourceLineNo">1396</span> while ((entry = queue.pollLast()) != null) {<a name="line.1396"></a>
-<span class="sourceLineNo">1397</span> BlockCacheKey blockCacheKey = entry.getKey();<a name="line.1397"></a>
-<span class="sourceLineNo">1398</span> BucketEntry be = entry.getValue();<a name="line.1398"></a>
-<span class="sourceLineNo">1399</span> if (evictBucketEntryIfNoRpcReferenced(blockCacheKey, be)) {<a name="line.1399"></a>
-<span class="sourceLineNo">1400</span> freedBytes += be.getLength();<a name="line.1400"></a>
-<span class="sourceLineNo">1401</span> }<a name="line.1401"></a>
-<span class="sourceLineNo">1402</span> if (freedBytes >= toFree) {<a name="line.1402"></a>
-<span class="sourceLineNo">1403</span> return freedBytes;<a name="line.1403"></a>
-<span class="sourceLineNo">1404</span> }<a name="line.1404"></a>
-<span class="sourceLineNo">1405</span> }<a name="line.1405"></a>
-<span class="sourceLineNo">1406</span> return freedBytes;<a name="line.1406"></a>
-<span class="sourceLineNo">1407</span> }<a name="line.1407"></a>
-<span class="sourceLineNo">1408</span><a name="line.1408"></a>
-<span class="sourceLineNo">1409</span> public long overflow() {<a name="line.1409"></a>
-<span class="sourceLineNo">1410</span> return totalSize - bucketSize;<a name="line.1410"></a>
+<span class="sourceLineNo">1380</span> private CachedEntryQueue queue;<a name="line.1380"></a>
+<span class="sourceLineNo">1381</span> private long totalSize = 0;<a name="line.1381"></a>
+<span class="sourceLineNo">1382</span> private long bucketSize;<a name="line.1382"></a>
+<span class="sourceLineNo">1383</span><a name="line.1383"></a>
+<span class="sourceLineNo">1384</span> public BucketEntryGroup(long bytesToFree, long blockSize, long bucketSize) {<a name="line.1384"></a>
+<span class="sourceLineNo">1385</span> this.bucketSize = bucketSize;<a name="line.1385"></a>
+<span class="sourceLineNo">1386</span> queue = new CachedEntryQueue(bytesToFree, blockSize);<a name="line.1386"></a>
+<span class="sourceLineNo">1387</span> totalSize = 0;<a name="line.1387"></a>
+<span class="sourceLineNo">1388</span> }<a name="line.1388"></a>
+<span class="sourceLineNo">1389</span><a name="line.1389"></a>
+<span class="sourceLineNo">1390</span> public void add(Map.Entry<BlockCacheKey, BucketEntry> block) {<a name="line.1390"></a>
+<span class="sourceLineNo">1391</span> totalSize += block.getValue().getLength();<a name="line.1391"></a>
+<span class="sourceLineNo">1392</span> queue.add(block);<a name="line.1392"></a>
+<span class="sourceLineNo">1393</span> }<a name="line.1393"></a>
+<span class="sourceLineNo">1394</span><a name="line.1394"></a>
+<span class="sourceLineNo">1395</span> public long free(long toFree) {<a name="line.1395"></a>
+<span class="sourceLineNo">1396</span> Map.Entry<BlockCacheKey, BucketEntry> entry;<a name="line.1396"></a>
+<span class="sourceLineNo">1397</span> long freedBytes = 0;<a name="line.1397"></a>
+<span class="sourceLineNo">1398</span> // TODO avoid a cycling siutation. We find no block which is not in use and so no way to free<a name="line.1398"></a>
+<span class="sourceLineNo">1399</span> // What to do then? Caching attempt fail? Need some changes in cacheBlock API?<a name="line.1399"></a>
+<span class="sourceLineNo">1400</span> while ((entry = queue.pollLast()) != null) {<a name="line.1400"></a>
+<span class="sourceLineNo">1401</span> BlockCacheKey blockCacheKey = entry.getKey();<a name="line.1401"></a>
+<span class="sourceLineNo">1402</span> BucketEntry be = entry.getValue();<a name="line.1402"></a>
+<span class="sourceLineNo">1403</span> if (evictBucketEntryIfNoRpcReferenced(blockCacheKey, be)) {<a name="line.1403"></a>
+<span class="sourceLineNo">1404</span> freedBytes += be.getLength();<a name="line.1404"></a>
+<span class="sourceLineNo">1405</span> }<a name="line.1405"></a>
+<span class="sourceLineNo">1406</span> if (freedBytes >= toFree) {<a name="line.1406"></a>
+<span class="sourceLineNo">1407</span> return freedBytes;<a name="line.1407"></a>
+<span class="sourceLineNo">1408</span> }<a name="line.1408"></a>
+<span class="sourceLineNo">1409</span> }<a name="line.1409"></a>
+<span class="sourceLineNo">1410</span> return freedBytes;<a name="line.1410"></a>
<span class="sourceLineNo">1411</span> }<a name="line.1411"></a>
<span class="sourceLineNo">1412</span><a name="line.1412"></a>
-<span class="sourceLineNo">1413</span> public long totalSize() {<a name="line.1413"></a>
-<span class="sourceLineNo">1414</span> return totalSize;<a name="line.1414"></a>
+<span class="sourceLineNo">1413</span> public long overflow() {<a name="line.1413"></a>
+<span class="sourceLineNo">1414</span> return totalSize - bucketSize;<a name="line.1414"></a>
<span class="sourceLineNo">1415</span> }<a name="line.1415"></a>
-<span class="sourceLineNo">1416</span> }<a name="line.1416"></a>
-<span class="sourceLineNo">1417</span><a name="line.1417"></a>
-<span class="sourceLineNo">1418</span> /**<a name="line.1418"></a>
-<span class="sourceLineNo">1419</span> * Block Entry stored in the memory with key,data and so on<a name="line.1419"></a>
-<span class="sourceLineNo">1420</span> */<a name="line.1420"></a>
-<span class="sourceLineNo">1421</span> static class RAMQueueEntry {<a name="line.1421"></a>
-<span class="sourceLineNo">1422</span> private final BlockCacheKey key;<a name="line.1422"></a>
-<span class="sourceLineNo">1423</span> private final Cacheable data;<a name="line.1423"></a>
-<span class="sourceLineNo">1424</span> private long accessCounter;<a name="line.1424"></a>
-<span class="sourceLineNo">1425</span> private boolean inMemory;<a name="line.1425"></a>
-<span class="sourceLineNo">1426</span><a name="line.1426"></a>
-<span class="sourceLineNo">1427</span> RAMQueueEntry(BlockCacheKey bck, Cacheable data, long accessCounter, boolean inMemory) {<a name="line.1427"></a>
-<span class="sourceLineNo">1428</span> this.key = bck;<a name="line.1428"></a>
-<span class="sourceLineNo">1429</span> this.data = data;<a name="line.1429"></a>
-<span class="sourceLineNo">1430</span> this.accessCounter = accessCounter;<a name="line.1430"></a>
-<span class="sourceLineNo">1431</span> this.inMemory = inMemory;<a name="line.1431"></a>
-<span class="sourceLineNo">1432</span> }<a name="line.1432"></a>
-<span class="sourceLineNo">1433</span><a name="line.1433"></a>
-<span class="sourceLineNo">1434</span> public Cacheable getData() {<a name="line.1434"></a>
-<span class="sourceLineNo">1435</span> return data;<a name="line.1435"></a>
+<span class="sourceLineNo">1416</span><a name="line.1416"></a>
+<span class="sourceLineNo">1417</span> public long totalSize() {<a name="line.1417"></a>
+<span class="sourceLineNo">1418</span> return totalSize;<a name="line.1418"></a>
+<span class="sourceLineNo">1419</span> }<a name="line.1419"></a>
+<span class="sourceLineNo">1420</span> }<a name="line.1420"></a>
+<span class="sourceLineNo">1421</span><a name="line.1421"></a>
+<span class="sourceLineNo">1422</span> /**<a name="line.1422"></a>
+<span class="sourceLineNo">1423</span> * Block Entry stored in the memory with key,data and so on<a name="line.1423"></a>
+<span class="sourceLineNo">1424</span> */<a name="line.1424"></a>
+<span class="sourceLineNo">1425</span> static class RAMQueueEntry {<a name="line.1425"></a>
+<span class="sourceLineNo">1426</span> private final BlockCacheKey key;<a name="line.1426"></a>
+<span class="sourceLineNo">1427</span> private final Cacheable data;<a name="line.1427"></a>
+<span class="sourceLineNo">1428</span> private long accessCounter;<a name="line.1428"></a>
+<span class="sourceLineNo">1429</span> private boolean inMemory;<a name="line.1429"></a>
+<span class="sourceLineNo">1430</span><a name="line.1430"></a>
+<span class="sourceLineNo">1431</span> RAMQueueEntry(BlockCacheKey bck, Cacheable data, long accessCounter, boolean inMemory) {<a name="line.1431"></a>
+<span class="sourceLineNo">1432</span> this.key = bck;<a name="line.1432"></a>
+<span class="sourceLineNo">1433</span> this.data = data;<a name="line.1433"></a>
+<span class="sourceLineNo">1434</span> this.accessCounter = accessCounter;<a name="line.1434"></a>
+<span class="sourceLineNo">1435</span> this.inMemory = inMemory;<a name="line.1435"></a>
<span class="sourceLineNo">1436</span> }<a name="line.1436"></a>
<span class="sourceLineNo">1437</span><a name="line.1437"></a>
-<span class="sourceLineNo">1438</span> public BlockCacheKey getKey() {<a name="line.1438"></a>
-<span class="sourceLineNo">1439</span> return key;<a name="line.1439"></a>
+<span class="sourceLineNo">1438</span> public Cacheable getData() {<a name="line.1438"></a>
+<span class="sourceLineNo">1439</span> return data;<a name="line.1439"></a>
<span class="sourceLineNo">1440</span> }<a name="line.1440"></a>
<span class="sourceLineNo">1441</span><a name="line.1441"></a>
-<span class="sourceLineNo">1442</span> public void access(long accessCounter) {<a name="line.1442"></a>
-<span class="sourceLineNo">1443</span> this.accessCounter = accessCounter;<a name="line.1443"></a>
+<span class="sourceLineNo">1442</span> public BlockCacheKey getKey() {<a name="line.1442"></a>
+<span class="sourceLineNo">1443</span> return key;<a name="line.1443"></a>
<span class="sourceLineNo">1444</span> }<a name="line.1444"></a>
<span class="sourceLineNo">1445</span><a name="line.1445"></a>
-<span class="sourceLineNo">1446</span> private ByteBuffAllocator getByteBuffAllocator() {<a name="line.1446"></a>
-<span class="sourceLineNo">1447</span> if (data instanceof HFileBlock) {<a name="line.1447"></a>
-<span class="sourceLineNo">1448</span> return ((HFileBlock) data).getByteBuffAllocator();<a name="line.1448"></a>
-<span class="sourceLineNo">1449</span> }<a name="line.1449"></a>
-<span class="sourceLineNo">1450</span> return ByteBuffAllocator.HEAP;<a name="line.1450"></a>
-<span class="sourceLineNo">1451</span> }<a name="line.1451"></a>
-<span class="sourceLineNo">1452</span><a name="line.1452"></a>
-<span class="sourceLineNo">1453</span> public BucketEntry writeToCache(final IOEngine ioEngine, final BucketAllocator alloc,<a name="line.1453"></a>
-<span class="sourceLineNo">1454</span> final LongAdder realCacheSize, Function<BucketEntry, Recycler> createRecycler)<a name="line.1454"></a>
-<span class="sourceLineNo">1455</span> throws IOException {<a name="line.1455"></a>
-<span class="sourceLineNo">1456</span> int len = data.getSerializedLength();<a name="line.1456"></a>
-<span class="sourceLineNo">1457</span> // This cacheable thing can't be serialized<a name="line.1457"></a>
-<span class="sourceLineNo">1458</span> if (len == 0) {<a name="line.1458"></a>
-<span class="sourceLineNo">1459</span> return null;<a name="line.1459"></a>
-<span class="sourceLineNo">1460</span> }<a name="line.1460"></a>
-<span class="sourceLineNo">1461</span> long offset = alloc.allocateBlock(len);<a name="line.1461"></a>
-<span class="sourceLineNo">1462</span> boolean succ = false;<a name="line.1462"></a>
-<span class="sourceLineNo">1463</span> BucketEntry bucketEntry = null;<a name="line.1463"></a>
-<span class="sourceLineNo">1464</span> try {<a name="line.1464"></a>
-<span class="sourceLineNo">1465</span> bucketEntry = new BucketEntry(offset, len, accessCounter, inMemory, createRecycler,<a name="line.1465"></a>
-<span class="sourceLineNo">1466</span> getByteBuffAllocator());<a name="line.1466"></a>
-<span class="sourceLineNo">1467</span> bucketEntry.setDeserializerReference(data.getDeserializer());<a name="line.1467"></a>
-<span class="sourceLineNo">1468</span> if (data instanceof HFileBlock) {<a name="line.1468"></a>
-<span class="sourceLineNo">1469</span> // If an instance of HFileBlock, save on some allocations.<a name="line.1469"></a>
-<span class="sourceLineNo">1470</span> HFileBlock block = (HFileBlock) data;<a name="line.1470"></a>
-<span class="sourceLineNo">1471</span> ByteBuff sliceBuf = block.getBufferReadOnly();<a name="line.1471"></a>
-<span class="sourceLineNo">1472</span> ByteBuffer metadata = block.getMetaData();<a name="line.1472"></a>
-<span class="sourceLineNo">1473</span> ioEngine.write(sliceBuf, offset);<a name="line.1473"></a>
-<span class="sourceLineNo">1474</span> ioEngine.write(metadata, offset + len - metadata.limit());<a name="line.1474"></a>
-<span class="sourceLineNo">1475</span> } else {<a name="line.1475"></a>
-<span class="sourceLineNo">1476</span> // Only used for testing.<a name="line.1476"></a>
-<span class="sourceLineNo">1477</span> ByteBuffer bb = ByteBuffer.allocate(len);<a name="line.1477"></a>
-<span class="sourceLineNo">1478</span> data.serialize(bb, true);<a name="line.1478"></a>
-<span class="sourceLineNo">1479</span> ioEngine.write(bb, offset);<a name="line.1479"></a>
-<span class="sourceLineNo">1480</span> }<a name="line.1480"></a>
-<span class="sourceLineNo">1481</span> succ = true;<a name="line.1481"></a>
-<span class="sourceLineNo">1482</span> } finally {<a name="line.1482"></a>
-<span class="sourceLineNo">1483</span> if (!succ) {<a name="line.1483"></a>
-<span class="sourceLineNo">1484</span> alloc.freeBlock(offset);<a name="line.1484"></a>
-<span class="sourceLineNo">1485</span> }<a name="line.1485"></a>
-<span class="sourceLineNo">1486</span> }<a name="line.1486"></a>
-<span class="sourceLineNo">1487</span> realCacheSize.add(len);<a name="line.1487"></a>
-<span class="sourceLineNo">1488</span> return bucketEntry;<a name="line.1488"></a>
-<span class="sourceLineNo">1489</span> }<a name="line.1489"></a>
-<span class="sourceLineNo">1490</span> }<a name="line.1490"></a>
-<span class="sourceLineNo">1491</span><a name="line.1491"></a>
-<span class="sourceLineNo">1492</span> /**<a name="line.1492"></a>
-<span class="sourceLineNo">1493</span> * Only used in test<a name="line.1493"></a>
-<span class="sourceLineNo">1494</span> * @throws InterruptedException<a name="line.1494"></a>
-<span class="sourceLineNo">1495</span> */<a name="line.1495"></a>
-<span class="sourceLineNo">1496</span> void stopWriterThreads() throws InterruptedException {<a name="line.1496"></a>
-<span class="sourceLineNo">1497</span> for (WriterThread writerThread : writerThreads) {<a name="line.1497"></a>
-<span class="sourceLineNo">1498</span> writerThread.disableWriter();<a name="line.1498"></a>
-<span class="sourceLineNo">1499</span> writerThread.interrupt();<a name="line.1499"></a>
-<span class="sourceLineNo">1500</span> writerThread.join();<a name="line.1500"></a>
-<span class="sourceLineNo">1501</span> }<a name="line.1501"></a>
-<span class="sourceLineNo">1502</span> }<a name="line.1502"></a>
-<span class="sourceLineNo">1503</span><a name="line.1503"></a>
-<span class="sourceLineNo">1504</span> @Override<a name="line.1504"></a>
-<span class="sourceLineNo">1505</span> public Iterator<CachedBlock> iterator() {<a name="line.1505"></a>
-<span class="sourceLineNo">1506</span> // Don't bother with ramcache since stuff is in here only a little while.<a name="line.1506"></a>
-<span class="sourceLineNo">1507</span> final Iterator<Map.Entry<BlockCacheKey, BucketEntry>> i =<a name="line.1507"></a>
-<span class="sourceLineNo">1508</span> this.backingMap.entrySet().iterator();<a name="line.1508"></a>
-<span class="sourceLineNo">1509</span> return new Iterator<CachedBlock>() {<a name="line.1509"></a>
-<span class="sourceLineNo">1510</span> private final long now = System.nanoTime();<a name="line.1510"></a>
-<span class="sourceLineNo">1511</span><a name="line.1511"></a>
-<span class="sourceLineNo">1512</span> @Override<a name="line.1512"></a>
-<span class="sourceLineNo">1513</span> public boolean hasNext() {<a name="line.1513"></a>
-<span class="sourceLineNo">1514</span> return i.hasNext();<a name="line.1514"></a>
-<span class="sourceLineNo">1515</span> }<a name="line.1515"></a>
-<span class="sourceLineNo">1516</span><a name="line.1516"></a>
-<span class="sourceLineNo">1517</span> @Override<a name="line.1517"></a>
-<span class="sourceLineNo">1518</span> public CachedBlock next() {<a name="line.1518"></a>
-<span class="sourceLineNo">1519</span> final Map.Entry<BlockCacheKey, BucketEntry> e = i.next();<a name="line.1519"></a>
-<span class="sourceLineNo">1520</span> return new CachedBlock() {<a name="line.1520"></a>
-<span class="sourceLineNo">1521</span> @Override<a name="line.1521"></a>
-<span class="sourceLineNo">1522</span> public String toString() {<a name="line.1522"></a>
-<span class="sourceLineNo">1523</span> return BlockCacheUtil.toString(this, now);<a name="line.1523"></a>
-<span class="sourceLineNo">1524</span> }<a name="line.1524"></a>
-<span class="sourceLineNo">1525</span><a name="line.1525"></a>
-<span class="sourceLineNo">1526</span> @Override<a name="line.1526"></a>
-<span class="sourceLineNo">1527</span> public BlockPriority getBlockPriority() {<a name="line.1527"></a>
-<span class="sourceLineNo">1528</span> return e.getValue().getPriority();<a name="line.1528"></a>
-<span class="sourceLineNo">1529</span> }<a name="line.1529"></a>
-<span class="sourceLineNo">1530</span><a name="line.1530"></a>
-<span class="sourceLineNo">1531</span> @Override<a name="line.1531"></a>
-<span class="sourceLineNo">1532</span> public BlockType getBlockType() {<a name="line.1532"></a>
-<span class="sourceLineNo">1533</span> // Not held by BucketEntry. Could add it if wanted on BucketEntry creation.<a name="line.1533"></a>
-<span class="sourceLineNo">1534</span> return null;<a name="line.1534"></a>
-<span class="sourceLineNo">1535</span> }<a name="line.1535"></a>
-<span class="sourceLineNo">1536</span><a name="line.1536"></a>
-<span class="sourceLineNo">1537</span> @Override<a name="line.1537"></a>
-<span class="sourceLineNo">1538</span> public long getOffset() {<a name="line.1538"></a>
-<span class="sourceLineNo">1539</span> return e.getKey().getOffset();<a name="line.1539"></a>
-<span class="sourceLineNo">1540</span> }<a name="line.1540"></a>
-<span class="sourceLineNo">1541</span><a name="line.1541"></a>
-<span class="sourceLineNo">1542</span> @Override<a name="line.1542"></a>
-<span class="sourceLineNo">1543</span> public long getSize() {<a name="line.1543"></a>
-<span class="sourceLineNo">1544</span> return e.getValue().getLength();<a name="line.1544"></a>
-<span class="sourceLineNo">1545</span> }<a name="line.1545"></a>
-<span class="sourceLineNo">1546</span><a name="line.1546"></a>
-<span class="sourceLineNo">1547</span> @Override<a name="line.1547"></a>
-<span class="sourceLineNo">1548</span> public long getCachedTime() {<a name="line.1548"></a>
-<span class="sourceLineNo">1549</span> return e.getValue().getCachedTime();<a name="line.1549"></a>
-<span class="sourceLineNo">1550</span> }<a name="line.1550"></a>
-<span class="sourceLineNo">1551</span><a name="line.1551"></a>
-<span class="sourceLineNo">1552</span> @Override<a name="line.1552"></a>
-<span class="sourceLineNo">1553</span> public String getFilename() {<a name="line.1553"></a>
-<span class="sourceLineNo">1554</span> return e.getKey().getHfileName();<a name="line.1554"></a>
-<span class="sourceLineNo">1555</span> }<a name="line.1555"></a>
-<span class="sourceLineNo">1556</span><a name="line.1556"></a>
-<span class="sourceLineNo">1557</span> @Override<a name="line.1557"></a>
-<span class="sourceLineNo">1558</span> public int compareTo(CachedBlock other) {<a name="line.1558"></a>
-<span class="sourceLineNo">1559</span> int diff = this.getFilename().compareTo(other.getFilename());<a name="line.1559"></a>
-<span class="sourceLineNo">1560</span> if (diff != 0) return diff;<a name="line.1560"></a>
-<span class="sourceLineNo">1561</span><a name="line.1561"></a>
-<span class="sourceLineNo">1562</span> diff = Long.compare(this.getOffset(), other.getOffset());<a name="line.1562"></a>
-<span class="sourceLineNo">1563</span> if (diff != 0) return diff;<a name="line.1563"></a>
-<span class="sourceLineNo">1564</span> if (other.getCachedTime() < 0 || this.getCachedTime() < 0) {<a name="line.1564"></a>
-<span class="sourceLineNo">1565</span> throw new IllegalStateException("" + this.getCachedTime() + ", " +<a name="line.1565"></a>
-<span class="sourceLineNo">1566</span> other.getCachedTime());<a name="line.1566"></a>
-<span class="sourceLineNo">1567</span> }<a name="line.1567"></a>
-<span class="sourceLineNo">1568</span> return Long.compare(other.getCachedTime(), this.getCachedTime());<a name="line.1568"></a>
-<span class="sourceLineNo">1569</span> }<a name="line.1569"></a>
-<span class="sourceLineNo">1570</span><a name="line.1570"></a>
-<span class="sourceLineNo">1571</span> @Override<a name="line.1571"></a>
-<span class="sourceLineNo">1572</span> public int hashCode() {<a name="line.1572"></a>
-<span class="sourceLineNo">1573</span> return e.getKey().hashCode();<a name="line.1573"></a>
-<span class="sourceLineNo">1574</span> }<a name="line.1574"></a>
-<span class="sourceLineNo">1575</span><a name="line.1575"></a>
-<span class="sourceLineNo">1576</span> @Override<a name="line.1576"></a>
-<span class="sourceLineNo">1577</span> public boolean equals(Object obj) {<a name="line.1577"></a>
-<span class="sourceLineNo">1578</span> if (obj instanceof CachedBlock) {<a name="line.1578"></a>
-<span class="sourceLineNo">1579</span> CachedBlock cb = (CachedBlock)obj;<a name="line.1579"></a>
-<span class="sourceLineNo">1580</span> return compareTo(cb) == 0;<a name="line.1580"></a>
-<span class="sourceLineNo">1581</span> } else {<a name="line.1581"></a>
-<span class="sourceLineNo">1582</span> return false;<a name="line.1582"></a>
-<span class="sourceLineNo">1583</span> }<a name="line.1583"></a>
-<span class="sourceLineNo">1584</span> }<a name="line.1584"></a>
-<span class="sourceLineNo">1585</span> };<a name="line.1585"></a>
-<span class="sourceLineNo">1586</span> }<a name="line.1586"></a>
-<span class="sourceLineNo">1587</span><a name="line.1587"></a>
-<span class="sourceLineNo">1588</span> @Override<a name="line.1588"></a>
-<span class="sourceLineNo">1589</span> public void remove() {<a name="line.1589"></a>
-<span class="sourceLineNo">1590</span> throw new UnsupportedOperationException();<a name="line.1590"></a>
-<span class="sourceLineNo">1591</span> }<a name="line.1591"></a>
-<span class="sourceLineNo">1592</span> };<a name="line.1592"></a>
-<span class="sourceLineNo">1593</span> }<a name="line.1593"></a>
-<span class="sourceLineNo">1594</span><a name="line.1594"></a>
-<span class="sourceLineNo">1595</span> @Override<a name="line.1595"></a>
-<span class="sourceLineNo">1596</span> public BlockCache[] getBlockCaches() {<a name="line.1596"></a>
-<span class="sourceLineNo">1597</span> return null;<a name="line.1597"></a>
-<span class="sourceLineNo">1598</span> }<a name="line.1598"></a>
-<span class="sourceLineNo">1599</span><a name="line.1599"></a>
-<span class="sourceLineNo">1600</span> public int getRpcRefCount(BlockCacheKey cacheKey) {<a name="line.1600"></a>
-<span class="sourceLineNo">1601</span> BucketEntry bucketEntry = backingMap.get(cacheKey);<a name="line.1601"></a>
-<span class="sourceLineNo">1602</span> if (bucketEntry != null) {<a name="line.1602"></a>
-<span class="sourceLineNo">1603</span> return bucketEntry.refCnt() - (bucketEntry.markedAsEvicted.get() ? 0 : 1);<a name="line.1603"></a>
-<span class="sourceLineNo">1604</span> }<a name="line.1604"></a>
-<span class="sourceLineNo">1605</span> return 0;<a name="line.1605"></a>
-<span class="sourceLineNo">1606</span> }<a name="line.1606"></a>
-<span class="sourceLineNo">1607</span><a name="line.1607"></a>
-<span class="sourceLineNo">1608</span> float getAcceptableFactor() {<a name="line.1608"></a>
-<span class="sourceLineNo">1609</span> return acceptableFactor;<a name="line.1609"></a>
+<span class="sourceLineNo">1446</span> public void access(long accessCounter) {<a name="line.1446"></a>
+<span class="sourceLineNo">1447</span> this.accessCounter = accessCounter;<a name="line.1447"></a>
+<span class="sourceLineNo">1448</span> }<a name="line.1448"></a>
+<span class="sourceLineNo">1449</span><a name="line.1449"></a>
+<span class="sourceLineNo">1450</span> private ByteBuffAllocator getByteBuffAllocator() {<a name="line.1450"></a>
+<span class="sourceLineNo">1451</span> if (data instanceof HFileBlock) {<a name="line.1451"></a>
+<span class="sourceLineNo">1452</span> return ((HFileBlock) data).getByteBuffAllocator();<a name="line.1452"></a>
+<span class="sourceLineNo">1453</span> }<a name="line.1453"></a>
+<span class="sourceLineNo">1454</span> return ByteBuffAllocator.HEAP;<a name="line.1454"></a>
+<span class="sourceLineNo">1455</span> }<a name="line.1455"></a>
+<span class="sourceLineNo">1456</span><a name="line.1456"></a>
+<span class="sourceLineNo">1457</span> public BucketEntry writeToCache(final IOEngine ioEngine, final BucketAllocator alloc,<a name="line.1457"></a>
+<span class="sourceLineNo">1458</span> final LongAdder realCacheSize, Function<BucketEntry, Recycler> createRecycler)<a name="line.1458"></a>
+<span class="sourceLineNo">1459</span> throws IOException {<a name="line.1459"></a>
+<span class="sourceLineNo">1460</span> int len = data.getSerializedLength();<a name="line.1460"></a>
+<span class="sourceLineNo">1461</span> // This cacheable thing can't be serialized<a name="line.1461"></a>
+<span class="sourceLineNo">1462</span> if (len == 0) {<a name="line.1462"></a>
+<span class="sourceLineNo">1463</span> return null;<a name="line.1463"></a>
+<span class="sourceLineNo">1464</span> }<a name="line.1464"></a>
+<span class="sourceLineNo">1465</span> long offset = alloc.allocateBlock(len);<a name="line.1465"></a>
+<span class="sourceLineNo">1466</span> boolean succ = false;<a name="line.1466"></a>
+<span class="sourceLineNo">1467</span> BucketEntry bucketEntry = null;<a name="line.1467"></a>
+<span class="sourceLineNo">1468</span> try {<a name="line.1468"></a>
+<span class="sourceLineNo">1469</span> bucketEntry = new BucketEntry(offset, len, accessCounter, inMemory, createRecycler,<a name="line.1469"></a>
+<span class="sourceLineNo">1470</span> getByteBuffAllocator());<a name="line.1470"></a>
+<span class="sourceLineNo">1471</span> bucketEntry.setDeserializerReference(data.getDeserializer());<a name="line.1471"></a>
+<span class="sourceLineNo">1472</span> if (data instanceof HFileBlock) {<a name="line.1472"></a>
+<span class="sourceLineNo">1473</span> // If an instance of HFileBlock, save on some allocations.<a name="line.1473"></a>
+<span class="sourceLineNo">1474</span> HFileBlock block = (HFileBlock) data;<a name="line.1474"></a>
+<span class="sourceLineNo">1475</span> ByteBuff sliceBuf = block.getBufferReadOnly();<a name="line.1475"></a>
+<span class="sourceLineNo">1476</span> ByteBuffer metadata = block.getMetaData();<a name="line.1476"></a>
+<span class="sourceLineNo">1477</span> ioEngine.write(sliceBuf, offset);<a name="line.1477"></a>
+<span class="sourceLineNo">1478</span> ioEngine.write(metadata, offset + len - metadata.limit());<a name="line.1478"></a>
+<span class="sourceLineNo">1479</span> } else {<a name="line.1479"></a>
+<span class="sourceLineNo">1480</span> // Only used for testing.<a name="line.1480"></a>
+<span class="sourceLineNo">1481</span> ByteBuffer bb = ByteBuffer.allocate(len);<a name="line.1481"></a>
+<span class="sourceLineNo">1482</span> data.serialize(bb, true);<a name="line.1482"></a>
+<span class="sourceLineNo">1483</span> ioEngine.write(bb, offset);<a name="line.1483"></a>
+<span class="sourceLineNo">1484</span> }<a name="line.1484"></a>
+<span class="sourceLineNo">1485</span> succ = true;<a name="line.1485"></a>
+<span class="sourceLineNo">1486</span> } finally {<a name="line.1486"></a>
+<span class="sourceLineNo">1487</span> if (!succ) {<a name="line.1487"></a>
+<span class="sourceLineNo">1488</span> alloc.freeBlock(offset);<a name="line.1488"></a>
+<span class="sourceLineNo">1489</span> }<a name="line.1489"></a>
+<span class="sourceLineNo">1490</span> }<a name="line.1490"></a>
+<span class="sourceLineNo">1491</span> realCacheSize.add(len);<a name="line.1491"></a>
+<span class="sourceLineNo">1492</span> return bucketEntry;<a name="line.1492"></a>
+<span class="sourceLineNo">1493</span> }<a name="line.1493"></a>
+<span class="sourceLineNo">1494</span> }<a name="line.1494"></a>
+<span class="sourceLineNo">1495</span><a name="line.1495"></a>
+<span class="sourceLineNo">1496</span> /**<a name="line.1496"></a>
+<span class="sourceLineNo">1497</span> * Only used in test<a name="line.1497"></a>
+<span class="sourceLineNo">1498</span> * @throws InterruptedException<a name="line.1498"></a>
+<span class="sourceLineNo">1499</span> */<a name="line.1499"></a>
+<span class="sourceLineNo">1500</span> void stopWriterThreads() throws InterruptedException {<a name="line.1500"></a>
+<span class="sourceLineNo">1501</span> for (WriterThread writerThread : writerThreads) {<a name="line.1501"></a>
+<span class="sourceLineNo">1502</span> writerThread.disableWriter();<a name="line.1502"></a>
+<span class="sourceLineNo">1503</span> writerThread.interrupt();<a name="line.1503"></a>
+<span class="sourceLineNo">1504</span> writerThread.join();<a name="line.1504"></a>
+<span class="sourceLineNo">1505</span> }<a name="line.1505"></a>
+<span class="sourceLineNo">1506</span> }<a name="line.1506"></a>
+<span class="sourceLineNo">1507</span><a name="line.1507"></a>
+<span class="sourceLineNo">1508</span> @Override<a name="line.1508"></a>
+<span class="sourceLineNo">1509</span> public Iterator<CachedBlock> iterator() {<a name="line.1509"></a>
+<span class="sourceLineNo">1510</span> // Don't bother with ramcache since stuff is in here only a little while.<a name="line.1510"></a>
+<span class="sourceLineNo">1511</span> final Iterator<Map.Entry<BlockCacheKey, BucketEntry>> i =<a name="line.1511"></a>
+<span class="sourceLineNo">1512</span> this.backingMap.entrySet().iterator();<a name="line.1512"></a>
+<span class="sourceLineNo">1513</span> return new Iterator<CachedBlock>() {<a name="line.1513"></a>
+<span class="sourceLineNo">1514</span> private final long now = System.nanoTime();<a name="line.1514"></a>
+<span class="sourceLineNo">1515</span><a name="line.1515"></a>
+<span class="sourceLineNo">1516</span> @Override<a name="line.1516"></a>
+<span class="sourceLineNo">1517</span> public boolean hasNext() {<a name="line.1517"></a>
+<span class="sourceLineNo">1518</span> return i.hasNext();<a name="line.1518"></a>
+<span class="sourceLineNo">1519</span> }<a name="line.1519"></a>
+<span class="sourceLineNo">1520</span><a name="line.1520"></a>
+<span class="sourceLineNo">1521</span> @Override<a name="line.1521"></a>
+<span class="sourceLineNo">1522</span> public CachedBlock next() {<a name="line.1522"></a>
+<span class="sourceLineNo">1523</span> final Map.Entry<BlockCacheKey, BucketEntry> e = i.next();<a name="line.1523"></a>
+<span class="sourceLineNo">1524</span> return new CachedBlock() {<a name="line.1524"></a>
+<span class="sourceLineNo">1525</span> @Override<a name="line.1525"></a>
+<span class="sourceLineNo">1526</span> public String toString() {<a name="line.1526"></a>
+<span class="sourceLineNo">1527</span> return BlockCacheUtil.toString(this, now);<a name="line.1527"></a>
+<span class="sourceLineNo">1528</span> }<a name="line.1528"></a>
+<span class="sourceLineNo">1529</span><a name="line.1529"></a>
+<span class="sourceLineNo">1530</span> @Override<a name="line.1530"></a>
+<span class="sourceLineNo">1531</span> public BlockPriority getBlockPriority() {<a name="line.1531"></a>
+<span class="sourceLineNo">1532</span> return e.getValue().getPriority();<a name="line.1532"></a>
+<span class="sourceLineNo">1533</span> }<a name="line.1533"></a>
+<span class="sourceLineNo">1534</span><a name="line.1534"></a>
+<span class="sourceLineNo">1535</span> @Override<a name="line.1535"></a>
+<span class="sourceLineNo">1536</span> public BlockType getBlockType() {<a name="line.1536"></a>
+<span class="sourceLineNo">1537</span> // Not held by BucketEntry. Could add it if wanted on BucketEntry creation.<a name="line.1537"></a>
+<span class="sourceLineNo">1538</span> return null;<a name="line.1538"></a>
+<span class="sourceLineNo">1539</span> }<a name="line.1539"></a>
+<span class="sourceLineNo">1540</span><a name="line.1540"></a>
+<span class="sourceLineNo">1541</span> @Override<a name="line.1541"></a>
+<span class="sourceLineNo">1542</span> public long getOffset() {<a name="line.1542"></a>
+<span class="sourceLineNo">1543</span> return e.getKey().getOffset();<a name="line.1543"></a>
+<span class="sourceLineNo">1544</span> }<a name="line.1544"></a>
+<span class="sourceLineNo">1545</span><a name="line.1545"></a>
+<span class="sourceLineNo">1546</span> @Override<a name="line.1546"></a>
+<span class="sourceLineNo">1547</span> public long getSize() {<a name="line.1547"></a>
+<span class="sourceLineNo">1548</span> return e.getValue().getLength();<a name="line.1548"></a>
+<span class="sourceLineNo">1549</span> }<a name="line.1549"></a>
+<span class="sourceLineNo">1550</span><a name="line.1550"></a>
+<span class="sourceLineNo">1551</span> @Override<a name="line.1551"></a>
+<span class="sourceLineNo">1552</span> public long getCachedTime() {<a name="line.1552"></a>
+<span class="sourceLineNo">1553</span> return e.getValue().getCachedTime();<a name="line.1553"></a>
+<span class="sourceLineNo">1554</span> }<a name="line.1554"></a>
+<span class="sourceLineNo">1555</span><a name="line.1555"></a>
+<span class="sourceLineNo">1556</span> @Override<a name="line.1556"></a>
+<span class="sourceLineNo">1557</span> public String getFilename() {<a name="line.1557"></a>
+<span class="sourceLineNo">1558</span> return e.getKey().getHfileName();<a name="line.1558"></a>
+<span class="sourceLineNo">1559</span> }<a name="line.1559"></a>
+<span class="sourceLineNo">1560</span><a name="line.1560"></a>
+<span class="sourceLineNo">1561</span> @Override<a name="line.1561"></a>
+<span class="sourceLineNo">1562</span> public int compareTo(CachedBlock other) {<a name="line.1562"></a>
+<span class="sourceLineNo">1563</span> int diff = this.getFilename().compareTo(other.getFilename());<a name="line.1563"></a>
+<span class="sourceLineNo">1564</span> if (diff != 0) return diff;<a name="line.1564"></a>
+<span class="sourceLineNo">1565</span><a name="line.1565"></a>
+<span class="sourceLineNo">1566</span> diff = Long.compare(this.getOffset(), other.getOffset());<a name="line.1566"></a>
+<span class="sourceLineNo">1567</span> if (diff != 0) return diff;<a name="line.1567"></a>
+<span class="sourceLineNo">1568</span> if (other.getCachedTime() < 0 || this.getCachedTime() < 0) {<a name="line.1568"></a>
+<span class="sourceLineNo">1569</span> throw new IllegalStateException("" + this.getCachedTime() + ", " +<a name="line.1569"></a>
+<span class="sourceLineNo">1570</span> other.getCachedTime());<a name="line.1570"></a>
+<span class="sourceLineNo">1571</span> }<a name="line.1571"></a>
+<span class="sourceLineNo">1572</span> return Long.compare(other.getCachedTime(), this.getCachedTime());<a name="line.1572"></a>
+<span class="sourceLineNo">1573</span> }<a name="line.1573"></a>
+<span class="sourceLineNo">1574</span><a name="line.1574"></a>
+<span class="sourceLineNo">1575</span> @Override<a name="line.1575"></a>
+<span class="sourceLineNo">1576</span> public int hashCode() {<a name="line.1576"></a>
+<span class="sourceLineNo">1577</span> return e.getKey().hashCode();<a name="line.1577"></a>
+<span class="sourceLineNo">1578</span> }<a name="line.1578"></a>
+<span class="sourceLineNo">1579</span><a name="line.1579"></a>
+<span class="sourceLineNo">1580</span> @Override<a name="line.1580"></a>
+<span class="sourceLineNo">1581</span> public boolean equals(Object obj) {<a name="line.1581"></a>
+<span class="sourceLineNo">1582</span> if (obj instanceof CachedBlock) {<a name="line.1582"></a>
+<span class="sourceLineNo">1583</span> CachedBlock cb = (CachedBlock)obj;<a name="line.1583"></a>
+<span class="sourceLineNo">1584</span> return compareTo(cb) == 0;<a name="line.1584"></a>
+<span class="sourceLineNo">1585</span> } else {<a name="line.1585"></a>
+<span class="sourceLineNo">1586</span> return false;<a name="line.1586"></a>
+<span class="sourceLineNo">1587</span> }<a name="line.1587"></a>
+<span class="sourceLineNo">1588</span> }<a name="line.1588"></a>
+<span class="sourceLineNo">1589</span> };<a name="line.1589"></a>
+<span class="sourceLineNo">1590</span> }<a name="line.1590"></a>
+<span class="sourceLineNo">1591</span><a name="line.1591"></a>
+<span class="sourceLineNo">1592</span> @Override<a name="line.1592"></a>
+<span class="sourceLineNo">1593</span> public void remove() {<a name="line.1593"></a>
+<span class="sourceLineNo">1594</span> throw new UnsupportedOperationException();<a name="line.1594"></a>
+<span class="sourceLineNo">1595</span> }<a name="line.1595"></a>
+<span class="sourceLineNo">1596</span> };<a name="line.1596"></a>
+<span class="sourceLineNo">1597</span> }<a name="line.1597"></a>
+<span class="sourceLineNo">1598</span><a name="line.1598"></a>
+<span class="sourceLineNo">1599</span> @Override<a name="line.1599"></a>
+<span class="sourceLineNo">1600</span> public BlockCache[] getBlockCaches() {<a name="line.1600"></a>
+<span class="sourceLineNo">1601</span> return null;<a name="line.1601"></a>
+<span class="sourceLineNo">1602</span> }<a name="line.1602"></a>
+<span class="sourceLineNo">1603</span><a name="line.1603"></a>
+<span class="sourceLineNo">1604</span> public int getRpcRefCount(BlockCacheKey cacheKey) {<a name="line.1604"></a>
+<span class="sourceLineNo">1605</span> BucketEntry bucketEntry = backingMap.get(cacheKey);<a name="line.1605"></a>
+<span class="sourceLineNo">1606</span> if (bucketEntry != null) {<a name="line.1606"></a>
+<span class="sourceLineNo">1607</span> return bucketEntry.refCnt() - (bucketEntry.markedAsEvicted.get() ? 0 : 1);<a name="line.1607"></a>
+<span class="sourceLineNo">1608</span> }<a name="line.1608"></a>
+<span class="sourceLineNo">1609</span> return 0;<a name="line.1609"></a>
<span class="sourceLineNo">1610</span> }<a name="line.1610"></a>
<span class="sourceLineNo">1611</span><a name="line.1611"></a>
-<span class="sourceLineNo">1612</span> float getMinFactor() {<a name="line.1612"></a>
-<span class="sourceLineNo">1613</span> return minFactor;<a name="line.1613"></a>
+<span class="sourceLineNo">1612</span> float getAcceptableFactor() {<a name="line.1612"></a>
+<span class="sourceLineNo">1613</span> return acceptableFactor;<a name="line.1613"></a>
<span class="sourceLineNo">1614</span> }<a name="line.1614"></a>
<span class="sourceLineNo">1615</span><a name="line.1615"></a>
-<span class="sourceLineNo">1616</span> float getExtraFreeFactor() {<a name="line.1616"></a>
-<span class="sourceLineNo">1617</span> return extraFreeFactor;<a name="line.1617"></a>
+<span class="sourceLineNo">1616</span> float getMinFactor() {<a name="line.1616"></a>
+<span class="sourceLineNo">1617</span> return minFactor;<a name="line.1617"></a>
<span class="sourceLineNo">1618</span> }<a name="line.1618"></a>
<span class="sourceLineNo">1619</span><a name="line.1619"></a>
-<span class="sourceLineNo">1620</span> float getSingleFactor() {<a name="line.1620"></a>
-<span class="sourceLineNo">1621</span> return singleFactor;<a name="line.1621"></a>
+<span class="sourceLineNo">1620</span> float getExtraFreeFactor() {<a name="line.1620"></a>
+<span class="sourceLineNo">1621</span> return extraFreeFactor;<a name="line.1621"></a>
<span class="sourceLineNo">1622</span> }<a name="line.1622"></a>
<span class="sourceLineNo">1623</span><a name="line.1623"></a>
-<span class="sourceLineNo">1624</span> float getMultiFactor() {<a name="line.1624"></a>
-<span class="sourceLineNo">1625</span> return multiFactor;<a name="line.1625"></a>
+<span class="sourceLineNo">1624</span> float getSingleFactor() {<a name="line.1624"></a>
+<span class="sourceLineNo">1625</span> return singleFactor;<a name="line.1625"></a>
<span class="sourceLineNo">1626</span> }<a name="line.1626"></a>
<span class="sourceLineNo">1627</span><a name="line.1627"></a>
-<span class="sourceLineNo">1628</span> float getMemoryFactor() {<a name="line.1628"></a>
-<span class="sourceLineNo">1629</span> return memoryFactor;<a name="line.1629"></a>
+<span class="sourceLineNo">1628</span> float getMultiFactor() {<a name="line.1628"></a>
+<span class="sourceLineNo">1629</span> return multiFactor;<a name="line.1629"></a>
<span class="sourceLineNo">1630</span> }<a name="line.1630"></a>
<span class="sourceLineNo">1631</span><a name="line.1631"></a>
-<span class="sourceLineNo">1632</span> /**<a name="line.1632"></a>
-<span class="sourceLineNo">1633</span> * Wrapped the delegate ConcurrentMap with maintaining its block's reference count.<a name="line.1633"></a>
-<span class="sourceLineNo">1634</span> */<a name="line.1634"></a>
-<span class="sourceLineNo">1635</span> static class RAMCache {<a name="line.1635"></a>
-<span class="sourceLineNo">1636</span> /**<a name="line.1636"></a>
-<span class="sourceLineNo">1637</span> * Defined the map as {@link ConcurrentHashMap} explicitly here, because in<a name="line.1637"></a>
-<span class="sourceLineNo">1638</span> * {@link RAMCache#get(BlockCacheKey)} and<a name="line.1638"></a>
-<span class="sourceLineNo">1639</span> * {@link RAMCache#putIfAbsent(BlockCacheKey, BucketCache.RAMQueueEntry)} , we need to<a name="line.1639"></a>
-<span class="sourceLineNo">1640</span> * guarantee the atomicity of map#computeIfPresent(key, func) and map#putIfAbsent(key, func).<a name="line.1640"></a>
-<span class="sourceLineNo">1641</span> * Besides, the func method can execute exactly once only when the key is present(or absent)<a name="line.1641"></a>
-<span class="sourceLineNo">1642</span> * and under the lock context. Otherwise, the reference count of block will be messed up.<a name="line.1642"></a>
-<span class="sourceLineNo">1643</span> * Notice that the {@link java.util.concurrent.ConcurrentSkipListMap} can not guarantee that.<a name="line.1643"></a>
-<span class="sourceLineNo">1644</span> */<a name="line.1644"></a>
-<span class="sourceLineNo">1645</span> final ConcurrentHashMap<BlockCacheKey, RAMQueueEntry> delegate = new ConcurrentHashMap<>();<a name="line.1645"></a>
-<span class="sourceLineNo">1646</span><a name="line.1646"></a>
-<span class="sourceLineNo">1647</span> public boolean containsKey(BlockCacheKey key) {<a name="line.1647"></a>
-<span class="sourceLineNo">1648</span> return delegate.containsKey(key);<a name="line.1648"></a>
-<span class="sourceLineNo">1649</span> }<a name="line.1649"></a>
+<span class="sourceLineNo">1632</span> float getMemoryFactor() {<a name="line.1632"></a>
+<span class="sourceLineNo">1633</span> return memoryFactor;<a name="line.1633"></a>
+<span class="sourceLineNo">1634</span> }<a name="line.1634"></a>
+<span class="sourceLineNo">1635</span><a name="line.1635"></a>
+<span class="sourceLineNo">1636</span> /**<a name="line.1636"></a>
+<span class="sourceLineNo">1637</span> * Wrapped the delegate ConcurrentMap with maintaining its block's reference count.<a name="line.1637"></a>
+<span class="sourceLineNo">1638</span> */<a name="line.1638"></a>
+<span class="sourceLineNo">1639</span> static class RAMCache {<a name="line.1639"></a>
+<span class="sourceLineNo">1640</span> /**<a name="line.1640"></a>
+<span class="sourceLineNo">1641</span> * Defined the map as {@link ConcurrentHashMap} explicitly here, because in<a name="line.1641"></a>
+<span class="sourceLineNo">1642</span> * {@link RAMCache#get(BlockCacheKey)} and<a name="line.1642"></a>
+<span class="sourceLineNo">1643</span> * {@link RAMCache#putIfAbsent(BlockCacheKey, BucketCache.RAMQueueEntry)} , we need to<a name="line.1643"></a>
+<span class="sourceLineNo">1644</span> * guarantee the atomicity of map#computeIfPresent(key, func) and map#putIfAbsent(key, func).<a name="line.1644"></a>
+<span class="sourceLineNo">1645</span> * Besides, the func method can execute exactly once only when the key is present(or absent)<a name="line.1645"></a>
+<span class="sourceLineNo">1646</span> * and under the lock context. Otherwise, the reference count of block will be messed up.<a name="line.1646"></a>
+<span class="sourceLineNo">1647</span> * Notice that the {@link java.util.concurrent.ConcurrentSkipListMap} can not guarantee that.<a name="line.1647"></a>
+<span class="sourceLineNo">1648</span> */<a name="line.1648"></a>
+<span class="sourceLineNo">1649</span> final ConcurrentHashMap<BlockCacheKey, RAMQueueEntry> delegate = new ConcurrentHashMap<>();<a name="line.1649"></a>
<span class="sourceLineNo">1650</span><a name="line.1650"></a>
-<span class="sourceLineNo">1651</span> public RAMQueueEntry get(BlockCacheKey key) {<a name="line.1651"></a>
-<span class="sourceLineNo">1652</span> return delegate.computeIfPresent(key, (k, re) -> {<a name="line.1652"></a>
-<span class="sourceLineNo">1653</span> // It'll be referenced by RPC, so retain atomically here. if the get and retain is not<a name="line.1653"></a>
-<span class="sourceLineNo">1654</span> // atomic, another thread may remove and release the block, when retaining in this thread we<a name="line.1654"></a>
-<span class="sourceLineNo">1655</span> // may retain a block with refCnt=0 which is disallowed. (see HBASE-22422)<a name="line.1655"></a>
-<span class="sourceLineNo">1656</span> re.getData().retain();<a name="line.1656"></a>
-<span class="sourceLineNo">1657</span> return re;<a name="line.1657"></a>
-<span class="sourceLineNo">1658</span> });<a name="line.1658"></a>
-<span class="sourceLineNo">1659</span> }<a name="line.1659"></a>
-<span class="sourceLineNo">1660</span><a name="line.1660"></a>
-<span class="sourceLineNo">1661</span> /**<a name="line.1661"></a>
-<span class="sourceLineNo">1662</span> * Return the previous associated value, or null if absent. It has the same meaning as<a name="line.1662"></a>
-<span class="sourceLineNo">1663</span> * {@link ConcurrentMap#putIfAbsent(Object, Object)}<a name="line.1663"></a>
-<span class="sourceLineNo">1664</span> */<a name="line.1664"></a>
-<span class="sourceLineNo">1665</span> public RAMQueueEntry putIfAbsent(BlockCacheKey key, RAMQueueEntry entry) {<a name="line.1665"></a>
-<span class="sourceLineNo">1666</span> AtomicBoolean absent = new AtomicBoolean(false);<a name="line.1666"></a>
-<span class="sourceLineNo">1667</span> RAMQueueEntry re = delegate.computeIfAbsent(key, k -> {<a name="line.1667"></a>
-<span class="sourceLineNo">1668</span> // The RAMCache reference to this entry, so reference count should be increment.<a name="line.1668"></a>
-<span class="sourceLineNo">1669</span> entry.getData().retain();<a name="line.1669"></a>
-<span class="sourceLineNo">1670</span> absent.set(true);<a name="line.1670"></a>
-<span class="sourceLineNo">1671</span> return entry;<a name="line.1671"></a>
-<span class="sourceLineNo">1672</span> });<a name="line.1672"></a>
-<span class="sourceLineNo">1673</span> return absent.get() ? null : re;<a name="line.1673"></a>
-<span class="sourceLineNo">1674</span> }<a name="line.1674"></a>
-<span class="sourceLineNo">1675</span><a name="line.1675"></a>
-<span class="sourceLineNo">1676</span> public boolean remove(BlockCacheKey key) {<a name="line.1676"></a>
-<span class="sourceLineNo">1677</span> return remove(key, re->{});<a name="line.1677"></a>
+<span class="sourceLineNo">1651</span> public boolean containsKey(BlockCacheKey key) {<a name="line.1651"></a>
+<span class="sourceLineNo">1652</span> return delegate.containsKey(key);<a name="line.1652"></a>
+<span class="sourceLineNo">1653</span> }<a name="line.1653"></a>
+<span class="sourceLineNo">1654</span><a name="line.1654"></a>
+<span class="sourceLineNo">1655</span> public RAMQueueEntry get(BlockCacheKey key) {<a name="line.1655"></a>
+<span class="sourceLineNo">1656</span> return delegate.computeIfPresent(key, (k, re) -> {<a name="line.1656"></a>
+<span class="sourceLineNo">1657</span> // It'll be referenced by RPC, so retain atomically here. if the get and retain is not<a name="line.1657"></a>
+<span class="sourceLineNo">1658</span> // atomic, another thread may remove and release the block, when retaining in this thread we<a name="line.1658"></a>
+<span class="sourceLineNo">1659</span> // may retain a block with refCnt=0 which is disallowed. (see HBASE-22422)<a name="line.1659"></a>
+<span class="sourceLineNo">1660</span> re.getData().retain();<a name="line.1660"></a>
+<span class="sourceLineNo">1661</span> return re;<a name="line.1661"></a>
+<span class="sourceLineNo">1662</span> });<a name="line.1662"></a>
+<span class="sourceLineNo">1663</span> }<a name="line.1663"></a>
+<span class="sourceLineNo">1664</span><a name="line.1664"></a>
+<span class="sourceLineNo">1665</span> /**<a name="line.1665"></a>
+<span class="sourceLineNo">1666</span> * Return the previous associated value, or null if absent. It has the same meaning as<a name="line.1666"></a>
+<span class="sourceLineNo">1667</span> * {@link ConcurrentMap#putIfAbsent(Object, Object)}<a name="line.1667"></a>
+<span class="sourceLineNo">1668</span> */<a name="line.1668"></a>
+<span class="sourceLineNo">1669</span> public RAMQueueEntry putIfAbsent(BlockCacheKey key, RAMQueueEntry entry) {<a name="line.1669"></a>
+<span class="sourceLineNo">1670</span> AtomicBoolean absent = new AtomicBoolean(false);<a name="line.1670"></a>
+<span class="sourceLineNo">1671</span> RAMQueueEntry re = delegate.computeIfAbsent(key, k -> {<a name="line.1671"></a>
+<span class="sourceLineNo">1672</span> // The RAMCache reference to this entry, so reference count should be increment.<a name="line.1672"></a>
+<span class="sourceLineNo">1673</span> entry.getData().retain();<a name="line.1673"></a>
+<span class="sourceLineNo">1674</span> absent.set(true);<a name="line.1674"></a>
+<span class="sourceLineNo">1675</span> return entry;<a name="line.1675"></a>
+<span class="sourceLineNo">1676</span> });<a name="line.1676"></a>
+<span class="sourceLineNo">1677</span> return absent.get() ? null : re;<a name="line.1677"></a>
<span class="sourceLineNo">1678</span> }<a name="line.1678"></a>
<span class="sourceLineNo">1679</span><a name="line.1679"></a>
-<span class="sourceLineNo">1680</span> /**<a name="line.1680"></a>
-<span class="sourceLineNo">1681</span> * Defined an {@link Consumer} here, because once the removed entry release its reference count,<a name="line.1681"></a>
-<span class="sourceLineNo">1682</span> * then it's ByteBuffers may be recycled and accessing it outside this method will be thrown an<a name="line.1682"></a>
-<span class="sourceLineNo">1683</span> * exception. the consumer will access entry to remove before release its reference count.<a name="line.1683"></a>
-<span class="sourceLineNo">1684</span> * Notice, don't change its reference count in the {@link Consumer}<a name="line.1684"></a>
-<span class="sourceLineNo">1685</span> */<a name="line.1685"></a>
-<span class="sourceLineNo">1686</span> public boolean remove(BlockCacheKey key, Consumer<RAMQueueEntry> action) {<a name="line.1686"></a>
-<span class="sourceLineNo">1687</span> RAMQueueEntry previous = delegate.remove(key);<a name="line.1687"></a>
-<span class="sourceLineNo">1688</span> action.accept(previous);<a name="line.1688"></a>
-<span class="sourceLineNo">1689</span> if (previous != null) {<a name="line.1689"></a>
-<span class="sourceLineNo">1690</span> previous.getData().release();<a name="line.1690"></a>
-<span class="sourceLineNo">1691</span> }<a name="line.1691"></a>
-<span class="sourceLineNo">1692</span> return previous != null;<a name="line.1692"></a>
-<span class="sourceLineNo">1693</span> }<a name="line.1693"></a>
-<span class="sourceLineNo">1694</span><a name="line.1694"></a>
-<span class="sourceLineNo">1695</span> public boolean isEmpty() {<a name="line.1695"></a>
-<span class="sourceLineNo">1696</span> return delegate.isEmpty();<a name="line.1696"></a>
+<span class="sourceLineNo">1680</span> public boolean remove(BlockCacheKey key) {<a name="line.1680"></a>
+<span class="sourceLineNo">1681</span> return remove(key, re->{});<a name="line.1681"></a>
+<span class="sourceLineNo">1682</span> }<a name="line.1682"></a>
+<span class="sourceLineNo">1683</span><a name="line.1683"></a>
+<span class="sourceLineNo">1684</span> /**<a name="line.1684"></a>
+<span class="sourceLineNo">1685</span> * Defined an {@link Consumer} here, because once the removed entry release its reference count,<a name="line.1685"></a>
+<span class="sourceLineNo">1686</span> * then it's ByteBuffers may be recycled and accessing it outside this method will be thrown an<a name="line.1686"></a>
+<span class="sourceLineNo">1687</span> * exception. the consumer will access entry to remove before release its reference count.<a name="line.1687"></a>
+<span class="sourceLineNo">1688</span> * Notice, don't change its reference count in the {@link Consumer}<a name="line.1688"></a>
+<span class="sourceLineNo">1689</span> */<a name="line.1689"></a>
+<span class="sourceLineNo">1690</span> public boolean remove(BlockCacheKey key, Consumer<RAMQueueEntry> action) {<a name="line.1690"></a>
+<span class="sourceLineNo">1691</span> RAMQueueEntry previous = delegate.remove(key);<a name="line.1691"></a>
+<span class="sourceLineNo">1692</span> action.accept(previous);<a name="line.1692"></a>
+<span class="sourceLineNo">1693</span> if (previous != null) {<a name="line.1693"></a>
+<span class="sourceLineNo">1694</span> previous.getData().release();<a name="line.1694"></a>
+<span class="sourceLineNo">1695</span> }<a name="line.1695"></a>
+<span class="sourceLineNo">1696</span> return previous != null;<a name="line.1696"></a>
<span class="sourceLineNo">1697</span> }<a name="line.1697"></a>
<span class="sourceLineNo">1698</span><a name="line.1698"></a>
-<span class="sourceLineNo">1699</span> public void clear() {<a name="line.1699"></a>
-<span class="sourceLineNo">1700</span> Iterator<Map.Entry<BlockCacheKey, RAMQueueEntry>> it = delegate.entrySet().iterator();<a name="line.1700"></a>
-<span class="sourceLineNo">1701</span> while (it.hasNext()) {<a name="line.1701"></a>
-<span class="sourceLineNo">1702</span> RAMQueueEntry re = it.next().getValue();<a name="line.1702"></a>
-<span class="sourceLineNo">1703</span> it.remove();<a name="line.1703"></a>
-<span class="sourceLineNo">1704</span> re.getData().release();<a name="line.1704"></a>
-<span class="sourceLineNo">1705</span> }<a name="line.1705"></a>
-<span class="sourceLineNo">1706</span> }<a name="line.1706"></a>
-<span class="sourceLineNo">1707</span> }<a name="line.1707"></a>
-<span class="sourceLineNo">1708</span>}<a name="line.1708"></a>
+<span class="sourceLineNo">1699</span> public boolean isEmpty() {<a name="line.1699"></a>
+<span class="sourceLineNo">1700</span> return delegate.isEmpty();<a name="line.1700"></a>
+<span class="sourceLineNo">1701</span> }<a name="line.1701"></a>
+<span class="sourceLineNo">1702</span><a name="line.1702"></a>
+<span class="sourceLineNo">1703</span> public void clear() {<a name="line.1703"></a>
+<span class="sourceLineNo">1704</span> Iterator<Map.Entry<BlockCacheKey, RAMQueueEntry>> it = delegate.entrySet().iterator();<a name="line.1704"></a>
+<span class="sourceLineNo">1705</span> while (it.hasNext()) {<a name="line.1705"></a>
+<span class="sourceLineNo">1706</span> RAMQueueEntry re = it.next().getValue();<a name="line.1706"></a>
+<span class="sourceLineNo">1707</span> it.remove();<a name="line.1707"></a>
+<span class="sourceLineNo">1708</span> re.getData().release();<a name="line.1708"></a>
+<span class="sourceLineNo">1709</span> }<a name="line.1709"></a>
+<span class="sourceLineNo">1710</span> }<a name="line.1710"></a>
+<span class="sourceLineNo">1711</span> }<a name="line.1711"></a>
+<span class="sourceLineNo">1712</span>}<a name="line.1712"></a>
diff --git a/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html b/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html
index 6e1ac11..5d27f48 100644
--- a/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html
+++ b/devapidocs/src-html/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.StatisticsThread.html
@@ -494,1226 +494,1230 @@
<span class="sourceLineNo">486</span> } else {<a name="line.486"></a>
<span class="sourceLineNo">487</span> this.blockNumber.increment();<a name="line.487"></a>
<span class="sourceLineNo">488</span> this.heapSize.add(cachedItem.heapSize());<a name="line.488"></a>
-<span class="sourceLineNo">489</span> blocksByHFile.add(cacheKey);<a name="line.489"></a>
-<span class="sourceLineNo">490</span> }<a name="line.490"></a>
-<span class="sourceLineNo">491</span> }<a name="line.491"></a>
-<span class="sourceLineNo">492</span><a name="line.492"></a>
-<span class="sourceLineNo">493</span> /**<a name="line.493"></a>
-<span class="sourceLineNo">494</span> * Get the buffer of the block with the specified key.<a name="line.494"></a>
-<span class="sourceLineNo">495</span> * @param key block's cache key<a name="line.495"></a>
-<span class="sourceLineNo">496</span> * @param caching true if the caller caches blocks on cache misses<a name="line.496"></a>
-<span class="sourceLineNo">497</span> * @param repeat Whether this is a repeat lookup for the same block<a name="line.497"></a>
-<span class="sourceLineNo">498</span> * @param updateCacheMetrics Whether we should update cache metrics or not<a name="line.498"></a>
-<span class="sourceLineNo">499</span> * @return buffer of specified cache key, or null if not in cache<a name="line.499"></a>
-<span class="sourceLineNo">500</span> */<a name="line.500"></a>
-<span class="sourceLineNo">501</span> @Override<a name="line.501"></a>
-<span class="sourceLineNo">502</span> public Cacheable getBlock(BlockCacheKey key, boolean caching, boolean repeat,<a name="line.502"></a>
-<span class="sourceLineNo">503</span> boolean updateCacheMetrics) {<a name="line.503"></a>
-<span class="sourceLineNo">504</span> if (!cacheEnabled) {<a name="line.504"></a>
-<span class="sourceLineNo">505</span> return null;<a name="line.505"></a>
-<span class="sourceLineNo">506</span> }<a name="line.506"></a>
-<span class="sourceLineNo">507</span> RAMQueueEntry re = ramCache.get(key);<a name="line.507"></a>
-<span class="sourceLineNo">508</span> if (re != null) {<a name="line.508"></a>
-<span class="sourceLineNo">509</span> if (updateCacheMetrics) {<a name="line.509"></a>
-<span class="sourceLineNo">510</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.510"></a>
-<span class="sourceLineNo">511</span> }<a name="line.511"></a>
-<span class="sourceLineNo">512</span> re.access(accessCount.incrementAndGet());<a name="line.512"></a>
-<span class="sourceLineNo">513</span> return re.getData();<a name="line.513"></a>
-<span class="sourceLineNo">514</span> }<a name="line.514"></a>
-<span class="sourceLineNo">515</span> BucketEntry bucketEntry = backingMap.get(key);<a name="line.515"></a>
-<span class="sourceLineNo">516</span> if (bucketEntry != null) {<a name="line.516"></a>
-<span class="sourceLineNo">517</span> long start = System.nanoTime();<a name="line.517"></a>
-<span class="sourceLineNo">518</span> ReentrantReadWriteLock lock = offsetLock.getLock(bucketEntry.offset());<a name="line.518"></a>
-<span class="sourceLineNo">519</span> try {<a name="line.519"></a>
-<span class="sourceLineNo">520</span> lock.readLock().lock();<a name="line.520"></a>
-<span class="sourceLineNo">521</span> // We can not read here even if backingMap does contain the given key because its offset<a name="line.521"></a>
-<span class="sourceLineNo">522</span> // maybe changed. If we lock BlockCacheKey instead of offset, then we can only check<a name="line.522"></a>
-<span class="sourceLineNo">523</span> // existence here.<a name="line.523"></a>
-<span class="sourceLineNo">524</span> if (bucketEntry.equals(backingMap.get(key))) {<a name="line.524"></a>
-<span class="sourceLineNo">525</span> // Read the block from IOEngine based on the bucketEntry's offset and length, NOTICE: the<a name="line.525"></a>
-<span class="sourceLineNo">526</span> // block will use the refCnt of bucketEntry, which means if two HFileBlock mapping to<a name="line.526"></a>
-<span class="sourceLineNo">527</span> // the same BucketEntry, then all of the three will share the same refCnt.<a name="line.527"></a>
-<span class="sourceLineNo">528</span> Cacheable cachedBlock = ioEngine.read(bucketEntry);<a name="line.528"></a>
-<span class="sourceLineNo">529</span> if (ioEngine.usesSharedMemory()) {<a name="line.529"></a>
-<span class="sourceLineNo">530</span> // If IOEngine use shared memory, cachedBlock and BucketEntry will share the<a name="line.530"></a>
-<span class="sourceLineNo">531</span> // same RefCnt, do retain here, in order to count the number of RPC references<a name="line.531"></a>
-<span class="sourceLineNo">532</span> cachedBlock.retain();<a name="line.532"></a>
-<span class="sourceLineNo">533</span> }<a name="line.533"></a>
-<span class="sourceLineNo">534</span> // Update the cache statistics.<a name="line.534"></a>
-<span class="sourceLineNo">535</span> if (updateCacheMetrics) {<a name="line.535"></a>
-<span class="sourceLineNo">536</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.536"></a>
-<span class="sourceLineNo">537</span> cacheStats.ioHit(System.nanoTime() - start);<a name="line.537"></a>
-<span class="sourceLineNo">538</span> }<a name="line.538"></a>
-<span class="sourceLineNo">539</span> bucketEntry.access(accessCount.incrementAndGet());<a name="line.539"></a>
-<span class="sourceLineNo">540</span> if (this.ioErrorStartTime > 0) {<a name="line.540"></a>
-<span class="sourceLineNo">541</span> ioErrorStartTime = -1;<a name="line.541"></a>
-<span class="sourceLineNo">542</span> }<a name="line.542"></a>
-<span class="sourceLineNo">543</span> return cachedBlock;<a name="line.543"></a>
-<span class="sourceLineNo">544</span> }<a name="line.544"></a>
-<span class="sourceLineNo">545</span> } catch (IOException ioex) {<a name="line.545"></a>
-<span class="sourceLineNo">546</span> LOG.error("Failed reading block " + key + " from bucket cache", ioex);<a name="line.546"></a>
-<span class="sourceLineNo">547</span> checkIOErrorIsTolerated();<a name="line.547"></a>
-<span class="sourceLineNo">548</span> } finally {<a name="line.548"></a>
-<span class="sourceLineNo">549</span> lock.readLock().unlock();<a name="line.549"></a>
-<span class="sourceLineNo">550</span> }<a name="line.550"></a>
-<span class="sourceLineNo">551</span> }<a name="line.551"></a>
-<span class="sourceLineNo">552</span> if (!repeat && updateCacheMetrics) {<a name="line.552"></a>
-<span class="sourceLineNo">553</span> cacheStats.miss(caching, key.isPrimary(), key.getBlockType());<a name="line.553"></a>
-<span class="sourceLineNo">554</span> }<a name="line.554"></a>
-<span class="sourceLineNo">555</span> return null;<a name="line.555"></a>
-<span class="sourceLineNo">556</span> }<a name="line.556"></a>
-<span class="sourceLineNo">557</span><a name="line.557"></a>
-<span class="sourceLineNo">558</span> /**<a name="line.558"></a>
-<span class="sourceLineNo">559</span> * This method is invoked after the bucketEntry is removed from {@link BucketCache#backingMap}<a name="line.559"></a>
-<span class="sourceLineNo">560</span> */<a name="line.560"></a>
-<span class="sourceLineNo">561</span> void blockEvicted(BlockCacheKey cacheKey, BucketEntry bucketEntry, boolean decrementBlockNumber) {<a name="line.561"></a>
-<span class="sourceLineNo">562</span> bucketEntry.markAsEvicted();<a name="line.562"></a>
-<span class="sourceLineNo">563</span> blocksByHFile.remove(cacheKey);<a name="line.563"></a>
-<span class="sourceLineNo">564</span> if (decrementBlockNumber) {<a name="line.564"></a>
-<span class="sourceLineNo">565</span> this.blockNumber.decrement();<a name="line.565"></a>
-<span class="sourceLineNo">566</span> }<a name="line.566"></a>
-<span class="sourceLineNo">567</span> cacheStats.evicted(bucketEntry.getCachedTime(), cacheKey.isPrimary());<a name="line.567"></a>
-<span class="sourceLineNo">568</span> }<a name="line.568"></a>
-<span class="sourceLineNo">569</span><a name="line.569"></a>
-<span class="sourceLineNo">570</span> /**<a name="line.570"></a>
-<span class="sourceLineNo">571</span> * Free the {{@link BucketEntry} actually,which could only be invoked when the<a name="line.571"></a>
-<span class="sourceLineNo">572</span> * {@link BucketEntry#refCnt} becoming 0.<a name="line.572"></a>
-<span class="sourceLineNo">573</span> */<a name="line.573"></a>
-<span class="sourceLineNo">574</span> void freeBucketEntry(BucketEntry bucketEntry) {<a name="line.574"></a>
-<span class="sourceLineNo">575</span> bucketAllocator.freeBlock(bucketEntry.offset());<a name="line.575"></a>
-<span class="sourceLineNo">576</span> realCacheSize.add(-1 * bucketEntry.getLength());<a name="line.576"></a>
-<span class="sourceLineNo">577</span> }<a name="line.577"></a>
-<span class="sourceLineNo">578</span><a name="line.578"></a>
-<span class="sourceLineNo">579</span> /**<a name="line.579"></a>
-<span class="sourceLineNo">580</span> * Try to evict the block from {@link BlockCache} by force. We'll call this in few cases:<br><a name="line.580"></a>
-<span class="sourceLineNo">581</span> * 1. Close an HFile, and clear all cached blocks. <br><a name="line.581"></a>
-<span class="sourceLineNo">582</span> * 2. Call {@link Admin#clearBlockCache(TableName)} to clear all blocks for a given table.<br><a name="line.582"></a>
-<span class="sourceLineNo">583</span> * <p><a name="line.583"></a>
-<span class="sourceLineNo">584</span> * Firstly, we'll try to remove the block from RAMCache,and then try to evict from backingMap.<a name="line.584"></a>
-<span class="sourceLineNo">585</span> * Here we evict the block from backingMap immediately, but only free the reference from bucket<a name="line.585"></a>
-<span class="sourceLineNo">586</span> * cache by calling {@link BucketEntry#markedAsEvicted}. If there're still some RPC referring this<a name="line.586"></a>
-<span class="sourceLineNo">587</span> * block, block can only be de-allocated when all of them release the block.<a name="line.587"></a>
-<span class="sourceLineNo">588</span> * <p><a name="line.588"></a>
-<span class="sourceLineNo">589</span> * NOTICE: we need to grab the write offset lock firstly before releasing the reference from<a name="line.589"></a>
-<span class="sourceLineNo">590</span> * bucket cache. if we don't, we may read an {@link BucketEntry} with refCnt = 0 when<a name="line.590"></a>
-<span class="sourceLineNo">591</span> * {@link BucketCache#getBlock(BlockCacheKey, boolean, boolean, boolean)}, it's a memory leak.<a name="line.591"></a>
-<span class="sourceLineNo">592</span> * @param cacheKey Block to evict<a name="line.592"></a>
-<span class="sourceLineNo">593</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.593"></a>
-<span class="sourceLineNo">594</span> */<a name="line.594"></a>
-<span class="sourceLineNo">595</span> @Override<a name="line.595"></a>
-<span class="sourceLineNo">596</span> public boolean evictBlock(BlockCacheKey cacheKey) {<a name="line.596"></a>
-<span class="sourceLineNo">597</span> return doEvictBlock(cacheKey, null);<a name="line.597"></a>
-<span class="sourceLineNo">598</span> }<a name="line.598"></a>
-<span class="sourceLineNo">599</span><a name="line.599"></a>
-<span class="sourceLineNo">600</span> /**<a name="line.600"></a>
-<span class="sourceLineNo">601</span> * Evict the {@link BlockCacheKey} and {@link BucketEntry} from {@link BucketCache#backingMap} and<a name="line.601"></a>
-<span class="sourceLineNo">602</span> * {@link BucketCache#ramCache}. <br/><a name="line.602"></a>
-<span class="sourceLineNo">603</span> * NOTE:When Evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.603"></a>
-<span class="sourceLineNo">604</span> * {@link BucketEntry} could be removed.<a name="line.604"></a>
-<span class="sourceLineNo">605</span> * @param cacheKey {@link BlockCacheKey} to evict.<a name="line.605"></a>
-<span class="sourceLineNo">606</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.606"></a>
-<span class="sourceLineNo">607</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.607"></a>
-<span class="sourceLineNo">608</span> */<a name="line.608"></a>
-<span class="sourceLineNo">609</span> private boolean doEvictBlock(BlockCacheKey cacheKey, BucketEntry bucketEntry) {<a name="line.609"></a>
-<span class="sourceLineNo">610</span> if (!cacheEnabled) {<a name="line.610"></a>
-<span class="sourceLineNo">611</span> return false;<a name="line.611"></a>
-<span class="sourceLineNo">612</span> }<a name="line.612"></a>
-<span class="sourceLineNo">613</span> boolean existedInRamCache = removeFromRamCache(cacheKey);<a name="line.613"></a>
-<span class="sourceLineNo">614</span> if (bucketEntry == null) {<a name="line.614"></a>
-<span class="sourceLineNo">615</span> bucketEntry = backingMap.get(cacheKey);<a name="line.615"></a>
-<span class="sourceLineNo">616</span> }<a name="line.616"></a>
-<span class="sourceLineNo">617</span> final BucketEntry bucketEntryToUse = bucketEntry;<a name="line.617"></a>
-<span class="sourceLineNo">618</span><a name="line.618"></a>
-<span class="sourceLineNo">619</span> if (bucketEntryToUse == null) {<a name="line.619"></a>
-<span class="sourceLineNo">620</span> if (existedInRamCache) {<a name="line.620"></a>
-<span class="sourceLineNo">621</span> cacheStats.evicted(0, cacheKey.isPrimary());<a name="line.621"></a>
-<span class="sourceLineNo">622</span> }<a name="line.622"></a>
-<span class="sourceLineNo">623</span> return existedInRamCache;<a name="line.623"></a>
-<span class="sourceLineNo">624</span> } else {<a name="line.624"></a>
-<span class="sourceLineNo">625</span> return bucketEntryToUse.withWriteLock(offsetLock, () -> {<a name="line.625"></a>
-<span class="sourceLineNo">626</span> if (backingMap.remove(cacheKey, bucketEntryToUse)) {<a name="line.626"></a>
-<span class="sourceLineNo">627</span> blockEvicted(cacheKey, bucketEntryToUse, !existedInRamCache);<a name="line.627"></a>
-<span class="sourceLineNo">628</span> return true;<a name="line.628"></a>
-<span class="sourceLineNo">629</span> }<a name="line.629"></a>
-<span class="sourceLineNo">630</span> return false;<a name="line.630"></a>
-<span class="sourceLineNo">631</span> });<a name="line.631"></a>
-<span class="sourceLineNo">632</span> }<a name="line.632"></a>
-<span class="sourceLineNo">633</span> }<a name="line.633"></a>
-<span class="sourceLineNo">634</span><a name="line.634"></a>
-<span class="sourceLineNo">635</span> /**<a name="line.635"></a>
-<span class="sourceLineNo">636</span> * <pre><a name="line.636"></a>
-<span class="sourceLineNo">637</span> * Create the {@link Recycler} for {@link BucketEntry#refCnt},which would be used as<a name="line.637"></a>
-<span class="sourceLineNo">638</span> * {@link RefCnt#recycler} of {@link HFileBlock#buf} returned from {@link BucketCache#getBlock}.<a name="line.638"></a>
-<span class="sourceLineNo">639</span> * NOTE: for {@link BucketCache#getBlock},the {@link RefCnt#recycler} of {@link HFileBlock#buf}<a name="line.639"></a>
-<span class="sourceLineNo">640</span> * from {@link BucketCache#backingMap} and {@link BucketCache#ramCache} are different:<a name="line.640"></a>
-<span class="sourceLineNo">641</span> * 1.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#backingMap},<a name="line.641"></a>
-<span class="sourceLineNo">642</span> * it is the return value of current {@link BucketCache#createRecycler} method.<a name="line.642"></a>
-<span class="sourceLineNo">643</span> *<a name="line.643"></a>
-<span class="sourceLineNo">644</span> * 2.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#ramCache},<a name="line.644"></a>
-<span class="sourceLineNo">645</span> * it is {@link ByteBuffAllocator#putbackBuffer}.<a name="line.645"></a>
-<span class="sourceLineNo">646</span> * </pre><a name="line.646"></a>
-<span class="sourceLineNo">647</span> */<a name="line.647"></a>
-<span class="sourceLineNo">648</span> private Recycler createRecycler(final BucketEntry bucketEntry) {<a name="line.648"></a>
-<span class="sourceLineNo">649</span> return () -> {<a name="line.649"></a>
-<span class="sourceLineNo">650</span> freeBucketEntry(bucketEntry);<a name="line.650"></a>
-<span class="sourceLineNo">651</span> return;<a name="line.651"></a>
-<span class="sourceLineNo">652</span> };<a name="line.652"></a>
-<span class="sourceLineNo">653</span> }<a name="line.653"></a>
-<span class="sourceLineNo">654</span><a name="line.654"></a>
-<span class="sourceLineNo">655</span> /**<a name="line.655"></a>
-<span class="sourceLineNo">656</span> * NOTE: This method is only for test.<a name="line.656"></a>
-<span class="sourceLineNo">657</span> */<a name="line.657"></a>
-<span class="sourceLineNo">658</span> public boolean evictBlockIfNoRpcReferenced(BlockCacheKey blockCacheKey) {<a name="line.658"></a>
-<span class="sourceLineNo">659</span> BucketEntry bucketEntry = backingMap.get(blockCacheKey);<a name="line.659"></a>
-<span class="sourceLineNo">660</span> if (bucketEntry == null) {<a name="line.660"></a>
-<span class="sourceLineNo">661</span> return false;<a name="line.661"></a>
-<span class="sourceLineNo">662</span> }<a name="line.662"></a>
-<span class="sourceLineNo">663</span> return evictBucketEntryIfNoRpcReferenced(blockCacheKey, bucketEntry);<a name="line.663"></a>
-<span class="sourceLineNo">664</span> }<a name="line.664"></a>
-<span class="sourceLineNo">665</span><a name="line.665"></a>
-<span class="sourceLineNo">666</span> /**<a name="line.666"></a>
-<span class="sourceLineNo">667</span> * Evict {@link BlockCacheKey} and its corresponding {@link BucketEntry} only if<a name="line.667"></a>
-<span class="sourceLineNo">668</span> * {@link BucketEntry#isRpcRef} is false. <br/><a name="line.668"></a>
-<span class="sourceLineNo">669</span> * NOTE:When evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.669"></a>
-<span class="sourceLineNo">670</span> * {@link BucketEntry} could be removed.<a name="line.670"></a>
-<span class="sourceLineNo">671</span> * @param blockCacheKey {@link BlockCacheKey} to evict.<a name="line.671"></a>
-<span class="sourceLineNo">672</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.672"></a>
-<span class="sourceLineNo">673</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.673"></a>
-<span class="sourceLineNo">674</span> */<a name="line.674"></a>
-<span class="sourceLineNo">675</span> boolean evictBucketEntryIfNoRpcReferenced(BlockCacheKey blockCacheKey, BucketEntry bucketEntry) {<a name="line.675"></a>
-<span class="sourceLineNo">676</span> if (!bucketEntry.isRpcRef()) {<a name="line.676"></a>
-<span class="sourceLineNo">677</span> return doEvictBlock(blockCacheKey, bucketEntry);<a name="line.677"></a>
-<span class="sourceLineNo">678</span> }<a name="line.678"></a>
-<span class="sourceLineNo">679</span> return false;<a name="line.679"></a>
-<span class="sourceLineNo">680</span> }<a name="line.680"></a>
-<span class="sourceLineNo">681</span><a name="line.681"></a>
-<span class="sourceLineNo">682</span> protected boolean removeFromRamCache(BlockCacheKey cacheKey) {<a name="line.682"></a>
-<span class="sourceLineNo">683</span> return ramCache.remove(cacheKey, re -> {<a name="line.683"></a>
-<span class="sourceLineNo">684</span> if (re != null) {<a name="line.684"></a>
-<span class="sourceLineNo">685</span> this.blockNumber.decrement();<a name="line.685"></a>
-<span class="sourceLineNo">686</span> this.heapSize.add(-1 * re.getData().heapSize());<a name="line.686"></a>
-<span class="sourceLineNo">687</span> }<a name="line.687"></a>
-<span class="sourceLineNo">688</span> });<a name="line.688"></a>
-<span class="sourceLineNo">689</span> }<a name="line.689"></a>
-<span class="sourceLineNo">690</span><a name="line.690"></a>
-<span class="sourceLineNo">691</span> /*<a name="line.691"></a>
-<span class="sourceLineNo">692</span> * Statistics thread. Periodically output cache statistics to the log.<a name="line.692"></a>
-<span class="sourceLineNo">693</span> */<a name="line.693"></a>
-<span class="sourceLineNo">694</span> private static class StatisticsThread extends Thread {<a name="line.694"></a>
-<span class="sourceLineNo">695</span> private final BucketCache bucketCache;<a name="line.695"></a>
-<span class="sourceLineNo">696</span><a name="line.696"></a>
-<span class="sourceLineNo">697</span> public StatisticsThread(BucketCache bucketCache) {<a name="line.697"></a>
-<span class="sourceLineNo">698</span> super("BucketCacheStatsThread");<a name="line.698"></a>
-<span class="sourceLineNo">699</span> setDaemon(true);<a name="line.699"></a>
-<span class="sourceLineNo">700</span> this.bucketCache = bucketCache;<a name="line.700"></a>
-<span class="sourceLineNo">701</span> }<a name="line.701"></a>
-<span class="sourceLineNo">702</span><a name="line.702"></a>
-<span class="sourceLineNo">703</span> @Override<a name="line.703"></a>
-<span class="sourceLineNo">704</span> public void run() {<a name="line.704"></a>
-<span class="sourceLineNo">705</span> bucketCache.logStats();<a name="line.705"></a>
-<span class="sourceLineNo">706</span> }<a name="line.706"></a>
-<span class="sourceLineNo">707</span> }<a name="line.707"></a>
-<span class="sourceLineNo">708</span><a name="line.708"></a>
-<span class="sourceLineNo">709</span> public void logStats() {<a name="line.709"></a>
-<span class="sourceLineNo">710</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.710"></a>
-<span class="sourceLineNo">711</span> long usedSize = bucketAllocator.getUsedSize();<a name="line.711"></a>
-<span class="sourceLineNo">712</span> long freeSize = totalSize - usedSize;<a name="line.712"></a>
-<span class="sourceLineNo">713</span> long cacheSize = getRealCacheSize();<a name="line.713"></a>
-<span class="sourceLineNo">714</span> LOG.info("failedBlockAdditions=" + cacheStats.getFailedInserts() + ", " +<a name="line.714"></a>
-<span class="sourceLineNo">715</span> "totalSize=" + StringUtils.byteDesc(totalSize) + ", " +<a name="line.715"></a>
-<span class="sourceLineNo">716</span> "freeSize=" + StringUtils.byteDesc(freeSize) + ", " +<a name="line.716"></a>
-<span class="sourceLineNo">717</span> "usedSize=" + StringUtils.byteDesc(usedSize) +", " +<a name="line.717"></a>
-<span class="sourceLineNo">718</span> "cacheSize=" + StringUtils.byteDesc(cacheSize) +", " +<a name="line.718"></a>
-<span class="sourceLineNo">719</span> "accesses=" + cacheStats.getRequestCount() + ", " +<a name="line.719"></a>
-<span class="sourceLineNo">720</span> "hits=" + cacheStats.getHitCount() + ", " +<a name="line.720"></a>
-<span class="sourceLineNo">721</span> "IOhitsPerSecond=" + cacheStats.getIOHitsPerSecond() + ", " +<a name="line.721"></a>
-<span class="sourceLineNo">722</span> "IOTimePerHit=" + String.format("%.2f", cacheStats.getIOTimePerHit())+ ", " +<a name="line.722"></a>
-<span class="sourceLineNo">723</span> "hitRatio=" + (cacheStats.getHitCount() == 0 ? "0," :<a name="line.723"></a>
-<span class="sourceLineNo">724</span> (StringUtils.formatPercent(cacheStats.getHitRatio(), 2)+ ", ")) +<a name="line.724"></a>
-<span class="sourceLineNo">725</span> "cachingAccesses=" + cacheStats.getRequestCachingCount() + ", " +<a name="line.725"></a>
-<span class="sourceLineNo">726</span> "cachingHits=" + cacheStats.getHitCachingCount() + ", " +<a name="line.726"></a>
-<span class="sourceLineNo">727</span> "cachingHitsRatio=" +(cacheStats.getHitCachingCount() == 0 ? "0," :<a name="line.727"></a>
-<span class="sourceLineNo">728</span> (StringUtils.formatPercent(cacheStats.getHitCachingRatio(), 2)+ ", ")) +<a name="line.728"></a>
-<span class="sourceLineNo">729</span> "evictions=" + cacheStats.getEvictionCount() + ", " +<a name="line.729"></a>
-<span class="sourceLineNo">730</span> "evicted=" + cacheStats.getEvictedCount() + ", " +<a name="line.730"></a>
-<span class="sourceLineNo">731</span> "evictedPerRun=" + cacheStats.evictedPerEviction());<a name="line.731"></a>
-<span class="sourceLineNo">732</span> cacheStats.reset();<a name="line.732"></a>
-<span class="sourceLineNo">733</span> }<a name="line.733"></a>
-<span class="sourceLineNo">734</span><a name="line.734"></a>
-<span class="sourceLineNo">735</span> public long getRealCacheSize() {<a name="line.735"></a>
-<span class="sourceLineNo">736</span> return this.realCacheSize.sum();<a name="line.736"></a>
-<span class="sourceLineNo">737</span> }<a name="line.737"></a>
-<span class="sourceLineNo">738</span><a name="line.738"></a>
-<span class="sourceLineNo">739</span> public long acceptableSize() {<a name="line.739"></a>
-<span class="sourceLineNo">740</span> return (long) Math.floor(bucketAllocator.getTotalSize() * acceptableFactor);<a name="line.740"></a>
-<span class="sourceLineNo">741</span> }<a name="line.741"></a>
-<span class="sourceLineNo">742</span><a name="line.742"></a>
-<span class="sourceLineNo">743</span> long getPartitionSize(float partitionFactor) {<a name="line.743"></a>
-<span class="sourceLineNo">744</span> return (long) Math.floor(bucketAllocator.getTotalSize() * partitionFactor * minFactor);<a name="line.744"></a>
-<span class="sourceLineNo">745</span> }<a name="line.745"></a>
-<span class="sourceLineNo">746</span><a name="line.746"></a>
-<span class="sourceLineNo">747</span> /**<a name="line.747"></a>
-<span class="sourceLineNo">748</span> * Return the count of bucketSizeinfos still need free space<a name="line.748"></a>
-<span class="sourceLineNo">749</span> */<a name="line.749"></a>
-<span class="sourceLineNo">750</span> private int bucketSizesAboveThresholdCount(float minFactor) {<a name="line.750"></a>
-<span class="sourceLineNo">751</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.751"></a>
-<span class="sourceLineNo">752</span> int fullCount = 0;<a name="line.752"></a>
-<span class="sourceLineNo">753</span> for (int i = 0; i < stats.length; i++) {<a name="line.753"></a>
-<span class="sourceLineNo">754</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.754"></a>
-<span class="sourceLineNo">755</span> freeGoal = Math.max(freeGoal, 1);<a name="line.755"></a>
-<span class="sourceLineNo">756</span> if (stats[i].freeCount() < freeGoal) {<a name="line.756"></a>
-<span class="sourceLineNo">757</span> fullCount++;<a name="line.757"></a>
-<span class="sourceLineNo">758</span> }<a name="line.758"></a>
-<span class="sourceLineNo">759</span> }<a name="line.759"></a>
-<span class="sourceLineNo">760</span> return fullCount;<a name="line.760"></a>
-<span class="sourceLineNo">761</span> }<a name="line.761"></a>
-<span class="sourceLineNo">762</span><a name="line.762"></a>
-<span class="sourceLineNo">763</span> /**<a name="line.763"></a>
-<span class="sourceLineNo">764</span> * This method will find the buckets that are minimally occupied<a name="line.764"></a>
-<span class="sourceLineNo">765</span> * and are not reference counted and will free them completely<a name="line.765"></a>
-<span class="sourceLineNo">766</span> * without any constraint on the access times of the elements,<a name="line.766"></a>
-<span class="sourceLineNo">767</span> * and as a process will completely free at most the number of buckets<a name="line.767"></a>
-<span class="sourceLineNo">768</span> * passed, sometimes it might not due to changing refCounts<a name="line.768"></a>
-<span class="sourceLineNo">769</span> *<a name="line.769"></a>
-<span class="sourceLineNo">770</span> * @param completelyFreeBucketsNeeded number of buckets to free<a name="line.770"></a>
-<span class="sourceLineNo">771</span> **/<a name="line.771"></a>
-<span class="sourceLineNo">772</span> private void freeEntireBuckets(int completelyFreeBucketsNeeded) {<a name="line.772"></a>
-<span class="sourceLineNo">773</span> if (completelyFreeBucketsNeeded != 0) {<a name="line.773"></a>
-<span class="sourceLineNo">774</span> // First we will build a set where the offsets are reference counted, usually<a name="line.774"></a>
-<span class="sourceLineNo">775</span> // this set is small around O(Handler Count) unless something else is wrong<a name="line.775"></a>
-<span class="sourceLineNo">776</span> Set<Integer> inUseBuckets = new HashSet<>();<a name="line.776"></a>
-<span class="sourceLineNo">777</span> backingMap.forEach((k, be) -> {<a name="line.777"></a>
-<span class="sourceLineNo">778</span> if (be.isRpcRef()) {<a name="line.778"></a>
-<span class="sourceLineNo">779</span> inUseBuckets.add(bucketAllocator.getBucketIndex(be.offset()));<a name="line.779"></a>
-<span class="sourceLineNo">780</span> }<a name="line.780"></a>
-<span class="sourceLineNo">781</span> });<a name="line.781"></a>
-<span class="sourceLineNo">782</span> Set<Integer> candidateBuckets =<a name="line.782"></a>
-<span class="sourceLineNo">783</span> bucketAllocator.getLeastFilledBuckets(inUseBuckets, completelyFreeBucketsNeeded);<a name="line.783"></a>
-<span class="sourceLineNo">784</span> for (Map.Entry<BlockCacheKey, BucketEntry> entry : backingMap.entrySet()) {<a name="line.784"></a>
-<span class="sourceLineNo">785</span> if (candidateBuckets.contains(bucketAllocator.getBucketIndex(entry.getValue().offset()))) {<a name="line.785"></a>
-<span class="sourceLineNo">786</span> evictBucketEntryIfNoRpcReferenced(entry.getKey(), entry.getValue());<a name="line.786"></a>
-<span class="sourceLineNo">787</span> }<a name="line.787"></a>
-<span class="sourceLineNo">788</span> }<a name="line.788"></a>
-<span class="sourceLineNo">789</span> }<a name="line.789"></a>
-<span class="sourceLineNo">790</span> }<a name="line.790"></a>
-<span class="sourceLineNo">791</span><a name="line.791"></a>
-<span class="sourceLineNo">792</span> /**<a name="line.792"></a>
-<span class="sourceLineNo">793</span> * Free the space if the used size reaches acceptableSize() or one size block<a name="line.793"></a>
-<span class="sourceLineNo">794</span> * couldn't be allocated. When freeing the space, we use the LRU algorithm and<a name="line.794"></a>
-<span class="sourceLineNo">795</span> * ensure there must be some blocks evicted<a name="line.795"></a>
-<span class="sourceLineNo">796</span> * @param why Why we are being called<a name="line.796"></a>
-<span class="sourceLineNo">797</span> */<a name="line.797"></a>
-<span class="sourceLineNo">798</span> private void freeSpace(final String why) {<a name="line.798"></a>
-<span class="sourceLineNo">799</span> // Ensure only one freeSpace progress at a time<a name="line.799"></a>
-<span class="sourceLineNo">800</span> if (!freeSpaceLock.tryLock()) {<a name="line.800"></a>
-<span class="sourceLineNo">801</span> return;<a name="line.801"></a>
-<span class="sourceLineNo">802</span> }<a name="line.802"></a>
-<span class="sourceLineNo">803</span> try {<a name="line.803"></a>
-<span class="sourceLineNo">804</span> freeInProgress = true;<a name="line.804"></a>
-<span class="sourceLineNo">805</span> long bytesToFreeWithoutExtra = 0;<a name="line.805"></a>
-<span class="sourceLineNo">806</span> // Calculate free byte for each bucketSizeinfo<a name="line.806"></a>
-<span class="sourceLineNo">807</span> StringBuilder msgBuffer = LOG.isDebugEnabled()? new StringBuilder(): null;<a name="line.807"></a>
-<span class="sourceLineNo">808</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.808"></a>
-<span class="sourceLineNo">809</span> long[] bytesToFreeForBucket = new long[stats.length];<a name="line.809"></a>
-<span class="sourceLineNo">810</span> for (int i = 0; i < stats.length; i++) {<a name="line.810"></a>
-<span class="sourceLineNo">811</span> bytesToFreeForBucket[i] = 0;<a name="line.811"></a>
-<span class="sourceLineNo">812</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.812"></a>
-<span class="sourceLineNo">813</span> freeGoal = Math.max(freeGoal, 1);<a name="line.813"></a>
-<span class="sourceLineNo">814</span> if (stats[i].freeCount() < freeGoal) {<a name="line.814"></a>
-<span class="sourceLineNo">815</span> bytesToFreeForBucket[i] = stats[i].itemSize() * (freeGoal - stats[i].freeCount());<a name="line.815"></a>
-<span class="sourceLineNo">816</span> bytesToFreeWithoutExtra += bytesToFreeForBucket[i];<a name="line.816"></a>
-<span class="sourceLineNo">817</span> if (msgBuffer != null) {<a name="line.817"></a>
-<span class="sourceLineNo">818</span> msgBuffer.append("Free for bucketSize(" + stats[i].itemSize() + ")="<a name="line.818"></a>
-<span class="sourceLineNo">819</span> + StringUtils.byteDesc(bytesToFreeForBucket[i]) + ", ");<a name="line.819"></a>
-<span class="sourceLineNo">820</span> }<a name="line.820"></a>
-<span class="sourceLineNo">821</span> }<a name="line.821"></a>
-<span class="sourceLineNo">822</span> }<a name="line.822"></a>
-<span class="sourceLineNo">823</span> if (msgBuffer != null) {<a name="line.823"></a>
-<span class="sourceLineNo">824</span> msgBuffer.append("Free for total=" + StringUtils.byteDesc(bytesToFreeWithoutExtra) + ", ");<a name="line.824"></a>
-<span class="sourceLineNo">825</span> }<a name="line.825"></a>
-<span class="sourceLineNo">826</span><a name="line.826"></a>
-<span class="sourceLineNo">827</span> if (bytesToFreeWithoutExtra <= 0) {<a name="line.827"></a>
-<span class="sourceLineNo">828</span> return;<a name="line.828"></a>
-<span class="sourceLineNo">829</span> }<a name="line.829"></a>
-<span class="sourceLineNo">830</span> long currentSize = bucketAllocator.getUsedSize();<a name="line.830"></a>
-<span class="sourceLineNo">831</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.831"></a>
-<span class="sourceLineNo">832</span> if (LOG.isDebugEnabled() && msgBuffer != null) {<a name="line.832"></a>
-<span class="sourceLineNo">833</span> LOG.debug("Free started because \"" + why + "\"; " + msgBuffer.toString() +<a name="line.833"></a>
-<span class="sourceLineNo">834</span> " of current used=" + StringUtils.byteDesc(currentSize) + ", actual cacheSize=" +<a name="line.834"></a>
-<span class="sourceLineNo">835</span> StringUtils.byteDesc(realCacheSize.sum()) + ", total=" + StringUtils.byteDesc(totalSize));<a name="line.835"></a>
-<span class="sourceLineNo">836</span> }<a name="line.836"></a>
-<span class="sourceLineNo">837</span><a name="line.837"></a>
-<span class="sourceLineNo">838</span> long bytesToFreeWithExtra = (long) Math.floor(bytesToFreeWithoutExtra<a name="line.838"></a>
-<span class="sourceLineNo">839</span> * (1 + extraFreeFactor));<a name="line.839"></a>
-<span class="sourceLineNo">840</span><a name="line.840"></a>
-<span class="sourceLineNo">841</span> // Instantiate priority buckets<a name="line.841"></a>
-<span class="sourceLineNo">842</span> BucketEntryGroup bucketSingle = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.842"></a>
-<span class="sourceLineNo">843</span> blockSize, getPartitionSize(singleFactor));<a name="line.843"></a>
-<span class="sourceLineNo">844</span> BucketEntryGroup bucketMulti = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.844"></a>
-<span class="sourceLineNo">845</span> blockSize, getPartitionSize(multiFactor));<a name="line.845"></a>
-<span class="sourceLineNo">846</span> BucketEntryGroup bucketMemory = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.846"></a>
-<span class="sourceLineNo">847</span> blockSize, getPartitionSize(memoryFactor));<a name="line.847"></a>
-<span class="sourceLineNo">848</span><a name="line.848"></a>
-<span class="sourceLineNo">849</span> // Scan entire map putting bucket entry into appropriate bucket entry<a name="line.849"></a>
-<span class="sourceLineNo">850</span> // group<a name="line.850"></a>
-<span class="sourceLineNo">851</span> for (Map.Entry<BlockCacheKey, BucketEntry> bucketEntryWithKey : backingMap.entrySet()) {<a name="line.851"></a>
-<span class="sourceLineNo">852</span> switch (bucketEntryWithKey.getValue().getPriority()) {<a name="line.852"></a>
-<span class="sourceLineNo">853</span> case SINGLE: {<a name="line.853"></a>
-<span class="sourceLineNo">854</span> bucketSingle.add(bucketEntryWithKey);<a name="line.854"></a>
-<span class="sourceLineNo">855</span> break;<a name="line.855"></a>
-<span class="sourceLineNo">856</span> }<a name="line.856"></a>
-<span class="sourceLineNo">857</span> case MULTI: {<a name="line.857"></a>
-<span class="sourceLineNo">858</span> bucketMulti.add(bucketEntryWithKey);<a name="line.858"></a>
-<span class="sourceLineNo">859</span> break;<a name="line.859"></a>
-<span class="sourceLineNo">860</span> }<a name="line.860"></a>
-<span class="sourceLineNo">861</span> case MEMORY: {<a name="line.861"></a>
-<span class="sourceLineNo">862</span> bucketMemory.add(bucketEntryWithKey);<a name="line.862"></a>
-<span class="sourceLineNo">863</span> break;<a name="line.863"></a>
-<span class="sourceLineNo">864</span> }<a name="line.864"></a>
-<span class="sourceLineNo">865</span> }<a name="line.865"></a>
-<span class="sourceLineNo">866</span> }<a name="line.866"></a>
-<span class="sourceLineNo">867</span><a name="line.867"></a>
-<span class="sourceLineNo">868</span> PriorityQueue<BucketEntryGroup> bucketQueue = new PriorityQueue<>(3,<a name="line.868"></a>
-<span class="sourceLineNo">869</span> Comparator.comparingLong(BucketEntryGroup::overflow));<a name="line.869"></a>
-<span class="sourceLineNo">870</span><a name="line.870"></a>
-<span class="sourceLineNo">871</span> bucketQueue.add(bucketSingle);<a name="line.871"></a>
-<span class="sourceLineNo">872</span> bucketQueue.add(bucketMulti);<a name="line.872"></a>
-<span class="sourceLineNo">873</span> bucketQueue.add(bucketMemory);<a name="line.873"></a>
-<span class="sourceLineNo">874</span><a name="line.874"></a>
-<span class="sourceLineNo">875</span> int remainingBuckets = bucketQueue.size();<a name="line.875"></a>
-<span class="sourceLineNo">876</span> long bytesFreed = 0;<a name="line.876"></a>
-<span class="sourceLineNo">877</span><a name="line.877"></a>
-<span class="sourceLineNo">878</span> BucketEntryGroup bucketGroup;<a name="line.878"></a>
-<span class="sourceLineNo">879</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.879"></a>
-<span class="sourceLineNo">880</span> long overflow = bucketGroup.overflow();<a name="line.880"></a>
-<span class="sourceLineNo">881</span> if (overflow > 0) {<a name="line.881"></a>
-<span class="sourceLineNo">882</span> long bucketBytesToFree = Math.min(overflow,<a name="line.882"></a>
-<span class="sourceLineNo">883</span> (bytesToFreeWithoutExtra - bytesFreed) / remainingBuckets);<a name="line.883"></a>
-<span class="sourceLineNo">884</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.884"></a>
-<span class="sourceLineNo">885</span> }<a name="line.885"></a>
-<span class="sourceLineNo">886</span> remainingBuckets--;<a name="line.886"></a>
-<span class="sourceLineNo">887</span> }<a name="line.887"></a>
-<span class="sourceLineNo">888</span><a name="line.888"></a>
-<span class="sourceLineNo">889</span> // Check and free if there are buckets that still need freeing of space<a name="line.889"></a>
-<span class="sourceLineNo">890</span> if (bucketSizesAboveThresholdCount(minFactor) > 0) {<a name="line.890"></a>
-<span class="sourceLineNo">891</span> bucketQueue.clear();<a name="line.891"></a>
-<span class="sourceLineNo">892</span> remainingBuckets = 3;<a name="line.892"></a>
-<span class="sourceLineNo">893</span><a name="line.893"></a>
-<span class="sourceLineNo">894</span> bucketQueue.add(bucketSingle);<a name="line.894"></a>
-<span class="sourceLineNo">895</span> bucketQueue.add(bucketMulti);<a name="line.895"></a>
-<span class="sourceLineNo">896</span> bucketQueue.add(bucketMemory);<a name="line.896"></a>
-<span class="sourceLineNo">897</span><a name="line.897"></a>
-<span class="sourceLineNo">898</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.898"></a>
-<span class="sourceLineNo">899</span> long bucketBytesToFree = (bytesToFreeWithExtra - bytesFreed) / remainingBuckets;<a name="line.899"></a>
-<span class="sourceLineNo">900</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.900"></a>
-<span class="sourceLineNo">901</span> remainingBuckets--;<a name="line.901"></a>
-<span class="sourceLineNo">902</span> }<a name="line.902"></a>
-<span class="sourceLineNo">903</span> }<a name="line.903"></a>
-<span class="sourceLineNo">904</span><a name="line.904"></a>
-<span class="sourceLineNo">905</span> // Even after the above free we might still need freeing because of the<a name="line.905"></a>
-<span class="sourceLineNo">906</span> // De-fragmentation of the buckets (also called Slab Calcification problem), i.e<a name="line.906"></a>
-<span class="sourceLineNo">907</span> // there might be some buckets where the occupancy is very sparse and thus are not<a name="line.907"></a>
-<span class="sourceLineNo">908</span> // yielding the free for the other bucket sizes, the fix for this to evict some<a name="line.908"></a>
-<span class="sourceLineNo">909</span> // of the buckets, we do this by evicting the buckets that are least fulled<a name="line.909"></a>
-<span class="sourceLineNo">910</span> freeEntireBuckets(DEFAULT_FREE_ENTIRE_BLOCK_FACTOR *<a name="line.910"></a>
-<span class="sourceLineNo">911</span> bucketSizesAboveThresholdCount(1.0f));<a name="line.911"></a>
-<span class="sourceLineNo">912</span><a name="line.912"></a>
-<span class="sourceLineNo">913</span> if (LOG.isDebugEnabled()) {<a name="line.913"></a>
-<span class="sourceLineNo">914</span> long single = bucketSingle.totalSize();<a name="line.914"></a>
-<span class="sourceLineNo">915</span> long multi = bucketMulti.totalSize();<a name="line.915"></a>
-<span class="sourceLineNo">916</span> long memory = bucketMemory.totalSize();<a name="line.916"></a>
-<span class="sourceLineNo">917</span> if (LOG.isDebugEnabled()) {<a name="line.917"></a>
-<span class="sourceLineNo">918</span> LOG.debug("Bucket cache free space completed; " + "freed="<a name="line.918"></a>
-<span class="sourceLineNo">919</span> + StringUtils.byteDesc(bytesFreed) + ", " + "total="<a name="line.919"></a>
-<span class="sourceLineNo">920</span> + StringUtils.byteDesc(totalSize) + ", " + "single="<a name="line.920"></a>
-<span class="sourceLineNo">921</span> + StringUtils.byteDesc(single) + ", " + "multi="<a name="line.921"></a>
-<span class="sourceLineNo">922</span> + StringUtils.byteDesc(multi) + ", " + "memory="<a name="line.922"></a>
-<span class="sourceLineNo">923</span> + StringUtils.byteDesc(memory));<a name="line.923"></a>
-<span class="sourceLineNo">924</span> }<a name="line.924"></a>
-<span class="sourceLineNo">925</span> }<a name="line.925"></a>
-<span class="sourceLineNo">926</span><a name="line.926"></a>
-<span class="sourceLineNo">927</span> } catch (Throwable t) {<a name="line.927"></a>
-<span class="sourceLineNo">928</span> LOG.warn("Failed freeing space", t);<a name="line.928"></a>
-<span class="sourceLineNo">929</span> } finally {<a name="line.929"></a>
-<span class="sourceLineNo">930</span> cacheStats.evict();<a name="line.930"></a>
-<span class="sourceLineNo">931</span> freeInProgress = false;<a name="line.931"></a>
-<span class="sourceLineNo">932</span> freeSpaceLock.unlock();<a name="line.932"></a>
-<span class="sourceLineNo">933</span> }<a name="line.933"></a>
-<span class="sourceLineNo">934</span> }<a name="line.934"></a>
-<span class="sourceLineNo">935</span><a name="line.935"></a>
-<span class="sourceLineNo">936</span> // This handles flushing the RAM cache to IOEngine.<a name="line.936"></a>
-<span class="sourceLineNo">937</span> class WriterThread extends Thread {<a name="line.937"></a>
-<span class="sourceLineNo">938</span> private final BlockingQueue<RAMQueueEntry> inputQueue;<a name="line.938"></a>
-<span class="sourceLineNo">939</span> private volatile boolean writerEnabled = true;<a name="line.939"></a>
-<span class="sourceLineNo">940</span><a name="line.940"></a>
-<span class="sourceLineNo">941</span> WriterThread(BlockingQueue<RAMQueueEntry> queue) {<a name="line.941"></a>
-<span class="sourceLineNo">942</span> super("BucketCacheWriterThread");<a name="line.942"></a>
-<span class="sourceLineNo">943</span> this.inputQueue = queue;<a name="line.943"></a>
-<span class="sourceLineNo">944</span> }<a name="line.944"></a>
-<span class="sourceLineNo">945</span><a name="line.945"></a>
-<span class="sourceLineNo">946</span> // Used for test<a name="line.946"></a>
-<span class="sourceLineNo">947</span> void disableWriter() {<a name="line.947"></a>
-<span class="sourceLineNo">948</span> this.writerEnabled = false;<a name="line.948"></a>
-<span class="sourceLineNo">949</span> }<a name="line.949"></a>
-<span class="sourceLineNo">950</span><a name="line.950"></a>
-<span class="sourceLineNo">951</span> @Override<a name="line.951"></a>
-<span class="sourceLineNo">952</span> public void run() {<a name="line.952"></a>
-<span class="sourceLineNo">953</span> List<RAMQueueEntry> entries = new ArrayList<>();<a name="line.953"></a>
-<span class="sourceLineNo">954</span> try {<a name="line.954"></a>
-<span class="sourceLineNo">955</span> while (cacheEnabled && writerEnabled) {<a name="line.955"></a>
-<span class="sourceLineNo">956</span> try {<a name="line.956"></a>
-<span class="sourceLineNo">957</span> try {<a name="line.957"></a>
-<span class="sourceLineNo">958</span> // Blocks<a name="line.958"></a>
-<span class="sourceLineNo">959</span> entries = getRAMQueueEntries(inputQueue, entries);<a name="line.959"></a>
-<span class="sourceLineNo">960</span> } catch (InterruptedException ie) {<a name="line.960"></a>
-<span class="sourceLineNo">961</span> if (!cacheEnabled || !writerEnabled) {<a name="line.961"></a>
-<span class="sourceLineNo">962</span> break;<a name="line.962"></a>
-<span class="sourceLineNo">963</span> }<a name="line.963"></a>
-<span class="sourceLineNo">964</span> }<a name="line.964"></a>
-<span class="sourceLineNo">965</span> doDrain(entries);<a name="line.965"></a>
-<span class="sourceLineNo">966</span> } catch (Exception ioe) {<a name="line.966"></a>
-<span class="sourceLineNo">967</span> LOG.error("WriterThread encountered error", ioe);<a name="line.967"></a>
-<span class="sourceLineNo">968</span> }<a name="line.968"></a>
-<span class="sourceLineNo">969</span> }<a name="line.969"></a>
-<span class="sourceLineNo">970</span> } catch (Throwable t) {<a name="line.970"></a>
-<span class="sourceLineNo">971</span> LOG.warn("Failed doing drain", t);<a name="line.971"></a>
-<span class="sourceLineNo">972</span> }<a name="line.972"></a>
-<span class="sourceLineNo">973</span> LOG.info(this.getName() + " exiting, cacheEnabled=" + cacheEnabled);<a name="line.973"></a>
-<span class="sourceLineNo">974</span> }<a name="line.974"></a>
-<span class="sourceLineNo">975</span> }<a name="line.975"></a>
-<span class="sourceLineNo">976</span><a name="line.976"></a>
-<span class="sourceLineNo">977</span> /**<a name="line.977"></a>
-<span class="sourceLineNo">978</span> * Put the new bucket entry into backingMap. Notice that we are allowed to replace the existing<a name="line.978"></a>
-<span class="sourceLineNo">979</span> * cache with a new block for the same cache key. there's a corner case: one thread cache a block<a name="line.979"></a>
-<span class="sourceLineNo">980</span> * in ramCache, copy to io-engine and add a bucket entry to backingMap. Caching another new block<a name="line.980"></a>
-<span class="sourceLineNo">981</span> * with the same cache key do the same thing for the same cache key, so if not evict the previous<a name="line.981"></a>
-<span class="sourceLineNo">982</span> * bucket entry, then memory leak happen because the previous bucketEntry is gone but the<a name="line.982"></a>
-<span class="sourceLineNo">983</span> * bucketAllocator do not free its memory.<a name="line.983"></a>
-<span class="sourceLineNo">984</span> * @see BlockCacheUtil#shouldReplaceExistingCacheBlock(BlockCache blockCache,BlockCacheKey<a name="line.984"></a>
-<span class="sourceLineNo">985</span> * cacheKey, Cacheable newBlock)<a name="line.985"></a>
-<span class="sourceLineNo">986</span> * @param key Block cache key<a name="line.986"></a>
-<span class="sourceLineNo">987</span> * @param bucketEntry Bucket entry to put into backingMap.<a name="line.987"></a>
-<span class="sourceLineNo">988</span> */<a name="line.988"></a>
-<span class="sourceLineNo">989</span> protected void putIntoBackingMap(BlockCacheKey key, BucketEntry bucketEntry) {<a name="line.989"></a>
-<span class="sourceLineNo">990</span> BucketEntry previousEntry = backingMap.put(key, bucketEntry);<a name="line.990"></a>
-<span class="sourceLineNo">991</span> if (previousEntry != null && previousEntry != bucketEntry) {<a name="line.991"></a>
-<span class="sourceLineNo">992</span> previousEntry.withWriteLock(offsetLock, () -> {<a name="line.992"></a>
-<span class="sourceLineNo">993</span> blockEvicted(key, previousEntry, false);<a name="line.993"></a>
-<span class="sourceLineNo">994</span> return null;<a name="line.994"></a>
-<span class="sourceLineNo">995</span> });<a name="line.995"></a>
-<span class="sourceLineNo">996</span> }<a name="line.996"></a>
-<span class="sourceLineNo">997</span> }<a name="line.997"></a>
-<span class="sourceLineNo">998</span><a name="line.998"></a>
-<span class="sourceLineNo">999</span> /**<a name="line.999"></a>
-<span class="sourceLineNo">1000</span> * Flush the entries in ramCache to IOEngine and add bucket entry to backingMap. Process all that<a name="line.1000"></a>
-<span class="sourceLineNo">1001</span> * are passed in even if failure being sure to remove from ramCache else we'll never undo the<a name="line.1001"></a>
-<span class="sourceLineNo">1002</span> * references and we'll OOME.<a name="line.1002"></a>
-<span class="sourceLineNo">1003</span> * @param entries Presumes list passed in here will be processed by this invocation only. No<a name="line.1003"></a>
-<span class="sourceLineNo">1004</span> * interference expected.<a name="line.1004"></a>
-<span class="sourceLineNo">1005</span> */<a name="line.1005"></a>
-<span class="sourceLineNo">1006</span> void doDrain(final List<RAMQueueEntry> entries) throws InterruptedException {<a name="line.1006"></a>
-<span class="sourceLineNo">1007</span> if (entries.isEmpty()) {<a name="line.1007"></a>
-<span class="sourceLineNo">1008</span> return;<a name="line.1008"></a>
-<span class="sourceLineNo">1009</span> }<a name="line.1009"></a>
-<span class="sourceLineNo">1010</span> // This method is a little hard to follow. We run through the passed in entries and for each<a name="line.1010"></a>
-<span class="sourceLineNo">1011</span> // successful add, we add a non-null BucketEntry to the below bucketEntries. Later we must<a name="line.1011"></a>
-<span class="sourceLineNo">1012</span> // do cleanup making sure we've cleared ramCache of all entries regardless of whether we<a name="line.1012"></a>
-<span class="sourceLineNo">1013</span> // successfully added the item to the bucketcache; if we don't do the cleanup, we'll OOME by<a name="line.1013"></a>
-<span class="sourceLineNo">1014</span> // filling ramCache. We do the clean up by again running through the passed in entries<a name="line.1014"></a>
-<span class="sourceLineNo">1015</span> // doing extra work when we find a non-null bucketEntries corresponding entry.<a name="line.1015"></a>
-<span class="sourceLineNo">1016</span> final int size = entries.size();<a name="line.1016"></a>
-<span class="sourceLineNo">1017</span> BucketEntry[] bucketEntries = new BucketEntry[size];<a name="line.1017"></a>
-<span class="sourceLineNo">1018</span> // Index updated inside loop if success or if we can't succeed. We retry if cache is full<a name="line.1018"></a>
-<span class="sourceLineNo">1019</span> // when we go to add an entry by going around the loop again without upping the index.<a name="line.1019"></a>
-<span class="sourceLineNo">1020</span> int index = 0;<a name="line.1020"></a>
-<span class="sourceLineNo">1021</span> while (cacheEnabled && index < size) {<a name="line.1021"></a>
-<span class="sourceLineNo">1022</span> RAMQueueEntry re = null;<a name="line.1022"></a>
-<span class="sourceLineNo">1023</span> try {<a name="line.1023"></a>
-<span class="sourceLineNo">1024</span> re = entries.get(index);<a name="line.1024"></a>
-<span class="sourceLineNo">1025</span> if (re == null) {<a name="line.1025"></a>
-<span class="sourceLineNo">1026</span> LOG.warn("Couldn't get entry or changed on us; who else is messing with it?");<a name="line.1026"></a>
-<span class="sourceLineNo">1027</span> index++;<a name="line.1027"></a>
-<span class="sourceLineNo">1028</span> continue;<a name="line.1028"></a>
-<span class="sourceLineNo">1029</span> }<a name="line.1029"></a>
-<span class="sourceLineNo">1030</span> BucketEntry bucketEntry = re.writeToCache(ioEngine, bucketAllocator, realCacheSize,<a name="line.1030"></a>
-<span class="sourceLineNo">1031</span> this::createRecycler);<a name="line.1031"></a>
-<span class="sourceLineNo">1032</span> // Successfully added. Up index and add bucketEntry. Clear io exceptions.<a name="line.1032"></a>
-<span class="sourceLineNo">1033</span> bucketEntries[index] = bucketEntry;<a name="line.1033"></a>
-<span class="sourceLineNo">1034</span> if (ioErrorStartTime > 0) {<a name="line.1034"></a>
-<span class="sourceLineNo">1035</span> ioErrorStartTime = -1;<a name="line.1035"></a>
-<span class="sourceLineNo">1036</span> }<a name="line.1036"></a>
-<span class="sourceLineNo">1037</span> index++;<a name="line.1037"></a>
-<span class="sourceLineNo">1038</span> } catch (BucketAllocatorException fle) {<a name="line.1038"></a>
-<span class="sourceLineNo">1039</span> LOG.warn("Failed allocation for " + (re == null ? "" : re.getKey()) + "; " + fle);<a name="line.1039"></a>
-<span class="sourceLineNo">1040</span> // Presume can't add. Too big? Move index on. Entry will be cleared from ramCache below.<a name="line.1040"></a>
-<span class="sourceLineNo">1041</span> bucketEntries[index] = null;<a name="line.1041"></a>
-<span class="sourceLineNo">1042</span> index++;<a name="line.1042"></a>
-<span class="sourceLineNo">1043</span> } catch (CacheFullException cfe) {<a name="line.1043"></a>
-<span class="sourceLineNo">1044</span> // Cache full when we tried to add. Try freeing space and then retrying (don't up index)<a name="line.1044"></a>
-<span class="sourceLineNo">1045</span> if (!freeInProgress) {<a name="line.1045"></a>
-<span class="sourceLineNo">1046</span> freeSpace("Full!");<a name="line.1046"></a>
-<span class="sourceLineNo">1047</span> } else {<a name="line.1047"></a>
-<span class="sourceLineNo">1048</span> Thread.sleep(50);<a name="line.1048"></a>
-<span class="sourceLineNo">1049</span> }<a name="line.1049"></a>
-<span class="sourceLineNo">1050</span> } catch (IOException ioex) {<a name="line.1050"></a>
-<span class="sourceLineNo">1051</span> // Hopefully transient. Retry. checkIOErrorIsTolerated disables cache if problem.<a name="line.1051"></a>
-<span class="sourceLineNo">1052</span> LOG.error("Failed writing to bucket cache", ioex);<a name="line.1052"></a>
-<span class="sourceLineNo">1053</span> checkIOErrorIsTolerated();<a name="line.1053"></a>
-<span class="sourceLineNo">1054</span> }<a name="line.1054"></a>
-<span class="sourceLineNo">1055</span> }<a name="line.1055"></a>
-<span class="sourceLineNo">1056</span><a name="line.1056"></a>
-<span class="sourceLineNo">1057</span> // Make sure data pages are written on media before we update maps.<a name="line.1057"></a>
-<span class="sourceLineNo">1058</span> try {<a name="line.1058"></a>
-<span class="sourceLineNo">1059</span> ioEngine.sync();<a name="line.1059"></a>
-<span class="sourceLineNo">1060</span> } catch (IOException ioex) {<a name="line.1060"></a>
-<span class="sourceLineNo">1061</span> LOG.error("Failed syncing IO engine", ioex);<a name="line.1061"></a>
-<span class="sourceLineNo">1062</span> checkIOErrorIsTolerated();<a name="line.1062"></a>
-<span class="sourceLineNo">1063</span> // Since we failed sync, free the blocks in bucket allocator<a name="line.1063"></a>
-<span class="sourceLineNo">1064</span> for (int i = 0; i < entries.size(); ++i) {<a name="line.1064"></a>
-<span class="sourceLineNo">1065</span> if (bucketEntries[i] != null) {<a name="line.1065"></a>
-<span class="sourceLineNo">1066</span> bucketAllocator.freeBlock(bucketEntries[i].offset());<a name="line.1066"></a>
-<span class="sourceLineNo">1067</span> bucketEntries[i] = null;<a name="line.1067"></a>
-<span class="sourceLineNo">1068</span> }<a name="line.1068"></a>
-<span class="sourceLineNo">1069</span> }<a name="line.1069"></a>
-<span class="sourceLineNo">1070</span> }<a name="line.1070"></a>
-<span class="sourceLineNo">1071</span><a name="line.1071"></a>
-<span class="sourceLineNo">1072</span> // Now add to backingMap if successfully added to bucket cache. Remove from ramCache if<a name="line.1072"></a>
-<span class="sourceLineNo">1073</span> // success or error.<a name="line.1073"></a>
-<span class="sourceLineNo">1074</span> for (int i = 0; i < size; ++i) {<a name="line.1074"></a>
-<span class="sourceLineNo">1075</span> BlockCacheKey key = entries.get(i).getKey();<a name="line.1075"></a>
-<span class="sourceLineNo">1076</span> // Only add if non-null entry.<a name="line.1076"></a>
-<span class="sourceLineNo">1077</span> if (bucketEntries[i] != null) {<a name="line.1077"></a>
-<span class="sourceLineNo">1078</span> putIntoBackingMap(key, bucketEntries[i]);<a name="line.1078"></a>
-<span class="sourceLineNo">1079</span> }<a name="line.1079"></a>
-<span class="sourceLineNo">1080</span> // Always remove from ramCache even if we failed adding it to the block cache above.<a name="line.1080"></a>
-<span class="sourceLineNo">1081</span> boolean existed = ramCache.remove(key, re -> {<a name="line.1081"></a>
-<span class="sourceLineNo">1082</span> if (re != null) {<a name="line.1082"></a>
-<span class="sourceLineNo">1083</span> heapSize.add(-1 * re.getData().heapSize());<a name="line.1083"></a>
-<span class="sourceLineNo">1084</span> }<a name="line.1084"></a>
-<span class="sourceLineNo">1085</span> });<a name="line.1085"></a>
-<span class="sourceLineNo">1086</span> if (!existed && bucketEntries[i] != null) {<a name="line.1086"></a>
-<span class="sourceLineNo">1087</span> // Block should have already been evicted. Remove it and free space.<a name="line.1087"></a>
-<span class="sourceLineNo">1088</span> final BucketEntry bucketEntry = bucketEntries[i];<a name="line.1088"></a>
-<span class="sourceLineNo">1089</span> bucketEntry.withWriteLock(offsetLock, () -> {<a name="line.1089"></a>
-<span class="sourceLineNo">1090</span> if (backingMap.remove(key, bucketEntry)) {<a name="line.1090"></a>
-<span class="sourceLineNo">1091</span> blockEvicted(key, bucketEntry, false);<a name="line.1091"></a>
-<span class="sourceLineNo">1092</span> }<a name="line.1092"></a>
-<span class="sourceLineNo">1093</span> return null;<a name="line.1093"></a>
-<span class="sourceLineNo">1094</span> });<a name="line.1094"></a>
-<span class="sourceLineNo">1095</span> }<a name="line.1095"></a>
-<span class="sourceLineNo">1096</span> }<a name="line.1096"></a>
-<span class="sourceLineNo">1097</span><a name="line.1097"></a>
-<span class="sourceLineNo">1098</span> long used = bucketAllocator.getUsedSize();<a name="line.1098"></a>
-<span class="sourceLineNo">1099</span> if (used > acceptableSize()) {<a name="line.1099"></a>
-<span class="sourceLineNo">1100</span> freeSpace("Used=" + used + " > acceptable=" + acceptableSize());<a name="line.1100"></a>
-<span class="sourceLineNo">1101</span> }<a name="line.1101"></a>
-<span class="sourceLineNo">1102</span> return;<a name="line.1102"></a>
-<span class="sourceLineNo">1103</span> }<a name="line.1103"></a>
-<span class="sourceLineNo">1104</span><a name="line.1104"></a>
-<span class="sourceLineNo">1105</span> /**<a name="line.1105"></a>
-<span class="sourceLineNo">1106</span> * Blocks until elements available in {@code q} then tries to grab as many as possible before<a name="line.1106"></a>
-<span class="sourceLineNo">1107</span> * returning.<a name="line.1107"></a>
-<span class="sourceLineNo">1108</span> * @param receptacle Where to stash the elements taken from queue. We clear before we use it just<a name="line.1108"></a>
-<span class="sourceLineNo">1109</span> * in case.<a name="line.1109"></a>
-<span class="sourceLineNo">1110</span> * @param q The queue to take from.<a name="line.1110"></a>
-<span class="sourceLineNo">1111</span> * @return {@code receptacle} laden with elements taken from the queue or empty if none found.<a name="line.1111"></a>
-<span class="sourceLineNo">1112</span> */<a name="line.1112"></a>
-<span class="sourceLineNo">1113</span> static List<RAMQueueEntry> getRAMQueueEntries(BlockingQueue<RAMQueueEntry> q,<a name="line.1113"></a>
-<span class="sourceLineNo">1114</span> List<RAMQueueEntry> receptacle) throws InterruptedException {<a name="line.1114"></a>
-<span class="sourceLineNo">1115</span> // Clear sets all entries to null and sets size to 0. We retain allocations. Presume it<a name="line.1115"></a>
-<span class="sourceLineNo">1116</span> // ok even if list grew to accommodate thousands.<a name="line.1116"></a>
-<span class="sourceLineNo">1117</span> receptacle.clear();<a name="line.1117"></a>
-<span class="sourceLineNo">1118</span> receptacle.add(q.take());<a name="line.1118"></a>
-<span class="sourceLineNo">1119</span> q.drainTo(receptacle);<a name="line.1119"></a>
-<span class="sourceLineNo">1120</span> return receptacle;<a name="line.1120"></a>
-<span class="sourceLineNo">1121</span> }<a name="line.1121"></a>
-<span class="sourceLineNo">1122</span><a name="line.1122"></a>
-<span class="sourceLineNo">1123</span> /**<a name="line.1123"></a>
-<span class="sourceLineNo">1124</span> * @see #retrieveFromFile(int[])<a name="line.1124"></a>
-<span class="sourceLineNo">1125</span> */<a name="line.1125"></a>
-<span class="sourceLineNo">1126</span> @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="OBL_UNSATISFIED_OBLIGATION",<a name="line.1126"></a>
-<span class="sourceLineNo">1127</span> justification = "false positive, try-with-resources ensures close is called.")<a name="line.1127"></a>
-<span class="sourceLineNo">1128</span> private void persistToFile() throws IOException {<a name="line.1128"></a>
-<span class="sourceLineNo">1129</span> assert !cacheEnabled;<a name="line.1129"></a>
-<span class="sourceLineNo">1130</span> if (!ioEngine.isPersistent()) {<a name="line.1130"></a>
-<span class="sourceLineNo">1131</span> throw new IOException("Attempt to persist non-persistent cache mappings!");<a name="line.1131"></a>
-<span class="sourceLineNo">1132</span> }<a name="line.1132"></a>
-<span class="sourceLineNo">1133</span> try (FileOutputStream fos = new FileOutputStream(persistencePath, false)) {<a name="line.1133"></a>
-<span class="sourceLineNo">1134</span> fos.write(ProtobufMagic.PB_MAGIC);<a name="line.1134"></a>
-<span class="sourceLineNo">1135</span> BucketProtoUtils.toPB(this).writeDelimitedTo(fos);<a name="line.1135"></a>
+<span class="sourceLineNo">489</span> }<a name="line.489"></a>
+<span class="sourceLineNo">490</span> }<a name="line.490"></a>
+<span class="sourceLineNo">491</span><a name="line.491"></a>
+<span class="sourceLineNo">492</span> /**<a name="line.492"></a>
+<span class="sourceLineNo">493</span> * Get the buffer of the block with the specified key.<a name="line.493"></a>
+<span class="sourceLineNo">494</span> * @param key block's cache key<a name="line.494"></a>
+<span class="sourceLineNo">495</span> * @param caching true if the caller caches blocks on cache misses<a name="line.495"></a>
+<span class="sourceLineNo">496</span> * @param repeat Whether this is a repeat lookup for the same block<a name="line.496"></a>
+<span class="sourceLineNo">497</span> * @param updateCacheMetrics Whether we should update cache metrics or not<a name="line.497"></a>
+<span class="sourceLineNo">498</span> * @return buffer of specified cache key, or null if not in cache<a name="line.498"></a>
+<span class="sourceLineNo">499</span> */<a name="line.499"></a>
+<span class="sourceLineNo">500</span> @Override<a name="line.500"></a>
+<span class="sourceLineNo">501</span> public Cacheable getBlock(BlockCacheKey key, boolean caching, boolean repeat,<a name="line.501"></a>
+<span class="sourceLineNo">502</span> boolean updateCacheMetrics) {<a name="line.502"></a>
+<span class="sourceLineNo">503</span> if (!cacheEnabled) {<a name="line.503"></a>
+<span class="sourceLineNo">504</span> return null;<a name="line.504"></a>
+<span class="sourceLineNo">505</span> }<a name="line.505"></a>
+<span class="sourceLineNo">506</span> RAMQueueEntry re = ramCache.get(key);<a name="line.506"></a>
+<span class="sourceLineNo">507</span> if (re != null) {<a name="line.507"></a>
+<span class="sourceLineNo">508</span> if (updateCacheMetrics) {<a name="line.508"></a>
+<span class="sourceLineNo">509</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.509"></a>
+<span class="sourceLineNo">510</span> }<a name="line.510"></a>
+<span class="sourceLineNo">511</span> re.access(accessCount.incrementAndGet());<a name="line.511"></a>
+<span class="sourceLineNo">512</span> return re.getData();<a name="line.512"></a>
+<span class="sourceLineNo">513</span> }<a name="line.513"></a>
+<span class="sourceLineNo">514</span> BucketEntry bucketEntry = backingMap.get(key);<a name="line.514"></a>
+<span class="sourceLineNo">515</span> if (bucketEntry != null) {<a name="line.515"></a>
+<span class="sourceLineNo">516</span> long start = System.nanoTime();<a name="line.516"></a>
+<span class="sourceLineNo">517</span> ReentrantReadWriteLock lock = offsetLock.getLock(bucketEntry.offset());<a name="line.517"></a>
+<span class="sourceLineNo">518</span> try {<a name="line.518"></a>
+<span class="sourceLineNo">519</span> lock.readLock().lock();<a name="line.519"></a>
+<span class="sourceLineNo">520</span> // We can not read here even if backingMap does contain the given key because its offset<a name="line.520"></a>
+<span class="sourceLineNo">521</span> // maybe changed. If we lock BlockCacheKey instead of offset, then we can only check<a name="line.521"></a>
+<span class="sourceLineNo">522</span> // existence here.<a name="line.522"></a>
+<span class="sourceLineNo">523</span> if (bucketEntry.equals(backingMap.get(key))) {<a name="line.523"></a>
+<span class="sourceLineNo">524</span> // Read the block from IOEngine based on the bucketEntry's offset and length, NOTICE: the<a name="line.524"></a>
+<span class="sourceLineNo">525</span> // block will use the refCnt of bucketEntry, which means if two HFileBlock mapping to<a name="line.525"></a>
+<span class="sourceLineNo">526</span> // the same BucketEntry, then all of the three will share the same refCnt.<a name="line.526"></a>
+<span class="sourceLineNo">527</span> Cacheable cachedBlock = ioEngine.read(bucketEntry);<a name="line.527"></a>
+<span class="sourceLineNo">528</span> if (ioEngine.usesSharedMemory()) {<a name="line.528"></a>
+<span class="sourceLineNo">529</span> // If IOEngine use shared memory, cachedBlock and BucketEntry will share the<a name="line.529"></a>
+<span class="sourceLineNo">530</span> // same RefCnt, do retain here, in order to count the number of RPC references<a name="line.530"></a>
+<span class="sourceLineNo">531</span> cachedBlock.retain();<a name="line.531"></a>
+<span class="sourceLineNo">532</span> }<a name="line.532"></a>
+<span class="sourceLineNo">533</span> // Update the cache statistics.<a name="line.533"></a>
+<span class="sourceLineNo">534</span> if (updateCacheMetrics) {<a name="line.534"></a>
+<span class="sourceLineNo">535</span> cacheStats.hit(caching, key.isPrimary(), key.getBlockType());<a name="line.535"></a>
+<span class="sourceLineNo">536</span> cacheStats.ioHit(System.nanoTime() - start);<a name="line.536"></a>
+<span class="sourceLineNo">537</span> }<a name="line.537"></a>
+<span class="sourceLineNo">538</span> bucketEntry.access(accessCount.incrementAndGet());<a name="line.538"></a>
+<span class="sourceLineNo">539</span> if (this.ioErrorStartTime > 0) {<a name="line.539"></a>
+<span class="sourceLineNo">540</span> ioErrorStartTime = -1;<a name="line.540"></a>
+<span class="sourceLineNo">541</span> }<a name="line.541"></a>
+<span class="sourceLineNo">542</span> return cachedBlock;<a name="line.542"></a>
+<span class="sourceLineNo">543</span> }<a name="line.543"></a>
+<span class="sourceLineNo">544</span> } catch (IOException ioex) {<a name="line.544"></a>
+<span class="sourceLineNo">545</span> LOG.error("Failed reading block " + key + " from bucket cache", ioex);<a name="line.545"></a>
+<span class="sourceLineNo">546</span> checkIOErrorIsTolerated();<a name="line.546"></a>
+<span class="sourceLineNo">547</span> } finally {<a name="line.547"></a>
+<span class="sourceLineNo">548</span> lock.readLock().unlock();<a name="line.548"></a>
+<span class="sourceLineNo">549</span> }<a name="line.549"></a>
+<span class="sourceLineNo">550</span> }<a name="line.550"></a>
+<span class="sourceLineNo">551</span> if (!repeat && updateCacheMetrics) {<a name="line.551"></a>
+<span class="sourceLineNo">552</span> cacheStats.miss(caching, key.isPrimary(), key.getBlockType());<a name="line.552"></a>
+<span class="sourceLineNo">553</span> }<a name="line.553"></a>
+<span class="sourceLineNo">554</span> return null;<a name="line.554"></a>
+<span class="sourceLineNo">555</span> }<a name="line.555"></a>
+<span class="sourceLineNo">556</span><a name="line.556"></a>
+<span class="sourceLineNo">557</span> /**<a name="line.557"></a>
+<span class="sourceLineNo">558</span> * This method is invoked after the bucketEntry is removed from {@link BucketCache#backingMap}<a name="line.558"></a>
+<span class="sourceLineNo">559</span> */<a name="line.559"></a>
+<span class="sourceLineNo">560</span> void blockEvicted(BlockCacheKey cacheKey, BucketEntry bucketEntry, boolean decrementBlockNumber) {<a name="line.560"></a>
+<span class="sourceLineNo">561</span> bucketEntry.markAsEvicted();<a name="line.561"></a>
+<span class="sourceLineNo">562</span> blocksByHFile.remove(cacheKey);<a name="line.562"></a>
+<span class="sourceLineNo">563</span> if (decrementBlockNumber) {<a name="line.563"></a>
+<span class="sourceLineNo">564</span> this.blockNumber.decrement();<a name="line.564"></a>
+<span class="sourceLineNo">565</span> }<a name="line.565"></a>
+<span class="sourceLineNo">566</span> cacheStats.evicted(bucketEntry.getCachedTime(), cacheKey.isPrimary());<a name="line.566"></a>
+<span class="sourceLineNo">567</span> }<a name="line.567"></a>
+<span class="sourceLineNo">568</span><a name="line.568"></a>
+<span class="sourceLineNo">569</span> /**<a name="line.569"></a>
+<span class="sourceLineNo">570</span> * Free the {{@link BucketEntry} actually,which could only be invoked when the<a name="line.570"></a>
+<span class="sourceLineNo">571</span> * {@link BucketEntry#refCnt} becoming 0.<a name="line.571"></a>
+<span class="sourceLineNo">572</span> */<a name="line.572"></a>
+<span class="sourceLineNo">573</span> void freeBucketEntry(BucketEntry bucketEntry) {<a name="line.573"></a>
+<span class="sourceLineNo">574</span> bucketAllocator.freeBlock(bucketEntry.offset());<a name="line.574"></a>
+<span class="sourceLineNo">575</span> realCacheSize.add(-1 * bucketEntry.getLength());<a name="line.575"></a>
+<span class="sourceLineNo">576</span> }<a name="line.576"></a>
+<span class="sourceLineNo">577</span><a name="line.577"></a>
+<span class="sourceLineNo">578</span> /**<a name="line.578"></a>
+<span class="sourceLineNo">579</span> * Try to evict the block from {@link BlockCache} by force. We'll call this in few cases:<br><a name="line.579"></a>
+<span class="sourceLineNo">580</span> * 1. Close an HFile, and clear all cached blocks. <br><a name="line.580"></a>
+<span class="sourceLineNo">581</span> * 2. Call {@link Admin#clearBlockCache(TableName)} to clear all blocks for a given table.<br><a name="line.581"></a>
+<span class="sourceLineNo">582</span> * <p><a name="line.582"></a>
+<span class="sourceLineNo">583</span> * Firstly, we'll try to remove the block from RAMCache,and then try to evict from backingMap.<a name="line.583"></a>
+<span class="sourceLineNo">584</span> * Here we evict the block from backingMap immediately, but only free the reference from bucket<a name="line.584"></a>
+<span class="sourceLineNo">585</span> * cache by calling {@link BucketEntry#markedAsEvicted}. If there're still some RPC referring this<a name="line.585"></a>
+<span class="sourceLineNo">586</span> * block, block can only be de-allocated when all of them release the block.<a name="line.586"></a>
+<span class="sourceLineNo">587</span> * <p><a name="line.587"></a>
+<span class="sourceLineNo">588</span> * NOTICE: we need to grab the write offset lock firstly before releasing the reference from<a name="line.588"></a>
+<span class="sourceLineNo">589</span> * bucket cache. if we don't, we may read an {@link BucketEntry} with refCnt = 0 when<a name="line.589"></a>
+<span class="sourceLineNo">590</span> * {@link BucketCache#getBlock(BlockCacheKey, boolean, boolean, boolean)}, it's a memory leak.<a name="line.590"></a>
+<span class="sourceLineNo">591</span> * @param cacheKey Block to evict<a name="line.591"></a>
+<span class="sourceLineNo">592</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.592"></a>
+<span class="sourceLineNo">593</span> */<a name="line.593"></a>
+<span class="sourceLineNo">594</span> @Override<a name="line.594"></a>
+<span class="sourceLineNo">595</span> public boolean evictBlock(BlockCacheKey cacheKey) {<a name="line.595"></a>
+<span class="sourceLineNo">596</span> return doEvictBlock(cacheKey, null);<a name="line.596"></a>
+<span class="sourceLineNo">597</span> }<a name="line.597"></a>
+<span class="sourceLineNo">598</span><a name="line.598"></a>
+<span class="sourceLineNo">599</span> /**<a name="line.599"></a>
+<span class="sourceLineNo">600</span> * Evict the {@link BlockCacheKey} and {@link BucketEntry} from {@link BucketCache#backingMap} and<a name="line.600"></a>
+<span class="sourceLineNo">601</span> * {@link BucketCache#ramCache}. <br/><a name="line.601"></a>
+<span class="sourceLineNo">602</span> * NOTE:When Evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.602"></a>
+<span class="sourceLineNo">603</span> * {@link BucketEntry} could be removed.<a name="line.603"></a>
+<span class="sourceLineNo">604</span> * @param cacheKey {@link BlockCacheKey} to evict.<a name="line.604"></a>
+<span class="sourceLineNo">605</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.605"></a>
+<span class="sourceLineNo">606</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.606"></a>
+<span class="sourceLineNo">607</span> */<a name="line.607"></a>
+<span class="sourceLineNo">608</span> private boolean doEvictBlock(BlockCacheKey cacheKey, BucketEntry bucketEntry) {<a name="line.608"></a>
+<span class="sourceLineNo">609</span> if (!cacheEnabled) {<a name="line.609"></a>
+<span class="sourceLineNo">610</span> return false;<a name="line.610"></a>
+<span class="sourceLineNo">611</span> }<a name="line.611"></a>
+<span class="sourceLineNo">612</span> boolean existedInRamCache = removeFromRamCache(cacheKey);<a name="line.612"></a>
+<span class="sourceLineNo">613</span> if (bucketEntry == null) {<a name="line.613"></a>
+<span class="sourceLineNo">614</span> bucketEntry = backingMap.get(cacheKey);<a name="line.614"></a>
+<span class="sourceLineNo">615</span> }<a name="line.615"></a>
+<span class="sourceLineNo">616</span> final BucketEntry bucketEntryToUse = bucketEntry;<a name="line.616"></a>
+<span class="sourceLineNo">617</span><a name="line.617"></a>
+<span class="sourceLineNo">618</span> if (bucketEntryToUse == null) {<a name="line.618"></a>
+<span class="sourceLineNo">619</span> if (existedInRamCache) {<a name="line.619"></a>
+<span class="sourceLineNo">620</span> cacheStats.evicted(0, cacheKey.isPrimary());<a name="line.620"></a>
+<span class="sourceLineNo">621</span> }<a name="line.621"></a>
+<span class="sourceLineNo">622</span> return existedInRamCache;<a name="line.622"></a>
+<span class="sourceLineNo">623</span> } else {<a name="line.623"></a>
+<span class="sourceLineNo">624</span> return bucketEntryToUse.withWriteLock(offsetLock, () -> {<a name="line.624"></a>
+<span class="sourceLineNo">625</span> if (backingMap.remove(cacheKey, bucketEntryToUse)) {<a name="line.625"></a>
+<span class="sourceLineNo">626</span> blockEvicted(cacheKey, bucketEntryToUse, !existedInRamCache);<a name="line.626"></a>
+<span class="sourceLineNo">627</span> return true;<a name="line.627"></a>
+<span class="sourceLineNo">628</span> }<a name="line.628"></a>
+<span class="sourceLineNo">629</span> return false;<a name="line.629"></a>
+<span class="sourceLineNo">630</span> });<a name="line.630"></a>
+<span class="sourceLineNo">631</span> }<a name="line.631"></a>
+<span class="sourceLineNo">632</span> }<a name="line.632"></a>
+<span class="sourceLineNo">633</span><a name="line.633"></a>
+<span class="sourceLineNo">634</span> /**<a name="line.634"></a>
+<span class="sourceLineNo">635</span> * <pre><a name="line.635"></a>
+<span class="sourceLineNo">636</span> * Create the {@link Recycler} for {@link BucketEntry#refCnt},which would be used as<a name="line.636"></a>
+<span class="sourceLineNo">637</span> * {@link RefCnt#recycler} of {@link HFileBlock#buf} returned from {@link BucketCache#getBlock}.<a name="line.637"></a>
+<span class="sourceLineNo">638</span> * NOTE: for {@link BucketCache#getBlock},the {@link RefCnt#recycler} of {@link HFileBlock#buf}<a name="line.638"></a>
+<span class="sourceLineNo">639</span> * from {@link BucketCache#backingMap} and {@link BucketCache#ramCache} are different:<a name="line.639"></a>
+<span class="sourceLineNo">640</span> * 1.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#backingMap},<a name="line.640"></a>
+<span class="sourceLineNo">641</span> * it is the return value of current {@link BucketCache#createRecycler} method.<a name="line.641"></a>
+<span class="sourceLineNo">642</span> *<a name="line.642"></a>
+<span class="sourceLineNo">643</span> * 2.For {@link RefCnt#recycler} of {@link HFileBlock#buf} from {@link BucketCache#ramCache},<a name="line.643"></a>
+<span class="sourceLineNo">644</span> * it is {@link ByteBuffAllocator#putbackBuffer}.<a name="line.644"></a>
+<span class="sourceLineNo">645</span> * </pre><a name="line.645"></a>
+<span class="sourceLineNo">646</span> */<a name="line.646"></a>
+<span class="sourceLineNo">647</span> private Recycler createRecycler(final BucketEntry bucketEntry) {<a name="line.647"></a>
+<span class="sourceLineNo">648</span> return () -> {<a name="line.648"></a>
+<span class="sourceLineNo">649</span> freeBucketEntry(bucketEntry);<a name="line.649"></a>
+<span class="sourceLineNo">650</span> return;<a name="line.650"></a>
+<span class="sourceLineNo">651</span> };<a name="line.651"></a>
+<span class="sourceLineNo">652</span> }<a name="line.652"></a>
+<span class="sourceLineNo">653</span><a name="line.653"></a>
+<span class="sourceLineNo">654</span> /**<a name="line.654"></a>
+<span class="sourceLineNo">655</span> * NOTE: This method is only for test.<a name="line.655"></a>
+<span class="sourceLineNo">656</span> */<a name="line.656"></a>
+<span class="sourceLineNo">657</span> public boolean evictBlockIfNoRpcReferenced(BlockCacheKey blockCacheKey) {<a name="line.657"></a>
+<span class="sourceLineNo">658</span> BucketEntry bucketEntry = backingMap.get(blockCacheKey);<a name="line.658"></a>
+<span class="sourceLineNo">659</span> if (bucketEntry == null) {<a name="line.659"></a>
+<span class="sourceLineNo">660</span> return false;<a name="line.660"></a>
+<span class="sourceLineNo">661</span> }<a name="line.661"></a>
+<span class="sourceLineNo">662</span> return evictBucketEntryIfNoRpcReferenced(blockCacheKey, bucketEntry);<a name="line.662"></a>
+<span class="sourceLineNo">663</span> }<a name="line.663"></a>
+<span class="sourceLineNo">664</span><a name="line.664"></a>
+<span class="sourceLineNo">665</span> /**<a name="line.665"></a>
+<span class="sourceLineNo">666</span> * Evict {@link BlockCacheKey} and its corresponding {@link BucketEntry} only if<a name="line.666"></a>
+<span class="sourceLineNo">667</span> * {@link BucketEntry#isRpcRef} is false. <br/><a name="line.667"></a>
+<span class="sourceLineNo">668</span> * NOTE:When evict from {@link BucketCache#backingMap},only the matched {@link BlockCacheKey} and<a name="line.668"></a>
+<span class="sourceLineNo">669</span> * {@link BucketEntry} could be removed.<a name="line.669"></a>
+<span class="sourceLineNo">670</span> * @param blockCacheKey {@link BlockCacheKey} to evict.<a name="line.670"></a>
+<span class="sourceLineNo">671</span> * @param bucketEntry {@link BucketEntry} matched {@link BlockCacheKey} to evict.<a name="line.671"></a>
+<span class="sourceLineNo">672</span> * @return true to indicate whether we've evicted successfully or not.<a name="line.672"></a>
+<span class="sourceLineNo">673</span> */<a name="line.673"></a>
+<span class="sourceLineNo">674</span> boolean evictBucketEntryIfNoRpcReferenced(BlockCacheKey blockCacheKey, BucketEntry bucketEntry) {<a name="line.674"></a>
+<span class="sourceLineNo">675</span> if (!bucketEntry.isRpcRef()) {<a name="line.675"></a>
+<span class="sourceLineNo">676</span> return doEvictBlock(blockCacheKey, bucketEntry);<a name="line.676"></a>
+<span class="sourceLineNo">677</span> }<a name="line.677"></a>
+<span class="sourceLineNo">678</span> return false;<a name="line.678"></a>
+<span class="sourceLineNo">679</span> }<a name="line.679"></a>
+<span class="sourceLineNo">680</span><a name="line.680"></a>
+<span class="sourceLineNo">681</span> protected boolean removeFromRamCache(BlockCacheKey cacheKey) {<a name="line.681"></a>
+<span class="sourceLineNo">682</span> return ramCache.remove(cacheKey, re -> {<a name="line.682"></a>
+<span class="sourceLineNo">683</span> if (re != null) {<a name="line.683"></a>
+<span class="sourceLineNo">684</span> this.blockNumber.decrement();<a name="line.684"></a>
+<span class="sourceLineNo">685</span> this.heapSize.add(-1 * re.getData().heapSize());<a name="line.685"></a>
+<span class="sourceLineNo">686</span> }<a name="line.686"></a>
+<span class="sourceLineNo">687</span> });<a name="line.687"></a>
+<span class="sourceLineNo">688</span> }<a name="line.688"></a>
+<span class="sourceLineNo">689</span><a name="line.689"></a>
+<span class="sourceLineNo">690</span> /*<a name="line.690"></a>
+<span class="sourceLineNo">691</span> * Statistics thread. Periodically output cache statistics to the log.<a name="line.691"></a>
+<span class="sourceLineNo">692</span> */<a name="line.692"></a>
+<span class="sourceLineNo">693</span> private static class StatisticsThread extends Thread {<a name="line.693"></a>
+<span class="sourceLineNo">694</span> private final BucketCache bucketCache;<a name="line.694"></a>
+<span class="sourceLineNo">695</span><a name="line.695"></a>
+<span class="sourceLineNo">696</span> public StatisticsThread(BucketCache bucketCache) {<a name="line.696"></a>
+<span class="sourceLineNo">697</span> super("BucketCacheStatsThread");<a name="line.697"></a>
+<span class="sourceLineNo">698</span> setDaemon(true);<a name="line.698"></a>
+<span class="sourceLineNo">699</span> this.bucketCache = bucketCache;<a name="line.699"></a>
+<span class="sourceLineNo">700</span> }<a name="line.700"></a>
+<span class="sourceLineNo">701</span><a name="line.701"></a>
+<span class="sourceLineNo">702</span> @Override<a name="line.702"></a>
+<span class="sourceLineNo">703</span> public void run() {<a name="line.703"></a>
+<span class="sourceLineNo">704</span> bucketCache.logStats();<a name="line.704"></a>
+<span class="sourceLineNo">705</span> }<a name="line.705"></a>
+<span class="sourceLineNo">706</span> }<a name="line.706"></a>
+<span class="sourceLineNo">707</span><a name="line.707"></a>
+<span class="sourceLineNo">708</span> public void logStats() {<a name="line.708"></a>
+<span class="sourceLineNo">709</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.709"></a>
+<span class="sourceLineNo">710</span> long usedSize = bucketAllocator.getUsedSize();<a name="line.710"></a>
+<span class="sourceLineNo">711</span> long freeSize = totalSize - usedSize;<a name="line.711"></a>
+<span class="sourceLineNo">712</span> long cacheSize = getRealCacheSize();<a name="line.712"></a>
+<span class="sourceLineNo">713</span> LOG.info("failedBlockAdditions=" + cacheStats.getFailedInserts() + ", " +<a name="line.713"></a>
+<span class="sourceLineNo">714</span> "totalSize=" + StringUtils.byteDesc(totalSize) + ", " +<a name="line.714"></a>
+<span class="sourceLineNo">715</span> "freeSize=" + StringUtils.byteDesc(freeSize) + ", " +<a name="line.715"></a>
+<span class="sourceLineNo">716</span> "usedSize=" + StringUtils.byteDesc(usedSize) +", " +<a name="line.716"></a>
+<span class="sourceLineNo">717</span> "cacheSize=" + StringUtils.byteDesc(cacheSize) +", " +<a name="line.717"></a>
+<span class="sourceLineNo">718</span> "accesses=" + cacheStats.getRequestCount() + ", " +<a name="line.718"></a>
+<span class="sourceLineNo">719</span> "hits=" + cacheStats.getHitCount() + ", " +<a name="line.719"></a>
+<span class="sourceLineNo">720</span> "IOhitsPerSecond=" + cacheStats.getIOHitsPerSecond() + ", " +<a name="line.720"></a>
+<span class="sourceLineNo">721</span> "IOTimePerHit=" + String.format("%.2f", cacheStats.getIOTimePerHit())+ ", " +<a name="line.721"></a>
+<span class="sourceLineNo">722</span> "hitRatio=" + (cacheStats.getHitCount() == 0 ? "0," :<a name="line.722"></a>
+<span class="sourceLineNo">723</span> (StringUtils.formatPercent(cacheStats.getHitRatio(), 2)+ ", ")) +<a name="line.723"></a>
+<span class="sourceLineNo">724</span> "cachingAccesses=" + cacheStats.getRequestCachingCount() + ", " +<a name="line.724"></a>
+<span class="sourceLineNo">725</span> "cachingHits=" + cacheStats.getHitCachingCount() + ", " +<a name="line.725"></a>
+<span class="sourceLineNo">726</span> "cachingHitsRatio=" +(cacheStats.getHitCachingCount() == 0 ? "0," :<a name="line.726"></a>
+<span class="sourceLineNo">727</span> (StringUtils.formatPercent(cacheStats.getHitCachingRatio(), 2)+ ", ")) +<a name="line.727"></a>
+<span class="sourceLineNo">728</span> "evictions=" + cacheStats.getEvictionCount() + ", " +<a name="line.728"></a>
+<span class="sourceLineNo">729</span> "evicted=" + cacheStats.getEvictedCount() + ", " +<a name="line.729"></a>
+<span class="sourceLineNo">730</span> "evictedPerRun=" + cacheStats.evictedPerEviction());<a name="line.730"></a>
+<span class="sourceLineNo">731</span> cacheStats.reset();<a name="line.731"></a>
+<span class="sourceLineNo">732</span> }<a name="line.732"></a>
+<span class="sourceLineNo">733</span><a name="line.733"></a>
+<span class="sourceLineNo">734</span> public long getRealCacheSize() {<a name="line.734"></a>
+<span class="sourceLineNo">735</span> return this.realCacheSize.sum();<a name="line.735"></a>
+<span class="sourceLineNo">736</span> }<a name="line.736"></a>
+<span class="sourceLineNo">737</span><a name="line.737"></a>
+<span class="sourceLineNo">738</span> public long acceptableSize() {<a name="line.738"></a>
+<span class="sourceLineNo">739</span> return (long) Math.floor(bucketAllocator.getTotalSize() * acceptableFactor);<a name="line.739"></a>
+<span class="sourceLineNo">740</span> }<a name="line.740"></a>
+<span class="sourceLineNo">741</span><a name="line.741"></a>
+<span class="sourceLineNo">742</span> long getPartitionSize(float partitionFactor) {<a name="line.742"></a>
+<span class="sourceLineNo">743</span> return (long) Math.floor(bucketAllocator.getTotalSize() * partitionFactor * minFactor);<a name="line.743"></a>
+<span class="sourceLineNo">744</span> }<a name="line.744"></a>
+<span class="sourceLineNo">745</span><a name="line.745"></a>
+<span class="sourceLineNo">746</span> /**<a name="line.746"></a>
+<span class="sourceLineNo">747</span> * Return the count of bucketSizeinfos still need free space<a name="line.747"></a>
+<span class="sourceLineNo">748</span> */<a name="line.748"></a>
+<span class="sourceLineNo">749</span> private int bucketSizesAboveThresholdCount(float minFactor) {<a name="line.749"></a>
+<span class="sourceLineNo">750</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.750"></a>
+<span class="sourceLineNo">751</span> int fullCount = 0;<a name="line.751"></a>
+<span class="sourceLineNo">752</span> for (int i = 0; i < stats.length; i++) {<a name="line.752"></a>
+<span class="sourceLineNo">753</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.753"></a>
+<span class="sourceLineNo">754</span> freeGoal = Math.max(freeGoal, 1);<a name="line.754"></a>
+<span class="sourceLineNo">755</span> if (stats[i].freeCount() < freeGoal) {<a name="line.755"></a>
+<span class="sourceLineNo">756</span> fullCount++;<a name="line.756"></a>
+<span class="sourceLineNo">757</span> }<a name="line.757"></a>
+<span class="sourceLineNo">758</span> }<a name="line.758"></a>
+<span class="sourceLineNo">759</span> return fullCount;<a name="line.759"></a>
+<span class="sourceLineNo">760</span> }<a name="line.760"></a>
+<span class="sourceLineNo">761</span><a name="line.761"></a>
+<span class="sourceLineNo">762</span> /**<a name="line.762"></a>
+<span class="sourceLineNo">763</span> * This method will find the buckets that are minimally occupied<a name="line.763"></a>
+<span class="sourceLineNo">764</span> * and are not reference counted and will free them completely<a name="line.764"></a>
+<span class="sourceLineNo">765</span> * without any constraint on the access times of the elements,<a name="line.765"></a>
+<span class="sourceLineNo">766</span> * and as a process will completely free at most the number of buckets<a name="line.766"></a>
+<span class="sourceLineNo">767</span> * passed, sometimes it might not due to changing refCounts<a name="line.767"></a>
+<span class="sourceLineNo">768</span> *<a name="line.768"></a>
+<span class="sourceLineNo">769</span> * @param completelyFreeBucketsNeeded number of buckets to free<a name="line.769"></a>
+<span class="sourceLineNo">770</span> **/<a name="line.770"></a>
+<span class="sourceLineNo">771</span> private void freeEntireBuckets(int completelyFreeBucketsNeeded) {<a name="line.771"></a>
+<span class="sourceLineNo">772</span> if (completelyFreeBucketsNeeded != 0) {<a name="line.772"></a>
+<span class="sourceLineNo">773</span> // First we will build a set where the offsets are reference counted, usually<a name="line.773"></a>
+<span class="sourceLineNo">774</span> // this set is small around O(Handler Count) unless something else is wrong<a name="line.774"></a>
+<span class="sourceLineNo">775</span> Set<Integer> inUseBuckets = new HashSet<>();<a name="line.775"></a>
+<span class="sourceLineNo">776</span> backingMap.forEach((k, be) -> {<a name="line.776"></a>
+<span class="sourceLineNo">777</span> if (be.isRpcRef()) {<a name="line.777"></a>
+<span class="sourceLineNo">778</span> inUseBuckets.add(bucketAllocator.getBucketIndex(be.offset()));<a name="line.778"></a>
+<span class="sourceLineNo">779</span> }<a name="line.779"></a>
+<span class="sourceLineNo">780</span> });<a name="line.780"></a>
+<span class="sourceLineNo">781</span> Set<Integer> candidateBuckets =<a name="line.781"></a>
+<span class="sourceLineNo">782</span> bucketAllocator.getLeastFilledBuckets(inUseBuckets, completelyFreeBucketsNeeded);<a name="line.782"></a>
+<span class="sourceLineNo">783</span> for (Map.Entry<BlockCacheKey, BucketEntry> entry : backingMap.entrySet()) {<a name="line.783"></a>
+<span class="sourceLineNo">784</span> if (candidateBuckets.contains(bucketAllocator.getBucketIndex(entry.getValue().offset()))) {<a name="line.784"></a>
+<span class="sourceLineNo">785</span> evictBucketEntryIfNoRpcReferenced(entry.getKey(), entry.getValue());<a name="line.785"></a>
+<span class="sourceLineNo">786</span> }<a name="line.786"></a>
+<span class="sourceLineNo">787</span> }<a name="line.787"></a>
+<span class="sourceLineNo">788</span> }<a name="line.788"></a>
+<span class="sourceLineNo">789</span> }<a name="line.789"></a>
+<span class="sourceLineNo">790</span><a name="line.790"></a>
+<span class="sourceLineNo">791</span> /**<a name="line.791"></a>
+<span class="sourceLineNo">792</span> * Free the space if the used size reaches acceptableSize() or one size block<a name="line.792"></a>
+<span class="sourceLineNo">793</span> * couldn't be allocated. When freeing the space, we use the LRU algorithm and<a name="line.793"></a>
+<span class="sourceLineNo">794</span> * ensure there must be some blocks evicted<a name="line.794"></a>
+<span class="sourceLineNo">795</span> * @param why Why we are being called<a name="line.795"></a>
+<span class="sourceLineNo">796</span> */<a name="line.796"></a>
+<span class="sourceLineNo">797</span> private void freeSpace(final String why) {<a name="line.797"></a>
+<span class="sourceLineNo">798</span> // Ensure only one freeSpace progress at a time<a name="line.798"></a>
+<span class="sourceLineNo">799</span> if (!freeSpaceLock.tryLock()) {<a name="line.799"></a>
+<span class="sourceLineNo">800</span> return;<a name="line.800"></a>
+<span class="sourceLineNo">801</span> }<a name="line.801"></a>
+<span class="sourceLineNo">802</span> try {<a name="line.802"></a>
+<span class="sourceLineNo">803</span> freeInProgress = true;<a name="line.803"></a>
+<span class="sourceLineNo">804</span> long bytesToFreeWithoutExtra = 0;<a name="line.804"></a>
+<span class="sourceLineNo">805</span> // Calculate free byte for each bucketSizeinfo<a name="line.805"></a>
+<span class="sourceLineNo">806</span> StringBuilder msgBuffer = LOG.isDebugEnabled()? new StringBuilder(): null;<a name="line.806"></a>
+<span class="sourceLineNo">807</span> BucketAllocator.IndexStatistics[] stats = bucketAllocator.getIndexStatistics();<a name="line.807"></a>
+<span class="sourceLineNo">808</span> long[] bytesToFreeForBucket = new long[stats.length];<a name="line.808"></a>
+<span class="sourceLineNo">809</span> for (int i = 0; i < stats.length; i++) {<a name="line.809"></a>
+<span class="sourceLineNo">810</span> bytesToFreeForBucket[i] = 0;<a name="line.810"></a>
+<span class="sourceLineNo">811</span> long freeGoal = (long) Math.floor(stats[i].totalCount() * (1 - minFactor));<a name="line.811"></a>
+<span class="sourceLineNo">812</span> freeGoal = Math.max(freeGoal, 1);<a name="line.812"></a>
+<span class="sourceLineNo">813</span> if (stats[i].freeCount() < freeGoal) {<a name="line.813"></a>
+<span class="sourceLineNo">814</span> bytesToFreeForBucket[i] = stats[i].itemSize() * (freeGoal - stats[i].freeCount());<a name="line.814"></a>
+<span class="sourceLineNo">815</span> bytesToFreeWithoutExtra += bytesToFreeForBucket[i];<a name="line.815"></a>
+<span class="sourceLineNo">816</span> if (msgBuffer != null) {<a name="line.816"></a>
+<span class="sourceLineNo">817</span> msgBuffer.append("Free for bucketSize(" + stats[i].itemSize() + ")="<a name="line.817"></a>
+<span class="sourceLineNo">818</span> + StringUtils.byteDesc(bytesToFreeForBucket[i]) + ", ");<a name="line.818"></a>
+<span class="sourceLineNo">819</span> }<a name="line.819"></a>
+<span class="sourceLineNo">820</span> }<a name="line.820"></a>
+<span class="sourceLineNo">821</span> }<a name="line.821"></a>
+<span class="sourceLineNo">822</span> if (msgBuffer != null) {<a name="line.822"></a>
+<span class="sourceLineNo">823</span> msgBuffer.append("Free for total=" + StringUtils.byteDesc(bytesToFreeWithoutExtra) + ", ");<a name="line.823"></a>
+<span class="sourceLineNo">824</span> }<a name="line.824"></a>
+<span class="sourceLineNo">825</span><a name="line.825"></a>
+<span class="sourceLineNo">826</span> if (bytesToFreeWithoutExtra <= 0) {<a name="line.826"></a>
+<span class="sourceLineNo">827</span> return;<a name="line.827"></a>
+<span class="sourceLineNo">828</span> }<a name="line.828"></a>
+<span class="sourceLineNo">829</span> long currentSize = bucketAllocator.getUsedSize();<a name="line.829"></a>
+<span class="sourceLineNo">830</span> long totalSize = bucketAllocator.getTotalSize();<a name="line.830"></a>
+<span class="sourceLineNo">831</span> if (LOG.isDebugEnabled() && msgBuffer != null) {<a name="line.831"></a>
+<span class="sourceLineNo">832</span> LOG.debug("Free started because \"" + why + "\"; " + msgBuffer.toString() +<a name="line.832"></a>
+<span class="sourceLineNo">833</span> " of current used=" + StringUtils.byteDesc(currentSize) + ", actual cacheSize=" +<a name="line.833"></a>
+<span class="sourceLineNo">834</span> StringUtils.byteDesc(realCacheSize.sum()) + ", total=" + StringUtils.byteDesc(totalSize));<a name="line.834"></a>
+<span class="sourceLineNo">835</span> }<a name="line.835"></a>
+<span class="sourceLineNo">836</span><a name="line.836"></a>
+<span class="sourceLineNo">837</span> long bytesToFreeWithExtra = (long) Math.floor(bytesToFreeWithoutExtra<a name="line.837"></a>
+<span class="sourceLineNo">838</span> * (1 + extraFreeFactor));<a name="line.838"></a>
+<span class="sourceLineNo">839</span><a name="line.839"></a>
+<span class="sourceLineNo">840</span> // Instantiate priority buckets<a name="line.840"></a>
+<span class="sourceLineNo">841</span> BucketEntryGroup bucketSingle = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.841"></a>
+<span class="sourceLineNo">842</span> blockSize, getPartitionSize(singleFactor));<a name="line.842"></a>
+<span class="sourceLineNo">843</span> BucketEntryGroup bucketMulti = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.843"></a>
+<span class="sourceLineNo">844</span> blockSize, getPartitionSize(multiFactor));<a name="line.844"></a>
+<span class="sourceLineNo">845</span> BucketEntryGroup bucketMemory = new BucketEntryGroup(bytesToFreeWithExtra,<a name="line.845"></a>
+<span class="sourceLineNo">846</span> blockSize, getPartitionSize(memoryFactor));<a name="line.846"></a>
+<span class="sourceLineNo">847</span><a name="line.847"></a>
+<span class="sourceLineNo">848</span> // Scan entire map putting bucket entry into appropriate bucket entry<a name="line.848"></a>
+<span class="sourceLineNo">849</span> // group<a name="line.849"></a>
+<span class="sourceLineNo">850</span> for (Map.Entry<BlockCacheKey, BucketEntry> bucketEntryWithKey : backingMap.entrySet()) {<a name="line.850"></a>
+<span class="sourceLineNo">851</span> switch (bucketEntryWithKey.getValue().getPriority()) {<a name="line.851"></a>
+<span class="sourceLineNo">852</span> case SINGLE: {<a name="line.852"></a>
+<span class="sourceLineNo">853</span> bucketSingle.add(bucketEntryWithKey);<a name="line.853"></a>
+<span class="sourceLineNo">854</span> break;<a name="line.854"></a>
+<span class="sourceLineNo">855</span> }<a name="line.855"></a>
+<span class="sourceLineNo">856</span> case MULTI: {<a name="line.856"></a>
+<span class="sourceLineNo">857</span> bucketMulti.add(bucketEntryWithKey);<a name="line.857"></a>
+<span class="sourceLineNo">858</span> break;<a name="line.858"></a>
+<span class="sourceLineNo">859</span> }<a name="line.859"></a>
+<span class="sourceLineNo">860</span> case MEMORY: {<a name="line.860"></a>
+<span class="sourceLineNo">861</span> bucketMemory.add(bucketEntryWithKey);<a name="line.861"></a>
+<span class="sourceLineNo">862</span> break;<a name="line.862"></a>
+<span class="sourceLineNo">863</span> }<a name="line.863"></a>
+<span class="sourceLineNo">864</span> }<a name="line.864"></a>
+<span class="sourceLineNo">865</span> }<a name="line.865"></a>
+<span class="sourceLineNo">866</span><a name="line.866"></a>
+<span class="sourceLineNo">867</span> PriorityQueue<BucketEntryGroup> bucketQueue = new PriorityQueue<>(3,<a name="line.867"></a>
+<span class="sourceLineNo">868</span> Comparator.comparingLong(BucketEntryGroup::overflow));<a name="line.868"></a>
+<span class="sourceLineNo">869</span><a name="line.869"></a>
+<span class="sourceLineNo">870</span> bucketQueue.add(bucketSingle);<a name="line.870"></a>
+<span class="sourceLineNo">871</span> bucketQueue.add(bucketMulti);<a name="line.871"></a>
+<span class="sourceLineNo">872</span> bucketQueue.add(bucketMemory);<a name="line.872"></a>
+<span class="sourceLineNo">873</span><a name="line.873"></a>
+<span class="sourceLineNo">874</span> int remainingBuckets = bucketQueue.size();<a name="line.874"></a>
+<span class="sourceLineNo">875</span> long bytesFreed = 0;<a name="line.875"></a>
+<span class="sourceLineNo">876</span><a name="line.876"></a>
+<span class="sourceLineNo">877</span> BucketEntryGroup bucketGroup;<a name="line.877"></a>
+<span class="sourceLineNo">878</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.878"></a>
+<span class="sourceLineNo">879</span> long overflow = bucketGroup.overflow();<a name="line.879"></a>
+<span class="sourceLineNo">880</span> if (overflow > 0) {<a name="line.880"></a>
+<span class="sourceLineNo">881</span> long bucketBytesToFree = Math.min(overflow,<a name="line.881"></a>
+<span class="sourceLineNo">882</span> (bytesToFreeWithoutExtra - bytesFreed) / remainingBuckets);<a name="line.882"></a>
+<span class="sourceLineNo">883</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.883"></a>
+<span class="sourceLineNo">884</span> }<a name="line.884"></a>
+<span class="sourceLineNo">885</span> remainingBuckets--;<a name="line.885"></a>
+<span class="sourceLineNo">886</span> }<a name="line.886"></a>
+<span class="sourceLineNo">887</span><a name="line.887"></a>
+<span class="sourceLineNo">888</span> // Check and free if there are buckets that still need freeing of space<a name="line.888"></a>
+<span class="sourceLineNo">889</span> if (bucketSizesAboveThresholdCount(minFactor) > 0) {<a name="line.889"></a>
+<span class="sourceLineNo">890</span> bucketQueue.clear();<a name="line.890"></a>
+<span class="sourceLineNo">891</span> remainingBuckets = 3;<a name="line.891"></a>
+<span class="sourceLineNo">892</span><a name="line.892"></a>
+<span class="sourceLineNo">893</span> bucketQueue.add(bucketSingle);<a name="line.893"></a>
+<span class="sourceLineNo">894</span> bucketQueue.add(bucketMulti);<a name="line.894"></a>
+<span class="sourceLineNo">895</span> bucketQueue.add(bucketMemory);<a name="line.895"></a>
+<span class="sourceLineNo">896</span><a name="line.896"></a>
+<span class="sourceLineNo">897</span> while ((bucketGroup = bucketQueue.poll()) != null) {<a name="line.897"></a>
+<span class="sourceLineNo">898</span> long bucketBytesToFree = (bytesToFreeWithExtra - bytesFreed) / remainingBuckets;<a name="line.898"></a>
+<span class="sourceLineNo">899</span> bytesFreed += bucketGroup.free(bucketBytesToFree);<a name="line.899"></a>
+<span class="sourceLineNo">900</span> remainingBuckets--;<a name="line.900"></a>
+<span class="sourceLineNo">901</span> }<a name="line.901"></a>
+<span class="sourceLineNo">902</span> }<a name="line.902"></a>
+<span class="sourceLineNo">903</span><a name="line.903"></a>
+<span class="sourceLineNo">904</span> // Even after the above free we might still need freeing because of the<a name="line.904"></a>
+<span class="sourceLineNo">905</span> // De-fragmentation of the buckets (also called Slab Calcification problem), i.e<a name="line.905"></a>
+<span class="sourceLineNo">906</span> // there might be some buckets where the occupancy is very sparse and thus are not<a name="line.906"></a>
+<span class="sourceLineNo">907</span> // yielding the free for the other bucket sizes, the fix for this to evict some<a name="line.907"></a>
+<span class="sourceLineNo">908</span> // of the buckets, we do this by evicting the buckets that are least fulled<a name="line.908"></a>
+<span class="sourceLineNo">909</span> freeEntireBuckets(DEFAULT_FREE_ENTIRE_BLOCK_FACTOR *<a name="line.909"></a>
+<span class="sourceLineNo">910</span> bucketSizesAboveThresholdCount(1.0f));<a name="line.910"></a>
+<span class="sourceLineNo">911</span><a name="line.911"></a>
+<span class="sourceLineNo">912</span> if (LOG.isDebugEnabled()) {<a name="line.912"></a>
+<span class="sourceLineNo">913</span> long single = bucketSingle.totalSize();<a name="line.913"></a>
+<span class="sourceLineNo">914</span> long multi = bucketMulti.totalSize();<a name="line.914"></a>
+<span class="sourceLineNo">915</span> long memory = bucketMemory.totalSize();<a name="line.915"></a>
+<span class="sourceLineNo">916</span> if (LOG.isDebugEnabled()) {<a name="line.916"></a>
+<span class="sourceLineNo">917</span> LOG.debug("Bucket cache free space completed; " + "freed="<a name="line.917"></a>
+<span class="sourceLineNo">918</span> + StringUtils.byteDesc(bytesFreed) + ", " + "total="<a name="line.918"></a>
+<span class="sourceLineNo">919</span> + StringUtils.byteDesc(totalSize) + ", " + "single="<a name="line.919"></a>
+<span class="sourceLineNo">920</span> + StringUtils.byteDesc(single) + ", " + "multi="<a name="line.920"></a>
+<span class="sourceLineNo">921</span> + StringUtils.byteDesc(multi) + ", " + "memory="<a name="line.921"></a>
+<span class="sourceLineNo">922</span> + StringUtils.byteDesc(memory));<a name="line.922"></a>
+<span class="sourceLineNo">923</span> }<a name="line.923"></a>
+<span class="sourceLineNo">924</span> }<a name="line.924"></a>
+<span class="sourceLineNo">925</span><a name="line.925"></a>
+<span class="sourceLineNo">926</span> } catch (Throwable t) {<a name="line.926"></a>
+<span class="sourceLineNo">927</span> LOG.warn("Failed freeing space", t);<a name="line.927"></a>
+<span class="sourceLineNo">928</span> } finally {<a name="line.928"></a>
+<span class="sourceLineNo">929</span> cacheStats.evict();<a name="line.929"></a>
+<span class="sourceLineNo">930</span> freeInProgress = false;<a name="line.930"></a>
+<span class="sourceLineNo">931</span> freeSpaceLock.unlock();<a name="line.931"></a>
+<span class="sourceLineNo">932</span> }<a name="line.932"></a>
+<span class="sourceLineNo">933</span> }<a name="line.933"></a>
+<span class="sourceLineNo">934</span><a name="line.934"></a>
+<span class="sourceLineNo">935</span> // This handles flushing the RAM cache to IOEngine.<a name="line.935"></a>
+<span class="sourceLineNo">936</span> class WriterThread extends Thread {<a name="line.936"></a>
+<span class="sourceLineNo">937</span> private final BlockingQueue<RAMQueueEntry> inputQueue;<a name="line.937"></a>
+<span class="sourceLineNo">938</span> private volatile boolean writerEnabled = true;<a name="line.938"></a>
+<span class="sourceLineNo">939</span><a name="line.939"></a>
+<span class="sourceLineNo">940</span> WriterThread(BlockingQueue<RAMQueueEntry> queue) {<a name="line.940"></a>
+<span class="sourceLineNo">941</span> super("BucketCacheWriterThread");<a name="line.941"></a>
+<span class="sourceLineNo">942</span> this.inputQueue = queue;<a name="line.942"></a>
+<span class="sourceLineNo">943</span> }<a name="line.943"></a>
+<span class="sourceLineNo">944</span><a name="line.944"></a>
+<span class="sourceLineNo">945</span> // Used for test<a name="line.945"></a>
+<span class="sourceLineNo">946</span> void disableWriter() {<a name="line.946"></a>
+<span class="sourceLineNo">947</span> this.writerEnabled = false;<a name="line.947"></a>
+<span class="sourceLineNo">948</span> }<a name="line.948"></a>
+<span class="sourceLineNo">949</span><a name="line.949"></a>
+<span class="sourceLineNo">950</span> @Override<a name="line.950"></a>
+<span class="sourceLineNo">951</span> public void run() {<a name="line.951"></a>
+<span class="sourceLineNo">952</span> List<RAMQueueEntry> entries = new ArrayList<>();<a name="line.952"></a>
+<span class="sourceLineNo">953</span> try {<a name="line.953"></a>
+<span class="sourceLineNo">954</span> while (cacheEnabled && writerEnabled) {<a name="line.954"></a>
+<span class="sourceLineNo">955</span> try {<a name="line.955"></a>
+<span class="sourceLineNo">956</span> try {<a name="line.956"></a>
+<span class="sourceLineNo">957</span> // Blocks<a name="line.957"></a>
+<span class="sourceLineNo">958</span> entries = getRAMQueueEntries(inputQueue, entries);<a name="line.958"></a>
+<span class="sourceLineNo">959</span> } catch (InterruptedException ie) {<a name="line.959"></a>
+<span class="sourceLineNo">960</span> if (!cacheEnabled || !writerEnabled) {<a name="line.960"></a>
+<span class="sourceLineNo">961</span> break;<a name="line.961"></a>
+<span class="sourceLineNo">962</span> }<a name="line.962"></a>
+<span class="sourceLineNo">963</span> }<a name="line.963"></a>
+<span class="sourceLineNo">964</span> doDrain(entries);<a name="line.964"></a>
+<span class="sourceLineNo">965</span> } catch (Exception ioe) {<a name="line.965"></a>
+<span class="sourceLineNo">966</span> LOG.error("WriterThread encountered error", ioe);<a name="line.966"></a>
+<span class="sourceLineNo">967</span> }<a name="line.967"></a>
+<span class="sourceLineNo">968</span> }<a name="line.968"></a>
+<span class="sourceLineNo">969</span> } catch (Throwable t) {<a name="line.969"></a>
+<span class="sourceLineNo">970</span> LOG.warn("Failed doing drain", t);<a name="line.970"></a>
+<span class="sourceLineNo">971</span> }<a name="line.971"></a>
+<span class="sourceLineNo">972</span> LOG.info(this.getName() + " exiting, cacheEnabled=" + cacheEnabled);<a name="line.972"></a>
+<span class="sourceLineNo">973</span> }<a name="line.973"></a>
+<span class="sourceLineNo">974</span> }<a name="line.974"></a>
+<span class="sourceLineNo">975</span><a name="line.975"></a>
+<span class="sourceLineNo">976</span> /**<a name="line.976"></a>
+<span class="sourceLineNo">977</span> * Put the new bucket entry into backingMap. Notice that we are allowed to replace the existing<a name="line.977"></a>
+<span class="sourceLineNo">978</span> * cache with a new block for the same cache key. there's a corner case: one thread cache a block<a name="line.978"></a>
+<span class="sourceLineNo">979</span> * in ramCache, copy to io-engine and add a bucket entry to backingMap. Caching another new block<a name="line.979"></a>
+<span class="sourceLineNo">980</span> * with the same cache key do the same thing for the same cache key, so if not evict the previous<a name="line.980"></a>
+<span class="sourceLineNo">981</span> * bucket entry, then memory leak happen because the previous bucketEntry is gone but the<a name="line.981"></a>
+<span class="sourceLineNo">982</span> * bucketAllocator do not free its memory.<a name="line.982"></a>
+<span class="sourceLineNo">983</span> * @see BlockCacheUtil#shouldReplaceExistingCacheBlock(BlockCache blockCache,BlockCacheKey<a name="line.983"></a>
+<span class="sourceLineNo">984</span> * cacheKey, Cacheable newBlock)<a name="line.984"></a>
+<span class="sourceLineNo">985</span> * @param key Block cache key<a name="line.985"></a>
+<span class="sourceLineNo">986</span> * @param bucketEntry Bucket entry to put into backingMap.<a name="line.986"></a>
+<span class="sourceLineNo">987</span> */<a name="line.987"></a>
+<span class="sourceLineNo">988</span> protected void putIntoBackingMap(BlockCacheKey key, BucketEntry bucketEntry) {<a name="line.988"></a>
+<span class="sourceLineNo">989</span> BucketEntry previousEntry = backingMap.put(key, bucketEntry);<a name="line.989"></a>
+<span class="sourceLineNo">990</span> if (previousEntry != null && previousEntry != bucketEntry) {<a name="line.990"></a>
+<span class="sourceLineNo">991</span> previousEntry.withWriteLock(offsetLock, () -> {<a name="line.991"></a>
+<span class="sourceLineNo">992</span> blockEvicted(key, previousEntry, false);<a name="line.992"></a>
+<span class="sourceLineNo">993</span> return null;<a name="line.993"></a>
+<span class="sourceLineNo">994</span> });<a name="line.994"></a>
+<span class="sourceLineNo">995</span> }<a name="line.995"></a>
+<span class="sourceLineNo">996</span> }<a name="line.996"></a>
+<span class="sourceLineNo">997</span><a name="line.997"></a>
+<span class="sourceLineNo">998</span> /**<a name="line.998"></a>
+<span class="sourceLineNo">999</span> * Flush the entries in ramCache to IOEngine and add bucket entry to backingMap. Process all that<a name="line.999"></a>
+<span class="sourceLineNo">1000</span> * are passed in even if failure being sure to remove from ramCache else we'll never undo the<a name="line.1000"></a>
+<span class="sourceLineNo">1001</span> * references and we'll OOME.<a name="line.1001"></a>
+<span class="sourceLineNo">1002</span> * @param entries Presumes list passed in here will be processed by this invocation only. No<a name="line.1002"></a>
+<span class="sourceLineNo">1003</span> * interference expected.<a name="line.1003"></a>
+<span class="sourceLineNo">1004</span> */<a name="line.1004"></a>
+<span class="sourceLineNo">1005</span> void doDrain(final List<RAMQueueEntry> entries) throws InterruptedException {<a name="line.1005"></a>
+<span class="sourceLineNo">1006</span> if (entries.isEmpty()) {<a name="line.1006"></a>
+<span class="sourceLineNo">1007</span> return;<a name="line.1007"></a>
+<span class="sourceLineNo">1008</span> }<a name="line.1008"></a>
+<span class="sourceLineNo">1009</span> // This method is a little hard to follow. We run through the passed in entries and for each<a name="line.1009"></a>
+<span class="sourceLineNo">1010</span> // successful add, we add a non-null BucketEntry to the below bucketEntries. Later we must<a name="line.1010"></a>
+<span class="sourceLineNo">1011</span> // do cleanup making sure we've cleared ramCache of all entries regardless of whether we<a name="line.1011"></a>
+<span class="sourceLineNo">1012</span> // successfully added the item to the bucketcache; if we don't do the cleanup, we'll OOME by<a name="line.1012"></a>
+<span class="sourceLineNo">1013</span> // filling ramCache. We do the clean up by again running through the passed in entries<a name="line.1013"></a>
+<span class="sourceLineNo">1014</span> // doing extra work when we find a non-null bucketEntries corresponding entry.<a name="line.1014"></a>
+<span class="sourceLineNo">1015</span> final int size = entries.size();<a name="line.1015"></a>
+<span class="sourceLineNo">1016</span> BucketEntry[] bucketEntries = new BucketEntry[size];<a name="line.1016"></a>
+<span class="sourceLineNo">1017</span> // Index updated inside loop if success or if we can't succeed. We retry if cache is full<a name="line.1017"></a>
+<span class="sourceLineNo">1018</span> // when we go to add an entry by going around the loop again without upping the index.<a name="line.1018"></a>
+<span class="sourceLineNo">1019</span> int index = 0;<a name="line.1019"></a>
+<span class="sourceLineNo">1020</span> while (cacheEnabled && index < size) {<a name="line.1020"></a>
+<span class="sourceLineNo">1021</span> RAMQueueEntry re = null;<a name="line.1021"></a>
+<span class="sourceLineNo">1022</span> try {<a name="line.1022"></a>
+<span class="sourceLineNo">1023</span> re = entries.get(index);<a name="line.1023"></a>
+<span class="sourceLineNo">1024</span> if (re == null) {<a name="line.1024"></a>
+<span class="sourceLineNo">1025</span> LOG.warn("Couldn't get entry or changed on us; who else is messing with it?");<a name="line.1025"></a>
+<span class="sourceLineNo">1026</span> index++;<a name="line.1026"></a>
+<span class="sourceLineNo">1027</span> continue;<a name="line.1027"></a>
+<span class="sourceLineNo">1028</span> }<a name="line.1028"></a>
+<span class="sourceLineNo">1029</span> BlockCacheKey cacheKey = re.getKey();<a name="line.1029"></a>
+<span class="sourceLineNo">1030</span> if (ramCache.containsKey(cacheKey)) {<a name="line.1030"></a>
+<span class="sourceLineNo">1031</span> blocksByHFile.add(cacheKey);<a name="line.1031"></a>
+<span class="sourceLineNo">1032</span> }<a name="line.1032"></a>
+<span class="sourceLineNo">1033</span><a name="line.1033"></a>
+<span class="sourceLineNo">1034</span> BucketEntry bucketEntry = re.writeToCache(ioEngine, bucketAllocator, realCacheSize,<a name="line.1034"></a>
+<span class="sourceLineNo">1035</span> this::createRecycler);<a name="line.1035"></a>
+<span class="sourceLineNo">1036</span> // Successfully added. Up index and add bucketEntry. Clear io exceptions.<a name="line.1036"></a>
+<span class="sourceLineNo">1037</span> bucketEntries[index] = bucketEntry;<a name="line.1037"></a>
+<span class="sourceLineNo">1038</span> if (ioErrorStartTime > 0) {<a name="line.1038"></a>
+<span class="sourceLineNo">1039</span> ioErrorStartTime = -1;<a name="line.1039"></a>
+<span class="sourceLineNo">1040</span> }<a name="line.1040"></a>
+<span class="sourceLineNo">1041</span> index++;<a name="line.1041"></a>
+<span class="sourceLineNo">1042</span> } catch (BucketAllocatorException fle) {<a name="line.1042"></a>
+<span class="sourceLineNo">1043</span> LOG.warn("Failed allocation for " + (re == null ? "" : re.getKey()) + "; " + fle);<a name="line.1043"></a>
+<span class="sourceLineNo">1044</span> // Presume can't add. Too big? Move index on. Entry will be cleared from ramCache below.<a name="line.1044"></a>
+<span class="sourceLineNo">1045</span> bucketEntries[index] = null;<a name="line.1045"></a>
+<span class="sourceLineNo">1046</span> index++;<a name="line.1046"></a>
+<span class="sourceLineNo">1047</span> } catch (CacheFullException cfe) {<a name="line.1047"></a>
+<span class="sourceLineNo">1048</span> // Cache full when we tried to add. Try freeing space and then retrying (don't up index)<a name="line.1048"></a>
+<span class="sourceLineNo">1049</span> if (!freeInProgress) {<a name="line.1049"></a>
+<span class="sourceLineNo">1050</span> freeSpace("Full!");<a name="line.1050"></a>
+<span class="sourceLineNo">1051</span> } else {<a name="line.1051"></a>
+<span class="sourceLineNo">1052</span> Thread.sleep(50);<a name="line.1052"></a>
+<span class="sourceLineNo">1053</span> }<a name="line.1053"></a>
+<span class="sourceLineNo">1054</span> } catch (IOException ioex) {<a name="line.1054"></a>
+<span class="sourceLineNo">1055</span> // Hopefully transient. Retry. checkIOErrorIsTolerated disables cache if problem.<a name="line.1055"></a>
+<span class="sourceLineNo">1056</span> LOG.error("Failed writing to bucket cache", ioex);<a name="line.1056"></a>
+<span class="sourceLineNo">1057</span> checkIOErrorIsTolerated();<a name="line.1057"></a>
+<span class="sourceLineNo">1058</span> }<a name="line.1058"></a>
+<span class="sourceLineNo">1059</span> }<a name="line.1059"></a>
+<span class="sourceLineNo">1060</span><a name="line.1060"></a>
+<span class="sourceLineNo">1061</span> // Make sure data pages are written on media before we update maps.<a name="line.1061"></a>
+<span class="sourceLineNo">1062</span> try {<a name="line.1062"></a>
+<span class="sourceLineNo">1063</span> ioEngine.sync();<a name="line.1063"></a>
+<span class="sourceLineNo">1064</span> } catch (IOException ioex) {<a name="line.1064"></a>
+<span class="sourceLineNo">1065</span> LOG.error("Failed syncing IO engine", ioex);<a name="line.1065"></a>
+<span class="sourceLineNo">1066</span> checkIOErrorIsTolerated();<a name="line.1066"></a>
+<span class="sourceLineNo">1067</span> // Since we failed sync, free the blocks in bucket allocator<a name="line.1067"></a>
+<span class="sourceLineNo">1068</span> for (int i = 0; i < entries.size(); ++i) {<a name="line.1068"></a>
+<span class="sourceLineNo">1069</span> if (bucketEntries[i] != null) {<a name="line.1069"></a>
+<span class="sourceLineNo">1070</span> bucketAllocator.freeBlock(bucketEntries[i].offset());<a name="line.1070"></a>
+<span class="sourceLineNo">1071</span> bucketEntries[i] = null;<a name="line.1071"></a>
+<span class="sourceLineNo">1072</span> }<a name="line.1072"></a>
+<span class="sourceLineNo">1073</span> }<a name="line.1073"></a>
+<span class="sourceLineNo">1074</span> }<a name="line.1074"></a>
+<span class="sourceLineNo">1075</span><a name="line.1075"></a>
+<span class="sourceLineNo">1076</span> // Now add to backingMap if successfully added to bucket cache. Remove from ramCache if<a name="line.1076"></a>
+<span class="sourceLineNo">1077</span> // success or error.<a name="line.1077"></a>
+<span class="sourceLineNo">1078</span> for (int i = 0; i < size; ++i) {<a name="line.1078"></a>
+<span class="sourceLineNo">1079</span> BlockCacheKey key = entries.get(i).getKey();<a name="line.1079"></a>
+<span class="sourceLineNo">1080</span> // Only add if non-null entry.<a name="line.1080"></a>
+<span class="sourceLineNo">1081</span> if (bucketEntries[i] != null) {<a name="line.1081"></a>
+<span class="sourceLineNo">1082</span> putIntoBackingMap(key, bucketEntries[i]);<a name="line.1082"></a>
+<span class="sourceLineNo">1083</span> }<a name="line.1083"></a>
+<span class="sourceLineNo">1084</span> // Always remove from ramCache even if we failed adding it to the block cache above.<a name="line.1084"></a>
+<span class="sourceLineNo">1085</span> boolean existed = ramCache.remove(key, re -> {<a name="line.1085"></a>
+<span class="sourceLineNo">1086</span> if (re != null) {<a name="line.1086"></a>
+<span class="sourceLineNo">1087</span> heapSize.add(-1 * re.getData().heapSize());<a name="line.1087"></a>
+<span class="sourceLineNo">1088</span> }<a name="line.1088"></a>
+<span class="sourceLineNo">1089</span> });<a name="line.1089"></a>
+<span class="sourceLineNo">1090</span> if (!existed && bucketEntries[i] != null) {<a name="line.1090"></a>
+<span class="sourceLineNo">1091</span> // Block should have already been evicted. Remove it and free space.<a name="line.1091"></a>
+<span class="sourceLineNo">1092</span> final BucketEntry bucketEntry = bucketEntries[i];<a name="line.1092"></a>
+<span class="sourceLineNo">1093</span> bucketEntry.withWriteLock(offsetLock, () -> {<a name="line.1093"></a>
+<span class="sourceLineNo">1094</span> if (backingMap.remove(key, bucketEntry)) {<a name="line.1094"></a>
+<span class="sourceLineNo">1095</span> blockEvicted(key, bucketEntry, false);<a name="line.1095"></a>
+<span class="sourceLineNo">1096</span> }<a name="line.1096"></a>
+<span class="sourceLineNo">1097</span> return null;<a name="line.1097"></a>
+<span class="sourceLineNo">1098</span> });<a name="line.1098"></a>
+<span class="sourceLineNo">1099</span> }<a name="line.1099"></a>
+<span class="sourceLineNo">1100</span> }<a name="line.1100"></a>
+<span class="sourceLineNo">1101</span><a name="line.1101"></a>
+<span class="sourceLineNo">1102</span> long used = bucketAllocator.getUsedSize();<a name="line.1102"></a>
+<span class="sourceLineNo">1103</span> if (used > acceptableSize()) {<a name="line.1103"></a>
+<span class="sourceLineNo">1104</span> freeSpace("Used=" + used + " > acceptable=" + acceptableSize());<a name="line.1104"></a>
+<span class="sourceLineNo">1105</span> }<a name="line.1105"></a>
+<span class="sourceLineNo">1106</span> return;<a name="line.1106"></a>
+<span class="sourceLineNo">1107</span> }<a name="line.1107"></a>
+<span class="sourceLineNo">1108</span><a name="line.1108"></a>
+<span class="sourceLineNo">1109</span> /**<a name="line.1109"></a>
+<span class="sourceLineNo">1110</span> * Blocks until elements available in {@code q} then tries to grab as many as possible before<a name="line.1110"></a>
+<span class="sourceLineNo">1111</span> * returning.<a name="line.1111"></a>
+<span class="sourceLineNo">1112</span> * @param receptacle Where to stash the elements taken from queue. We clear before we use it just<a name="line.1112"></a>
+<span class="sourceLineNo">1113</span> * in case.<a name="line.1113"></a>
+<span class="sourceLineNo">1114</span> * @param q The queue to take from.<a name="line.1114"></a>
+<span class="sourceLineNo">1115</span> * @return {@code receptacle} laden with elements taken from the queue or empty if none found.<a name="line.1115"></a>
+<span class="sourceLineNo">1116</span> */<a name="line.1116"></a>
+<span class="sourceLineNo">1117</span> static List<RAMQueueEntry> getRAMQueueEntries(BlockingQueue<RAMQueueEntry> q,<a name="line.1117"></a>
+<span class="sourceLineNo">1118</span> List<RAMQueueEntry> receptacle) throws InterruptedException {<a name="line.1118"></a>
+<span class="sourceLineNo">1119</span> // Clear sets all entries to null and sets size to 0. We retain allocations. Presume it<a name="line.1119"></a>
+<span class="sourceLineNo">1120</span> // ok even if list grew to accommodate thousands.<a name="line.1120"></a>
+<span class="sourceLineNo">1121</span> receptacle.clear();<a name="line.1121"></a>
+<span class="sourceLineNo">1122</span> receptacle.add(q.take());<a name="line.1122"></a>
+<span class="sourceLineNo">1123</span> q.drainTo(receptacle);<a name="line.1123"></a>
+<span class="sourceLineNo">1124</span> return receptacle;<a name="line.1124"></a>
+<span class="sourceLineNo">1125</span> }<a name="line.1125"></a>
+<span class="sourceLineNo">1126</span><a name="line.1126"></a>
+<span class="sourceLineNo">1127</span> /**<a name="line.1127"></a>
+<span class="sourceLineNo">1128</span> * @see #retrieveFromFile(int[])<a name="line.1128"></a>
+<span class="sourceLineNo">1129</span> */<a name="line.1129"></a>
+<span class="sourceLineNo">1130</span> @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="OBL_UNSATISFIED_OBLIGATION",<a name="line.1130"></a>
+<span class="sourceLineNo">1131</span> justification = "false positive, try-with-resources ensures close is called.")<a name="line.1131"></a>
+<span class="sourceLineNo">1132</span> private void persistToFile() throws IOException {<a name="line.1132"></a>
+<span class="sourceLineNo">1133</span> assert !cacheEnabled;<a name="line.1133"></a>
+<span class="sourceLineNo">1134</span> if (!ioEngine.isPersistent()) {<a name="line.1134"></a>
+<span class="sourceLineNo">1135</span> throw new IOException("Attempt to persist non-persistent cache mappings!");<a name="line.1135"></a>
<span class="sourceLineNo">1136</span> }<a name="line.1136"></a>
-<span class="sourceLineNo">1137</span> }<a name="line.1137"></a>
-<span class="sourceLineNo">1138</span><a name="line.1138"></a>
-<span class="sourceLineNo">1139</span> /**<a name="line.1139"></a>
-<span class="sourceLineNo">1140</span> * @see #persistToFile()<a name="line.1140"></a>
-<span class="sourceLineNo">1141</span> */<a name="line.1141"></a>
-<span class="sourceLineNo">1142</span> private void retrieveFromFile(int[] bucketSizes) throws IOException {<a name="line.1142"></a>
-<span class="sourceLineNo">1143</span> File persistenceFile = new File(persistencePath);<a name="line.1143"></a>
-<span class="sourceLineNo">1144</span> if (!persistenceFile.exists()) {<a name="line.1144"></a>
-<span class="sourceLineNo">1145</span> return;<a name="line.1145"></a>
-<span class="sourceLineNo">1146</span> }<a name="line.1146"></a>
-<span class="sourceLineNo">1147</span> assert !cacheEnabled;<a name="line.1147"></a>
-<span class="sourceLineNo">1148</span><a name="line.1148"></a>
-<span class="sourceLineNo">1149</span> try (FileInputStream in = deleteFileOnClose(persistenceFile)) {<a name="line.1149"></a>
-<span class="sourceLineNo">1150</span> int pblen = ProtobufMagic.lengthOfPBMagic();<a name="line.1150"></a>
-<span class="sourceLineNo">1151</span> byte[] pbuf = new byte[pblen];<a name="line.1151"></a>
-<span class="sourceLineNo">1152</span> int read = in.read(pbuf);<a name="line.1152"></a>
-<span class="sourceLineNo">1153</span> if (read != pblen) {<a name="line.1153"></a>
-<span class="sourceLineNo">1154</span> throw new IOException("Incorrect number of bytes read while checking for protobuf magic "<a name="line.1154"></a>
-<span class="sourceLineNo">1155</span> + "number. Requested=" + pblen + ", Received= " + read + ", File=" + persistencePath);<a name="line.1155"></a>
-<span class="sourceLineNo">1156</span> }<a name="line.1156"></a>
-<span class="sourceLineNo">1157</span> if (! ProtobufMagic.isPBMagicPrefix(pbuf)) {<a name="line.1157"></a>
-<span class="sourceLineNo">1158</span> // In 3.0 we have enough flexibility to dump the old cache data.<a name="line.1158"></a>
-<span class="sourceLineNo">1159</span> // TODO: In 2.x line, this might need to be filled in to support reading the old format<a name="line.1159"></a>
-<span class="sourceLineNo">1160</span> throw new IOException("Persistence file does not start with protobuf magic number. " +<a name="line.1160"></a>
-<span class="sourceLineNo">1161</span> persistencePath);<a name="line.1161"></a>
-<span class="sourceLineNo">1162</span> }<a name="line.1162"></a>
-<span class="sourceLineNo">1163</span> parsePB(BucketCacheProtos.BucketCacheEntry.parseDelimitedFrom(in));<a name="line.1163"></a>
-<span class="sourceLineNo">1164</span> bucketAllocator = new BucketAllocator(cacheCapacity, bucketSizes, backingMap, realCacheSize);<a name="line.1164"></a>
-<span class="sourceLineNo">1165</span> blockNumber.add(backingMap.size());<a name="line.1165"></a>
-<span class="sourceLineNo">1166</span> }<a name="line.1166"></a>
-<span class="sourceLineNo">1167</span> }<a name="line.1167"></a>
-<span class="sourceLineNo">1168</span><a name="line.1168"></a>
-<span class="sourceLineNo">1169</span> /**<a name="line.1169"></a>
-<span class="sourceLineNo">1170</span> * Create an input stream that deletes the file after reading it. Use in try-with-resources to<a name="line.1170"></a>
-<span class="sourceLineNo">1171</span> * avoid this pattern where an exception thrown from a finally block may mask earlier exceptions:<a name="line.1171"></a>
-<span class="sourceLineNo">1172</span> * <pre><a name="line.1172"></a>
-<span class="sourceLineNo">1173</span> * File f = ...<a name="line.1173"></a>
-<span class="sourceLineNo">1174</span> * try (FileInputStream fis = new FileInputStream(f)) {<a name="line.1174"></a>
-<span class="sourceLineNo">1175</span> * // use the input stream<a name="line.1175"></a>
-<span class="sourceLineNo">1176</span> * } finally {<a name="line.1176"></a>
-<span class="sourceLineNo">1177</span> * if (!f.delete()) throw new IOException("failed to delete");<a name="line.1177"></a>
-<span class="sourceLineNo">1178</span> * }<a name="line.1178"></a>
-<span class="sourceLineNo">1179</span> * </pre><a name="line.1179"></a>
-<span class="sourceLineNo">1180</span> * @param file the file to read and delete<a name="line.1180"></a>
-<span class="sourceLineNo">1181</span> * @return a FileInputStream for the given file<a name="line.1181"></a>
-<span class="sourceLineNo">1182</span> * @throws IOException if there is a problem creating the stream<a name="line.1182"></a>
-<span class="sourceLineNo">1183</span> */<a name="line.1183"></a>
-<span class="sourceLineNo">1184</span> private FileInputStream deleteFileOnClose(final File file) throws IOException {<a name="line.1184"></a>
-<span class="sourceLineNo">1185</span> return new FileInputStream(file) {<a name="line.1185"></a>
-<span class="sourceLineNo">1186</span> private File myFile;<a name="line.1186"></a>
-<span class="sourceLineNo">1187</span> private FileInputStream init(File file) {<a name="line.1187"></a>
-<span class="sourceLineNo">1188</span> myFile = file;<a name="line.1188"></a>
-<span class="sourceLineNo">1189</span> return this;<a name="line.1189"></a>
-<span class="sourceLineNo">1190</span> }<a name="line.1190"></a>
-<span class="sourceLineNo">1191</span> @Override<a name="line.1191"></a>
-<span class="sourceLineNo">1192</span> public void close() throws IOException {<a name="line.1192"></a>
-<span class="sourceLineNo">1193</span> // close() will be called during try-with-resources and it will be<a name="line.1193"></a>
-<span class="sourceLineNo">1194</span> // called by finalizer thread during GC. To avoid double-free resource,<a name="line.1194"></a>
-<span class="sourceLineNo">1195</span> // set myFile to null after the first call.<a name="line.1195"></a>
-<span class="sourceLineNo">1196</span> if (myFile == null) {<a name="line.1196"></a>
-<span class="sourceLineNo">1197</span> return;<a name="line.1197"></a>
-<span class="sourceLineNo">1198</span> }<a name="line.1198"></a>
-<span class="sourceLineNo">1199</span><a name="line.1199"></a>
-<span class="sourceLineNo">1200</span> super.close();<a name="line.1200"></a>
-<span class="sourceLineNo">1201</span> if (!myFile.delete()) {<a name="line.1201"></a>
-<span class="sourceLineNo">1202</span> throw new IOException("Failed deleting persistence file " + myFile.getAbsolutePath());<a name="line.1202"></a>
-<span class="sourceLineNo">1203</span> }<a name="line.1203"></a>
-<span class="sourceLineNo">1204</span> myFile = null;<a name="line.1204"></a>
-<span class="sourceLineNo">1205</span> }<a name="line.1205"></a>
-<span class="sourceLineNo">1206</span> }.init(file);<a name="line.1206"></a>
-<span class="sourceLineNo">1207</span> }<a name="line.1207"></a>
-<span class="sourceLineNo">1208</span><a name="line.1208"></a>
-<span class="sourceLineNo">1209</span> private void verifyCapacityAndClasses(long capacitySize, String ioclass, String mapclass)<a name="line.1209"></a>
-<span class="sourceLineNo">1210</span> throws IOException {<a name="line.1210"></a>
-<span class="sourceLineNo">1211</span> if (capacitySize != cacheCapacity) {<a name="line.1211"></a>
-<span class="sourceLineNo">1212</span> throw new IOException("Mismatched cache capacity:"<a name="line.1212"></a>
-<span class="sourceLineNo">1213</span> + StringUtils.byteDesc(capacitySize) + ", expected: "<a name="line.1213"></a>
-<span class="sourceLineNo">1214</span> + StringUtils.byteDesc(cacheCapacity));<a name="line.1214"></a>
-<span class="sourceLineNo">1215</span> }<a name="line.1215"></a>
-<span class="sourceLineNo">1216</span> if (!ioEngine.getClass().getName().equals(ioclass)) {<a name="line.1216"></a>
-<span class="sourceLineNo">1217</span> throw new IOException("Class name for IO engine mismatch: " + ioclass<a name="line.1217"></a>
-<span class="sourceLineNo">1218</span> + ", expected:" + ioEngine.getClass().getName());<a name="line.1218"></a>
+<span class="sourceLineNo">1137</span> try (FileOutputStream fos = new FileOutputStream(persistencePath, false)) {<a name="line.1137"></a>
+<span class="sourceLineNo">1138</span> fos.write(ProtobufMagic.PB_MAGIC);<a name="line.1138"></a>
+<span class="sourceLineNo">1139</span> BucketProtoUtils.toPB(this).writeDelimitedTo(fos);<a name="line.1139"></a>
+<span class="sourceLineNo">1140</span> }<a name="line.1140"></a>
+<span class="sourceLineNo">1141</span> }<a name="line.1141"></a>
+<span class="sourceLineNo">1142</span><a name="line.1142"></a>
+<span class="sourceLineNo">1143</span> /**<a name="line.1143"></a>
+<span class="sourceLineNo">1144</span> * @see #persistToFile()<a name="line.1144"></a>
+<span class="sourceLineNo">1145</span> */<a name="line.1145"></a>
+<span class="sourceLineNo">1146</span> private void retrieveFromFile(int[] bucketSizes) throws IOException {<a name="line.1146"></a>
+<span class="sourceLineNo">1147</span> File persistenceFile = new File(persistencePath);<a name="line.1147"></a>
+<span class="sourceLineNo">1148</span> if (!persistenceFile.exists()) {<a name="line.1148"></a>
+<span class="sourceLineNo">1149</span> return;<a name="line.1149"></a>
+<span class="sourceLineNo">1150</span> }<a name="line.1150"></a>
+<span class="sourceLineNo">1151</span> assert !cacheEnabled;<a name="line.1151"></a>
+<span class="sourceLineNo">1152</span><a name="line.1152"></a>
+<span class="sourceLineNo">1153</span> try (FileInputStream in = deleteFileOnClose(persistenceFile)) {<a name="line.1153"></a>
+<span class="sourceLineNo">1154</span> int pblen = ProtobufMagic.lengthOfPBMagic();<a name="line.1154"></a>
+<span class="sourceLineNo">1155</span> byte[] pbuf = new byte[pblen];<a name="line.1155"></a>
+<span class="sourceLineNo">1156</span> int read = in.read(pbuf);<a name="line.1156"></a>
+<span class="sourceLineNo">1157</span> if (read != pblen) {<a name="line.1157"></a>
+<span class="sourceLineNo">1158</span> throw new IOException("Incorrect number of bytes read while checking for protobuf magic "<a name="line.1158"></a>
+<span class="sourceLineNo">1159</span> + "number. Requested=" + pblen + ", Received= " + read + ", File=" + persistencePath);<a name="line.1159"></a>
+<span class="sourceLineNo">1160</span> }<a name="line.1160"></a>
+<span class="sourceLineNo">1161</span> if (! ProtobufMagic.isPBMagicPrefix(pbuf)) {<a name="line.1161"></a>
+<span class="sourceLineNo">1162</span> // In 3.0 we have enough flexibility to dump the old cache data.<a name="line.1162"></a>
+<span class="sourceLineNo">1163</span> // TODO: In 2.x line, this might need to be filled in to support reading the old format<a name="line.1163"></a>
+<span class="sourceLineNo">1164</span> throw new IOException("Persistence file does not start with protobuf magic number. " +<a name="line.1164"></a>
+<span class="sourceLineNo">1165</span> persistencePath);<a name="line.1165"></a>
+<span class="sourceLineNo">1166</span> }<a name="line.1166"></a>
+<span class="sourceLineNo">1167</span> parsePB(BucketCacheProtos.BucketCacheEntry.parseDelimitedFrom(in));<a name="line.1167"></a>
+<span class="sourceLineNo">1168</span> bucketAllocator = new BucketAllocator(cacheCapacity, bucketSizes, backingMap, realCacheSize);<a name="line.1168"></a>
+<span class="sourceLineNo">1169</span> blockNumber.add(backingMap.size());<a name="line.1169"></a>
+<span class="sourceLineNo">1170</span> }<a name="line.1170"></a>
+<span class="sourceLineNo">1171</span> }<a name="line.1171"></a>
+<span class="sourceLineNo">1172</span><a name="line.1172"></a>
+<span class="sourceLineNo">1173</span> /**<a name="line.1173"></a>
+<span class="sourceLineNo">1174</span> * Create an input stream that deletes the file after reading it. Use in try-with-resources to<a name="line.1174"></a>
+<span class="sourceLineNo">1175</span> * avoid this pattern where an exception thrown from a finally block may mask earlier exceptions:<a name="line.1175"></a>
+<span class="sourceLineNo">1176</span> * <pre><a name="line.1176"></a>
+<span class="sourceLineNo">1177</span> * File f = ...<a name="line.1177"></a>
+<span class="sourceLineNo">1178</span> * try (FileInputStream fis = new FileInputStream(f)) {<a name="line.1178"></a>
+<span class="sourceLineNo">1179</span> * // use the input stream<a name="line.1179"></a>
+<span class="sourceLineNo">1180</span> * } finally {<a name="line.1180"></a>
+<span class="sourceLineNo">1181</span> * if (!f.delete()) throw new IOException("failed to delete");<a name="line.1181"></a>
+<span class="sourceLineNo">1182</span> * }<a name="line.1182"></a>
+<span class="sourceLineNo">1183</span> * </pre><a name="line.1183"></a>
+<span class="sourceLineNo">1184</span> * @param file the file to read and delete<a name="line.1184"></a>
+<span class="sourceLineNo">1185</span> * @return a FileInputStream for the given file<a name="line.1185"></a>
+<span class="sourceLineNo">1186</span> * @throws IOException if there is a problem creating the stream<a name="line.1186"></a>
+<span class="sourceLineNo">1187</span> */<a name="line.1187"></a>
+<span class="sourceLineNo">1188</span> private FileInputStream deleteFileOnClose(final File file) throws IOException {<a name="line.1188"></a>
+<span class="sourceLineNo">1189</span> return new FileInputStream(file) {<a name="line.1189"></a>
+<span class="sourceLineNo">1190</span> private File myFile;<a name="line.1190"></a>
+<span class="sourceLineNo">1191</span> private FileInputStream init(File file) {<a name="line.1191"></a>
+<span class="sourceLineNo">1192</span> myFile = file;<a name="line.1192"></a>
+<span class="sourceLineNo">1193</span> return this;<a name="line.1193"></a>
+<span class="sourceLineNo">1194</span> }<a name="line.1194"></a>
+<span class="sourceLineNo">1195</span> @Override<a name="line.1195"></a>
+<span class="sourceLineNo">1196</span> public void close() throws IOException {<a name="line.1196"></a>
+<span class="sourceLineNo">1197</span> // close() will be called during try-with-resources and it will be<a name="line.1197"></a>
+<span class="sourceLineNo">1198</span> // called by finalizer thread during GC. To avoid double-free resource,<a name="line.1198"></a>
+<span class="sourceLineNo">1199</span> // set myFile to null after the first call.<a name="line.1199"></a>
+<span class="sourceLineNo">1200</span> if (myFile == null) {<a name="line.1200"></a>
+<span class="sourceLineNo">1201</span> return;<a name="line.1201"></a>
+<span class="sourceLineNo">1202</span> }<a name="line.1202"></a>
+<span class="sourceLineNo">1203</span><a name="line.1203"></a>
+<span class="sourceLineNo">1204</span> super.close();<a name="line.1204"></a>
+<span class="sourceLineNo">1205</span> if (!myFile.delete()) {<a name="line.1205"></a>
+<span class="sourceLineNo">1206</span> throw new IOException("Failed deleting persistence file " + myFile.getAbsolutePath());<a name="line.1206"></a>
+<span class="sourceLineNo">1207</span> }<a name="line.1207"></a>
+<span class="sourceLineNo">1208</span> myFile = null;<a name="line.1208"></a>
+<span class="sourceLineNo">1209</span> }<a name="line.1209"></a>
+<span class="sourceLineNo">1210</span> }.init(file);<a name="line.1210"></a>
+<span class="sourceLineNo">1211</span> }<a name="line.1211"></a>
+<span class="sourceLineNo">1212</span><a name="line.1212"></a>
+<span class="sourceLineNo">1213</span> private void verifyCapacityAndClasses(long capacitySize, String ioclass, String mapclass)<a name="line.1213"></a>
+<span class="sourceLineNo">1214</span> throws IOException {<a name="line.1214"></a>
+<span class="sourceLineNo">1215</span> if (capacitySize != cacheCapacity) {<a name="line.1215"></a>
+<span class="sourceLineNo">1216</span> throw new IOException("Mismatched cache capacity:"<a name="line.1216"></a>
+<span class="sourceLineNo">1217</span> + StringUtils.byteDesc(capacitySize) + ", expected: "<a name="line.1217"></a>
+<span class="sourceLineNo">1218</span> + StringUtils.byteDesc(cacheCapacity));<a name="line.1218"></a>
<span class="sourceLineNo">1219</span> }<a name="line.1219"></a>
-<span class="sourceLineNo">1220</span> if (!backingMap.getClass().getName().equals(mapclass)) {<a name="line.1220"></a>
-<span class="sourceLineNo">1221</span> throw new IOException("Class name for cache map mismatch: " + mapclass<a name="line.1221"></a>
-<span class="sourceLineNo">1222</span> + ", expected:" + backingMap.getClass().getName());<a name="line.1222"></a>
+<span class="sourceLineNo">1220</span> if (!ioEngine.getClass().getName().equals(ioclass)) {<a name="line.1220"></a>
+<span class="sourceLineNo">1221</span> throw new IOException("Class name for IO engine mismatch: " + ioclass<a name="line.1221"></a>
+<span class="sourceLineNo">1222</span> + ", expected:" + ioEngine.getClass().getName());<a name="line.1222"></a>
<span class="sourceLineNo">1223</span> }<a name="line.1223"></a>
-<span class="sourceLineNo">1224</span> }<a name="line.1224"></a>
-<span class="sourceLineNo">1225</span><a name="line.1225"></a>
-<span class="sourceLineNo">1226</span> private void parsePB(BucketCacheProtos.BucketCacheEntry proto) throws IOException {<a name="line.1226"></a>
-<span class="sourceLineNo">1227</span> if (proto.hasChecksum()) {<a name="line.1227"></a>
-<span class="sourceLineNo">1228</span> ((PersistentIOEngine) ioEngine).verifyFileIntegrity(proto.getChecksum().toByteArray(),<a name="line.1228"></a>
-<span class="sourceLineNo">1229</span> algorithm);<a name="line.1229"></a>
-<span class="sourceLineNo">1230</span> } else {<a name="line.1230"></a>
-<span class="sourceLineNo">1231</span> // if has not checksum, it means the persistence file is old format<a name="line.1231"></a>
-<span class="sourceLineNo">1232</span> LOG.info("Persistent file is old format, it does not support verifying file integrity!");<a name="line.1232"></a>
-<span class="sourceLineNo">1233</span> }<a name="line.1233"></a>
-<span class="sourceLineNo">1234</span> verifyCapacityAndClasses(proto.getCacheCapacity(), proto.getIoClass(), proto.getMapClass());<a name="line.1234"></a>
-<span class="sourceLineNo">1235</span> backingMap = BucketProtoUtils.fromPB(proto.getDeserializersMap(), proto.getBackingMap(),<a name="line.1235"></a>
-<span class="sourceLineNo">1236</span> this::createRecycler);<a name="line.1236"></a>
-<span class="sourceLineNo">1237</span> }<a name="line.1237"></a>
-<span class="sourceLineNo">1238</span><a name="line.1238"></a>
-<span class="sourceLineNo">1239</span> /**<a name="line.1239"></a>
-<span class="sourceLineNo">1240</span> * Check whether we tolerate IO error this time. If the duration of IOEngine<a name="line.1240"></a>
-<span class="sourceLineNo">1241</span> * throwing errors exceeds ioErrorsDurationTimeTolerated, we will disable the<a name="line.1241"></a>
-<span class="sourceLineNo">1242</span> * cache<a name="line.1242"></a>
-<span class="sourceLineNo">1243</span> */<a name="line.1243"></a>
-<span class="sourceLineNo">1244</span> private void checkIOErrorIsTolerated() {<a name="line.1244"></a>
-<span class="sourceLineNo">1245</span> long now = EnvironmentEdgeManager.currentTime();<a name="line.1245"></a>
-<span class="sourceLineNo">1246</span> // Do a single read to a local variable to avoid timing issue - HBASE-24454<a name="line.1246"></a>
-<span class="sourceLineNo">1247</span> long ioErrorStartTimeTmp = this.ioErrorStartTime;<a name="line.1247"></a>
-<span class="sourceLineNo">1248</span> if (ioErrorStartTimeTmp > 0) {<a name="line.1248"></a>
-<span class="sourceLineNo">1249</span> if (cacheEnabled && (now - ioErrorStartTimeTmp) > this.ioErrorsTolerationDuration) {<a name="line.1249"></a>
-<span class="sourceLineNo">1250</span> LOG.error("IO errors duration time has exceeded " + ioErrorsTolerationDuration +<a name="line.1250"></a>
-<span class="sourceLineNo">1251</span> "ms, disabling cache, please check your IOEngine");<a name="line.1251"></a>
-<span class="sourceLineNo">1252</span> disableCache();<a name="line.1252"></a>
-<span class="sourceLineNo">1253</span> }<a name="line.1253"></a>
-<span class="sourceLineNo">1254</span> } else {<a name="line.1254"></a>
-<span class="sourceLineNo">1255</span> this.ioErrorStartTime = now;<a name="line.1255"></a>
-<span class="sourceLineNo">1256</span> }<a name="line.1256"></a>
-<span class="sourceLineNo">1257</span> }<a name="line.1257"></a>
-<span class="sourceLineNo">1258</span><a name="line.1258"></a>
-<span class="sourceLineNo">1259</span> /**<a name="line.1259"></a>
-<span class="sourceLineNo">1260</span> * Used to shut down the cache -or- turn it off in the case of something broken.<a name="line.1260"></a>
-<span class="sourceLineNo">1261</span> */<a name="line.1261"></a>
-<span class="sourceLineNo">1262</span> private void disableCache() {<a name="line.1262"></a>
-<span class="sourceLineNo">1263</span> if (!cacheEnabled) return;<a name="line.1263"></a>
-<span class="sourceLineNo">1264</span> cacheEnabled = false;<a name="line.1264"></a>
-<span class="sourceLineNo">1265</span> ioEngine.shutdown();<a name="line.1265"></a>
-<span class="sourceLineNo">1266</span> this.scheduleThreadPool.shutdown();<a name="line.1266"></a>
-<span class="sourceLineNo">1267</span> for (int i = 0; i < writerThreads.length; ++i) writerThreads[i].interrupt();<a name="line.1267"></a>
-<span class="sourceLineNo">1268</span> this.ramCache.clear();<a name="line.1268"></a>
-<span class="sourceLineNo">1269</span> if (!ioEngine.isPersistent() || persistencePath == null) {<a name="line.1269"></a>
-<span class="sourceLineNo">1270</span> // If persistent ioengine and a path, we will serialize out the backingMap.<a name="line.1270"></a>
-<span class="sourceLineNo">1271</span> this.backingMap.clear();<a name="line.1271"></a>
-<span class="sourceLineNo">1272</span> }<a name="line.1272"></a>
-<span class="sourceLineNo">1273</span> }<a name="line.1273"></a>
-<span class="sourceLineNo">1274</span><a name="line.1274"></a>
-<span class="sourceLineNo">1275</span> private void join() throws InterruptedException {<a name="line.1275"></a>
-<span class="sourceLineNo">1276</span> for (int i = 0; i < writerThreads.length; ++i)<a name="line.1276"></a>
-<span class="sourceLineNo">1277</span> writerThreads[i].join();<a name="line.1277"></a>
-<span class="sourceLineNo">1278</span> }<a name="line.1278"></a>
-<span class="sourceLineNo">1279</span><a name="line.1279"></a>
-<span class="sourceLineNo">1280</span> @Override<a name="line.1280"></a>
-<span class="sourceLineNo">1281</span> public void shutdown() {<a name="line.1281"></a>
-<span class="sourceLineNo">1282</span> disableCache();<a name="line.1282"></a>
-<span class="sourceLineNo">1283</span> LOG.info("Shutdown bucket cache: IO persistent=" + ioEngine.isPersistent()<a name="line.1283"></a>
-<span class="sourceLineNo">1284</span> + "; path to write=" + persistencePath);<a name="line.1284"></a>
-<span class="sourceLineNo">1285</span> if (ioEngine.isPersistent() && persistencePath != null) {<a name="line.1285"></a>
-<span class="sourceLineNo">1286</span> try {<a name="line.1286"></a>
-<span class="sourceLineNo">1287</span> join();<a name="line.1287"></a>
-<span class="sourceLineNo">1288</span> persistToFile();<a name="line.1288"></a>
-<span class="sourceLineNo">1289</span> } catch (IOException ex) {<a name="line.1289"></a>
-<span class="sourceLineNo">1290</span> LOG.error("Unable to persist data on exit: " + ex.toString(), ex);<a name="line.1290"></a>
-<span class="sourceLineNo">1291</span> } catch (InterruptedException e) {<a name="line.1291"></a>
-<span class="sourceLineNo">1292</span> LOG.warn("Failed to persist data on exit", e);<a name="line.1292"></a>
-<span class="sourceLineNo">1293</span> }<a name="line.1293"></a>
-<span class="sourceLineNo">1294</span> }<a name="line.1294"></a>
-<span class="sourceLineNo">1295</span> }<a name="line.1295"></a>
-<span class="sourceLineNo">1296</span><a name="line.1296"></a>
-<span class="sourceLineNo">1297</span> @Override<a name="line.1297"></a>
-<span class="sourceLineNo">1298</span> public CacheStats getStats() {<a name="line.1298"></a>
-<span class="sourceLineNo">1299</span> return cacheStats;<a name="line.1299"></a>
-<span class="sourceLineNo">1300</span> }<a name="line.1300"></a>
-<span class="sourceLineNo">1301</span><a name="line.1301"></a>
-<span class="sourceLineNo">1302</span> public BucketAllocator getAllocator() {<a name="line.1302"></a>
-<span class="sourceLineNo">1303</span> return this.bucketAllocator;<a name="line.1303"></a>
+<span class="sourceLineNo">1224</span> if (!backingMap.getClass().getName().equals(mapclass)) {<a name="line.1224"></a>
+<span class="sourceLineNo">1225</span> throw new IOException("Class name for cache map mismatch: " + mapclass<a name="line.1225"></a>
+<span class="sourceLineNo">1226</span> + ", expected:" + backingMap.getClass().getName());<a name="line.1226"></a>
+<span class="sourceLineNo">1227</span> }<a name="line.1227"></a>
+<span class="sourceLineNo">1228</span> }<a name="line.1228"></a>
+<span class="sourceLineNo">1229</span><a name="line.1229"></a>
+<span class="sourceLineNo">1230</span> private void parsePB(BucketCacheProtos.BucketCacheEntry proto) throws IOException {<a name="line.1230"></a>
+<span class="sourceLineNo">1231</span> if (proto.hasChecksum()) {<a name="line.1231"></a>
+<span class="sourceLineNo">1232</span> ((PersistentIOEngine) ioEngine).verifyFileIntegrity(proto.getChecksum().toByteArray(),<a name="line.1232"></a>
+<span class="sourceLineNo">1233</span> algorithm);<a name="line.1233"></a>
+<span class="sourceLineNo">1234</span> } else {<a name="line.1234"></a>
+<span class="sourceLineNo">1235</span> // if has not checksum, it means the persistence file is old format<a name="line.1235"></a>
+<span class="sourceLineNo">1236</span> LOG.info("Persistent file is old format, it does not support verifying file integrity!");<a name="line.1236"></a>
+<span class="sourceLineNo">1237</span> }<a name="line.1237"></a>
+<span class="sourceLineNo">1238</span> verifyCapacityAndClasses(proto.getCacheCapacity(), proto.getIoClass(), proto.getMapClass());<a name="line.1238"></a>
+<span class="sourceLineNo">1239</span> backingMap = BucketProtoUtils.fromPB(proto.getDeserializersMap(), proto.getBackingMap(),<a name="line.1239"></a>
+<span class="sourceLineNo">1240</span> this::createRecycler);<a name="line.1240"></a>
+<span class="sourceLineNo">1241</span> }<a name="line.1241"></a>
+<span class="sourceLineNo">1242</span><a name="line.1242"></a>
+<span class="sourceLineNo">1243</span> /**<a name="line.1243"></a>
+<span class="sourceLineNo">1244</span> * Check whether we tolerate IO error this time. If the duration of IOEngine<a name="line.1244"></a>
+<span class="sourceLineNo">1245</span> * throwing errors exceeds ioErrorsDurationTimeTolerated, we will disable the<a name="line.1245"></a>
+<span class="sourceLineNo">1246</span> * cache<a name="line.1246"></a>
+<span class="sourceLineNo">1247</span> */<a name="line.1247"></a>
+<span class="sourceLineNo">1248</span> private void checkIOErrorIsTolerated() {<a name="line.1248"></a>
+<span class="sourceLineNo">1249</span> long now = EnvironmentEdgeManager.currentTime();<a name="line.1249"></a>
+<span class="sourceLineNo">1250</span> // Do a single read to a local variable to avoid timing issue - HBASE-24454<a name="line.1250"></a>
+<span class="sourceLineNo">1251</span> long ioErrorStartTimeTmp = this.ioErrorStartTime;<a name="line.1251"></a>
+<span class="sourceLineNo">1252</span> if (ioErrorStartTimeTmp > 0) {<a name="line.1252"></a>
+<span class="sourceLineNo">1253</span> if (cacheEnabled && (now - ioErrorStartTimeTmp) > this.ioErrorsTolerationDuration) {<a name="line.1253"></a>
+<span class="sourceLineNo">1254</span> LOG.error("IO errors duration time has exceeded " + ioErrorsTolerationDuration +<a name="line.1254"></a>
+<span class="sourceLineNo">1255</span> "ms, disabling cache, please check your IOEngine");<a name="line.1255"></a>
+<span class="sourceLineNo">1256</span> disableCache();<a name="line.1256"></a>
+<span class="sourceLineNo">1257</span> }<a name="line.1257"></a>
+<span class="sourceLineNo">1258</span> } else {<a name="line.1258"></a>
+<span class="sourceLineNo">1259</span> this.ioErrorStartTime = now;<a name="line.1259"></a>
+<span class="sourceLineNo">1260</span> }<a name="line.1260"></a>
+<span class="sourceLineNo">1261</span> }<a name="line.1261"></a>
+<span class="sourceLineNo">1262</span><a name="line.1262"></a>
+<span class="sourceLineNo">1263</span> /**<a name="line.1263"></a>
+<span class="sourceLineNo">1264</span> * Used to shut down the cache -or- turn it off in the case of something broken.<a name="line.1264"></a>
+<span class="sourceLineNo">1265</span> */<a name="line.1265"></a>
+<span class="sourceLineNo">1266</span> private void disableCache() {<a name="line.1266"></a>
+<span class="sourceLineNo">1267</span> if (!cacheEnabled) return;<a name="line.1267"></a>
+<span class="sourceLineNo">1268</span> cacheEnabled = false;<a name="line.1268"></a>
+<span class="sourceLineNo">1269</span> ioEngine.shutdown();<a name="line.1269"></a>
+<span class="sourceLineNo">1270</span> this.scheduleThreadPool.shutdown();<a name="line.1270"></a>
+<span class="sourceLineNo">1271</span> for (int i = 0; i < writerThreads.length; ++i) writerThreads[i].interrupt();<a name="line.1271"></a>
+<span class="sourceLineNo">1272</span> this.ramCache.clear();<a name="line.1272"></a>
+<span class="sourceLineNo">1273</span> if (!ioEngine.isPersistent() || persistencePath == null) {<a name="line.1273"></a>
+<span class="sourceLineNo">1274</span> // If persistent ioengine and a path, we will serialize out the backingMap.<a name="line.1274"></a>
+<span class="sourceLineNo">1275</span> this.backingMap.clear();<a name="line.1275"></a>
+<span class="sourceLineNo">1276</span> }<a name="line.1276"></a>
+<span class="sourceLineNo">1277</span> }<a name="line.1277"></a>
+<span class="sourceLineNo">1278</span><a name="line.1278"></a>
+<span class="sourceLineNo">1279</span> private void join() throws InterruptedException {<a name="line.1279"></a>
+<span class="sourceLineNo">1280</span> for (int i = 0; i < writerThreads.length; ++i)<a name="line.1280"></a>
+<span class="sourceLineNo">1281</span> writerThreads[i].join();<a name="line.1281"></a>
+<span class="sourceLineNo">1282</span> }<a name="line.1282"></a>
+<span class="sourceLineNo">1283</span><a name="line.1283"></a>
+<span class="sourceLineNo">1284</span> @Override<a name="line.1284"></a>
+<span class="sourceLineNo">1285</span> public void shutdown() {<a name="line.1285"></a>
+<span class="sourceLineNo">1286</span> disableCache();<a name="line.1286"></a>
+<span class="sourceLineNo">1287</span> LOG.info("Shutdown bucket cache: IO persistent=" + ioEngine.isPersistent()<a name="line.1287"></a>
+<span class="sourceLineNo">1288</span> + "; path to write=" + persistencePath);<a name="line.1288"></a>
+<span class="sourceLineNo">1289</span> if (ioEngine.isPersistent() && persistencePath != null) {<a name="line.1289"></a>
+<span class="sourceLineNo">1290</span> try {<a name="line.1290"></a>
+<span class="sourceLineNo">1291</span> join();<a name="line.1291"></a>
+<span class="sourceLineNo">1292</span> persistToFile();<a name="line.1292"></a>
+<span class="sourceLineNo">1293</span> } catch (IOException ex) {<a name="line.1293"></a>
+<span class="sourceLineNo">1294</span> LOG.error("Unable to persist data on exit: " + ex.toString(), ex);<a name="line.1294"></a>
+<span class="sourceLineNo">1295</span> } catch (InterruptedException e) {<a name="line.1295"></a>
+<span class="sourceLineNo">1296</span> LOG.warn("Failed to persist data on exit", e);<a name="line.1296"></a>
+<span class="sourceLineNo">1297</span> }<a name="line.1297"></a>
+<span class="sourceLineNo">1298</span> }<a name="line.1298"></a>
+<span class="sourceLineNo">1299</span> }<a name="line.1299"></a>
+<span class="sourceLineNo">1300</span><a name="line.1300"></a>
+<span class="sourceLineNo">1301</span> @Override<a name="line.1301"></a>
+<span class="sourceLineNo">1302</span> public CacheStats getStats() {<a name="line.1302"></a>
+<span class="sourceLineNo">1303</span> return cacheStats;<a name="line.1303"></a>
<span class="sourceLineNo">1304</span> }<a name="line.1304"></a>
<span class="sourceLineNo">1305</span><a name="line.1305"></a>
-<span class="sourceLineNo">1306</span> @Override<a name="line.1306"></a>
-<span class="sourceLineNo">1307</span> public long heapSize() {<a name="line.1307"></a>
-<span class="sourceLineNo">1308</span> return this.heapSize.sum();<a name="line.1308"></a>
-<span class="sourceLineNo">1309</span> }<a name="line.1309"></a>
-<span class="sourceLineNo">1310</span><a name="line.1310"></a>
-<span class="sourceLineNo">1311</span> @Override<a name="line.1311"></a>
-<span class="sourceLineNo">1312</span> public long size() {<a name="line.1312"></a>
-<span class="sourceLineNo">1313</span> return this.realCacheSize.sum();<a name="line.1313"></a>
-<span class="sourceLineNo">1314</span> }<a name="line.1314"></a>
-<span class="sourceLineNo">1315</span><a name="line.1315"></a>
-<span class="sourceLineNo">1316</span> @Override<a name="line.1316"></a>
-<span class="sourceLineNo">1317</span> public long getCurrentDataSize() {<a name="line.1317"></a>
-<span class="sourceLineNo">1318</span> return size();<a name="line.1318"></a>
-<span class="sourceLineNo">1319</span> }<a name="line.1319"></a>
-<span class="sourceLineNo">1320</span><a name="line.1320"></a>
-<span class="sourceLineNo">1321</span> @Override<a name="line.1321"></a>
-<span class="sourceLineNo">1322</span> public long getFreeSize() {<a name="line.1322"></a>
-<span class="sourceLineNo">1323</span> return this.bucketAllocator.getFreeSize();<a name="line.1323"></a>
-<span class="sourceLineNo">1324</span> }<a name="line.1324"></a>
-<span class="sourceLineNo">1325</span><a name="line.1325"></a>
-<span class="sourceLineNo">1326</span> @Override<a name="line.1326"></a>
-<span class="sourceLineNo">1327</span> public long getBlockCount() {<a name="line.1327"></a>
-<span class="sourceLineNo">1328</span> return this.blockNumber.sum();<a name="line.1328"></a>
-<span class="sourceLineNo">1329</span> }<a name="line.1329"></a>
-<span class="sourceLineNo">1330</span><a name="line.1330"></a>
-<span class="sourceLineNo">1331</span> @Override<a name="line.1331"></a>
-<span class="sourceLineNo">1332</span> public long getDataBlockCount() {<a name="line.1332"></a>
-<span class="sourceLineNo">1333</span> return getBlockCount();<a name="line.1333"></a>
-<span class="sourceLineNo">1334</span> }<a name="line.1334"></a>
-<span class="sourceLineNo">1335</span><a name="line.1335"></a>
-<span class="sourceLineNo">1336</span> @Override<a name="line.1336"></a>
-<span class="sourceLineNo">1337</span> public long getCurrentSize() {<a name="line.1337"></a>
-<span class="sourceLineNo">1338</span> return this.bucketAllocator.getUsedSize();<a name="line.1338"></a>
-<span class="sourceLineNo">1339</span> }<a name="line.1339"></a>
-<span class="sourceLineNo">1340</span><a name="line.1340"></a>
-<span class="sourceLineNo">1341</span> protected String getAlgorithm() {<a name="line.1341"></a>
-<span class="sourceLineNo">1342</span> return algorithm;<a name="line.1342"></a>
+<span class="sourceLineNo">1306</span> public BucketAllocator getAllocator() {<a name="line.1306"></a>
... 5848 lines suppressed ...