This is an automated email from the ASF dual-hosted git repository.

apurtell pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/master by this push:
     new 63c0838bb22 HBASE-27826 Add FSFT implementations for Virtual links and 
enable them as part of SplitProcedure (#6936)
63c0838bb22 is described below

commit 63c0838bb22554e0f0e8ea936fc24992d4754833
Author: gvprathyusha6 <[email protected]>
AuthorDate: Tue Apr 28 03:58:49 2026 +0530

    HBASE-27826 Add FSFT implementations for Virtual links and enable them as 
part of SplitProcedure (#6936)
    
    Signed-off-by: Andrew Purtell <[email protected]>
    Reviewed-by: sanjeet006py <[email protected]>
---
 .../protobuf/server/region/StoreFileTracker.proto  |   2 +
 .../java/org/apache/hadoop/hbase/io/HFileLink.java |   2 +-
 .../java/org/apache/hadoop/hbase/io/Reference.java |   2 +-
 .../assignment/MergeTableRegionsProcedure.java     |   8 +-
 .../assignment/SplitTableRegionProcedure.java      | 130 +++++++++++----------
 .../hadoop/hbase/regionserver/HMobStore.java       |   1 +
 .../hbase/regionserver/HRegionFileSystem.java      |  54 ++++++---
 .../apache/hadoop/hbase/regionserver/HStore.java   |  12 +-
 .../hadoop/hbase/regionserver/StoreFileInfo.java   |  52 ++++++++-
 .../hadoop/hbase/regionserver/StoreUtils.java      |  11 ++
 .../FileBasedStoreFileTracker.java                 |  86 +++++++++++++-
 .../storefiletracker/StoreFileListFile.java        |   2 +-
 .../storefiletracker/StoreFileTracker.java         |  20 +++-
 .../storefiletracker/StoreFileTrackerBase.java     |  42 +++++--
 .../storefiletracker/StoreFileTrackerFactory.java  |  11 +-
 .../hbase/snapshot/RestoreSnapshotHelper.java      | 108 +++++++++--------
 ...shotFromClientAfterSplittingRegionTestBase.java |   1 -
 .../apache/hadoop/hbase/io/hfile/TestPrefetch.java |   2 +-
 .../hfile/bucket/TestPrefetchWithBucketCache.java  |   2 +-
 .../hbase/master/cleaner/TestHFileLinkCleaner.java |  18 ++-
 .../hbase/mob/TestMobCompactionWithDefaults.java   |  83 +++++++++----
 .../regionserver/TestDirectStoreSplitsMerges.java  |  14 +--
 .../regionserver/TestHRegionReplayEvents.java      |  19 ++-
 .../hadoop/hbase/regionserver/TestHStoreFile.java  |  10 +-
 .../regionserver/TestMergesSplitsAddToTracker.java |   8 +-
 .../TestSplitTransactionOnCluster.java             |   9 +-
 .../hbase/regionserver/TestStoreFileInfo.java      |   8 +-
 .../storefiletracker/TestStoreFileListFile.java    |  49 ++++++++
 28 files changed, 555 insertions(+), 211 deletions(-)

diff --git 
a/hbase-protocol-shaded/src/main/protobuf/server/region/StoreFileTracker.proto 
b/hbase-protocol-shaded/src/main/protobuf/server/region/StoreFileTracker.proto
index 001cb3ea233..985c693b9c6 100644
--- 
a/hbase-protocol-shaded/src/main/protobuf/server/region/StoreFileTracker.proto
+++ 
b/hbase-protocol-shaded/src/main/protobuf/server/region/StoreFileTracker.proto
@@ -25,9 +25,11 @@ option java_generic_services = true;
 option java_generate_equals_and_hash = true;
 option optimize_for = SPEED;
 
+import "server/io/FS.proto";
 message StoreFileEntry {
   required string name = 1;
   required uint64 size = 2;
+  optional Reference reference = 3;
 }
 
 message StoreFileList {
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/HFileLink.java 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/HFileLink.java
index bd5fac1c3c4..b31ebfe578d 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/HFileLink.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/HFileLink.java
@@ -81,7 +81,7 @@ public class HFileLink extends FileLink {
    * The pattern should be used for hfile and reference links that can be 
found in
    * /hbase/table/region/family/
    */
-  private static final Pattern REF_OR_HFILE_LINK_PATTERN =
+  public static final Pattern REF_OR_HFILE_LINK_PATTERN =
     Pattern.compile(String.format("^(?:(%s)(?:=))?(%s)=(%s)-(.+)$", 
TableName.VALID_NAMESPACE_REGEX,
       TableName.VALID_TABLE_QUALIFIER_REGEX, 
RegionInfoBuilder.ENCODED_REGION_NAME_REGEX));
 
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/Reference.java 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/Reference.java
index 5388a1105c3..ab305d02f0f 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/Reference.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/Reference.java
@@ -56,7 +56,7 @@ public class Reference {
    * For split HStoreFiles, it specifies if the file covers the lower half or 
the upper half of the
    * key range
    */
-  static enum Range {
+  public static enum Range {
     /** HStoreFile contains upper half of key range */
     top,
     /** HStoreFile contains lower half of key range */
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/MergeTableRegionsProcedure.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/MergeTableRegionsProcedure.java
index c370fed9d9c..8cc447c5224 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/MergeTableRegionsProcedure.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/MergeTableRegionsProcedure.java
@@ -605,7 +605,7 @@ public class MergeTableRegionsProcedure
     final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
     final Path tableDir = CommonFSUtils.getTableDir(mfs.getRootDir(), 
regionsToMerge[0].getTable());
     final FileSystem fs = mfs.getFileSystem();
-    List<Path> mergedFiles = new ArrayList<>();
+    List<StoreFileInfo> mergedFiles = new ArrayList<StoreFileInfo>();
     HRegionFileSystem mergeRegionFs = HRegionFileSystem
       .createRegionOnFileSystem(env.getMasterConfiguration(), fs, tableDir, 
mergedRegion);
 
@@ -622,11 +622,11 @@ public class MergeTableRegionsProcedure
       .setState(State.MERGING_NEW);
   }
 
-  private List<Path> mergeStoreFiles(MasterProcedureEnv env, HRegionFileSystem 
regionFs,
+  private List<StoreFileInfo> mergeStoreFiles(MasterProcedureEnv env, 
HRegionFileSystem regionFs,
     HRegionFileSystem mergeRegionFs, RegionInfo mergedRegion) throws 
IOException {
     final TableDescriptor htd =
       
env.getMasterServices().getTableDescriptors().get(mergedRegion.getTable());
-    List<Path> mergedFiles = new ArrayList<>();
+    List<StoreFileInfo> mergedFiles = new ArrayList<StoreFileInfo>();
     for (ColumnFamilyDescriptor hcd : htd.getColumnFamilies()) {
       String family = hcd.getNameAsString();
       StoreFileTracker tracker =
@@ -643,7 +643,7 @@ public class MergeTableRegionsProcedure
           // is running in a regionserver's Store context, or we might not be 
able
           // to read the hfiles.
           storeFileInfo.setConf(storeConfiguration);
-          Path refFile = 
mergeRegionFs.mergeStoreFile(regionFs.getRegionInfo(), family,
+          StoreFileInfo refFile = 
mergeRegionFs.mergeStoreFile(regionFs.getRegionInfo(), family,
             new HStoreFile(storeFileInfo, hcd.getBloomFilterType(), 
CacheConfig.DISABLED), tracker);
           mergedFiles.add(refFile);
         }
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java
index 3d3d3d18de2..07d4698a42a 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java
@@ -71,7 +71,6 @@ import 
org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFac
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.CommonFSUtils;
 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
-import org.apache.hadoop.hbase.util.FSUtils;
 import org.apache.hadoop.hbase.util.Pair;
 import org.apache.hadoop.hbase.util.Threads;
 import org.apache.hadoop.hbase.wal.WALSplitUtil;
@@ -660,20 +659,57 @@ public class SplitTableRegionProcedure
     HRegionFileSystem regionFs = HRegionFileSystem.openRegionFromFileSystem(
       env.getMasterConfiguration(), fs, tabledir, getParentRegion(), false);
     regionFs.createSplitsDir(daughterOneRI, daughterTwoRI);
+    Pair<List<StoreFileInfo>, List<StoreFileInfo>> expectedReferences =
+      splitStoreFiles(env, regionFs);
+    final ExecutorService threadPool = Executors.newFixedThreadPool(2,
+      new 
ThreadFactoryBuilder().setNameFormat("RegionCommitter-pool-%d").setDaemon(true)
+        
.setUncaughtExceptionHandler(Threads.LOGGING_EXCEPTION_HANDLER).build());
+    Future<Path> futureOne = threadPool.submit(new Callable<Path>() {
+      @Override
+      public Path call() throws IOException {
+        return regionFs.commitDaughterRegion(daughterOneRI, 
expectedReferences.getFirst(), env);
+      }
+    });
+    Future<Path> futureTwo = threadPool.submit(new Callable<Path>() {
+      @Override
+      public Path call() throws IOException {
+        return regionFs.commitDaughterRegion(daughterTwoRI, 
expectedReferences.getSecond(), env);
+      }
+    });
+    handleThreadPoolShutdown(threadPool, env.getMasterConfiguration());
 
-    Pair<List<Path>, List<Path>> expectedReferences = splitStoreFiles(env, 
regionFs);
-
-    assertSplitResultFilesCount(fs, expectedReferences.getFirst().size(),
-      regionFs.getSplitsDir(daughterOneRI));
-    regionFs.commitDaughterRegion(daughterOneRI, 
expectedReferences.getFirst(), env);
-    assertSplitResultFilesCount(fs, expectedReferences.getFirst().size(),
-      new Path(tabledir, daughterOneRI.getEncodedName()));
+    try {
+      futureOne.get();
+      futureTwo.get();
+    } catch (InterruptedException e) {
+      throw (InterruptedIOException) new InterruptedIOException().initCause(e);
+    } catch (ExecutionException e) {
+      throw new IOException("Daughter region commit failed", e);
+    }
+  }
 
-    assertSplitResultFilesCount(fs, expectedReferences.getSecond().size(),
-      regionFs.getSplitsDir(daughterTwoRI));
-    regionFs.commitDaughterRegion(daughterTwoRI, 
expectedReferences.getSecond(), env);
-    assertSplitResultFilesCount(fs, expectedReferences.getSecond().size(),
-      new Path(tabledir, daughterTwoRI.getEncodedName()));
+  private void handleThreadPoolShutdown(ExecutorService threadPool, 
Configuration conf)
+    throws IOException {
+    threadPool.shutdown();
+    // Wait for all the tasks to finish.
+    // When splits ran on the RegionServer, how-long-to-wait-configuration was 
named
+    // fileSplitTimeout. If set, use its value.
+    long fileSplitTimeout = conf.getLong("hbase.master.fileSplitTimeout",
+      conf.getLong("hbase.regionserver.fileSplitTimeout", 600000));
+    try {
+      boolean stillRunning = !threadPool.awaitTermination(fileSplitTimeout, 
TimeUnit.MILLISECONDS);
+      if (stillRunning) {
+        threadPool.shutdownNow();
+        // wait for the thread to shutdown completely.
+        while (!threadPool.isTerminated()) {
+          Thread.sleep(50);
+        }
+        throw new IOException(
+          "Took too long to split the files and create the references, 
aborting split");
+      }
+    } catch (InterruptedException e) {
+      throw (InterruptedIOException) new InterruptedIOException().initCause(e);
+    }
   }
 
   private void deleteDaughterRegions(final MasterProcedureEnv env) throws 
IOException {
@@ -689,8 +725,8 @@ public class SplitTableRegionProcedure
    * Create Split directory
    * @param env MasterProcedureEnv
    */
-  private Pair<List<Path>, List<Path>> splitStoreFiles(final 
MasterProcedureEnv env,
-    final HRegionFileSystem regionFs) throws IOException {
+  private Pair<List<StoreFileInfo>, List<StoreFileInfo>> splitStoreFiles(
+    final MasterProcedureEnv env, final HRegionFileSystem regionFs) throws 
IOException {
     final Configuration conf = env.getMasterConfiguration();
     TableDescriptor htd = 
env.getMasterServices().getTableDescriptors().get(getTableName());
     // The following code sets up a thread pool executor with as many slots as
@@ -745,7 +781,8 @@ public class SplitTableRegionProcedure
     final ExecutorService threadPool = Executors.newFixedThreadPool(maxThreads,
       new 
ThreadFactoryBuilder().setNameFormat("StoreFileSplitter-pool-%d").setDaemon(true)
         
.setUncaughtExceptionHandler(Threads.LOGGING_EXCEPTION_HANDLER).build());
-    final List<Future<Pair<Path, Path>>> futures = new 
ArrayList<Future<Pair<Path, Path>>>(nbFiles);
+    final List<Future<Pair<StoreFileInfo, StoreFileInfo>>> futures =
+      new ArrayList<Future<Pair<StoreFileInfo, StoreFileInfo>>>(nbFiles);
 
     // Split each store file.
     for (Map.Entry<String, Collection<StoreFileInfo>> e : files.entrySet()) {
@@ -769,35 +806,13 @@ public class SplitTableRegionProcedure
         }
       }
     }
-    // Shutdown the pool
-    threadPool.shutdown();
-
-    // Wait for all the tasks to finish.
-    // When splits ran on the RegionServer, how-long-to-wait-configuration was 
named
-    // hbase.regionserver.fileSplitTimeout. If set, use its value.
-    long fileSplitTimeout = conf.getLong("hbase.master.fileSplitTimeout",
-      conf.getLong("hbase.regionserver.fileSplitTimeout", 600000));
-    try {
-      boolean stillRunning = !threadPool.awaitTermination(fileSplitTimeout, 
TimeUnit.MILLISECONDS);
-      if (stillRunning) {
-        threadPool.shutdownNow();
-        // wait for the thread to shutdown completely.
-        while (!threadPool.isTerminated()) {
-          Thread.sleep(50);
-        }
-        throw new IOException(
-          "Took too long to split the" + " files and create the references, 
aborting split");
-      }
-    } catch (InterruptedException e) {
-      throw (InterruptedIOException) new InterruptedIOException().initCause(e);
-    }
-
-    List<Path> daughterA = new ArrayList<>();
-    List<Path> daughterB = new ArrayList<>();
+    handleThreadPoolShutdown(threadPool, conf);
+    List<StoreFileInfo> daughterA = new ArrayList<>();
+    List<StoreFileInfo> daughterB = new ArrayList<>();
     // Look for any exception
-    for (Future<Pair<Path, Path>> future : futures) {
+    for (Future<Pair<StoreFileInfo, StoreFileInfo>> future : futures) {
       try {
-        Pair<Path, Path> p = future.get();
+        Pair<StoreFileInfo, StoreFileInfo> p = future.get();
         if (p.getFirst() != null) {
           daughterA.add(p.getFirst());
         }
@@ -819,19 +834,8 @@ public class SplitTableRegionProcedure
     return new Pair<>(daughterA, daughterB);
   }
 
-  private void assertSplitResultFilesCount(final FileSystem fs,
-    final int expectedSplitResultFileCount, Path dir) throws IOException {
-    if (expectedSplitResultFileCount != 0) {
-      int resultFileCount = FSUtils.getRegionReferenceAndLinkFileCount(fs, 
dir);
-      if (expectedSplitResultFileCount != resultFileCount) {
-        throw new IOException("Failing split. Didn't have expected reference 
and HFileLink files"
-          + ", expected=" + expectedSplitResultFileCount + ", actual=" + 
resultFileCount);
-      }
-    }
-  }
-
-  private Pair<Path, Path> splitStoreFile(HRegionFileSystem regionFs, 
TableDescriptor htd,
-    ColumnFamilyDescriptor hcd, HStoreFile sf) throws IOException {
+  private Pair<StoreFileInfo, StoreFileInfo> splitStoreFile(HRegionFileSystem 
regionFs,
+    TableDescriptor htd, ColumnFamilyDescriptor hcd, HStoreFile sf) throws 
IOException {
     if (LOG.isDebugEnabled()) {
       LOG.debug("pid=" + getProcId() + " splitting started for store file: " + 
sf.getPath()
         + " for region: " + getParentRegion().getShortNameToLog());
@@ -847,22 +851,22 @@ public class SplitTableRegionProcedure
       StoreFileTrackerFactory.create(regionFs.getFileSystem().getConf(), htd, 
hcd,
         HRegionFileSystem.create(regionFs.getFileSystem().getConf(), 
regionFs.getFileSystem(),
           regionFs.getTableDir(), daughterTwoRI));
-    final Path path_first = regionFs.splitStoreFile(this.daughterOneRI, 
familyName, sf, splitRow,
-      false, splitPolicy, daughterOneSft);
-    final Path path_second = regionFs.splitStoreFile(this.daughterTwoRI, 
familyName, sf, splitRow,
-      true, splitPolicy, daughterTwoSft);
+    final StoreFileInfo sfiFirst = regionFs.splitStoreFile(this.daughterOneRI, 
familyName, sf,
+      splitRow, false, splitPolicy, daughterOneSft);
+    final StoreFileInfo sfiSecond = 
regionFs.splitStoreFile(this.daughterTwoRI, familyName, sf,
+      splitRow, true, splitPolicy, daughterTwoSft);
     if (LOG.isDebugEnabled()) {
       LOG.debug("pid=" + getProcId() + " splitting complete for store file: " 
+ sf.getPath()
         + " for region: " + getParentRegion().getShortNameToLog());
     }
-    return new Pair<Path, Path>(path_first, path_second);
+    return new Pair<StoreFileInfo, StoreFileInfo>(sfiFirst, sfiSecond);
   }
 
   /**
    * Utility class used to do the file splitting / reference writing in 
parallel instead of
    * sequentially.
    */
-  private class StoreFileSplitter implements Callable<Pair<Path, Path>> {
+  private class StoreFileSplitter implements Callable<Pair<StoreFileInfo, 
StoreFileInfo>> {
     private final HRegionFileSystem regionFs;
     private final ColumnFamilyDescriptor hcd;
     private final HStoreFile sf;
@@ -883,7 +887,7 @@ public class SplitTableRegionProcedure
     }
 
     @Override
-    public Pair<Path, Path> call() throws IOException {
+    public Pair<StoreFileInfo, StoreFileInfo> call() throws IOException {
       return splitStoreFile(regionFs, htd, hcd, sf);
     }
   }
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HMobStore.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HMobStore.java
index f828fe33cea..daed80ee8db 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HMobStore.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HMobStore.java
@@ -102,6 +102,7 @@ public class HMobStore extends HStore {
   // table, we need to find the original mob files by this table name. For 
details please see
   // cloning snapshot for mob files.
   private final byte[] refCellTags;
+  private StoreFileTracker mobStoreSFT = null;
 
   public HMobStore(final HRegion region, final ColumnFamilyDescriptor family,
     final Configuration confParam, boolean warmup) throws IOException {
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionFileSystem.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionFileSystem.java
index a1bf09cba5d..ac7a5faa274 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionFileSystem.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionFileSystem.java
@@ -496,7 +496,7 @@ public class HRegionFileSystem {
    * in the filesystem.
    * @param regionInfo daughter {@link 
org.apache.hadoop.hbase.client.RegionInfo}
    */
-  public Path commitDaughterRegion(final RegionInfo regionInfo, List<Path> 
allRegionFiles,
+  public Path commitDaughterRegion(final RegionInfo regionInfo, 
List<StoreFileInfo> allRegionFiles,
     MasterProcedureEnv env) throws IOException {
     Path regionDir = this.getSplitsDir(regionInfo);
     if (fs.exists(regionDir)) {
@@ -511,21 +511,43 @@ public class HRegionFileSystem {
     return regionDir;
   }
 
-  private void insertRegionFilesIntoStoreTracker(List<Path> allFiles, 
MasterProcedureEnv env,
-    HRegionFileSystem regionFs) throws IOException {
+  private void insertRegionFilesIntoStoreTracker(List<StoreFileInfo> allFiles,
+    MasterProcedureEnv env, HRegionFileSystem regionFs) throws IOException {
     TableDescriptor tblDesc =
       env.getMasterServices().getTableDescriptors().get(regionInfo.getTable());
     // we need to map trackers per store
     Map<String, StoreFileTracker> trackerMap = new HashMap<>();
     // we need to map store files per store
     Map<String, List<StoreFileInfo>> fileInfoMap = new HashMap<>();
-    for (Path file : allFiles) {
+    for (StoreFileInfo sfi : allFiles) {
+      Path file = sfi.getPath();
       String familyName = file.getParent().getName();
       trackerMap.computeIfAbsent(familyName, t -> 
StoreFileTrackerFactory.create(conf, tblDesc,
         tblDesc.getColumnFamily(Bytes.toBytes(familyName)), regionFs));
       fileInfoMap.computeIfAbsent(familyName, l -> new ArrayList<>());
       List<StoreFileInfo> infos = fileInfoMap.get(familyName);
-      infos.add(trackerMap.get(familyName).getStoreFileInfo(file, true));
+      infos.add(sfi);
+    }
+    for (Map.Entry<String, StoreFileTracker> entry : trackerMap.entrySet()) {
+      entry.getValue().add(fileInfoMap.get(entry.getKey()));
+    }
+  }
+
+  private void insertRegionfilePathsIntoStoreTracker(List<StoreFileInfo> 
allFiles,
+    MasterProcedureEnv env, HRegionFileSystem regionFs) throws IOException {
+    TableDescriptor tblDesc =
+      env.getMasterServices().getTableDescriptors().get(regionInfo.getTable());
+    // we need to map trackers per store
+    Map<String, StoreFileTracker> trackerMap = new HashMap<>();
+    // we need to map store files per store
+    Map<String, List<StoreFileInfo>> fileInfoMap = new HashMap<>();
+    for (StoreFileInfo file : allFiles) {
+      String familyName = file.getPath().getParent().getName();
+      trackerMap.computeIfAbsent(familyName, t -> 
StoreFileTrackerFactory.create(conf, tblDesc,
+        tblDesc.getColumnFamily(familyName.getBytes()), regionFs));
+      fileInfoMap.computeIfAbsent(familyName, l -> new ArrayList<>());
+      List<StoreFileInfo> infos = fileInfoMap.get(familyName);
+      infos.add(file);
     }
     for (Map.Entry<String, StoreFileTracker> entry : trackerMap.entrySet()) {
       entry.getValue().add(fileInfoMap.get(entry.getKey()));
@@ -568,8 +590,9 @@ public class HRegionFileSystem {
    *                    have a reference to a Region.
    * @return Path to created reference.
    */
-  public Path splitStoreFile(RegionInfo hri, String familyName, HStoreFile f, 
byte[] splitRow,
-    boolean top, RegionSplitPolicy splitPolicy, StoreFileTracker tracker) 
throws IOException {
+  public StoreFileInfo splitStoreFile(RegionInfo hri, String familyName, 
HStoreFile f,
+    byte[] splitRow, boolean top, RegionSplitPolicy splitPolicy, 
StoreFileTracker tracker)
+    throws IOException {
     Path splitDir = new Path(getSplitsDir(hri), familyName);
     // Add the referred-to regions name as a dot separated suffix.
     // See REF_NAME_REGEX regex above. The referred-to regions name is
@@ -581,7 +604,7 @@ public class HRegionFileSystem {
     Path p = new Path(splitDir, f.getPath().getName() + "." + 
parentRegionName);
     if (fs.exists(p)) {
       LOG.warn("Found an already existing split file for {}. Assuming this is 
a recovery.", p);
-      return p;
+      return tracker.getStoreFileInfo(fs.getFileStatus(p), p, true);
     }
     boolean createLinkFile = false;
     if (splitPolicy == null || 
!splitPolicy.skipStoreFileRangeCheck(familyName)) {
@@ -639,12 +662,12 @@ public class HRegionFileSystem {
           hfileName = m.group(4);
         }
         // must create back reference here
-        tracker.createHFileLink(linkedTable, linkedRegion, hfileName, true);
+        HFileLink hFileLink = tracker.createHFileLink(linkedTable, 
linkedRegion, hfileName, true);
         Path path =
           new Path(splitDir, HFileLink.createHFileLinkName(linkedTable, 
linkedRegion, hfileName));
         LOG.info("Created linkFile:" + path.toString() + " for child: " + 
hri.getEncodedName()
           + ", parent: " + regionInfoForFs.getEncodedName());
-        return path;
+        return new StoreFileInfo(conf, fs, path, hFileLink);
       } catch (IOException e) {
         // if create HFileLink file failed, then just skip the error and 
create Reference file
         LOG.error("Create link file for " + hfileName + " for child " + 
hri.getEncodedName()
@@ -655,7 +678,7 @@ public class HRegionFileSystem {
     Reference r =
       top ? Reference.createTopReference(splitRow) : 
Reference.createBottomReference(splitRow);
     tracker.createReference(r, p);
-    return p;
+    return new StoreFileInfo(conf, fs, p, r);
   }
 
   // 
===========================================================================
@@ -696,7 +719,7 @@ public class HRegionFileSystem {
    * @return Path to created reference.
    * @throws IOException if the merge write fails.
    */
-  public Path mergeStoreFile(RegionInfo mergingRegion, String familyName, 
HStoreFile f,
+  public StoreFileInfo mergeStoreFile(RegionInfo mergingRegion, String 
familyName, HStoreFile f,
     StoreFileTracker tracker) throws IOException {
     Path referenceDir = new Path(getMergesDir(regionInfoForFs), familyName);
     // A whole reference to the store file.
@@ -710,13 +733,14 @@ public class HRegionFileSystem {
     // suffix and into the new region location (under same family).
     Path p = new Path(referenceDir, f.getPath().getName() + "." + 
mergingRegionName);
     tracker.createReference(r, p);
-    return p;
+    StoreFileInfo storeFileInfo = new StoreFileInfo(conf, fs, p, r);
+    return storeFileInfo;
   }
 
   /**
    * Commit a merged region, making it ready for use.
    */
-  public void commitMergedRegion(List<Path> allMergedFiles, MasterProcedureEnv 
env)
+  public void commitMergedRegion(List<StoreFileInfo> allMergedFiles, 
MasterProcedureEnv env)
     throws IOException {
     Path regionDir = getMergesDir(regionInfoForFs);
     if (regionDir != null && fs.exists(regionDir)) {
@@ -724,7 +748,7 @@ public class HRegionFileSystem {
       Path regionInfoFile = new Path(regionDir, REGION_INFO_FILE);
       byte[] regionInfoContent = getRegionInfoFileContent(regionInfo);
       writeRegionInfoFileContent(conf, fs, regionInfoFile, regionInfoContent);
-      insertRegionFilesIntoStoreTracker(allMergedFiles, env, this);
+      insertRegionfilePathsIntoStoreTracker(allMergedFiles, env, this);
     }
   }
 
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HStore.java 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HStore.java
index fde89d122e2..918dbd850ae 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HStore.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HStore.java
@@ -74,6 +74,7 @@ import org.apache.hadoop.hbase.conf.ConfigKey;
 import org.apache.hadoop.hbase.conf.ConfigurationManager;
 import org.apache.hadoop.hbase.conf.PropagatingConfigurationObserver;
 import org.apache.hadoop.hbase.coprocessor.ReadOnlyConfiguration;
+import org.apache.hadoop.hbase.io.HFileLink;
 import org.apache.hadoop.hbase.io.HeapSize;
 import org.apache.hadoop.hbase.io.hfile.CacheConfig;
 import org.apache.hadoop.hbase.io.hfile.HFile;
@@ -1393,6 +1394,11 @@ public class HStore
     for (HStoreFile sf : this.getStorefiles()) {
       if (inputFiles.contains(sf.getPath().getName())) {
         inputStoreFiles.add(sf);
+      } else if (
+        !isPrimaryReplicaStore() && sf.getFileInfo().isLink()
+          && 
inputFiles.contains(HFileLink.getReferencedHFileName(sf.getPath().getName()))
+      ) {
+        inputStoreFiles.add(sf);
       }
     }
 
@@ -1404,7 +1410,8 @@ public class HStore
         compactionOutputs.remove(sf.getPath().getName());
       }
       for (String compactionOutput : compactionOutputs) {
-        StoreFileTracker sft = StoreFileTrackerFactory.create(conf, false, 
storeContext);
+        StoreFileTracker sft =
+          StoreFileTrackerFactory.create(conf, isPrimaryReplicaStore(), 
storeContext);
         StoreFileInfo storeFileInfo =
           getRegionFileSystem().getStoreFileInfo(getColumnFamilyName(), 
compactionOutput, sft);
         HStoreFile storeFile = 
storeEngine.createStoreFileAndReader(storeFileInfo);
@@ -2049,7 +2056,8 @@ public class HStore
       List<HStoreFile> storeFiles = new ArrayList<>(fileNames.size());
       for (String file : fileNames) {
         // open the file as a store file (hfile link, etc)
-        StoreFileTracker sft = StoreFileTrackerFactory.create(conf, false, 
storeContext);
+        StoreFileTracker sft =
+          StoreFileTrackerFactory.create(conf, isPrimaryReplicaStore(), 
storeContext);
         StoreFileInfo storeFileInfo =
           getRegionFileSystem().getStoreFileInfo(getColumnFamilyName(), file, 
sft);
         HStoreFile storeFile = 
storeEngine.createStoreFileAndReader(storeFileInfo);
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileInfo.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileInfo.java
index 1184f39da66..8fdd3f40461 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileInfo.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileInfo.java
@@ -173,6 +173,17 @@ public class StoreFileInfo implements Configurable {
     this(conf, fs, fileStatus, null, link);
   }
 
+  /**
+   * Create a Store File Info from an HFileLink
+   * @param conf       The {@link Configuration} to use
+   * @param fs         The current file system to use
+   * @param fileStatus The {@link FileStatus} of the file
+   */
+  public StoreFileInfo(final Configuration conf, final FileSystem fs, final 
Path initiaPath,
+    final HFileLink link) {
+    this(conf, fs, initiaPath, null, link);
+  }
+
   /**
    * Create a Store File Info from an HFileLink
    * @param conf       The {@link Configuration} to use
@@ -185,6 +196,18 @@ public class StoreFileInfo implements Configurable {
     this(conf, fs, fileStatus, reference, null);
   }
 
+  /**
+   * Create a Store File Info from an HFileLink
+   * @param conf       The {@link Configuration} to use
+   * @param fs         The current file system to use
+   * @param fileStatus The {@link FileStatus} of the file
+   * @param reference  The reference instance
+   */
+  public StoreFileInfo(final Configuration conf, final FileSystem fs, final 
Path initialPath,
+    final Reference reference) {
+    this(conf, fs, initialPath, reference, null);
+  }
+
   /**
    * Create a Store File Info from an HFileLink and a Reference
    * @param conf       The {@link Configuration} to use
@@ -206,6 +229,26 @@ public class StoreFileInfo implements Configurable {
       this.conf.getBoolean(STORE_FILE_READER_NO_READAHEAD, 
DEFAULT_STORE_FILE_READER_NO_READAHEAD);
   }
 
+  /**
+   * Create a Store File Info from an HFileLink and a Reference
+   * @param conf       The {@link Configuration} to use
+   * @param fs         The current file system to use
+   * @param fileStatus The {@link FileStatus} of the file
+   * @param reference  The reference instance
+   * @param link       The link instance
+   */
+  public StoreFileInfo(final Configuration conf, final FileSystem fs, final 
Path path,
+    final Reference reference, final HFileLink link) {
+    this.fs = fs;
+    this.conf = conf;
+    this.primaryReplica = false;
+    this.initialPath = path;
+    this.reference = reference;
+    this.link = link;
+    this.noReadahead =
+      this.conf.getBoolean(STORE_FILE_READER_NO_READAHEAD, 
DEFAULT_STORE_FILE_READER_NO_READAHEAD);
+  }
+
   /**
    * Create a Store File Info from an HFileLink and a Reference
    * @param conf       The {@link Configuration} to use
@@ -611,12 +654,17 @@ public class StoreFileInfo implements Configurable {
    * @return <tt>true</tt> if the file could be a valid store file, 
<tt>false</tt> otherwise
    */
   public static boolean validateStoreFileName(final String fileName) {
-    if (HFileLink.isHFileLink(fileName) || isReference(fileName)) {
+    if (HFileLink.isHFileLink(fileName) || isReference(fileName) || 
isMobFileLink(fileName)) {
       return true;
     }
     return !fileName.contains("-");
   }
 
+  public static boolean isMobFileLink(String fileName) {
+    Matcher m = HFileLink.REF_OR_HFILE_LINK_PATTERN.matcher(fileName);
+    return m.matches() && !isReference(fileName);
+  }
+
   /**
    * Return if the specified file is a valid store file or not.
    * @param fileStatus The {@link FileStatus} of the file
@@ -632,7 +680,7 @@ public class StoreFileInfo implements Configurable {
     // Check for empty hfile. Should never be the case but can happen
     // after data loss in hdfs for whatever reason (upgrade, etc.): HBASE-646
     // NOTE: that the HFileLink is just a name, so it's an empty file.
-    if (!HFileLink.isHFileLink(p) && fileStatus.getLen() <= 0) {
+    if (!HFileLink.isHFileLink(p) && fileStatus.getLen() <= 0 && 
!isMobFileLink(p.getName())) {
       LOG.warn("Skipping {} because it is empty. HBASE-646 DATA LOSS?", p);
       return false;
     }
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreUtils.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreUtils.java
index c9ee019e9af..778a419bb30 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreUtils.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreUtils.java
@@ -18,6 +18,7 @@
 package org.apache.hadoop.hbase.regionserver;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
@@ -35,6 +36,7 @@ import org.apache.hadoop.hbase.ExtendedCell;
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
 import org.apache.hadoop.hbase.client.TableDescriptor;
+import org.apache.hadoop.hbase.io.hfile.CacheConfig;
 import org.apache.hadoop.hbase.io.hfile.HFile;
 import org.apache.hadoop.hbase.util.ChecksumType;
 import org.apache.yetus.audience.InterfaceAudience;
@@ -183,6 +185,15 @@ public final class StoreUtils {
     return 
storefiles.stream().map(HStoreFile::getFileInfo).collect(Collectors.toList());
   }
 
+  public static List<HStoreFile> toHStoreFile(List<StoreFileInfo> 
storeFileInfoList,
+    BloomType bloomType, CacheConfig cacheConf) throws IOException {
+    List<HStoreFile> hStoreFiles = new ArrayList<HStoreFile>();
+    for (StoreFileInfo storeFileInfo : storeFileInfoList) {
+      hStoreFiles.add(new HStoreFile(storeFileInfo, bloomType, cacheConf));
+    }
+    return hStoreFiles;
+  }
+
   public static long getTotalUncompressedBytes(List<HStoreFile> files) {
     return files.stream()
       .mapToLong(file -> getStorefileFieldSize(file, 
StoreFileReader::getTotalUncompressedBytes))
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/FileBasedStoreFileTracker.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/FileBasedStoreFileTracker.java
index b000d837d59..fae97b9dd93 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/FileBasedStoreFileTracker.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/FileBasedStoreFileTracker.java
@@ -17,6 +17,7 @@
  */
 package org.apache.hadoop.hbase.regionserver.storefiletracker;
 
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -29,13 +30,19 @@ import java.util.stream.Collectors;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.io.HFileLink;
+import org.apache.hadoop.hbase.io.Reference;
 import org.apache.hadoop.hbase.regionserver.StoreContext;
 import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
+import org.apache.hadoop.hbase.util.HFileArchiveUtil;
 import org.apache.hadoop.hbase.util.ServerRegionReplicaUtil;
 import org.apache.yetus.audience.InterfaceAudience;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import org.apache.hbase.thirdparty.com.google.protobuf.ByteString;
+
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.StoreFileTrackerProtos.StoreFileEntry;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.StoreFileTrackerProtos.StoreFileList;
 
@@ -107,8 +114,16 @@ class FileBasedStoreFileTracker extends 
StoreFileTrackerBase {
   }
 
   private StoreFileEntry toStoreFileEntry(StoreFileInfo info) {
-    return 
StoreFileEntry.newBuilder().setName(info.getPath().getName()).setSize(info.getSize())
-      .build();
+    
org.apache.hadoop.hbase.shaded.protobuf.generated.StoreFileTrackerProtos.StoreFileEntry.Builder
 entryBuilder =
+      
StoreFileEntry.newBuilder().setName(info.getPath().getName()).setSize(info.getSize());
+    if (info.isReference()) {
+      org.apache.hadoop.hbase.shaded.protobuf.generated.FSProtos.Reference 
reference =
+        
org.apache.hadoop.hbase.shaded.protobuf.generated.FSProtos.Reference.newBuilder()
+          .setSplitkey(ByteString.copyFrom(info.getReference().getSplitKey()))
+          .setRange(info.getReference().convert().getRange()).build();
+      entryBuilder.setReference(reference);
+    }
+    return entryBuilder.build();
   }
 
   @Override
@@ -119,7 +134,8 @@ class FileBasedStoreFileTracker extends 
StoreFileTrackerBase {
         builder.addStoreFile(toStoreFileEntry(info));
       }
       for (StoreFileInfo info : newFiles) {
-        builder.addStoreFile(toStoreFileEntry(info));
+        if (!storefiles.containsKey(info.getPath().getName()))
+          builder.addStoreFile(toStoreFileEntry(info));
       }
       backedFile.update(builder);
       if (LOG.isTraceEnabled()) {
@@ -176,4 +192,68 @@ class FileBasedStoreFileTracker extends 
StoreFileTrackerBase {
       }
     }
   }
+
+  @Override
+  public Reference readReference(Path p) throws IOException {
+    String fileName = p.getName();
+    StoreFileList list = backedFile.load(true);
+    for (StoreFileEntry entry : list.getStoreFileList()) {
+      if (entry.getName().equals(fileName)) {
+        if (entry.hasReference()) {
+          return Reference.convert(entry.getReference());
+        } else {
+          LOG.debug(
+            "Fallback to reading reference from FS as it is not part of 
StoreFileEntry. This is when FSFT is reading older version of 
StoreFileListFile");
+          return super.readReference(p);
+        }
+      }
+    }
+    throw new FileNotFoundException("Reference does not exist for path : " + 
p);
+  }
+
+  @Override
+  public boolean hasReferences() throws IOException {
+    StoreFileList list = backedFile.load(true);
+    for (StoreFileEntry entry : list.getStoreFileList()) {
+      if (entry.hasReference() || HFileLink.isHFileLink(entry.getName())) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public HFileLink createHFileLink(TableName linkedTable, String linkedRegion, 
String hfileName,
+    boolean createBackRef) throws IOException {
+    FileSystem fs = ctx.getRegionFileSystem().getFileSystem();
+    HFileLink hfileLink = HFileLink.build(conf, linkedTable, linkedRegion,
+      ctx.getFamily().getNameAsString(), hfileName);
+    Path backRefPath = null;
+    if (createBackRef) {
+      Path archiveStoreDir = HFileArchiveUtil.getStoreArchivePath(conf, 
linkedTable, linkedRegion,
+        ctx.getFamily().getNameAsString());
+      Path backRefssDir = HFileLink.getBackReferencesDir(archiveStoreDir, 
hfileName);
+      fs.mkdirs(backRefssDir);
+      // Create the reference for the link
+      String refName = 
HFileLink.createBackReferenceName(ctx.getTableName().toString(),
+        ctx.getRegionInfo().getEncodedName());
+      backRefPath = new Path(backRefssDir, refName);
+      fs.createNewFile(backRefPath);
+    }
+    return hfileLink;
+  }
+
+  @Override
+  public Reference createReference(Reference reference, Path path) throws 
IOException {
+    return reference;
+  }
+
+  @Override
+  public Reference createAndCommitReference(Reference reference, Path path) 
throws IOException {
+    StoreFileInfo storeFileInfo =
+      new StoreFileInfo(ctx.getRegionFileSystem().getFileSystem().getConf(),
+        ctx.getRegionFileSystem().getFileSystem(), path, reference);
+    add(Collections.singleton(storeFileInfo));
+    return reference;
+  }
 }
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileListFile.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileListFile.java
index e107688b3d7..fffd4ab499e 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileListFile.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileListFile.java
@@ -74,7 +74,7 @@ class StoreFileListFile {
   private static final Logger LOG = 
LoggerFactory.getLogger(StoreFileListFile.class);
 
   // the current version for StoreFileList
-  static final long VERSION = 1;
+  static final long VERSION = 2;
 
   static final String TRACK_FILE_DIR = ".filelist";
 
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTracker.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTracker.java
index 7e6980e854e..c56a40997c1 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTracker.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTracker.java
@@ -24,6 +24,7 @@ import org.apache.hadoop.fs.FileStatus;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
+import org.apache.hadoop.hbase.io.HFileLink;
 import org.apache.hadoop.hbase.io.Reference;
 import org.apache.hadoop.hbase.regionserver.CreateStoreFileWriterParams;
 import org.apache.hadoop.hbase.regionserver.HStoreFile;
@@ -103,6 +104,8 @@ public interface StoreFileTracker {
 
   Reference createReference(Reference reference, Path path) throws IOException;
 
+  Reference createAndCommitReference(Reference reference, Path path) throws 
IOException;
+
   /**
    * Reads the reference file from the given path.
    * @param path the {@link Path} to the reference file in the file system.
@@ -132,7 +135,20 @@ public interface StoreFileTracker {
    * @return the file link name.
    * @throws IOException on file or parent directory creation failure.
    */
-  String createHFileLink(final TableName linkedTable, final String 
linkedRegion,
+  HFileLink createHFileLink(final TableName linkedTable, final String 
linkedRegion,
+    final String hfileName, final boolean createBackRef) throws IOException;
+
+  /**
+   * Create a new HFileLink and add to SFT
+   * <p>
+   * It also adds a back-reference to the hfile back-reference directory to 
simplify the
+   * reference-count and the cleaning process.
+   * @param hfileLinkName - HFileLink name (it contains hfile-region-table)
+   * @param createBackRef - Whether back reference should be created. Defaults 
to true.
+   * @return the file link name.
+   * @throws IOException on file or parent directory creation failure.
+   */
+  HFileLink createAndCommitHFileLink(final TableName linkedTable, final String 
linkedRegion,
     final String hfileName, final boolean createBackRef) throws IOException;
 
   /**
@@ -145,7 +161,7 @@ public interface StoreFileTracker {
    * @return the file link name.
    * @throws IOException on file or parent directory creation failure.
    */
-  String createFromHFileLink(final String hfileName, final boolean 
createBackRef)
+  HFileLink createFromHFileLink(final String hfileName, final boolean 
createBackRef)
     throws IOException;
 
   /**
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTrackerBase.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTrackerBase.java
index 87eca7b93c9..843ec651fb9 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTrackerBase.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTrackerBase.java
@@ -23,6 +23,7 @@ import java.io.BufferedInputStream;
 import java.io.DataInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.regex.Matcher;
@@ -219,6 +220,11 @@ abstract class StoreFileTrackerBase implements 
StoreFileTracker {
     return reference;
   }
 
+  @Override
+  public Reference createAndCommitReference(Reference reference, Path path) 
throws IOException {
+    return createReference(reference, path);
+  }
+
   /**
    * Returns true if the specified family has reference files
    * @param familyName Column Family Name
@@ -324,7 +330,18 @@ abstract class StoreFileTrackerBase implements 
StoreFileTracker {
       isPrimaryReplica);
   }
 
-  public String createHFileLink(final TableName linkedTable, final String 
linkedRegion,
+  public HFileLink createAndCommitHFileLink(final TableName linkedTable, final 
String linkedRegion,
+    final String hfileName, final boolean createBackRef) throws IOException {
+    HFileLink hFileLink = createHFileLink(linkedTable, linkedRegion, 
hfileName, createBackRef);
+    Path path = new Path(ctx.getFamilyStoreDirectoryPath(),
+      HFileLink.createHFileLinkName(linkedTable, linkedRegion, hfileName));
+    StoreFileInfo storeFileInfo =
+      new StoreFileInfo(conf, this.ctx.getRegionFileSystem().getFileSystem(), 
path, hFileLink);
+    add(Arrays.asList(storeFileInfo));
+    return hFileLink;
+  }
+
+  public HFileLink createHFileLink(final TableName linkedTable, final String 
linkedRegion,
     final String hfileName, final boolean createBackRef) throws IOException {
     String name = HFileLink.createHFileLinkName(linkedTable, linkedRegion, 
hfileName);
     String refName = 
HFileLink.createBackReferenceName(ctx.getTableName().toString(),
@@ -349,7 +366,8 @@ abstract class StoreFileTrackerBase implements 
StoreFileTracker {
     try {
       // Create the link
       if (fs.createNewFile(new Path(ctx.getFamilyStoreDirectoryPath(), name))) 
{
-        return name;
+        return new HFileLink(new Path(ctx.getFamilyStoreDirectoryPath(), 
name), backRefPath, null,
+          archiveStoreDir);
       }
     } catch (IOException e) {
       LOG.error("couldn't create the link=" + name + " for " + 
ctx.getFamilyStoreDirectoryPath(),
@@ -365,14 +383,22 @@ abstract class StoreFileTrackerBase implements 
StoreFileTracker {
 
   }
 
-  public String createFromHFileLink(final String hfileLinkName, final boolean 
createBackRef)
+  public HFileLink createFromHFileLink(final String hfileLinkName, final 
boolean createBackRef)
     throws IOException {
-    Matcher m = HFileLink.LINK_NAME_PATTERN.matcher(hfileLinkName);
-    if (!m.matches()) {
-      throw new IllegalArgumentException(hfileLinkName + " is not a valid 
HFileLink name!");
+    Matcher hfileLinkMatcher = 
HFileLink.LINK_NAME_PATTERN.matcher(hfileLinkName);
+    if (hfileLinkMatcher.matches()) {
+      return createHFileLink(
+        TableName.valueOf(hfileLinkMatcher.group(1), 
hfileLinkMatcher.group(2)),
+        hfileLinkMatcher.group(3), hfileLinkMatcher.group(4), createBackRef);
+    }
+    if (StoreFileInfo.isMobFileLink(hfileLinkName)) {
+      Matcher mobLinkMatcher = 
HFileLink.REF_OR_HFILE_LINK_PATTERN.matcher(hfileLinkName);
+      if (mobLinkMatcher.matches()) {
+        return createHFileLink(TableName.valueOf(mobLinkMatcher.group(1), 
mobLinkMatcher.group(2)),
+          mobLinkMatcher.group(3), mobLinkMatcher.group(4), createBackRef);
+      }
     }
-    return createHFileLink(TableName.valueOf(m.group(1), m.group(2)), 
m.group(3), m.group(4),
-      createBackRef);
+    throw new IllegalArgumentException(hfileLinkName + " is not a valid 
HFileLink name!");
   }
 
   @Override
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTrackerFactory.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTrackerFactory.java
index 828f1974fca..a2410238bb3 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTrackerFactory.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTrackerFactory.java
@@ -24,6 +24,7 @@ import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
 import org.apache.hadoop.hbase.client.TableDescriptor;
 import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
+import org.apache.hadoop.hbase.mob.MobUtils;
 import org.apache.hadoop.hbase.procedure2.util.StringUtils;
 import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
 import org.apache.hadoop.hbase.regionserver.StoreContext;
@@ -118,7 +119,15 @@ public final class StoreFileTrackerFactory {
 
   public static StoreFileTracker create(Configuration conf, boolean 
isPrimaryReplica,
     StoreContext ctx) {
-    Class<? extends StoreFileTracker> tracker = getTrackerClass(conf);
+    Class<? extends StoreFileTracker> tracker;
+    if (
+      ctx != null && ctx.getRegionInfo().getEncodedName()
+        .equals(MobUtils.getMobRegionInfo(ctx.getTableName()).getEncodedName())
+    ) {
+      tracker = Trackers.DEFAULT.clazz;
+    } else {
+      tracker = getTrackerClass(conf);
+    }
     LOG.debug("instantiating StoreFileTracker impl {}", tracker.getName());
     return ReflectionUtils.newInstance(tracker, conf, isPrimaryReplica, ctx);
   }
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/RestoreSnapshotHelper.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/RestoreSnapshotHelper.java
index 3f01432472d..f0f1ba3899a 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/RestoreSnapshotHelper.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/RestoreSnapshotHelper.java
@@ -32,6 +32,7 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.ThreadPoolExecutor;
+import java.util.stream.Collectors;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileStatus;
 import org.apache.hadoop.fs.FileSystem;
@@ -39,6 +40,7 @@ import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hbase.MetaTableAccessor;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.backup.HFileArchiver;
+import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
 import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
 import org.apache.hadoop.hbase.client.Connection;
 import org.apache.hadoop.hbase.client.ConnectionFactory;
@@ -465,7 +467,8 @@ public class RestoreSnapshotHelper {
    */
   private void restoreRegion(final RegionInfo regionInfo,
     final SnapshotRegionManifest regionManifest) throws IOException {
-    restoreRegion(regionInfo, regionManifest, new Path(tableDir, 
regionInfo.getEncodedName()));
+    restoreRegion(regionInfo, regionManifest, new Path(tableDir, 
regionInfo.getEncodedName()),
+      tableDir);
   }
 
   /**
@@ -478,7 +481,8 @@ public class RestoreSnapshotHelper {
       return;
     }
     restoreRegion(regionInfo, regionManifest,
-      MobUtils.getMobRegionPath(conf, tableDesc.getTableName()));
+      MobUtils.getMobRegionPath(conf, tableDesc.getTableName()),
+      MobUtils.getMobTableDir(conf, tableDesc.getTableName()));
   }
 
   /**
@@ -486,26 +490,27 @@ public class RestoreSnapshotHelper {
    * snapshot.
    */
   private void restoreRegion(final RegionInfo regionInfo,
-    final SnapshotRegionManifest regionManifest, Path regionDir) throws 
IOException {
+    final SnapshotRegionManifest regionManifest, Path regionDir, Path 
tableDir) throws IOException {
     Map<String, List<SnapshotRegionManifest.StoreFile>> snapshotFiles =
       getRegionHFileReferences(regionManifest);
 
     String tableName = tableDesc.getTableName().getNameAsString();
     final String snapshotName = snapshotDesc.getName();
 
-    Path regionPath = new Path(tableDir, regionInfo.getEncodedName());
-    HRegionFileSystem regionFS = (fs.exists(regionPath))
+    HRegionFileSystem regionFS = (fs.exists(regionDir))
       ? HRegionFileSystem.openRegionFromFileSystem(conf, fs, tableDir, 
regionInfo, false)
       : HRegionFileSystem.createRegionOnFileSystem(conf, fs, tableDir, 
regionInfo);
 
     // Restore families present in the table
     for (Path familyDir : FSUtils.getFamilyDirs(fs, regionDir)) {
       byte[] family = Bytes.toBytes(familyDir.getName());
-
+      ColumnFamilyDescriptor familyDescriptor = 
ColumnFamilyDescriptorBuilder.of(family);
       StoreFileTracker tracker = StoreFileTrackerFactory.create(conf, true,
-        
StoreContext.getBuilder().withColumnFamilyDescriptor(tableDesc.getColumnFamily(family))
+        StoreContext.getBuilder().withColumnFamilyDescriptor(familyDescriptor)
           
.withFamilyStoreDirectoryPath(familyDir).withRegionFileSystem(regionFS).build());
-      Set<String> familyFiles = getTableRegionFamilyFiles(familyDir);
+      List<StoreFileInfo> storeFileInfos = tracker.load();
+      List<String> familyFiles = storeFileInfos.stream()
+        .map(storeFileInfo -> 
storeFileInfo.getPath().getName()).collect(Collectors.toList());
       List<SnapshotRegionManifest.StoreFile> snapshotFamilyFiles =
         snapshotFiles.remove(familyDir.getName());
       List<StoreFileInfo> filesToTrack = new ArrayList<>();
@@ -526,11 +531,13 @@ public class RestoreSnapshotHelper {
 
         // Remove hfiles not present in the snapshot
         for (String hfileName : familyFiles) {
-          Path hfile = new Path(familyDir, hfileName);
-          if (!fs.getFileStatus(hfile).isDirectory()) {
-            LOG.trace("Removing HFile=" + hfileName + " not present in 
snapshot=" + snapshotName
-              + " from region=" + regionInfo.getEncodedName() + " table=" + 
tableName);
-            HFileArchiver.archiveStoreFile(conf, fs, regionInfo, tableDir, 
family, hfile);
+          for (StoreFileInfo storeFileInfo : storeFileInfos) {
+            if (hfileName.equals(storeFileInfo.getPath().getName())) {
+              tracker.removeStoreFiles(
+                
StoreUtils.toHStoreFile(Collections.singletonList(storeFileInfo), null, null));
+              LOG.trace("Removing HFile=" + hfileName + " not present in 
snapshot=" + snapshotName
+                + " from region=" + regionInfo.getEncodedName() + " table=" + 
tableName);
+            }
           }
         }
 
@@ -538,15 +545,17 @@ public class RestoreSnapshotHelper {
         for (SnapshotRegionManifest.StoreFile storeFile : hfilesToAdd) {
           LOG.debug("Restoring missing HFileLink " + storeFile.getName() + " 
of snapshot="
             + snapshotName + " to region=" + regionInfo.getEncodedName() + " 
table=" + tableName);
-          String fileName =
+          StoreFileInfo storeFileInfo =
             restoreStoreFile(familyDir, regionInfo, storeFile, createBackRefs, 
tracker);
           // mark the reference file to be added to tracker
-          filesToTrack.add(tracker.getStoreFileInfo(new Path(familyDir, 
fileName), true));
+          filesToTrack.add(storeFileInfo);
         }
       } else {
         // Family doesn't exists in the snapshot
         LOG.trace("Removing family=" + Bytes.toString(family) + " in 
snapshot=" + snapshotName
           + " from region=" + regionInfo.getEncodedName() + " table=" + 
tableName);
+        LOG.debug("Removing family=" + Bytes.toString(family) + " in 
snapshot=" + snapshotName
+          + " from region=" + regionInfo.getEncodedName() + " table=" + 
tableName);
         HFileArchiver.archiveFamilyByFamilyDir(fs, conf, regionInfo, 
familyDir, family);
         fs.delete(familyDir, true);
       }
@@ -571,29 +580,14 @@ public class RestoreSnapshotHelper {
       for (SnapshotRegionManifest.StoreFile storeFile : 
familyEntry.getValue()) {
         LOG.trace("Adding HFileLink (Not present in the table) " + 
storeFile.getName()
           + " of snapshot " + snapshotName + " to table=" + tableName);
-        String fileName =
+        StoreFileInfo storeFileInfo =
           restoreStoreFile(familyDir, regionInfo, storeFile, createBackRefs, 
tracker);
-        files.add(tracker.getStoreFileInfo(new Path(familyDir, fileName), 
true));
+        files.add(storeFileInfo);
       }
       tracker.set(files);
     }
   }
 
-  private Set<String> getTableRegionFamilyFiles(final Path familyDir) throws 
IOException {
-    FileStatus[] hfiles = CommonFSUtils.listStatus(fs, familyDir);
-    if (hfiles == null) {
-      return Collections.emptySet();
-    }
-
-    Set<String> familyFiles = new HashSet<>(hfiles.length);
-    for (int i = 0; i < hfiles.length; ++i) {
-      String hfileName = hfiles[i].getPath().getName();
-      familyFiles.add(hfileName);
-    }
-
-    return familyFiles;
-  }
-
   /**
    * Clone specified regions. For each region create a new region and create a 
HFileLink for each
    * hfile.
@@ -661,8 +655,7 @@ public class RestoreSnapshotHelper {
     for (SnapshotRegionManifest.FamilyFiles familyFiles : 
manifest.getFamilyFilesList()) {
       Path familyDir = new Path(regionDir, 
familyFiles.getFamilyName().toStringUtf8());
       List<StoreFileInfo> clonedFiles = new ArrayList<>();
-      Path regionPath = new Path(tableDir, newRegionInfo.getEncodedName());
-      HRegionFileSystem regionFS = (fs.exists(regionPath))
+      HRegionFileSystem regionFS = (fs.exists(regionDir))
         ? HRegionFileSystem.openRegionFromFileSystem(conf, fs, tableDir, 
newRegionInfo, false)
         : HRegionFileSystem.createRegionOnFileSystem(conf, fs, tableDir, 
newRegionInfo);
 
@@ -671,11 +664,14 @@ public class RestoreSnapshotHelper {
       StoreFileTracker tracker =
         StoreFileTrackerFactory
           .create(sftConf, true,
-            StoreContext.getBuilder().withFamilyStoreDirectoryPath(familyDir)
+            StoreContext.getBuilder()
+              .withFamilyStoreDirectoryPath(
+                new Path(regionDir, 
familyFiles.getFamilyName().toStringUtf8()))
               .withRegionFileSystem(regionFS)
               .withColumnFamilyDescriptor(
                 
ColumnFamilyDescriptorBuilder.of(familyFiles.getFamilyName().toByteArray()))
               .build());
+      tracker.load();
       for (SnapshotRegionManifest.StoreFile storeFile : 
familyFiles.getStoreFilesList()) {
         LOG.info("Adding HFileLink " + storeFile.getName() + " from cloned 
region " + "in snapshot "
           + snapshotName + " to table=" + tableName);
@@ -686,17 +682,16 @@ public class RestoreSnapshotHelper {
           if (fs.exists(mobPath)) {
             fs.delete(mobPath, true);
           }
-          restoreStoreFile(familyDir, snapshotRegionInfo, storeFile, 
createBackRefs, tracker);
+          StoreFileInfo storeFileInfo =
+            restoreStoreFile(familyDir, snapshotRegionInfo, storeFile, 
createBackRefs, tracker);
+          clonedFiles.add(storeFileInfo);
         } else {
-          String file =
+          StoreFileInfo storeFileInfo =
             restoreStoreFile(familyDir, snapshotRegionInfo, storeFile, 
createBackRefs, tracker);
-          clonedFiles.add(tracker.getStoreFileInfo(new Path(familyDir, file), 
true));
+          clonedFiles.add(storeFileInfo);
         }
       }
-      // we don't need to track files under mobdir
-      if (!MobUtils.isMobRegionInfo(newRegionInfo)) {
-        tracker.set(clonedFiles);
-      }
+      tracker.add(clonedFiles);
     }
 
   }
@@ -727,17 +722,23 @@ public class RestoreSnapshotHelper {
    * @param createBackRef - Whether back reference should be created. Defaults 
to true.
    * @param storeFile     store file name (can be a Reference, HFileLink or 
simple HFile)
    */
-  private String restoreStoreFile(final Path familyDir, final RegionInfo 
regionInfo,
+  private StoreFileInfo restoreStoreFile(final Path familyDir, final 
RegionInfo regionInfo,
     final SnapshotRegionManifest.StoreFile storeFile, final boolean 
createBackRef,
     final StoreFileTracker tracker) throws IOException {
     String hfileName = storeFile.getName();
-    if (HFileLink.isHFileLink(hfileName)) {
-      return tracker.createFromHFileLink(hfileName, createBackRef);
+    StoreFileInfo info = null;
+    if (HFileLink.isHFileLink(hfileName) || 
StoreFileInfo.isMobFileLink(hfileName)) {
+      HFileLink hfileLink = tracker.createFromHFileLink(hfileName, 
createBackRef);
+      info = new StoreFileInfo(conf, fs, new Path(familyDir, hfileName), 
hfileLink);
+      return info;
     } else if (StoreFileInfo.isReference(hfileName)) {
       return restoreReferenceFile(familyDir, regionInfo, storeFile, tracker);
     } else {
-      return tracker.createHFileLink(regionInfo.getTable(), 
regionInfo.getEncodedName(), hfileName,
-        createBackRef);
+      HFileLink hfileLink = 
tracker.createAndCommitHFileLink(regionInfo.getTable(),
+        regionInfo.getEncodedName(), hfileName, createBackRef);
+      return new StoreFileInfo(conf, fs, new Path(familyDir, HFileLink
+        .createHFileLinkName(regionInfo.getTable(), 
regionInfo.getEncodedName(), hfileName)),
+        hfileLink);
     }
   }
 
@@ -764,10 +765,11 @@ public class RestoreSnapshotHelper {
    * @param regionInfo destination region info for the table
    * @param storeFile  reference file name
    */
-  private String restoreReferenceFile(final Path familyDir, final RegionInfo 
regionInfo,
+  private StoreFileInfo restoreReferenceFile(final Path familyDir, final 
RegionInfo regionInfo,
     final SnapshotRegionManifest.StoreFile storeFile, final StoreFileTracker 
tracker)
     throws IOException {
     String hfileName = storeFile.getName();
+    StoreFileInfo storeFileInfo = null;
 
     // Extract the referred information (hfile name and parent region)
     Path refPath =
@@ -800,11 +802,15 @@ public class RestoreSnapshotHelper {
     // Create the new reference
     if (storeFile.hasReference()) {
       Reference reference = Reference.convert(storeFile.getReference());
-      tracker.createReference(reference, outPath);
+      tracker.createAndCommitReference(reference, outPath);
+      storeFileInfo = new StoreFileInfo(conf, fs, outPath, reference);
     } else {
       InputStream in;
       if (linkPath != null) {
-        in = HFileLink.buildFromHFileLinkPattern(conf, linkPath).open(fs);
+        HFileLink hfileLink = HFileLink.buildFromHFileLinkPattern(conf, 
linkPath);
+        storeFileInfo = new StoreFileInfo(conf, fs, outPath, hfileLink);
+        tracker.add(Collections.singletonList(storeFileInfo));
+        in = hfileLink.open(fs);
       } else {
         linkPath = new Path(new Path(
           HRegion.getRegionDir(snapshotManifest.getSnapshotDir(), 
regionInfo.getEncodedName()),
@@ -832,7 +838,7 @@ public class RestoreSnapshotHelper {
         daughters.setSecond(regionName);
       }
     }
-    return outPath.getName();
+    return storeFileInfo;
   }
 
   /**
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/CloneSnapshotFromClientAfterSplittingRegionTestBase.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/CloneSnapshotFromClientAfterSplittingRegionTestBase.java
index 6d74233dd77..5027da6762f 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/CloneSnapshotFromClientAfterSplittingRegionTestBase.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/CloneSnapshotFromClientAfterSplittingRegionTestBase.java
@@ -68,7 +68,6 @@ public class 
CloneSnapshotFromClientAfterSplittingRegionTestBase
 
       // Take a snapshot
       admin.snapshot(snapshotName2, tableName);
-
       // Clone the snapshot to another table
       admin.cloneSnapshot(snapshotName2, clonedTableName);
       SnapshotTestingUtils.waitForTableToBeOnline(TEST_UTIL, clonedTableName);
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestPrefetch.java 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestPrefetch.java
index 73b0f527981..37b3de11bd4 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestPrefetch.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestPrefetch.java
@@ -429,7 +429,7 @@ public class TestPrefetch {
         .withRegionFileSystem(regionFS).build());
     HStoreFile file = new HStoreFile(fs, storeFile, conf, cacheConf, 
BloomType.NONE, true, sft);
     Path ref = regionFS.splitStoreFile(region, "cf", file, 
fileWithSplitPoint.getSecond(), false,
-      new ConstantSizeRegionSplitPolicy(), sft);
+      new ConstantSizeRegionSplitPolicy(), sft).getPath();
     conf.setBoolean(HBASE_REGION_SERVER_ENABLE_COMPACTION, compactionEnabled);
     HStoreFile refHsf = new HStoreFile(this.fs, ref, conf, cacheConf, 
BloomType.NONE, true, sft);
     refHsf.initReader();
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/bucket/TestPrefetchWithBucketCache.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/bucket/TestPrefetchWithBucketCache.java
index 8e341979a59..0513808938e 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/bucket/TestPrefetchWithBucketCache.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/bucket/TestPrefetchWithBucketCache.java
@@ -200,7 +200,7 @@ public class TestPrefetchWithBucketCache {
     byte[] splitPoint = RandomKeyValueUtil.randomOrderedKey(rand, 50);
     HStoreFile file = new HStoreFile(fs, storeFile, conf, cacheConf, 
BloomType.NONE, true, sft);
     Path ref = regionFS.splitStoreFile(region, "cf", file, splitPoint, false,
-      new ConstantSizeRegionSplitPolicy(), sft);
+      new ConstantSizeRegionSplitPolicy(), sft).getPath();
     HStoreFile refHsf = new HStoreFile(this.fs, ref, conf, cacheConf, 
BloomType.NONE, true, sft);
     // starts reader for the ref. The ref should resolve to the original file 
blocks
     // and not duplicate blocks in the cache.
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/cleaner/TestHFileLinkCleaner.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/cleaner/TestHFileLinkCleaner.java
index 9c6c6f95f86..ffd12ba3423 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/cleaner/TestHFileLinkCleaner.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/cleaner/TestHFileLinkCleaner.java
@@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
+import java.util.Collections;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileStatus;
 import org.apache.hadoop.fs.FileSystem;
@@ -33,8 +34,11 @@ import 
org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
 import org.apache.hadoop.hbase.client.RegionInfo;
 import org.apache.hadoop.hbase.client.RegionInfoBuilder;
 import org.apache.hadoop.hbase.io.HFileLink;
+import org.apache.hadoop.hbase.regionserver.BloomType;
 import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
+import org.apache.hadoop.hbase.regionserver.HStoreFile;
 import org.apache.hadoop.hbase.regionserver.StoreContext;
+import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
 import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker;
 import 
org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
 import org.apache.hadoop.hbase.testclassification.MasterTests;
@@ -77,6 +81,8 @@ public class TestHFileLinkCleaner {
   private Path linkBackRef;
   private FileStatus[] backRefs;
   private HFileCleaner cleaner;
+  private StoreFileTracker sft;
+  private HFileLink hfileLink;
   private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
   private static DirScanPool POOL;
   private static final long TTL = 1000;
@@ -119,7 +125,7 @@ public class TestHFileLinkCleaner {
 
     HRegionFileSystem regionFS = HRegionFileSystem.create(conf, fs,
       CommonFSUtils.getTableDir(rootDir, tableLinkName), hriLink);
-    StoreFileTracker sft = StoreFileTrackerFactory.create(conf, true,
+    sft = StoreFileTrackerFactory.create(conf, true,
       StoreContext.getBuilder()
         .withFamilyStoreDirectoryPath(new Path(regionFS.getRegionDir(), 
familyName))
         
.withColumnFamilyDescriptor(ColumnFamilyDescriptorBuilder.of(familyName))
@@ -136,8 +142,9 @@ public class TestHFileLinkCleaner {
     // Create link to hfile
     familyLinkPath = getFamilyDirPath(rootDir, tableLinkName, 
hriLink.getEncodedName(), familyName);
     fs.mkdirs(familyLinkPath);
-    hfileLinkName =
+    hfileLink =
       sft.createHFileLink(hri.getTable(), hri.getEncodedName(), hfileName, 
createBackReference);
+    hfileLinkName = hfileName;
     linkBackRefDir = HFileLink.getBackReferencesDir(archiveStoreDir, 
hfileName);
     assertTrue(fs.exists(linkBackRefDir));
     backRefs = fs.listStatus(linkBackRefDir);
@@ -185,7 +192,12 @@ public class TestHFileLinkCleaner {
     assertTrue(fs.exists(hfilePath));
 
     // simulate after removing the reference in data directory, the Link 
backref can be removed
-    fs.delete(new Path(familyLinkPath, hfileLinkName), false);
+    Path linkPath = new Path(familyLinkPath,
+      HFileLink.createHFileLinkName(hri.getTable(), hri.getEncodedName(), 
hfileName));
+    HStoreFile storeFile =
+      new HStoreFile(new StoreFileInfo(conf, fs, linkPath, hfileLink), 
BloomType.NONE, null);
+    sft.removeStoreFiles(Collections.singletonList(storeFile));
+
     cleaner.chore();
     assertFalse(fs.exists(linkBackRef), "Link should be deleted");
   }
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/mob/TestMobCompactionWithDefaults.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/mob/TestMobCompactionWithDefaults.java
index 787dc5b46dc..44eaca9928f 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/mob/TestMobCompactionWithDefaults.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/mob/TestMobCompactionWithDefaults.java
@@ -43,9 +43,13 @@ import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.client.ResultScanner;
 import org.apache.hadoop.hbase.client.Table;
 import org.apache.hadoop.hbase.client.TableDescriptor;
+import org.apache.hadoop.hbase.regionserver.HRegion;
+import org.apache.hadoop.hbase.regionserver.HStore;
+import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker;
 import 
org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
 import org.apache.hadoop.hbase.testclassification.LargeTests;
 import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.CommonFSUtils;
 import org.apache.hadoop.hbase.util.RegionSplitter;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -169,7 +173,13 @@ public class TestMobCompactionWithDefaults {
     LOG.info("MOB compaction " + description() + " started");
     loadAndFlushThreeTimes(rows, table, famStr);
     mobCompact(tableDescriptor, familyDescriptor);
-    assertEquals(numRegions * 4, getNumberOfMobFiles(table, famStr),
+    long filesAfterCompaction =
+      getNumberOfFilesInMobFamilyDir(tableDescriptor, familyDescriptor, 
famStr);
+    LOG.info("Files after compaction: {}", filesAfterCompaction);
+
+    assertEquals(numRegions * 4,
+      getNumberOfFilesInMobFamilyDir(tableDescriptor,
+        tableDescriptor.getColumnFamily(famStr.getBytes()), famStr),
       "Should have 4 MOB files per region due to 3xflush + compaction.");
     cleanupAndVerifyCounts(table, famStr, 3 * rows);
     LOG.info("MOB compaction " + description() + " finished OK");
@@ -183,15 +193,32 @@ public class TestMobCompactionWithDefaults {
     LOG.debug("Taking snapshot and cloning table {}", table);
     admin.snapshot(TestMobUtils.getTableName(testMethodName), table);
     admin.cloneSnapshot(TestMobUtils.getTableName(testMethodName), clone);
-    assertEquals(3 * numRegions, getNumberOfMobFiles(clone, famStr),
+    assertEquals(3 * numRegions,
+      getNumberOfStoreFiles(admin.getDescriptor(clone),
+        admin.getDescriptor(clone).getColumnFamily(famStr.getBytes()), famStr),
       "Should have 3 hlinks per region in MOB area from snapshot clone");
-    mobCompact(admin.getDescriptor(clone), familyDescriptor);
-    assertEquals(4 * numRegions, getNumberOfMobFiles(clone, famStr),
+    mobCompact(admin.getDescriptor(clone),
+      admin.getDescriptor(clone).getColumnFamily(famStr.getBytes()));
+    assertEquals(4 * numRegions,
+      getNumberOfFilesInMobFamilyDir(admin.getDescriptor(clone),
+        admin.getDescriptor(clone).getColumnFamily(famStr.getBytes()), famStr),
       "Should have 3 hlinks + 1 MOB file per region due to clone + compact");
     cleanupAndVerifyCounts(clone, famStr, 3 * rows);
     LOG.info("MOB compaction of cloned snapshot, " + description() + " 
finished OK");
   }
 
+  protected long getNumberOfFilesInMobFamilyDir(TableDescriptor descriptor,
+    ColumnFamilyDescriptor familyDesc, String family) throws IOException {
+    FileSystem fs = FileSystem.get(conf);
+    Path dir = MobUtils.getMobFamilyPath(conf, descriptor.getTableName(), 
family);
+    FileStatus[] stat = fs.listStatus(dir);
+    for (FileStatus st : stat) {
+      LOG.debug("MOB Directory content: {}", st.getPath());
+    }
+    LOG.debug("MOB Directory content total files: {}", stat.length);
+    return stat.length;
+  }
+
   @TestTemplate
   public void testMobFileCompactionAfterSnapshotCloneAndFlush()
     throws InterruptedException, IOException {
@@ -201,11 +228,17 @@ public class TestMobCompactionWithDefaults {
     LOG.debug("Taking snapshot and cloning table {}", table);
     admin.snapshot(TestMobUtils.getTableName(testMethodName), table);
     admin.cloneSnapshot(TestMobUtils.getTableName(testMethodName), clone);
-    assertEquals(3 * numRegions, getNumberOfMobFiles(clone, famStr),
+    assertEquals(3 * numRegions,
+      getNumberOfStoreFiles(admin.getDescriptor(clone),
+        admin.getDescriptor(clone).getColumnFamily(famStr.getBytes()), famStr),
       "Should have 3 hlinks per region in MOB area from snapshot clone");
     loadAndFlushThreeTimes(rows, clone, famStr);
-    mobCompact(admin.getDescriptor(clone), familyDescriptor);
-    assertEquals(7 * numRegions, getNumberOfMobFiles(clone, famStr),
+    mobCompact(admin.getDescriptor(clone),
+      admin.getDescriptor(clone).getColumnFamily(famStr.getBytes()));
+    CommonFSUtils.logFileSystemState(FileSystem.get(conf), 
HTU.getDefaultRootDirPath(), LOG);
+    assertEquals(7 * numRegions,
+      getNumberOfFilesInMobFamilyDir(admin.getDescriptor(clone),
+        admin.getDescriptor(clone).getColumnFamily(famStr.getBytes()), famStr),
       "Should have 7 MOB file per region due to clone + 3xflush + compact");
     cleanupAndVerifyCounts(clone, famStr, 6 * rows);
     LOG.info("MOB compaction of cloned snapshot w flush, " + description() + " 
finished OK");
@@ -213,12 +246,15 @@ public class TestMobCompactionWithDefaults {
 
   protected void loadAndFlushThreeTimes(int rows, TableName table, String 
family)
     throws IOException {
-    final long start = getNumberOfMobFiles(table, family);
+    final long start = 
getNumberOfFilesInMobFamilyDir(admin.getDescriptor(table),
+      admin.getDescriptor(table).getColumnFamily(family.getBytes()), family);
     // Load and flush data 3 times
     loadData(table, rows);
     loadData(table, rows);
     loadData(table, rows);
-    assertEquals(start + numRegions * 3, getNumberOfMobFiles(table, family),
+    assertEquals(start + numRegions * 3,
+      getNumberOfFilesInMobFamilyDir(admin.getDescriptor(table),
+        admin.getDescriptor(table).getColumnFamily(family.getBytes()), family),
       "Should have 3 more mob files per region from flushing.");
   }
 
@@ -285,7 +321,9 @@ public class TestMobCompactionWithDefaults {
       
HTU.getMiniHBaseCluster().getRegionServer(sn).getRSMobFileCleanerChore().chore();
     }
 
-    assertEquals(numRegions, getNumberOfMobFiles(table, family),
+    assertEquals(numRegions,
+      getNumberOfFilesInMobFamilyDir(admin.getDescriptor(table),
+        admin.getDescriptor(table).getColumnFamily(family.getBytes()), family),
       "After cleaning, we should have 1 MOB file per region based on size.");
 
     LOG.debug("checking count of rows");
@@ -294,18 +332,6 @@ public class TestMobCompactionWithDefaults {
 
   }
 
-  protected long getNumberOfMobFiles(TableName tableName, String family) 
throws IOException {
-    FileSystem fs = FileSystem.get(conf);
-    Path dir = MobUtils.getMobFamilyPath(conf, tableName, family);
-    FileStatus[] stat = fs.listStatus(dir);
-    for (FileStatus st : stat) {
-      LOG.debug("MOB Directory content: {}", st.getPath());
-    }
-    LOG.debug("MOB Directory content total files: {}", stat.length);
-
-    return stat.length;
-  }
-
   protected long scanTable(TableName tableName) {
     try (final Table table = HTU.getConnection().getTable(tableName);
       final ResultScanner scanner = table.getScanner(fam)) {
@@ -326,4 +352,17 @@ public class TestMobCompactionWithDefaults {
     }
     return 0;
   }
+
+  protected long getNumberOfStoreFiles(TableDescriptor descriptor,
+    ColumnFamilyDescriptor familyDesc, String family) throws IOException {
+    List<HRegion> regions = 
HTU.getHBaseCluster().getRegions(descriptor.getTableName());
+    long totalFiles = 0;
+    for (HRegion region : regions) {
+      HStore store = region.getStore(familyDesc.getName());
+      // This counts regular region files (with MOB references) via SFT
+      StoreFileTracker sft = StoreFileTrackerFactory.create(conf, false, 
store.getStoreContext());
+      totalFiles += sft.load().size();
+    }
+    return totalFiles;
+  }
 }
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestDirectStoreSplitsMerges.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestDirectStoreSplitsMerges.java
index cc9455641cb..2d7d4b92a2e 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestDirectStoreSplitsMerges.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestDirectStoreSplitsMerges.java
@@ -91,7 +91,7 @@ public class TestDirectStoreSplitsMerges {
       
StoreFileTrackerFactory.create(TEST_UTIL.getHBaseCluster().getMaster().getConfiguration(),
         true, region.getStores().get(0).getStoreContext());
     Path result = regionFS.splitStoreFile(daughterA, 
Bytes.toString(FAMILY_NAME), file,
-      Bytes.toBytes("002"), false, region.getSplitPolicy(), sft);
+      Bytes.toBytes("002"), false, region.getSplitPolicy(), sft).getPath();
     // asserts the reference file naming is correct
     validateResultingFile(region.getRegionInfo().getEncodedName(), result);
     // Additionally check if split region dir was created directly under table 
dir, not on .tmp
@@ -170,13 +170,13 @@ public class TestDirectStoreSplitsMerges {
     Path splitDirA = regionFS.getSplitsDir(daughterA);
     Path splitDirB = regionFS.getSplitsDir(daughterB);
     HStoreFile file = (HStoreFile) 
region.getStore(FAMILY_NAME).getStorefiles().toArray()[0];
-    List<Path> filesA = new ArrayList<>();
+    List<StoreFileInfo> filesA = new ArrayList<>();
     StoreFileTracker sft =
       
StoreFileTrackerFactory.create(TEST_UTIL.getHBaseCluster().getMaster().getConfiguration(),
         true, region.getStores().get(0).getStoreContext());
     filesA.add(regionFS.splitStoreFile(daughterA, Bytes.toString(FAMILY_NAME), 
file,
       Bytes.toBytes("002"), false, region.getSplitPolicy(), sft));
-    List<Path> filesB = new ArrayList<>();
+    List<StoreFileInfo> filesB = new ArrayList<>();
     filesB.add(regionFS.splitStoreFile(daughterB, Bytes.toString(FAMILY_NAME), 
file,
       Bytes.toBytes("002"), true, region.getSplitPolicy(), sft));
     MasterProcedureEnv env =
@@ -217,7 +217,7 @@ public class TestDirectStoreSplitsMerges {
       true, first.getStore(FAMILY_NAME).getStoreContext()));
     // merge file from second region
     file = (HStoreFile) 
second.getStore(FAMILY_NAME).getStorefiles().toArray()[0];
-    List<Path> mergedFiles = new ArrayList<>();
+    List<StoreFileInfo> mergedFiles = new ArrayList<>();
     mergedFiles.add(mergeFileFromRegion(mergeRegionFs, second, file, 
StoreFileTrackerFactory
       .create(configuration, true, 
second.getStore(FAMILY_NAME).getStoreContext())));
     MasterProcedureEnv env =
@@ -241,11 +241,11 @@ public class TestDirectStoreSplitsMerges {
     }
   }
 
-  private Path mergeFileFromRegion(HRegionFileSystem regionFS, HRegion 
regionToMerge,
+  private StoreFileInfo mergeFileFromRegion(HRegionFileSystem regionFS, 
HRegion regionToMerge,
     HStoreFile file, StoreFileTracker sft) throws IOException {
-    Path mergedFile = regionFS.mergeStoreFile(regionToMerge.getRegionInfo(),
+    StoreFileInfo mergedFile = 
regionFS.mergeStoreFile(regionToMerge.getRegionInfo(),
       Bytes.toString(FAMILY_NAME), file, sft);
-    validateResultingFile(regionToMerge.getRegionInfo().getEncodedName(), 
mergedFile);
+    validateResultingFile(regionToMerge.getRegionInfo().getEncodedName(), 
mergedFile.getPath());
     return mergedFile;
   }
 
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegionReplayEvents.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegionReplayEvents.java
index 3647a4e47ad..7b6262a1610 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegionReplayEvents.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegionReplayEvents.java
@@ -66,6 +66,9 @@ import org.apache.hadoop.hbase.io.hfile.HFile;
 import org.apache.hadoop.hbase.io.hfile.HFileContextBuilder;
 import org.apache.hadoop.hbase.regionserver.HRegion.FlushResultImpl;
 import org.apache.hadoop.hbase.regionserver.HRegion.PrepareFlushResult;
+import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequester;
+import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker;
+import 
org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
 import 
org.apache.hadoop.hbase.regionserver.throttle.NoLimitThroughputController;
 import org.apache.hadoop.hbase.testclassification.LargeTests;
 import org.apache.hadoop.hbase.util.Bytes;
@@ -193,6 +196,10 @@ public class TestHRegionReplayEvents {
     when(rss.getServerName()).thenReturn(ServerName.valueOf("foo", 1, 1));
     when(rss.getConfiguration()).thenReturn(CONF);
     when(rss.getRegionServerAccounting()).thenReturn(new 
RegionServerAccounting(CONF));
+    when(rss.getRegionServerSpaceQuotaManager()).thenReturn(null); // or mock 
it properly
+    when(rss.getFlushRequester()).thenReturn(mock(FlushRequester.class));
+    
when(rss.getCompactionRequestor()).thenReturn(mock(CompactionRequester.class));
+    when(rss.getMetrics()).thenReturn(mock(MetricsRegionServer.class));
     String string =
       
org.apache.hadoop.hbase.executor.EventType.RS_COMPACTED_FILES_DISCHARGER.toString();
     ExecutorService es = new ExecutorService(string);
@@ -421,10 +428,12 @@ public class TestHRegionReplayEvents {
 
         // assert that the compaction is applied
         for (HStore store : secondaryRegion.getStores()) {
+          StoreFileTracker sft =
+            StoreFileTrackerFactory.create(CONF, false, 
store.getStoreContext());
           if (store.getColumnFamilyName().equals("cf1")) {
             assertEquals(1, store.getStorefilesCount());
           } else {
-            assertEquals(expectedStoreFileCount, store.getStorefilesCount());
+            assertEquals(expectedStoreFileCount, sft.load().size());
           }
         }
       } else {
@@ -1538,17 +1547,17 @@ public class TestHRegionReplayEvents {
     // replay the bulk load event
     secondaryRegion.replayWALBulkLoadEventMarker(bulkloadEvent);
 
-    List<String> storeFileName = new ArrayList<>();
+    List<String> storeFileNames = new ArrayList<>();
     for (StoreDescriptor storeDesc : bulkloadEvent.getStoresList()) {
-      storeFileName.addAll(storeDesc.getStoreFileList());
+      storeFileNames.addAll(storeDesc.getStoreFileList());
     }
     // assert that the bulk loaded files are picked
     for (HStore s : secondaryRegion.getStores()) {
       for (HStoreFile sf : s.getStorefiles()) {
-        storeFileName.remove(sf.getPath().getName());
+        storeFileNames.remove(sf.getPath().getName());
       }
     }
-    assertTrue("Found some store file isn't loaded:" + storeFileName, 
storeFileName.isEmpty());
+    assertTrue("Found some store file isn't loaded:" + storeFileNames, 
storeFileNames.isEmpty());
 
     LOG.info("-- Verifying edits from secondary");
     for (byte[] family : families) {
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHStoreFile.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHStoreFile.java
index 69f549dd6b9..1580d1e690e 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHStoreFile.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHStoreFile.java
@@ -1171,12 +1171,12 @@ public class TestHStoreFile {
   private Path splitStoreFile(final HRegionFileSystem regionFs, final 
RegionInfo hri,
     final String family, final HStoreFile sf, final byte[] splitKey, boolean 
isTopRef,
     StoreFileTracker sft) throws IOException {
-    Path path = regionFs.splitStoreFile(hri, family, sf, splitKey, isTopRef, 
null, sft);
-    if (null == path) {
+    StoreFileInfo sfi = regionFs.splitStoreFile(hri, family, sf, splitKey, 
isTopRef, null, sft);
+    if (null == sfi) {
       return null;
     }
-    List<Path> splitFiles = new ArrayList<>();
-    splitFiles.add(path);
+    List<StoreFileInfo> splitFiles = new ArrayList<>();
+    splitFiles.add(sfi);
     MasterProcedureEnv mockEnv = mock(MasterProcedureEnv.class);
     MasterServices mockServices = mock(MasterServices.class);
     when(mockEnv.getMasterServices()).thenReturn(mockServices);
@@ -1187,7 +1187,7 @@ public class TestHStoreFile {
       .setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)).build();
     when(mockTblDescs.get(any())).thenReturn(mockTblDesc);
     Path regionDir = regionFs.commitDaughterRegion(hri, splitFiles, mockEnv);
-    return new Path(new Path(regionDir, family), path.getName());
+    return new Path(new Path(regionDir, family), sfi.getPath().getName());
   }
 
   private StoreFileWriter writeStoreFile(Configuration conf, CacheConfig 
cacheConf, Path path,
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestMergesSplitsAddToTracker.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestMergesSplitsAddToTracker.java
index 47790dd20da..59f16f56dca 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestMergesSplitsAddToTracker.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestMergesSplitsAddToTracker.java
@@ -122,7 +122,7 @@ public class TestMergesSplitsAddToTracker {
       .setEndKey(region.getRegionInfo().getEndKey()).setSplit(false)
       .setRegionId(region.getRegionInfo().getRegionId()).build();
     HStoreFile file = (HStoreFile) 
region.getStore(FAMILY_NAME).getStorefiles().toArray()[0];
-    List<Path> splitFilesA = new ArrayList<>();
+    List<StoreFileInfo> splitFilesA = new ArrayList<>();
     HRegionFileSystem regionFs = region.getRegionFileSystem();
     StoreFileTracker sft = 
StoreFileTrackerFactory.create(region.getBaseConf(), true,
       StoreContext.getBuilder()
@@ -130,7 +130,7 @@ public class TestMergesSplitsAddToTracker {
         .withRegionFileSystem(regionFs).build());
     splitFilesA.add(regionFS.splitStoreFile(daughterA, 
Bytes.toString(FAMILY_NAME), file,
       Bytes.toBytes("002"), false, region.getSplitPolicy(), sft));
-    List<Path> splitFilesB = new ArrayList<>();
+    List<StoreFileInfo> splitFilesB = new ArrayList<>();
     splitFilesB.add(regionFS.splitStoreFile(daughterB, 
Bytes.toString(FAMILY_NAME), file,
       Bytes.toBytes("002"), true, region.getSplitPolicy(), sft));
     MasterProcedureEnv env =
@@ -164,7 +164,7 @@ public class TestMergesSplitsAddToTracker {
       TEST_UTIL.getHBaseCluster().getMaster().getConfiguration(), 
regionFS.getFileSystem(),
       regionFS.getTableDir(), mergeResult);
 
-    List<Path> mergedFiles = new ArrayList<>();
+    List<StoreFileInfo> mergedFiles = new ArrayList<>();
     // merge file from first region
     mergedFiles.add(mergeFileFromRegion(first, mergeFS));
     // merge file from second region
@@ -256,7 +256,7 @@ public class TestMergesSplitsAddToTracker {
     }
   }
 
-  private Path mergeFileFromRegion(HRegion regionToMerge, HRegionFileSystem 
mergeFS)
+  private StoreFileInfo mergeFileFromRegion(HRegion regionToMerge, 
HRegionFileSystem mergeFS)
     throws IOException {
     HStoreFile file = (HStoreFile) 
regionToMerge.getStore(FAMILY_NAME).getStorefiles().toArray()[0];
     HRegionFileSystem regionFs = regionToMerge.getRegionFileSystem();
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java
index 5c81bd8cd0d..32f88ea8dc6 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java
@@ -957,11 +957,12 @@ public class TestSplitTransactionOnCluster {
       HRegionFileSystem hfs = region.getRegionFileSystem();
       StoreFileTracker sft = 
StoreFileTrackerFactory.create(TESTING_UTIL.getConfiguration(), true,
         store.getStoreContext());
-      Path referencePath = hfs.splitStoreFile(region.getRegionInfo(), "f",
-        storefiles.iterator().next(), Bytes.toBytes("row1"), false, 
region.getSplitPolicy(), sft);
-      assertNull(referencePath);
-      referencePath = hfs.splitStoreFile(region.getRegionInfo(), "i_f",
+      StoreFileInfo referenceStoreFileInfo = 
hfs.splitStoreFile(region.getRegionInfo(), "f",
         storefiles.iterator().next(), Bytes.toBytes("row1"), false, 
region.getSplitPolicy(), sft);
+      assertNull(referenceStoreFileInfo);
+      Path referencePath =
+        hfs.splitStoreFile(region.getRegionInfo(), "i_f", 
storefiles.iterator().next(),
+          Bytes.toBytes("row1"), false, region.getSplitPolicy(), 
sft).getPath();
       assertNotNull(referencePath);
     } finally {
       TESTING_UTIL.deleteTable(tableName);
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestStoreFileInfo.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestStoreFileInfo.java
index 29040ad58be..530a39f7306 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestStoreFileInfo.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestStoreFileInfo.java
@@ -93,10 +93,10 @@ public class TestStoreFileInfo {
     HFileLink link2 = new HFileLink(new Path(origin, "f1"), new Path(tmp, 
"f1"),
       new Path(mob, "f1"), new Path(archive, "f1"));
 
-    StoreFileInfo info1 =
-      new StoreFileInfo(TEST_UTIL.getConfiguration(), 
TEST_UTIL.getTestFileSystem(), null, link1);
-    StoreFileInfo info2 =
-      new StoreFileInfo(TEST_UTIL.getConfiguration(), 
TEST_UTIL.getTestFileSystem(), null, link2);
+    StoreFileInfo info1 = new StoreFileInfo(TEST_UTIL.getConfiguration(),
+      TEST_UTIL.getTestFileSystem(), new Path(archive, "f1"), link1);
+    StoreFileInfo info2 = new StoreFileInfo(TEST_UTIL.getConfiguration(),
+      TEST_UTIL.getTestFileSystem(), new Path(archive, "f1"), link2);
 
     assertEquals(info1, info2);
     assertEquals(info1.hashCode(), info2.hashCode());
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/storefiletracker/TestStoreFileListFile.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/storefiletracker/TestStoreFileListFile.java
index 82278a9e350..64865caac1e 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/storefiletracker/TestStoreFileListFile.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/storefiletracker/TestStoreFileListFile.java
@@ -21,7 +21,10 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.io.IOException;
@@ -32,8 +35,10 @@ import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hbase.HBaseClassTestRule;
 import org.apache.hadoop.hbase.HBaseCommonTestingUtil;
+import org.apache.hadoop.hbase.io.Reference;
 import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
 import org.apache.hadoop.hbase.regionserver.StoreContext;
+import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
 import org.apache.hadoop.hbase.testclassification.RegionServerTests;
 import org.apache.hadoop.hbase.testclassification.SmallTests;
 import org.apache.hadoop.hbase.util.Bytes;
@@ -256,4 +261,48 @@ public class TestStoreFileListFile {
     StoreFileList list = storeFileListFile.load(true);
     assertEquals(1, list.getStoreFileCount());
   }
+
+  @Test
+  public void testReadReferenceFallbackForOldStoreFileEntry() throws 
IOException {
+    FileSystem fs = FileSystem.get(UTIL.getConfiguration());
+    HRegionFileSystem hfs = mock(HRegionFileSystem.class);
+    when(hfs.getFileSystem()).thenReturn(fs);
+    StoreContext ctx = 
StoreContext.getBuilder().withFamilyStoreDirectoryPath(testDir)
+      .withRegionFileSystem(hfs).build();
+    FileBasedStoreFileTracker tracker =
+      new FileBasedStoreFileTracker(UTIL.getConfiguration(), true, ctx);
+
+    String referenceFileName = "abcdef0123456789.parentRegion";
+    assertTrue(StoreFileInfo.isReference(referenceFileName));
+    Reference expected = 
Reference.createTopReference(Bytes.toBytes("split-row"));
+    Path referenceFile = new Path(testDir, referenceFileName);
+    expected.write(fs, referenceFile);
+
+    // Simulate older StoreFileListFile where StoreFileEntry does not contain 
reference field.
+    StoreFileListFile oldFormatStoreFileListFile = new StoreFileListFile(ctx);
+    oldFormatStoreFileListFile.update(StoreFileList.newBuilder()
+      
.addStoreFile(StoreFileEntry.newBuilder().setName(referenceFileName).setSize(1)));
+
+    org.apache.logging.log4j.core.Appender mockAppender =
+      mock(org.apache.logging.log4j.core.Appender.class);
+    when(mockAppender.getName()).thenReturn("mockAppender");
+    when(mockAppender.isStarted()).thenReturn(true);
+    org.apache.logging.log4j.core.Logger logger =
+      (org.apache.logging.log4j.core.Logger) 
org.apache.logging.log4j.LogManager
+        .getLogger(FileBasedStoreFileTracker.class);
+    org.apache.logging.log4j.Level oldLevel = logger.getLevel();
+    logger.setLevel(org.apache.logging.log4j.Level.DEBUG);
+    logger.addAppender(mockAppender);
+    try {
+      Reference actual = tracker.readReference(referenceFile);
+      assertEquals(expected, actual);
+      verify(mockAppender, atLeastOnce()).append(
+        argThat(event -> event.getMessage() != null && 
event.getMessage().getFormattedMessage()
+          .contains("Fallback to reading reference from FS as it is not part 
of StoreFileEntry. "
+            + "This is when FSFT is reading older version of 
StoreFileListFile")));
+    } finally {
+      logger.removeAppender(mockAppender);
+      logger.setLevel(oldLevel);
+    }
+  }
 }


Reply via email to