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 2026/09/10 00:00:23 UTC
(hbase-site) branch asf-site updated: Published site at 50757d0ce9054766a628ca67dae0b656957cf043.
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits 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 70254b0ed81 Published site at 50757d0ce9054766a628ca67dae0b656957cf043.
70254b0ed81 is described below
commit 70254b0ed819e0a2f52e66c85265dd5789fe8526
Author: HBase <de...@hbase.apache.org>
AuthorDate: Wed Sep 9 23:54:25 2026 +0000
Published site at 50757d0ce9054766a628ca67dae0b656957cf043.
---
.../hadoop/hbase/client/TableSnapshotScanner.html | 434 ++++----
.../TableSnapshotInputFormatImpl.InputSplit.html | 2 +-
.../TableSnapshotInputFormatImpl.RecordReader.html | 2 +-
.../mapreduce/TableSnapshotInputFormatImpl.html | 2 +-
books/apache-hbase-reference-guide-dark-mode.pdf | Bin 38775819 -> 38775817 bytes
books/apache-hbase-reference-guide.pdf | Bin 38665189 -> 38665189 bytes
checkstyle-aggregate.html | 2 +-
dependency-convergence.html | 2 +-
dependency-info.html | 2 +-
dependency-management.html | 2 +-
.../hadoop/hbase/client/TableSnapshotScanner.html | 48 +-
.../hadoop/hbase/client/TableSnapshotScanner.html | 434 ++++----
.../TableSnapshotInputFormatImpl.InputSplit.html | 2 +-
.../TableSnapshotInputFormatImpl.RecordReader.html | 2 +-
.../mapreduce/TableSnapshotInputFormatImpl.html | 2 +-
hbase-website/dependency-convergence.html | 2 +-
hbase-website/dependency-info.html | 2 +-
hbase-website/dependency-management.html | 2 +-
hbase-website/index.html | 2 +-
hbase-website/issue-management.html | 2 +-
hbase-website/licenses.html | 2 +-
hbase-website/mailing-lists.html | 2 +-
hbase-website/plugin-management.html | 2 +-
hbase-website/plugins.html | 2 +-
hbase-website/project-info.html | 2 +-
hbase-website/scm.html | 2 +-
hbase-website/summary.html | 2 +-
hbase-website/team.html | 2 +-
issue-management.html | 2 +-
licenses.html | 2 +-
mailing-lists.html | 2 +-
plugin-management.html | 2 +-
plugins.html | 2 +-
project-info.html | 2 +-
project-reports.html | 2 +-
scm.html | 2 +-
summary.html | 2 +-
team.html | 2 +-
testapidocs/index-all.html | 2 +
testapidocs/member-search-index.js | 2 +-
.../hbase/client/TestTableSnapshotScanner.html | 1156 ++++++++++----------
testdevapidocs/index-all.html | 2 +
testdevapidocs/member-search-index.js | 2 +-
.../hbase/client/TestTableSnapshotScanner.html | 109 +-
.../client/TestTableSnapshotScannerWithSFT.html | 2 +-
.../hbase/master/assignment/TestRollbackSCP.html | 6 +-
.../hbase/client/TestTableSnapshotScanner.html | 1156 ++++++++++----------
.../TestRollbackSCP.AssignmentManagerForTest.html | 99 +-
.../assignment/TestRollbackSCP.HMasterForTest.html | 99 +-
.../hbase/master/assignment/TestRollbackSCP.html | 99 +-
50 files changed, 1907 insertions(+), 1809 deletions(-)
diff --git a/apidocs/src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html b/apidocs/src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html
index 9217df49239..5c7d1bfa0e1 100644
--- a/apidocs/src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html
+++ b/apidocs/src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html
@@ -39,220 +39,226 @@
<span class="source-line-no">026</span><span id="line-26">import org.apache.hadoop.fs.Path;</span>
<span class="source-line-no">027</span><span id="line-27">import org.apache.hadoop.hbase.PrivateCellUtil;</span>
<span class="source-line-no">028</span><span id="line-28">import org.apache.hadoop.hbase.client.metrics.ScanMetrics;</span>
-<span class="source-line-no">029</span><span id="line-29">import org.apache.hadoop.hbase.regionserver.MemStoreLAB;</span>
-<span class="source-line-no">030</span><span id="line-30">import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;</span>
-<span class="source-line-no">031</span><span id="line-31">import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;</span>
-<span class="source-line-no">032</span><span id="line-32">import org.apache.hadoop.hbase.snapshot.SnapshotManifest;</span>
-<span class="source-line-no">033</span><span id="line-33">import org.apache.hadoop.hbase.util.CommonFSUtils;</span>
-<span class="source-line-no">034</span><span id="line-34">import org.apache.yetus.audience.InterfaceAudience;</span>
-<span class="source-line-no">035</span><span id="line-35">import org.slf4j.Logger;</span>
-<span class="source-line-no">036</span><span id="line-36">import org.slf4j.LoggerFactory;</span>
-<span class="source-line-no">037</span><span id="line-37"></span>
-<span class="source-line-no">038</span><span id="line-38">import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;</span>
-<span class="source-line-no">039</span><span id="line-39">import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;</span>
-<span class="source-line-no">040</span><span id="line-40">import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;</span>
-<span class="source-line-no">041</span><span id="line-41"></span>
-<span class="source-line-no">042</span><span id="line-42">/**</span>
-<span class="source-line-no">043</span><span id="line-43"> * A Scanner which performs a scan over snapshot files. Using this class requires copying the</span>
-<span class="source-line-no">044</span><span id="line-44"> * snapshot to a temporary empty directory, which will copy the snapshot reference files into that</span>
-<span class="source-line-no">045</span><span id="line-45"> * directory. Actual data files are not copied.</span>
-<span class="source-line-no">046</span><span id="line-46"> * <p></span>
-<span class="source-line-no">047</span><span id="line-47"> * This also allows one to run the scan from an online or offline hbase cluster. The snapshot files</span>
-<span class="source-line-no">048</span><span id="line-48"> * can be exported by using the org.apache.hadoop.hbase.snapshot.ExportSnapshot tool, to a pure-hdfs</span>
-<span class="source-line-no">049</span><span id="line-49"> * cluster, and this scanner can be used to run the scan directly over the snapshot files. The</span>
-<span class="source-line-no">050</span><span id="line-50"> * snapshot should not be deleted while there are open scanners reading from snapshot files.</span>
-<span class="source-line-no">051</span><span id="line-51"> * <p></span>
-<span class="source-line-no">052</span><span id="line-52"> * An internal RegionScanner is used to execute the {@link Scan} obtained from the user for each</span>
-<span class="source-line-no">053</span><span id="line-53"> * region in the snapshot.</span>
-<span class="source-line-no">054</span><span id="line-54"> * <p></span>
-<span class="source-line-no">055</span><span id="line-55"> * HBase owns all the data and snapshot files on the filesystem. Only the HBase user can read from</span>
-<span class="source-line-no">056</span><span id="line-56"> * snapshot files and data files. HBase also enforces security because all the requests are handled</span>
-<span class="source-line-no">057</span><span id="line-57"> * by the server layer, and the user cannot read from the data files directly. To read from snapshot</span>
-<span class="source-line-no">058</span><span id="line-58"> * files directly from the file system, the user who is running the MR job must have sufficient</span>
-<span class="source-line-no">059</span><span id="line-59"> * permissions to access snapshot and reference files. This means that to run mapreduce over</span>
-<span class="source-line-no">060</span><span id="line-60"> * snapshot files, the job has to be run as the HBase user or the user must have group or other</span>
-<span class="source-line-no">061</span><span id="line-61"> * priviledges in the filesystem (See HBASE-8369). Note that, given other users access to read from</span>
-<span class="source-line-no">062</span><span id="line-62"> * snapshot/data files will completely circumvent the access control enforced by HBase. See</span>
-<span class="source-line-no">063</span><span id="line-63"> * org.apache.hadoop.hbase.mapreduce.TableSnapshotInputFormat.</span>
-<span class="source-line-no">064</span><span id="line-64"> */</span>
-<span class="source-line-no">065</span><span id="line-65">@InterfaceAudience.Private</span>
-<span class="source-line-no">066</span><span id="line-66">public class TableSnapshotScanner extends AbstractClientScanner {</span>
-<span class="source-line-no">067</span><span id="line-67"></span>
-<span class="source-line-no">068</span><span id="line-68"> private static final Logger LOG = LoggerFactory.getLogger(TableSnapshotScanner.class);</span>
-<span class="source-line-no">069</span><span id="line-69"></span>
-<span class="source-line-no">070</span><span id="line-70"> private Configuration conf;</span>
-<span class="source-line-no">071</span><span id="line-71"> private String snapshotName;</span>
-<span class="source-line-no">072</span><span id="line-72"> private FileSystem fs;</span>
-<span class="source-line-no">073</span><span id="line-73"> private Path rootDir;</span>
-<span class="source-line-no">074</span><span id="line-74"> private Path restoreDir;</span>
-<span class="source-line-no">075</span><span id="line-75"> private Scan scan;</span>
-<span class="source-line-no">076</span><span id="line-76"> private ArrayList<RegionInfo> regions;</span>
-<span class="source-line-no">077</span><span id="line-77"> private TableDescriptor htd;</span>
-<span class="source-line-no">078</span><span id="line-78"> private final boolean snapshotAlreadyRestored;</span>
-<span class="source-line-no">079</span><span id="line-79"></span>
-<span class="source-line-no">080</span><span id="line-80"> private ClientSideRegionScanner currentRegionScanner = null;</span>
-<span class="source-line-no">081</span><span id="line-81"> private int currentRegion = -1;</span>
-<span class="source-line-no">082</span><span id="line-82"></span>
-<span class="source-line-no">083</span><span id="line-83"> private int numOfCompleteRows = 0;</span>
-<span class="source-line-no">084</span><span id="line-84"></span>
-<span class="source-line-no">085</span><span id="line-85"> /**</span>
-<span class="source-line-no">086</span><span id="line-86"> * Creates a TableSnapshotScanner.</span>
-<span class="source-line-no">087</span><span id="line-87"> * @param conf the configuration</span>
-<span class="source-line-no">088</span><span id="line-88"> * @param restoreDir a temporary directory to copy the snapshot files into. Current user should</span>
-<span class="source-line-no">089</span><span id="line-89"> * have write permissions to this directory, and this should not be a</span>
-<span class="source-line-no">090</span><span id="line-90"> * subdirectory of rootDir. The scanner deletes the contents of the directory</span>
-<span class="source-line-no">091</span><span id="line-91"> * once the scanner is closed.</span>
-<span class="source-line-no">092</span><span id="line-92"> * @param snapshotName the name of the snapshot to read from</span>
-<span class="source-line-no">093</span><span id="line-93"> * @param scan a Scan representing scan parameters</span>
-<span class="source-line-no">094</span><span id="line-94"> * @throws IOException in case of error</span>
-<span class="source-line-no">095</span><span id="line-95"> */</span>
-<span class="source-line-no">096</span><span id="line-96"> public TableSnapshotScanner(Configuration conf, Path restoreDir, String snapshotName, Scan scan)</span>
-<span class="source-line-no">097</span><span id="line-97"> throws IOException {</span>
-<span class="source-line-no">098</span><span id="line-98"> this(conf, CommonFSUtils.getRootDir(conf), restoreDir, snapshotName, scan);</span>
-<span class="source-line-no">099</span><span id="line-99"> }</span>
-<span class="source-line-no">100</span><span id="line-100"></span>
-<span class="source-line-no">101</span><span id="line-101"> public TableSnapshotScanner(Configuration conf, Path rootDir, Path restoreDir,</span>
-<span class="source-line-no">102</span><span id="line-102"> String snapshotName, Scan scan) throws IOException {</span>
-<span class="source-line-no">103</span><span id="line-103"> this(conf, rootDir, restoreDir, snapshotName, scan, false);</span>
-<span class="source-line-no">104</span><span id="line-104"> }</span>
-<span class="source-line-no">105</span><span id="line-105"></span>
-<span class="source-line-no">106</span><span id="line-106"> /**</span>
-<span class="source-line-no">107</span><span id="line-107"> * Creates a TableSnapshotScanner.</span>
-<span class="source-line-no">108</span><span id="line-108"> * @param conf the configuration</span>
-<span class="source-line-no">109</span><span id="line-109"> * @param rootDir root directory for HBase.</span>
-<span class="source-line-no">110</span><span id="line-110"> * @param restoreDir a temporary directory to copy the snapshot files into. Current</span>
-<span class="source-line-no">111</span><span id="line-111"> * user should have write permissions to this directory, and this</span>
-<span class="source-line-no">112</span><span id="line-112"> * should not be a subdirectory of rootdir. The scanner deletes the</span>
-<span class="source-line-no">113</span><span id="line-113"> * contents of the directory once the scanner is closed.</span>
-<span class="source-line-no">114</span><span id="line-114"> * @param snapshotName the name of the snapshot to read from</span>
-<span class="source-line-no">115</span><span id="line-115"> * @param scan a Scan representing scan parameters</span>
-<span class="source-line-no">116</span><span id="line-116"> * @param snapshotAlreadyRestored true to indicate that snapshot has been restored.</span>
-<span class="source-line-no">117</span><span id="line-117"> * @throws IOException in case of error</span>
-<span class="source-line-no">118</span><span id="line-118"> */</span>
-<span class="source-line-no">119</span><span id="line-119"> public TableSnapshotScanner(Configuration conf, Path rootDir, Path restoreDir,</span>
-<span class="source-line-no">120</span><span id="line-120"> String snapshotName, Scan scan, boolean snapshotAlreadyRestored) throws IOException {</span>
-<span class="source-line-no">121</span><span id="line-121"> this.conf = conf;</span>
-<span class="source-line-no">122</span><span id="line-122"> this.snapshotName = snapshotName;</span>
-<span class="source-line-no">123</span><span id="line-123"> this.rootDir = rootDir;</span>
-<span class="source-line-no">124</span><span id="line-124"> this.scan = scan;</span>
-<span class="source-line-no">125</span><span id="line-125"> this.snapshotAlreadyRestored = snapshotAlreadyRestored;</span>
-<span class="source-line-no">126</span><span id="line-126"> this.fs = rootDir.getFileSystem(conf);</span>
-<span class="source-line-no">127</span><span id="line-127"> conf.setBoolean(MemStoreLAB.USEMSLAB_KEY, false);</span>
-<span class="source-line-no">128</span><span id="line-128"></span>
-<span class="source-line-no">129</span><span id="line-129"> if (snapshotAlreadyRestored) {</span>
-<span class="source-line-no">130</span><span id="line-130"> this.restoreDir = restoreDir;</span>
-<span class="source-line-no">131</span><span id="line-131"> openWithoutRestoringSnapshot();</span>
-<span class="source-line-no">132</span><span id="line-132"> } else {</span>
-<span class="source-line-no">133</span><span id="line-133"> // restoreDir will be deleted in close(), use a unique sub directory</span>
-<span class="source-line-no">134</span><span id="line-134"> this.restoreDir = new Path(restoreDir, UUID.randomUUID().toString());</span>
-<span class="source-line-no">135</span><span id="line-135"> openWithRestoringSnapshot();</span>
-<span class="source-line-no">136</span><span id="line-136"> }</span>
-<span class="source-line-no">137</span><span id="line-137"></span>
-<span class="source-line-no">138</span><span id="line-138"> initScanMetrics(scan);</span>
-<span class="source-line-no">139</span><span id="line-139"> }</span>
-<span class="source-line-no">140</span><span id="line-140"></span>
-<span class="source-line-no">141</span><span id="line-141"> private void openWithoutRestoringSnapshot() throws IOException {</span>
-<span class="source-line-no">142</span><span id="line-142"> Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);</span>
-<span class="source-line-no">143</span><span id="line-143"> SnapshotProtos.SnapshotDescription snapshotDesc =</span>
-<span class="source-line-no">144</span><span id="line-144"> SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);</span>
-<span class="source-line-no">145</span><span id="line-145"></span>
-<span class="source-line-no">146</span><span id="line-146"> SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshotDesc);</span>
-<span class="source-line-no">147</span><span id="line-147"> List<SnapshotRegionManifest> regionManifests = manifest.getRegionManifests();</span>
-<span class="source-line-no">148</span><span id="line-148"> if (regionManifests == null) {</span>
-<span class="source-line-no">149</span><span id="line-149"> throw new IllegalArgumentException("Snapshot seems empty, snapshotName: " + snapshotName);</span>
-<span class="source-line-no">150</span><span id="line-150"> }</span>
-<span class="source-line-no">151</span><span id="line-151"></span>
-<span class="source-line-no">152</span><span id="line-152"> regions = new ArrayList<>(regionManifests.size());</span>
-<span class="source-line-no">153</span><span id="line-153"> regionManifests.stream().map(r -> ProtobufUtil.toRegionInfo(r.getRegionInfo()))</span>
-<span class="source-line-no">154</span><span id="line-154"> .filter(this::isValidRegion).sorted().forEach(r -> regions.add(r));</span>
-<span class="source-line-no">155</span><span id="line-155"> htd = manifest.getTableDescriptor();</span>
-<span class="source-line-no">156</span><span id="line-156"> }</span>
-<span class="source-line-no">157</span><span id="line-157"></span>
-<span class="source-line-no">158</span><span id="line-158"> private boolean isValidRegion(RegionInfo hri) {</span>
-<span class="source-line-no">159</span><span id="line-159"> // An offline split parent region should be excluded.</span>
-<span class="source-line-no">160</span><span id="line-160"> if (hri.isOffline() && (hri.isSplit() || hri.isSplitParent())) {</span>
-<span class="source-line-no">161</span><span id="line-161"> return false;</span>
-<span class="source-line-no">162</span><span id="line-162"> }</span>
-<span class="source-line-no">163</span><span id="line-163"> return PrivateCellUtil.overlappingKeys(scan.getStartRow(), scan.getStopRow(), hri.getStartKey(),</span>
-<span class="source-line-no">164</span><span id="line-164"> hri.getEndKey());</span>
-<span class="source-line-no">165</span><span id="line-165"> }</span>
-<span class="source-line-no">166</span><span id="line-166"></span>
-<span class="source-line-no">167</span><span id="line-167"> private void openWithRestoringSnapshot() throws IOException {</span>
-<span class="source-line-no">168</span><span id="line-168"> final RestoreSnapshotHelper.RestoreMetaChanges meta =</span>
-<span class="source-line-no">169</span><span id="line-169"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
-<span class="source-line-no">170</span><span id="line-170"> final List<RegionInfo> restoredRegions = meta.getRegionsToAdd();</span>
-<span class="source-line-no">171</span><span id="line-171"></span>
-<span class="source-line-no">172</span><span id="line-172"> htd = meta.getTableDescriptor();</span>
-<span class="source-line-no">173</span><span id="line-173"> regions = new ArrayList<>(restoredRegions.size());</span>
-<span class="source-line-no">174</span><span id="line-174"> restoredRegions.stream().filter(this::isValidRegion).sorted().forEach(r -> regions.add(r));</span>
-<span class="source-line-no">175</span><span id="line-175"> }</span>
-<span class="source-line-no">176</span><span id="line-176"></span>
-<span class="source-line-no">177</span><span id="line-177"> @Override</span>
-<span class="source-line-no">178</span><span id="line-178"> public Result next() throws IOException {</span>
-<span class="source-line-no">179</span><span id="line-179"> Result result = null;</span>
-<span class="source-line-no">180</span><span id="line-180"> while (true) {</span>
-<span class="source-line-no">181</span><span id="line-181"> if (currentRegionScanner == null) {</span>
-<span class="source-line-no">182</span><span id="line-182"> currentRegion++;</span>
-<span class="source-line-no">183</span><span id="line-183"> if (currentRegion >= regions.size()) {</span>
-<span class="source-line-no">184</span><span id="line-184"> return null;</span>
-<span class="source-line-no">185</span><span id="line-185"> }</span>
-<span class="source-line-no">186</span><span id="line-186"></span>
-<span class="source-line-no">187</span><span id="line-187"> RegionInfo hri = regions.get(currentRegion);</span>
-<span class="source-line-no">188</span><span id="line-188"> currentRegionScanner =</span>
-<span class="source-line-no">189</span><span id="line-189"> new ClientSideRegionScanner(conf, fs, restoreDir, htd, hri, scan, scanMetrics);</span>
-<span class="source-line-no">190</span><span id="line-190"> if (this.scanMetrics != null) {</span>
-<span class="source-line-no">191</span><span id="line-191"> this.scanMetrics.addToCounter(ScanMetrics.REGIONS_SCANNED_METRIC_NAME, 1);</span>
-<span class="source-line-no">192</span><span id="line-192"> }</span>
-<span class="source-line-no">193</span><span id="line-193"> }</span>
-<span class="source-line-no">194</span><span id="line-194"></span>
-<span class="source-line-no">195</span><span id="line-195"> try {</span>
-<span class="source-line-no">196</span><span id="line-196"> result = currentRegionScanner.next();</span>
-<span class="source-line-no">197</span><span id="line-197"> if (result != null) {</span>
-<span class="source-line-no">198</span><span id="line-198"> if (scan.getLimit() > 0 && ++this.numOfCompleteRows > scan.getLimit()) {</span>
-<span class="source-line-no">199</span><span id="line-199"> result = null;</span>
-<span class="source-line-no">200</span><span id="line-200"> }</span>
-<span class="source-line-no">201</span><span id="line-201"> return result;</span>
-<span class="source-line-no">202</span><span id="line-202"> }</span>
-<span class="source-line-no">203</span><span id="line-203"> } finally {</span>
-<span class="source-line-no">204</span><span id="line-204"> if (result == null) {</span>
-<span class="source-line-no">205</span><span id="line-205"> currentRegionScanner.close();</span>
-<span class="source-line-no">206</span><span id="line-206"> currentRegionScanner = null;</span>
-<span class="source-line-no">207</span><span id="line-207"> }</span>
-<span class="source-line-no">208</span><span id="line-208"> }</span>
-<span class="source-line-no">209</span><span id="line-209"> }</span>
-<span class="source-line-no">210</span><span id="line-210"> }</span>
-<span class="source-line-no">211</span><span id="line-211"></span>
-<span class="source-line-no">212</span><span id="line-212"> private void cleanup() {</span>
-<span class="source-line-no">213</span><span id="line-213"> try {</span>
-<span class="source-line-no">214</span><span id="line-214"> if (fs.exists(this.restoreDir)) {</span>
-<span class="source-line-no">215</span><span id="line-215"> if (!fs.delete(this.restoreDir, true)) {</span>
-<span class="source-line-no">216</span><span id="line-216"> LOG.warn(</span>
-<span class="source-line-no">217</span><span id="line-217"> "Delete restore directory for the snapshot failed. restoreDir: " + this.restoreDir);</span>
-<span class="source-line-no">218</span><span id="line-218"> }</span>
-<span class="source-line-no">219</span><span id="line-219"> }</span>
-<span class="source-line-no">220</span><span id="line-220"> } catch (IOException ex) {</span>
-<span class="source-line-no">221</span><span id="line-221"> LOG.warn(</span>
-<span class="source-line-no">222</span><span id="line-222"> "Could not delete restore directory for the snapshot. restoreDir: " + this.restoreDir, ex);</span>
-<span class="source-line-no">223</span><span id="line-223"> }</span>
-<span class="source-line-no">224</span><span id="line-224"> }</span>
-<span class="source-line-no">225</span><span id="line-225"></span>
-<span class="source-line-no">226</span><span id="line-226"> @Override</span>
-<span class="source-line-no">227</span><span id="line-227"> public void close() {</span>
-<span class="source-line-no">228</span><span id="line-228"> if (currentRegionScanner != null) {</span>
-<span class="source-line-no">229</span><span id="line-229"> currentRegionScanner.close();</span>
-<span class="source-line-no">230</span><span id="line-230"> }</span>
-<span class="source-line-no">231</span><span id="line-231"> // if snapshotAlreadyRestored is true, then we should invoke cleanup() method by hand.</span>
-<span class="source-line-no">232</span><span id="line-232"> if (!this.snapshotAlreadyRestored) {</span>
-<span class="source-line-no">233</span><span id="line-233"> cleanup();</span>
-<span class="source-line-no">234</span><span id="line-234"> }</span>
-<span class="source-line-no">235</span><span id="line-235"> }</span>
-<span class="source-line-no">236</span><span id="line-236"></span>
-<span class="source-line-no">237</span><span id="line-237"> @Override</span>
-<span class="source-line-no">238</span><span id="line-238"> public boolean renewLease() {</span>
-<span class="source-line-no">239</span><span id="line-239"> throw new UnsupportedOperationException();</span>
-<span class="source-line-no">240</span><span id="line-240"> }</span>
-<span class="source-line-no">241</span><span id="line-241"></span>
-<span class="source-line-no">242</span><span id="line-242">}</span>
+<span class="source-line-no">029</span><span id="line-29">import org.apache.hadoop.hbase.mob.MobUtils;</span>
+<span class="source-line-no">030</span><span id="line-30">import org.apache.hadoop.hbase.regionserver.MemStoreLAB;</span>
+<span class="source-line-no">031</span><span id="line-31">import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;</span>
+<span class="source-line-no">032</span><span id="line-32">import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;</span>
+<span class="source-line-no">033</span><span id="line-33">import org.apache.hadoop.hbase.snapshot.SnapshotManifest;</span>
+<span class="source-line-no">034</span><span id="line-34">import org.apache.hadoop.hbase.util.CommonFSUtils;</span>
+<span class="source-line-no">035</span><span id="line-35">import org.apache.yetus.audience.InterfaceAudience;</span>
+<span class="source-line-no">036</span><span id="line-36">import org.slf4j.Logger;</span>
+<span class="source-line-no">037</span><span id="line-37">import org.slf4j.LoggerFactory;</span>
+<span class="source-line-no">038</span><span id="line-38"></span>
+<span class="source-line-no">039</span><span id="line-39">import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;</span>
+<span class="source-line-no">040</span><span id="line-40">import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;</span>
+<span class="source-line-no">041</span><span id="line-41">import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;</span>
+<span class="source-line-no">042</span><span id="line-42"></span>
+<span class="source-line-no">043</span><span id="line-43">/**</span>
+<span class="source-line-no">044</span><span id="line-44"> * A Scanner which performs a scan over snapshot files. Using this class requires copying the</span>
+<span class="source-line-no">045</span><span id="line-45"> * snapshot to a temporary empty directory, which will copy the snapshot reference files into that</span>
+<span class="source-line-no">046</span><span id="line-46"> * directory. Actual data files are not copied.</span>
+<span class="source-line-no">047</span><span id="line-47"> * <p></span>
+<span class="source-line-no">048</span><span id="line-48"> * This also allows one to run the scan from an online or offline hbase cluster. The snapshot files</span>
+<span class="source-line-no">049</span><span id="line-49"> * can be exported by using the org.apache.hadoop.hbase.snapshot.ExportSnapshot tool, to a pure-hdfs</span>
+<span class="source-line-no">050</span><span id="line-50"> * cluster, and this scanner can be used to run the scan directly over the snapshot files. The</span>
+<span class="source-line-no">051</span><span id="line-51"> * snapshot should not be deleted while there are open scanners reading from snapshot files.</span>
+<span class="source-line-no">052</span><span id="line-52"> * <p></span>
+<span class="source-line-no">053</span><span id="line-53"> * An internal RegionScanner is used to execute the {@link Scan} obtained from the user for each</span>
+<span class="source-line-no">054</span><span id="line-54"> * region in the snapshot.</span>
+<span class="source-line-no">055</span><span id="line-55"> * <p></span>
+<span class="source-line-no">056</span><span id="line-56"> * HBase owns all the data and snapshot files on the filesystem. Only the HBase user can read from</span>
+<span class="source-line-no">057</span><span id="line-57"> * snapshot files and data files. HBase also enforces security because all the requests are handled</span>
+<span class="source-line-no">058</span><span id="line-58"> * by the server layer, and the user cannot read from the data files directly. To read from snapshot</span>
+<span class="source-line-no">059</span><span id="line-59"> * files directly from the file system, the user who is running the MR job must have sufficient</span>
+<span class="source-line-no">060</span><span id="line-60"> * permissions to access snapshot and reference files. This means that to run mapreduce over</span>
+<span class="source-line-no">061</span><span id="line-61"> * snapshot files, the job has to be run as the HBase user or the user must have group or other</span>
+<span class="source-line-no">062</span><span id="line-62"> * priviledges in the filesystem (See HBASE-8369). Note that, given other users access to read from</span>
+<span class="source-line-no">063</span><span id="line-63"> * snapshot/data files will completely circumvent the access control enforced by HBase. See</span>
+<span class="source-line-no">064</span><span id="line-64"> * org.apache.hadoop.hbase.mapreduce.TableSnapshotInputFormat.</span>
+<span class="source-line-no">065</span><span id="line-65"> */</span>
+<span class="source-line-no">066</span><span id="line-66">@InterfaceAudience.Private</span>
+<span class="source-line-no">067</span><span id="line-67">public class TableSnapshotScanner extends AbstractClientScanner {</span>
+<span class="source-line-no">068</span><span id="line-68"></span>
+<span class="source-line-no">069</span><span id="line-69"> private static final Logger LOG = LoggerFactory.getLogger(TableSnapshotScanner.class);</span>
+<span class="source-line-no">070</span><span id="line-70"></span>
+<span class="source-line-no">071</span><span id="line-71"> private Configuration conf;</span>
+<span class="source-line-no">072</span><span id="line-72"> private String snapshotName;</span>
+<span class="source-line-no">073</span><span id="line-73"> private FileSystem fs;</span>
+<span class="source-line-no">074</span><span id="line-74"> private Path rootDir;</span>
+<span class="source-line-no">075</span><span id="line-75"> private Path restoreDir;</span>
+<span class="source-line-no">076</span><span id="line-76"> private Scan scan;</span>
+<span class="source-line-no">077</span><span id="line-77"> private ArrayList<RegionInfo> regions;</span>
+<span class="source-line-no">078</span><span id="line-78"> private TableDescriptor htd;</span>
+<span class="source-line-no">079</span><span id="line-79"> private final boolean snapshotAlreadyRestored;</span>
+<span class="source-line-no">080</span><span id="line-80"></span>
+<span class="source-line-no">081</span><span id="line-81"> private ClientSideRegionScanner currentRegionScanner = null;</span>
+<span class="source-line-no">082</span><span id="line-82"> private int currentRegion = -1;</span>
+<span class="source-line-no">083</span><span id="line-83"></span>
+<span class="source-line-no">084</span><span id="line-84"> private int numOfCompleteRows = 0;</span>
+<span class="source-line-no">085</span><span id="line-85"></span>
+<span class="source-line-no">086</span><span id="line-86"> /**</span>
+<span class="source-line-no">087</span><span id="line-87"> * Creates a TableSnapshotScanner.</span>
+<span class="source-line-no">088</span><span id="line-88"> * @param conf the configuration</span>
+<span class="source-line-no">089</span><span id="line-89"> * @param restoreDir a temporary directory to copy the snapshot files into. Current user should</span>
+<span class="source-line-no">090</span><span id="line-90"> * have write permissions to this directory, and this should not be a</span>
+<span class="source-line-no">091</span><span id="line-91"> * subdirectory of rootDir. The scanner deletes the contents of the directory</span>
+<span class="source-line-no">092</span><span id="line-92"> * once the scanner is closed.</span>
+<span class="source-line-no">093</span><span id="line-93"> * @param snapshotName the name of the snapshot to read from</span>
+<span class="source-line-no">094</span><span id="line-94"> * @param scan a Scan representing scan parameters</span>
+<span class="source-line-no">095</span><span id="line-95"> * @throws IOException in case of error</span>
+<span class="source-line-no">096</span><span id="line-96"> */</span>
+<span class="source-line-no">097</span><span id="line-97"> public TableSnapshotScanner(Configuration conf, Path restoreDir, String snapshotName, Scan scan)</span>
+<span class="source-line-no">098</span><span id="line-98"> throws IOException {</span>
+<span class="source-line-no">099</span><span id="line-99"> this(conf, CommonFSUtils.getRootDir(conf), restoreDir, snapshotName, scan);</span>
+<span class="source-line-no">100</span><span id="line-100"> }</span>
+<span class="source-line-no">101</span><span id="line-101"></span>
+<span class="source-line-no">102</span><span id="line-102"> public TableSnapshotScanner(Configuration conf, Path rootDir, Path restoreDir,</span>
+<span class="source-line-no">103</span><span id="line-103"> String snapshotName, Scan scan) throws IOException {</span>
+<span class="source-line-no">104</span><span id="line-104"> this(conf, rootDir, restoreDir, snapshotName, scan, false);</span>
+<span class="source-line-no">105</span><span id="line-105"> }</span>
+<span class="source-line-no">106</span><span id="line-106"></span>
+<span class="source-line-no">107</span><span id="line-107"> /**</span>
+<span class="source-line-no">108</span><span id="line-108"> * Creates a TableSnapshotScanner.</span>
+<span class="source-line-no">109</span><span id="line-109"> * @param conf the configuration</span>
+<span class="source-line-no">110</span><span id="line-110"> * @param rootDir root directory for HBase.</span>
+<span class="source-line-no">111</span><span id="line-111"> * @param restoreDir a temporary directory to copy the snapshot files into. Current</span>
+<span class="source-line-no">112</span><span id="line-112"> * user should have write permissions to this directory, and this</span>
+<span class="source-line-no">113</span><span id="line-113"> * should not be a subdirectory of rootdir. The scanner deletes the</span>
+<span class="source-line-no">114</span><span id="line-114"> * contents of the directory once the scanner is closed.</span>
+<span class="source-line-no">115</span><span id="line-115"> * @param snapshotName the name of the snapshot to read from</span>
+<span class="source-line-no">116</span><span id="line-116"> * @param scan a Scan representing scan parameters</span>
+<span class="source-line-no">117</span><span id="line-117"> * @param snapshotAlreadyRestored true to indicate that snapshot has been restored.</span>
+<span class="source-line-no">118</span><span id="line-118"> * @throws IOException in case of error</span>
+<span class="source-line-no">119</span><span id="line-119"> */</span>
+<span class="source-line-no">120</span><span id="line-120"> public TableSnapshotScanner(Configuration conf, Path rootDir, Path restoreDir,</span>
+<span class="source-line-no">121</span><span id="line-121"> String snapshotName, Scan scan, boolean snapshotAlreadyRestored) throws IOException {</span>
+<span class="source-line-no">122</span><span id="line-122"> this.conf = conf;</span>
+<span class="source-line-no">123</span><span id="line-123"> this.snapshotName = snapshotName;</span>
+<span class="source-line-no">124</span><span id="line-124"> this.rootDir = rootDir;</span>
+<span class="source-line-no">125</span><span id="line-125"> this.scan = scan;</span>
+<span class="source-line-no">126</span><span id="line-126"> this.snapshotAlreadyRestored = snapshotAlreadyRestored;</span>
+<span class="source-line-no">127</span><span id="line-127"> this.fs = rootDir.getFileSystem(conf);</span>
+<span class="source-line-no">128</span><span id="line-128"> conf.setBoolean(MemStoreLAB.USEMSLAB_KEY, false);</span>
+<span class="source-line-no">129</span><span id="line-129"></span>
+<span class="source-line-no">130</span><span id="line-130"> if (snapshotAlreadyRestored) {</span>
+<span class="source-line-no">131</span><span id="line-131"> this.restoreDir = restoreDir;</span>
+<span class="source-line-no">132</span><span id="line-132"> openWithoutRestoringSnapshot();</span>
+<span class="source-line-no">133</span><span id="line-133"> } else {</span>
+<span class="source-line-no">134</span><span id="line-134"> // restoreDir will be deleted in close(), use a unique sub directory</span>
+<span class="source-line-no">135</span><span id="line-135"> this.restoreDir = new Path(restoreDir, UUID.randomUUID().toString());</span>
+<span class="source-line-no">136</span><span id="line-136"> openWithRestoringSnapshot();</span>
+<span class="source-line-no">137</span><span id="line-137"> }</span>
+<span class="source-line-no">138</span><span id="line-138"></span>
+<span class="source-line-no">139</span><span id="line-139"> initScanMetrics(scan);</span>
+<span class="source-line-no">140</span><span id="line-140"> }</span>
+<span class="source-line-no">141</span><span id="line-141"></span>
+<span class="source-line-no">142</span><span id="line-142"> private void openWithoutRestoringSnapshot() throws IOException {</span>
+<span class="source-line-no">143</span><span id="line-143"> Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);</span>
+<span class="source-line-no">144</span><span id="line-144"> SnapshotProtos.SnapshotDescription snapshotDesc =</span>
+<span class="source-line-no">145</span><span id="line-145"> SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);</span>
+<span class="source-line-no">146</span><span id="line-146"></span>
+<span class="source-line-no">147</span><span id="line-147"> SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshotDesc);</span>
+<span class="source-line-no">148</span><span id="line-148"> List<SnapshotRegionManifest> regionManifests = manifest.getRegionManifests();</span>
+<span class="source-line-no">149</span><span id="line-149"> if (regionManifests == null) {</span>
+<span class="source-line-no">150</span><span id="line-150"> throw new IllegalArgumentException("Snapshot seems empty, snapshotName: " + snapshotName);</span>
+<span class="source-line-no">151</span><span id="line-151"> }</span>
+<span class="source-line-no">152</span><span id="line-152"></span>
+<span class="source-line-no">153</span><span id="line-153"> regions = new ArrayList<>(regionManifests.size());</span>
+<span class="source-line-no">154</span><span id="line-154"> regionManifests.stream().map(r -> ProtobufUtil.toRegionInfo(r.getRegionInfo()))</span>
+<span class="source-line-no">155</span><span id="line-155"> .filter(this::isValidRegion).sorted().forEach(r -> regions.add(r));</span>
+<span class="source-line-no">156</span><span id="line-156"> htd = manifest.getTableDescriptor();</span>
+<span class="source-line-no">157</span><span id="line-157"> }</span>
+<span class="source-line-no">158</span><span id="line-158"></span>
+<span class="source-line-no">159</span><span id="line-159"> private boolean isValidRegion(RegionInfo hri) {</span>
+<span class="source-line-no">160</span><span id="line-160"> // An offline split parent region should be excluded.</span>
+<span class="source-line-no">161</span><span id="line-161"> if (hri.isOffline() && (hri.isSplit() || hri.isSplitParent())) {</span>
+<span class="source-line-no">162</span><span id="line-162"> return false;</span>
+<span class="source-line-no">163</span><span id="line-163"> }</span>
+<span class="source-line-no">164</span><span id="line-164"> // The mob region is a dummy region used only to organise mob files under mobdir. It has no</span>
+<span class="source-line-no">165</span><span id="line-165"> // region directory under the table dir to open. See HBASE-30365 and HBASE-30368.</span>
+<span class="source-line-no">166</span><span id="line-166"> if (MobUtils.isMobRegionInfo(hri)) {</span>
+<span class="source-line-no">167</span><span id="line-167"> return false;</span>
+<span class="source-line-no">168</span><span id="line-168"> }</span>
+<span class="source-line-no">169</span><span id="line-169"> return PrivateCellUtil.overlappingKeys(scan.getStartRow(), scan.getStopRow(), hri.getStartKey(),</span>
+<span class="source-line-no">170</span><span id="line-170"> hri.getEndKey());</span>
+<span class="source-line-no">171</span><span id="line-171"> }</span>
+<span class="source-line-no">172</span><span id="line-172"></span>
+<span class="source-line-no">173</span><span id="line-173"> private void openWithRestoringSnapshot() throws IOException {</span>
+<span class="source-line-no">174</span><span id="line-174"> final RestoreSnapshotHelper.RestoreMetaChanges meta =</span>
+<span class="source-line-no">175</span><span id="line-175"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
+<span class="source-line-no">176</span><span id="line-176"> final List<RegionInfo> restoredRegions = meta.getRegionsToAdd();</span>
+<span class="source-line-no">177</span><span id="line-177"></span>
+<span class="source-line-no">178</span><span id="line-178"> htd = meta.getTableDescriptor();</span>
+<span class="source-line-no">179</span><span id="line-179"> regions = new ArrayList<>(restoredRegions.size());</span>
+<span class="source-line-no">180</span><span id="line-180"> restoredRegions.stream().filter(this::isValidRegion).sorted().forEach(r -> regions.add(r));</span>
+<span class="source-line-no">181</span><span id="line-181"> }</span>
+<span class="source-line-no">182</span><span id="line-182"></span>
+<span class="source-line-no">183</span><span id="line-183"> @Override</span>
+<span class="source-line-no">184</span><span id="line-184"> public Result next() throws IOException {</span>
+<span class="source-line-no">185</span><span id="line-185"> Result result = null;</span>
+<span class="source-line-no">186</span><span id="line-186"> while (true) {</span>
+<span class="source-line-no">187</span><span id="line-187"> if (currentRegionScanner == null) {</span>
+<span class="source-line-no">188</span><span id="line-188"> currentRegion++;</span>
+<span class="source-line-no">189</span><span id="line-189"> if (currentRegion >= regions.size()) {</span>
+<span class="source-line-no">190</span><span id="line-190"> return null;</span>
+<span class="source-line-no">191</span><span id="line-191"> }</span>
+<span class="source-line-no">192</span><span id="line-192"></span>
+<span class="source-line-no">193</span><span id="line-193"> RegionInfo hri = regions.get(currentRegion);</span>
+<span class="source-line-no">194</span><span id="line-194"> currentRegionScanner =</span>
+<span class="source-line-no">195</span><span id="line-195"> new ClientSideRegionScanner(conf, fs, restoreDir, htd, hri, scan, scanMetrics);</span>
+<span class="source-line-no">196</span><span id="line-196"> if (this.scanMetrics != null) {</span>
+<span class="source-line-no">197</span><span id="line-197"> this.scanMetrics.addToCounter(ScanMetrics.REGIONS_SCANNED_METRIC_NAME, 1);</span>
+<span class="source-line-no">198</span><span id="line-198"> }</span>
+<span class="source-line-no">199</span><span id="line-199"> }</span>
+<span class="source-line-no">200</span><span id="line-200"></span>
+<span class="source-line-no">201</span><span id="line-201"> try {</span>
+<span class="source-line-no">202</span><span id="line-202"> result = currentRegionScanner.next();</span>
+<span class="source-line-no">203</span><span id="line-203"> if (result != null) {</span>
+<span class="source-line-no">204</span><span id="line-204"> if (scan.getLimit() > 0 && ++this.numOfCompleteRows > scan.getLimit()) {</span>
+<span class="source-line-no">205</span><span id="line-205"> result = null;</span>
+<span class="source-line-no">206</span><span id="line-206"> }</span>
+<span class="source-line-no">207</span><span id="line-207"> return result;</span>
+<span class="source-line-no">208</span><span id="line-208"> }</span>
+<span class="source-line-no">209</span><span id="line-209"> } finally {</span>
+<span class="source-line-no">210</span><span id="line-210"> if (result == null) {</span>
+<span class="source-line-no">211</span><span id="line-211"> currentRegionScanner.close();</span>
+<span class="source-line-no">212</span><span id="line-212"> currentRegionScanner = null;</span>
+<span class="source-line-no">213</span><span id="line-213"> }</span>
+<span class="source-line-no">214</span><span id="line-214"> }</span>
+<span class="source-line-no">215</span><span id="line-215"> }</span>
+<span class="source-line-no">216</span><span id="line-216"> }</span>
+<span class="source-line-no">217</span><span id="line-217"></span>
+<span class="source-line-no">218</span><span id="line-218"> private void cleanup() {</span>
+<span class="source-line-no">219</span><span id="line-219"> try {</span>
+<span class="source-line-no">220</span><span id="line-220"> if (fs.exists(this.restoreDir)) {</span>
+<span class="source-line-no">221</span><span id="line-221"> if (!fs.delete(this.restoreDir, true)) {</span>
+<span class="source-line-no">222</span><span id="line-222"> LOG.warn(</span>
+<span class="source-line-no">223</span><span id="line-223"> "Delete restore directory for the snapshot failed. restoreDir: " + this.restoreDir);</span>
+<span class="source-line-no">224</span><span id="line-224"> }</span>
+<span class="source-line-no">225</span><span id="line-225"> }</span>
+<span class="source-line-no">226</span><span id="line-226"> } catch (IOException ex) {</span>
+<span class="source-line-no">227</span><span id="line-227"> LOG.warn(</span>
+<span class="source-line-no">228</span><span id="line-228"> "Could not delete restore directory for the snapshot. restoreDir: " + this.restoreDir, ex);</span>
+<span class="source-line-no">229</span><span id="line-229"> }</span>
+<span class="source-line-no">230</span><span id="line-230"> }</span>
+<span class="source-line-no">231</span><span id="line-231"></span>
+<span class="source-line-no">232</span><span id="line-232"> @Override</span>
+<span class="source-line-no">233</span><span id="line-233"> public void close() {</span>
+<span class="source-line-no">234</span><span id="line-234"> if (currentRegionScanner != null) {</span>
+<span class="source-line-no">235</span><span id="line-235"> currentRegionScanner.close();</span>
+<span class="source-line-no">236</span><span id="line-236"> }</span>
+<span class="source-line-no">237</span><span id="line-237"> // if snapshotAlreadyRestored is true, then we should invoke cleanup() method by hand.</span>
+<span class="source-line-no">238</span><span id="line-238"> if (!this.snapshotAlreadyRestored) {</span>
+<span class="source-line-no">239</span><span id="line-239"> cleanup();</span>
+<span class="source-line-no">240</span><span id="line-240"> }</span>
+<span class="source-line-no">241</span><span id="line-241"> }</span>
+<span class="source-line-no">242</span><span id="line-242"></span>
+<span class="source-line-no">243</span><span id="line-243"> @Override</span>
+<span class="source-line-no">244</span><span id="line-244"> public boolean renewLease() {</span>
+<span class="source-line-no">245</span><span id="line-245"> throw new UnsupportedOperationException();</span>
+<span class="source-line-no">246</span><span id="line-246"> }</span>
+<span class="source-line-no">247</span><span id="line-247"></span>
+<span class="source-line-no">248</span><span id="line-248">}</span>
diff --git a/apidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.InputSplit.html b/apidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.InputSplit.html
index 4f25eecac2b..5d87836eb78 100644
--- a/apidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.InputSplit.html
+++ b/apidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.InputSplit.html
@@ -376,7 +376,7 @@
<span class="source-line-no">363</span><span id="line-363"> continue;</span>
<span class="source-line-no">364</span><span id="line-364"> }</span>
<span class="source-line-no">365</span><span id="line-365"> // The mob region is a dummy region used only to organise mob files under mobdir. It has no</span>
-<span class="source-line-no">366</span><span id="line-366"> // region directory under the table dir to open, and holds no rows. See HBASE-30365.</span>
+<span class="source-line-no">366</span><span id="line-366"> // region directory under the table dir to open. See HBASE-30365.</span>
<span class="source-line-no">367</span><span id="line-367"> if (MobUtils.isMobRegionInfo(hri)) {</span>
<span class="source-line-no">368</span><span id="line-368"> continue;</span>
<span class="source-line-no">369</span><span id="line-369"> }</span>
diff --git a/apidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.RecordReader.html b/apidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.RecordReader.html
index 988b049b680..c9f4537ca7b 100644
--- a/apidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.RecordReader.html
+++ b/apidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.RecordReader.html
@@ -376,7 +376,7 @@
<span class="source-line-no">363</span><span id="line-363"> continue;</span>
<span class="source-line-no">364</span><span id="line-364"> }</span>
<span class="source-line-no">365</span><span id="line-365"> // The mob region is a dummy region used only to organise mob files under mobdir. It has no</span>
-<span class="source-line-no">366</span><span id="line-366"> // region directory under the table dir to open, and holds no rows. See HBASE-30365.</span>
+<span class="source-line-no">366</span><span id="line-366"> // region directory under the table dir to open. See HBASE-30365.</span>
<span class="source-line-no">367</span><span id="line-367"> if (MobUtils.isMobRegionInfo(hri)) {</span>
<span class="source-line-no">368</span><span id="line-368"> continue;</span>
<span class="source-line-no">369</span><span id="line-369"> }</span>
diff --git a/apidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.html b/apidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.html
index ac564cc4ba2..8edc449b184 100644
--- a/apidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.html
+++ b/apidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.html
@@ -376,7 +376,7 @@
<span class="source-line-no">363</span><span id="line-363"> continue;</span>
<span class="source-line-no">364</span><span id="line-364"> }</span>
<span class="source-line-no">365</span><span id="line-365"> // The mob region is a dummy region used only to organise mob files under mobdir. It has no</span>
-<span class="source-line-no">366</span><span id="line-366"> // region directory under the table dir to open, and holds no rows. See HBASE-30365.</span>
+<span class="source-line-no">366</span><span id="line-366"> // region directory under the table dir to open. See HBASE-30365.</span>
<span class="source-line-no">367</span><span id="line-367"> if (MobUtils.isMobRegionInfo(hri)) {</span>
<span class="source-line-no">368</span><span id="line-368"> continue;</span>
<span class="source-line-no">369</span><span id="line-369"> }</span>
diff --git a/books/apache-hbase-reference-guide-dark-mode.pdf b/books/apache-hbase-reference-guide-dark-mode.pdf
index ad23ae19731..b19a4236fd7 100644
Binary files a/books/apache-hbase-reference-guide-dark-mode.pdf and b/books/apache-hbase-reference-guide-dark-mode.pdf differ
diff --git a/books/apache-hbase-reference-guide.pdf b/books/apache-hbase-reference-guide.pdf
index 44408ef21f6..ef62e2b1940 100644
Binary files a/books/apache-hbase-reference-guide.pdf and b/books/apache-hbase-reference-guide.pdf differ
diff --git a/checkstyle-aggregate.html b/checkstyle-aggregate.html
index da33538c277..f3ac404e867 100644
--- a/checkstyle-aggregate.html
+++ b/checkstyle-aggregate.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-checkstyle-plugin:3.6.0:checkstyle-aggregate at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-checkstyle-plugin:3.6.0:checkstyle-aggregate at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/dependency-convergence.html b/dependency-convergence.html
index b079abe4c5a..2ae791aa053 100644
--- a/dependency-convergence.html
+++ b/dependency-convergence.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:dependency-convergence at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:dependency-convergence at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/dependency-info.html b/dependency-info.html
index b62fb46e634..57c7e53fd2e 100644
--- a/dependency-info.html
+++ b/dependency-info.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:dependency-info at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:dependency-info at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/dependency-management.html b/dependency-management.html
index 34582d3d809..a5ce0cd59cd 100644
--- a/dependency-management.html
+++ b/dependency-management.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:dependency-management at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:dependency-management at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/devapidocs/org/apache/hadoop/hbase/client/TableSnapshotScanner.html b/devapidocs/org/apache/hadoop/hbase/client/TableSnapshotScanner.html
index a4fb611424f..8fd5ed43bd1 100644
--- a/devapidocs/org/apache/hadoop/hbase/client/TableSnapshotScanner.html
+++ b/devapidocs/org/apache/hadoop/hbase/client/TableSnapshotScanner.html
@@ -84,7 +84,7 @@ loadScripts(document, 'script');</script>
</dl>
<hr>
<div class="type-signature"><span class="annotations">@Private
-</span><span class="modifiers">public class </span><span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-65">TableSnapshotScanner</a></span>
+</span><span class="modifiers">public class </span><span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-66">TableSnapshotScanner</a></span>
<span class="extends-implements">extends <a href="AbstractClientScanner.html" title="class in org.apache.hadoop.hbase.client">AbstractClientScanner</a></span></div>
<div class="block">A Scanner which performs a scan over snapshot files. Using this class requires copying the
snapshot to a temporary empty directory, which will copy the snapshot reference files into that
@@ -264,79 +264,79 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="LOG">
<h3>LOG</h3>
-<div class="member-signature"><span class="modifiers">private static final</span> <span class="return-type">org.slf4j.Logger</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-68">LOG</a></span></div>
+<div class="member-signature"><span class="modifiers">private static final</span> <span class="return-type">org.slf4j.Logger</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-69">LOG</a></span></div>
</section>
</li>
<li>
<section class="detail" id="conf">
<h3>conf</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.conf.Configuration</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-70">conf</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.conf.Configuration</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-71">conf</a></span></div>
</section>
</li>
<li>
<section class="detail" id="snapshotName">
<h3>snapshotName</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html" title="class or interface in java.lang" class="external-link">String</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-71">snapshotName</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html" title="class or interface in java.lang" class="external-link">String</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-72">snapshotName</a></span></div>
</section>
</li>
<li>
<section class="detail" id="fs">
<h3>fs</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.fs.FileSystem</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-72">fs</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.fs.FileSystem</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-73">fs</a></span></div>
</section>
</li>
<li>
<section class="detail" id="rootDir">
<h3>rootDir</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.fs.Path</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-73">rootDir</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.fs.Path</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-74">rootDir</a></span></div>
</section>
</li>
<li>
<section class="detail" id="restoreDir">
<h3>restoreDir</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.fs.Path</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-74">restoreDir</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.fs.Path</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-75">restoreDir</a></span></div>
</section>
</li>
<li>
<section class="detail" id="scan">
<h3>scan</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type"><a href="Scan.html" title="class in org.apache.hadoop.hbase.client">Scan</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-75">scan</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type"><a href="Scan.html" title="class in org.apache.hadoop.hbase.client">Scan</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-76">scan</a></span></div>
</section>
</li>
<li>
<section class="detail" id="regions">
<h3>regions</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/ArrayList.html" title="class or interface in java.util" class="external-link">ArrayList</a><<a href="RegionInfo.html" title="interface in org.apache.hadoop.hbase.client">RegionInfo</a>></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScan [...]
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/ArrayList.html" title="class or interface in java.util" class="external-link">ArrayList</a><<a href="RegionInfo.html" title="interface in org.apache.hadoop.hbase.client">RegionInfo</a>></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScan [...]
</section>
</li>
<li>
<section class="detail" id="htd">
<h3>htd</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type"><a href="TableDescriptor.html" title="interface in org.apache.hadoop.hbase.client">TableDescriptor</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-77">htd</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type"><a href="TableDescriptor.html" title="interface in org.apache.hadoop.hbase.client">TableDescriptor</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-78">htd</a></span></div>
</section>
</li>
<li>
<section class="detail" id="snapshotAlreadyRestored">
<h3>snapshotAlreadyRestored</h3>
-<div class="member-signature"><span class="modifiers">private final</span> <span class="return-type">boolean</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-78">snapshotAlreadyRestored</a></span></div>
+<div class="member-signature"><span class="modifiers">private final</span> <span class="return-type">boolean</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-79">snapshotAlreadyRestored</a></span></div>
</section>
</li>
<li>
<section class="detail" id="currentRegionScanner">
<h3>currentRegionScanner</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type"><a href="ClientSideRegionScanner.html" title="class in org.apache.hadoop.hbase.client">ClientSideRegionScanner</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-80">currentRegionScanner</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type"><a href="ClientSideRegionScanner.html" title="class in org.apache.hadoop.hbase.client">ClientSideRegionScanner</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-81">currentRegionScanner</a></span></div>
</section>
</li>
<li>
<section class="detail" id="currentRegion">
<h3>currentRegion</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">int</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-81">currentRegion</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">int</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-82">currentRegion</a></span></div>
</section>
</li>
<li>
<section class="detail" id="numOfCompleteRows">
<h3>numOfCompleteRows</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">int</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-83">numOfCompleteRows</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">int</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-84">numOfCompleteRows</a></span></div>
</section>
</li>
</ul>
@@ -350,7 +350,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="<init>(org.apache.hadoop.conf.Configuration,org.apache.hadoop.fs.Path,java.lang.String,org.apache.hadoop.hbase.client.Scan)">
<h3>TableSnapshotScanner</h3>
-<div class="member-signature"><span class="modifiers">public</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-96">TableSnapshotScanner</a></span><wbr><span class="parameters">(org.apache.hadoop.conf.Configuration conf,
+<div class="member-signature"><span class="modifiers">public</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-97">TableSnapshotScanner</a></span><wbr><span class="parameters">(org.apache.hadoop.conf.Configuration conf,
org.apache.hadoop.fs.Path restoreDir,
<a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html" title="class or interface in java.lang" class="external-link">String</a> snapshotName,
<a href="Scan.html" title="class in org.apache.hadoop.hbase.client">Scan</a> scan)</span>
@@ -373,7 +373,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="<init>(org.apache.hadoop.conf.Configuration,org.apache.hadoop.fs.Path,org.apache.hadoop.fs.Path,java.lang.String,org.apache.hadoop.hbase.client.Scan)">
<h3>TableSnapshotScanner</h3>
-<div class="member-signature"><span class="modifiers">public</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-101">TableSnapshotScanner</a></span><wbr><span class="parameters">(org.apache.hadoop.conf.Configuration conf,
+<div class="member-signature"><span class="modifiers">public</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-102">TableSnapshotScanner</a></span><wbr><span class="parameters">(org.apache.hadoop.conf.Configuration conf,
org.apache.hadoop.fs.Path rootDir,
org.apache.hadoop.fs.Path restoreDir,
<a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html" title="class or interface in java.lang" class="external-link">String</a> snapshotName,
@@ -388,7 +388,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="<init>(org.apache.hadoop.conf.Configuration,org.apache.hadoop.fs.Path,org.apache.hadoop.fs.Path,java.lang.String,org.apache.hadoop.hbase.client.Scan,boolean)">
<h3>TableSnapshotScanner</h3>
-<div class="member-signature"><span class="modifiers">public</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-119">TableSnapshotScanner</a></span><wbr><span class="parameters">(org.apache.hadoop.conf.Configuration conf,
+<div class="member-signature"><span class="modifiers">public</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-120">TableSnapshotScanner</a></span><wbr><span class="parameters">(org.apache.hadoop.conf.Configuration conf,
org.apache.hadoop.fs.Path rootDir,
org.apache.hadoop.fs.Path restoreDir,
<a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html" title="class or interface in java.lang" class="external-link">String</a> snapshotName,
@@ -423,7 +423,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="openWithoutRestoringSnapshot()">
<h3>openWithoutRestoringSnapshot</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-141">openWithoutRestoringSnapshot</a></span>()
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-142">openWithoutRestoringSnapshot</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/IOException.html" title="class or interface in java.io" class="external-link">IOException</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -434,13 +434,13 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="isValidRegion(org.apache.hadoop.hbase.client.RegionInfo)">
<h3>isValidRegion</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">boolean</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-158">isValidRegion</a></span><wbr><span class="parameters">(<a href="RegionInfo.html" title="interface in org.apache.hadoop.hbase.client">RegionInfo</a> hri)</span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">boolean</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-159">isValidRegion</a></span><wbr><span class="parameters">(<a href="RegionInfo.html" title="interface in org.apache.hadoop.hbase.client">RegionInfo</a> hri)</span></div>
</section>
</li>
<li>
<section class="detail" id="openWithRestoringSnapshot()">
<h3>openWithRestoringSnapshot</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-167">openWithRestoringSnapshot</a></span>()
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-173">openWithRestoringSnapshot</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/IOException.html" title="class or interface in java.io" class="external-link">IOException</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -451,7 +451,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="next()">
<h3>next</h3>
-<div class="member-signature"><span class="modifiers">public</span> <span class="return-type"><a href="Result.html" title="class in org.apache.hadoop.hbase.client">Result</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-177">next</a></span>()
+<div class="member-signature"><span class="modifiers">public</span> <span class="return-type"><a href="Result.html" title="class in org.apache.hadoop.hbase.client">Result</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-183">next</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/IOException.html" title="class or interface in java.io" class="external-link">IOException</a></span></div>
<div class="block"><span class="descfrm-type-label">Description copied from interface: <code><a href="ResultScanner.html#next()">ResultScanner</a></code></span></div>
<div class="block">Grab the next row's worth of values. The scanner will return a Result.</div>
@@ -466,13 +466,13 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="cleanup()">
<h3>cleanup</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-212">cleanup</a></span>()</div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-218">cleanup</a></span>()</div>
</section>
</li>
<li>
<section class="detail" id="close()">
<h3>close</h3>
-<div class="member-signature"><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-226">close</a></span>()</div>
+<div class="member-signature"><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-232">close</a></span>()</div>
<div class="block"><span class="descfrm-type-label">Description copied from interface: <code><a href="ResultScanner.html#close()">ResultScanner</a></code></span></div>
<div class="block">Closes the scanner and releases any resources it has allocated</div>
</section>
@@ -480,7 +480,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="renewLease()">
<h3>renewLease</h3>
-<div class="member-signature"><span class="modifiers">public</span> <span class="return-type">boolean</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-237">renewLease</a></span>()</div>
+<div class="member-signature"><span class="modifiers">public</span> <span class="return-type">boolean</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html#line-243">renewLease</a></span>()</div>
<div class="block"><span class="descfrm-type-label">Description copied from interface: <code><a href="ResultScanner.html#renewLease()">ResultScanner</a></code></span></div>
<div class="block">Allow the client to renew the scanner's lease on the server.</div>
<dl class="notes">
diff --git a/devapidocs/src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html b/devapidocs/src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html
index 9217df49239..5c7d1bfa0e1 100644
--- a/devapidocs/src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html
+++ b/devapidocs/src-html/org/apache/hadoop/hbase/client/TableSnapshotScanner.html
@@ -39,220 +39,226 @@
<span class="source-line-no">026</span><span id="line-26">import org.apache.hadoop.fs.Path;</span>
<span class="source-line-no">027</span><span id="line-27">import org.apache.hadoop.hbase.PrivateCellUtil;</span>
<span class="source-line-no">028</span><span id="line-28">import org.apache.hadoop.hbase.client.metrics.ScanMetrics;</span>
-<span class="source-line-no">029</span><span id="line-29">import org.apache.hadoop.hbase.regionserver.MemStoreLAB;</span>
-<span class="source-line-no">030</span><span id="line-30">import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;</span>
-<span class="source-line-no">031</span><span id="line-31">import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;</span>
-<span class="source-line-no">032</span><span id="line-32">import org.apache.hadoop.hbase.snapshot.SnapshotManifest;</span>
-<span class="source-line-no">033</span><span id="line-33">import org.apache.hadoop.hbase.util.CommonFSUtils;</span>
-<span class="source-line-no">034</span><span id="line-34">import org.apache.yetus.audience.InterfaceAudience;</span>
-<span class="source-line-no">035</span><span id="line-35">import org.slf4j.Logger;</span>
-<span class="source-line-no">036</span><span id="line-36">import org.slf4j.LoggerFactory;</span>
-<span class="source-line-no">037</span><span id="line-37"></span>
-<span class="source-line-no">038</span><span id="line-38">import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;</span>
-<span class="source-line-no">039</span><span id="line-39">import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;</span>
-<span class="source-line-no">040</span><span id="line-40">import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;</span>
-<span class="source-line-no">041</span><span id="line-41"></span>
-<span class="source-line-no">042</span><span id="line-42">/**</span>
-<span class="source-line-no">043</span><span id="line-43"> * A Scanner which performs a scan over snapshot files. Using this class requires copying the</span>
-<span class="source-line-no">044</span><span id="line-44"> * snapshot to a temporary empty directory, which will copy the snapshot reference files into that</span>
-<span class="source-line-no">045</span><span id="line-45"> * directory. Actual data files are not copied.</span>
-<span class="source-line-no">046</span><span id="line-46"> * <p></span>
-<span class="source-line-no">047</span><span id="line-47"> * This also allows one to run the scan from an online or offline hbase cluster. The snapshot files</span>
-<span class="source-line-no">048</span><span id="line-48"> * can be exported by using the org.apache.hadoop.hbase.snapshot.ExportSnapshot tool, to a pure-hdfs</span>
-<span class="source-line-no">049</span><span id="line-49"> * cluster, and this scanner can be used to run the scan directly over the snapshot files. The</span>
-<span class="source-line-no">050</span><span id="line-50"> * snapshot should not be deleted while there are open scanners reading from snapshot files.</span>
-<span class="source-line-no">051</span><span id="line-51"> * <p></span>
-<span class="source-line-no">052</span><span id="line-52"> * An internal RegionScanner is used to execute the {@link Scan} obtained from the user for each</span>
-<span class="source-line-no">053</span><span id="line-53"> * region in the snapshot.</span>
-<span class="source-line-no">054</span><span id="line-54"> * <p></span>
-<span class="source-line-no">055</span><span id="line-55"> * HBase owns all the data and snapshot files on the filesystem. Only the HBase user can read from</span>
-<span class="source-line-no">056</span><span id="line-56"> * snapshot files and data files. HBase also enforces security because all the requests are handled</span>
-<span class="source-line-no">057</span><span id="line-57"> * by the server layer, and the user cannot read from the data files directly. To read from snapshot</span>
-<span class="source-line-no">058</span><span id="line-58"> * files directly from the file system, the user who is running the MR job must have sufficient</span>
-<span class="source-line-no">059</span><span id="line-59"> * permissions to access snapshot and reference files. This means that to run mapreduce over</span>
-<span class="source-line-no">060</span><span id="line-60"> * snapshot files, the job has to be run as the HBase user or the user must have group or other</span>
-<span class="source-line-no">061</span><span id="line-61"> * priviledges in the filesystem (See HBASE-8369). Note that, given other users access to read from</span>
-<span class="source-line-no">062</span><span id="line-62"> * snapshot/data files will completely circumvent the access control enforced by HBase. See</span>
-<span class="source-line-no">063</span><span id="line-63"> * org.apache.hadoop.hbase.mapreduce.TableSnapshotInputFormat.</span>
-<span class="source-line-no">064</span><span id="line-64"> */</span>
-<span class="source-line-no">065</span><span id="line-65">@InterfaceAudience.Private</span>
-<span class="source-line-no">066</span><span id="line-66">public class TableSnapshotScanner extends AbstractClientScanner {</span>
-<span class="source-line-no">067</span><span id="line-67"></span>
-<span class="source-line-no">068</span><span id="line-68"> private static final Logger LOG = LoggerFactory.getLogger(TableSnapshotScanner.class);</span>
-<span class="source-line-no">069</span><span id="line-69"></span>
-<span class="source-line-no">070</span><span id="line-70"> private Configuration conf;</span>
-<span class="source-line-no">071</span><span id="line-71"> private String snapshotName;</span>
-<span class="source-line-no">072</span><span id="line-72"> private FileSystem fs;</span>
-<span class="source-line-no">073</span><span id="line-73"> private Path rootDir;</span>
-<span class="source-line-no">074</span><span id="line-74"> private Path restoreDir;</span>
-<span class="source-line-no">075</span><span id="line-75"> private Scan scan;</span>
-<span class="source-line-no">076</span><span id="line-76"> private ArrayList<RegionInfo> regions;</span>
-<span class="source-line-no">077</span><span id="line-77"> private TableDescriptor htd;</span>
-<span class="source-line-no">078</span><span id="line-78"> private final boolean snapshotAlreadyRestored;</span>
-<span class="source-line-no">079</span><span id="line-79"></span>
-<span class="source-line-no">080</span><span id="line-80"> private ClientSideRegionScanner currentRegionScanner = null;</span>
-<span class="source-line-no">081</span><span id="line-81"> private int currentRegion = -1;</span>
-<span class="source-line-no">082</span><span id="line-82"></span>
-<span class="source-line-no">083</span><span id="line-83"> private int numOfCompleteRows = 0;</span>
-<span class="source-line-no">084</span><span id="line-84"></span>
-<span class="source-line-no">085</span><span id="line-85"> /**</span>
-<span class="source-line-no">086</span><span id="line-86"> * Creates a TableSnapshotScanner.</span>
-<span class="source-line-no">087</span><span id="line-87"> * @param conf the configuration</span>
-<span class="source-line-no">088</span><span id="line-88"> * @param restoreDir a temporary directory to copy the snapshot files into. Current user should</span>
-<span class="source-line-no">089</span><span id="line-89"> * have write permissions to this directory, and this should not be a</span>
-<span class="source-line-no">090</span><span id="line-90"> * subdirectory of rootDir. The scanner deletes the contents of the directory</span>
-<span class="source-line-no">091</span><span id="line-91"> * once the scanner is closed.</span>
-<span class="source-line-no">092</span><span id="line-92"> * @param snapshotName the name of the snapshot to read from</span>
-<span class="source-line-no">093</span><span id="line-93"> * @param scan a Scan representing scan parameters</span>
-<span class="source-line-no">094</span><span id="line-94"> * @throws IOException in case of error</span>
-<span class="source-line-no">095</span><span id="line-95"> */</span>
-<span class="source-line-no">096</span><span id="line-96"> public TableSnapshotScanner(Configuration conf, Path restoreDir, String snapshotName, Scan scan)</span>
-<span class="source-line-no">097</span><span id="line-97"> throws IOException {</span>
-<span class="source-line-no">098</span><span id="line-98"> this(conf, CommonFSUtils.getRootDir(conf), restoreDir, snapshotName, scan);</span>
-<span class="source-line-no">099</span><span id="line-99"> }</span>
-<span class="source-line-no">100</span><span id="line-100"></span>
-<span class="source-line-no">101</span><span id="line-101"> public TableSnapshotScanner(Configuration conf, Path rootDir, Path restoreDir,</span>
-<span class="source-line-no">102</span><span id="line-102"> String snapshotName, Scan scan) throws IOException {</span>
-<span class="source-line-no">103</span><span id="line-103"> this(conf, rootDir, restoreDir, snapshotName, scan, false);</span>
-<span class="source-line-no">104</span><span id="line-104"> }</span>
-<span class="source-line-no">105</span><span id="line-105"></span>
-<span class="source-line-no">106</span><span id="line-106"> /**</span>
-<span class="source-line-no">107</span><span id="line-107"> * Creates a TableSnapshotScanner.</span>
-<span class="source-line-no">108</span><span id="line-108"> * @param conf the configuration</span>
-<span class="source-line-no">109</span><span id="line-109"> * @param rootDir root directory for HBase.</span>
-<span class="source-line-no">110</span><span id="line-110"> * @param restoreDir a temporary directory to copy the snapshot files into. Current</span>
-<span class="source-line-no">111</span><span id="line-111"> * user should have write permissions to this directory, and this</span>
-<span class="source-line-no">112</span><span id="line-112"> * should not be a subdirectory of rootdir. The scanner deletes the</span>
-<span class="source-line-no">113</span><span id="line-113"> * contents of the directory once the scanner is closed.</span>
-<span class="source-line-no">114</span><span id="line-114"> * @param snapshotName the name of the snapshot to read from</span>
-<span class="source-line-no">115</span><span id="line-115"> * @param scan a Scan representing scan parameters</span>
-<span class="source-line-no">116</span><span id="line-116"> * @param snapshotAlreadyRestored true to indicate that snapshot has been restored.</span>
-<span class="source-line-no">117</span><span id="line-117"> * @throws IOException in case of error</span>
-<span class="source-line-no">118</span><span id="line-118"> */</span>
-<span class="source-line-no">119</span><span id="line-119"> public TableSnapshotScanner(Configuration conf, Path rootDir, Path restoreDir,</span>
-<span class="source-line-no">120</span><span id="line-120"> String snapshotName, Scan scan, boolean snapshotAlreadyRestored) throws IOException {</span>
-<span class="source-line-no">121</span><span id="line-121"> this.conf = conf;</span>
-<span class="source-line-no">122</span><span id="line-122"> this.snapshotName = snapshotName;</span>
-<span class="source-line-no">123</span><span id="line-123"> this.rootDir = rootDir;</span>
-<span class="source-line-no">124</span><span id="line-124"> this.scan = scan;</span>
-<span class="source-line-no">125</span><span id="line-125"> this.snapshotAlreadyRestored = snapshotAlreadyRestored;</span>
-<span class="source-line-no">126</span><span id="line-126"> this.fs = rootDir.getFileSystem(conf);</span>
-<span class="source-line-no">127</span><span id="line-127"> conf.setBoolean(MemStoreLAB.USEMSLAB_KEY, false);</span>
-<span class="source-line-no">128</span><span id="line-128"></span>
-<span class="source-line-no">129</span><span id="line-129"> if (snapshotAlreadyRestored) {</span>
-<span class="source-line-no">130</span><span id="line-130"> this.restoreDir = restoreDir;</span>
-<span class="source-line-no">131</span><span id="line-131"> openWithoutRestoringSnapshot();</span>
-<span class="source-line-no">132</span><span id="line-132"> } else {</span>
-<span class="source-line-no">133</span><span id="line-133"> // restoreDir will be deleted in close(), use a unique sub directory</span>
-<span class="source-line-no">134</span><span id="line-134"> this.restoreDir = new Path(restoreDir, UUID.randomUUID().toString());</span>
-<span class="source-line-no">135</span><span id="line-135"> openWithRestoringSnapshot();</span>
-<span class="source-line-no">136</span><span id="line-136"> }</span>
-<span class="source-line-no">137</span><span id="line-137"></span>
-<span class="source-line-no">138</span><span id="line-138"> initScanMetrics(scan);</span>
-<span class="source-line-no">139</span><span id="line-139"> }</span>
-<span class="source-line-no">140</span><span id="line-140"></span>
-<span class="source-line-no">141</span><span id="line-141"> private void openWithoutRestoringSnapshot() throws IOException {</span>
-<span class="source-line-no">142</span><span id="line-142"> Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);</span>
-<span class="source-line-no">143</span><span id="line-143"> SnapshotProtos.SnapshotDescription snapshotDesc =</span>
-<span class="source-line-no">144</span><span id="line-144"> SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);</span>
-<span class="source-line-no">145</span><span id="line-145"></span>
-<span class="source-line-no">146</span><span id="line-146"> SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshotDesc);</span>
-<span class="source-line-no">147</span><span id="line-147"> List<SnapshotRegionManifest> regionManifests = manifest.getRegionManifests();</span>
-<span class="source-line-no">148</span><span id="line-148"> if (regionManifests == null) {</span>
-<span class="source-line-no">149</span><span id="line-149"> throw new IllegalArgumentException("Snapshot seems empty, snapshotName: " + snapshotName);</span>
-<span class="source-line-no">150</span><span id="line-150"> }</span>
-<span class="source-line-no">151</span><span id="line-151"></span>
-<span class="source-line-no">152</span><span id="line-152"> regions = new ArrayList<>(regionManifests.size());</span>
-<span class="source-line-no">153</span><span id="line-153"> regionManifests.stream().map(r -> ProtobufUtil.toRegionInfo(r.getRegionInfo()))</span>
-<span class="source-line-no">154</span><span id="line-154"> .filter(this::isValidRegion).sorted().forEach(r -> regions.add(r));</span>
-<span class="source-line-no">155</span><span id="line-155"> htd = manifest.getTableDescriptor();</span>
-<span class="source-line-no">156</span><span id="line-156"> }</span>
-<span class="source-line-no">157</span><span id="line-157"></span>
-<span class="source-line-no">158</span><span id="line-158"> private boolean isValidRegion(RegionInfo hri) {</span>
-<span class="source-line-no">159</span><span id="line-159"> // An offline split parent region should be excluded.</span>
-<span class="source-line-no">160</span><span id="line-160"> if (hri.isOffline() && (hri.isSplit() || hri.isSplitParent())) {</span>
-<span class="source-line-no">161</span><span id="line-161"> return false;</span>
-<span class="source-line-no">162</span><span id="line-162"> }</span>
-<span class="source-line-no">163</span><span id="line-163"> return PrivateCellUtil.overlappingKeys(scan.getStartRow(), scan.getStopRow(), hri.getStartKey(),</span>
-<span class="source-line-no">164</span><span id="line-164"> hri.getEndKey());</span>
-<span class="source-line-no">165</span><span id="line-165"> }</span>
-<span class="source-line-no">166</span><span id="line-166"></span>
-<span class="source-line-no">167</span><span id="line-167"> private void openWithRestoringSnapshot() throws IOException {</span>
-<span class="source-line-no">168</span><span id="line-168"> final RestoreSnapshotHelper.RestoreMetaChanges meta =</span>
-<span class="source-line-no">169</span><span id="line-169"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
-<span class="source-line-no">170</span><span id="line-170"> final List<RegionInfo> restoredRegions = meta.getRegionsToAdd();</span>
-<span class="source-line-no">171</span><span id="line-171"></span>
-<span class="source-line-no">172</span><span id="line-172"> htd = meta.getTableDescriptor();</span>
-<span class="source-line-no">173</span><span id="line-173"> regions = new ArrayList<>(restoredRegions.size());</span>
-<span class="source-line-no">174</span><span id="line-174"> restoredRegions.stream().filter(this::isValidRegion).sorted().forEach(r -> regions.add(r));</span>
-<span class="source-line-no">175</span><span id="line-175"> }</span>
-<span class="source-line-no">176</span><span id="line-176"></span>
-<span class="source-line-no">177</span><span id="line-177"> @Override</span>
-<span class="source-line-no">178</span><span id="line-178"> public Result next() throws IOException {</span>
-<span class="source-line-no">179</span><span id="line-179"> Result result = null;</span>
-<span class="source-line-no">180</span><span id="line-180"> while (true) {</span>
-<span class="source-line-no">181</span><span id="line-181"> if (currentRegionScanner == null) {</span>
-<span class="source-line-no">182</span><span id="line-182"> currentRegion++;</span>
-<span class="source-line-no">183</span><span id="line-183"> if (currentRegion >= regions.size()) {</span>
-<span class="source-line-no">184</span><span id="line-184"> return null;</span>
-<span class="source-line-no">185</span><span id="line-185"> }</span>
-<span class="source-line-no">186</span><span id="line-186"></span>
-<span class="source-line-no">187</span><span id="line-187"> RegionInfo hri = regions.get(currentRegion);</span>
-<span class="source-line-no">188</span><span id="line-188"> currentRegionScanner =</span>
-<span class="source-line-no">189</span><span id="line-189"> new ClientSideRegionScanner(conf, fs, restoreDir, htd, hri, scan, scanMetrics);</span>
-<span class="source-line-no">190</span><span id="line-190"> if (this.scanMetrics != null) {</span>
-<span class="source-line-no">191</span><span id="line-191"> this.scanMetrics.addToCounter(ScanMetrics.REGIONS_SCANNED_METRIC_NAME, 1);</span>
-<span class="source-line-no">192</span><span id="line-192"> }</span>
-<span class="source-line-no">193</span><span id="line-193"> }</span>
-<span class="source-line-no">194</span><span id="line-194"></span>
-<span class="source-line-no">195</span><span id="line-195"> try {</span>
-<span class="source-line-no">196</span><span id="line-196"> result = currentRegionScanner.next();</span>
-<span class="source-line-no">197</span><span id="line-197"> if (result != null) {</span>
-<span class="source-line-no">198</span><span id="line-198"> if (scan.getLimit() > 0 && ++this.numOfCompleteRows > scan.getLimit()) {</span>
-<span class="source-line-no">199</span><span id="line-199"> result = null;</span>
-<span class="source-line-no">200</span><span id="line-200"> }</span>
-<span class="source-line-no">201</span><span id="line-201"> return result;</span>
-<span class="source-line-no">202</span><span id="line-202"> }</span>
-<span class="source-line-no">203</span><span id="line-203"> } finally {</span>
-<span class="source-line-no">204</span><span id="line-204"> if (result == null) {</span>
-<span class="source-line-no">205</span><span id="line-205"> currentRegionScanner.close();</span>
-<span class="source-line-no">206</span><span id="line-206"> currentRegionScanner = null;</span>
-<span class="source-line-no">207</span><span id="line-207"> }</span>
-<span class="source-line-no">208</span><span id="line-208"> }</span>
-<span class="source-line-no">209</span><span id="line-209"> }</span>
-<span class="source-line-no">210</span><span id="line-210"> }</span>
-<span class="source-line-no">211</span><span id="line-211"></span>
-<span class="source-line-no">212</span><span id="line-212"> private void cleanup() {</span>
-<span class="source-line-no">213</span><span id="line-213"> try {</span>
-<span class="source-line-no">214</span><span id="line-214"> if (fs.exists(this.restoreDir)) {</span>
-<span class="source-line-no">215</span><span id="line-215"> if (!fs.delete(this.restoreDir, true)) {</span>
-<span class="source-line-no">216</span><span id="line-216"> LOG.warn(</span>
-<span class="source-line-no">217</span><span id="line-217"> "Delete restore directory for the snapshot failed. restoreDir: " + this.restoreDir);</span>
-<span class="source-line-no">218</span><span id="line-218"> }</span>
-<span class="source-line-no">219</span><span id="line-219"> }</span>
-<span class="source-line-no">220</span><span id="line-220"> } catch (IOException ex) {</span>
-<span class="source-line-no">221</span><span id="line-221"> LOG.warn(</span>
-<span class="source-line-no">222</span><span id="line-222"> "Could not delete restore directory for the snapshot. restoreDir: " + this.restoreDir, ex);</span>
-<span class="source-line-no">223</span><span id="line-223"> }</span>
-<span class="source-line-no">224</span><span id="line-224"> }</span>
-<span class="source-line-no">225</span><span id="line-225"></span>
-<span class="source-line-no">226</span><span id="line-226"> @Override</span>
-<span class="source-line-no">227</span><span id="line-227"> public void close() {</span>
-<span class="source-line-no">228</span><span id="line-228"> if (currentRegionScanner != null) {</span>
-<span class="source-line-no">229</span><span id="line-229"> currentRegionScanner.close();</span>
-<span class="source-line-no">230</span><span id="line-230"> }</span>
-<span class="source-line-no">231</span><span id="line-231"> // if snapshotAlreadyRestored is true, then we should invoke cleanup() method by hand.</span>
-<span class="source-line-no">232</span><span id="line-232"> if (!this.snapshotAlreadyRestored) {</span>
-<span class="source-line-no">233</span><span id="line-233"> cleanup();</span>
-<span class="source-line-no">234</span><span id="line-234"> }</span>
-<span class="source-line-no">235</span><span id="line-235"> }</span>
-<span class="source-line-no">236</span><span id="line-236"></span>
-<span class="source-line-no">237</span><span id="line-237"> @Override</span>
-<span class="source-line-no">238</span><span id="line-238"> public boolean renewLease() {</span>
-<span class="source-line-no">239</span><span id="line-239"> throw new UnsupportedOperationException();</span>
-<span class="source-line-no">240</span><span id="line-240"> }</span>
-<span class="source-line-no">241</span><span id="line-241"></span>
-<span class="source-line-no">242</span><span id="line-242">}</span>
+<span class="source-line-no">029</span><span id="line-29">import org.apache.hadoop.hbase.mob.MobUtils;</span>
+<span class="source-line-no">030</span><span id="line-30">import org.apache.hadoop.hbase.regionserver.MemStoreLAB;</span>
+<span class="source-line-no">031</span><span id="line-31">import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;</span>
+<span class="source-line-no">032</span><span id="line-32">import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;</span>
+<span class="source-line-no">033</span><span id="line-33">import org.apache.hadoop.hbase.snapshot.SnapshotManifest;</span>
+<span class="source-line-no">034</span><span id="line-34">import org.apache.hadoop.hbase.util.CommonFSUtils;</span>
+<span class="source-line-no">035</span><span id="line-35">import org.apache.yetus.audience.InterfaceAudience;</span>
+<span class="source-line-no">036</span><span id="line-36">import org.slf4j.Logger;</span>
+<span class="source-line-no">037</span><span id="line-37">import org.slf4j.LoggerFactory;</span>
+<span class="source-line-no">038</span><span id="line-38"></span>
+<span class="source-line-no">039</span><span id="line-39">import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;</span>
+<span class="source-line-no">040</span><span id="line-40">import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;</span>
+<span class="source-line-no">041</span><span id="line-41">import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;</span>
+<span class="source-line-no">042</span><span id="line-42"></span>
+<span class="source-line-no">043</span><span id="line-43">/**</span>
+<span class="source-line-no">044</span><span id="line-44"> * A Scanner which performs a scan over snapshot files. Using this class requires copying the</span>
+<span class="source-line-no">045</span><span id="line-45"> * snapshot to a temporary empty directory, which will copy the snapshot reference files into that</span>
+<span class="source-line-no">046</span><span id="line-46"> * directory. Actual data files are not copied.</span>
+<span class="source-line-no">047</span><span id="line-47"> * <p></span>
+<span class="source-line-no">048</span><span id="line-48"> * This also allows one to run the scan from an online or offline hbase cluster. The snapshot files</span>
+<span class="source-line-no">049</span><span id="line-49"> * can be exported by using the org.apache.hadoop.hbase.snapshot.ExportSnapshot tool, to a pure-hdfs</span>
+<span class="source-line-no">050</span><span id="line-50"> * cluster, and this scanner can be used to run the scan directly over the snapshot files. The</span>
+<span class="source-line-no">051</span><span id="line-51"> * snapshot should not be deleted while there are open scanners reading from snapshot files.</span>
+<span class="source-line-no">052</span><span id="line-52"> * <p></span>
+<span class="source-line-no">053</span><span id="line-53"> * An internal RegionScanner is used to execute the {@link Scan} obtained from the user for each</span>
+<span class="source-line-no">054</span><span id="line-54"> * region in the snapshot.</span>
+<span class="source-line-no">055</span><span id="line-55"> * <p></span>
+<span class="source-line-no">056</span><span id="line-56"> * HBase owns all the data and snapshot files on the filesystem. Only the HBase user can read from</span>
+<span class="source-line-no">057</span><span id="line-57"> * snapshot files and data files. HBase also enforces security because all the requests are handled</span>
+<span class="source-line-no">058</span><span id="line-58"> * by the server layer, and the user cannot read from the data files directly. To read from snapshot</span>
+<span class="source-line-no">059</span><span id="line-59"> * files directly from the file system, the user who is running the MR job must have sufficient</span>
+<span class="source-line-no">060</span><span id="line-60"> * permissions to access snapshot and reference files. This means that to run mapreduce over</span>
+<span class="source-line-no">061</span><span id="line-61"> * snapshot files, the job has to be run as the HBase user or the user must have group or other</span>
+<span class="source-line-no">062</span><span id="line-62"> * priviledges in the filesystem (See HBASE-8369). Note that, given other users access to read from</span>
+<span class="source-line-no">063</span><span id="line-63"> * snapshot/data files will completely circumvent the access control enforced by HBase. See</span>
+<span class="source-line-no">064</span><span id="line-64"> * org.apache.hadoop.hbase.mapreduce.TableSnapshotInputFormat.</span>
+<span class="source-line-no">065</span><span id="line-65"> */</span>
+<span class="source-line-no">066</span><span id="line-66">@InterfaceAudience.Private</span>
+<span class="source-line-no">067</span><span id="line-67">public class TableSnapshotScanner extends AbstractClientScanner {</span>
+<span class="source-line-no">068</span><span id="line-68"></span>
+<span class="source-line-no">069</span><span id="line-69"> private static final Logger LOG = LoggerFactory.getLogger(TableSnapshotScanner.class);</span>
+<span class="source-line-no">070</span><span id="line-70"></span>
+<span class="source-line-no">071</span><span id="line-71"> private Configuration conf;</span>
+<span class="source-line-no">072</span><span id="line-72"> private String snapshotName;</span>
+<span class="source-line-no">073</span><span id="line-73"> private FileSystem fs;</span>
+<span class="source-line-no">074</span><span id="line-74"> private Path rootDir;</span>
+<span class="source-line-no">075</span><span id="line-75"> private Path restoreDir;</span>
+<span class="source-line-no">076</span><span id="line-76"> private Scan scan;</span>
+<span class="source-line-no">077</span><span id="line-77"> private ArrayList<RegionInfo> regions;</span>
+<span class="source-line-no">078</span><span id="line-78"> private TableDescriptor htd;</span>
+<span class="source-line-no">079</span><span id="line-79"> private final boolean snapshotAlreadyRestored;</span>
+<span class="source-line-no">080</span><span id="line-80"></span>
+<span class="source-line-no">081</span><span id="line-81"> private ClientSideRegionScanner currentRegionScanner = null;</span>
+<span class="source-line-no">082</span><span id="line-82"> private int currentRegion = -1;</span>
+<span class="source-line-no">083</span><span id="line-83"></span>
+<span class="source-line-no">084</span><span id="line-84"> private int numOfCompleteRows = 0;</span>
+<span class="source-line-no">085</span><span id="line-85"></span>
+<span class="source-line-no">086</span><span id="line-86"> /**</span>
+<span class="source-line-no">087</span><span id="line-87"> * Creates a TableSnapshotScanner.</span>
+<span class="source-line-no">088</span><span id="line-88"> * @param conf the configuration</span>
+<span class="source-line-no">089</span><span id="line-89"> * @param restoreDir a temporary directory to copy the snapshot files into. Current user should</span>
+<span class="source-line-no">090</span><span id="line-90"> * have write permissions to this directory, and this should not be a</span>
+<span class="source-line-no">091</span><span id="line-91"> * subdirectory of rootDir. The scanner deletes the contents of the directory</span>
+<span class="source-line-no">092</span><span id="line-92"> * once the scanner is closed.</span>
+<span class="source-line-no">093</span><span id="line-93"> * @param snapshotName the name of the snapshot to read from</span>
+<span class="source-line-no">094</span><span id="line-94"> * @param scan a Scan representing scan parameters</span>
+<span class="source-line-no">095</span><span id="line-95"> * @throws IOException in case of error</span>
+<span class="source-line-no">096</span><span id="line-96"> */</span>
+<span class="source-line-no">097</span><span id="line-97"> public TableSnapshotScanner(Configuration conf, Path restoreDir, String snapshotName, Scan scan)</span>
+<span class="source-line-no">098</span><span id="line-98"> throws IOException {</span>
+<span class="source-line-no">099</span><span id="line-99"> this(conf, CommonFSUtils.getRootDir(conf), restoreDir, snapshotName, scan);</span>
+<span class="source-line-no">100</span><span id="line-100"> }</span>
+<span class="source-line-no">101</span><span id="line-101"></span>
+<span class="source-line-no">102</span><span id="line-102"> public TableSnapshotScanner(Configuration conf, Path rootDir, Path restoreDir,</span>
+<span class="source-line-no">103</span><span id="line-103"> String snapshotName, Scan scan) throws IOException {</span>
+<span class="source-line-no">104</span><span id="line-104"> this(conf, rootDir, restoreDir, snapshotName, scan, false);</span>
+<span class="source-line-no">105</span><span id="line-105"> }</span>
+<span class="source-line-no">106</span><span id="line-106"></span>
+<span class="source-line-no">107</span><span id="line-107"> /**</span>
+<span class="source-line-no">108</span><span id="line-108"> * Creates a TableSnapshotScanner.</span>
+<span class="source-line-no">109</span><span id="line-109"> * @param conf the configuration</span>
+<span class="source-line-no">110</span><span id="line-110"> * @param rootDir root directory for HBase.</span>
+<span class="source-line-no">111</span><span id="line-111"> * @param restoreDir a temporary directory to copy the snapshot files into. Current</span>
+<span class="source-line-no">112</span><span id="line-112"> * user should have write permissions to this directory, and this</span>
+<span class="source-line-no">113</span><span id="line-113"> * should not be a subdirectory of rootdir. The scanner deletes the</span>
+<span class="source-line-no">114</span><span id="line-114"> * contents of the directory once the scanner is closed.</span>
+<span class="source-line-no">115</span><span id="line-115"> * @param snapshotName the name of the snapshot to read from</span>
+<span class="source-line-no">116</span><span id="line-116"> * @param scan a Scan representing scan parameters</span>
+<span class="source-line-no">117</span><span id="line-117"> * @param snapshotAlreadyRestored true to indicate that snapshot has been restored.</span>
+<span class="source-line-no">118</span><span id="line-118"> * @throws IOException in case of error</span>
+<span class="source-line-no">119</span><span id="line-119"> */</span>
+<span class="source-line-no">120</span><span id="line-120"> public TableSnapshotScanner(Configuration conf, Path rootDir, Path restoreDir,</span>
+<span class="source-line-no">121</span><span id="line-121"> String snapshotName, Scan scan, boolean snapshotAlreadyRestored) throws IOException {</span>
+<span class="source-line-no">122</span><span id="line-122"> this.conf = conf;</span>
+<span class="source-line-no">123</span><span id="line-123"> this.snapshotName = snapshotName;</span>
+<span class="source-line-no">124</span><span id="line-124"> this.rootDir = rootDir;</span>
+<span class="source-line-no">125</span><span id="line-125"> this.scan = scan;</span>
+<span class="source-line-no">126</span><span id="line-126"> this.snapshotAlreadyRestored = snapshotAlreadyRestored;</span>
+<span class="source-line-no">127</span><span id="line-127"> this.fs = rootDir.getFileSystem(conf);</span>
+<span class="source-line-no">128</span><span id="line-128"> conf.setBoolean(MemStoreLAB.USEMSLAB_KEY, false);</span>
+<span class="source-line-no">129</span><span id="line-129"></span>
+<span class="source-line-no">130</span><span id="line-130"> if (snapshotAlreadyRestored) {</span>
+<span class="source-line-no">131</span><span id="line-131"> this.restoreDir = restoreDir;</span>
+<span class="source-line-no">132</span><span id="line-132"> openWithoutRestoringSnapshot();</span>
+<span class="source-line-no">133</span><span id="line-133"> } else {</span>
+<span class="source-line-no">134</span><span id="line-134"> // restoreDir will be deleted in close(), use a unique sub directory</span>
+<span class="source-line-no">135</span><span id="line-135"> this.restoreDir = new Path(restoreDir, UUID.randomUUID().toString());</span>
+<span class="source-line-no">136</span><span id="line-136"> openWithRestoringSnapshot();</span>
+<span class="source-line-no">137</span><span id="line-137"> }</span>
+<span class="source-line-no">138</span><span id="line-138"></span>
+<span class="source-line-no">139</span><span id="line-139"> initScanMetrics(scan);</span>
+<span class="source-line-no">140</span><span id="line-140"> }</span>
+<span class="source-line-no">141</span><span id="line-141"></span>
+<span class="source-line-no">142</span><span id="line-142"> private void openWithoutRestoringSnapshot() throws IOException {</span>
+<span class="source-line-no">143</span><span id="line-143"> Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);</span>
+<span class="source-line-no">144</span><span id="line-144"> SnapshotProtos.SnapshotDescription snapshotDesc =</span>
+<span class="source-line-no">145</span><span id="line-145"> SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);</span>
+<span class="source-line-no">146</span><span id="line-146"></span>
+<span class="source-line-no">147</span><span id="line-147"> SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshotDesc);</span>
+<span class="source-line-no">148</span><span id="line-148"> List<SnapshotRegionManifest> regionManifests = manifest.getRegionManifests();</span>
+<span class="source-line-no">149</span><span id="line-149"> if (regionManifests == null) {</span>
+<span class="source-line-no">150</span><span id="line-150"> throw new IllegalArgumentException("Snapshot seems empty, snapshotName: " + snapshotName);</span>
+<span class="source-line-no">151</span><span id="line-151"> }</span>
+<span class="source-line-no">152</span><span id="line-152"></span>
+<span class="source-line-no">153</span><span id="line-153"> regions = new ArrayList<>(regionManifests.size());</span>
+<span class="source-line-no">154</span><span id="line-154"> regionManifests.stream().map(r -> ProtobufUtil.toRegionInfo(r.getRegionInfo()))</span>
+<span class="source-line-no">155</span><span id="line-155"> .filter(this::isValidRegion).sorted().forEach(r -> regions.add(r));</span>
+<span class="source-line-no">156</span><span id="line-156"> htd = manifest.getTableDescriptor();</span>
+<span class="source-line-no">157</span><span id="line-157"> }</span>
+<span class="source-line-no">158</span><span id="line-158"></span>
+<span class="source-line-no">159</span><span id="line-159"> private boolean isValidRegion(RegionInfo hri) {</span>
+<span class="source-line-no">160</span><span id="line-160"> // An offline split parent region should be excluded.</span>
+<span class="source-line-no">161</span><span id="line-161"> if (hri.isOffline() && (hri.isSplit() || hri.isSplitParent())) {</span>
+<span class="source-line-no">162</span><span id="line-162"> return false;</span>
+<span class="source-line-no">163</span><span id="line-163"> }</span>
+<span class="source-line-no">164</span><span id="line-164"> // The mob region is a dummy region used only to organise mob files under mobdir. It has no</span>
+<span class="source-line-no">165</span><span id="line-165"> // region directory under the table dir to open. See HBASE-30365 and HBASE-30368.</span>
+<span class="source-line-no">166</span><span id="line-166"> if (MobUtils.isMobRegionInfo(hri)) {</span>
+<span class="source-line-no">167</span><span id="line-167"> return false;</span>
+<span class="source-line-no">168</span><span id="line-168"> }</span>
+<span class="source-line-no">169</span><span id="line-169"> return PrivateCellUtil.overlappingKeys(scan.getStartRow(), scan.getStopRow(), hri.getStartKey(),</span>
+<span class="source-line-no">170</span><span id="line-170"> hri.getEndKey());</span>
+<span class="source-line-no">171</span><span id="line-171"> }</span>
+<span class="source-line-no">172</span><span id="line-172"></span>
+<span class="source-line-no">173</span><span id="line-173"> private void openWithRestoringSnapshot() throws IOException {</span>
+<span class="source-line-no">174</span><span id="line-174"> final RestoreSnapshotHelper.RestoreMetaChanges meta =</span>
+<span class="source-line-no">175</span><span id="line-175"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
+<span class="source-line-no">176</span><span id="line-176"> final List<RegionInfo> restoredRegions = meta.getRegionsToAdd();</span>
+<span class="source-line-no">177</span><span id="line-177"></span>
+<span class="source-line-no">178</span><span id="line-178"> htd = meta.getTableDescriptor();</span>
+<span class="source-line-no">179</span><span id="line-179"> regions = new ArrayList<>(restoredRegions.size());</span>
+<span class="source-line-no">180</span><span id="line-180"> restoredRegions.stream().filter(this::isValidRegion).sorted().forEach(r -> regions.add(r));</span>
+<span class="source-line-no">181</span><span id="line-181"> }</span>
+<span class="source-line-no">182</span><span id="line-182"></span>
+<span class="source-line-no">183</span><span id="line-183"> @Override</span>
+<span class="source-line-no">184</span><span id="line-184"> public Result next() throws IOException {</span>
+<span class="source-line-no">185</span><span id="line-185"> Result result = null;</span>
+<span class="source-line-no">186</span><span id="line-186"> while (true) {</span>
+<span class="source-line-no">187</span><span id="line-187"> if (currentRegionScanner == null) {</span>
+<span class="source-line-no">188</span><span id="line-188"> currentRegion++;</span>
+<span class="source-line-no">189</span><span id="line-189"> if (currentRegion >= regions.size()) {</span>
+<span class="source-line-no">190</span><span id="line-190"> return null;</span>
+<span class="source-line-no">191</span><span id="line-191"> }</span>
+<span class="source-line-no">192</span><span id="line-192"></span>
+<span class="source-line-no">193</span><span id="line-193"> RegionInfo hri = regions.get(currentRegion);</span>
+<span class="source-line-no">194</span><span id="line-194"> currentRegionScanner =</span>
+<span class="source-line-no">195</span><span id="line-195"> new ClientSideRegionScanner(conf, fs, restoreDir, htd, hri, scan, scanMetrics);</span>
+<span class="source-line-no">196</span><span id="line-196"> if (this.scanMetrics != null) {</span>
+<span class="source-line-no">197</span><span id="line-197"> this.scanMetrics.addToCounter(ScanMetrics.REGIONS_SCANNED_METRIC_NAME, 1);</span>
+<span class="source-line-no">198</span><span id="line-198"> }</span>
+<span class="source-line-no">199</span><span id="line-199"> }</span>
+<span class="source-line-no">200</span><span id="line-200"></span>
+<span class="source-line-no">201</span><span id="line-201"> try {</span>
+<span class="source-line-no">202</span><span id="line-202"> result = currentRegionScanner.next();</span>
+<span class="source-line-no">203</span><span id="line-203"> if (result != null) {</span>
+<span class="source-line-no">204</span><span id="line-204"> if (scan.getLimit() > 0 && ++this.numOfCompleteRows > scan.getLimit()) {</span>
+<span class="source-line-no">205</span><span id="line-205"> result = null;</span>
+<span class="source-line-no">206</span><span id="line-206"> }</span>
+<span class="source-line-no">207</span><span id="line-207"> return result;</span>
+<span class="source-line-no">208</span><span id="line-208"> }</span>
+<span class="source-line-no">209</span><span id="line-209"> } finally {</span>
+<span class="source-line-no">210</span><span id="line-210"> if (result == null) {</span>
+<span class="source-line-no">211</span><span id="line-211"> currentRegionScanner.close();</span>
+<span class="source-line-no">212</span><span id="line-212"> currentRegionScanner = null;</span>
+<span class="source-line-no">213</span><span id="line-213"> }</span>
+<span class="source-line-no">214</span><span id="line-214"> }</span>
+<span class="source-line-no">215</span><span id="line-215"> }</span>
+<span class="source-line-no">216</span><span id="line-216"> }</span>
+<span class="source-line-no">217</span><span id="line-217"></span>
+<span class="source-line-no">218</span><span id="line-218"> private void cleanup() {</span>
+<span class="source-line-no">219</span><span id="line-219"> try {</span>
+<span class="source-line-no">220</span><span id="line-220"> if (fs.exists(this.restoreDir)) {</span>
+<span class="source-line-no">221</span><span id="line-221"> if (!fs.delete(this.restoreDir, true)) {</span>
+<span class="source-line-no">222</span><span id="line-222"> LOG.warn(</span>
+<span class="source-line-no">223</span><span id="line-223"> "Delete restore directory for the snapshot failed. restoreDir: " + this.restoreDir);</span>
+<span class="source-line-no">224</span><span id="line-224"> }</span>
+<span class="source-line-no">225</span><span id="line-225"> }</span>
+<span class="source-line-no">226</span><span id="line-226"> } catch (IOException ex) {</span>
+<span class="source-line-no">227</span><span id="line-227"> LOG.warn(</span>
+<span class="source-line-no">228</span><span id="line-228"> "Could not delete restore directory for the snapshot. restoreDir: " + this.restoreDir, ex);</span>
+<span class="source-line-no">229</span><span id="line-229"> }</span>
+<span class="source-line-no">230</span><span id="line-230"> }</span>
+<span class="source-line-no">231</span><span id="line-231"></span>
+<span class="source-line-no">232</span><span id="line-232"> @Override</span>
+<span class="source-line-no">233</span><span id="line-233"> public void close() {</span>
+<span class="source-line-no">234</span><span id="line-234"> if (currentRegionScanner != null) {</span>
+<span class="source-line-no">235</span><span id="line-235"> currentRegionScanner.close();</span>
+<span class="source-line-no">236</span><span id="line-236"> }</span>
+<span class="source-line-no">237</span><span id="line-237"> // if snapshotAlreadyRestored is true, then we should invoke cleanup() method by hand.</span>
+<span class="source-line-no">238</span><span id="line-238"> if (!this.snapshotAlreadyRestored) {</span>
+<span class="source-line-no">239</span><span id="line-239"> cleanup();</span>
+<span class="source-line-no">240</span><span id="line-240"> }</span>
+<span class="source-line-no">241</span><span id="line-241"> }</span>
+<span class="source-line-no">242</span><span id="line-242"></span>
+<span class="source-line-no">243</span><span id="line-243"> @Override</span>
+<span class="source-line-no">244</span><span id="line-244"> public boolean renewLease() {</span>
+<span class="source-line-no">245</span><span id="line-245"> throw new UnsupportedOperationException();</span>
+<span class="source-line-no">246</span><span id="line-246"> }</span>
+<span class="source-line-no">247</span><span id="line-247"></span>
+<span class="source-line-no">248</span><span id="line-248">}</span>
diff --git a/devapidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.InputSplit.html b/devapidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.InputSplit.html
index 4f25eecac2b..5d87836eb78 100644
--- a/devapidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.InputSplit.html
+++ b/devapidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.InputSplit.html
@@ -376,7 +376,7 @@
<span class="source-line-no">363</span><span id="line-363"> continue;</span>
<span class="source-line-no">364</span><span id="line-364"> }</span>
<span class="source-line-no">365</span><span id="line-365"> // The mob region is a dummy region used only to organise mob files under mobdir. It has no</span>
-<span class="source-line-no">366</span><span id="line-366"> // region directory under the table dir to open, and holds no rows. See HBASE-30365.</span>
+<span class="source-line-no">366</span><span id="line-366"> // region directory under the table dir to open. See HBASE-30365.</span>
<span class="source-line-no">367</span><span id="line-367"> if (MobUtils.isMobRegionInfo(hri)) {</span>
<span class="source-line-no">368</span><span id="line-368"> continue;</span>
<span class="source-line-no">369</span><span id="line-369"> }</span>
diff --git a/devapidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.RecordReader.html b/devapidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.RecordReader.html
index 988b049b680..c9f4537ca7b 100644
--- a/devapidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.RecordReader.html
+++ b/devapidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.RecordReader.html
@@ -376,7 +376,7 @@
<span class="source-line-no">363</span><span id="line-363"> continue;</span>
<span class="source-line-no">364</span><span id="line-364"> }</span>
<span class="source-line-no">365</span><span id="line-365"> // The mob region is a dummy region used only to organise mob files under mobdir. It has no</span>
-<span class="source-line-no">366</span><span id="line-366"> // region directory under the table dir to open, and holds no rows. See HBASE-30365.</span>
+<span class="source-line-no">366</span><span id="line-366"> // region directory under the table dir to open. See HBASE-30365.</span>
<span class="source-line-no">367</span><span id="line-367"> if (MobUtils.isMobRegionInfo(hri)) {</span>
<span class="source-line-no">368</span><span id="line-368"> continue;</span>
<span class="source-line-no">369</span><span id="line-369"> }</span>
diff --git a/devapidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.html b/devapidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.html
index ac564cc4ba2..8edc449b184 100644
--- a/devapidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.html
+++ b/devapidocs/src-html/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.html
@@ -376,7 +376,7 @@
<span class="source-line-no">363</span><span id="line-363"> continue;</span>
<span class="source-line-no">364</span><span id="line-364"> }</span>
<span class="source-line-no">365</span><span id="line-365"> // The mob region is a dummy region used only to organise mob files under mobdir. It has no</span>
-<span class="source-line-no">366</span><span id="line-366"> // region directory under the table dir to open, and holds no rows. See HBASE-30365.</span>
+<span class="source-line-no">366</span><span id="line-366"> // region directory under the table dir to open. See HBASE-30365.</span>
<span class="source-line-no">367</span><span id="line-367"> if (MobUtils.isMobRegionInfo(hri)) {</span>
<span class="source-line-no">368</span><span id="line-368"> continue;</span>
<span class="source-line-no">369</span><span id="line-369"> }</span>
diff --git a/hbase-website/dependency-convergence.html b/hbase-website/dependency-convergence.html
index 7c1a2ed8fad..aef5a6cff17 100644
--- a/hbase-website/dependency-convergence.html
+++ b/hbase-website/dependency-convergence.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:dependency-convergence at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:dependency-convergence at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/hbase-website/dependency-info.html b/hbase-website/dependency-info.html
index 153de835ff8..4408ea355d8 100644
--- a/hbase-website/dependency-info.html
+++ b/hbase-website/dependency-info.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:dependency-info at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:dependency-info at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/hbase-website/dependency-management.html b/hbase-website/dependency-management.html
index 388d9bf6f17..6345cff7542 100644
--- a/hbase-website/dependency-management.html
+++ b/hbase-website/dependency-management.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:dependency-management at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:dependency-management at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/hbase-website/index.html b/hbase-website/index.html
index fae1642d781..d43aea07c14 100644
--- a/hbase-website/index.html
+++ b/hbase-website/index.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:index at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:index at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/hbase-website/issue-management.html b/hbase-website/issue-management.html
index eae07b80e0e..e0f3cab047a 100644
--- a/hbase-website/issue-management.html
+++ b/hbase-website/issue-management.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:issue-management at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:issue-management at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/hbase-website/licenses.html b/hbase-website/licenses.html
index aad8af043bf..9b4d26a30e3 100644
--- a/hbase-website/licenses.html
+++ b/hbase-website/licenses.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:licenses at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:licenses at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/hbase-website/mailing-lists.html b/hbase-website/mailing-lists.html
index 18179843b06..a00b6fb363b 100644
--- a/hbase-website/mailing-lists.html
+++ b/hbase-website/mailing-lists.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:mailing-lists at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:mailing-lists at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/hbase-website/plugin-management.html b/hbase-website/plugin-management.html
index 931615ee5a1..935220c04c8 100644
--- a/hbase-website/plugin-management.html
+++ b/hbase-website/plugin-management.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:plugin-management at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:plugin-management at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/hbase-website/plugins.html b/hbase-website/plugins.html
index 4eebd6f0711..f8f4535a05b 100644
--- a/hbase-website/plugins.html
+++ b/hbase-website/plugins.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:plugins at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:plugins at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/hbase-website/project-info.html b/hbase-website/project-info.html
index b2d9d728de6..dc03eb5fc60 100644
--- a/hbase-website/project-info.html
+++ b/hbase-website/project-info.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-site-plugin:3.21.0:project-info at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-site-plugin:3.21.0:project-info at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/hbase-website/scm.html b/hbase-website/scm.html
index 5f454567fe3..dfe19274e02 100644
--- a/hbase-website/scm.html
+++ b/hbase-website/scm.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:scm at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:scm at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/hbase-website/summary.html b/hbase-website/summary.html
index dc4041e779f..22bc2720a26 100644
--- a/hbase-website/summary.html
+++ b/hbase-website/summary.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:summary at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:summary at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/hbase-website/team.html b/hbase-website/team.html
index 9325f507d7c..019a2db735b 100644
--- a/hbase-website/team.html
+++ b/hbase-website/team.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:team at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:team at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/issue-management.html b/issue-management.html
index 2a1d139740d..1264a6ad543 100644
--- a/issue-management.html
+++ b/issue-management.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:issue-management at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:issue-management at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/licenses.html b/licenses.html
index 26407271f0d..f622194180e 100644
--- a/licenses.html
+++ b/licenses.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:licenses at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:licenses at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/mailing-lists.html b/mailing-lists.html
index 1d14d77b25a..0b7997eba1d 100644
--- a/mailing-lists.html
+++ b/mailing-lists.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:mailing-lists at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:mailing-lists at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/plugin-management.html b/plugin-management.html
index d7ab5bd536c..287c8fd3a21 100644
--- a/plugin-management.html
+++ b/plugin-management.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:plugin-management at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:plugin-management at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/plugins.html b/plugins.html
index 7bb6082f09d..7dd9d58d330 100644
--- a/plugins.html
+++ b/plugins.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:plugins at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:plugins at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/project-info.html b/project-info.html
index 603b21b5536..a7ae2bed34b 100644
--- a/project-info.html
+++ b/project-info.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-site-plugin:3.21.0:project-info at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-site-plugin:3.21.0:project-info at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/project-reports.html b/project-reports.html
index d78478e7937..c3e477132a7 100644
--- a/project-reports.html
+++ b/project-reports.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-site-plugin:3.21.0:project-reports at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-site-plugin:3.21.0:project-reports at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/scm.html b/scm.html
index 4d1a1152dff..7130a31550f 100644
--- a/scm.html
+++ b/scm.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:scm at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:scm at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/summary.html b/summary.html
index 737e7fff8b4..1546cb2ed45 100644
--- a/summary.html
+++ b/summary.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:summary at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:summary at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/team.html b/team.html
index f2508911eb5..e1994ce0ecf 100644
--- a/team.html
+++ b/team.html
@@ -2,7 +2,7 @@
<!--
- | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:team at 2026-09-08
+ | Generated by Apache Maven Doxia Site Renderer 2.0.0 from org.apache.maven.plugins:maven-project-info-reports-plugin:3.9.0:team at 2026-09-09
| Rendered using Apache Maven Fluido Skin 2.0.0-M9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
diff --git a/testapidocs/index-all.html b/testapidocs/index-all.html
index dc81562c146..c56ab1a16eb 100644
--- a/testapidocs/index-all.html
+++ b/testapidocs/index-all.html
@@ -40200,6 +40200,8 @@ loadScripts(document, 'script');</script>
<dd> </dd>
<dt><a href="../apidocs/org/apache/hadoop/hbase/client/TestScannersFromClientSide.html#testScannerWithPartialResults()" class="member-name-link external-link" title="class or interface in org.apache.hadoop.hbase.client">testScannerWithPartialResults()</a> - Method in class org.apache.hadoop.hbase.client.<a href="../apidocs/org/apache/hadoop/hbase/client/TestScannersFromClientSide.html" title="class or interface in org.apache.hadoop.hbase.client" class="external-link">TestScannersFromClie [...]
<dd> </dd>
+<dt><a href="../apidocs/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#testScannerWithRestoredMobSnapshot()" class="member-name-link external-link" title="class or interface in org.apache.hadoop.hbase.client">testScannerWithRestoredMobSnapshot()</a> - Method in class org.apache.hadoop.hbase.client.<a href="../apidocs/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html" title="class or interface in org.apache.hadoop.hbase.client" class="external-link">TestTableSnaps [...]
+<dd> </dd>
<dt><a href="../apidocs/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#testScannerWithRestoreScanner()" class="member-name-link external-link" title="class or interface in org.apache.hadoop.hbase.client">testScannerWithRestoreScanner()</a> - Method in class org.apache.hadoop.hbase.client.<a href="../apidocs/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html" title="class or interface in org.apache.hadoop.hbase.client" class="external-link">TestTableSnapshotScanner [...]
<dd> </dd>
<dt><a href="../apidocs/org/apache/hadoop/hbase/rest/TestTableScan.html#testScanningUnknownColumnJson()" class="member-name-link external-link" title="class or interface in org.apache.hadoop.hbase.rest">testScanningUnknownColumnJson()</a> - Method in class org.apache.hadoop.hbase.rest.<a href="../apidocs/org/apache/hadoop/hbase/rest/TestTableScan.html" title="class or interface in org.apache.hadoop.hbase.rest" class="external-link">TestTableScan</a></dt>
diff --git a/testapidocs/member-search-index.js b/testapidocs/member-search-index.js
index 2ce794dd574..ab7a35a6f12 100644
--- a/testapidocs/member-search-index.js
+++ b/testapidocs/member-search-index.js
@@ -1 +1 @@
-memberSearchIndex = [{"p":"org.apache.hadoop.hbase.backup","c":"TestSystemTableSnapshot","l":"_testBackupRestoreSystemTable()"},{"p":"org.apache.hadoop.hbase.coprocessor","c":"TestRegionObserverInterface","l":"A"},{"p":"org.apache.hadoop.hbase.client","c":"TestGetProcedureResult.DummyProcedure","l":"abort(MasterProcedureEnv)","u":"abort(org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv)"},{"p":"org.apache.hadoop.hbase.master","c":"TestMigrateNamespaceTable.SuspendProcedure","l" [...]
\ No newline at end of file
+memberSearchIndex = [{"p":"org.apache.hadoop.hbase.backup","c":"TestSystemTableSnapshot","l":"_testBackupRestoreSystemTable()"},{"p":"org.apache.hadoop.hbase.coprocessor","c":"TestRegionObserverInterface","l":"A"},{"p":"org.apache.hadoop.hbase.client","c":"TestGetProcedureResult.DummyProcedure","l":"abort(MasterProcedureEnv)","u":"abort(org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv)"},{"p":"org.apache.hadoop.hbase.master","c":"TestMigrateNamespaceTable.SuspendProcedure","l" [...]
\ No newline at end of file
diff --git a/testapidocs/src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html b/testapidocs/src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html
index 9ffff1f2453..fbcf5419bbf 100644
--- a/testapidocs/src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html
+++ b/testapidocs/src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html
@@ -62,574 +62,600 @@
<span class="source-line-no">049</span><span id="line-49">import org.apache.hadoop.hbase.regionserver.StoreContext;</span>
<span class="source-line-no">050</span><span id="line-50">import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker;</span>
<span class="source-line-no">051</span><span id="line-51">import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;</span>
-<span class="source-line-no">052</span><span id="line-52">import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;</span>
-<span class="source-line-no">053</span><span id="line-53">import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils;</span>
-<span class="source-line-no">054</span><span id="line-54">import org.apache.hadoop.hbase.testclassification.ClientTests;</span>
-<span class="source-line-no">055</span><span id="line-55">import org.apache.hadoop.hbase.testclassification.LargeTests;</span>
-<span class="source-line-no">056</span><span id="line-56">import org.apache.hadoop.hbase.util.Bytes;</span>
-<span class="source-line-no">057</span><span id="line-57">import org.apache.hadoop.hbase.util.CommonFSUtils;</span>
-<span class="source-line-no">058</span><span id="line-58">import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;</span>
-<span class="source-line-no">059</span><span id="line-59">import org.apache.hadoop.hbase.util.FSUtils;</span>
-<span class="source-line-no">060</span><span id="line-60">import org.apache.hadoop.hbase.util.HFileArchiveUtil;</span>
-<span class="source-line-no">061</span><span id="line-61">import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;</span>
-<span class="source-line-no">062</span><span id="line-62">import org.junit.jupiter.api.AfterEach;</span>
-<span class="source-line-no">063</span><span id="line-63">import org.junit.jupiter.api.BeforeEach;</span>
-<span class="source-line-no">064</span><span id="line-64">import org.junit.jupiter.api.Tag;</span>
-<span class="source-line-no">065</span><span id="line-65">import org.junit.jupiter.api.Test;</span>
-<span class="source-line-no">066</span><span id="line-66">import org.junit.jupiter.api.TestInfo;</span>
-<span class="source-line-no">067</span><span id="line-67">import org.slf4j.Logger;</span>
-<span class="source-line-no">068</span><span id="line-68">import org.slf4j.LoggerFactory;</span>
-<span class="source-line-no">069</span><span id="line-69"></span>
-<span class="source-line-no">070</span><span id="line-70">@Tag(LargeTests.TAG)</span>
-<span class="source-line-no">071</span><span id="line-71">@Tag(ClientTests.TAG)</span>
-<span class="source-line-no">072</span><span id="line-72">public class TestTableSnapshotScanner {</span>
-<span class="source-line-no">073</span><span id="line-73"></span>
-<span class="source-line-no">074</span><span id="line-74"> private static final Logger LOG = LoggerFactory.getLogger(TestTableSnapshotScanner.class);</span>
-<span class="source-line-no">075</span><span id="line-75"> private final HBaseTestingUtil UTIL = new HBaseTestingUtil();</span>
-<span class="source-line-no">076</span><span id="line-76"> private static final int NUM_REGION_SERVERS = 2;</span>
-<span class="source-line-no">077</span><span id="line-77"> private static final byte[][] FAMILIES = { Bytes.toBytes("f1"), Bytes.toBytes("f2") };</span>
-<span class="source-line-no">078</span><span id="line-78"> public static byte[] bbb = Bytes.toBytes("bbb");</span>
-<span class="source-line-no">079</span><span id="line-79"> public static byte[] yyy = Bytes.toBytes("yyy");</span>
-<span class="source-line-no">080</span><span id="line-80"></span>
-<span class="source-line-no">081</span><span id="line-81"> private FileSystem fs;</span>
-<span class="source-line-no">082</span><span id="line-82"> private Path rootDir;</span>
-<span class="source-line-no">083</span><span id="line-83"> private boolean clusterUp;</span>
-<span class="source-line-no">084</span><span id="line-84"></span>
-<span class="source-line-no">085</span><span id="line-85"> private String methodName;</span>
-<span class="source-line-no">086</span><span id="line-86"></span>
-<span class="source-line-no">087</span><span id="line-87"> public static void blockUntilSplitFinished(HBaseTestingUtil util, TableName tableName,</span>
-<span class="source-line-no">088</span><span id="line-88"> int expectedRegionSize) throws Exception {</span>
-<span class="source-line-no">089</span><span id="line-89"> for (int i = 0; i < 100; i++) {</span>
-<span class="source-line-no">090</span><span id="line-90"> List<RegionInfo> hRegionInfoList = util.getAdmin().getRegions(tableName);</span>
-<span class="source-line-no">091</span><span id="line-91"> if (hRegionInfoList.size() >= expectedRegionSize) {</span>
-<span class="source-line-no">092</span><span id="line-92"> break;</span>
-<span class="source-line-no">093</span><span id="line-93"> }</span>
-<span class="source-line-no">094</span><span id="line-94"> Thread.sleep(1000);</span>
-<span class="source-line-no">095</span><span id="line-95"> }</span>
-<span class="source-line-no">096</span><span id="line-96"> }</span>
-<span class="source-line-no">097</span><span id="line-97"></span>
-<span class="source-line-no">098</span><span id="line-98"> @BeforeEach</span>
-<span class="source-line-no">099</span><span id="line-99"> public void setupCluster(TestInfo testInfo) throws Exception {</span>
-<span class="source-line-no">100</span><span id="line-100"> methodName = testInfo.getTestMethod().get().getName();</span>
-<span class="source-line-no">101</span><span id="line-101"> setupConf(UTIL.getConfiguration());</span>
-<span class="source-line-no">102</span><span id="line-102"> StartTestingClusterOption option =</span>
-<span class="source-line-no">103</span><span id="line-103"> StartTestingClusterOption.builder().numRegionServers(NUM_REGION_SERVERS)</span>
-<span class="source-line-no">104</span><span id="line-104"> .numDataNodes(NUM_REGION_SERVERS).createRootDir(true).build();</span>
-<span class="source-line-no">105</span><span id="line-105"> UTIL.startMiniCluster(option);</span>
-<span class="source-line-no">106</span><span id="line-106"> clusterUp = true;</span>
-<span class="source-line-no">107</span><span id="line-107"> rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();</span>
-<span class="source-line-no">108</span><span id="line-108"> fs = rootDir.getFileSystem(UTIL.getConfiguration());</span>
-<span class="source-line-no">109</span><span id="line-109"> }</span>
-<span class="source-line-no">110</span><span id="line-110"></span>
-<span class="source-line-no">111</span><span id="line-111"> @AfterEach</span>
-<span class="source-line-no">112</span><span id="line-112"> public void tearDownCluster() throws Exception {</span>
-<span class="source-line-no">113</span><span id="line-113"> if (clusterUp) {</span>
-<span class="source-line-no">114</span><span id="line-114"> UTIL.shutdownMiniCluster();</span>
-<span class="source-line-no">115</span><span id="line-115"> }</span>
-<span class="source-line-no">116</span><span id="line-116"> }</span>
-<span class="source-line-no">117</span><span id="line-117"></span>
-<span class="source-line-no">118</span><span id="line-118"> protected void setupConf(Configuration conf) {</span>
-<span class="source-line-no">119</span><span id="line-119"> // Enable snapshot</span>
-<span class="source-line-no">120</span><span id="line-120"> conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);</span>
-<span class="source-line-no">121</span><span id="line-121"> }</span>
-<span class="source-line-no">122</span><span id="line-122"></span>
-<span class="source-line-no">123</span><span id="line-123"> public static void createTableAndSnapshot(HBaseTestingUtil util, TableName tableName,</span>
-<span class="source-line-no">124</span><span id="line-124"> String snapshotName, int numRegions) throws Exception {</span>
-<span class="source-line-no">125</span><span id="line-125"> try {</span>
-<span class="source-line-no">126</span><span id="line-126"> util.deleteTable(tableName);</span>
-<span class="source-line-no">127</span><span id="line-127"> } catch (Exception ex) {</span>
-<span class="source-line-no">128</span><span id="line-128"> // ignore</span>
-<span class="source-line-no">129</span><span id="line-129"> }</span>
-<span class="source-line-no">130</span><span id="line-130"></span>
-<span class="source-line-no">131</span><span id="line-131"> if (numRegions > 1) {</span>
-<span class="source-line-no">132</span><span id="line-132"> util.createTable(tableName, FAMILIES, 1, bbb, yyy, numRegions);</span>
-<span class="source-line-no">133</span><span id="line-133"> } else {</span>
-<span class="source-line-no">134</span><span id="line-134"> util.createTable(tableName, FAMILIES);</span>
-<span class="source-line-no">135</span><span id="line-135"> }</span>
-<span class="source-line-no">136</span><span id="line-136"> Admin admin = util.getAdmin();</span>
-<span class="source-line-no">137</span><span id="line-137"></span>
-<span class="source-line-no">138</span><span id="line-138"> // put some stuff in the table</span>
-<span class="source-line-no">139</span><span id="line-139"> Table table = util.getConnection().getTable(tableName);</span>
-<span class="source-line-no">140</span><span id="line-140"> util.loadTable(table, FAMILIES);</span>
-<span class="source-line-no">141</span><span id="line-141"></span>
-<span class="source-line-no">142</span><span id="line-142"> Path rootDir = CommonFSUtils.getRootDir(util.getConfiguration());</span>
-<span class="source-line-no">143</span><span id="line-143"> FileSystem fs = rootDir.getFileSystem(util.getConfiguration());</span>
-<span class="source-line-no">144</span><span id="line-144"></span>
-<span class="source-line-no">145</span><span id="line-145"> SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, Arrays.asList(FAMILIES), null,</span>
-<span class="source-line-no">146</span><span id="line-146"> snapshotName, rootDir, fs, true);</span>
-<span class="source-line-no">147</span><span id="line-147"></span>
-<span class="source-line-no">148</span><span id="line-148"> // load different values</span>
-<span class="source-line-no">149</span><span id="line-149"> byte[] value = Bytes.toBytes("after_snapshot_value");</span>
-<span class="source-line-no">150</span><span id="line-150"> util.loadTable(table, FAMILIES, value);</span>
-<span class="source-line-no">151</span><span id="line-151"></span>
-<span class="source-line-no">152</span><span id="line-152"> // cause flush to create new files in the region</span>
-<span class="source-line-no">153</span><span id="line-153"> admin.flush(tableName);</span>
-<span class="source-line-no">154</span><span id="line-154"> table.close();</span>
-<span class="source-line-no">155</span><span id="line-155"> }</span>
-<span class="source-line-no">156</span><span id="line-156"></span>
-<span class="source-line-no">157</span><span id="line-157"> @Test</span>
-<span class="source-line-no">158</span><span id="line-158"> public void testNoDuplicateResultsWhenSplitting() throws Exception {</span>
-<span class="source-line-no">159</span><span id="line-159"> TableName tableName = TableName.valueOf("testNoDuplicateResultsWhenSplitting");</span>
-<span class="source-line-no">160</span><span id="line-160"> String snapshotName = "testSnapshotBug";</span>
-<span class="source-line-no">161</span><span id="line-161"> try {</span>
-<span class="source-line-no">162</span><span id="line-162"> if (UTIL.getAdmin().tableExists(tableName)) {</span>
-<span class="source-line-no">163</span><span id="line-163"> UTIL.deleteTable(tableName);</span>
-<span class="source-line-no">164</span><span id="line-164"> }</span>
-<span class="source-line-no">165</span><span id="line-165"></span>
-<span class="source-line-no">166</span><span id="line-166"> UTIL.createTable(tableName, FAMILIES);</span>
-<span class="source-line-no">167</span><span id="line-167"> Admin admin = UTIL.getAdmin();</span>
-<span class="source-line-no">168</span><span id="line-168"></span>
-<span class="source-line-no">169</span><span id="line-169"> // put some stuff in the table</span>
-<span class="source-line-no">170</span><span id="line-170"> Table table = UTIL.getConnection().getTable(tableName);</span>
-<span class="source-line-no">171</span><span id="line-171"> UTIL.loadTable(table, FAMILIES);</span>
-<span class="source-line-no">172</span><span id="line-172"></span>
-<span class="source-line-no">173</span><span id="line-173"> // split to 2 regions</span>
-<span class="source-line-no">174</span><span id="line-174"> admin.split(tableName, Bytes.toBytes("eee"));</span>
-<span class="source-line-no">175</span><span id="line-175"> blockUntilSplitFinished(UTIL, tableName, 2);</span>
-<span class="source-line-no">176</span><span id="line-176"></span>
-<span class="source-line-no">177</span><span id="line-177"> Path rootDir = CommonFSUtils.getRootDir(UTIL.getConfiguration());</span>
-<span class="source-line-no">178</span><span id="line-178"> FileSystem fs = rootDir.getFileSystem(UTIL.getConfiguration());</span>
-<span class="source-line-no">179</span><span id="line-179"></span>
-<span class="source-line-no">180</span><span id="line-180"> SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, Arrays.asList(FAMILIES),</span>
-<span class="source-line-no">181</span><span id="line-181"> null, snapshotName, rootDir, fs, true);</span>
-<span class="source-line-no">182</span><span id="line-182"></span>
-<span class="source-line-no">183</span><span id="line-183"> // load different values</span>
-<span class="source-line-no">184</span><span id="line-184"> byte[] value = Bytes.toBytes("after_snapshot_value");</span>
-<span class="source-line-no">185</span><span id="line-185"> UTIL.loadTable(table, FAMILIES, value);</span>
-<span class="source-line-no">186</span><span id="line-186"></span>
-<span class="source-line-no">187</span><span id="line-187"> // cause flush to create new files in the region</span>
-<span class="source-line-no">188</span><span id="line-188"> admin.flush(tableName);</span>
-<span class="source-line-no">189</span><span id="line-189"> table.close();</span>
-<span class="source-line-no">190</span><span id="line-190"></span>
-<span class="source-line-no">191</span><span id="line-191"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
-<span class="source-line-no">192</span><span id="line-192"> Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan</span>
-<span class="source-line-no">193</span><span id="line-193"></span>
-<span class="source-line-no">194</span><span id="line-194"> TableSnapshotScanner scanner =</span>
-<span class="source-line-no">195</span><span id="line-195"> new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan);</span>
-<span class="source-line-no">196</span><span id="line-196"></span>
-<span class="source-line-no">197</span><span id="line-197"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">198</span><span id="line-198"> scanner.close();</span>
-<span class="source-line-no">199</span><span id="line-199"> } catch (Exception e) {</span>
-<span class="source-line-no">200</span><span id="line-200"> e.printStackTrace();</span>
-<span class="source-line-no">201</span><span id="line-201"> } finally {</span>
-<span class="source-line-no">202</span><span id="line-202"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
-<span class="source-line-no">203</span><span id="line-203"> UTIL.deleteTable(tableName);</span>
-<span class="source-line-no">204</span><span id="line-204"> }</span>
-<span class="source-line-no">205</span><span id="line-205"> }</span>
-<span class="source-line-no">206</span><span id="line-206"></span>
-<span class="source-line-no">207</span><span id="line-207"> @Test</span>
-<span class="source-line-no">208</span><span id="line-208"> public void testScanLimit() throws Exception {</span>
-<span class="source-line-no">209</span><span id="line-209"> final TableName tableName = TableName.valueOf(methodName);</span>
-<span class="source-line-no">210</span><span id="line-210"> final String snapshotName = tableName + "Snapshot";</span>
-<span class="source-line-no">211</span><span id="line-211"> TableSnapshotScanner scanner = null;</span>
-<span class="source-line-no">212</span><span id="line-212"> try {</span>
-<span class="source-line-no">213</span><span id="line-213"> createTableAndSnapshot(UTIL, tableName, snapshotName, 50);</span>
-<span class="source-line-no">214</span><span id="line-214"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
-<span class="source-line-no">215</span><span id="line-215"> Scan scan = new Scan().withStartRow(bbb).setLimit(100); // limit the scan</span>
-<span class="source-line-no">216</span><span id="line-216"></span>
-<span class="source-line-no">217</span><span id="line-217"> scanner = new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan);</span>
-<span class="source-line-no">218</span><span id="line-218"> int count = 0;</span>
-<span class="source-line-no">219</span><span id="line-219"> while (true) {</span>
-<span class="source-line-no">220</span><span id="line-220"> Result result = scanner.next();</span>
-<span class="source-line-no">221</span><span id="line-221"> if (result == null) {</span>
-<span class="source-line-no">222</span><span id="line-222"> break;</span>
-<span class="source-line-no">223</span><span id="line-223"> }</span>
-<span class="source-line-no">224</span><span id="line-224"> count++;</span>
-<span class="source-line-no">225</span><span id="line-225"> }</span>
-<span class="source-line-no">226</span><span id="line-226"> assertEquals(100, count);</span>
-<span class="source-line-no">227</span><span id="line-227"> } finally {</span>
-<span class="source-line-no">228</span><span id="line-228"> if (scanner != null) {</span>
-<span class="source-line-no">229</span><span id="line-229"> scanner.close();</span>
-<span class="source-line-no">230</span><span id="line-230"> }</span>
-<span class="source-line-no">231</span><span id="line-231"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
-<span class="source-line-no">232</span><span id="line-232"> UTIL.deleteTable(tableName);</span>
-<span class="source-line-no">233</span><span id="line-233"> }</span>
-<span class="source-line-no">234</span><span id="line-234"> }</span>
-<span class="source-line-no">235</span><span id="line-235"></span>
-<span class="source-line-no">236</span><span id="line-236"> @Test</span>
-<span class="source-line-no">237</span><span id="line-237"> public void testWithSingleRegion() throws Exception {</span>
-<span class="source-line-no">238</span><span id="line-238"> testScanner(UTIL, "testWithSingleRegion", 1, false);</span>
-<span class="source-line-no">239</span><span id="line-239"> }</span>
-<span class="source-line-no">240</span><span id="line-240"></span>
-<span class="source-line-no">241</span><span id="line-241"> @Test</span>
-<span class="source-line-no">242</span><span id="line-242"> public void testWithMultiRegion() throws Exception {</span>
-<span class="source-line-no">243</span><span id="line-243"> testScanner(UTIL, "testWithMultiRegion", 10, false);</span>
-<span class="source-line-no">244</span><span id="line-244"> }</span>
-<span class="source-line-no">245</span><span id="line-245"></span>
-<span class="source-line-no">246</span><span id="line-246"> @Test</span>
-<span class="source-line-no">247</span><span id="line-247"> public void testWithOfflineHBaseMultiRegion() throws Exception {</span>
-<span class="source-line-no">248</span><span id="line-248"> testScanner(UTIL, "testWithMultiRegion", 20, true);</span>
-<span class="source-line-no">249</span><span id="line-249"> }</span>
-<span class="source-line-no">250</span><span id="line-250"></span>
-<span class="source-line-no">251</span><span id="line-251"> private ScanMetrics createTableSnapshotScannerAndGetScanMetrics(boolean enableScanMetrics,</span>
-<span class="source-line-no">252</span><span id="line-252"> boolean enableScanMetricsByRegion, byte[] endKey) throws Exception {</span>
-<span class="source-line-no">253</span><span id="line-253"> TableName tableName = TableName.valueOf(methodName + "_TABLE");</span>
-<span class="source-line-no">254</span><span id="line-254"> String snapshotName = methodName + "_SNAPSHOT";</span>
-<span class="source-line-no">255</span><span id="line-255"> try {</span>
-<span class="source-line-no">256</span><span id="line-256"> createTableAndSnapshot(UTIL, tableName, snapshotName, 50);</span>
-<span class="source-line-no">257</span><span id="line-257"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
-<span class="source-line-no">258</span><span id="line-258"> Scan scan = new Scan().withStartRow(bbb).withStopRow(endKey);</span>
-<span class="source-line-no">259</span><span id="line-259"> scan.setScanMetricsEnabled(enableScanMetrics);</span>
-<span class="source-line-no">260</span><span id="line-260"> scan.setEnableScanMetricsByRegion(enableScanMetricsByRegion);</span>
-<span class="source-line-no">261</span><span id="line-261"> Configuration conf = UTIL.getConfiguration();</span>
-<span class="source-line-no">262</span><span id="line-262"></span>
-<span class="source-line-no">263</span><span id="line-263"> TableSnapshotScanner snapshotScanner =</span>
-<span class="source-line-no">264</span><span id="line-264"> new TableSnapshotScanner(conf, restoreDir, snapshotName, scan);</span>
-<span class="source-line-no">265</span><span id="line-265"> verifyScanner(snapshotScanner, bbb, endKey);</span>
-<span class="source-line-no">266</span><span id="line-266"> return snapshotScanner.getScanMetrics();</span>
-<span class="source-line-no">267</span><span id="line-267"> } finally {</span>
-<span class="source-line-no">268</span><span id="line-268"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
-<span class="source-line-no">269</span><span id="line-269"> UTIL.deleteTable(tableName);</span>
-<span class="source-line-no">270</span><span id="line-270"> }</span>
-<span class="source-line-no">271</span><span id="line-271"> }</span>
-<span class="source-line-no">272</span><span id="line-272"></span>
-<span class="source-line-no">273</span><span id="line-273"> @Test</span>
-<span class="source-line-no">274</span><span id="line-274"> public void testScanMetricsDisabled() throws Exception {</span>
-<span class="source-line-no">275</span><span id="line-275"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(false, false, yyy);</span>
-<span class="source-line-no">276</span><span id="line-276"> assertNull(scanMetrics);</span>
-<span class="source-line-no">277</span><span id="line-277"> }</span>
-<span class="source-line-no">278</span><span id="line-278"></span>
-<span class="source-line-no">279</span><span id="line-279"> @Test</span>
-<span class="source-line-no">280</span><span id="line-280"> public void testScanMetricsWithScanMetricsByRegionDisabled() throws Exception {</span>
-<span class="source-line-no">281</span><span id="line-281"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, false, yyy);</span>
-<span class="source-line-no">282</span><span id="line-282"> assertNotNull(scanMetrics);</span>
-<span class="source-line-no">283</span><span id="line-283"> int rowsScanned = 0;</span>
-<span class="source-line-no">284</span><span id="line-284"> for (byte[] row : HBaseTestingUtil.ROWS) {</span>
-<span class="source-line-no">285</span><span id="line-285"> if (Bytes.compareTo(row, bbb) >= 0 && Bytes.compareTo(row, yyy) < 0) {</span>
-<span class="source-line-no">286</span><span id="line-286"> rowsScanned++;</span>
-<span class="source-line-no">287</span><span id="line-287"> }</span>
-<span class="source-line-no">288</span><span id="line-288"> }</span>
-<span class="source-line-no">289</span><span id="line-289"> Map<String, Long> metricsMap = scanMetrics.getMetricsMap();</span>
-<span class="source-line-no">290</span><span id="line-290"> assertEquals(rowsScanned, (long) metricsMap.get(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME));</span>
-<span class="source-line-no">291</span><span id="line-291"> }</span>
-<span class="source-line-no">292</span><span id="line-292"></span>
-<span class="source-line-no">293</span><span id="line-293"> @Test</span>
-<span class="source-line-no">294</span><span id="line-294"> public void testScanMetricsByRegionForSingleRegion() throws Exception {</span>
-<span class="source-line-no">295</span><span id="line-295"> // Scan single row with row key bbb</span>
-<span class="source-line-no">296</span><span id="line-296"> byte[] bbc = Bytes.toBytes("bbc");</span>
-<span class="source-line-no">297</span><span id="line-297"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, true, bbc);</span>
-<span class="source-line-no">298</span><span id="line-298"> assertNotNull(scanMetrics);</span>
-<span class="source-line-no">299</span><span id="line-299"> Map<ScanMetricsRegionInfo, Map<String, Long>> scanMetricsByRegion =</span>
-<span class="source-line-no">300</span><span id="line-300"> scanMetrics.collectMetricsByRegion();</span>
-<span class="source-line-no">301</span><span id="line-301"> assertEquals(1, scanMetricsByRegion.size());</span>
-<span class="source-line-no">302</span><span id="line-302"> for (Map.Entry<ScanMetricsRegionInfo, Map<String, Long>> entry : scanMetricsByRegion</span>
-<span class="source-line-no">303</span><span id="line-303"> .entrySet()) {</span>
-<span class="source-line-no">304</span><span id="line-304"> ScanMetricsRegionInfo scanMetricsRegionInfo = entry.getKey();</span>
-<span class="source-line-no">305</span><span id="line-305"> Map<String, Long> metricsMap = entry.getValue();</span>
-<span class="source-line-no">306</span><span id="line-306"> assertNull(scanMetricsRegionInfo.getServerName());</span>
-<span class="source-line-no">307</span><span id="line-307"> assertNotNull(scanMetricsRegionInfo.getEncodedRegionName());</span>
-<span class="source-line-no">308</span><span id="line-308"> assertEquals(1, (long) metricsMap.get(REGIONS_SCANNED_METRIC_NAME));</span>
-<span class="source-line-no">309</span><span id="line-309"> assertEquals(1, (long) metricsMap.get(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME));</span>
-<span class="source-line-no">310</span><span id="line-310"> }</span>
-<span class="source-line-no">311</span><span id="line-311"> }</span>
-<span class="source-line-no">312</span><span id="line-312"></span>
-<span class="source-line-no">313</span><span id="line-313"> @Test</span>
-<span class="source-line-no">314</span><span id="line-314"> public void testScanMetricsByRegionForMultiRegion() throws Exception {</span>
-<span class="source-line-no">315</span><span id="line-315"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, true, yyy);</span>
-<span class="source-line-no">316</span><span id="line-316"> assertNotNull(scanMetrics);</span>
-<span class="source-line-no">317</span><span id="line-317"> Map<ScanMetricsRegionInfo, Map<String, Long>> scanMetricsByRegion =</span>
-<span class="source-line-no">318</span><span id="line-318"> scanMetrics.collectMetricsByRegion();</span>
-<span class="source-line-no">319</span><span id="line-319"> for (Map.Entry<ScanMetricsRegionInfo, Map<String, Long>> entry : scanMetricsByRegion</span>
-<span class="source-line-no">320</span><span id="line-320"> .entrySet()) {</span>
-<span class="source-line-no">321</span><span id="line-321"> ScanMetricsRegionInfo scanMetricsRegionInfo = entry.getKey();</span>
-<span class="source-line-no">322</span><span id="line-322"> Map<String, Long> metricsMap = entry.getValue();</span>
-<span class="source-line-no">323</span><span id="line-323"> assertNull(scanMetricsRegionInfo.getServerName());</span>
-<span class="source-line-no">324</span><span id="line-324"> assertNotNull(scanMetricsRegionInfo.getEncodedRegionName());</span>
-<span class="source-line-no">325</span><span id="line-325"> assertEquals(1, (long) metricsMap.get(REGIONS_SCANNED_METRIC_NAME));</span>
-<span class="source-line-no">326</span><span id="line-326"> }</span>
-<span class="source-line-no">327</span><span id="line-327"> }</span>
-<span class="source-line-no">328</span><span id="line-328"></span>
-<span class="source-line-no">329</span><span id="line-329"> @Test</span>
-<span class="source-line-no">330</span><span id="line-330"> public void testScannerWithRestoreScanner() throws Exception {</span>
-<span class="source-line-no">331</span><span id="line-331"> TableName tableName = TableName.valueOf("testScanner");</span>
-<span class="source-line-no">332</span><span id="line-332"> String snapshotName = "testScannerWithRestoreScanner";</span>
-<span class="source-line-no">333</span><span id="line-333"> try {</span>
-<span class="source-line-no">334</span><span id="line-334"> createTableAndSnapshot(UTIL, tableName, snapshotName, 50);</span>
-<span class="source-line-no">335</span><span id="line-335"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
-<span class="source-line-no">336</span><span id="line-336"> Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan</span>
-<span class="source-line-no">337</span><span id="line-337"></span>
-<span class="source-line-no">338</span><span id="line-338"> Configuration conf = UTIL.getConfiguration();</span>
-<span class="source-line-no">339</span><span id="line-339"> Path rootDir = CommonFSUtils.getRootDir(conf);</span>
-<span class="source-line-no">340</span><span id="line-340"></span>
-<span class="source-line-no">341</span><span id="line-341"> TableSnapshotScanner scanner0 =</span>
-<span class="source-line-no">342</span><span id="line-342"> new TableSnapshotScanner(conf, restoreDir, snapshotName, scan);</span>
-<span class="source-line-no">343</span><span id="line-343"> verifyScanner(scanner0, bbb, yyy);</span>
-<span class="source-line-no">344</span><span id="line-344"> scanner0.close();</span>
-<span class="source-line-no">345</span><span id="line-345"></span>
-<span class="source-line-no">346</span><span id="line-346"> // restore snapshot.</span>
-<span class="source-line-no">347</span><span id="line-347"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
-<span class="source-line-no">348</span><span id="line-348"></span>
-<span class="source-line-no">349</span><span id="line-349"> // scan the snapshot without restoring snapshot</span>
-<span class="source-line-no">350</span><span id="line-350"> TableSnapshotScanner scanner =</span>
-<span class="source-line-no">351</span><span id="line-351"> new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true);</span>
-<span class="source-line-no">352</span><span id="line-352"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">353</span><span id="line-353"> scanner.close();</span>
-<span class="source-line-no">354</span><span id="line-354"></span>
-<span class="source-line-no">355</span><span id="line-355"> // check whether the snapshot has been deleted by the close of scanner.</span>
-<span class="source-line-no">356</span><span id="line-356"> scanner = new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true);</span>
-<span class="source-line-no">357</span><span id="line-357"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">358</span><span id="line-358"> scanner.close();</span>
-<span class="source-line-no">359</span><span id="line-359"></span>
-<span class="source-line-no">360</span><span id="line-360"> // restore snapshot again.</span>
-<span class="source-line-no">361</span><span id="line-361"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
-<span class="source-line-no">362</span><span id="line-362"></span>
-<span class="source-line-no">363</span><span id="line-363"> // check whether the snapshot has been deleted by the close of scanner.</span>
-<span class="source-line-no">364</span><span id="line-364"> scanner = new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true);</span>
-<span class="source-line-no">365</span><span id="line-365"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">366</span><span id="line-366"> scanner.close();</span>
-<span class="source-line-no">367</span><span id="line-367"> } finally {</span>
-<span class="source-line-no">368</span><span id="line-368"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
-<span class="source-line-no">369</span><span id="line-369"> UTIL.deleteTable(tableName);</span>
-<span class="source-line-no">370</span><span id="line-370"> }</span>
-<span class="source-line-no">371</span><span id="line-371"> }</span>
-<span class="source-line-no">372</span><span id="line-372"></span>
-<span class="source-line-no">373</span><span id="line-373"> private void testScanner(HBaseTestingUtil util, String snapshotName, int numRegions,</span>
-<span class="source-line-no">374</span><span id="line-374"> boolean shutdownCluster) throws Exception {</span>
-<span class="source-line-no">375</span><span id="line-375"> TableName tableName = TableName.valueOf("testScanner");</span>
-<span class="source-line-no">376</span><span id="line-376"> try {</span>
-<span class="source-line-no">377</span><span id="line-377"> createTableAndSnapshot(util, tableName, snapshotName, numRegions);</span>
-<span class="source-line-no">378</span><span id="line-378"></span>
-<span class="source-line-no">379</span><span id="line-379"> if (shutdownCluster) {</span>
-<span class="source-line-no">380</span><span id="line-380"> util.shutdownMiniHBaseCluster();</span>
-<span class="source-line-no">381</span><span id="line-381"> clusterUp = false;</span>
-<span class="source-line-no">382</span><span id="line-382"> }</span>
-<span class="source-line-no">383</span><span id="line-383"></span>
-<span class="source-line-no">384</span><span id="line-384"> Path restoreDir = util.getDataTestDirOnTestFS(snapshotName);</span>
-<span class="source-line-no">385</span><span id="line-385"> Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan</span>
-<span class="source-line-no">386</span><span id="line-386"></span>
-<span class="source-line-no">387</span><span id="line-387"> TableSnapshotScanner scanner =</span>
-<span class="source-line-no">388</span><span id="line-388"> new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan);</span>
-<span class="source-line-no">389</span><span id="line-389"></span>
-<span class="source-line-no">390</span><span id="line-390"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">391</span><span id="line-391"> scanner.close();</span>
+<span class="source-line-no">052</span><span id="line-52">import org.apache.hadoop.hbase.snapshot.MobSnapshotTestingUtils;</span>
+<span class="source-line-no">053</span><span id="line-53">import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;</span>
+<span class="source-line-no">054</span><span id="line-54">import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils;</span>
+<span class="source-line-no">055</span><span id="line-55">import org.apache.hadoop.hbase.testclassification.ClientTests;</span>
+<span class="source-line-no">056</span><span id="line-56">import org.apache.hadoop.hbase.testclassification.LargeTests;</span>
+<span class="source-line-no">057</span><span id="line-57">import org.apache.hadoop.hbase.util.Bytes;</span>
+<span class="source-line-no">058</span><span id="line-58">import org.apache.hadoop.hbase.util.CommonFSUtils;</span>
+<span class="source-line-no">059</span><span id="line-59">import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;</span>
+<span class="source-line-no">060</span><span id="line-60">import org.apache.hadoop.hbase.util.FSUtils;</span>
+<span class="source-line-no">061</span><span id="line-61">import org.apache.hadoop.hbase.util.HFileArchiveUtil;</span>
+<span class="source-line-no">062</span><span id="line-62">import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;</span>
+<span class="source-line-no">063</span><span id="line-63">import org.junit.jupiter.api.AfterEach;</span>
+<span class="source-line-no">064</span><span id="line-64">import org.junit.jupiter.api.BeforeEach;</span>
+<span class="source-line-no">065</span><span id="line-65">import org.junit.jupiter.api.Tag;</span>
+<span class="source-line-no">066</span><span id="line-66">import org.junit.jupiter.api.Test;</span>
+<span class="source-line-no">067</span><span id="line-67">import org.junit.jupiter.api.TestInfo;</span>
+<span class="source-line-no">068</span><span id="line-68">import org.slf4j.Logger;</span>
+<span class="source-line-no">069</span><span id="line-69">import org.slf4j.LoggerFactory;</span>
+<span class="source-line-no">070</span><span id="line-70"></span>
+<span class="source-line-no">071</span><span id="line-71">@Tag(LargeTests.TAG)</span>
+<span class="source-line-no">072</span><span id="line-72">@Tag(ClientTests.TAG)</span>
+<span class="source-line-no">073</span><span id="line-73">public class TestTableSnapshotScanner {</span>
+<span class="source-line-no">074</span><span id="line-74"></span>
+<span class="source-line-no">075</span><span id="line-75"> private static final Logger LOG = LoggerFactory.getLogger(TestTableSnapshotScanner.class);</span>
+<span class="source-line-no">076</span><span id="line-76"> private final HBaseTestingUtil UTIL = new HBaseTestingUtil();</span>
+<span class="source-line-no">077</span><span id="line-77"> private static final int NUM_REGION_SERVERS = 2;</span>
+<span class="source-line-no">078</span><span id="line-78"> private static final byte[][] FAMILIES = { Bytes.toBytes("f1"), Bytes.toBytes("f2") };</span>
+<span class="source-line-no">079</span><span id="line-79"> public static byte[] bbb = Bytes.toBytes("bbb");</span>
+<span class="source-line-no">080</span><span id="line-80"> public static byte[] yyy = Bytes.toBytes("yyy");</span>
+<span class="source-line-no">081</span><span id="line-81"></span>
+<span class="source-line-no">082</span><span id="line-82"> private FileSystem fs;</span>
+<span class="source-line-no">083</span><span id="line-83"> private Path rootDir;</span>
+<span class="source-line-no">084</span><span id="line-84"> private boolean clusterUp;</span>
+<span class="source-line-no">085</span><span id="line-85"></span>
+<span class="source-line-no">086</span><span id="line-86"> private String methodName;</span>
+<span class="source-line-no">087</span><span id="line-87"></span>
+<span class="source-line-no">088</span><span id="line-88"> public static void blockUntilSplitFinished(HBaseTestingUtil util, TableName tableName,</span>
+<span class="source-line-no">089</span><span id="line-89"> int expectedRegionSize) throws Exception {</span>
+<span class="source-line-no">090</span><span id="line-90"> for (int i = 0; i < 100; i++) {</span>
+<span class="source-line-no">091</span><span id="line-91"> List<RegionInfo> hRegionInfoList = util.getAdmin().getRegions(tableName);</span>
+<span class="source-line-no">092</span><span id="line-92"> if (hRegionInfoList.size() >= expectedRegionSize) {</span>
+<span class="source-line-no">093</span><span id="line-93"> break;</span>
+<span class="source-line-no">094</span><span id="line-94"> }</span>
+<span class="source-line-no">095</span><span id="line-95"> Thread.sleep(1000);</span>
+<span class="source-line-no">096</span><span id="line-96"> }</span>
+<span class="source-line-no">097</span><span id="line-97"> }</span>
+<span class="source-line-no">098</span><span id="line-98"></span>
+<span class="source-line-no">099</span><span id="line-99"> @BeforeEach</span>
+<span class="source-line-no">100</span><span id="line-100"> public void setupCluster(TestInfo testInfo) throws Exception {</span>
+<span class="source-line-no">101</span><span id="line-101"> methodName = testInfo.getTestMethod().get().getName();</span>
+<span class="source-line-no">102</span><span id="line-102"> setupConf(UTIL.getConfiguration());</span>
+<span class="source-line-no">103</span><span id="line-103"> StartTestingClusterOption option =</span>
+<span class="source-line-no">104</span><span id="line-104"> StartTestingClusterOption.builder().numRegionServers(NUM_REGION_SERVERS)</span>
+<span class="source-line-no">105</span><span id="line-105"> .numDataNodes(NUM_REGION_SERVERS).createRootDir(true).build();</span>
+<span class="source-line-no">106</span><span id="line-106"> UTIL.startMiniCluster(option);</span>
+<span class="source-line-no">107</span><span id="line-107"> clusterUp = true;</span>
+<span class="source-line-no">108</span><span id="line-108"> rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();</span>
+<span class="source-line-no">109</span><span id="line-109"> fs = rootDir.getFileSystem(UTIL.getConfiguration());</span>
+<span class="source-line-no">110</span><span id="line-110"> }</span>
+<span class="source-line-no">111</span><span id="line-111"></span>
+<span class="source-line-no">112</span><span id="line-112"> @AfterEach</span>
+<span class="source-line-no">113</span><span id="line-113"> public void tearDownCluster() throws Exception {</span>
+<span class="source-line-no">114</span><span id="line-114"> if (clusterUp) {</span>
+<span class="source-line-no">115</span><span id="line-115"> UTIL.shutdownMiniCluster();</span>
+<span class="source-line-no">116</span><span id="line-116"> }</span>
+<span class="source-line-no">117</span><span id="line-117"> }</span>
+<span class="source-line-no">118</span><span id="line-118"></span>
+<span class="source-line-no">119</span><span id="line-119"> protected void setupConf(Configuration conf) {</span>
+<span class="source-line-no">120</span><span id="line-120"> // Enable snapshot</span>
+<span class="source-line-no">121</span><span id="line-121"> conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);</span>
+<span class="source-line-no">122</span><span id="line-122"> }</span>
+<span class="source-line-no">123</span><span id="line-123"></span>
+<span class="source-line-no">124</span><span id="line-124"> public static void createTableAndSnapshot(HBaseTestingUtil util, TableName tableName,</span>
+<span class="source-line-no">125</span><span id="line-125"> String snapshotName, int numRegions) throws Exception {</span>
+<span class="source-line-no">126</span><span id="line-126"> try {</span>
+<span class="source-line-no">127</span><span id="line-127"> util.deleteTable(tableName);</span>
+<span class="source-line-no">128</span><span id="line-128"> } catch (Exception ex) {</span>
+<span class="source-line-no">129</span><span id="line-129"> // ignore</span>
+<span class="source-line-no">130</span><span id="line-130"> }</span>
+<span class="source-line-no">131</span><span id="line-131"></span>
+<span class="source-line-no">132</span><span id="line-132"> if (numRegions > 1) {</span>
+<span class="source-line-no">133</span><span id="line-133"> util.createTable(tableName, FAMILIES, 1, bbb, yyy, numRegions);</span>
+<span class="source-line-no">134</span><span id="line-134"> } else {</span>
+<span class="source-line-no">135</span><span id="line-135"> util.createTable(tableName, FAMILIES);</span>
+<span class="source-line-no">136</span><span id="line-136"> }</span>
+<span class="source-line-no">137</span><span id="line-137"> Admin admin = util.getAdmin();</span>
+<span class="source-line-no">138</span><span id="line-138"></span>
+<span class="source-line-no">139</span><span id="line-139"> // put some stuff in the table</span>
+<span class="source-line-no">140</span><span id="line-140"> Table table = util.getConnection().getTable(tableName);</span>
+<span class="source-line-no">141</span><span id="line-141"> util.loadTable(table, FAMILIES);</span>
+<span class="source-line-no">142</span><span id="line-142"></span>
+<span class="source-line-no">143</span><span id="line-143"> Path rootDir = CommonFSUtils.getRootDir(util.getConfiguration());</span>
+<span class="source-line-no">144</span><span id="line-144"> FileSystem fs = rootDir.getFileSystem(util.getConfiguration());</span>
+<span class="source-line-no">145</span><span id="line-145"></span>
+<span class="source-line-no">146</span><span id="line-146"> SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, Arrays.asList(FAMILIES), null,</span>
+<span class="source-line-no">147</span><span id="line-147"> snapshotName, rootDir, fs, true);</span>
+<span class="source-line-no">148</span><span id="line-148"></span>
+<span class="source-line-no">149</span><span id="line-149"> // load different values</span>
+<span class="source-line-no">150</span><span id="line-150"> byte[] value = Bytes.toBytes("after_snapshot_value");</span>
+<span class="source-line-no">151</span><span id="line-151"> util.loadTable(table, FAMILIES, value);</span>
+<span class="source-line-no">152</span><span id="line-152"></span>
+<span class="source-line-no">153</span><span id="line-153"> // cause flush to create new files in the region</span>
+<span class="source-line-no">154</span><span id="line-154"> admin.flush(tableName);</span>
+<span class="source-line-no">155</span><span id="line-155"> table.close();</span>
+<span class="source-line-no">156</span><span id="line-156"> }</span>
+<span class="source-line-no">157</span><span id="line-157"></span>
+<span class="source-line-no">158</span><span id="line-158"> @Test</span>
+<span class="source-line-no">159</span><span id="line-159"> public void testNoDuplicateResultsWhenSplitting() throws Exception {</span>
+<span class="source-line-no">160</span><span id="line-160"> TableName tableName = TableName.valueOf("testNoDuplicateResultsWhenSplitting");</span>
+<span class="source-line-no">161</span><span id="line-161"> String snapshotName = "testSnapshotBug";</span>
+<span class="source-line-no">162</span><span id="line-162"> try {</span>
+<span class="source-line-no">163</span><span id="line-163"> if (UTIL.getAdmin().tableExists(tableName)) {</span>
+<span class="source-line-no">164</span><span id="line-164"> UTIL.deleteTable(tableName);</span>
+<span class="source-line-no">165</span><span id="line-165"> }</span>
+<span class="source-line-no">166</span><span id="line-166"></span>
+<span class="source-line-no">167</span><span id="line-167"> UTIL.createTable(tableName, FAMILIES);</span>
+<span class="source-line-no">168</span><span id="line-168"> Admin admin = UTIL.getAdmin();</span>
+<span class="source-line-no">169</span><span id="line-169"></span>
+<span class="source-line-no">170</span><span id="line-170"> // put some stuff in the table</span>
+<span class="source-line-no">171</span><span id="line-171"> Table table = UTIL.getConnection().getTable(tableName);</span>
+<span class="source-line-no">172</span><span id="line-172"> UTIL.loadTable(table, FAMILIES);</span>
+<span class="source-line-no">173</span><span id="line-173"></span>
+<span class="source-line-no">174</span><span id="line-174"> // split to 2 regions</span>
+<span class="source-line-no">175</span><span id="line-175"> admin.split(tableName, Bytes.toBytes("eee"));</span>
+<span class="source-line-no">176</span><span id="line-176"> blockUntilSplitFinished(UTIL, tableName, 2);</span>
+<span class="source-line-no">177</span><span id="line-177"></span>
+<span class="source-line-no">178</span><span id="line-178"> Path rootDir = CommonFSUtils.getRootDir(UTIL.getConfiguration());</span>
+<span class="source-line-no">179</span><span id="line-179"> FileSystem fs = rootDir.getFileSystem(UTIL.getConfiguration());</span>
+<span class="source-line-no">180</span><span id="line-180"></span>
+<span class="source-line-no">181</span><span id="line-181"> SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, Arrays.asList(FAMILIES),</span>
+<span class="source-line-no">182</span><span id="line-182"> null, snapshotName, rootDir, fs, true);</span>
+<span class="source-line-no">183</span><span id="line-183"></span>
+<span class="source-line-no">184</span><span id="line-184"> // load different values</span>
+<span class="source-line-no">185</span><span id="line-185"> byte[] value = Bytes.toBytes("after_snapshot_value");</span>
+<span class="source-line-no">186</span><span id="line-186"> UTIL.loadTable(table, FAMILIES, value);</span>
+<span class="source-line-no">187</span><span id="line-187"></span>
+<span class="source-line-no">188</span><span id="line-188"> // cause flush to create new files in the region</span>
+<span class="source-line-no">189</span><span id="line-189"> admin.flush(tableName);</span>
+<span class="source-line-no">190</span><span id="line-190"> table.close();</span>
+<span class="source-line-no">191</span><span id="line-191"></span>
+<span class="source-line-no">192</span><span id="line-192"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
+<span class="source-line-no">193</span><span id="line-193"> Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan</span>
+<span class="source-line-no">194</span><span id="line-194"></span>
+<span class="source-line-no">195</span><span id="line-195"> TableSnapshotScanner scanner =</span>
+<span class="source-line-no">196</span><span id="line-196"> new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan);</span>
+<span class="source-line-no">197</span><span id="line-197"></span>
+<span class="source-line-no">198</span><span id="line-198"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">199</span><span id="line-199"> scanner.close();</span>
+<span class="source-line-no">200</span><span id="line-200"> } catch (Exception e) {</span>
+<span class="source-line-no">201</span><span id="line-201"> e.printStackTrace();</span>
+<span class="source-line-no">202</span><span id="line-202"> } finally {</span>
+<span class="source-line-no">203</span><span id="line-203"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
+<span class="source-line-no">204</span><span id="line-204"> UTIL.deleteTable(tableName);</span>
+<span class="source-line-no">205</span><span id="line-205"> }</span>
+<span class="source-line-no">206</span><span id="line-206"> }</span>
+<span class="source-line-no">207</span><span id="line-207"></span>
+<span class="source-line-no">208</span><span id="line-208"> @Test</span>
+<span class="source-line-no">209</span><span id="line-209"> public void testScanLimit() throws Exception {</span>
+<span class="source-line-no">210</span><span id="line-210"> final TableName tableName = TableName.valueOf(methodName);</span>
+<span class="source-line-no">211</span><span id="line-211"> final String snapshotName = tableName + "Snapshot";</span>
+<span class="source-line-no">212</span><span id="line-212"> TableSnapshotScanner scanner = null;</span>
+<span class="source-line-no">213</span><span id="line-213"> try {</span>
+<span class="source-line-no">214</span><span id="line-214"> createTableAndSnapshot(UTIL, tableName, snapshotName, 50);</span>
+<span class="source-line-no">215</span><span id="line-215"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
+<span class="source-line-no">216</span><span id="line-216"> Scan scan = new Scan().withStartRow(bbb).setLimit(100); // limit the scan</span>
+<span class="source-line-no">217</span><span id="line-217"></span>
+<span class="source-line-no">218</span><span id="line-218"> scanner = new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan);</span>
+<span class="source-line-no">219</span><span id="line-219"> int count = 0;</span>
+<span class="source-line-no">220</span><span id="line-220"> while (true) {</span>
+<span class="source-line-no">221</span><span id="line-221"> Result result = scanner.next();</span>
+<span class="source-line-no">222</span><span id="line-222"> if (result == null) {</span>
+<span class="source-line-no">223</span><span id="line-223"> break;</span>
+<span class="source-line-no">224</span><span id="line-224"> }</span>
+<span class="source-line-no">225</span><span id="line-225"> count++;</span>
+<span class="source-line-no">226</span><span id="line-226"> }</span>
+<span class="source-line-no">227</span><span id="line-227"> assertEquals(100, count);</span>
+<span class="source-line-no">228</span><span id="line-228"> } finally {</span>
+<span class="source-line-no">229</span><span id="line-229"> if (scanner != null) {</span>
+<span class="source-line-no">230</span><span id="line-230"> scanner.close();</span>
+<span class="source-line-no">231</span><span id="line-231"> }</span>
+<span class="source-line-no">232</span><span id="line-232"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
+<span class="source-line-no">233</span><span id="line-233"> UTIL.deleteTable(tableName);</span>
+<span class="source-line-no">234</span><span id="line-234"> }</span>
+<span class="source-line-no">235</span><span id="line-235"> }</span>
+<span class="source-line-no">236</span><span id="line-236"></span>
+<span class="source-line-no">237</span><span id="line-237"> @Test</span>
+<span class="source-line-no">238</span><span id="line-238"> public void testWithSingleRegion() throws Exception {</span>
+<span class="source-line-no">239</span><span id="line-239"> testScanner(UTIL, "testWithSingleRegion", 1, false);</span>
+<span class="source-line-no">240</span><span id="line-240"> }</span>
+<span class="source-line-no">241</span><span id="line-241"></span>
+<span class="source-line-no">242</span><span id="line-242"> @Test</span>
+<span class="source-line-no">243</span><span id="line-243"> public void testWithMultiRegion() throws Exception {</span>
+<span class="source-line-no">244</span><span id="line-244"> testScanner(UTIL, "testWithMultiRegion", 10, false);</span>
+<span class="source-line-no">245</span><span id="line-245"> }</span>
+<span class="source-line-no">246</span><span id="line-246"></span>
+<span class="source-line-no">247</span><span id="line-247"> @Test</span>
+<span class="source-line-no">248</span><span id="line-248"> public void testWithOfflineHBaseMultiRegion() throws Exception {</span>
+<span class="source-line-no">249</span><span id="line-249"> testScanner(UTIL, "testWithMultiRegion", 20, true);</span>
+<span class="source-line-no">250</span><span id="line-250"> }</span>
+<span class="source-line-no">251</span><span id="line-251"></span>
+<span class="source-line-no">252</span><span id="line-252"> private ScanMetrics createTableSnapshotScannerAndGetScanMetrics(boolean enableScanMetrics,</span>
+<span class="source-line-no">253</span><span id="line-253"> boolean enableScanMetricsByRegion, byte[] endKey) throws Exception {</span>
+<span class="source-line-no">254</span><span id="line-254"> TableName tableName = TableName.valueOf(methodName + "_TABLE");</span>
+<span class="source-line-no">255</span><span id="line-255"> String snapshotName = methodName + "_SNAPSHOT";</span>
+<span class="source-line-no">256</span><span id="line-256"> try {</span>
+<span class="source-line-no">257</span><span id="line-257"> createTableAndSnapshot(UTIL, tableName, snapshotName, 50);</span>
+<span class="source-line-no">258</span><span id="line-258"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
+<span class="source-line-no">259</span><span id="line-259"> Scan scan = new Scan().withStartRow(bbb).withStopRow(endKey);</span>
+<span class="source-line-no">260</span><span id="line-260"> scan.setScanMetricsEnabled(enableScanMetrics);</span>
+<span class="source-line-no">261</span><span id="line-261"> scan.setEnableScanMetricsByRegion(enableScanMetricsByRegion);</span>
+<span class="source-line-no">262</span><span id="line-262"> Configuration conf = UTIL.getConfiguration();</span>
+<span class="source-line-no">263</span><span id="line-263"></span>
+<span class="source-line-no">264</span><span id="line-264"> TableSnapshotScanner snapshotScanner =</span>
+<span class="source-line-no">265</span><span id="line-265"> new TableSnapshotScanner(conf, restoreDir, snapshotName, scan);</span>
+<span class="source-line-no">266</span><span id="line-266"> verifyScanner(snapshotScanner, bbb, endKey);</span>
+<span class="source-line-no">267</span><span id="line-267"> return snapshotScanner.getScanMetrics();</span>
+<span class="source-line-no">268</span><span id="line-268"> } finally {</span>
+<span class="source-line-no">269</span><span id="line-269"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
+<span class="source-line-no">270</span><span id="line-270"> UTIL.deleteTable(tableName);</span>
+<span class="source-line-no">271</span><span id="line-271"> }</span>
+<span class="source-line-no">272</span><span id="line-272"> }</span>
+<span class="source-line-no">273</span><span id="line-273"></span>
+<span class="source-line-no">274</span><span id="line-274"> @Test</span>
+<span class="source-line-no">275</span><span id="line-275"> public void testScanMetricsDisabled() throws Exception {</span>
+<span class="source-line-no">276</span><span id="line-276"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(false, false, yyy);</span>
+<span class="source-line-no">277</span><span id="line-277"> assertNull(scanMetrics);</span>
+<span class="source-line-no">278</span><span id="line-278"> }</span>
+<span class="source-line-no">279</span><span id="line-279"></span>
+<span class="source-line-no">280</span><span id="line-280"> @Test</span>
+<span class="source-line-no">281</span><span id="line-281"> public void testScanMetricsWithScanMetricsByRegionDisabled() throws Exception {</span>
+<span class="source-line-no">282</span><span id="line-282"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, false, yyy);</span>
+<span class="source-line-no">283</span><span id="line-283"> assertNotNull(scanMetrics);</span>
+<span class="source-line-no">284</span><span id="line-284"> int rowsScanned = 0;</span>
+<span class="source-line-no">285</span><span id="line-285"> for (byte[] row : HBaseTestingUtil.ROWS) {</span>
+<span class="source-line-no">286</span><span id="line-286"> if (Bytes.compareTo(row, bbb) >= 0 && Bytes.compareTo(row, yyy) < 0) {</span>
+<span class="source-line-no">287</span><span id="line-287"> rowsScanned++;</span>
+<span class="source-line-no">288</span><span id="line-288"> }</span>
+<span class="source-line-no">289</span><span id="line-289"> }</span>
+<span class="source-line-no">290</span><span id="line-290"> Map<String, Long> metricsMap = scanMetrics.getMetricsMap();</span>
+<span class="source-line-no">291</span><span id="line-291"> assertEquals(rowsScanned, (long) metricsMap.get(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME));</span>
+<span class="source-line-no">292</span><span id="line-292"> }</span>
+<span class="source-line-no">293</span><span id="line-293"></span>
+<span class="source-line-no">294</span><span id="line-294"> @Test</span>
+<span class="source-line-no">295</span><span id="line-295"> public void testScanMetricsByRegionForSingleRegion() throws Exception {</span>
+<span class="source-line-no">296</span><span id="line-296"> // Scan single row with row key bbb</span>
+<span class="source-line-no">297</span><span id="line-297"> byte[] bbc = Bytes.toBytes("bbc");</span>
+<span class="source-line-no">298</span><span id="line-298"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, true, bbc);</span>
+<span class="source-line-no">299</span><span id="line-299"> assertNotNull(scanMetrics);</span>
+<span class="source-line-no">300</span><span id="line-300"> Map<ScanMetricsRegionInfo, Map<String, Long>> scanMetricsByRegion =</span>
+<span class="source-line-no">301</span><span id="line-301"> scanMetrics.collectMetricsByRegion();</span>
+<span class="source-line-no">302</span><span id="line-302"> assertEquals(1, scanMetricsByRegion.size());</span>
+<span class="source-line-no">303</span><span id="line-303"> for (Map.Entry<ScanMetricsRegionInfo, Map<String, Long>> entry : scanMetricsByRegion</span>
+<span class="source-line-no">304</span><span id="line-304"> .entrySet()) {</span>
+<span class="source-line-no">305</span><span id="line-305"> ScanMetricsRegionInfo scanMetricsRegionInfo = entry.getKey();</span>
+<span class="source-line-no">306</span><span id="line-306"> Map<String, Long> metricsMap = entry.getValue();</span>
+<span class="source-line-no">307</span><span id="line-307"> assertNull(scanMetricsRegionInfo.getServerName());</span>
+<span class="source-line-no">308</span><span id="line-308"> assertNotNull(scanMetricsRegionInfo.getEncodedRegionName());</span>
+<span class="source-line-no">309</span><span id="line-309"> assertEquals(1, (long) metricsMap.get(REGIONS_SCANNED_METRIC_NAME));</span>
+<span class="source-line-no">310</span><span id="line-310"> assertEquals(1, (long) metricsMap.get(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME));</span>
+<span class="source-line-no">311</span><span id="line-311"> }</span>
+<span class="source-line-no">312</span><span id="line-312"> }</span>
+<span class="source-line-no">313</span><span id="line-313"></span>
+<span class="source-line-no">314</span><span id="line-314"> @Test</span>
+<span class="source-line-no">315</span><span id="line-315"> public void testScanMetricsByRegionForMultiRegion() throws Exception {</span>
+<span class="source-line-no">316</span><span id="line-316"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, true, yyy);</span>
+<span class="source-line-no">317</span><span id="line-317"> assertNotNull(scanMetrics);</span>
+<span class="source-line-no">318</span><span id="line-318"> Map<ScanMetricsRegionInfo, Map<String, Long>> scanMetricsByRegion =</span>
+<span class="source-line-no">319</span><span id="line-319"> scanMetrics.collectMetricsByRegion();</span>
+<span class="source-line-no">320</span><span id="line-320"> for (Map.Entry<ScanMetricsRegionInfo, Map<String, Long>> entry : scanMetricsByRegion</span>
+<span class="source-line-no">321</span><span id="line-321"> .entrySet()) {</span>
+<span class="source-line-no">322</span><span id="line-322"> ScanMetricsRegionInfo scanMetricsRegionInfo = entry.getKey();</span>
+<span class="source-line-no">323</span><span id="line-323"> Map<String, Long> metricsMap = entry.getValue();</span>
+<span class="source-line-no">324</span><span id="line-324"> assertNull(scanMetricsRegionInfo.getServerName());</span>
+<span class="source-line-no">325</span><span id="line-325"> assertNotNull(scanMetricsRegionInfo.getEncodedRegionName());</span>
+<span class="source-line-no">326</span><span id="line-326"> assertEquals(1, (long) metricsMap.get(REGIONS_SCANNED_METRIC_NAME));</span>
+<span class="source-line-no">327</span><span id="line-327"> }</span>
+<span class="source-line-no">328</span><span id="line-328"> }</span>
+<span class="source-line-no">329</span><span id="line-329"></span>
+<span class="source-line-no">330</span><span id="line-330"> @Test</span>
+<span class="source-line-no">331</span><span id="line-331"> public void testScannerWithRestoreScanner() throws Exception {</span>
+<span class="source-line-no">332</span><span id="line-332"> TableName tableName = TableName.valueOf("testScanner");</span>
+<span class="source-line-no">333</span><span id="line-333"> String snapshotName = "testScannerWithRestoreScanner";</span>
+<span class="source-line-no">334</span><span id="line-334"> try {</span>
+<span class="source-line-no">335</span><span id="line-335"> createTableAndSnapshot(UTIL, tableName, snapshotName, 50);</span>
+<span class="source-line-no">336</span><span id="line-336"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
+<span class="source-line-no">337</span><span id="line-337"> Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan</span>
+<span class="source-line-no">338</span><span id="line-338"></span>
+<span class="source-line-no">339</span><span id="line-339"> Configuration conf = UTIL.getConfiguration();</span>
+<span class="source-line-no">340</span><span id="line-340"> Path rootDir = CommonFSUtils.getRootDir(conf);</span>
+<span class="source-line-no">341</span><span id="line-341"></span>
+<span class="source-line-no">342</span><span id="line-342"> TableSnapshotScanner scanner0 =</span>
+<span class="source-line-no">343</span><span id="line-343"> new TableSnapshotScanner(conf, restoreDir, snapshotName, scan);</span>
+<span class="source-line-no">344</span><span id="line-344"> verifyScanner(scanner0, bbb, yyy);</span>
+<span class="source-line-no">345</span><span id="line-345"> scanner0.close();</span>
+<span class="source-line-no">346</span><span id="line-346"></span>
+<span class="source-line-no">347</span><span id="line-347"> // restore snapshot.</span>
+<span class="source-line-no">348</span><span id="line-348"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
+<span class="source-line-no">349</span><span id="line-349"></span>
+<span class="source-line-no">350</span><span id="line-350"> // scan the snapshot without restoring snapshot</span>
+<span class="source-line-no">351</span><span id="line-351"> TableSnapshotScanner scanner =</span>
+<span class="source-line-no">352</span><span id="line-352"> new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true);</span>
+<span class="source-line-no">353</span><span id="line-353"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">354</span><span id="line-354"> scanner.close();</span>
+<span class="source-line-no">355</span><span id="line-355"></span>
+<span class="source-line-no">356</span><span id="line-356"> // check whether the snapshot has been deleted by the close of scanner.</span>
+<span class="source-line-no">357</span><span id="line-357"> scanner = new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true);</span>
+<span class="source-line-no">358</span><span id="line-358"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">359</span><span id="line-359"> scanner.close();</span>
+<span class="source-line-no">360</span><span id="line-360"></span>
+<span class="source-line-no">361</span><span id="line-361"> // restore snapshot again.</span>
+<span class="source-line-no">362</span><span id="line-362"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
+<span class="source-line-no">363</span><span id="line-363"></span>
+<span class="source-line-no">364</span><span id="line-364"> // check whether the snapshot has been deleted by the close of scanner.</span>
+<span class="source-line-no">365</span><span id="line-365"> scanner = new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true);</span>
+<span class="source-line-no">366</span><span id="line-366"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">367</span><span id="line-367"> scanner.close();</span>
+<span class="source-line-no">368</span><span id="line-368"> } finally {</span>
+<span class="source-line-no">369</span><span id="line-369"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
+<span class="source-line-no">370</span><span id="line-370"> UTIL.deleteTable(tableName);</span>
+<span class="source-line-no">371</span><span id="line-371"> }</span>
+<span class="source-line-no">372</span><span id="line-372"> }</span>
+<span class="source-line-no">373</span><span id="line-373"></span>
+<span class="source-line-no">374</span><span id="line-374"> @Test</span>
+<span class="source-line-no">375</span><span id="line-375"> public void testScannerWithRestoredMobSnapshot() throws Exception {</span>
+<span class="source-line-no">376</span><span id="line-376"> TableName tableName = TableName.valueOf(methodName);</span>
+<span class="source-line-no">377</span><span id="line-377"> String snapshotName = methodName + "Snapshot";</span>
+<span class="source-line-no">378</span><span id="line-378"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
+<span class="source-line-no">379</span><span id="line-379"> try {</span>
+<span class="source-line-no">380</span><span id="line-380"> MobSnapshotTestingUtils.createMobTable(UTIL, tableName, new byte[0][], 1, FAMILIES);</span>
+<span class="source-line-no">381</span><span id="line-381"> try (Table table = UTIL.getConnection().getTable(tableName)) {</span>
+<span class="source-line-no">382</span><span id="line-382"> UTIL.loadTable(table, FAMILIES);</span>
+<span class="source-line-no">383</span><span id="line-383"> }</span>
+<span class="source-line-no">384</span><span id="line-384"> UTIL.getAdmin().snapshot(snapshotName, tableName);</span>
+<span class="source-line-no">385</span><span id="line-385"></span>
+<span class="source-line-no">386</span><span id="line-386"> Configuration conf = UTIL.getConfiguration();</span>
+<span class="source-line-no">387</span><span id="line-387"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
+<span class="source-line-no">388</span><span id="line-388"> try (TableSnapshotScanner scanner = new TableSnapshotScanner(conf, rootDir, restoreDir,</span>
+<span class="source-line-no">389</span><span id="line-389"> snapshotName, new Scan().withStartRow(Bytes.toBytes("zzzz")), true)) {</span>
+<span class="source-line-no">390</span><span id="line-390"> assertNull(scanner.next());</span>
+<span class="source-line-no">391</span><span id="line-391"> }</span>
<span class="source-line-no">392</span><span id="line-392"> } finally {</span>
-<span class="source-line-no">393</span><span id="line-393"> if (clusterUp) {</span>
-<span class="source-line-no">394</span><span id="line-394"> util.getAdmin().deleteSnapshot(snapshotName);</span>
-<span class="source-line-no">395</span><span id="line-395"> util.deleteTable(tableName);</span>
-<span class="source-line-no">396</span><span id="line-396"> }</span>
-<span class="source-line-no">397</span><span id="line-397"> }</span>
-<span class="source-line-no">398</span><span id="line-398"> }</span>
-<span class="source-line-no">399</span><span id="line-399"></span>
-<span class="source-line-no">400</span><span id="line-400"> private void verifyScanner(ResultScanner scanner, byte[] startRow, byte[] stopRow)</span>
-<span class="source-line-no">401</span><span id="line-401"> throws IOException, InterruptedException {</span>
-<span class="source-line-no">402</span><span id="line-402"></span>
-<span class="source-line-no">403</span><span id="line-403"> HBaseTestingUtil.SeenRowTracker rowTracker =</span>
-<span class="source-line-no">404</span><span id="line-404"> new HBaseTestingUtil.SeenRowTracker(startRow, stopRow);</span>
-<span class="source-line-no">405</span><span id="line-405"></span>
-<span class="source-line-no">406</span><span id="line-406"> while (true) {</span>
-<span class="source-line-no">407</span><span id="line-407"> Result result = scanner.next();</span>
-<span class="source-line-no">408</span><span id="line-408"> if (result == null) {</span>
-<span class="source-line-no">409</span><span id="line-409"> break;</span>
-<span class="source-line-no">410</span><span id="line-410"> }</span>
-<span class="source-line-no">411</span><span id="line-411"> verifyRow(result);</span>
-<span class="source-line-no">412</span><span id="line-412"> rowTracker.addRow(result.getRow());</span>
-<span class="source-line-no">413</span><span id="line-413"> }</span>
-<span class="source-line-no">414</span><span id="line-414"></span>
-<span class="source-line-no">415</span><span id="line-415"> // validate all rows are seen</span>
-<span class="source-line-no">416</span><span id="line-416"> rowTracker.validate();</span>
-<span class="source-line-no">417</span><span id="line-417"> }</span>
-<span class="source-line-no">418</span><span id="line-418"></span>
-<span class="source-line-no">419</span><span id="line-419"> private static void verifyRow(Result result) throws IOException {</span>
-<span class="source-line-no">420</span><span id="line-420"> byte[] row = result.getRow();</span>
-<span class="source-line-no">421</span><span id="line-421"> CellScanner scanner = result.cellScanner();</span>
-<span class="source-line-no">422</span><span id="line-422"> while (scanner.advance()) {</span>
-<span class="source-line-no">423</span><span id="line-423"> Cell cell = scanner.current();</span>
-<span class="source-line-no">424</span><span id="line-424"></span>
-<span class="source-line-no">425</span><span id="line-425"> // assert that all Cells in the Result have the same key</span>
-<span class="source-line-no">426</span><span id="line-426"> assertEquals(0, Bytes.compareTo(row, 0, row.length, cell.getRowArray(), cell.getRowOffset(),</span>
-<span class="source-line-no">427</span><span id="line-427"> cell.getRowLength()));</span>
-<span class="source-line-no">428</span><span id="line-428"> }</span>
-<span class="source-line-no">429</span><span id="line-429"></span>
-<span class="source-line-no">430</span><span id="line-430"> for (int j = 0; j < FAMILIES.length; j++) {</span>
-<span class="source-line-no">431</span><span id="line-431"> byte[] actual = result.getValue(FAMILIES[j], FAMILIES[j]);</span>
-<span class="source-line-no">432</span><span id="line-432"> assertArrayEquals(row, actual, "Row in snapshot does not match, expected:"</span>
-<span class="source-line-no">433</span><span id="line-433"> + Bytes.toString(row) + " ,actual:" + Bytes.toString(actual));</span>
-<span class="source-line-no">434</span><span id="line-434"> }</span>
-<span class="source-line-no">435</span><span id="line-435"> }</span>
-<span class="source-line-no">436</span><span id="line-436"></span>
-<span class="source-line-no">437</span><span id="line-437"> @Test</span>
-<span class="source-line-no">438</span><span id="line-438"> public void testMergeRegion() throws Exception {</span>
-<span class="source-line-no">439</span><span id="line-439"> TableName tableName = TableName.valueOf("testMergeRegion");</span>
-<span class="source-line-no">440</span><span id="line-440"> String snapshotName = tableName.getNameAsString() + "_snapshot";</span>
-<span class="source-line-no">441</span><span id="line-441"> Configuration conf = UTIL.getConfiguration();</span>
-<span class="source-line-no">442</span><span id="line-442"> Path rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();</span>
-<span class="source-line-no">443</span><span id="line-443"> long timeout = 20000; // 20s</span>
-<span class="source-line-no">444</span><span id="line-444"> try (Admin admin = UTIL.getAdmin()) {</span>
-<span class="source-line-no">445</span><span id="line-445"> List<String> serverList = admin.getRegionServers().stream().map(sn -> sn.getServerName())</span>
-<span class="source-line-no">446</span><span id="line-446"> .collect(Collectors.toList());</span>
-<span class="source-line-no">447</span><span id="line-447"> // create table with 3 regions</span>
-<span class="source-line-no">448</span><span id="line-448"> Table table = UTIL.createTable(tableName, FAMILIES, 1, bbb, yyy, 3);</span>
-<span class="source-line-no">449</span><span id="line-449"> List<RegionInfo> regions = admin.getRegions(tableName);</span>
-<span class="source-line-no">450</span><span id="line-450"> assertEquals(3, regions.size());</span>
-<span class="source-line-no">451</span><span id="line-451"> RegionInfo region0 = regions.get(0);</span>
-<span class="source-line-no">452</span><span id="line-452"> RegionInfo region1 = regions.get(1);</span>
-<span class="source-line-no">453</span><span id="line-453"> RegionInfo region2 = regions.get(2);</span>
-<span class="source-line-no">454</span><span id="line-454"> // put some data in the table</span>
-<span class="source-line-no">455</span><span id="line-455"> UTIL.loadTable(table, FAMILIES);</span>
-<span class="source-line-no">456</span><span id="line-456"> admin.flush(tableName);</span>
-<span class="source-line-no">457</span><span id="line-457"> // wait flush is finished</span>
-<span class="source-line-no">458</span><span id="line-458"> UTIL.waitFor(timeout, () -> {</span>
-<span class="source-line-no">459</span><span id="line-459"> try {</span>
-<span class="source-line-no">460</span><span id="line-460"> Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName);</span>
-<span class="source-line-no">461</span><span id="line-461"> for (RegionInfo region : regions) {</span>
-<span class="source-line-no">462</span><span id="line-462"> Path regionDir = new Path(tableDir, region.getEncodedName());</span>
-<span class="source-line-no">463</span><span id="line-463"> for (Path familyDir : FSUtils.getFamilyDirs(fs, regionDir)) {</span>
-<span class="source-line-no">464</span><span id="line-464"> for (FileStatus fs : fs.listStatus(familyDir)) {</span>
-<span class="source-line-no">465</span><span id="line-465"> if (!fs.getPath().getName().equals(".filelist")) {</span>
-<span class="source-line-no">466</span><span id="line-466"> return true;</span>
-<span class="source-line-no">467</span><span id="line-467"> }</span>
-<span class="source-line-no">468</span><span id="line-468"> }</span>
-<span class="source-line-no">469</span><span id="line-469"> return false;</span>
-<span class="source-line-no">470</span><span id="line-470"> }</span>
-<span class="source-line-no">471</span><span id="line-471"> }</span>
-<span class="source-line-no">472</span><span id="line-472"> return true;</span>
-<span class="source-line-no">473</span><span id="line-473"> } catch (IOException e) {</span>
-<span class="source-line-no">474</span><span id="line-474"> LOG.warn("Failed check if flush is finished", e);</span>
-<span class="source-line-no">475</span><span id="line-475"> return false;</span>
-<span class="source-line-no">476</span><span id="line-476"> }</span>
-<span class="source-line-no">477</span><span id="line-477"> });</span>
-<span class="source-line-no">478</span><span id="line-478"> // merge 2 regions</span>
-<span class="source-line-no">479</span><span id="line-479"> admin.compactionSwitch(false, serverList);</span>
-<span class="source-line-no">480</span><span id="line-480"> admin.mergeRegionsAsync(region0.getEncodedNameAsBytes(), region1.getEncodedNameAsBytes(),</span>
-<span class="source-line-no">481</span><span id="line-481"> true);</span>
-<span class="source-line-no">482</span><span id="line-482"> UTIL.waitFor(timeout, () -> admin.getRegions(tableName).size() == 2);</span>
-<span class="source-line-no">483</span><span id="line-483"> List<RegionInfo> mergedRegions = admin.getRegions(tableName);</span>
-<span class="source-line-no">484</span><span id="line-484"> RegionInfo mergedRegion =</span>
-<span class="source-line-no">485</span><span id="line-485"> mergedRegions.get(0).getEncodedName().equals(region2.getEncodedName())</span>
-<span class="source-line-no">486</span><span id="line-486"> ? mergedRegions.get(1)</span>
-<span class="source-line-no">487</span><span id="line-487"> : mergedRegions.get(0);</span>
-<span class="source-line-no">488</span><span id="line-488"> // snapshot</span>
-<span class="source-line-no">489</span><span id="line-489"> admin.snapshot(snapshotName, tableName);</span>
-<span class="source-line-no">490</span><span id="line-490"> assertEquals(1, admin.listSnapshots().size());</span>
-<span class="source-line-no">491</span><span id="line-491"> // major compact</span>
-<span class="source-line-no">492</span><span id="line-492"> admin.compactionSwitch(true, serverList);</span>
-<span class="source-line-no">493</span><span id="line-493"> admin.majorCompactRegion(mergedRegion.getRegionName());</span>
-<span class="source-line-no">494</span><span id="line-494"> // wait until merged region has no reference</span>
-<span class="source-line-no">495</span><span id="line-495"> UTIL.waitFor(timeout, () -> {</span>
-<span class="source-line-no">496</span><span id="line-496"> try {</span>
-<span class="source-line-no">497</span><span id="line-497"> for (RegionServerThread regionServerThread : UTIL.getMiniHBaseCluster()</span>
-<span class="source-line-no">498</span><span id="line-498"> .getRegionServerThreads()) {</span>
-<span class="source-line-no">499</span><span id="line-499"> HRegionServer regionServer = regionServerThread.getRegionServer();</span>
-<span class="source-line-no">500</span><span id="line-500"> for (HRegion subRegion : regionServer.getRegions(tableName)) {</span>
-<span class="source-line-no">501</span><span id="line-501"> if (</span>
-<span class="source-line-no">502</span><span id="line-502"> subRegion.getRegionInfo().getEncodedName().equals(mergedRegion.getEncodedName())</span>
-<span class="source-line-no">503</span><span id="line-503"> ) {</span>
-<span class="source-line-no">504</span><span id="line-504"> regionServer.getCompactedHFilesDischarger().chore();</span>
-<span class="source-line-no">505</span><span id="line-505"> }</span>
-<span class="source-line-no">506</span><span id="line-506"> }</span>
-<span class="source-line-no">507</span><span id="line-507"> }</span>
-<span class="source-line-no">508</span><span id="line-508"> Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName);</span>
-<span class="source-line-no">509</span><span id="line-509"> HRegionFileSystem regionFs = HRegionFileSystem</span>
-<span class="source-line-no">510</span><span id="line-510"> .openRegionFromFileSystem(UTIL.getConfiguration(), fs, tableDir, mergedRegion, true);</span>
-<span class="source-line-no">511</span><span id="line-511"> boolean references = false;</span>
-<span class="source-line-no">512</span><span id="line-512"> Path regionDir = new Path(tableDir, mergedRegion.getEncodedName());</span>
-<span class="source-line-no">513</span><span id="line-513"> for (Path familyDir : FSUtils.getFamilyDirs(fs, regionDir)) {</span>
-<span class="source-line-no">514</span><span id="line-514"> StoreContext storeContext = StoreContext.getBuilder()</span>
-<span class="source-line-no">515</span><span id="line-515"> .withColumnFamilyDescriptor(ColumnFamilyDescriptorBuilder.of(familyDir.getName()))</span>
-<span class="source-line-no">516</span><span id="line-516"> .withRegionFileSystem(regionFs).withFamilyStoreDirectoryPath(familyDir).build();</span>
-<span class="source-line-no">517</span><span id="line-517"> StoreFileTracker sft =</span>
-<span class="source-line-no">518</span><span id="line-518"> StoreFileTrackerFactory.create(UTIL.getConfiguration(), false, storeContext);</span>
-<span class="source-line-no">519</span><span id="line-519"> references = references || sft.hasReferences();</span>
-<span class="source-line-no">520</span><span id="line-520"> if (references) {</span>
-<span class="source-line-no">521</span><span id="line-521"> break;</span>
-<span class="source-line-no">522</span><span id="line-522"> }</span>
-<span class="source-line-no">523</span><span id="line-523"> }</span>
-<span class="source-line-no">524</span><span id="line-524"> return !references;</span>
-<span class="source-line-no">525</span><span id="line-525"> } catch (IOException e) {</span>
-<span class="source-line-no">526</span><span id="line-526"> LOG.warn("Failed check merged region has no reference", e);</span>
-<span class="source-line-no">527</span><span id="line-527"> return false;</span>
-<span class="source-line-no">528</span><span id="line-528"> }</span>
-<span class="source-line-no">529</span><span id="line-529"> });</span>
-<span class="source-line-no">530</span><span id="line-530"> // run catalog janitor to clean and wait for parent regions are archived</span>
-<span class="source-line-no">531</span><span id="line-531"> UTIL.getMiniHBaseCluster().getMaster().getCatalogJanitor().choreForTesting();</span>
-<span class="source-line-no">532</span><span id="line-532"> UTIL.waitFor(timeout, () -> {</span>
-<span class="source-line-no">533</span><span id="line-533"> try {</span>
+<span class="source-line-no">393</span><span id="line-393"> fs.delete(restoreDir, true);</span>
+<span class="source-line-no">394</span><span id="line-394"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
+<span class="source-line-no">395</span><span id="line-395"> UTIL.deleteTable(tableName);</span>
+<span class="source-line-no">396</span><span id="line-396"> }</span>
+<span class="source-line-no">397</span><span id="line-397"> }</span>
+<span class="source-line-no">398</span><span id="line-398"></span>
+<span class="source-line-no">399</span><span id="line-399"> private void testScanner(HBaseTestingUtil util, String snapshotName, int numRegions,</span>
+<span class="source-line-no">400</span><span id="line-400"> boolean shutdownCluster) throws Exception {</span>
+<span class="source-line-no">401</span><span id="line-401"> TableName tableName = TableName.valueOf("testScanner");</span>
+<span class="source-line-no">402</span><span id="line-402"> try {</span>
+<span class="source-line-no">403</span><span id="line-403"> createTableAndSnapshot(util, tableName, snapshotName, numRegions);</span>
+<span class="source-line-no">404</span><span id="line-404"></span>
+<span class="source-line-no">405</span><span id="line-405"> if (shutdownCluster) {</span>
+<span class="source-line-no">406</span><span id="line-406"> util.shutdownMiniHBaseCluster();</span>
+<span class="source-line-no">407</span><span id="line-407"> clusterUp = false;</span>
+<span class="source-line-no">408</span><span id="line-408"> }</span>
+<span class="source-line-no">409</span><span id="line-409"></span>
+<span class="source-line-no">410</span><span id="line-410"> Path restoreDir = util.getDataTestDirOnTestFS(snapshotName);</span>
+<span class="source-line-no">411</span><span id="line-411"> Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan</span>
+<span class="source-line-no">412</span><span id="line-412"></span>
+<span class="source-line-no">413</span><span id="line-413"> TableSnapshotScanner scanner =</span>
+<span class="source-line-no">414</span><span id="line-414"> new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan);</span>
+<span class="source-line-no">415</span><span id="line-415"></span>
+<span class="source-line-no">416</span><span id="line-416"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">417</span><span id="line-417"> scanner.close();</span>
+<span class="source-line-no">418</span><span id="line-418"> } finally {</span>
+<span class="source-line-no">419</span><span id="line-419"> if (clusterUp) {</span>
+<span class="source-line-no">420</span><span id="line-420"> util.getAdmin().deleteSnapshot(snapshotName);</span>
+<span class="source-line-no">421</span><span id="line-421"> util.deleteTable(tableName);</span>
+<span class="source-line-no">422</span><span id="line-422"> }</span>
+<span class="source-line-no">423</span><span id="line-423"> }</span>
+<span class="source-line-no">424</span><span id="line-424"> }</span>
+<span class="source-line-no">425</span><span id="line-425"></span>
+<span class="source-line-no">426</span><span id="line-426"> private void verifyScanner(ResultScanner scanner, byte[] startRow, byte[] stopRow)</span>
+<span class="source-line-no">427</span><span id="line-427"> throws IOException, InterruptedException {</span>
+<span class="source-line-no">428</span><span id="line-428"></span>
+<span class="source-line-no">429</span><span id="line-429"> HBaseTestingUtil.SeenRowTracker rowTracker =</span>
+<span class="source-line-no">430</span><span id="line-430"> new HBaseTestingUtil.SeenRowTracker(startRow, stopRow);</span>
+<span class="source-line-no">431</span><span id="line-431"></span>
+<span class="source-line-no">432</span><span id="line-432"> while (true) {</span>
+<span class="source-line-no">433</span><span id="line-433"> Result result = scanner.next();</span>
+<span class="source-line-no">434</span><span id="line-434"> if (result == null) {</span>
+<span class="source-line-no">435</span><span id="line-435"> break;</span>
+<span class="source-line-no">436</span><span id="line-436"> }</span>
+<span class="source-line-no">437</span><span id="line-437"> verifyRow(result);</span>
+<span class="source-line-no">438</span><span id="line-438"> rowTracker.addRow(result.getRow());</span>
+<span class="source-line-no">439</span><span id="line-439"> }</span>
+<span class="source-line-no">440</span><span id="line-440"></span>
+<span class="source-line-no">441</span><span id="line-441"> // validate all rows are seen</span>
+<span class="source-line-no">442</span><span id="line-442"> rowTracker.validate();</span>
+<span class="source-line-no">443</span><span id="line-443"> }</span>
+<span class="source-line-no">444</span><span id="line-444"></span>
+<span class="source-line-no">445</span><span id="line-445"> private static void verifyRow(Result result) throws IOException {</span>
+<span class="source-line-no">446</span><span id="line-446"> byte[] row = result.getRow();</span>
+<span class="source-line-no">447</span><span id="line-447"> CellScanner scanner = result.cellScanner();</span>
+<span class="source-line-no">448</span><span id="line-448"> while (scanner.advance()) {</span>
+<span class="source-line-no">449</span><span id="line-449"> Cell cell = scanner.current();</span>
+<span class="source-line-no">450</span><span id="line-450"></span>
+<span class="source-line-no">451</span><span id="line-451"> // assert that all Cells in the Result have the same key</span>
+<span class="source-line-no">452</span><span id="line-452"> assertEquals(0, Bytes.compareTo(row, 0, row.length, cell.getRowArray(), cell.getRowOffset(),</span>
+<span class="source-line-no">453</span><span id="line-453"> cell.getRowLength()));</span>
+<span class="source-line-no">454</span><span id="line-454"> }</span>
+<span class="source-line-no">455</span><span id="line-455"></span>
+<span class="source-line-no">456</span><span id="line-456"> for (int j = 0; j < FAMILIES.length; j++) {</span>
+<span class="source-line-no">457</span><span id="line-457"> byte[] actual = result.getValue(FAMILIES[j], FAMILIES[j]);</span>
+<span class="source-line-no">458</span><span id="line-458"> assertArrayEquals(row, actual, "Row in snapshot does not match, expected:"</span>
+<span class="source-line-no">459</span><span id="line-459"> + Bytes.toString(row) + " ,actual:" + Bytes.toString(actual));</span>
+<span class="source-line-no">460</span><span id="line-460"> }</span>
+<span class="source-line-no">461</span><span id="line-461"> }</span>
+<span class="source-line-no">462</span><span id="line-462"></span>
+<span class="source-line-no">463</span><span id="line-463"> @Test</span>
+<span class="source-line-no">464</span><span id="line-464"> public void testMergeRegion() throws Exception {</span>
+<span class="source-line-no">465</span><span id="line-465"> TableName tableName = TableName.valueOf("testMergeRegion");</span>
+<span class="source-line-no">466</span><span id="line-466"> String snapshotName = tableName.getNameAsString() + "_snapshot";</span>
+<span class="source-line-no">467</span><span id="line-467"> Configuration conf = UTIL.getConfiguration();</span>
+<span class="source-line-no">468</span><span id="line-468"> Path rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();</span>
+<span class="source-line-no">469</span><span id="line-469"> long timeout = 20000; // 20s</span>
+<span class="source-line-no">470</span><span id="line-470"> try (Admin admin = UTIL.getAdmin()) {</span>
+<span class="source-line-no">471</span><span id="line-471"> List<String> serverList = admin.getRegionServers().stream().map(sn -> sn.getServerName())</span>
+<span class="source-line-no">472</span><span id="line-472"> .collect(Collectors.toList());</span>
+<span class="source-line-no">473</span><span id="line-473"> // create table with 3 regions</span>
+<span class="source-line-no">474</span><span id="line-474"> Table table = UTIL.createTable(tableName, FAMILIES, 1, bbb, yyy, 3);</span>
+<span class="source-line-no">475</span><span id="line-475"> List<RegionInfo> regions = admin.getRegions(tableName);</span>
+<span class="source-line-no">476</span><span id="line-476"> assertEquals(3, regions.size());</span>
+<span class="source-line-no">477</span><span id="line-477"> RegionInfo region0 = regions.get(0);</span>
+<span class="source-line-no">478</span><span id="line-478"> RegionInfo region1 = regions.get(1);</span>
+<span class="source-line-no">479</span><span id="line-479"> RegionInfo region2 = regions.get(2);</span>
+<span class="source-line-no">480</span><span id="line-480"> // put some data in the table</span>
+<span class="source-line-no">481</span><span id="line-481"> UTIL.loadTable(table, FAMILIES);</span>
+<span class="source-line-no">482</span><span id="line-482"> admin.flush(tableName);</span>
+<span class="source-line-no">483</span><span id="line-483"> // wait flush is finished</span>
+<span class="source-line-no">484</span><span id="line-484"> UTIL.waitFor(timeout, () -> {</span>
+<span class="source-line-no">485</span><span id="line-485"> try {</span>
+<span class="source-line-no">486</span><span id="line-486"> Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName);</span>
+<span class="source-line-no">487</span><span id="line-487"> for (RegionInfo region : regions) {</span>
+<span class="source-line-no">488</span><span id="line-488"> Path regionDir = new Path(tableDir, region.getEncodedName());</span>
+<span class="source-line-no">489</span><span id="line-489"> for (Path familyDir : FSUtils.getFamilyDirs(fs, regionDir)) {</span>
+<span class="source-line-no">490</span><span id="line-490"> for (FileStatus fs : fs.listStatus(familyDir)) {</span>
+<span class="source-line-no">491</span><span id="line-491"> if (!fs.getPath().getName().equals(".filelist")) {</span>
+<span class="source-line-no">492</span><span id="line-492"> return true;</span>
+<span class="source-line-no">493</span><span id="line-493"> }</span>
+<span class="source-line-no">494</span><span id="line-494"> }</span>
+<span class="source-line-no">495</span><span id="line-495"> return false;</span>
+<span class="source-line-no">496</span><span id="line-496"> }</span>
+<span class="source-line-no">497</span><span id="line-497"> }</span>
+<span class="source-line-no">498</span><span id="line-498"> return true;</span>
+<span class="source-line-no">499</span><span id="line-499"> } catch (IOException e) {</span>
+<span class="source-line-no">500</span><span id="line-500"> LOG.warn("Failed check if flush is finished", e);</span>
+<span class="source-line-no">501</span><span id="line-501"> return false;</span>
+<span class="source-line-no">502</span><span id="line-502"> }</span>
+<span class="source-line-no">503</span><span id="line-503"> });</span>
+<span class="source-line-no">504</span><span id="line-504"> // merge 2 regions</span>
+<span class="source-line-no">505</span><span id="line-505"> admin.compactionSwitch(false, serverList);</span>
+<span class="source-line-no">506</span><span id="line-506"> admin.mergeRegionsAsync(region0.getEncodedNameAsBytes(), region1.getEncodedNameAsBytes(),</span>
+<span class="source-line-no">507</span><span id="line-507"> true);</span>
+<span class="source-line-no">508</span><span id="line-508"> UTIL.waitFor(timeout, () -> admin.getRegions(tableName).size() == 2);</span>
+<span class="source-line-no">509</span><span id="line-509"> List<RegionInfo> mergedRegions = admin.getRegions(tableName);</span>
+<span class="source-line-no">510</span><span id="line-510"> RegionInfo mergedRegion =</span>
+<span class="source-line-no">511</span><span id="line-511"> mergedRegions.get(0).getEncodedName().equals(region2.getEncodedName())</span>
+<span class="source-line-no">512</span><span id="line-512"> ? mergedRegions.get(1)</span>
+<span class="source-line-no">513</span><span id="line-513"> : mergedRegions.get(0);</span>
+<span class="source-line-no">514</span><span id="line-514"> // snapshot</span>
+<span class="source-line-no">515</span><span id="line-515"> admin.snapshot(snapshotName, tableName);</span>
+<span class="source-line-no">516</span><span id="line-516"> assertEquals(1, admin.listSnapshots().size());</span>
+<span class="source-line-no">517</span><span id="line-517"> // major compact</span>
+<span class="source-line-no">518</span><span id="line-518"> admin.compactionSwitch(true, serverList);</span>
+<span class="source-line-no">519</span><span id="line-519"> admin.majorCompactRegion(mergedRegion.getRegionName());</span>
+<span class="source-line-no">520</span><span id="line-520"> // wait until merged region has no reference</span>
+<span class="source-line-no">521</span><span id="line-521"> UTIL.waitFor(timeout, () -> {</span>
+<span class="source-line-no">522</span><span id="line-522"> try {</span>
+<span class="source-line-no">523</span><span id="line-523"> for (RegionServerThread regionServerThread : UTIL.getMiniHBaseCluster()</span>
+<span class="source-line-no">524</span><span id="line-524"> .getRegionServerThreads()) {</span>
+<span class="source-line-no">525</span><span id="line-525"> HRegionServer regionServer = regionServerThread.getRegionServer();</span>
+<span class="source-line-no">526</span><span id="line-526"> for (HRegion subRegion : regionServer.getRegions(tableName)) {</span>
+<span class="source-line-no">527</span><span id="line-527"> if (</span>
+<span class="source-line-no">528</span><span id="line-528"> subRegion.getRegionInfo().getEncodedName().equals(mergedRegion.getEncodedName())</span>
+<span class="source-line-no">529</span><span id="line-529"> ) {</span>
+<span class="source-line-no">530</span><span id="line-530"> regionServer.getCompactedHFilesDischarger().chore();</span>
+<span class="source-line-no">531</span><span id="line-531"> }</span>
+<span class="source-line-no">532</span><span id="line-532"> }</span>
+<span class="source-line-no">533</span><span id="line-533"> }</span>
<span class="source-line-no">534</span><span id="line-534"> Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName);</span>
-<span class="source-line-no">535</span><span id="line-535"> for (FileStatus fileStatus : fs.listStatus(tableDir)) {</span>
-<span class="source-line-no">536</span><span id="line-536"> String name = fileStatus.getPath().getName();</span>
-<span class="source-line-no">537</span><span id="line-537"> if (name.equals(region0.getEncodedName()) || name.equals(region1.getEncodedName())) {</span>
-<span class="source-line-no">538</span><span id="line-538"> return false;</span>
-<span class="source-line-no">539</span><span id="line-539"> }</span>
-<span class="source-line-no">540</span><span id="line-540"> }</span>
-<span class="source-line-no">541</span><span id="line-541"> return true;</span>
-<span class="source-line-no">542</span><span id="line-542"> } catch (IOException e) {</span>
-<span class="source-line-no">543</span><span id="line-543"> LOG.warn("Check if parent regions are archived error", e);</span>
-<span class="source-line-no">544</span><span id="line-544"> return false;</span>
-<span class="source-line-no">545</span><span id="line-545"> }</span>
-<span class="source-line-no">546</span><span id="line-546"> });</span>
-<span class="source-line-no">547</span><span id="line-547"> // set file modify time and then run cleaner</span>
-<span class="source-line-no">548</span><span id="line-548"> long time = EnvironmentEdgeManager.currentTime() - TimeToLiveHFileCleaner.DEFAULT_TTL * 1000;</span>
-<span class="source-line-no">549</span><span id="line-549"> traverseAndSetFileTime(HFileArchiveUtil.getArchivePath(conf), time);</span>
-<span class="source-line-no">550</span><span id="line-550"> UTIL.getMiniHBaseCluster().getMaster().getHFileCleaner().triggerCleanerNow().get();</span>
-<span class="source-line-no">551</span><span id="line-551"> // scan snapshot</span>
-<span class="source-line-no">552</span><span id="line-552"> try (TableSnapshotScanner scanner =</span>
-<span class="source-line-no">553</span><span id="line-553"> new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName,</span>
-<span class="source-line-no">554</span><span id="line-554"> new Scan().withStartRow(bbb).withStopRow(yyy))) {</span>
-<span class="source-line-no">555</span><span id="line-555"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">556</span><span id="line-556"> }</span>
-<span class="source-line-no">557</span><span id="line-557"> } catch (Exception e) {</span>
-<span class="source-line-no">558</span><span id="line-558"> LOG.error("scan snapshot error", e);</span>
-<span class="source-line-no">559</span><span id="line-559"> fail("Should not throw Exception: " + e.getMessage());</span>
-<span class="source-line-no">560</span><span id="line-560"> }</span>
-<span class="source-line-no">561</span><span id="line-561"> }</span>
-<span class="source-line-no">562</span><span id="line-562"></span>
-<span class="source-line-no">563</span><span id="line-563"> @Test</span>
-<span class="source-line-no">564</span><span id="line-564"> public void testDeleteTableWithMergedRegions() throws Exception {</span>
-<span class="source-line-no">565</span><span id="line-565"> final TableName tableName = TableName.valueOf(this.methodName);</span>
-<span class="source-line-no">566</span><span id="line-566"> String snapshotName = tableName.getNameAsString() + "_snapshot";</span>
-<span class="source-line-no">567</span><span id="line-567"> Configuration conf = UTIL.getConfiguration();</span>
-<span class="source-line-no">568</span><span id="line-568"> try (Admin admin = UTIL.getConnection().getAdmin()) {</span>
-<span class="source-line-no">569</span><span id="line-569"> // disable compaction</span>
-<span class="source-line-no">570</span><span id="line-570"> admin.compactionSwitch(false,</span>
-<span class="source-line-no">571</span><span id="line-571"> admin.getRegionServers().stream().map(s -> s.getServerName()).collect(Collectors.toList()));</span>
-<span class="source-line-no">572</span><span id="line-572"> // create table</span>
-<span class="source-line-no">573</span><span id="line-573"> Table table = UTIL.createTable(tableName, FAMILIES, 1, bbb, yyy, 3);</span>
-<span class="source-line-no">574</span><span id="line-574"> List<RegionInfo> regions = admin.getRegions(tableName);</span>
-<span class="source-line-no">575</span><span id="line-575"> assertEquals(3, regions.size());</span>
-<span class="source-line-no">576</span><span id="line-576"> // write some data</span>
-<span class="source-line-no">577</span><span id="line-577"> UTIL.loadTable(table, FAMILIES);</span>
-<span class="source-line-no">578</span><span id="line-578"> // merge region</span>
-<span class="source-line-no">579</span><span id="line-579"> admin.mergeRegionsAsync(new byte[][] { regions.get(0).getEncodedNameAsBytes(),</span>
-<span class="source-line-no">580</span><span id="line-580"> regions.get(1).getEncodedNameAsBytes() }, false).get();</span>
-<span class="source-line-no">581</span><span id="line-581"> regions = admin.getRegions(tableName);</span>
-<span class="source-line-no">582</span><span id="line-582"> assertEquals(2, regions.size());</span>
-<span class="source-line-no">583</span><span id="line-583"> // snapshot</span>
-<span class="source-line-no">584</span><span id="line-584"> admin.snapshot(snapshotName, tableName);</span>
-<span class="source-line-no">585</span><span id="line-585"> // verify snapshot</span>
-<span class="source-line-no">586</span><span id="line-586"> try (TableSnapshotScanner scanner =</span>
-<span class="source-line-no">587</span><span id="line-587"> new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName,</span>
-<span class="source-line-no">588</span><span id="line-588"> new Scan().withStartRow(bbb).withStopRow(yyy))) {</span>
-<span class="source-line-no">589</span><span id="line-589"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">590</span><span id="line-590"> }</span>
-<span class="source-line-no">591</span><span id="line-591"> // drop table</span>
-<span class="source-line-no">592</span><span id="line-592"> admin.disableTable(tableName);</span>
-<span class="source-line-no">593</span><span id="line-593"> admin.deleteTable(tableName);</span>
-<span class="source-line-no">594</span><span id="line-594"> // verify snapshot</span>
-<span class="source-line-no">595</span><span id="line-595"> try (TableSnapshotScanner scanner =</span>
-<span class="source-line-no">596</span><span id="line-596"> new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName,</span>
-<span class="source-line-no">597</span><span id="line-597"> new Scan().withStartRow(bbb).withStopRow(yyy))) {</span>
-<span class="source-line-no">598</span><span id="line-598"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">599</span><span id="line-599"> }</span>
-<span class="source-line-no">600</span><span id="line-600"> }</span>
-<span class="source-line-no">601</span><span id="line-601"> }</span>
-<span class="source-line-no">602</span><span id="line-602"></span>
-<span class="source-line-no">603</span><span id="line-603"> private void traverseAndSetFileTime(Path path, long time) throws IOException {</span>
-<span class="source-line-no">604</span><span id="line-604"> fs.setTimes(path, time, -1);</span>
-<span class="source-line-no">605</span><span id="line-605"> if (fs.isDirectory(path)) {</span>
-<span class="source-line-no">606</span><span id="line-606"> List<FileStatus> allPaths = Arrays.asList(fs.listStatus(path));</span>
-<span class="source-line-no">607</span><span id="line-607"> List<FileStatus> subDirs =</span>
-<span class="source-line-no">608</span><span id="line-608"> allPaths.stream().filter(FileStatus::isDirectory).collect(Collectors.toList());</span>
-<span class="source-line-no">609</span><span id="line-609"> List<FileStatus> files =</span>
-<span class="source-line-no">610</span><span id="line-610"> allPaths.stream().filter(FileStatus::isFile).collect(Collectors.toList());</span>
-<span class="source-line-no">611</span><span id="line-611"> for (FileStatus subDir : subDirs) {</span>
-<span class="source-line-no">612</span><span id="line-612"> traverseAndSetFileTime(subDir.getPath(), time);</span>
-<span class="source-line-no">613</span><span id="line-613"> }</span>
-<span class="source-line-no">614</span><span id="line-614"> for (FileStatus file : files) {</span>
-<span class="source-line-no">615</span><span id="line-615"> fs.setTimes(file.getPath(), time, -1);</span>
+<span class="source-line-no">535</span><span id="line-535"> HRegionFileSystem regionFs = HRegionFileSystem</span>
+<span class="source-line-no">536</span><span id="line-536"> .openRegionFromFileSystem(UTIL.getConfiguration(), fs, tableDir, mergedRegion, true);</span>
+<span class="source-line-no">537</span><span id="line-537"> boolean references = false;</span>
+<span class="source-line-no">538</span><span id="line-538"> Path regionDir = new Path(tableDir, mergedRegion.getEncodedName());</span>
+<span class="source-line-no">539</span><span id="line-539"> for (Path familyDir : FSUtils.getFamilyDirs(fs, regionDir)) {</span>
+<span class="source-line-no">540</span><span id="line-540"> StoreContext storeContext = StoreContext.getBuilder()</span>
+<span class="source-line-no">541</span><span id="line-541"> .withColumnFamilyDescriptor(ColumnFamilyDescriptorBuilder.of(familyDir.getName()))</span>
+<span class="source-line-no">542</span><span id="line-542"> .withRegionFileSystem(regionFs).withFamilyStoreDirectoryPath(familyDir).build();</span>
+<span class="source-line-no">543</span><span id="line-543"> StoreFileTracker sft =</span>
+<span class="source-line-no">544</span><span id="line-544"> StoreFileTrackerFactory.create(UTIL.getConfiguration(), false, storeContext);</span>
+<span class="source-line-no">545</span><span id="line-545"> references = references || sft.hasReferences();</span>
+<span class="source-line-no">546</span><span id="line-546"> if (references) {</span>
+<span class="source-line-no">547</span><span id="line-547"> break;</span>
+<span class="source-line-no">548</span><span id="line-548"> }</span>
+<span class="source-line-no">549</span><span id="line-549"> }</span>
+<span class="source-line-no">550</span><span id="line-550"> return !references;</span>
+<span class="source-line-no">551</span><span id="line-551"> } catch (IOException e) {</span>
+<span class="source-line-no">552</span><span id="line-552"> LOG.warn("Failed check merged region has no reference", e);</span>
+<span class="source-line-no">553</span><span id="line-553"> return false;</span>
+<span class="source-line-no">554</span><span id="line-554"> }</span>
+<span class="source-line-no">555</span><span id="line-555"> });</span>
+<span class="source-line-no">556</span><span id="line-556"> // run catalog janitor to clean and wait for parent regions are archived</span>
+<span class="source-line-no">557</span><span id="line-557"> UTIL.getMiniHBaseCluster().getMaster().getCatalogJanitor().choreForTesting();</span>
+<span class="source-line-no">558</span><span id="line-558"> UTIL.waitFor(timeout, () -> {</span>
+<span class="source-line-no">559</span><span id="line-559"> try {</span>
+<span class="source-line-no">560</span><span id="line-560"> Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName);</span>
+<span class="source-line-no">561</span><span id="line-561"> for (FileStatus fileStatus : fs.listStatus(tableDir)) {</span>
+<span class="source-line-no">562</span><span id="line-562"> String name = fileStatus.getPath().getName();</span>
+<span class="source-line-no">563</span><span id="line-563"> if (name.equals(region0.getEncodedName()) || name.equals(region1.getEncodedName())) {</span>
+<span class="source-line-no">564</span><span id="line-564"> return false;</span>
+<span class="source-line-no">565</span><span id="line-565"> }</span>
+<span class="source-line-no">566</span><span id="line-566"> }</span>
+<span class="source-line-no">567</span><span id="line-567"> return true;</span>
+<span class="source-line-no">568</span><span id="line-568"> } catch (IOException e) {</span>
+<span class="source-line-no">569</span><span id="line-569"> LOG.warn("Check if parent regions are archived error", e);</span>
+<span class="source-line-no">570</span><span id="line-570"> return false;</span>
+<span class="source-line-no">571</span><span id="line-571"> }</span>
+<span class="source-line-no">572</span><span id="line-572"> });</span>
+<span class="source-line-no">573</span><span id="line-573"> // set file modify time and then run cleaner</span>
+<span class="source-line-no">574</span><span id="line-574"> long time = EnvironmentEdgeManager.currentTime() - TimeToLiveHFileCleaner.DEFAULT_TTL * 1000;</span>
+<span class="source-line-no">575</span><span id="line-575"> traverseAndSetFileTime(HFileArchiveUtil.getArchivePath(conf), time);</span>
+<span class="source-line-no">576</span><span id="line-576"> UTIL.getMiniHBaseCluster().getMaster().getHFileCleaner().triggerCleanerNow().get();</span>
+<span class="source-line-no">577</span><span id="line-577"> // scan snapshot</span>
+<span class="source-line-no">578</span><span id="line-578"> try (TableSnapshotScanner scanner =</span>
+<span class="source-line-no">579</span><span id="line-579"> new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName,</span>
+<span class="source-line-no">580</span><span id="line-580"> new Scan().withStartRow(bbb).withStopRow(yyy))) {</span>
+<span class="source-line-no">581</span><span id="line-581"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">582</span><span id="line-582"> }</span>
+<span class="source-line-no">583</span><span id="line-583"> } catch (Exception e) {</span>
+<span class="source-line-no">584</span><span id="line-584"> LOG.error("scan snapshot error", e);</span>
+<span class="source-line-no">585</span><span id="line-585"> fail("Should not throw Exception: " + e.getMessage());</span>
+<span class="source-line-no">586</span><span id="line-586"> }</span>
+<span class="source-line-no">587</span><span id="line-587"> }</span>
+<span class="source-line-no">588</span><span id="line-588"></span>
+<span class="source-line-no">589</span><span id="line-589"> @Test</span>
+<span class="source-line-no">590</span><span id="line-590"> public void testDeleteTableWithMergedRegions() throws Exception {</span>
+<span class="source-line-no">591</span><span id="line-591"> final TableName tableName = TableName.valueOf(this.methodName);</span>
+<span class="source-line-no">592</span><span id="line-592"> String snapshotName = tableName.getNameAsString() + "_snapshot";</span>
+<span class="source-line-no">593</span><span id="line-593"> Configuration conf = UTIL.getConfiguration();</span>
+<span class="source-line-no">594</span><span id="line-594"> try (Admin admin = UTIL.getConnection().getAdmin()) {</span>
+<span class="source-line-no">595</span><span id="line-595"> // disable compaction</span>
+<span class="source-line-no">596</span><span id="line-596"> admin.compactionSwitch(false,</span>
+<span class="source-line-no">597</span><span id="line-597"> admin.getRegionServers().stream().map(s -> s.getServerName()).collect(Collectors.toList()));</span>
+<span class="source-line-no">598</span><span id="line-598"> // create table</span>
+<span class="source-line-no">599</span><span id="line-599"> Table table = UTIL.createTable(tableName, FAMILIES, 1, bbb, yyy, 3);</span>
+<span class="source-line-no">600</span><span id="line-600"> List<RegionInfo> regions = admin.getRegions(tableName);</span>
+<span class="source-line-no">601</span><span id="line-601"> assertEquals(3, regions.size());</span>
+<span class="source-line-no">602</span><span id="line-602"> // write some data</span>
+<span class="source-line-no">603</span><span id="line-603"> UTIL.loadTable(table, FAMILIES);</span>
+<span class="source-line-no">604</span><span id="line-604"> // merge region</span>
+<span class="source-line-no">605</span><span id="line-605"> admin.mergeRegionsAsync(new byte[][] { regions.get(0).getEncodedNameAsBytes(),</span>
+<span class="source-line-no">606</span><span id="line-606"> regions.get(1).getEncodedNameAsBytes() }, false).get();</span>
+<span class="source-line-no">607</span><span id="line-607"> regions = admin.getRegions(tableName);</span>
+<span class="source-line-no">608</span><span id="line-608"> assertEquals(2, regions.size());</span>
+<span class="source-line-no">609</span><span id="line-609"> // snapshot</span>
+<span class="source-line-no">610</span><span id="line-610"> admin.snapshot(snapshotName, tableName);</span>
+<span class="source-line-no">611</span><span id="line-611"> // verify snapshot</span>
+<span class="source-line-no">612</span><span id="line-612"> try (TableSnapshotScanner scanner =</span>
+<span class="source-line-no">613</span><span id="line-613"> new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName,</span>
+<span class="source-line-no">614</span><span id="line-614"> new Scan().withStartRow(bbb).withStopRow(yyy))) {</span>
+<span class="source-line-no">615</span><span id="line-615"> verifyScanner(scanner, bbb, yyy);</span>
<span class="source-line-no">616</span><span id="line-616"> }</span>
-<span class="source-line-no">617</span><span id="line-617"> }</span>
-<span class="source-line-no">618</span><span id="line-618"> }</span>
-<span class="source-line-no">619</span><span id="line-619">}</span>
+<span class="source-line-no">617</span><span id="line-617"> // drop table</span>
+<span class="source-line-no">618</span><span id="line-618"> admin.disableTable(tableName);</span>
+<span class="source-line-no">619</span><span id="line-619"> admin.deleteTable(tableName);</span>
+<span class="source-line-no">620</span><span id="line-620"> // verify snapshot</span>
+<span class="source-line-no">621</span><span id="line-621"> try (TableSnapshotScanner scanner =</span>
+<span class="source-line-no">622</span><span id="line-622"> new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName,</span>
+<span class="source-line-no">623</span><span id="line-623"> new Scan().withStartRow(bbb).withStopRow(yyy))) {</span>
+<span class="source-line-no">624</span><span id="line-624"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">625</span><span id="line-625"> }</span>
+<span class="source-line-no">626</span><span id="line-626"> }</span>
+<span class="source-line-no">627</span><span id="line-627"> }</span>
+<span class="source-line-no">628</span><span id="line-628"></span>
+<span class="source-line-no">629</span><span id="line-629"> private void traverseAndSetFileTime(Path path, long time) throws IOException {</span>
+<span class="source-line-no">630</span><span id="line-630"> fs.setTimes(path, time, -1);</span>
+<span class="source-line-no">631</span><span id="line-631"> if (fs.isDirectory(path)) {</span>
+<span class="source-line-no">632</span><span id="line-632"> List<FileStatus> allPaths = Arrays.asList(fs.listStatus(path));</span>
+<span class="source-line-no">633</span><span id="line-633"> List<FileStatus> subDirs =</span>
+<span class="source-line-no">634</span><span id="line-634"> allPaths.stream().filter(FileStatus::isDirectory).collect(Collectors.toList());</span>
+<span class="source-line-no">635</span><span id="line-635"> List<FileStatus> files =</span>
+<span class="source-line-no">636</span><span id="line-636"> allPaths.stream().filter(FileStatus::isFile).collect(Collectors.toList());</span>
+<span class="source-line-no">637</span><span id="line-637"> for (FileStatus subDir : subDirs) {</span>
+<span class="source-line-no">638</span><span id="line-638"> traverseAndSetFileTime(subDir.getPath(), time);</span>
+<span class="source-line-no">639</span><span id="line-639"> }</span>
+<span class="source-line-no">640</span><span id="line-640"> for (FileStatus file : files) {</span>
+<span class="source-line-no">641</span><span id="line-641"> fs.setTimes(file.getPath(), time, -1);</span>
+<span class="source-line-no">642</span><span id="line-642"> }</span>
+<span class="source-line-no">643</span><span id="line-643"> }</span>
+<span class="source-line-no">644</span><span id="line-644"> }</span>
+<span class="source-line-no">645</span><span id="line-645">}</span>
diff --git a/testdevapidocs/index-all.html b/testdevapidocs/index-all.html
index a1dbe7deaec..6dd990e9fee 100644
--- a/testdevapidocs/index-all.html
+++ b/testdevapidocs/index-all.html
@@ -83331,6 +83331,8 @@ loadScripts(document, 'script');</script>
<dd> </dd>
<dt><a href="org/apache/hadoop/hbase/client/TestScannersFromClientSide.html#testScannerWithPartialResults()" class="member-name-link">testScannerWithPartialResults()</a> - Method in class org.apache.hadoop.hbase.client.<a href="org/apache/hadoop/hbase/client/TestScannersFromClientSide.html" title="class in org.apache.hadoop.hbase.client">TestScannersFromClientSide</a></dt>
<dd> </dd>
+<dt><a href="org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#testScannerWithRestoredMobSnapshot()" class="member-name-link">testScannerWithRestoredMobSnapshot()</a> - Method in class org.apache.hadoop.hbase.client.<a href="org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html" title="class in org.apache.hadoop.hbase.client">TestTableSnapshotScanner</a></dt>
+<dd> </dd>
<dt><a href="org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#testScannerWithRestoreScanner()" class="member-name-link">testScannerWithRestoreScanner()</a> - Method in class org.apache.hadoop.hbase.client.<a href="org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html" title="class in org.apache.hadoop.hbase.client">TestTableSnapshotScanner</a></dt>
<dd> </dd>
<dt><a href="org/apache/hadoop/hbase/rest/TestTableScan.html#testScanningUnknownColumnJson()" class="member-name-link">testScanningUnknownColumnJson()</a> - Method in class org.apache.hadoop.hbase.rest.<a href="org/apache/hadoop/hbase/rest/TestTableScan.html" title="class in org.apache.hadoop.hbase.rest">TestTableScan</a></dt>
diff --git a/testdevapidocs/member-search-index.js b/testdevapidocs/member-search-index.js
index dcebeda9cbe..8b927003c20 100644
--- a/testdevapidocs/member-search-index.js
+++ b/testdevapidocs/member-search-index.js
@@ -1 +1 @@
-memberSearchIndex = [{"p":"org.apache.hadoop.hbase.master.janitor","c":"TestMetaFixerNoCluster","l":"_ARI"},{"p":"org.apache.hadoop.hbase.io","c":"TestHalfStoreFileReader","l":"_b(String)","u":"_b(java.lang.String)"},{"p":"org.apache.hadoop.hbase.master.janitor","c":"TestMetaFixerNoCluster","l":"_BRI"},{"p":"org.apache.hadoop.hbase.backup","c":"TestSystemTableSnapshot","l":"_testBackupRestoreSystemTable()"},{"p":"org.apache.hadoop.hbase.regionserver","c":"TestBlocksScanned","l":"_testBlo [...]
\ No newline at end of file
+memberSearchIndex = [{"p":"org.apache.hadoop.hbase.master.janitor","c":"TestMetaFixerNoCluster","l":"_ARI"},{"p":"org.apache.hadoop.hbase.io","c":"TestHalfStoreFileReader","l":"_b(String)","u":"_b(java.lang.String)"},{"p":"org.apache.hadoop.hbase.master.janitor","c":"TestMetaFixerNoCluster","l":"_BRI"},{"p":"org.apache.hadoop.hbase.backup","c":"TestSystemTableSnapshot","l":"_testBackupRestoreSystemTable()"},{"p":"org.apache.hadoop.hbase.regionserver","c":"TestBlocksScanned","l":"_testBlo [...]
\ No newline at end of file
diff --git a/testdevapidocs/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html b/testdevapidocs/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html
index 6786676ffe2..22e8b5a0975 100644
--- a/testdevapidocs/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html
+++ b/testdevapidocs/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html
@@ -82,7 +82,7 @@ loadScripts(document, 'script');</script>
</dl>
<hr>
<div class="type-signature"><span class="annotations">@Tag("org.apache.hadoop.hbase.testclassification.LargeTests") @Tag("org.apache.hadoop.hbase.testclassification.ClientTests")
-</span><span class="modifiers">public class </span><span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-70">TestTableSnapshotScanner</a></span>
+</span><span class="modifiers">public class </span><span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-71">TestTableSnapshotScanner</a></span>
<span class="extends-implements">extends <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Object.html" title="class or interface in java.lang" class="external-link">Object</a></span></div>
</section>
<section class="summary">
@@ -209,29 +209,32 @@ loadScripts(document, 'script');</script>
boolean shutdownCluster)</code></div>
<div class="col-last even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"> </div>
<div class="col-first odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code>void</code></div>
-<div class="col-second odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#testScannerWithRestoreScanner()" class="member-name-link">testScannerWithRestoreScanner</a>()</code></div>
+<div class="col-second odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#testScannerWithRestoredMobSnapshot()" class="member-name-link">testScannerWithRestoredMobSnapshot</a>()</code></div>
<div class="col-last odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"> </div>
<div class="col-first even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code>void</code></div>
-<div class="col-second even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#testWithMultiRegion()" class="member-name-link">testWithMultiRegion</a>()</code></div>
+<div class="col-second even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#testScannerWithRestoreScanner()" class="member-name-link">testScannerWithRestoreScanner</a>()</code></div>
<div class="col-last even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"> </div>
<div class="col-first odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code>void</code></div>
-<div class="col-second odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#testWithOfflineHBaseMultiRegion()" class="member-name-link">testWithOfflineHBaseMultiRegion</a>()</code></div>
+<div class="col-second odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#testWithMultiRegion()" class="member-name-link">testWithMultiRegion</a>()</code></div>
<div class="col-last odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"> </div>
<div class="col-first even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code>void</code></div>
-<div class="col-second even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#testWithSingleRegion()" class="member-name-link">testWithSingleRegion</a>()</code></div>
+<div class="col-second even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#testWithOfflineHBaseMultiRegion()" class="member-name-link">testWithOfflineHBaseMultiRegion</a>()</code></div>
<div class="col-last even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"> </div>
-<div class="col-first odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code>private void</code></div>
-<div class="col-second odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#traverseAndSetFileTime(org.apache.hadoop.fs.Path,long)" class="member-name-link">traverseAndSetFileTime</a><wbr>(org.apache.hadoop.fs.Path path,
- long time)</code></div>
+<div class="col-first odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code>void</code></div>
+<div class="col-second odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#testWithSingleRegion()" class="member-name-link">testWithSingleRegion</a>()</code></div>
<div class="col-last odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"> </div>
-<div class="col-first even-row-color method-summary-table method-summary-table-tab1 method-summary-table-tab4"><code>private static void</code></div>
-<div class="col-second even-row-color method-summary-table method-summary-table-tab1 method-summary-table-tab4"><code><a href="#verifyRow(org.apache.hadoop.hbase.client.Result)" class="member-name-link">verifyRow</a><wbr>(org.apache.hadoop.hbase.client.Result result)</code></div>
-<div class="col-last even-row-color method-summary-table method-summary-table-tab1 method-summary-table-tab4"> </div>
-<div class="col-first odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code>private void</code></div>
-<div class="col-second odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#verifyScanner(org.apache.hadoop.hbase.client.ResultScanner,byte%5B%5D,byte%5B%5D)" class="member-name-link">verifyScanner</a><wbr>(org.apache.hadoop.hbase.client.ResultScanner scanner,
+<div class="col-first even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code>private void</code></div>
+<div class="col-second even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#traverseAndSetFileTime(org.apache.hadoop.fs.Path,long)" class="member-name-link">traverseAndSetFileTime</a><wbr>(org.apache.hadoop.fs.Path path,
+ long time)</code></div>
+<div class="col-last even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"> </div>
+<div class="col-first odd-row-color method-summary-table method-summary-table-tab1 method-summary-table-tab4"><code>private static void</code></div>
+<div class="col-second odd-row-color method-summary-table method-summary-table-tab1 method-summary-table-tab4"><code><a href="#verifyRow(org.apache.hadoop.hbase.client.Result)" class="member-name-link">verifyRow</a><wbr>(org.apache.hadoop.hbase.client.Result result)</code></div>
+<div class="col-last odd-row-color method-summary-table method-summary-table-tab1 method-summary-table-tab4"> </div>
+<div class="col-first even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code>private void</code></div>
+<div class="col-second even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#verifyScanner(org.apache.hadoop.hbase.client.ResultScanner,byte%5B%5D,byte%5B%5D)" class="member-name-link">verifyScanner</a><wbr>(org.apache.hadoop.hbase.client.ResultScanner scanner,
byte[] startRow,
byte[] stopRow)</code></div>
-<div class="col-last odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"> </div>
+<div class="col-last even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"> </div>
</div>
</div>
</div>
@@ -252,19 +255,19 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="LOG">
<h3>LOG</h3>
-<div class="member-signature"><span class="modifiers">private static final</span> <span class="return-type">org.slf4j.Logger</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-74">LOG</a></span></div>
+<div class="member-signature"><span class="modifiers">private static final</span> <span class="return-type">org.slf4j.Logger</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-75">LOG</a></span></div>
</section>
</li>
<li>
<section class="detail" id="UTIL">
<h3>UTIL</h3>
-<div class="member-signature"><span class="modifiers">private final</span> <span class="return-type"><a href="../HBaseTestingUtil.html" title="class in org.apache.hadoop.hbase">HBaseTestingUtil</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-75">UTIL</a></span></div>
+<div class="member-signature"><span class="modifiers">private final</span> <span class="return-type"><a href="../HBaseTestingUtil.html" title="class in org.apache.hadoop.hbase">HBaseTestingUtil</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-76">UTIL</a></span></div>
</section>
</li>
<li>
<section class="detail" id="NUM_REGION_SERVERS">
<h3>NUM_REGION_SERVERS</h3>
-<div class="member-signature"><span class="modifiers">private static final</span> <span class="return-type">int</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-76">NUM_REGION_SERVERS</a></span></div>
+<div class="member-signature"><span class="modifiers">private static final</span> <span class="return-type">int</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-77">NUM_REGION_SERVERS</a></span></div>
<dl class="notes">
<dt>See Also:</dt>
<dd>
@@ -278,43 +281,43 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="FAMILIES">
<h3>FAMILIES</h3>
-<div class="member-signature"><span class="modifiers">private static final</span> <span class="return-type">byte[][]</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-77">FAMILIES</a></span></div>
+<div class="member-signature"><span class="modifiers">private static final</span> <span class="return-type">byte[][]</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-78">FAMILIES</a></span></div>
</section>
</li>
<li>
<section class="detail" id="bbb">
<h3>bbb</h3>
-<div class="member-signature"><span class="modifiers">public static</span> <span class="return-type">byte[]</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-78">bbb</a></span></div>
+<div class="member-signature"><span class="modifiers">public static</span> <span class="return-type">byte[]</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-79">bbb</a></span></div>
</section>
</li>
<li>
<section class="detail" id="yyy">
<h3>yyy</h3>
-<div class="member-signature"><span class="modifiers">public static</span> <span class="return-type">byte[]</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-79">yyy</a></span></div>
+<div class="member-signature"><span class="modifiers">public static</span> <span class="return-type">byte[]</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-80">yyy</a></span></div>
</section>
</li>
<li>
<section class="detail" id="fs">
<h3>fs</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.fs.FileSystem</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-81">fs</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.fs.FileSystem</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-82">fs</a></span></div>
</section>
</li>
<li>
<section class="detail" id="rootDir">
<h3>rootDir</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.fs.Path</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-82">rootDir</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.fs.Path</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-83">rootDir</a></span></div>
</section>
</li>
<li>
<section class="detail" id="clusterUp">
<h3>clusterUp</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">boolean</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-83">clusterUp</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">boolean</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-84">clusterUp</a></span></div>
</section>
</li>
<li>
<section class="detail" id="methodName">
<h3>methodName</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html" title="class or interface in java.lang" class="external-link">String</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-85">methodName</a></span></div>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html" title="class or interface in java.lang" class="external-link">String</a></span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-86">methodName</a></span></div>
</section>
</li>
</ul>
@@ -328,7 +331,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="<init>()">
<h3>TestTableSnapshotScanner</h3>
-<div class="member-signature"><span class="modifiers">public</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-72">TestTableSnapshotScanner</a></span>()</div>
+<div class="member-signature"><span class="modifiers">public</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-73">TestTableSnapshotScanner</a></span>()</div>
</section>
</li>
</ul>
@@ -342,7 +345,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="blockUntilSplitFinished(org.apache.hadoop.hbase.HBaseTestingUtil,org.apache.hadoop.hbase.TableName,int)">
<h3>blockUntilSplitFinished</h3>
-<div class="member-signature"><span class="modifiers">public static</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-87">blockUntilSplitFinished</a></span><wbr><span class="parameters">(<a href="../HBaseTestingUtil.html" title="class in org.apache.hadoop.hbase">HBaseTestingUtil</a> util,
+<div class="member-signature"><span class="modifiers">public static</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-88">blockUntilSplitFinished</a></span><wbr><span class="parameters">(<a href="../HBaseTestingUtil.html" title="class in org.apache.hadoop.hbase">HBaseTestingUtil</a> util,
org.apache.hadoop.hbase.TableName tableName,
int expectedRegionSize)</span>
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
@@ -356,7 +359,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="setupCluster(org.junit.jupiter.api.TestInfo)">
<h3>setupCluster</h3>
<div class="member-signature"><span class="annotations">@BeforeEach
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-98">setupCluster</a></span><wbr><span class="parameters">(org.junit.jupiter.api.TestInfo testInfo)</span>
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-99">setupCluster</a></span><wbr><span class="parameters">(org.junit.jupiter.api.TestInfo testInfo)</span>
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -368,7 +371,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="tearDownCluster()">
<h3>tearDownCluster</h3>
<div class="member-signature"><span class="annotations">@AfterEach
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-111">tearDownCluster</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-112">tearDownCluster</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -379,13 +382,13 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="setupConf(org.apache.hadoop.conf.Configuration)">
<h3>setupConf</h3>
-<div class="member-signature"><span class="modifiers">protected</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-118">setupConf</a></span><wbr><span class="parameters">(org.apache.hadoop.conf.Configuration conf)</span></div>
+<div class="member-signature"><span class="modifiers">protected</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-119">setupConf</a></span><wbr><span class="parameters">(org.apache.hadoop.conf.Configuration conf)</span></div>
</section>
</li>
<li>
<section class="detail" id="createTableAndSnapshot(org.apache.hadoop.hbase.HBaseTestingUtil,org.apache.hadoop.hbase.TableName,java.lang.String,int)">
<h3>createTableAndSnapshot</h3>
-<div class="member-signature"><span class="modifiers">public static</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-123">createTableAndSnapshot</a></span><wbr><span class="parameters">(<a href="../HBaseTestingUtil.html" title="class in org.apache.hadoop.hbase">HBaseTestingUtil</a> util,
+<div class="member-signature"><span class="modifiers">public static</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-124">createTableAndSnapshot</a></span><wbr><span class="parameters">(<a href="../HBaseTestingUtil.html" title="class in org.apache.hadoop.hbase">HBaseTestingUtil</a> util,
org.apache.hadoop.hbase.TableName tableName,
<a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html" title="class or interface in java.lang" class="external-link">String</a> snapshotName,
int numRegions)</span>
@@ -400,7 +403,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="testNoDuplicateResultsWhenSplitting()">
<h3>testNoDuplicateResultsWhenSplitting</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-157">testNoDuplicateResultsWhenSplitting</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-158">testNoDuplicateResultsWhenSplitting</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -412,7 +415,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="testScanLimit()">
<h3>testScanLimit</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-207">testScanLimit</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-208">testScanLimit</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -424,7 +427,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="testWithSingleRegion()">
<h3>testWithSingleRegion</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-236">testWithSingleRegion</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-237">testWithSingleRegion</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -436,7 +439,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="testWithMultiRegion()">
<h3>testWithMultiRegion</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-241">testWithMultiRegion</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-242">testWithMultiRegion</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -448,7 +451,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="testWithOfflineHBaseMultiRegion()">
<h3>testWithOfflineHBaseMultiRegion</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-246">testWithOfflineHBaseMultiRegion</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-247">testWithOfflineHBaseMultiRegion</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -459,7 +462,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="createTableSnapshotScannerAndGetScanMetrics(boolean,boolean,byte[])">
<h3>createTableSnapshotScannerAndGetScanMetrics</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.hbase.client.metrics.ScanMetrics</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-251">createTableSnapshotScannerAndGetScanMetrics</a></span><wbr><span class="parameters">(boolean enableScanMetrics,
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.hbase.client.metrics.ScanMetrics</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-252">createTableSnapshotScannerAndGetScanMetrics</a></span><wbr><span class="parameters">(boolean enableScanMetrics,
boolean enableScanMetricsByRegion,
byte[] endKey)</span>
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
@@ -473,7 +476,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="testScanMetricsDisabled()">
<h3>testScanMetricsDisabled</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-273">testScanMetricsDisabled</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-274">testScanMetricsDisabled</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -485,7 +488,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="testScanMetricsWithScanMetricsByRegionDisabled()">
<h3>testScanMetricsWithScanMetricsByRegionDisabled</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-279">testScanMetricsWithScanMetricsByRegionDisabled</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-280">testScanMetricsWithScanMetricsByRegionDisabled</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -497,7 +500,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="testScanMetricsByRegionForSingleRegion()">
<h3>testScanMetricsByRegionForSingleRegion</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-293">testScanMetricsByRegionForSingleRegion</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-294">testScanMetricsByRegionForSingleRegion</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -509,7 +512,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="testScanMetricsByRegionForMultiRegion()">
<h3>testScanMetricsByRegionForMultiRegion</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-313">testScanMetricsByRegionForMultiRegion</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-314">testScanMetricsByRegionForMultiRegion</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -521,7 +524,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="testScannerWithRestoreScanner()">
<h3>testScannerWithRestoreScanner</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-329">testScannerWithRestoreScanner</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-330">testScannerWithRestoreScanner</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -530,9 +533,21 @@ loadScripts(document, 'script');</script>
</section>
</li>
<li>
+<section class="detail" id="testScannerWithRestoredMobSnapshot()">
+<h3>testScannerWithRestoredMobSnapshot</h3>
+<div class="member-signature"><span class="annotations">@Test
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-374">testScannerWithRestoredMobSnapshot</a></span>()
+ throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
+<dl class="notes">
+<dt>Throws:</dt>
+<dd><code><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></code></dd>
+</dl>
+</section>
+</li>
+<li>
<section class="detail" id="testScanner(org.apache.hadoop.hbase.HBaseTestingUtil,java.lang.String,int,boolean)">
<h3>testScanner</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-373">testScanner</a></span><wbr><span class="parameters">(<a href="../HBaseTestingUtil.html" title="class in org.apache.hadoop.hbase">HBaseTestingUtil</a> util,
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-399">testScanner</a></span><wbr><span class="parameters">(<a href="../HBaseTestingUtil.html" title="class in org.apache.hadoop.hbase">HBaseTestingUtil</a> util,
<a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html" title="class or interface in java.lang" class="external-link">String</a> snapshotName,
int numRegions,
boolean shutdownCluster)</span>
@@ -546,7 +561,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="verifyScanner(org.apache.hadoop.hbase.client.ResultScanner,byte[],byte[])">
<h3>verifyScanner</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-400">verifyScanner</a></span><wbr><span class="parameters">(org.apache.hadoop.hbase.client.ResultScanner scanner,
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-426">verifyScanner</a></span><wbr><span class="parameters">(org.apache.hadoop.hbase.client.ResultScanner scanner,
byte[] startRow,
byte[] stopRow)</span>
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/IOException.html" title="class or interface in java.io" class="external-link">IOException</a>,
@@ -561,7 +576,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="verifyRow(org.apache.hadoop.hbase.client.Result)">
<h3>verifyRow</h3>
-<div class="member-signature"><span class="modifiers">private static</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-419">verifyRow</a></span><wbr><span class="parameters">(org.apache.hadoop.hbase.client.Result result)</span>
+<div class="member-signature"><span class="modifiers">private static</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-445">verifyRow</a></span><wbr><span class="parameters">(org.apache.hadoop.hbase.client.Result result)</span>
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/IOException.html" title="class or interface in java.io" class="external-link">IOException</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -573,7 +588,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="testMergeRegion()">
<h3>testMergeRegion</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-437">testMergeRegion</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-463">testMergeRegion</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -585,7 +600,7 @@ loadScripts(document, 'script');</script>
<section class="detail" id="testDeleteTableWithMergedRegions()">
<h3>testDeleteTableWithMergedRegions</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-563">testDeleteTableWithMergedRegions</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-589">testDeleteTableWithMergedRegions</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -596,7 +611,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="traverseAndSetFileTime(org.apache.hadoop.fs.Path,long)">
<h3>traverseAndSetFileTime</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-603">traverseAndSetFileTime</a></span><wbr><span class="parameters">(org.apache.hadoop.fs.Path path,
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html#line-629">traverseAndSetFileTime</a></span><wbr><span class="parameters">(org.apache.hadoop.fs.Path path,
long time)</span>
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/IOException.html" title="class or interface in java.io" class="external-link">IOException</a></span></div>
<dl class="notes">
diff --git a/testdevapidocs/org/apache/hadoop/hbase/client/TestTableSnapshotScannerWithSFT.html b/testdevapidocs/org/apache/hadoop/hbase/client/TestTableSnapshotScannerWithSFT.html
index 899fb830aa1..910a680cd5d 100644
--- a/testdevapidocs/org/apache/hadoop/hbase/client/TestTableSnapshotScannerWithSFT.html
+++ b/testdevapidocs/org/apache/hadoop/hbase/client/TestTableSnapshotScannerWithSFT.html
@@ -126,7 +126,7 @@ loadScripts(document, 'script');</script>
</div>
<div class="inherited-list">
<h3 id="methods-inherited-from-class-org.apache.hadoop.hbase.client.TestTableSnapshotScanner">Methods inherited from class org.apache.hadoop.hbase.client.<a href="TestTableSnapshotScanner.html" title="class in org.apache.hadoop.hbase.client">TestTableSnapshotScanner</a></h3>
-<code><a href="TestTableSnapshotScanner.html#blockUntilSplitFinished(org.apache.hadoop.hbase.HBaseTestingUtil,org.apache.hadoop.hbase.TableName,int)">blockUntilSplitFinished</a>, <a href="TestTableSnapshotScanner.html#createTableAndSnapshot(org.apache.hadoop.hbase.HBaseTestingUtil,org.apache.hadoop.hbase.TableName,java.lang.String,int)">createTableAndSnapshot</a>, <a href="TestTableSnapshotScanner.html#setupCluster(org.junit.jupiter.api.TestInfo)">setupCluster</a>, <a href="TestTableSnap [...]
+<code><a href="TestTableSnapshotScanner.html#blockUntilSplitFinished(org.apache.hadoop.hbase.HBaseTestingUtil,org.apache.hadoop.hbase.TableName,int)">blockUntilSplitFinished</a>, <a href="TestTableSnapshotScanner.html#createTableAndSnapshot(org.apache.hadoop.hbase.HBaseTestingUtil,org.apache.hadoop.hbase.TableName,java.lang.String,int)">createTableAndSnapshot</a>, <a href="TestTableSnapshotScanner.html#setupCluster(org.junit.jupiter.api.TestInfo)">setupCluster</a>, <a href="TestTableSnap [...]
<div class="inherited-list">
<h3 id="methods-inherited-from-class-java.lang.Object">Methods inherited from class java.lang.<a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Object.html" title="class or interface in java.lang" class="external-link">Object</a></h3>
<code><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Object.html#clone()" title="class or interface in java.lang" class="external-link">clone</a>, <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Object.html#equals(java.lang.Object)" title="class or interface in java.lang" class="external-link">equals</a>, <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Object.html#finalize()" title="class or [...]
diff --git a/testdevapidocs/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html b/testdevapidocs/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html
index 7fad1d7f82f..200713ce58b 100644
--- a/testdevapidocs/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html
+++ b/testdevapidocs/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html
@@ -272,7 +272,7 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="getSCPForServer(org.apache.hadoop.hbase.ServerName)">
<h3>getSCPForServer</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.hbase.master.procedure.ServerCrashProcedure</span> <span class="element-name"><a href="../../../../../../src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html#line-138">getSCPForServer</a></span><wbr><span class="parameters">(org.apache.hadoop.hbase.ServerName serverName)</span>
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.apache.hadoop.hbase.master.procedure.ServerCrashProcedure</span> <span class="element-name"><a href="../../../../../../src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html#line-143">getSCPForServer</a></span><wbr><span class="parameters">(org.apache.hadoop.hbase.ServerName serverName)</span>
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/IOException.html" title="class or interface in java.io" class="external-link">IOException</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
@@ -283,14 +283,14 @@ loadScripts(document, 'script');</script>
<li>
<section class="detail" id="subProcOf(org.apache.hadoop.hbase.procedure2.Procedure)">
<h3>subProcOf</h3>
-<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.hamcrest.Matcher<org.apache.hadoop.hbase.procedure2.Procedure<org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv>></span> <span class="element-name"><a href="../../../../../../src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html#line-144">subProcOf</a></span><wbr><span class="parameters">(org.apache.hadoop.hbase.procedure2.Procedure<org.a [...]
+<div class="member-signature"><span class="modifiers">private</span> <span class="return-type">org.hamcrest.Matcher<org.apache.hadoop.hbase.procedure2.Procedure<org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv>></span> <span class="element-name"><a href="../../../../../../src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html#line-149">subProcOf</a></span><wbr><span class="parameters">(org.apache.hadoop.hbase.procedure2.Procedure<org.a [...]
</section>
</li>
<li>
<section class="detail" id="testFailAndRollback()">
<h3>testFailAndRollback</h3>
<div class="member-signature"><span class="annotations">@Test
-</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../../src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html#line-163">testFailAndRollback</a></span>()
+</span><span class="modifiers">public</span> <span class="return-type">void</span> <span class="element-name"><a href="../../../../../../src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html#line-168">testFailAndRollback</a></span>()
throws <span class="exceptions"><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Exception.html" title="class or interface in java.lang" class="external-link">Exception</a></span></div>
<dl class="notes">
<dt>Throws:</dt>
diff --git a/testdevapidocs/src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html b/testdevapidocs/src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html
index 9ffff1f2453..fbcf5419bbf 100644
--- a/testdevapidocs/src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html
+++ b/testdevapidocs/src-html/org/apache/hadoop/hbase/client/TestTableSnapshotScanner.html
@@ -62,574 +62,600 @@
<span class="source-line-no">049</span><span id="line-49">import org.apache.hadoop.hbase.regionserver.StoreContext;</span>
<span class="source-line-no">050</span><span id="line-50">import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker;</span>
<span class="source-line-no">051</span><span id="line-51">import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;</span>
-<span class="source-line-no">052</span><span id="line-52">import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;</span>
-<span class="source-line-no">053</span><span id="line-53">import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils;</span>
-<span class="source-line-no">054</span><span id="line-54">import org.apache.hadoop.hbase.testclassification.ClientTests;</span>
-<span class="source-line-no">055</span><span id="line-55">import org.apache.hadoop.hbase.testclassification.LargeTests;</span>
-<span class="source-line-no">056</span><span id="line-56">import org.apache.hadoop.hbase.util.Bytes;</span>
-<span class="source-line-no">057</span><span id="line-57">import org.apache.hadoop.hbase.util.CommonFSUtils;</span>
-<span class="source-line-no">058</span><span id="line-58">import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;</span>
-<span class="source-line-no">059</span><span id="line-59">import org.apache.hadoop.hbase.util.FSUtils;</span>
-<span class="source-line-no">060</span><span id="line-60">import org.apache.hadoop.hbase.util.HFileArchiveUtil;</span>
-<span class="source-line-no">061</span><span id="line-61">import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;</span>
-<span class="source-line-no">062</span><span id="line-62">import org.junit.jupiter.api.AfterEach;</span>
-<span class="source-line-no">063</span><span id="line-63">import org.junit.jupiter.api.BeforeEach;</span>
-<span class="source-line-no">064</span><span id="line-64">import org.junit.jupiter.api.Tag;</span>
-<span class="source-line-no">065</span><span id="line-65">import org.junit.jupiter.api.Test;</span>
-<span class="source-line-no">066</span><span id="line-66">import org.junit.jupiter.api.TestInfo;</span>
-<span class="source-line-no">067</span><span id="line-67">import org.slf4j.Logger;</span>
-<span class="source-line-no">068</span><span id="line-68">import org.slf4j.LoggerFactory;</span>
-<span class="source-line-no">069</span><span id="line-69"></span>
-<span class="source-line-no">070</span><span id="line-70">@Tag(LargeTests.TAG)</span>
-<span class="source-line-no">071</span><span id="line-71">@Tag(ClientTests.TAG)</span>
-<span class="source-line-no">072</span><span id="line-72">public class TestTableSnapshotScanner {</span>
-<span class="source-line-no">073</span><span id="line-73"></span>
-<span class="source-line-no">074</span><span id="line-74"> private static final Logger LOG = LoggerFactory.getLogger(TestTableSnapshotScanner.class);</span>
-<span class="source-line-no">075</span><span id="line-75"> private final HBaseTestingUtil UTIL = new HBaseTestingUtil();</span>
-<span class="source-line-no">076</span><span id="line-76"> private static final int NUM_REGION_SERVERS = 2;</span>
-<span class="source-line-no">077</span><span id="line-77"> private static final byte[][] FAMILIES = { Bytes.toBytes("f1"), Bytes.toBytes("f2") };</span>
-<span class="source-line-no">078</span><span id="line-78"> public static byte[] bbb = Bytes.toBytes("bbb");</span>
-<span class="source-line-no">079</span><span id="line-79"> public static byte[] yyy = Bytes.toBytes("yyy");</span>
-<span class="source-line-no">080</span><span id="line-80"></span>
-<span class="source-line-no">081</span><span id="line-81"> private FileSystem fs;</span>
-<span class="source-line-no">082</span><span id="line-82"> private Path rootDir;</span>
-<span class="source-line-no">083</span><span id="line-83"> private boolean clusterUp;</span>
-<span class="source-line-no">084</span><span id="line-84"></span>
-<span class="source-line-no">085</span><span id="line-85"> private String methodName;</span>
-<span class="source-line-no">086</span><span id="line-86"></span>
-<span class="source-line-no">087</span><span id="line-87"> public static void blockUntilSplitFinished(HBaseTestingUtil util, TableName tableName,</span>
-<span class="source-line-no">088</span><span id="line-88"> int expectedRegionSize) throws Exception {</span>
-<span class="source-line-no">089</span><span id="line-89"> for (int i = 0; i < 100; i++) {</span>
-<span class="source-line-no">090</span><span id="line-90"> List<RegionInfo> hRegionInfoList = util.getAdmin().getRegions(tableName);</span>
-<span class="source-line-no">091</span><span id="line-91"> if (hRegionInfoList.size() >= expectedRegionSize) {</span>
-<span class="source-line-no">092</span><span id="line-92"> break;</span>
-<span class="source-line-no">093</span><span id="line-93"> }</span>
-<span class="source-line-no">094</span><span id="line-94"> Thread.sleep(1000);</span>
-<span class="source-line-no">095</span><span id="line-95"> }</span>
-<span class="source-line-no">096</span><span id="line-96"> }</span>
-<span class="source-line-no">097</span><span id="line-97"></span>
-<span class="source-line-no">098</span><span id="line-98"> @BeforeEach</span>
-<span class="source-line-no">099</span><span id="line-99"> public void setupCluster(TestInfo testInfo) throws Exception {</span>
-<span class="source-line-no">100</span><span id="line-100"> methodName = testInfo.getTestMethod().get().getName();</span>
-<span class="source-line-no">101</span><span id="line-101"> setupConf(UTIL.getConfiguration());</span>
-<span class="source-line-no">102</span><span id="line-102"> StartTestingClusterOption option =</span>
-<span class="source-line-no">103</span><span id="line-103"> StartTestingClusterOption.builder().numRegionServers(NUM_REGION_SERVERS)</span>
-<span class="source-line-no">104</span><span id="line-104"> .numDataNodes(NUM_REGION_SERVERS).createRootDir(true).build();</span>
-<span class="source-line-no">105</span><span id="line-105"> UTIL.startMiniCluster(option);</span>
-<span class="source-line-no">106</span><span id="line-106"> clusterUp = true;</span>
-<span class="source-line-no">107</span><span id="line-107"> rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();</span>
-<span class="source-line-no">108</span><span id="line-108"> fs = rootDir.getFileSystem(UTIL.getConfiguration());</span>
-<span class="source-line-no">109</span><span id="line-109"> }</span>
-<span class="source-line-no">110</span><span id="line-110"></span>
-<span class="source-line-no">111</span><span id="line-111"> @AfterEach</span>
-<span class="source-line-no">112</span><span id="line-112"> public void tearDownCluster() throws Exception {</span>
-<span class="source-line-no">113</span><span id="line-113"> if (clusterUp) {</span>
-<span class="source-line-no">114</span><span id="line-114"> UTIL.shutdownMiniCluster();</span>
-<span class="source-line-no">115</span><span id="line-115"> }</span>
-<span class="source-line-no">116</span><span id="line-116"> }</span>
-<span class="source-line-no">117</span><span id="line-117"></span>
-<span class="source-line-no">118</span><span id="line-118"> protected void setupConf(Configuration conf) {</span>
-<span class="source-line-no">119</span><span id="line-119"> // Enable snapshot</span>
-<span class="source-line-no">120</span><span id="line-120"> conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);</span>
-<span class="source-line-no">121</span><span id="line-121"> }</span>
-<span class="source-line-no">122</span><span id="line-122"></span>
-<span class="source-line-no">123</span><span id="line-123"> public static void createTableAndSnapshot(HBaseTestingUtil util, TableName tableName,</span>
-<span class="source-line-no">124</span><span id="line-124"> String snapshotName, int numRegions) throws Exception {</span>
-<span class="source-line-no">125</span><span id="line-125"> try {</span>
-<span class="source-line-no">126</span><span id="line-126"> util.deleteTable(tableName);</span>
-<span class="source-line-no">127</span><span id="line-127"> } catch (Exception ex) {</span>
-<span class="source-line-no">128</span><span id="line-128"> // ignore</span>
-<span class="source-line-no">129</span><span id="line-129"> }</span>
-<span class="source-line-no">130</span><span id="line-130"></span>
-<span class="source-line-no">131</span><span id="line-131"> if (numRegions > 1) {</span>
-<span class="source-line-no">132</span><span id="line-132"> util.createTable(tableName, FAMILIES, 1, bbb, yyy, numRegions);</span>
-<span class="source-line-no">133</span><span id="line-133"> } else {</span>
-<span class="source-line-no">134</span><span id="line-134"> util.createTable(tableName, FAMILIES);</span>
-<span class="source-line-no">135</span><span id="line-135"> }</span>
-<span class="source-line-no">136</span><span id="line-136"> Admin admin = util.getAdmin();</span>
-<span class="source-line-no">137</span><span id="line-137"></span>
-<span class="source-line-no">138</span><span id="line-138"> // put some stuff in the table</span>
-<span class="source-line-no">139</span><span id="line-139"> Table table = util.getConnection().getTable(tableName);</span>
-<span class="source-line-no">140</span><span id="line-140"> util.loadTable(table, FAMILIES);</span>
-<span class="source-line-no">141</span><span id="line-141"></span>
-<span class="source-line-no">142</span><span id="line-142"> Path rootDir = CommonFSUtils.getRootDir(util.getConfiguration());</span>
-<span class="source-line-no">143</span><span id="line-143"> FileSystem fs = rootDir.getFileSystem(util.getConfiguration());</span>
-<span class="source-line-no">144</span><span id="line-144"></span>
-<span class="source-line-no">145</span><span id="line-145"> SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, Arrays.asList(FAMILIES), null,</span>
-<span class="source-line-no">146</span><span id="line-146"> snapshotName, rootDir, fs, true);</span>
-<span class="source-line-no">147</span><span id="line-147"></span>
-<span class="source-line-no">148</span><span id="line-148"> // load different values</span>
-<span class="source-line-no">149</span><span id="line-149"> byte[] value = Bytes.toBytes("after_snapshot_value");</span>
-<span class="source-line-no">150</span><span id="line-150"> util.loadTable(table, FAMILIES, value);</span>
-<span class="source-line-no">151</span><span id="line-151"></span>
-<span class="source-line-no">152</span><span id="line-152"> // cause flush to create new files in the region</span>
-<span class="source-line-no">153</span><span id="line-153"> admin.flush(tableName);</span>
-<span class="source-line-no">154</span><span id="line-154"> table.close();</span>
-<span class="source-line-no">155</span><span id="line-155"> }</span>
-<span class="source-line-no">156</span><span id="line-156"></span>
-<span class="source-line-no">157</span><span id="line-157"> @Test</span>
-<span class="source-line-no">158</span><span id="line-158"> public void testNoDuplicateResultsWhenSplitting() throws Exception {</span>
-<span class="source-line-no">159</span><span id="line-159"> TableName tableName = TableName.valueOf("testNoDuplicateResultsWhenSplitting");</span>
-<span class="source-line-no">160</span><span id="line-160"> String snapshotName = "testSnapshotBug";</span>
-<span class="source-line-no">161</span><span id="line-161"> try {</span>
-<span class="source-line-no">162</span><span id="line-162"> if (UTIL.getAdmin().tableExists(tableName)) {</span>
-<span class="source-line-no">163</span><span id="line-163"> UTIL.deleteTable(tableName);</span>
-<span class="source-line-no">164</span><span id="line-164"> }</span>
-<span class="source-line-no">165</span><span id="line-165"></span>
-<span class="source-line-no">166</span><span id="line-166"> UTIL.createTable(tableName, FAMILIES);</span>
-<span class="source-line-no">167</span><span id="line-167"> Admin admin = UTIL.getAdmin();</span>
-<span class="source-line-no">168</span><span id="line-168"></span>
-<span class="source-line-no">169</span><span id="line-169"> // put some stuff in the table</span>
-<span class="source-line-no">170</span><span id="line-170"> Table table = UTIL.getConnection().getTable(tableName);</span>
-<span class="source-line-no">171</span><span id="line-171"> UTIL.loadTable(table, FAMILIES);</span>
-<span class="source-line-no">172</span><span id="line-172"></span>
-<span class="source-line-no">173</span><span id="line-173"> // split to 2 regions</span>
-<span class="source-line-no">174</span><span id="line-174"> admin.split(tableName, Bytes.toBytes("eee"));</span>
-<span class="source-line-no">175</span><span id="line-175"> blockUntilSplitFinished(UTIL, tableName, 2);</span>
-<span class="source-line-no">176</span><span id="line-176"></span>
-<span class="source-line-no">177</span><span id="line-177"> Path rootDir = CommonFSUtils.getRootDir(UTIL.getConfiguration());</span>
-<span class="source-line-no">178</span><span id="line-178"> FileSystem fs = rootDir.getFileSystem(UTIL.getConfiguration());</span>
-<span class="source-line-no">179</span><span id="line-179"></span>
-<span class="source-line-no">180</span><span id="line-180"> SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, Arrays.asList(FAMILIES),</span>
-<span class="source-line-no">181</span><span id="line-181"> null, snapshotName, rootDir, fs, true);</span>
-<span class="source-line-no">182</span><span id="line-182"></span>
-<span class="source-line-no">183</span><span id="line-183"> // load different values</span>
-<span class="source-line-no">184</span><span id="line-184"> byte[] value = Bytes.toBytes("after_snapshot_value");</span>
-<span class="source-line-no">185</span><span id="line-185"> UTIL.loadTable(table, FAMILIES, value);</span>
-<span class="source-line-no">186</span><span id="line-186"></span>
-<span class="source-line-no">187</span><span id="line-187"> // cause flush to create new files in the region</span>
-<span class="source-line-no">188</span><span id="line-188"> admin.flush(tableName);</span>
-<span class="source-line-no">189</span><span id="line-189"> table.close();</span>
-<span class="source-line-no">190</span><span id="line-190"></span>
-<span class="source-line-no">191</span><span id="line-191"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
-<span class="source-line-no">192</span><span id="line-192"> Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan</span>
-<span class="source-line-no">193</span><span id="line-193"></span>
-<span class="source-line-no">194</span><span id="line-194"> TableSnapshotScanner scanner =</span>
-<span class="source-line-no">195</span><span id="line-195"> new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan);</span>
-<span class="source-line-no">196</span><span id="line-196"></span>
-<span class="source-line-no">197</span><span id="line-197"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">198</span><span id="line-198"> scanner.close();</span>
-<span class="source-line-no">199</span><span id="line-199"> } catch (Exception e) {</span>
-<span class="source-line-no">200</span><span id="line-200"> e.printStackTrace();</span>
-<span class="source-line-no">201</span><span id="line-201"> } finally {</span>
-<span class="source-line-no">202</span><span id="line-202"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
-<span class="source-line-no">203</span><span id="line-203"> UTIL.deleteTable(tableName);</span>
-<span class="source-line-no">204</span><span id="line-204"> }</span>
-<span class="source-line-no">205</span><span id="line-205"> }</span>
-<span class="source-line-no">206</span><span id="line-206"></span>
-<span class="source-line-no">207</span><span id="line-207"> @Test</span>
-<span class="source-line-no">208</span><span id="line-208"> public void testScanLimit() throws Exception {</span>
-<span class="source-line-no">209</span><span id="line-209"> final TableName tableName = TableName.valueOf(methodName);</span>
-<span class="source-line-no">210</span><span id="line-210"> final String snapshotName = tableName + "Snapshot";</span>
-<span class="source-line-no">211</span><span id="line-211"> TableSnapshotScanner scanner = null;</span>
-<span class="source-line-no">212</span><span id="line-212"> try {</span>
-<span class="source-line-no">213</span><span id="line-213"> createTableAndSnapshot(UTIL, tableName, snapshotName, 50);</span>
-<span class="source-line-no">214</span><span id="line-214"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
-<span class="source-line-no">215</span><span id="line-215"> Scan scan = new Scan().withStartRow(bbb).setLimit(100); // limit the scan</span>
-<span class="source-line-no">216</span><span id="line-216"></span>
-<span class="source-line-no">217</span><span id="line-217"> scanner = new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan);</span>
-<span class="source-line-no">218</span><span id="line-218"> int count = 0;</span>
-<span class="source-line-no">219</span><span id="line-219"> while (true) {</span>
-<span class="source-line-no">220</span><span id="line-220"> Result result = scanner.next();</span>
-<span class="source-line-no">221</span><span id="line-221"> if (result == null) {</span>
-<span class="source-line-no">222</span><span id="line-222"> break;</span>
-<span class="source-line-no">223</span><span id="line-223"> }</span>
-<span class="source-line-no">224</span><span id="line-224"> count++;</span>
-<span class="source-line-no">225</span><span id="line-225"> }</span>
-<span class="source-line-no">226</span><span id="line-226"> assertEquals(100, count);</span>
-<span class="source-line-no">227</span><span id="line-227"> } finally {</span>
-<span class="source-line-no">228</span><span id="line-228"> if (scanner != null) {</span>
-<span class="source-line-no">229</span><span id="line-229"> scanner.close();</span>
-<span class="source-line-no">230</span><span id="line-230"> }</span>
-<span class="source-line-no">231</span><span id="line-231"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
-<span class="source-line-no">232</span><span id="line-232"> UTIL.deleteTable(tableName);</span>
-<span class="source-line-no">233</span><span id="line-233"> }</span>
-<span class="source-line-no">234</span><span id="line-234"> }</span>
-<span class="source-line-no">235</span><span id="line-235"></span>
-<span class="source-line-no">236</span><span id="line-236"> @Test</span>
-<span class="source-line-no">237</span><span id="line-237"> public void testWithSingleRegion() throws Exception {</span>
-<span class="source-line-no">238</span><span id="line-238"> testScanner(UTIL, "testWithSingleRegion", 1, false);</span>
-<span class="source-line-no">239</span><span id="line-239"> }</span>
-<span class="source-line-no">240</span><span id="line-240"></span>
-<span class="source-line-no">241</span><span id="line-241"> @Test</span>
-<span class="source-line-no">242</span><span id="line-242"> public void testWithMultiRegion() throws Exception {</span>
-<span class="source-line-no">243</span><span id="line-243"> testScanner(UTIL, "testWithMultiRegion", 10, false);</span>
-<span class="source-line-no">244</span><span id="line-244"> }</span>
-<span class="source-line-no">245</span><span id="line-245"></span>
-<span class="source-line-no">246</span><span id="line-246"> @Test</span>
-<span class="source-line-no">247</span><span id="line-247"> public void testWithOfflineHBaseMultiRegion() throws Exception {</span>
-<span class="source-line-no">248</span><span id="line-248"> testScanner(UTIL, "testWithMultiRegion", 20, true);</span>
-<span class="source-line-no">249</span><span id="line-249"> }</span>
-<span class="source-line-no">250</span><span id="line-250"></span>
-<span class="source-line-no">251</span><span id="line-251"> private ScanMetrics createTableSnapshotScannerAndGetScanMetrics(boolean enableScanMetrics,</span>
-<span class="source-line-no">252</span><span id="line-252"> boolean enableScanMetricsByRegion, byte[] endKey) throws Exception {</span>
-<span class="source-line-no">253</span><span id="line-253"> TableName tableName = TableName.valueOf(methodName + "_TABLE");</span>
-<span class="source-line-no">254</span><span id="line-254"> String snapshotName = methodName + "_SNAPSHOT";</span>
-<span class="source-line-no">255</span><span id="line-255"> try {</span>
-<span class="source-line-no">256</span><span id="line-256"> createTableAndSnapshot(UTIL, tableName, snapshotName, 50);</span>
-<span class="source-line-no">257</span><span id="line-257"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
-<span class="source-line-no">258</span><span id="line-258"> Scan scan = new Scan().withStartRow(bbb).withStopRow(endKey);</span>
-<span class="source-line-no">259</span><span id="line-259"> scan.setScanMetricsEnabled(enableScanMetrics);</span>
-<span class="source-line-no">260</span><span id="line-260"> scan.setEnableScanMetricsByRegion(enableScanMetricsByRegion);</span>
-<span class="source-line-no">261</span><span id="line-261"> Configuration conf = UTIL.getConfiguration();</span>
-<span class="source-line-no">262</span><span id="line-262"></span>
-<span class="source-line-no">263</span><span id="line-263"> TableSnapshotScanner snapshotScanner =</span>
-<span class="source-line-no">264</span><span id="line-264"> new TableSnapshotScanner(conf, restoreDir, snapshotName, scan);</span>
-<span class="source-line-no">265</span><span id="line-265"> verifyScanner(snapshotScanner, bbb, endKey);</span>
-<span class="source-line-no">266</span><span id="line-266"> return snapshotScanner.getScanMetrics();</span>
-<span class="source-line-no">267</span><span id="line-267"> } finally {</span>
-<span class="source-line-no">268</span><span id="line-268"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
-<span class="source-line-no">269</span><span id="line-269"> UTIL.deleteTable(tableName);</span>
-<span class="source-line-no">270</span><span id="line-270"> }</span>
-<span class="source-line-no">271</span><span id="line-271"> }</span>
-<span class="source-line-no">272</span><span id="line-272"></span>
-<span class="source-line-no">273</span><span id="line-273"> @Test</span>
-<span class="source-line-no">274</span><span id="line-274"> public void testScanMetricsDisabled() throws Exception {</span>
-<span class="source-line-no">275</span><span id="line-275"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(false, false, yyy);</span>
-<span class="source-line-no">276</span><span id="line-276"> assertNull(scanMetrics);</span>
-<span class="source-line-no">277</span><span id="line-277"> }</span>
-<span class="source-line-no">278</span><span id="line-278"></span>
-<span class="source-line-no">279</span><span id="line-279"> @Test</span>
-<span class="source-line-no">280</span><span id="line-280"> public void testScanMetricsWithScanMetricsByRegionDisabled() throws Exception {</span>
-<span class="source-line-no">281</span><span id="line-281"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, false, yyy);</span>
-<span class="source-line-no">282</span><span id="line-282"> assertNotNull(scanMetrics);</span>
-<span class="source-line-no">283</span><span id="line-283"> int rowsScanned = 0;</span>
-<span class="source-line-no">284</span><span id="line-284"> for (byte[] row : HBaseTestingUtil.ROWS) {</span>
-<span class="source-line-no">285</span><span id="line-285"> if (Bytes.compareTo(row, bbb) >= 0 && Bytes.compareTo(row, yyy) < 0) {</span>
-<span class="source-line-no">286</span><span id="line-286"> rowsScanned++;</span>
-<span class="source-line-no">287</span><span id="line-287"> }</span>
-<span class="source-line-no">288</span><span id="line-288"> }</span>
-<span class="source-line-no">289</span><span id="line-289"> Map<String, Long> metricsMap = scanMetrics.getMetricsMap();</span>
-<span class="source-line-no">290</span><span id="line-290"> assertEquals(rowsScanned, (long) metricsMap.get(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME));</span>
-<span class="source-line-no">291</span><span id="line-291"> }</span>
-<span class="source-line-no">292</span><span id="line-292"></span>
-<span class="source-line-no">293</span><span id="line-293"> @Test</span>
-<span class="source-line-no">294</span><span id="line-294"> public void testScanMetricsByRegionForSingleRegion() throws Exception {</span>
-<span class="source-line-no">295</span><span id="line-295"> // Scan single row with row key bbb</span>
-<span class="source-line-no">296</span><span id="line-296"> byte[] bbc = Bytes.toBytes("bbc");</span>
-<span class="source-line-no">297</span><span id="line-297"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, true, bbc);</span>
-<span class="source-line-no">298</span><span id="line-298"> assertNotNull(scanMetrics);</span>
-<span class="source-line-no">299</span><span id="line-299"> Map<ScanMetricsRegionInfo, Map<String, Long>> scanMetricsByRegion =</span>
-<span class="source-line-no">300</span><span id="line-300"> scanMetrics.collectMetricsByRegion();</span>
-<span class="source-line-no">301</span><span id="line-301"> assertEquals(1, scanMetricsByRegion.size());</span>
-<span class="source-line-no">302</span><span id="line-302"> for (Map.Entry<ScanMetricsRegionInfo, Map<String, Long>> entry : scanMetricsByRegion</span>
-<span class="source-line-no">303</span><span id="line-303"> .entrySet()) {</span>
-<span class="source-line-no">304</span><span id="line-304"> ScanMetricsRegionInfo scanMetricsRegionInfo = entry.getKey();</span>
-<span class="source-line-no">305</span><span id="line-305"> Map<String, Long> metricsMap = entry.getValue();</span>
-<span class="source-line-no">306</span><span id="line-306"> assertNull(scanMetricsRegionInfo.getServerName());</span>
-<span class="source-line-no">307</span><span id="line-307"> assertNotNull(scanMetricsRegionInfo.getEncodedRegionName());</span>
-<span class="source-line-no">308</span><span id="line-308"> assertEquals(1, (long) metricsMap.get(REGIONS_SCANNED_METRIC_NAME));</span>
-<span class="source-line-no">309</span><span id="line-309"> assertEquals(1, (long) metricsMap.get(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME));</span>
-<span class="source-line-no">310</span><span id="line-310"> }</span>
-<span class="source-line-no">311</span><span id="line-311"> }</span>
-<span class="source-line-no">312</span><span id="line-312"></span>
-<span class="source-line-no">313</span><span id="line-313"> @Test</span>
-<span class="source-line-no">314</span><span id="line-314"> public void testScanMetricsByRegionForMultiRegion() throws Exception {</span>
-<span class="source-line-no">315</span><span id="line-315"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, true, yyy);</span>
-<span class="source-line-no">316</span><span id="line-316"> assertNotNull(scanMetrics);</span>
-<span class="source-line-no">317</span><span id="line-317"> Map<ScanMetricsRegionInfo, Map<String, Long>> scanMetricsByRegion =</span>
-<span class="source-line-no">318</span><span id="line-318"> scanMetrics.collectMetricsByRegion();</span>
-<span class="source-line-no">319</span><span id="line-319"> for (Map.Entry<ScanMetricsRegionInfo, Map<String, Long>> entry : scanMetricsByRegion</span>
-<span class="source-line-no">320</span><span id="line-320"> .entrySet()) {</span>
-<span class="source-line-no">321</span><span id="line-321"> ScanMetricsRegionInfo scanMetricsRegionInfo = entry.getKey();</span>
-<span class="source-line-no">322</span><span id="line-322"> Map<String, Long> metricsMap = entry.getValue();</span>
-<span class="source-line-no">323</span><span id="line-323"> assertNull(scanMetricsRegionInfo.getServerName());</span>
-<span class="source-line-no">324</span><span id="line-324"> assertNotNull(scanMetricsRegionInfo.getEncodedRegionName());</span>
-<span class="source-line-no">325</span><span id="line-325"> assertEquals(1, (long) metricsMap.get(REGIONS_SCANNED_METRIC_NAME));</span>
-<span class="source-line-no">326</span><span id="line-326"> }</span>
-<span class="source-line-no">327</span><span id="line-327"> }</span>
-<span class="source-line-no">328</span><span id="line-328"></span>
-<span class="source-line-no">329</span><span id="line-329"> @Test</span>
-<span class="source-line-no">330</span><span id="line-330"> public void testScannerWithRestoreScanner() throws Exception {</span>
-<span class="source-line-no">331</span><span id="line-331"> TableName tableName = TableName.valueOf("testScanner");</span>
-<span class="source-line-no">332</span><span id="line-332"> String snapshotName = "testScannerWithRestoreScanner";</span>
-<span class="source-line-no">333</span><span id="line-333"> try {</span>
-<span class="source-line-no">334</span><span id="line-334"> createTableAndSnapshot(UTIL, tableName, snapshotName, 50);</span>
-<span class="source-line-no">335</span><span id="line-335"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
-<span class="source-line-no">336</span><span id="line-336"> Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan</span>
-<span class="source-line-no">337</span><span id="line-337"></span>
-<span class="source-line-no">338</span><span id="line-338"> Configuration conf = UTIL.getConfiguration();</span>
-<span class="source-line-no">339</span><span id="line-339"> Path rootDir = CommonFSUtils.getRootDir(conf);</span>
-<span class="source-line-no">340</span><span id="line-340"></span>
-<span class="source-line-no">341</span><span id="line-341"> TableSnapshotScanner scanner0 =</span>
-<span class="source-line-no">342</span><span id="line-342"> new TableSnapshotScanner(conf, restoreDir, snapshotName, scan);</span>
-<span class="source-line-no">343</span><span id="line-343"> verifyScanner(scanner0, bbb, yyy);</span>
-<span class="source-line-no">344</span><span id="line-344"> scanner0.close();</span>
-<span class="source-line-no">345</span><span id="line-345"></span>
-<span class="source-line-no">346</span><span id="line-346"> // restore snapshot.</span>
-<span class="source-line-no">347</span><span id="line-347"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
-<span class="source-line-no">348</span><span id="line-348"></span>
-<span class="source-line-no">349</span><span id="line-349"> // scan the snapshot without restoring snapshot</span>
-<span class="source-line-no">350</span><span id="line-350"> TableSnapshotScanner scanner =</span>
-<span class="source-line-no">351</span><span id="line-351"> new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true);</span>
-<span class="source-line-no">352</span><span id="line-352"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">353</span><span id="line-353"> scanner.close();</span>
-<span class="source-line-no">354</span><span id="line-354"></span>
-<span class="source-line-no">355</span><span id="line-355"> // check whether the snapshot has been deleted by the close of scanner.</span>
-<span class="source-line-no">356</span><span id="line-356"> scanner = new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true);</span>
-<span class="source-line-no">357</span><span id="line-357"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">358</span><span id="line-358"> scanner.close();</span>
-<span class="source-line-no">359</span><span id="line-359"></span>
-<span class="source-line-no">360</span><span id="line-360"> // restore snapshot again.</span>
-<span class="source-line-no">361</span><span id="line-361"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
-<span class="source-line-no">362</span><span id="line-362"></span>
-<span class="source-line-no">363</span><span id="line-363"> // check whether the snapshot has been deleted by the close of scanner.</span>
-<span class="source-line-no">364</span><span id="line-364"> scanner = new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true);</span>
-<span class="source-line-no">365</span><span id="line-365"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">366</span><span id="line-366"> scanner.close();</span>
-<span class="source-line-no">367</span><span id="line-367"> } finally {</span>
-<span class="source-line-no">368</span><span id="line-368"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
-<span class="source-line-no">369</span><span id="line-369"> UTIL.deleteTable(tableName);</span>
-<span class="source-line-no">370</span><span id="line-370"> }</span>
-<span class="source-line-no">371</span><span id="line-371"> }</span>
-<span class="source-line-no">372</span><span id="line-372"></span>
-<span class="source-line-no">373</span><span id="line-373"> private void testScanner(HBaseTestingUtil util, String snapshotName, int numRegions,</span>
-<span class="source-line-no">374</span><span id="line-374"> boolean shutdownCluster) throws Exception {</span>
-<span class="source-line-no">375</span><span id="line-375"> TableName tableName = TableName.valueOf("testScanner");</span>
-<span class="source-line-no">376</span><span id="line-376"> try {</span>
-<span class="source-line-no">377</span><span id="line-377"> createTableAndSnapshot(util, tableName, snapshotName, numRegions);</span>
-<span class="source-line-no">378</span><span id="line-378"></span>
-<span class="source-line-no">379</span><span id="line-379"> if (shutdownCluster) {</span>
-<span class="source-line-no">380</span><span id="line-380"> util.shutdownMiniHBaseCluster();</span>
-<span class="source-line-no">381</span><span id="line-381"> clusterUp = false;</span>
-<span class="source-line-no">382</span><span id="line-382"> }</span>
-<span class="source-line-no">383</span><span id="line-383"></span>
-<span class="source-line-no">384</span><span id="line-384"> Path restoreDir = util.getDataTestDirOnTestFS(snapshotName);</span>
-<span class="source-line-no">385</span><span id="line-385"> Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan</span>
-<span class="source-line-no">386</span><span id="line-386"></span>
-<span class="source-line-no">387</span><span id="line-387"> TableSnapshotScanner scanner =</span>
-<span class="source-line-no">388</span><span id="line-388"> new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan);</span>
-<span class="source-line-no">389</span><span id="line-389"></span>
-<span class="source-line-no">390</span><span id="line-390"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">391</span><span id="line-391"> scanner.close();</span>
+<span class="source-line-no">052</span><span id="line-52">import org.apache.hadoop.hbase.snapshot.MobSnapshotTestingUtils;</span>
+<span class="source-line-no">053</span><span id="line-53">import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;</span>
+<span class="source-line-no">054</span><span id="line-54">import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils;</span>
+<span class="source-line-no">055</span><span id="line-55">import org.apache.hadoop.hbase.testclassification.ClientTests;</span>
+<span class="source-line-no">056</span><span id="line-56">import org.apache.hadoop.hbase.testclassification.LargeTests;</span>
+<span class="source-line-no">057</span><span id="line-57">import org.apache.hadoop.hbase.util.Bytes;</span>
+<span class="source-line-no">058</span><span id="line-58">import org.apache.hadoop.hbase.util.CommonFSUtils;</span>
+<span class="source-line-no">059</span><span id="line-59">import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;</span>
+<span class="source-line-no">060</span><span id="line-60">import org.apache.hadoop.hbase.util.FSUtils;</span>
+<span class="source-line-no">061</span><span id="line-61">import org.apache.hadoop.hbase.util.HFileArchiveUtil;</span>
+<span class="source-line-no">062</span><span id="line-62">import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;</span>
+<span class="source-line-no">063</span><span id="line-63">import org.junit.jupiter.api.AfterEach;</span>
+<span class="source-line-no">064</span><span id="line-64">import org.junit.jupiter.api.BeforeEach;</span>
+<span class="source-line-no">065</span><span id="line-65">import org.junit.jupiter.api.Tag;</span>
+<span class="source-line-no">066</span><span id="line-66">import org.junit.jupiter.api.Test;</span>
+<span class="source-line-no">067</span><span id="line-67">import org.junit.jupiter.api.TestInfo;</span>
+<span class="source-line-no">068</span><span id="line-68">import org.slf4j.Logger;</span>
+<span class="source-line-no">069</span><span id="line-69">import org.slf4j.LoggerFactory;</span>
+<span class="source-line-no">070</span><span id="line-70"></span>
+<span class="source-line-no">071</span><span id="line-71">@Tag(LargeTests.TAG)</span>
+<span class="source-line-no">072</span><span id="line-72">@Tag(ClientTests.TAG)</span>
+<span class="source-line-no">073</span><span id="line-73">public class TestTableSnapshotScanner {</span>
+<span class="source-line-no">074</span><span id="line-74"></span>
+<span class="source-line-no">075</span><span id="line-75"> private static final Logger LOG = LoggerFactory.getLogger(TestTableSnapshotScanner.class);</span>
+<span class="source-line-no">076</span><span id="line-76"> private final HBaseTestingUtil UTIL = new HBaseTestingUtil();</span>
+<span class="source-line-no">077</span><span id="line-77"> private static final int NUM_REGION_SERVERS = 2;</span>
+<span class="source-line-no">078</span><span id="line-78"> private static final byte[][] FAMILIES = { Bytes.toBytes("f1"), Bytes.toBytes("f2") };</span>
+<span class="source-line-no">079</span><span id="line-79"> public static byte[] bbb = Bytes.toBytes("bbb");</span>
+<span class="source-line-no">080</span><span id="line-80"> public static byte[] yyy = Bytes.toBytes("yyy");</span>
+<span class="source-line-no">081</span><span id="line-81"></span>
+<span class="source-line-no">082</span><span id="line-82"> private FileSystem fs;</span>
+<span class="source-line-no">083</span><span id="line-83"> private Path rootDir;</span>
+<span class="source-line-no">084</span><span id="line-84"> private boolean clusterUp;</span>
+<span class="source-line-no">085</span><span id="line-85"></span>
+<span class="source-line-no">086</span><span id="line-86"> private String methodName;</span>
+<span class="source-line-no">087</span><span id="line-87"></span>
+<span class="source-line-no">088</span><span id="line-88"> public static void blockUntilSplitFinished(HBaseTestingUtil util, TableName tableName,</span>
+<span class="source-line-no">089</span><span id="line-89"> int expectedRegionSize) throws Exception {</span>
+<span class="source-line-no">090</span><span id="line-90"> for (int i = 0; i < 100; i++) {</span>
+<span class="source-line-no">091</span><span id="line-91"> List<RegionInfo> hRegionInfoList = util.getAdmin().getRegions(tableName);</span>
+<span class="source-line-no">092</span><span id="line-92"> if (hRegionInfoList.size() >= expectedRegionSize) {</span>
+<span class="source-line-no">093</span><span id="line-93"> break;</span>
+<span class="source-line-no">094</span><span id="line-94"> }</span>
+<span class="source-line-no">095</span><span id="line-95"> Thread.sleep(1000);</span>
+<span class="source-line-no">096</span><span id="line-96"> }</span>
+<span class="source-line-no">097</span><span id="line-97"> }</span>
+<span class="source-line-no">098</span><span id="line-98"></span>
+<span class="source-line-no">099</span><span id="line-99"> @BeforeEach</span>
+<span class="source-line-no">100</span><span id="line-100"> public void setupCluster(TestInfo testInfo) throws Exception {</span>
+<span class="source-line-no">101</span><span id="line-101"> methodName = testInfo.getTestMethod().get().getName();</span>
+<span class="source-line-no">102</span><span id="line-102"> setupConf(UTIL.getConfiguration());</span>
+<span class="source-line-no">103</span><span id="line-103"> StartTestingClusterOption option =</span>
+<span class="source-line-no">104</span><span id="line-104"> StartTestingClusterOption.builder().numRegionServers(NUM_REGION_SERVERS)</span>
+<span class="source-line-no">105</span><span id="line-105"> .numDataNodes(NUM_REGION_SERVERS).createRootDir(true).build();</span>
+<span class="source-line-no">106</span><span id="line-106"> UTIL.startMiniCluster(option);</span>
+<span class="source-line-no">107</span><span id="line-107"> clusterUp = true;</span>
+<span class="source-line-no">108</span><span id="line-108"> rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();</span>
+<span class="source-line-no">109</span><span id="line-109"> fs = rootDir.getFileSystem(UTIL.getConfiguration());</span>
+<span class="source-line-no">110</span><span id="line-110"> }</span>
+<span class="source-line-no">111</span><span id="line-111"></span>
+<span class="source-line-no">112</span><span id="line-112"> @AfterEach</span>
+<span class="source-line-no">113</span><span id="line-113"> public void tearDownCluster() throws Exception {</span>
+<span class="source-line-no">114</span><span id="line-114"> if (clusterUp) {</span>
+<span class="source-line-no">115</span><span id="line-115"> UTIL.shutdownMiniCluster();</span>
+<span class="source-line-no">116</span><span id="line-116"> }</span>
+<span class="source-line-no">117</span><span id="line-117"> }</span>
+<span class="source-line-no">118</span><span id="line-118"></span>
+<span class="source-line-no">119</span><span id="line-119"> protected void setupConf(Configuration conf) {</span>
+<span class="source-line-no">120</span><span id="line-120"> // Enable snapshot</span>
+<span class="source-line-no">121</span><span id="line-121"> conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);</span>
+<span class="source-line-no">122</span><span id="line-122"> }</span>
+<span class="source-line-no">123</span><span id="line-123"></span>
+<span class="source-line-no">124</span><span id="line-124"> public static void createTableAndSnapshot(HBaseTestingUtil util, TableName tableName,</span>
+<span class="source-line-no">125</span><span id="line-125"> String snapshotName, int numRegions) throws Exception {</span>
+<span class="source-line-no">126</span><span id="line-126"> try {</span>
+<span class="source-line-no">127</span><span id="line-127"> util.deleteTable(tableName);</span>
+<span class="source-line-no">128</span><span id="line-128"> } catch (Exception ex) {</span>
+<span class="source-line-no">129</span><span id="line-129"> // ignore</span>
+<span class="source-line-no">130</span><span id="line-130"> }</span>
+<span class="source-line-no">131</span><span id="line-131"></span>
+<span class="source-line-no">132</span><span id="line-132"> if (numRegions > 1) {</span>
+<span class="source-line-no">133</span><span id="line-133"> util.createTable(tableName, FAMILIES, 1, bbb, yyy, numRegions);</span>
+<span class="source-line-no">134</span><span id="line-134"> } else {</span>
+<span class="source-line-no">135</span><span id="line-135"> util.createTable(tableName, FAMILIES);</span>
+<span class="source-line-no">136</span><span id="line-136"> }</span>
+<span class="source-line-no">137</span><span id="line-137"> Admin admin = util.getAdmin();</span>
+<span class="source-line-no">138</span><span id="line-138"></span>
+<span class="source-line-no">139</span><span id="line-139"> // put some stuff in the table</span>
+<span class="source-line-no">140</span><span id="line-140"> Table table = util.getConnection().getTable(tableName);</span>
+<span class="source-line-no">141</span><span id="line-141"> util.loadTable(table, FAMILIES);</span>
+<span class="source-line-no">142</span><span id="line-142"></span>
+<span class="source-line-no">143</span><span id="line-143"> Path rootDir = CommonFSUtils.getRootDir(util.getConfiguration());</span>
+<span class="source-line-no">144</span><span id="line-144"> FileSystem fs = rootDir.getFileSystem(util.getConfiguration());</span>
+<span class="source-line-no">145</span><span id="line-145"></span>
+<span class="source-line-no">146</span><span id="line-146"> SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, Arrays.asList(FAMILIES), null,</span>
+<span class="source-line-no">147</span><span id="line-147"> snapshotName, rootDir, fs, true);</span>
+<span class="source-line-no">148</span><span id="line-148"></span>
+<span class="source-line-no">149</span><span id="line-149"> // load different values</span>
+<span class="source-line-no">150</span><span id="line-150"> byte[] value = Bytes.toBytes("after_snapshot_value");</span>
+<span class="source-line-no">151</span><span id="line-151"> util.loadTable(table, FAMILIES, value);</span>
+<span class="source-line-no">152</span><span id="line-152"></span>
+<span class="source-line-no">153</span><span id="line-153"> // cause flush to create new files in the region</span>
+<span class="source-line-no">154</span><span id="line-154"> admin.flush(tableName);</span>
+<span class="source-line-no">155</span><span id="line-155"> table.close();</span>
+<span class="source-line-no">156</span><span id="line-156"> }</span>
+<span class="source-line-no">157</span><span id="line-157"></span>
+<span class="source-line-no">158</span><span id="line-158"> @Test</span>
+<span class="source-line-no">159</span><span id="line-159"> public void testNoDuplicateResultsWhenSplitting() throws Exception {</span>
+<span class="source-line-no">160</span><span id="line-160"> TableName tableName = TableName.valueOf("testNoDuplicateResultsWhenSplitting");</span>
+<span class="source-line-no">161</span><span id="line-161"> String snapshotName = "testSnapshotBug";</span>
+<span class="source-line-no">162</span><span id="line-162"> try {</span>
+<span class="source-line-no">163</span><span id="line-163"> if (UTIL.getAdmin().tableExists(tableName)) {</span>
+<span class="source-line-no">164</span><span id="line-164"> UTIL.deleteTable(tableName);</span>
+<span class="source-line-no">165</span><span id="line-165"> }</span>
+<span class="source-line-no">166</span><span id="line-166"></span>
+<span class="source-line-no">167</span><span id="line-167"> UTIL.createTable(tableName, FAMILIES);</span>
+<span class="source-line-no">168</span><span id="line-168"> Admin admin = UTIL.getAdmin();</span>
+<span class="source-line-no">169</span><span id="line-169"></span>
+<span class="source-line-no">170</span><span id="line-170"> // put some stuff in the table</span>
+<span class="source-line-no">171</span><span id="line-171"> Table table = UTIL.getConnection().getTable(tableName);</span>
+<span class="source-line-no">172</span><span id="line-172"> UTIL.loadTable(table, FAMILIES);</span>
+<span class="source-line-no">173</span><span id="line-173"></span>
+<span class="source-line-no">174</span><span id="line-174"> // split to 2 regions</span>
+<span class="source-line-no">175</span><span id="line-175"> admin.split(tableName, Bytes.toBytes("eee"));</span>
+<span class="source-line-no">176</span><span id="line-176"> blockUntilSplitFinished(UTIL, tableName, 2);</span>
+<span class="source-line-no">177</span><span id="line-177"></span>
+<span class="source-line-no">178</span><span id="line-178"> Path rootDir = CommonFSUtils.getRootDir(UTIL.getConfiguration());</span>
+<span class="source-line-no">179</span><span id="line-179"> FileSystem fs = rootDir.getFileSystem(UTIL.getConfiguration());</span>
+<span class="source-line-no">180</span><span id="line-180"></span>
+<span class="source-line-no">181</span><span id="line-181"> SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, Arrays.asList(FAMILIES),</span>
+<span class="source-line-no">182</span><span id="line-182"> null, snapshotName, rootDir, fs, true);</span>
+<span class="source-line-no">183</span><span id="line-183"></span>
+<span class="source-line-no">184</span><span id="line-184"> // load different values</span>
+<span class="source-line-no">185</span><span id="line-185"> byte[] value = Bytes.toBytes("after_snapshot_value");</span>
+<span class="source-line-no">186</span><span id="line-186"> UTIL.loadTable(table, FAMILIES, value);</span>
+<span class="source-line-no">187</span><span id="line-187"></span>
+<span class="source-line-no">188</span><span id="line-188"> // cause flush to create new files in the region</span>
+<span class="source-line-no">189</span><span id="line-189"> admin.flush(tableName);</span>
+<span class="source-line-no">190</span><span id="line-190"> table.close();</span>
+<span class="source-line-no">191</span><span id="line-191"></span>
+<span class="source-line-no">192</span><span id="line-192"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
+<span class="source-line-no">193</span><span id="line-193"> Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan</span>
+<span class="source-line-no">194</span><span id="line-194"></span>
+<span class="source-line-no">195</span><span id="line-195"> TableSnapshotScanner scanner =</span>
+<span class="source-line-no">196</span><span id="line-196"> new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan);</span>
+<span class="source-line-no">197</span><span id="line-197"></span>
+<span class="source-line-no">198</span><span id="line-198"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">199</span><span id="line-199"> scanner.close();</span>
+<span class="source-line-no">200</span><span id="line-200"> } catch (Exception e) {</span>
+<span class="source-line-no">201</span><span id="line-201"> e.printStackTrace();</span>
+<span class="source-line-no">202</span><span id="line-202"> } finally {</span>
+<span class="source-line-no">203</span><span id="line-203"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
+<span class="source-line-no">204</span><span id="line-204"> UTIL.deleteTable(tableName);</span>
+<span class="source-line-no">205</span><span id="line-205"> }</span>
+<span class="source-line-no">206</span><span id="line-206"> }</span>
+<span class="source-line-no">207</span><span id="line-207"></span>
+<span class="source-line-no">208</span><span id="line-208"> @Test</span>
+<span class="source-line-no">209</span><span id="line-209"> public void testScanLimit() throws Exception {</span>
+<span class="source-line-no">210</span><span id="line-210"> final TableName tableName = TableName.valueOf(methodName);</span>
+<span class="source-line-no">211</span><span id="line-211"> final String snapshotName = tableName + "Snapshot";</span>
+<span class="source-line-no">212</span><span id="line-212"> TableSnapshotScanner scanner = null;</span>
+<span class="source-line-no">213</span><span id="line-213"> try {</span>
+<span class="source-line-no">214</span><span id="line-214"> createTableAndSnapshot(UTIL, tableName, snapshotName, 50);</span>
+<span class="source-line-no">215</span><span id="line-215"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
+<span class="source-line-no">216</span><span id="line-216"> Scan scan = new Scan().withStartRow(bbb).setLimit(100); // limit the scan</span>
+<span class="source-line-no">217</span><span id="line-217"></span>
+<span class="source-line-no">218</span><span id="line-218"> scanner = new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan);</span>
+<span class="source-line-no">219</span><span id="line-219"> int count = 0;</span>
+<span class="source-line-no">220</span><span id="line-220"> while (true) {</span>
+<span class="source-line-no">221</span><span id="line-221"> Result result = scanner.next();</span>
+<span class="source-line-no">222</span><span id="line-222"> if (result == null) {</span>
+<span class="source-line-no">223</span><span id="line-223"> break;</span>
+<span class="source-line-no">224</span><span id="line-224"> }</span>
+<span class="source-line-no">225</span><span id="line-225"> count++;</span>
+<span class="source-line-no">226</span><span id="line-226"> }</span>
+<span class="source-line-no">227</span><span id="line-227"> assertEquals(100, count);</span>
+<span class="source-line-no">228</span><span id="line-228"> } finally {</span>
+<span class="source-line-no">229</span><span id="line-229"> if (scanner != null) {</span>
+<span class="source-line-no">230</span><span id="line-230"> scanner.close();</span>
+<span class="source-line-no">231</span><span id="line-231"> }</span>
+<span class="source-line-no">232</span><span id="line-232"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
+<span class="source-line-no">233</span><span id="line-233"> UTIL.deleteTable(tableName);</span>
+<span class="source-line-no">234</span><span id="line-234"> }</span>
+<span class="source-line-no">235</span><span id="line-235"> }</span>
+<span class="source-line-no">236</span><span id="line-236"></span>
+<span class="source-line-no">237</span><span id="line-237"> @Test</span>
+<span class="source-line-no">238</span><span id="line-238"> public void testWithSingleRegion() throws Exception {</span>
+<span class="source-line-no">239</span><span id="line-239"> testScanner(UTIL, "testWithSingleRegion", 1, false);</span>
+<span class="source-line-no">240</span><span id="line-240"> }</span>
+<span class="source-line-no">241</span><span id="line-241"></span>
+<span class="source-line-no">242</span><span id="line-242"> @Test</span>
+<span class="source-line-no">243</span><span id="line-243"> public void testWithMultiRegion() throws Exception {</span>
+<span class="source-line-no">244</span><span id="line-244"> testScanner(UTIL, "testWithMultiRegion", 10, false);</span>
+<span class="source-line-no">245</span><span id="line-245"> }</span>
+<span class="source-line-no">246</span><span id="line-246"></span>
+<span class="source-line-no">247</span><span id="line-247"> @Test</span>
+<span class="source-line-no">248</span><span id="line-248"> public void testWithOfflineHBaseMultiRegion() throws Exception {</span>
+<span class="source-line-no">249</span><span id="line-249"> testScanner(UTIL, "testWithMultiRegion", 20, true);</span>
+<span class="source-line-no">250</span><span id="line-250"> }</span>
+<span class="source-line-no">251</span><span id="line-251"></span>
+<span class="source-line-no">252</span><span id="line-252"> private ScanMetrics createTableSnapshotScannerAndGetScanMetrics(boolean enableScanMetrics,</span>
+<span class="source-line-no">253</span><span id="line-253"> boolean enableScanMetricsByRegion, byte[] endKey) throws Exception {</span>
+<span class="source-line-no">254</span><span id="line-254"> TableName tableName = TableName.valueOf(methodName + "_TABLE");</span>
+<span class="source-line-no">255</span><span id="line-255"> String snapshotName = methodName + "_SNAPSHOT";</span>
+<span class="source-line-no">256</span><span id="line-256"> try {</span>
+<span class="source-line-no">257</span><span id="line-257"> createTableAndSnapshot(UTIL, tableName, snapshotName, 50);</span>
+<span class="source-line-no">258</span><span id="line-258"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
+<span class="source-line-no">259</span><span id="line-259"> Scan scan = new Scan().withStartRow(bbb).withStopRow(endKey);</span>
+<span class="source-line-no">260</span><span id="line-260"> scan.setScanMetricsEnabled(enableScanMetrics);</span>
+<span class="source-line-no">261</span><span id="line-261"> scan.setEnableScanMetricsByRegion(enableScanMetricsByRegion);</span>
+<span class="source-line-no">262</span><span id="line-262"> Configuration conf = UTIL.getConfiguration();</span>
+<span class="source-line-no">263</span><span id="line-263"></span>
+<span class="source-line-no">264</span><span id="line-264"> TableSnapshotScanner snapshotScanner =</span>
+<span class="source-line-no">265</span><span id="line-265"> new TableSnapshotScanner(conf, restoreDir, snapshotName, scan);</span>
+<span class="source-line-no">266</span><span id="line-266"> verifyScanner(snapshotScanner, bbb, endKey);</span>
+<span class="source-line-no">267</span><span id="line-267"> return snapshotScanner.getScanMetrics();</span>
+<span class="source-line-no">268</span><span id="line-268"> } finally {</span>
+<span class="source-line-no">269</span><span id="line-269"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
+<span class="source-line-no">270</span><span id="line-270"> UTIL.deleteTable(tableName);</span>
+<span class="source-line-no">271</span><span id="line-271"> }</span>
+<span class="source-line-no">272</span><span id="line-272"> }</span>
+<span class="source-line-no">273</span><span id="line-273"></span>
+<span class="source-line-no">274</span><span id="line-274"> @Test</span>
+<span class="source-line-no">275</span><span id="line-275"> public void testScanMetricsDisabled() throws Exception {</span>
+<span class="source-line-no">276</span><span id="line-276"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(false, false, yyy);</span>
+<span class="source-line-no">277</span><span id="line-277"> assertNull(scanMetrics);</span>
+<span class="source-line-no">278</span><span id="line-278"> }</span>
+<span class="source-line-no">279</span><span id="line-279"></span>
+<span class="source-line-no">280</span><span id="line-280"> @Test</span>
+<span class="source-line-no">281</span><span id="line-281"> public void testScanMetricsWithScanMetricsByRegionDisabled() throws Exception {</span>
+<span class="source-line-no">282</span><span id="line-282"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, false, yyy);</span>
+<span class="source-line-no">283</span><span id="line-283"> assertNotNull(scanMetrics);</span>
+<span class="source-line-no">284</span><span id="line-284"> int rowsScanned = 0;</span>
+<span class="source-line-no">285</span><span id="line-285"> for (byte[] row : HBaseTestingUtil.ROWS) {</span>
+<span class="source-line-no">286</span><span id="line-286"> if (Bytes.compareTo(row, bbb) >= 0 && Bytes.compareTo(row, yyy) < 0) {</span>
+<span class="source-line-no">287</span><span id="line-287"> rowsScanned++;</span>
+<span class="source-line-no">288</span><span id="line-288"> }</span>
+<span class="source-line-no">289</span><span id="line-289"> }</span>
+<span class="source-line-no">290</span><span id="line-290"> Map<String, Long> metricsMap = scanMetrics.getMetricsMap();</span>
+<span class="source-line-no">291</span><span id="line-291"> assertEquals(rowsScanned, (long) metricsMap.get(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME));</span>
+<span class="source-line-no">292</span><span id="line-292"> }</span>
+<span class="source-line-no">293</span><span id="line-293"></span>
+<span class="source-line-no">294</span><span id="line-294"> @Test</span>
+<span class="source-line-no">295</span><span id="line-295"> public void testScanMetricsByRegionForSingleRegion() throws Exception {</span>
+<span class="source-line-no">296</span><span id="line-296"> // Scan single row with row key bbb</span>
+<span class="source-line-no">297</span><span id="line-297"> byte[] bbc = Bytes.toBytes("bbc");</span>
+<span class="source-line-no">298</span><span id="line-298"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, true, bbc);</span>
+<span class="source-line-no">299</span><span id="line-299"> assertNotNull(scanMetrics);</span>
+<span class="source-line-no">300</span><span id="line-300"> Map<ScanMetricsRegionInfo, Map<String, Long>> scanMetricsByRegion =</span>
+<span class="source-line-no">301</span><span id="line-301"> scanMetrics.collectMetricsByRegion();</span>
+<span class="source-line-no">302</span><span id="line-302"> assertEquals(1, scanMetricsByRegion.size());</span>
+<span class="source-line-no">303</span><span id="line-303"> for (Map.Entry<ScanMetricsRegionInfo, Map<String, Long>> entry : scanMetricsByRegion</span>
+<span class="source-line-no">304</span><span id="line-304"> .entrySet()) {</span>
+<span class="source-line-no">305</span><span id="line-305"> ScanMetricsRegionInfo scanMetricsRegionInfo = entry.getKey();</span>
+<span class="source-line-no">306</span><span id="line-306"> Map<String, Long> metricsMap = entry.getValue();</span>
+<span class="source-line-no">307</span><span id="line-307"> assertNull(scanMetricsRegionInfo.getServerName());</span>
+<span class="source-line-no">308</span><span id="line-308"> assertNotNull(scanMetricsRegionInfo.getEncodedRegionName());</span>
+<span class="source-line-no">309</span><span id="line-309"> assertEquals(1, (long) metricsMap.get(REGIONS_SCANNED_METRIC_NAME));</span>
+<span class="source-line-no">310</span><span id="line-310"> assertEquals(1, (long) metricsMap.get(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME));</span>
+<span class="source-line-no">311</span><span id="line-311"> }</span>
+<span class="source-line-no">312</span><span id="line-312"> }</span>
+<span class="source-line-no">313</span><span id="line-313"></span>
+<span class="source-line-no">314</span><span id="line-314"> @Test</span>
+<span class="source-line-no">315</span><span id="line-315"> public void testScanMetricsByRegionForMultiRegion() throws Exception {</span>
+<span class="source-line-no">316</span><span id="line-316"> ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, true, yyy);</span>
+<span class="source-line-no">317</span><span id="line-317"> assertNotNull(scanMetrics);</span>
+<span class="source-line-no">318</span><span id="line-318"> Map<ScanMetricsRegionInfo, Map<String, Long>> scanMetricsByRegion =</span>
+<span class="source-line-no">319</span><span id="line-319"> scanMetrics.collectMetricsByRegion();</span>
+<span class="source-line-no">320</span><span id="line-320"> for (Map.Entry<ScanMetricsRegionInfo, Map<String, Long>> entry : scanMetricsByRegion</span>
+<span class="source-line-no">321</span><span id="line-321"> .entrySet()) {</span>
+<span class="source-line-no">322</span><span id="line-322"> ScanMetricsRegionInfo scanMetricsRegionInfo = entry.getKey();</span>
+<span class="source-line-no">323</span><span id="line-323"> Map<String, Long> metricsMap = entry.getValue();</span>
+<span class="source-line-no">324</span><span id="line-324"> assertNull(scanMetricsRegionInfo.getServerName());</span>
+<span class="source-line-no">325</span><span id="line-325"> assertNotNull(scanMetricsRegionInfo.getEncodedRegionName());</span>
+<span class="source-line-no">326</span><span id="line-326"> assertEquals(1, (long) metricsMap.get(REGIONS_SCANNED_METRIC_NAME));</span>
+<span class="source-line-no">327</span><span id="line-327"> }</span>
+<span class="source-line-no">328</span><span id="line-328"> }</span>
+<span class="source-line-no">329</span><span id="line-329"></span>
+<span class="source-line-no">330</span><span id="line-330"> @Test</span>
+<span class="source-line-no">331</span><span id="line-331"> public void testScannerWithRestoreScanner() throws Exception {</span>
+<span class="source-line-no">332</span><span id="line-332"> TableName tableName = TableName.valueOf("testScanner");</span>
+<span class="source-line-no">333</span><span id="line-333"> String snapshotName = "testScannerWithRestoreScanner";</span>
+<span class="source-line-no">334</span><span id="line-334"> try {</span>
+<span class="source-line-no">335</span><span id="line-335"> createTableAndSnapshot(UTIL, tableName, snapshotName, 50);</span>
+<span class="source-line-no">336</span><span id="line-336"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
+<span class="source-line-no">337</span><span id="line-337"> Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan</span>
+<span class="source-line-no">338</span><span id="line-338"></span>
+<span class="source-line-no">339</span><span id="line-339"> Configuration conf = UTIL.getConfiguration();</span>
+<span class="source-line-no">340</span><span id="line-340"> Path rootDir = CommonFSUtils.getRootDir(conf);</span>
+<span class="source-line-no">341</span><span id="line-341"></span>
+<span class="source-line-no">342</span><span id="line-342"> TableSnapshotScanner scanner0 =</span>
+<span class="source-line-no">343</span><span id="line-343"> new TableSnapshotScanner(conf, restoreDir, snapshotName, scan);</span>
+<span class="source-line-no">344</span><span id="line-344"> verifyScanner(scanner0, bbb, yyy);</span>
+<span class="source-line-no">345</span><span id="line-345"> scanner0.close();</span>
+<span class="source-line-no">346</span><span id="line-346"></span>
+<span class="source-line-no">347</span><span id="line-347"> // restore snapshot.</span>
+<span class="source-line-no">348</span><span id="line-348"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
+<span class="source-line-no">349</span><span id="line-349"></span>
+<span class="source-line-no">350</span><span id="line-350"> // scan the snapshot without restoring snapshot</span>
+<span class="source-line-no">351</span><span id="line-351"> TableSnapshotScanner scanner =</span>
+<span class="source-line-no">352</span><span id="line-352"> new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true);</span>
+<span class="source-line-no">353</span><span id="line-353"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">354</span><span id="line-354"> scanner.close();</span>
+<span class="source-line-no">355</span><span id="line-355"></span>
+<span class="source-line-no">356</span><span id="line-356"> // check whether the snapshot has been deleted by the close of scanner.</span>
+<span class="source-line-no">357</span><span id="line-357"> scanner = new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true);</span>
+<span class="source-line-no">358</span><span id="line-358"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">359</span><span id="line-359"> scanner.close();</span>
+<span class="source-line-no">360</span><span id="line-360"></span>
+<span class="source-line-no">361</span><span id="line-361"> // restore snapshot again.</span>
+<span class="source-line-no">362</span><span id="line-362"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
+<span class="source-line-no">363</span><span id="line-363"></span>
+<span class="source-line-no">364</span><span id="line-364"> // check whether the snapshot has been deleted by the close of scanner.</span>
+<span class="source-line-no">365</span><span id="line-365"> scanner = new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true);</span>
+<span class="source-line-no">366</span><span id="line-366"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">367</span><span id="line-367"> scanner.close();</span>
+<span class="source-line-no">368</span><span id="line-368"> } finally {</span>
+<span class="source-line-no">369</span><span id="line-369"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
+<span class="source-line-no">370</span><span id="line-370"> UTIL.deleteTable(tableName);</span>
+<span class="source-line-no">371</span><span id="line-371"> }</span>
+<span class="source-line-no">372</span><span id="line-372"> }</span>
+<span class="source-line-no">373</span><span id="line-373"></span>
+<span class="source-line-no">374</span><span id="line-374"> @Test</span>
+<span class="source-line-no">375</span><span id="line-375"> public void testScannerWithRestoredMobSnapshot() throws Exception {</span>
+<span class="source-line-no">376</span><span id="line-376"> TableName tableName = TableName.valueOf(methodName);</span>
+<span class="source-line-no">377</span><span id="line-377"> String snapshotName = methodName + "Snapshot";</span>
+<span class="source-line-no">378</span><span id="line-378"> Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName);</span>
+<span class="source-line-no">379</span><span id="line-379"> try {</span>
+<span class="source-line-no">380</span><span id="line-380"> MobSnapshotTestingUtils.createMobTable(UTIL, tableName, new byte[0][], 1, FAMILIES);</span>
+<span class="source-line-no">381</span><span id="line-381"> try (Table table = UTIL.getConnection().getTable(tableName)) {</span>
+<span class="source-line-no">382</span><span id="line-382"> UTIL.loadTable(table, FAMILIES);</span>
+<span class="source-line-no">383</span><span id="line-383"> }</span>
+<span class="source-line-no">384</span><span id="line-384"> UTIL.getAdmin().snapshot(snapshotName, tableName);</span>
+<span class="source-line-no">385</span><span id="line-385"></span>
+<span class="source-line-no">386</span><span id="line-386"> Configuration conf = UTIL.getConfiguration();</span>
+<span class="source-line-no">387</span><span id="line-387"> RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName);</span>
+<span class="source-line-no">388</span><span id="line-388"> try (TableSnapshotScanner scanner = new TableSnapshotScanner(conf, rootDir, restoreDir,</span>
+<span class="source-line-no">389</span><span id="line-389"> snapshotName, new Scan().withStartRow(Bytes.toBytes("zzzz")), true)) {</span>
+<span class="source-line-no">390</span><span id="line-390"> assertNull(scanner.next());</span>
+<span class="source-line-no">391</span><span id="line-391"> }</span>
<span class="source-line-no">392</span><span id="line-392"> } finally {</span>
-<span class="source-line-no">393</span><span id="line-393"> if (clusterUp) {</span>
-<span class="source-line-no">394</span><span id="line-394"> util.getAdmin().deleteSnapshot(snapshotName);</span>
-<span class="source-line-no">395</span><span id="line-395"> util.deleteTable(tableName);</span>
-<span class="source-line-no">396</span><span id="line-396"> }</span>
-<span class="source-line-no">397</span><span id="line-397"> }</span>
-<span class="source-line-no">398</span><span id="line-398"> }</span>
-<span class="source-line-no">399</span><span id="line-399"></span>
-<span class="source-line-no">400</span><span id="line-400"> private void verifyScanner(ResultScanner scanner, byte[] startRow, byte[] stopRow)</span>
-<span class="source-line-no">401</span><span id="line-401"> throws IOException, InterruptedException {</span>
-<span class="source-line-no">402</span><span id="line-402"></span>
-<span class="source-line-no">403</span><span id="line-403"> HBaseTestingUtil.SeenRowTracker rowTracker =</span>
-<span class="source-line-no">404</span><span id="line-404"> new HBaseTestingUtil.SeenRowTracker(startRow, stopRow);</span>
-<span class="source-line-no">405</span><span id="line-405"></span>
-<span class="source-line-no">406</span><span id="line-406"> while (true) {</span>
-<span class="source-line-no">407</span><span id="line-407"> Result result = scanner.next();</span>
-<span class="source-line-no">408</span><span id="line-408"> if (result == null) {</span>
-<span class="source-line-no">409</span><span id="line-409"> break;</span>
-<span class="source-line-no">410</span><span id="line-410"> }</span>
-<span class="source-line-no">411</span><span id="line-411"> verifyRow(result);</span>
-<span class="source-line-no">412</span><span id="line-412"> rowTracker.addRow(result.getRow());</span>
-<span class="source-line-no">413</span><span id="line-413"> }</span>
-<span class="source-line-no">414</span><span id="line-414"></span>
-<span class="source-line-no">415</span><span id="line-415"> // validate all rows are seen</span>
-<span class="source-line-no">416</span><span id="line-416"> rowTracker.validate();</span>
-<span class="source-line-no">417</span><span id="line-417"> }</span>
-<span class="source-line-no">418</span><span id="line-418"></span>
-<span class="source-line-no">419</span><span id="line-419"> private static void verifyRow(Result result) throws IOException {</span>
-<span class="source-line-no">420</span><span id="line-420"> byte[] row = result.getRow();</span>
-<span class="source-line-no">421</span><span id="line-421"> CellScanner scanner = result.cellScanner();</span>
-<span class="source-line-no">422</span><span id="line-422"> while (scanner.advance()) {</span>
-<span class="source-line-no">423</span><span id="line-423"> Cell cell = scanner.current();</span>
-<span class="source-line-no">424</span><span id="line-424"></span>
-<span class="source-line-no">425</span><span id="line-425"> // assert that all Cells in the Result have the same key</span>
-<span class="source-line-no">426</span><span id="line-426"> assertEquals(0, Bytes.compareTo(row, 0, row.length, cell.getRowArray(), cell.getRowOffset(),</span>
-<span class="source-line-no">427</span><span id="line-427"> cell.getRowLength()));</span>
-<span class="source-line-no">428</span><span id="line-428"> }</span>
-<span class="source-line-no">429</span><span id="line-429"></span>
-<span class="source-line-no">430</span><span id="line-430"> for (int j = 0; j < FAMILIES.length; j++) {</span>
-<span class="source-line-no">431</span><span id="line-431"> byte[] actual = result.getValue(FAMILIES[j], FAMILIES[j]);</span>
-<span class="source-line-no">432</span><span id="line-432"> assertArrayEquals(row, actual, "Row in snapshot does not match, expected:"</span>
-<span class="source-line-no">433</span><span id="line-433"> + Bytes.toString(row) + " ,actual:" + Bytes.toString(actual));</span>
-<span class="source-line-no">434</span><span id="line-434"> }</span>
-<span class="source-line-no">435</span><span id="line-435"> }</span>
-<span class="source-line-no">436</span><span id="line-436"></span>
-<span class="source-line-no">437</span><span id="line-437"> @Test</span>
-<span class="source-line-no">438</span><span id="line-438"> public void testMergeRegion() throws Exception {</span>
-<span class="source-line-no">439</span><span id="line-439"> TableName tableName = TableName.valueOf("testMergeRegion");</span>
-<span class="source-line-no">440</span><span id="line-440"> String snapshotName = tableName.getNameAsString() + "_snapshot";</span>
-<span class="source-line-no">441</span><span id="line-441"> Configuration conf = UTIL.getConfiguration();</span>
-<span class="source-line-no">442</span><span id="line-442"> Path rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();</span>
-<span class="source-line-no">443</span><span id="line-443"> long timeout = 20000; // 20s</span>
-<span class="source-line-no">444</span><span id="line-444"> try (Admin admin = UTIL.getAdmin()) {</span>
-<span class="source-line-no">445</span><span id="line-445"> List<String> serverList = admin.getRegionServers().stream().map(sn -> sn.getServerName())</span>
-<span class="source-line-no">446</span><span id="line-446"> .collect(Collectors.toList());</span>
-<span class="source-line-no">447</span><span id="line-447"> // create table with 3 regions</span>
-<span class="source-line-no">448</span><span id="line-448"> Table table = UTIL.createTable(tableName, FAMILIES, 1, bbb, yyy, 3);</span>
-<span class="source-line-no">449</span><span id="line-449"> List<RegionInfo> regions = admin.getRegions(tableName);</span>
-<span class="source-line-no">450</span><span id="line-450"> assertEquals(3, regions.size());</span>
-<span class="source-line-no">451</span><span id="line-451"> RegionInfo region0 = regions.get(0);</span>
-<span class="source-line-no">452</span><span id="line-452"> RegionInfo region1 = regions.get(1);</span>
-<span class="source-line-no">453</span><span id="line-453"> RegionInfo region2 = regions.get(2);</span>
-<span class="source-line-no">454</span><span id="line-454"> // put some data in the table</span>
-<span class="source-line-no">455</span><span id="line-455"> UTIL.loadTable(table, FAMILIES);</span>
-<span class="source-line-no">456</span><span id="line-456"> admin.flush(tableName);</span>
-<span class="source-line-no">457</span><span id="line-457"> // wait flush is finished</span>
-<span class="source-line-no">458</span><span id="line-458"> UTIL.waitFor(timeout, () -> {</span>
-<span class="source-line-no">459</span><span id="line-459"> try {</span>
-<span class="source-line-no">460</span><span id="line-460"> Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName);</span>
-<span class="source-line-no">461</span><span id="line-461"> for (RegionInfo region : regions) {</span>
-<span class="source-line-no">462</span><span id="line-462"> Path regionDir = new Path(tableDir, region.getEncodedName());</span>
-<span class="source-line-no">463</span><span id="line-463"> for (Path familyDir : FSUtils.getFamilyDirs(fs, regionDir)) {</span>
-<span class="source-line-no">464</span><span id="line-464"> for (FileStatus fs : fs.listStatus(familyDir)) {</span>
-<span class="source-line-no">465</span><span id="line-465"> if (!fs.getPath().getName().equals(".filelist")) {</span>
-<span class="source-line-no">466</span><span id="line-466"> return true;</span>
-<span class="source-line-no">467</span><span id="line-467"> }</span>
-<span class="source-line-no">468</span><span id="line-468"> }</span>
-<span class="source-line-no">469</span><span id="line-469"> return false;</span>
-<span class="source-line-no">470</span><span id="line-470"> }</span>
-<span class="source-line-no">471</span><span id="line-471"> }</span>
-<span class="source-line-no">472</span><span id="line-472"> return true;</span>
-<span class="source-line-no">473</span><span id="line-473"> } catch (IOException e) {</span>
-<span class="source-line-no">474</span><span id="line-474"> LOG.warn("Failed check if flush is finished", e);</span>
-<span class="source-line-no">475</span><span id="line-475"> return false;</span>
-<span class="source-line-no">476</span><span id="line-476"> }</span>
-<span class="source-line-no">477</span><span id="line-477"> });</span>
-<span class="source-line-no">478</span><span id="line-478"> // merge 2 regions</span>
-<span class="source-line-no">479</span><span id="line-479"> admin.compactionSwitch(false, serverList);</span>
-<span class="source-line-no">480</span><span id="line-480"> admin.mergeRegionsAsync(region0.getEncodedNameAsBytes(), region1.getEncodedNameAsBytes(),</span>
-<span class="source-line-no">481</span><span id="line-481"> true);</span>
-<span class="source-line-no">482</span><span id="line-482"> UTIL.waitFor(timeout, () -> admin.getRegions(tableName).size() == 2);</span>
-<span class="source-line-no">483</span><span id="line-483"> List<RegionInfo> mergedRegions = admin.getRegions(tableName);</span>
-<span class="source-line-no">484</span><span id="line-484"> RegionInfo mergedRegion =</span>
-<span class="source-line-no">485</span><span id="line-485"> mergedRegions.get(0).getEncodedName().equals(region2.getEncodedName())</span>
-<span class="source-line-no">486</span><span id="line-486"> ? mergedRegions.get(1)</span>
-<span class="source-line-no">487</span><span id="line-487"> : mergedRegions.get(0);</span>
-<span class="source-line-no">488</span><span id="line-488"> // snapshot</span>
-<span class="source-line-no">489</span><span id="line-489"> admin.snapshot(snapshotName, tableName);</span>
-<span class="source-line-no">490</span><span id="line-490"> assertEquals(1, admin.listSnapshots().size());</span>
-<span class="source-line-no">491</span><span id="line-491"> // major compact</span>
-<span class="source-line-no">492</span><span id="line-492"> admin.compactionSwitch(true, serverList);</span>
-<span class="source-line-no">493</span><span id="line-493"> admin.majorCompactRegion(mergedRegion.getRegionName());</span>
-<span class="source-line-no">494</span><span id="line-494"> // wait until merged region has no reference</span>
-<span class="source-line-no">495</span><span id="line-495"> UTIL.waitFor(timeout, () -> {</span>
-<span class="source-line-no">496</span><span id="line-496"> try {</span>
-<span class="source-line-no">497</span><span id="line-497"> for (RegionServerThread regionServerThread : UTIL.getMiniHBaseCluster()</span>
-<span class="source-line-no">498</span><span id="line-498"> .getRegionServerThreads()) {</span>
-<span class="source-line-no">499</span><span id="line-499"> HRegionServer regionServer = regionServerThread.getRegionServer();</span>
-<span class="source-line-no">500</span><span id="line-500"> for (HRegion subRegion : regionServer.getRegions(tableName)) {</span>
-<span class="source-line-no">501</span><span id="line-501"> if (</span>
-<span class="source-line-no">502</span><span id="line-502"> subRegion.getRegionInfo().getEncodedName().equals(mergedRegion.getEncodedName())</span>
-<span class="source-line-no">503</span><span id="line-503"> ) {</span>
-<span class="source-line-no">504</span><span id="line-504"> regionServer.getCompactedHFilesDischarger().chore();</span>
-<span class="source-line-no">505</span><span id="line-505"> }</span>
-<span class="source-line-no">506</span><span id="line-506"> }</span>
-<span class="source-line-no">507</span><span id="line-507"> }</span>
-<span class="source-line-no">508</span><span id="line-508"> Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName);</span>
-<span class="source-line-no">509</span><span id="line-509"> HRegionFileSystem regionFs = HRegionFileSystem</span>
-<span class="source-line-no">510</span><span id="line-510"> .openRegionFromFileSystem(UTIL.getConfiguration(), fs, tableDir, mergedRegion, true);</span>
-<span class="source-line-no">511</span><span id="line-511"> boolean references = false;</span>
-<span class="source-line-no">512</span><span id="line-512"> Path regionDir = new Path(tableDir, mergedRegion.getEncodedName());</span>
-<span class="source-line-no">513</span><span id="line-513"> for (Path familyDir : FSUtils.getFamilyDirs(fs, regionDir)) {</span>
-<span class="source-line-no">514</span><span id="line-514"> StoreContext storeContext = StoreContext.getBuilder()</span>
-<span class="source-line-no">515</span><span id="line-515"> .withColumnFamilyDescriptor(ColumnFamilyDescriptorBuilder.of(familyDir.getName()))</span>
-<span class="source-line-no">516</span><span id="line-516"> .withRegionFileSystem(regionFs).withFamilyStoreDirectoryPath(familyDir).build();</span>
-<span class="source-line-no">517</span><span id="line-517"> StoreFileTracker sft =</span>
-<span class="source-line-no">518</span><span id="line-518"> StoreFileTrackerFactory.create(UTIL.getConfiguration(), false, storeContext);</span>
-<span class="source-line-no">519</span><span id="line-519"> references = references || sft.hasReferences();</span>
-<span class="source-line-no">520</span><span id="line-520"> if (references) {</span>
-<span class="source-line-no">521</span><span id="line-521"> break;</span>
-<span class="source-line-no">522</span><span id="line-522"> }</span>
-<span class="source-line-no">523</span><span id="line-523"> }</span>
-<span class="source-line-no">524</span><span id="line-524"> return !references;</span>
-<span class="source-line-no">525</span><span id="line-525"> } catch (IOException e) {</span>
-<span class="source-line-no">526</span><span id="line-526"> LOG.warn("Failed check merged region has no reference", e);</span>
-<span class="source-line-no">527</span><span id="line-527"> return false;</span>
-<span class="source-line-no">528</span><span id="line-528"> }</span>
-<span class="source-line-no">529</span><span id="line-529"> });</span>
-<span class="source-line-no">530</span><span id="line-530"> // run catalog janitor to clean and wait for parent regions are archived</span>
-<span class="source-line-no">531</span><span id="line-531"> UTIL.getMiniHBaseCluster().getMaster().getCatalogJanitor().choreForTesting();</span>
-<span class="source-line-no">532</span><span id="line-532"> UTIL.waitFor(timeout, () -> {</span>
-<span class="source-line-no">533</span><span id="line-533"> try {</span>
+<span class="source-line-no">393</span><span id="line-393"> fs.delete(restoreDir, true);</span>
+<span class="source-line-no">394</span><span id="line-394"> UTIL.getAdmin().deleteSnapshot(snapshotName);</span>
+<span class="source-line-no">395</span><span id="line-395"> UTIL.deleteTable(tableName);</span>
+<span class="source-line-no">396</span><span id="line-396"> }</span>
+<span class="source-line-no">397</span><span id="line-397"> }</span>
+<span class="source-line-no">398</span><span id="line-398"></span>
+<span class="source-line-no">399</span><span id="line-399"> private void testScanner(HBaseTestingUtil util, String snapshotName, int numRegions,</span>
+<span class="source-line-no">400</span><span id="line-400"> boolean shutdownCluster) throws Exception {</span>
+<span class="source-line-no">401</span><span id="line-401"> TableName tableName = TableName.valueOf("testScanner");</span>
+<span class="source-line-no">402</span><span id="line-402"> try {</span>
+<span class="source-line-no">403</span><span id="line-403"> createTableAndSnapshot(util, tableName, snapshotName, numRegions);</span>
+<span class="source-line-no">404</span><span id="line-404"></span>
+<span class="source-line-no">405</span><span id="line-405"> if (shutdownCluster) {</span>
+<span class="source-line-no">406</span><span id="line-406"> util.shutdownMiniHBaseCluster();</span>
+<span class="source-line-no">407</span><span id="line-407"> clusterUp = false;</span>
+<span class="source-line-no">408</span><span id="line-408"> }</span>
+<span class="source-line-no">409</span><span id="line-409"></span>
+<span class="source-line-no">410</span><span id="line-410"> Path restoreDir = util.getDataTestDirOnTestFS(snapshotName);</span>
+<span class="source-line-no">411</span><span id="line-411"> Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan</span>
+<span class="source-line-no">412</span><span id="line-412"></span>
+<span class="source-line-no">413</span><span id="line-413"> TableSnapshotScanner scanner =</span>
+<span class="source-line-no">414</span><span id="line-414"> new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan);</span>
+<span class="source-line-no">415</span><span id="line-415"></span>
+<span class="source-line-no">416</span><span id="line-416"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">417</span><span id="line-417"> scanner.close();</span>
+<span class="source-line-no">418</span><span id="line-418"> } finally {</span>
+<span class="source-line-no">419</span><span id="line-419"> if (clusterUp) {</span>
+<span class="source-line-no">420</span><span id="line-420"> util.getAdmin().deleteSnapshot(snapshotName);</span>
+<span class="source-line-no">421</span><span id="line-421"> util.deleteTable(tableName);</span>
+<span class="source-line-no">422</span><span id="line-422"> }</span>
+<span class="source-line-no">423</span><span id="line-423"> }</span>
+<span class="source-line-no">424</span><span id="line-424"> }</span>
+<span class="source-line-no">425</span><span id="line-425"></span>
+<span class="source-line-no">426</span><span id="line-426"> private void verifyScanner(ResultScanner scanner, byte[] startRow, byte[] stopRow)</span>
+<span class="source-line-no">427</span><span id="line-427"> throws IOException, InterruptedException {</span>
+<span class="source-line-no">428</span><span id="line-428"></span>
+<span class="source-line-no">429</span><span id="line-429"> HBaseTestingUtil.SeenRowTracker rowTracker =</span>
+<span class="source-line-no">430</span><span id="line-430"> new HBaseTestingUtil.SeenRowTracker(startRow, stopRow);</span>
+<span class="source-line-no">431</span><span id="line-431"></span>
+<span class="source-line-no">432</span><span id="line-432"> while (true) {</span>
+<span class="source-line-no">433</span><span id="line-433"> Result result = scanner.next();</span>
+<span class="source-line-no">434</span><span id="line-434"> if (result == null) {</span>
+<span class="source-line-no">435</span><span id="line-435"> break;</span>
+<span class="source-line-no">436</span><span id="line-436"> }</span>
+<span class="source-line-no">437</span><span id="line-437"> verifyRow(result);</span>
+<span class="source-line-no">438</span><span id="line-438"> rowTracker.addRow(result.getRow());</span>
+<span class="source-line-no">439</span><span id="line-439"> }</span>
+<span class="source-line-no">440</span><span id="line-440"></span>
+<span class="source-line-no">441</span><span id="line-441"> // validate all rows are seen</span>
+<span class="source-line-no">442</span><span id="line-442"> rowTracker.validate();</span>
+<span class="source-line-no">443</span><span id="line-443"> }</span>
+<span class="source-line-no">444</span><span id="line-444"></span>
+<span class="source-line-no">445</span><span id="line-445"> private static void verifyRow(Result result) throws IOException {</span>
+<span class="source-line-no">446</span><span id="line-446"> byte[] row = result.getRow();</span>
+<span class="source-line-no">447</span><span id="line-447"> CellScanner scanner = result.cellScanner();</span>
+<span class="source-line-no">448</span><span id="line-448"> while (scanner.advance()) {</span>
+<span class="source-line-no">449</span><span id="line-449"> Cell cell = scanner.current();</span>
+<span class="source-line-no">450</span><span id="line-450"></span>
+<span class="source-line-no">451</span><span id="line-451"> // assert that all Cells in the Result have the same key</span>
+<span class="source-line-no">452</span><span id="line-452"> assertEquals(0, Bytes.compareTo(row, 0, row.length, cell.getRowArray(), cell.getRowOffset(),</span>
+<span class="source-line-no">453</span><span id="line-453"> cell.getRowLength()));</span>
+<span class="source-line-no">454</span><span id="line-454"> }</span>
+<span class="source-line-no">455</span><span id="line-455"></span>
+<span class="source-line-no">456</span><span id="line-456"> for (int j = 0; j < FAMILIES.length; j++) {</span>
+<span class="source-line-no">457</span><span id="line-457"> byte[] actual = result.getValue(FAMILIES[j], FAMILIES[j]);</span>
+<span class="source-line-no">458</span><span id="line-458"> assertArrayEquals(row, actual, "Row in snapshot does not match, expected:"</span>
+<span class="source-line-no">459</span><span id="line-459"> + Bytes.toString(row) + " ,actual:" + Bytes.toString(actual));</span>
+<span class="source-line-no">460</span><span id="line-460"> }</span>
+<span class="source-line-no">461</span><span id="line-461"> }</span>
+<span class="source-line-no">462</span><span id="line-462"></span>
+<span class="source-line-no">463</span><span id="line-463"> @Test</span>
+<span class="source-line-no">464</span><span id="line-464"> public void testMergeRegion() throws Exception {</span>
+<span class="source-line-no">465</span><span id="line-465"> TableName tableName = TableName.valueOf("testMergeRegion");</span>
+<span class="source-line-no">466</span><span id="line-466"> String snapshotName = tableName.getNameAsString() + "_snapshot";</span>
+<span class="source-line-no">467</span><span id="line-467"> Configuration conf = UTIL.getConfiguration();</span>
+<span class="source-line-no">468</span><span id="line-468"> Path rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();</span>
+<span class="source-line-no">469</span><span id="line-469"> long timeout = 20000; // 20s</span>
+<span class="source-line-no">470</span><span id="line-470"> try (Admin admin = UTIL.getAdmin()) {</span>
+<span class="source-line-no">471</span><span id="line-471"> List<String> serverList = admin.getRegionServers().stream().map(sn -> sn.getServerName())</span>
+<span class="source-line-no">472</span><span id="line-472"> .collect(Collectors.toList());</span>
+<span class="source-line-no">473</span><span id="line-473"> // create table with 3 regions</span>
+<span class="source-line-no">474</span><span id="line-474"> Table table = UTIL.createTable(tableName, FAMILIES, 1, bbb, yyy, 3);</span>
+<span class="source-line-no">475</span><span id="line-475"> List<RegionInfo> regions = admin.getRegions(tableName);</span>
+<span class="source-line-no">476</span><span id="line-476"> assertEquals(3, regions.size());</span>
+<span class="source-line-no">477</span><span id="line-477"> RegionInfo region0 = regions.get(0);</span>
+<span class="source-line-no">478</span><span id="line-478"> RegionInfo region1 = regions.get(1);</span>
+<span class="source-line-no">479</span><span id="line-479"> RegionInfo region2 = regions.get(2);</span>
+<span class="source-line-no">480</span><span id="line-480"> // put some data in the table</span>
+<span class="source-line-no">481</span><span id="line-481"> UTIL.loadTable(table, FAMILIES);</span>
+<span class="source-line-no">482</span><span id="line-482"> admin.flush(tableName);</span>
+<span class="source-line-no">483</span><span id="line-483"> // wait flush is finished</span>
+<span class="source-line-no">484</span><span id="line-484"> UTIL.waitFor(timeout, () -> {</span>
+<span class="source-line-no">485</span><span id="line-485"> try {</span>
+<span class="source-line-no">486</span><span id="line-486"> Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName);</span>
+<span class="source-line-no">487</span><span id="line-487"> for (RegionInfo region : regions) {</span>
+<span class="source-line-no">488</span><span id="line-488"> Path regionDir = new Path(tableDir, region.getEncodedName());</span>
+<span class="source-line-no">489</span><span id="line-489"> for (Path familyDir : FSUtils.getFamilyDirs(fs, regionDir)) {</span>
+<span class="source-line-no">490</span><span id="line-490"> for (FileStatus fs : fs.listStatus(familyDir)) {</span>
+<span class="source-line-no">491</span><span id="line-491"> if (!fs.getPath().getName().equals(".filelist")) {</span>
+<span class="source-line-no">492</span><span id="line-492"> return true;</span>
+<span class="source-line-no">493</span><span id="line-493"> }</span>
+<span class="source-line-no">494</span><span id="line-494"> }</span>
+<span class="source-line-no">495</span><span id="line-495"> return false;</span>
+<span class="source-line-no">496</span><span id="line-496"> }</span>
+<span class="source-line-no">497</span><span id="line-497"> }</span>
+<span class="source-line-no">498</span><span id="line-498"> return true;</span>
+<span class="source-line-no">499</span><span id="line-499"> } catch (IOException e) {</span>
+<span class="source-line-no">500</span><span id="line-500"> LOG.warn("Failed check if flush is finished", e);</span>
+<span class="source-line-no">501</span><span id="line-501"> return false;</span>
+<span class="source-line-no">502</span><span id="line-502"> }</span>
+<span class="source-line-no">503</span><span id="line-503"> });</span>
+<span class="source-line-no">504</span><span id="line-504"> // merge 2 regions</span>
+<span class="source-line-no">505</span><span id="line-505"> admin.compactionSwitch(false, serverList);</span>
+<span class="source-line-no">506</span><span id="line-506"> admin.mergeRegionsAsync(region0.getEncodedNameAsBytes(), region1.getEncodedNameAsBytes(),</span>
+<span class="source-line-no">507</span><span id="line-507"> true);</span>
+<span class="source-line-no">508</span><span id="line-508"> UTIL.waitFor(timeout, () -> admin.getRegions(tableName).size() == 2);</span>
+<span class="source-line-no">509</span><span id="line-509"> List<RegionInfo> mergedRegions = admin.getRegions(tableName);</span>
+<span class="source-line-no">510</span><span id="line-510"> RegionInfo mergedRegion =</span>
+<span class="source-line-no">511</span><span id="line-511"> mergedRegions.get(0).getEncodedName().equals(region2.getEncodedName())</span>
+<span class="source-line-no">512</span><span id="line-512"> ? mergedRegions.get(1)</span>
+<span class="source-line-no">513</span><span id="line-513"> : mergedRegions.get(0);</span>
+<span class="source-line-no">514</span><span id="line-514"> // snapshot</span>
+<span class="source-line-no">515</span><span id="line-515"> admin.snapshot(snapshotName, tableName);</span>
+<span class="source-line-no">516</span><span id="line-516"> assertEquals(1, admin.listSnapshots().size());</span>
+<span class="source-line-no">517</span><span id="line-517"> // major compact</span>
+<span class="source-line-no">518</span><span id="line-518"> admin.compactionSwitch(true, serverList);</span>
+<span class="source-line-no">519</span><span id="line-519"> admin.majorCompactRegion(mergedRegion.getRegionName());</span>
+<span class="source-line-no">520</span><span id="line-520"> // wait until merged region has no reference</span>
+<span class="source-line-no">521</span><span id="line-521"> UTIL.waitFor(timeout, () -> {</span>
+<span class="source-line-no">522</span><span id="line-522"> try {</span>
+<span class="source-line-no">523</span><span id="line-523"> for (RegionServerThread regionServerThread : UTIL.getMiniHBaseCluster()</span>
+<span class="source-line-no">524</span><span id="line-524"> .getRegionServerThreads()) {</span>
+<span class="source-line-no">525</span><span id="line-525"> HRegionServer regionServer = regionServerThread.getRegionServer();</span>
+<span class="source-line-no">526</span><span id="line-526"> for (HRegion subRegion : regionServer.getRegions(tableName)) {</span>
+<span class="source-line-no">527</span><span id="line-527"> if (</span>
+<span class="source-line-no">528</span><span id="line-528"> subRegion.getRegionInfo().getEncodedName().equals(mergedRegion.getEncodedName())</span>
+<span class="source-line-no">529</span><span id="line-529"> ) {</span>
+<span class="source-line-no">530</span><span id="line-530"> regionServer.getCompactedHFilesDischarger().chore();</span>
+<span class="source-line-no">531</span><span id="line-531"> }</span>
+<span class="source-line-no">532</span><span id="line-532"> }</span>
+<span class="source-line-no">533</span><span id="line-533"> }</span>
<span class="source-line-no">534</span><span id="line-534"> Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName);</span>
-<span class="source-line-no">535</span><span id="line-535"> for (FileStatus fileStatus : fs.listStatus(tableDir)) {</span>
-<span class="source-line-no">536</span><span id="line-536"> String name = fileStatus.getPath().getName();</span>
-<span class="source-line-no">537</span><span id="line-537"> if (name.equals(region0.getEncodedName()) || name.equals(region1.getEncodedName())) {</span>
-<span class="source-line-no">538</span><span id="line-538"> return false;</span>
-<span class="source-line-no">539</span><span id="line-539"> }</span>
-<span class="source-line-no">540</span><span id="line-540"> }</span>
-<span class="source-line-no">541</span><span id="line-541"> return true;</span>
-<span class="source-line-no">542</span><span id="line-542"> } catch (IOException e) {</span>
-<span class="source-line-no">543</span><span id="line-543"> LOG.warn("Check if parent regions are archived error", e);</span>
-<span class="source-line-no">544</span><span id="line-544"> return false;</span>
-<span class="source-line-no">545</span><span id="line-545"> }</span>
-<span class="source-line-no">546</span><span id="line-546"> });</span>
-<span class="source-line-no">547</span><span id="line-547"> // set file modify time and then run cleaner</span>
-<span class="source-line-no">548</span><span id="line-548"> long time = EnvironmentEdgeManager.currentTime() - TimeToLiveHFileCleaner.DEFAULT_TTL * 1000;</span>
-<span class="source-line-no">549</span><span id="line-549"> traverseAndSetFileTime(HFileArchiveUtil.getArchivePath(conf), time);</span>
-<span class="source-line-no">550</span><span id="line-550"> UTIL.getMiniHBaseCluster().getMaster().getHFileCleaner().triggerCleanerNow().get();</span>
-<span class="source-line-no">551</span><span id="line-551"> // scan snapshot</span>
-<span class="source-line-no">552</span><span id="line-552"> try (TableSnapshotScanner scanner =</span>
-<span class="source-line-no">553</span><span id="line-553"> new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName,</span>
-<span class="source-line-no">554</span><span id="line-554"> new Scan().withStartRow(bbb).withStopRow(yyy))) {</span>
-<span class="source-line-no">555</span><span id="line-555"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">556</span><span id="line-556"> }</span>
-<span class="source-line-no">557</span><span id="line-557"> } catch (Exception e) {</span>
-<span class="source-line-no">558</span><span id="line-558"> LOG.error("scan snapshot error", e);</span>
-<span class="source-line-no">559</span><span id="line-559"> fail("Should not throw Exception: " + e.getMessage());</span>
-<span class="source-line-no">560</span><span id="line-560"> }</span>
-<span class="source-line-no">561</span><span id="line-561"> }</span>
-<span class="source-line-no">562</span><span id="line-562"></span>
-<span class="source-line-no">563</span><span id="line-563"> @Test</span>
-<span class="source-line-no">564</span><span id="line-564"> public void testDeleteTableWithMergedRegions() throws Exception {</span>
-<span class="source-line-no">565</span><span id="line-565"> final TableName tableName = TableName.valueOf(this.methodName);</span>
-<span class="source-line-no">566</span><span id="line-566"> String snapshotName = tableName.getNameAsString() + "_snapshot";</span>
-<span class="source-line-no">567</span><span id="line-567"> Configuration conf = UTIL.getConfiguration();</span>
-<span class="source-line-no">568</span><span id="line-568"> try (Admin admin = UTIL.getConnection().getAdmin()) {</span>
-<span class="source-line-no">569</span><span id="line-569"> // disable compaction</span>
-<span class="source-line-no">570</span><span id="line-570"> admin.compactionSwitch(false,</span>
-<span class="source-line-no">571</span><span id="line-571"> admin.getRegionServers().stream().map(s -> s.getServerName()).collect(Collectors.toList()));</span>
-<span class="source-line-no">572</span><span id="line-572"> // create table</span>
-<span class="source-line-no">573</span><span id="line-573"> Table table = UTIL.createTable(tableName, FAMILIES, 1, bbb, yyy, 3);</span>
-<span class="source-line-no">574</span><span id="line-574"> List<RegionInfo> regions = admin.getRegions(tableName);</span>
-<span class="source-line-no">575</span><span id="line-575"> assertEquals(3, regions.size());</span>
-<span class="source-line-no">576</span><span id="line-576"> // write some data</span>
-<span class="source-line-no">577</span><span id="line-577"> UTIL.loadTable(table, FAMILIES);</span>
-<span class="source-line-no">578</span><span id="line-578"> // merge region</span>
-<span class="source-line-no">579</span><span id="line-579"> admin.mergeRegionsAsync(new byte[][] { regions.get(0).getEncodedNameAsBytes(),</span>
-<span class="source-line-no">580</span><span id="line-580"> regions.get(1).getEncodedNameAsBytes() }, false).get();</span>
-<span class="source-line-no">581</span><span id="line-581"> regions = admin.getRegions(tableName);</span>
-<span class="source-line-no">582</span><span id="line-582"> assertEquals(2, regions.size());</span>
-<span class="source-line-no">583</span><span id="line-583"> // snapshot</span>
-<span class="source-line-no">584</span><span id="line-584"> admin.snapshot(snapshotName, tableName);</span>
-<span class="source-line-no">585</span><span id="line-585"> // verify snapshot</span>
-<span class="source-line-no">586</span><span id="line-586"> try (TableSnapshotScanner scanner =</span>
-<span class="source-line-no">587</span><span id="line-587"> new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName,</span>
-<span class="source-line-no">588</span><span id="line-588"> new Scan().withStartRow(bbb).withStopRow(yyy))) {</span>
-<span class="source-line-no">589</span><span id="line-589"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">590</span><span id="line-590"> }</span>
-<span class="source-line-no">591</span><span id="line-591"> // drop table</span>
-<span class="source-line-no">592</span><span id="line-592"> admin.disableTable(tableName);</span>
-<span class="source-line-no">593</span><span id="line-593"> admin.deleteTable(tableName);</span>
-<span class="source-line-no">594</span><span id="line-594"> // verify snapshot</span>
-<span class="source-line-no">595</span><span id="line-595"> try (TableSnapshotScanner scanner =</span>
-<span class="source-line-no">596</span><span id="line-596"> new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName,</span>
-<span class="source-line-no">597</span><span id="line-597"> new Scan().withStartRow(bbb).withStopRow(yyy))) {</span>
-<span class="source-line-no">598</span><span id="line-598"> verifyScanner(scanner, bbb, yyy);</span>
-<span class="source-line-no">599</span><span id="line-599"> }</span>
-<span class="source-line-no">600</span><span id="line-600"> }</span>
-<span class="source-line-no">601</span><span id="line-601"> }</span>
-<span class="source-line-no">602</span><span id="line-602"></span>
-<span class="source-line-no">603</span><span id="line-603"> private void traverseAndSetFileTime(Path path, long time) throws IOException {</span>
-<span class="source-line-no">604</span><span id="line-604"> fs.setTimes(path, time, -1);</span>
-<span class="source-line-no">605</span><span id="line-605"> if (fs.isDirectory(path)) {</span>
-<span class="source-line-no">606</span><span id="line-606"> List<FileStatus> allPaths = Arrays.asList(fs.listStatus(path));</span>
-<span class="source-line-no">607</span><span id="line-607"> List<FileStatus> subDirs =</span>
-<span class="source-line-no">608</span><span id="line-608"> allPaths.stream().filter(FileStatus::isDirectory).collect(Collectors.toList());</span>
-<span class="source-line-no">609</span><span id="line-609"> List<FileStatus> files =</span>
-<span class="source-line-no">610</span><span id="line-610"> allPaths.stream().filter(FileStatus::isFile).collect(Collectors.toList());</span>
-<span class="source-line-no">611</span><span id="line-611"> for (FileStatus subDir : subDirs) {</span>
-<span class="source-line-no">612</span><span id="line-612"> traverseAndSetFileTime(subDir.getPath(), time);</span>
-<span class="source-line-no">613</span><span id="line-613"> }</span>
-<span class="source-line-no">614</span><span id="line-614"> for (FileStatus file : files) {</span>
-<span class="source-line-no">615</span><span id="line-615"> fs.setTimes(file.getPath(), time, -1);</span>
+<span class="source-line-no">535</span><span id="line-535"> HRegionFileSystem regionFs = HRegionFileSystem</span>
+<span class="source-line-no">536</span><span id="line-536"> .openRegionFromFileSystem(UTIL.getConfiguration(), fs, tableDir, mergedRegion, true);</span>
+<span class="source-line-no">537</span><span id="line-537"> boolean references = false;</span>
+<span class="source-line-no">538</span><span id="line-538"> Path regionDir = new Path(tableDir, mergedRegion.getEncodedName());</span>
+<span class="source-line-no">539</span><span id="line-539"> for (Path familyDir : FSUtils.getFamilyDirs(fs, regionDir)) {</span>
+<span class="source-line-no">540</span><span id="line-540"> StoreContext storeContext = StoreContext.getBuilder()</span>
+<span class="source-line-no">541</span><span id="line-541"> .withColumnFamilyDescriptor(ColumnFamilyDescriptorBuilder.of(familyDir.getName()))</span>
+<span class="source-line-no">542</span><span id="line-542"> .withRegionFileSystem(regionFs).withFamilyStoreDirectoryPath(familyDir).build();</span>
+<span class="source-line-no">543</span><span id="line-543"> StoreFileTracker sft =</span>
+<span class="source-line-no">544</span><span id="line-544"> StoreFileTrackerFactory.create(UTIL.getConfiguration(), false, storeContext);</span>
+<span class="source-line-no">545</span><span id="line-545"> references = references || sft.hasReferences();</span>
+<span class="source-line-no">546</span><span id="line-546"> if (references) {</span>
+<span class="source-line-no">547</span><span id="line-547"> break;</span>
+<span class="source-line-no">548</span><span id="line-548"> }</span>
+<span class="source-line-no">549</span><span id="line-549"> }</span>
+<span class="source-line-no">550</span><span id="line-550"> return !references;</span>
+<span class="source-line-no">551</span><span id="line-551"> } catch (IOException e) {</span>
+<span class="source-line-no">552</span><span id="line-552"> LOG.warn("Failed check merged region has no reference", e);</span>
+<span class="source-line-no">553</span><span id="line-553"> return false;</span>
+<span class="source-line-no">554</span><span id="line-554"> }</span>
+<span class="source-line-no">555</span><span id="line-555"> });</span>
+<span class="source-line-no">556</span><span id="line-556"> // run catalog janitor to clean and wait for parent regions are archived</span>
+<span class="source-line-no">557</span><span id="line-557"> UTIL.getMiniHBaseCluster().getMaster().getCatalogJanitor().choreForTesting();</span>
+<span class="source-line-no">558</span><span id="line-558"> UTIL.waitFor(timeout, () -> {</span>
+<span class="source-line-no">559</span><span id="line-559"> try {</span>
+<span class="source-line-no">560</span><span id="line-560"> Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName);</span>
+<span class="source-line-no">561</span><span id="line-561"> for (FileStatus fileStatus : fs.listStatus(tableDir)) {</span>
+<span class="source-line-no">562</span><span id="line-562"> String name = fileStatus.getPath().getName();</span>
+<span class="source-line-no">563</span><span id="line-563"> if (name.equals(region0.getEncodedName()) || name.equals(region1.getEncodedName())) {</span>
+<span class="source-line-no">564</span><span id="line-564"> return false;</span>
+<span class="source-line-no">565</span><span id="line-565"> }</span>
+<span class="source-line-no">566</span><span id="line-566"> }</span>
+<span class="source-line-no">567</span><span id="line-567"> return true;</span>
+<span class="source-line-no">568</span><span id="line-568"> } catch (IOException e) {</span>
+<span class="source-line-no">569</span><span id="line-569"> LOG.warn("Check if parent regions are archived error", e);</span>
+<span class="source-line-no">570</span><span id="line-570"> return false;</span>
+<span class="source-line-no">571</span><span id="line-571"> }</span>
+<span class="source-line-no">572</span><span id="line-572"> });</span>
+<span class="source-line-no">573</span><span id="line-573"> // set file modify time and then run cleaner</span>
+<span class="source-line-no">574</span><span id="line-574"> long time = EnvironmentEdgeManager.currentTime() - TimeToLiveHFileCleaner.DEFAULT_TTL * 1000;</span>
+<span class="source-line-no">575</span><span id="line-575"> traverseAndSetFileTime(HFileArchiveUtil.getArchivePath(conf), time);</span>
+<span class="source-line-no">576</span><span id="line-576"> UTIL.getMiniHBaseCluster().getMaster().getHFileCleaner().triggerCleanerNow().get();</span>
+<span class="source-line-no">577</span><span id="line-577"> // scan snapshot</span>
+<span class="source-line-no">578</span><span id="line-578"> try (TableSnapshotScanner scanner =</span>
+<span class="source-line-no">579</span><span id="line-579"> new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName,</span>
+<span class="source-line-no">580</span><span id="line-580"> new Scan().withStartRow(bbb).withStopRow(yyy))) {</span>
+<span class="source-line-no">581</span><span id="line-581"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">582</span><span id="line-582"> }</span>
+<span class="source-line-no">583</span><span id="line-583"> } catch (Exception e) {</span>
+<span class="source-line-no">584</span><span id="line-584"> LOG.error("scan snapshot error", e);</span>
+<span class="source-line-no">585</span><span id="line-585"> fail("Should not throw Exception: " + e.getMessage());</span>
+<span class="source-line-no">586</span><span id="line-586"> }</span>
+<span class="source-line-no">587</span><span id="line-587"> }</span>
+<span class="source-line-no">588</span><span id="line-588"></span>
+<span class="source-line-no">589</span><span id="line-589"> @Test</span>
+<span class="source-line-no">590</span><span id="line-590"> public void testDeleteTableWithMergedRegions() throws Exception {</span>
+<span class="source-line-no">591</span><span id="line-591"> final TableName tableName = TableName.valueOf(this.methodName);</span>
+<span class="source-line-no">592</span><span id="line-592"> String snapshotName = tableName.getNameAsString() + "_snapshot";</span>
+<span class="source-line-no">593</span><span id="line-593"> Configuration conf = UTIL.getConfiguration();</span>
+<span class="source-line-no">594</span><span id="line-594"> try (Admin admin = UTIL.getConnection().getAdmin()) {</span>
+<span class="source-line-no">595</span><span id="line-595"> // disable compaction</span>
+<span class="source-line-no">596</span><span id="line-596"> admin.compactionSwitch(false,</span>
+<span class="source-line-no">597</span><span id="line-597"> admin.getRegionServers().stream().map(s -> s.getServerName()).collect(Collectors.toList()));</span>
+<span class="source-line-no">598</span><span id="line-598"> // create table</span>
+<span class="source-line-no">599</span><span id="line-599"> Table table = UTIL.createTable(tableName, FAMILIES, 1, bbb, yyy, 3);</span>
+<span class="source-line-no">600</span><span id="line-600"> List<RegionInfo> regions = admin.getRegions(tableName);</span>
+<span class="source-line-no">601</span><span id="line-601"> assertEquals(3, regions.size());</span>
+<span class="source-line-no">602</span><span id="line-602"> // write some data</span>
+<span class="source-line-no">603</span><span id="line-603"> UTIL.loadTable(table, FAMILIES);</span>
+<span class="source-line-no">604</span><span id="line-604"> // merge region</span>
+<span class="source-line-no">605</span><span id="line-605"> admin.mergeRegionsAsync(new byte[][] { regions.get(0).getEncodedNameAsBytes(),</span>
+<span class="source-line-no">606</span><span id="line-606"> regions.get(1).getEncodedNameAsBytes() }, false).get();</span>
+<span class="source-line-no">607</span><span id="line-607"> regions = admin.getRegions(tableName);</span>
+<span class="source-line-no">608</span><span id="line-608"> assertEquals(2, regions.size());</span>
+<span class="source-line-no">609</span><span id="line-609"> // snapshot</span>
+<span class="source-line-no">610</span><span id="line-610"> admin.snapshot(snapshotName, tableName);</span>
+<span class="source-line-no">611</span><span id="line-611"> // verify snapshot</span>
+<span class="source-line-no">612</span><span id="line-612"> try (TableSnapshotScanner scanner =</span>
+<span class="source-line-no">613</span><span id="line-613"> new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName,</span>
+<span class="source-line-no">614</span><span id="line-614"> new Scan().withStartRow(bbb).withStopRow(yyy))) {</span>
+<span class="source-line-no">615</span><span id="line-615"> verifyScanner(scanner, bbb, yyy);</span>
<span class="source-line-no">616</span><span id="line-616"> }</span>
-<span class="source-line-no">617</span><span id="line-617"> }</span>
-<span class="source-line-no">618</span><span id="line-618"> }</span>
-<span class="source-line-no">619</span><span id="line-619">}</span>
+<span class="source-line-no">617</span><span id="line-617"> // drop table</span>
+<span class="source-line-no">618</span><span id="line-618"> admin.disableTable(tableName);</span>
+<span class="source-line-no">619</span><span id="line-619"> admin.deleteTable(tableName);</span>
+<span class="source-line-no">620</span><span id="line-620"> // verify snapshot</span>
+<span class="source-line-no">621</span><span id="line-621"> try (TableSnapshotScanner scanner =</span>
+<span class="source-line-no">622</span><span id="line-622"> new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName,</span>
+<span class="source-line-no">623</span><span id="line-623"> new Scan().withStartRow(bbb).withStopRow(yyy))) {</span>
+<span class="source-line-no">624</span><span id="line-624"> verifyScanner(scanner, bbb, yyy);</span>
+<span class="source-line-no">625</span><span id="line-625"> }</span>
+<span class="source-line-no">626</span><span id="line-626"> }</span>
+<span class="source-line-no">627</span><span id="line-627"> }</span>
+<span class="source-line-no">628</span><span id="line-628"></span>
+<span class="source-line-no">629</span><span id="line-629"> private void traverseAndSetFileTime(Path path, long time) throws IOException {</span>
+<span class="source-line-no">630</span><span id="line-630"> fs.setTimes(path, time, -1);</span>
+<span class="source-line-no">631</span><span id="line-631"> if (fs.isDirectory(path)) {</span>
+<span class="source-line-no">632</span><span id="line-632"> List<FileStatus> allPaths = Arrays.asList(fs.listStatus(path));</span>
+<span class="source-line-no">633</span><span id="line-633"> List<FileStatus> subDirs =</span>
+<span class="source-line-no">634</span><span id="line-634"> allPaths.stream().filter(FileStatus::isDirectory).collect(Collectors.toList());</span>
+<span class="source-line-no">635</span><span id="line-635"> List<FileStatus> files =</span>
+<span class="source-line-no">636</span><span id="line-636"> allPaths.stream().filter(FileStatus::isFile).collect(Collectors.toList());</span>
+<span class="source-line-no">637</span><span id="line-637"> for (FileStatus subDir : subDirs) {</span>
+<span class="source-line-no">638</span><span id="line-638"> traverseAndSetFileTime(subDir.getPath(), time);</span>
+<span class="source-line-no">639</span><span id="line-639"> }</span>
+<span class="source-line-no">640</span><span id="line-640"> for (FileStatus file : files) {</span>
+<span class="source-line-no">641</span><span id="line-641"> fs.setTimes(file.getPath(), time, -1);</span>
+<span class="source-line-no">642</span><span id="line-642"> }</span>
+<span class="source-line-no">643</span><span id="line-643"> }</span>
+<span class="source-line-no">644</span><span id="line-644"> }</span>
+<span class="source-line-no">645</span><span id="line-645">}</span>
diff --git a/testdevapidocs/src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.AssignmentManagerForTest.html b/testdevapidocs/src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.AssignmentManagerForTest.html
index 14270af61a4..adb586288f0 100644
--- a/testdevapidocs/src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.AssignmentManagerForTest.html
+++ b/testdevapidocs/src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.AssignmentManagerForTest.html
@@ -146,54 +146,59 @@
<span class="source-line-no">133</span><span id="line-133"> @BeforeEach</span>
<span class="source-line-no">134</span><span id="line-134"> public void setUp() throws IOException {</span>
<span class="source-line-no">135</span><span id="line-135"> UTIL.ensureSomeNonStoppedRegionServersAvailable(2);</span>
-<span class="source-line-no">136</span><span id="line-136"> }</span>
-<span class="source-line-no">137</span><span id="line-137"></span>
-<span class="source-line-no">138</span><span id="line-138"> private ServerCrashProcedure getSCPForServer(ServerName serverName) throws IOException {</span>
-<span class="source-line-no">139</span><span id="line-139"> return UTIL.getMiniHBaseCluster().getMaster().getProcedures().stream()</span>
-<span class="source-line-no">140</span><span id="line-140"> .filter(p -> p instanceof ServerCrashProcedure).map(p -> (ServerCrashProcedure) p)</span>
-<span class="source-line-no">141</span><span id="line-141"> .filter(p -> p.getServerName().equals(serverName)).findFirst().orElse(null);</span>
-<span class="source-line-no">142</span><span id="line-142"> }</span>
-<span class="source-line-no">143</span><span id="line-143"></span>
-<span class="source-line-no">144</span><span id="line-144"> private Matcher<Procedure<MasterProcedureEnv>> subProcOf(Procedure<MasterProcedureEnv> proc) {</span>
-<span class="source-line-no">145</span><span id="line-145"> return new BaseMatcher<Procedure<MasterProcedureEnv>>() {</span>
-<span class="source-line-no">146</span><span id="line-146"></span>
-<span class="source-line-no">147</span><span id="line-147"> @Override</span>
-<span class="source-line-no">148</span><span id="line-148"> public boolean matches(Object item) {</span>
-<span class="source-line-no">149</span><span id="line-149"> if (!(item instanceof Procedure)) {</span>
-<span class="source-line-no">150</span><span id="line-150"> return false;</span>
-<span class="source-line-no">151</span><span id="line-151"> }</span>
-<span class="source-line-no">152</span><span id="line-152"> Procedure<?> p = (Procedure<?>) item;</span>
-<span class="source-line-no">153</span><span id="line-153"> return p.hasParent() && p.getRootProcId() == proc.getProcId();</span>
-<span class="source-line-no">154</span><span id="line-154"> }</span>
-<span class="source-line-no">155</span><span id="line-155"></span>
-<span class="source-line-no">156</span><span id="line-156"> @Override</span>
-<span class="source-line-no">157</span><span id="line-157"> public void describeTo(Description description) {</span>
-<span class="source-line-no">158</span><span id="line-158"> description.appendText("sub procedure of(").appendValue(proc).appendText(")");</span>
+<span class="source-line-no">136</span><span id="line-136"> // Surefire reruns failed tests in the same JVM without rerunning @BeforeAll, so reset the</span>
+<span class="source-line-no">137</span><span id="line-137"> // fault injection state before each attempt.</span>
+<span class="source-line-no">138</span><span id="line-138"> INJECTED.set(false);</span>
+<span class="source-line-no">139</span><span id="line-139"> ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdateInRollback(</span>
+<span class="source-line-no">140</span><span id="line-140"> UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor(), false);</span>
+<span class="source-line-no">141</span><span id="line-141"> }</span>
+<span class="source-line-no">142</span><span id="line-142"></span>
+<span class="source-line-no">143</span><span id="line-143"> private ServerCrashProcedure getSCPForServer(ServerName serverName) throws IOException {</span>
+<span class="source-line-no">144</span><span id="line-144"> return UTIL.getMiniHBaseCluster().getMaster().getProcedures().stream()</span>
+<span class="source-line-no">145</span><span id="line-145"> .filter(p -> p instanceof ServerCrashProcedure).map(p -> (ServerCrashProcedure) p)</span>
+<span class="source-line-no">146</span><span id="line-146"> .filter(p -> p.getServerName().equals(serverName)).findFirst().orElse(null);</span>
+<span class="source-line-no">147</span><span id="line-147"> }</span>
+<span class="source-line-no">148</span><span id="line-148"></span>
+<span class="source-line-no">149</span><span id="line-149"> private Matcher<Procedure<MasterProcedureEnv>> subProcOf(Procedure<MasterProcedureEnv> proc) {</span>
+<span class="source-line-no">150</span><span id="line-150"> return new BaseMatcher<Procedure<MasterProcedureEnv>>() {</span>
+<span class="source-line-no">151</span><span id="line-151"></span>
+<span class="source-line-no">152</span><span id="line-152"> @Override</span>
+<span class="source-line-no">153</span><span id="line-153"> public boolean matches(Object item) {</span>
+<span class="source-line-no">154</span><span id="line-154"> if (!(item instanceof Procedure)) {</span>
+<span class="source-line-no">155</span><span id="line-155"> return false;</span>
+<span class="source-line-no">156</span><span id="line-156"> }</span>
+<span class="source-line-no">157</span><span id="line-157"> Procedure<?> p = (Procedure<?>) item;</span>
+<span class="source-line-no">158</span><span id="line-158"> return p.hasParent() && p.getRootProcId() == proc.getProcId();</span>
<span class="source-line-no">159</span><span id="line-159"> }</span>
-<span class="source-line-no">160</span><span id="line-160"> };</span>
-<span class="source-line-no">161</span><span id="line-161"> }</span>
-<span class="source-line-no">162</span><span id="line-162"></span>
-<span class="source-line-no">163</span><span id="line-163"> @Test</span>
-<span class="source-line-no">164</span><span id="line-164"> public void testFailAndRollback() throws Exception {</span>
-<span class="source-line-no">165</span><span id="line-165"> HRegionServer rsWithMeta = UTIL.getRSForFirstRegionInTable(TableName.META_TABLE_NAME);</span>
-<span class="source-line-no">166</span><span id="line-166"> UTIL.getMiniHBaseCluster().killRegionServer(rsWithMeta.getServerName());</span>
-<span class="source-line-no">167</span><span id="line-167"> UTIL.waitFor(15000, () -> getSCPForServer(rsWithMeta.getServerName()) != null);</span>
-<span class="source-line-no">168</span><span id="line-168"> ServerCrashProcedure scp = getSCPForServer(rsWithMeta.getServerName());</span>
-<span class="source-line-no">169</span><span id="line-169"> ProcedureExecutor<MasterProcedureEnv> procExec =</span>
-<span class="source-line-no">170</span><span id="line-170"> UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor();</span>
-<span class="source-line-no">171</span><span id="line-171"> // wait for the procedure to stop, as we inject a code bug and also set kill before store update</span>
-<span class="source-line-no">172</span><span id="line-172"> UTIL.waitFor(30000, () -> !procExec.isRunning());</span>
-<span class="source-line-no">173</span><span id="line-173"> // make sure that finally we could successfully rollback the procedure</span>
-<span class="source-line-no">174</span><span id="line-174"> while (scp.getState() != ProcedureState.FAILED || !procExec.isRunning()) {</span>
-<span class="source-line-no">175</span><span id="line-175"> MasterProcedureTestingUtility.restartMasterProcedureExecutor(procExec);</span>
-<span class="source-line-no">176</span><span id="line-176"> ProcedureTestingUtility.waitProcedure(procExec, scp);</span>
-<span class="source-line-no">177</span><span id="line-177"> }</span>
-<span class="source-line-no">178</span><span id="line-178"> assertEquals(scp.getState(), ProcedureState.FAILED);</span>
-<span class="source-line-no">179</span><span id="line-179"> assertThat(scp.getException().getMessage(), containsString("inject code bug"));</span>
-<span class="source-line-no">180</span><span id="line-180"> // make sure all sub procedures are cleaned up</span>
-<span class="source-line-no">181</span><span id="line-181"> assertThat(procExec.getProcedures(), everyItem(not(subProcOf(scp))));</span>
-<span class="source-line-no">182</span><span id="line-182"> }</span>
-<span class="source-line-no">183</span><span id="line-183">}</span>
+<span class="source-line-no">160</span><span id="line-160"></span>
+<span class="source-line-no">161</span><span id="line-161"> @Override</span>
+<span class="source-line-no">162</span><span id="line-162"> public void describeTo(Description description) {</span>
+<span class="source-line-no">163</span><span id="line-163"> description.appendText("sub procedure of(").appendValue(proc).appendText(")");</span>
+<span class="source-line-no">164</span><span id="line-164"> }</span>
+<span class="source-line-no">165</span><span id="line-165"> };</span>
+<span class="source-line-no">166</span><span id="line-166"> }</span>
+<span class="source-line-no">167</span><span id="line-167"></span>
+<span class="source-line-no">168</span><span id="line-168"> @Test</span>
+<span class="source-line-no">169</span><span id="line-169"> public void testFailAndRollback() throws Exception {</span>
+<span class="source-line-no">170</span><span id="line-170"> HRegionServer rsWithMeta = UTIL.getRSForFirstRegionInTable(TableName.META_TABLE_NAME);</span>
+<span class="source-line-no">171</span><span id="line-171"> UTIL.getMiniHBaseCluster().killRegionServer(rsWithMeta.getServerName());</span>
+<span class="source-line-no">172</span><span id="line-172"> UTIL.waitFor(15000, () -> getSCPForServer(rsWithMeta.getServerName()) != null);</span>
+<span class="source-line-no">173</span><span id="line-173"> ServerCrashProcedure scp = getSCPForServer(rsWithMeta.getServerName());</span>
+<span class="source-line-no">174</span><span id="line-174"> ProcedureExecutor<MasterProcedureEnv> procExec =</span>
+<span class="source-line-no">175</span><span id="line-175"> UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor();</span>
+<span class="source-line-no">176</span><span id="line-176"> // wait for the procedure to stop, as we inject a code bug and also set kill before store update</span>
+<span class="source-line-no">177</span><span id="line-177"> UTIL.waitFor(30000, () -> !procExec.isRunning());</span>
+<span class="source-line-no">178</span><span id="line-178"> // make sure that finally we could successfully rollback the procedure</span>
+<span class="source-line-no">179</span><span id="line-179"> while (scp.getState() != ProcedureState.FAILED || !procExec.isRunning()) {</span>
+<span class="source-line-no">180</span><span id="line-180"> MasterProcedureTestingUtility.restartMasterProcedureExecutor(procExec);</span>
+<span class="source-line-no">181</span><span id="line-181"> ProcedureTestingUtility.waitProcedure(procExec, scp);</span>
+<span class="source-line-no">182</span><span id="line-182"> }</span>
+<span class="source-line-no">183</span><span id="line-183"> assertEquals(scp.getState(), ProcedureState.FAILED);</span>
+<span class="source-line-no">184</span><span id="line-184"> assertThat(scp.getException().getMessage(), containsString("inject code bug"));</span>
+<span class="source-line-no">185</span><span id="line-185"> // make sure all sub procedures are cleaned up</span>
+<span class="source-line-no">186</span><span id="line-186"> assertThat(procExec.getProcedures(), everyItem(not(subProcOf(scp))));</span>
+<span class="source-line-no">187</span><span id="line-187"> }</span>
+<span class="source-line-no">188</span><span id="line-188">}</span>
diff --git a/testdevapidocs/src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.HMasterForTest.html b/testdevapidocs/src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.HMasterForTest.html
index 6de9d95a1ea..5a1ae2b0896 100644
--- a/testdevapidocs/src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.HMasterForTest.html
+++ b/testdevapidocs/src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.HMasterForTest.html
@@ -146,54 +146,59 @@
<span class="source-line-no">133</span><span id="line-133"> @BeforeEach</span>
<span class="source-line-no">134</span><span id="line-134"> public void setUp() throws IOException {</span>
<span class="source-line-no">135</span><span id="line-135"> UTIL.ensureSomeNonStoppedRegionServersAvailable(2);</span>
-<span class="source-line-no">136</span><span id="line-136"> }</span>
-<span class="source-line-no">137</span><span id="line-137"></span>
-<span class="source-line-no">138</span><span id="line-138"> private ServerCrashProcedure getSCPForServer(ServerName serverName) throws IOException {</span>
-<span class="source-line-no">139</span><span id="line-139"> return UTIL.getMiniHBaseCluster().getMaster().getProcedures().stream()</span>
-<span class="source-line-no">140</span><span id="line-140"> .filter(p -> p instanceof ServerCrashProcedure).map(p -> (ServerCrashProcedure) p)</span>
-<span class="source-line-no">141</span><span id="line-141"> .filter(p -> p.getServerName().equals(serverName)).findFirst().orElse(null);</span>
-<span class="source-line-no">142</span><span id="line-142"> }</span>
-<span class="source-line-no">143</span><span id="line-143"></span>
-<span class="source-line-no">144</span><span id="line-144"> private Matcher<Procedure<MasterProcedureEnv>> subProcOf(Procedure<MasterProcedureEnv> proc) {</span>
-<span class="source-line-no">145</span><span id="line-145"> return new BaseMatcher<Procedure<MasterProcedureEnv>>() {</span>
-<span class="source-line-no">146</span><span id="line-146"></span>
-<span class="source-line-no">147</span><span id="line-147"> @Override</span>
-<span class="source-line-no">148</span><span id="line-148"> public boolean matches(Object item) {</span>
-<span class="source-line-no">149</span><span id="line-149"> if (!(item instanceof Procedure)) {</span>
-<span class="source-line-no">150</span><span id="line-150"> return false;</span>
-<span class="source-line-no">151</span><span id="line-151"> }</span>
-<span class="source-line-no">152</span><span id="line-152"> Procedure<?> p = (Procedure<?>) item;</span>
-<span class="source-line-no">153</span><span id="line-153"> return p.hasParent() && p.getRootProcId() == proc.getProcId();</span>
-<span class="source-line-no">154</span><span id="line-154"> }</span>
-<span class="source-line-no">155</span><span id="line-155"></span>
-<span class="source-line-no">156</span><span id="line-156"> @Override</span>
-<span class="source-line-no">157</span><span id="line-157"> public void describeTo(Description description) {</span>
-<span class="source-line-no">158</span><span id="line-158"> description.appendText("sub procedure of(").appendValue(proc).appendText(")");</span>
+<span class="source-line-no">136</span><span id="line-136"> // Surefire reruns failed tests in the same JVM without rerunning @BeforeAll, so reset the</span>
+<span class="source-line-no">137</span><span id="line-137"> // fault injection state before each attempt.</span>
+<span class="source-line-no">138</span><span id="line-138"> INJECTED.set(false);</span>
+<span class="source-line-no">139</span><span id="line-139"> ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdateInRollback(</span>
+<span class="source-line-no">140</span><span id="line-140"> UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor(), false);</span>
+<span class="source-line-no">141</span><span id="line-141"> }</span>
+<span class="source-line-no">142</span><span id="line-142"></span>
+<span class="source-line-no">143</span><span id="line-143"> private ServerCrashProcedure getSCPForServer(ServerName serverName) throws IOException {</span>
+<span class="source-line-no">144</span><span id="line-144"> return UTIL.getMiniHBaseCluster().getMaster().getProcedures().stream()</span>
+<span class="source-line-no">145</span><span id="line-145"> .filter(p -> p instanceof ServerCrashProcedure).map(p -> (ServerCrashProcedure) p)</span>
+<span class="source-line-no">146</span><span id="line-146"> .filter(p -> p.getServerName().equals(serverName)).findFirst().orElse(null);</span>
+<span class="source-line-no">147</span><span id="line-147"> }</span>
+<span class="source-line-no">148</span><span id="line-148"></span>
+<span class="source-line-no">149</span><span id="line-149"> private Matcher<Procedure<MasterProcedureEnv>> subProcOf(Procedure<MasterProcedureEnv> proc) {</span>
+<span class="source-line-no">150</span><span id="line-150"> return new BaseMatcher<Procedure<MasterProcedureEnv>>() {</span>
+<span class="source-line-no">151</span><span id="line-151"></span>
+<span class="source-line-no">152</span><span id="line-152"> @Override</span>
+<span class="source-line-no">153</span><span id="line-153"> public boolean matches(Object item) {</span>
+<span class="source-line-no">154</span><span id="line-154"> if (!(item instanceof Procedure)) {</span>
+<span class="source-line-no">155</span><span id="line-155"> return false;</span>
+<span class="source-line-no">156</span><span id="line-156"> }</span>
+<span class="source-line-no">157</span><span id="line-157"> Procedure<?> p = (Procedure<?>) item;</span>
+<span class="source-line-no">158</span><span id="line-158"> return p.hasParent() && p.getRootProcId() == proc.getProcId();</span>
<span class="source-line-no">159</span><span id="line-159"> }</span>
-<span class="source-line-no">160</span><span id="line-160"> };</span>
-<span class="source-line-no">161</span><span id="line-161"> }</span>
-<span class="source-line-no">162</span><span id="line-162"></span>
-<span class="source-line-no">163</span><span id="line-163"> @Test</span>
-<span class="source-line-no">164</span><span id="line-164"> public void testFailAndRollback() throws Exception {</span>
-<span class="source-line-no">165</span><span id="line-165"> HRegionServer rsWithMeta = UTIL.getRSForFirstRegionInTable(TableName.META_TABLE_NAME);</span>
-<span class="source-line-no">166</span><span id="line-166"> UTIL.getMiniHBaseCluster().killRegionServer(rsWithMeta.getServerName());</span>
-<span class="source-line-no">167</span><span id="line-167"> UTIL.waitFor(15000, () -> getSCPForServer(rsWithMeta.getServerName()) != null);</span>
-<span class="source-line-no">168</span><span id="line-168"> ServerCrashProcedure scp = getSCPForServer(rsWithMeta.getServerName());</span>
-<span class="source-line-no">169</span><span id="line-169"> ProcedureExecutor<MasterProcedureEnv> procExec =</span>
-<span class="source-line-no">170</span><span id="line-170"> UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor();</span>
-<span class="source-line-no">171</span><span id="line-171"> // wait for the procedure to stop, as we inject a code bug and also set kill before store update</span>
-<span class="source-line-no">172</span><span id="line-172"> UTIL.waitFor(30000, () -> !procExec.isRunning());</span>
-<span class="source-line-no">173</span><span id="line-173"> // make sure that finally we could successfully rollback the procedure</span>
-<span class="source-line-no">174</span><span id="line-174"> while (scp.getState() != ProcedureState.FAILED || !procExec.isRunning()) {</span>
-<span class="source-line-no">175</span><span id="line-175"> MasterProcedureTestingUtility.restartMasterProcedureExecutor(procExec);</span>
-<span class="source-line-no">176</span><span id="line-176"> ProcedureTestingUtility.waitProcedure(procExec, scp);</span>
-<span class="source-line-no">177</span><span id="line-177"> }</span>
-<span class="source-line-no">178</span><span id="line-178"> assertEquals(scp.getState(), ProcedureState.FAILED);</span>
-<span class="source-line-no">179</span><span id="line-179"> assertThat(scp.getException().getMessage(), containsString("inject code bug"));</span>
-<span class="source-line-no">180</span><span id="line-180"> // make sure all sub procedures are cleaned up</span>
-<span class="source-line-no">181</span><span id="line-181"> assertThat(procExec.getProcedures(), everyItem(not(subProcOf(scp))));</span>
-<span class="source-line-no">182</span><span id="line-182"> }</span>
-<span class="source-line-no">183</span><span id="line-183">}</span>
+<span class="source-line-no">160</span><span id="line-160"></span>
+<span class="source-line-no">161</span><span id="line-161"> @Override</span>
+<span class="source-line-no">162</span><span id="line-162"> public void describeTo(Description description) {</span>
+<span class="source-line-no">163</span><span id="line-163"> description.appendText("sub procedure of(").appendValue(proc).appendText(")");</span>
+<span class="source-line-no">164</span><span id="line-164"> }</span>
+<span class="source-line-no">165</span><span id="line-165"> };</span>
+<span class="source-line-no">166</span><span id="line-166"> }</span>
+<span class="source-line-no">167</span><span id="line-167"></span>
+<span class="source-line-no">168</span><span id="line-168"> @Test</span>
+<span class="source-line-no">169</span><span id="line-169"> public void testFailAndRollback() throws Exception {</span>
+<span class="source-line-no">170</span><span id="line-170"> HRegionServer rsWithMeta = UTIL.getRSForFirstRegionInTable(TableName.META_TABLE_NAME);</span>
+<span class="source-line-no">171</span><span id="line-171"> UTIL.getMiniHBaseCluster().killRegionServer(rsWithMeta.getServerName());</span>
+<span class="source-line-no">172</span><span id="line-172"> UTIL.waitFor(15000, () -> getSCPForServer(rsWithMeta.getServerName()) != null);</span>
+<span class="source-line-no">173</span><span id="line-173"> ServerCrashProcedure scp = getSCPForServer(rsWithMeta.getServerName());</span>
+<span class="source-line-no">174</span><span id="line-174"> ProcedureExecutor<MasterProcedureEnv> procExec =</span>
+<span class="source-line-no">175</span><span id="line-175"> UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor();</span>
+<span class="source-line-no">176</span><span id="line-176"> // wait for the procedure to stop, as we inject a code bug and also set kill before store update</span>
+<span class="source-line-no">177</span><span id="line-177"> UTIL.waitFor(30000, () -> !procExec.isRunning());</span>
+<span class="source-line-no">178</span><span id="line-178"> // make sure that finally we could successfully rollback the procedure</span>
+<span class="source-line-no">179</span><span id="line-179"> while (scp.getState() != ProcedureState.FAILED || !procExec.isRunning()) {</span>
+<span class="source-line-no">180</span><span id="line-180"> MasterProcedureTestingUtility.restartMasterProcedureExecutor(procExec);</span>
+<span class="source-line-no">181</span><span id="line-181"> ProcedureTestingUtility.waitProcedure(procExec, scp);</span>
+<span class="source-line-no">182</span><span id="line-182"> }</span>
+<span class="source-line-no">183</span><span id="line-183"> assertEquals(scp.getState(), ProcedureState.FAILED);</span>
+<span class="source-line-no">184</span><span id="line-184"> assertThat(scp.getException().getMessage(), containsString("inject code bug"));</span>
+<span class="source-line-no">185</span><span id="line-185"> // make sure all sub procedures are cleaned up</span>
+<span class="source-line-no">186</span><span id="line-186"> assertThat(procExec.getProcedures(), everyItem(not(subProcOf(scp))));</span>
+<span class="source-line-no">187</span><span id="line-187"> }</span>
+<span class="source-line-no">188</span><span id="line-188">}</span>
diff --git a/testdevapidocs/src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html b/testdevapidocs/src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html
index fabe4c735c5..82053529f6b 100644
--- a/testdevapidocs/src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html
+++ b/testdevapidocs/src-html/org/apache/hadoop/hbase/master/assignment/TestRollbackSCP.html
@@ -146,54 +146,59 @@
<span class="source-line-no">133</span><span id="line-133"> @BeforeEach</span>
<span class="source-line-no">134</span><span id="line-134"> public void setUp() throws IOException {</span>
<span class="source-line-no">135</span><span id="line-135"> UTIL.ensureSomeNonStoppedRegionServersAvailable(2);</span>
-<span class="source-line-no">136</span><span id="line-136"> }</span>
-<span class="source-line-no">137</span><span id="line-137"></span>
-<span class="source-line-no">138</span><span id="line-138"> private ServerCrashProcedure getSCPForServer(ServerName serverName) throws IOException {</span>
-<span class="source-line-no">139</span><span id="line-139"> return UTIL.getMiniHBaseCluster().getMaster().getProcedures().stream()</span>
-<span class="source-line-no">140</span><span id="line-140"> .filter(p -> p instanceof ServerCrashProcedure).map(p -> (ServerCrashProcedure) p)</span>
-<span class="source-line-no">141</span><span id="line-141"> .filter(p -> p.getServerName().equals(serverName)).findFirst().orElse(null);</span>
-<span class="source-line-no">142</span><span id="line-142"> }</span>
-<span class="source-line-no">143</span><span id="line-143"></span>
-<span class="source-line-no">144</span><span id="line-144"> private Matcher<Procedure<MasterProcedureEnv>> subProcOf(Procedure<MasterProcedureEnv> proc) {</span>
-<span class="source-line-no">145</span><span id="line-145"> return new BaseMatcher<Procedure<MasterProcedureEnv>>() {</span>
-<span class="source-line-no">146</span><span id="line-146"></span>
-<span class="source-line-no">147</span><span id="line-147"> @Override</span>
-<span class="source-line-no">148</span><span id="line-148"> public boolean matches(Object item) {</span>
-<span class="source-line-no">149</span><span id="line-149"> if (!(item instanceof Procedure)) {</span>
-<span class="source-line-no">150</span><span id="line-150"> return false;</span>
-<span class="source-line-no">151</span><span id="line-151"> }</span>
-<span class="source-line-no">152</span><span id="line-152"> Procedure<?> p = (Procedure<?>) item;</span>
-<span class="source-line-no">153</span><span id="line-153"> return p.hasParent() && p.getRootProcId() == proc.getProcId();</span>
-<span class="source-line-no">154</span><span id="line-154"> }</span>
-<span class="source-line-no">155</span><span id="line-155"></span>
-<span class="source-line-no">156</span><span id="line-156"> @Override</span>
-<span class="source-line-no">157</span><span id="line-157"> public void describeTo(Description description) {</span>
-<span class="source-line-no">158</span><span id="line-158"> description.appendText("sub procedure of(").appendValue(proc).appendText(")");</span>
+<span class="source-line-no">136</span><span id="line-136"> // Surefire reruns failed tests in the same JVM without rerunning @BeforeAll, so reset the</span>
+<span class="source-line-no">137</span><span id="line-137"> // fault injection state before each attempt.</span>
+<span class="source-line-no">138</span><span id="line-138"> INJECTED.set(false);</span>
+<span class="source-line-no">139</span><span id="line-139"> ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdateInRollback(</span>
+<span class="source-line-no">140</span><span id="line-140"> UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor(), false);</span>
+<span class="source-line-no">141</span><span id="line-141"> }</span>
+<span class="source-line-no">142</span><span id="line-142"></span>
+<span class="source-line-no">143</span><span id="line-143"> private ServerCrashProcedure getSCPForServer(ServerName serverName) throws IOException {</span>
+<span class="source-line-no">144</span><span id="line-144"> return UTIL.getMiniHBaseCluster().getMaster().getProcedures().stream()</span>
+<span class="source-line-no">145</span><span id="line-145"> .filter(p -> p instanceof ServerCrashProcedure).map(p -> (ServerCrashProcedure) p)</span>
+<span class="source-line-no">146</span><span id="line-146"> .filter(p -> p.getServerName().equals(serverName)).findFirst().orElse(null);</span>
+<span class="source-line-no">147</span><span id="line-147"> }</span>
+<span class="source-line-no">148</span><span id="line-148"></span>
+<span class="source-line-no">149</span><span id="line-149"> private Matcher<Procedure<MasterProcedureEnv>> subProcOf(Procedure<MasterProcedureEnv> proc) {</span>
+<span class="source-line-no">150</span><span id="line-150"> return new BaseMatcher<Procedure<MasterProcedureEnv>>() {</span>
+<span class="source-line-no">151</span><span id="line-151"></span>
+<span class="source-line-no">152</span><span id="line-152"> @Override</span>
+<span class="source-line-no">153</span><span id="line-153"> public boolean matches(Object item) {</span>
+<span class="source-line-no">154</span><span id="line-154"> if (!(item instanceof Procedure)) {</span>
+<span class="source-line-no">155</span><span id="line-155"> return false;</span>
+<span class="source-line-no">156</span><span id="line-156"> }</span>
+<span class="source-line-no">157</span><span id="line-157"> Procedure<?> p = (Procedure<?>) item;</span>
+<span class="source-line-no">158</span><span id="line-158"> return p.hasParent() && p.getRootProcId() == proc.getProcId();</span>
<span class="source-line-no">159</span><span id="line-159"> }</span>
-<span class="source-line-no">160</span><span id="line-160"> };</span>
-<span class="source-line-no">161</span><span id="line-161"> }</span>
-<span class="source-line-no">162</span><span id="line-162"></span>
-<span class="source-line-no">163</span><span id="line-163"> @Test</span>
-<span class="source-line-no">164</span><span id="line-164"> public void testFailAndRollback() throws Exception {</span>
-<span class="source-line-no">165</span><span id="line-165"> HRegionServer rsWithMeta = UTIL.getRSForFirstRegionInTable(TableName.META_TABLE_NAME);</span>
-<span class="source-line-no">166</span><span id="line-166"> UTIL.getMiniHBaseCluster().killRegionServer(rsWithMeta.getServerName());</span>
-<span class="source-line-no">167</span><span id="line-167"> UTIL.waitFor(15000, () -> getSCPForServer(rsWithMeta.getServerName()) != null);</span>
-<span class="source-line-no">168</span><span id="line-168"> ServerCrashProcedure scp = getSCPForServer(rsWithMeta.getServerName());</span>
-<span class="source-line-no">169</span><span id="line-169"> ProcedureExecutor<MasterProcedureEnv> procExec =</span>
-<span class="source-line-no">170</span><span id="line-170"> UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor();</span>
-<span class="source-line-no">171</span><span id="line-171"> // wait for the procedure to stop, as we inject a code bug and also set kill before store update</span>
-<span class="source-line-no">172</span><span id="line-172"> UTIL.waitFor(30000, () -> !procExec.isRunning());</span>
-<span class="source-line-no">173</span><span id="line-173"> // make sure that finally we could successfully rollback the procedure</span>
-<span class="source-line-no">174</span><span id="line-174"> while (scp.getState() != ProcedureState.FAILED || !procExec.isRunning()) {</span>
-<span class="source-line-no">175</span><span id="line-175"> MasterProcedureTestingUtility.restartMasterProcedureExecutor(procExec);</span>
-<span class="source-line-no">176</span><span id="line-176"> ProcedureTestingUtility.waitProcedure(procExec, scp);</span>
-<span class="source-line-no">177</span><span id="line-177"> }</span>
-<span class="source-line-no">178</span><span id="line-178"> assertEquals(scp.getState(), ProcedureState.FAILED);</span>
-<span class="source-line-no">179</span><span id="line-179"> assertThat(scp.getException().getMessage(), containsString("inject code bug"));</span>
-<span class="source-line-no">180</span><span id="line-180"> // make sure all sub procedures are cleaned up</span>
-<span class="source-line-no">181</span><span id="line-181"> assertThat(procExec.getProcedures(), everyItem(not(subProcOf(scp))));</span>
-<span class="source-line-no">182</span><span id="line-182"> }</span>
-<span class="source-line-no">183</span><span id="line-183">}</span>
+<span class="source-line-no">160</span><span id="line-160"></span>
+<span class="source-line-no">161</span><span id="line-161"> @Override</span>
+<span class="source-line-no">162</span><span id="line-162"> public void describeTo(Description description) {</span>
+<span class="source-line-no">163</span><span id="line-163"> description.appendText("sub procedure of(").appendValue(proc).appendText(")");</span>
+<span class="source-line-no">164</span><span id="line-164"> }</span>
+<span class="source-line-no">165</span><span id="line-165"> };</span>
+<span class="source-line-no">166</span><span id="line-166"> }</span>
+<span class="source-line-no">167</span><span id="line-167"></span>
+<span class="source-line-no">168</span><span id="line-168"> @Test</span>
+<span class="source-line-no">169</span><span id="line-169"> public void testFailAndRollback() throws Exception {</span>
+<span class="source-line-no">170</span><span id="line-170"> HRegionServer rsWithMeta = UTIL.getRSForFirstRegionInTable(TableName.META_TABLE_NAME);</span>
+<span class="source-line-no">171</span><span id="line-171"> UTIL.getMiniHBaseCluster().killRegionServer(rsWithMeta.getServerName());</span>
+<span class="source-line-no">172</span><span id="line-172"> UTIL.waitFor(15000, () -> getSCPForServer(rsWithMeta.getServerName()) != null);</span>
+<span class="source-line-no">173</span><span id="line-173"> ServerCrashProcedure scp = getSCPForServer(rsWithMeta.getServerName());</span>
+<span class="source-line-no">174</span><span id="line-174"> ProcedureExecutor<MasterProcedureEnv> procExec =</span>
+<span class="source-line-no">175</span><span id="line-175"> UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor();</span>
+<span class="source-line-no">176</span><span id="line-176"> // wait for the procedure to stop, as we inject a code bug and also set kill before store update</span>
+<span class="source-line-no">177</span><span id="line-177"> UTIL.waitFor(30000, () -> !procExec.isRunning());</span>
+<span class="source-line-no">178</span><span id="line-178"> // make sure that finally we could successfully rollback the procedure</span>
+<span class="source-line-no">179</span><span id="line-179"> while (scp.getState() != ProcedureState.FAILED || !procExec.isRunning()) {</span>
+<span class="source-line-no">180</span><span id="line-180"> MasterProcedureTestingUtility.restartMasterProcedureExecutor(procExec);</span>
+<span class="source-line-no">181</span><span id="line-181"> ProcedureTestingUtility.waitProcedure(procExec, scp);</span>
+<span class="source-line-no">182</span><span id="line-182"> }</span>
+<span class="source-line-no">183</span><span id="line-183"> assertEquals(scp.getState(), ProcedureState.FAILED);</span>
+<span class="source-line-no">184</span><span id="line-184"> assertThat(scp.getException().getMessage(), containsString("inject code bug"));</span>
+<span class="source-line-no">185</span><span id="line-185"> // make sure all sub procedures are cleaned up</span>
+<span class="source-line-no">186</span><span id="line-186"> assertThat(procExec.getProcedures(), everyItem(not(subProcOf(scp))));</span>
+<span class="source-line-no">187</span><span id="line-187"> }</span>
+<span class="source-line-no">188</span><span id="line-188">}</span>