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

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

commit 771e552cf4a6436176de06eb033c6de6ee545c71
Author: BukrosSzabolcs <bukros.szabo...@gmail.com>
AuthorDate: Wed Dec 15 20:09:03 2021 -0500

    HBASE-26286: Add support for specifying store file tracker when restoring 
or cloning snapshot
    
    Closes #3851
    
    Signed-off-by: Duo Zhang <zhang...@apache.org>
    Signed-off-by: Josh Elser <els...@apache.org>
---
 .../java/org/apache/hadoop/hbase/client/Admin.java | 44 ++++++++++++--
 .../hadoop/hbase/client/AdminOverAsyncAdmin.java   |  7 ++-
 .../org/apache/hadoop/hbase/client/AsyncAdmin.java | 14 ++++-
 .../hadoop/hbase/client/AsyncHBaseAdmin.java       |  6 +-
 .../hbase/client/ColumnFamilyDescriptor.java       |  5 ++
 .../client/ColumnFamilyDescriptorBuilder.java      |  6 ++
 .../hadoop/hbase/client/RawAsyncHBaseAdmin.java    | 24 +++++---
 .../src/main/protobuf/server/master/Master.proto   |  1 +
 .../protobuf/server/master/MasterProcedure.proto   |  1 +
 .../org/apache/hadoop/hbase/master/HMaster.java    | 25 ++++----
 .../hadoop/hbase/master/MasterRpcServices.java     |  2 +-
 .../master/procedure/CloneSnapshotProcedure.java   | 54 +++++++++++++++-
 .../master/procedure/RestoreSnapshotProcedure.java |  7 ++-
 .../hbase/master/snapshot/SnapshotManager.java     | 27 +++++---
 .../storefiletracker/StoreFileTrackerFactory.java  | 39 +++++++++++-
 .../hbase/snapshot/RestoreSnapshotHelper.java      | 11 ++--
 .../TestCloneSnapshotFromClientCustomSFT.java      | 71 ++++++++++++++++++++++
 .../storefiletracker/TestStoreFileTracker.java     |  2 -
 .../TestStoreFileTrackerFactory.java               | 52 ++++++++++++++++
 .../hbase/rsgroup/VerifyingRSGroupAdmin.java       |  5 +-
 hbase-shell/src/main/ruby/hbase/admin.rb           |  4 +-
 hbase-shell/src/main/ruby/hbase_constants.rb       |  1 +
 .../src/main/ruby/shell/commands/clone_snapshot.rb |  6 +-
 .../hadoop/hbase/thrift2/client/ThriftAdmin.java   |  4 +-
 24 files changed, 357 insertions(+), 61 deletions(-)

diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
index 48893cc..6c36660 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
@@ -69,6 +69,7 @@ import org.apache.hadoop.hbase.util.Pair;
 import org.apache.yetus.audience.InterfaceAudience;
 
 import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableList;
+import org.apache.yetus.audience.InterfaceStability;
 
 /**
  * The administrative API for HBase. Obtain an instance from {@link 
Connection#getAdmin()} and
@@ -1620,7 +1621,7 @@ public interface Admin extends Abortable, Closeable {
    * @throws IllegalArgumentException if the restore request is formatted 
incorrectly
    */
   void restoreSnapshot(String snapshotName, boolean takeFailSafeSnapshot, 
boolean restoreAcl)
-      throws IOException, RestoreSnapshotException;
+    throws IOException, RestoreSnapshotException;
 
   /**
    * Create a new table by cloning the snapshot content.
@@ -1633,7 +1634,25 @@ public interface Admin extends Abortable, Closeable {
    */
   default void cloneSnapshot(String snapshotName, TableName tableName)
       throws IOException, TableExistsException, RestoreSnapshotException {
-    cloneSnapshot(snapshotName, tableName, false);
+    cloneSnapshot(snapshotName, tableName, false, null);
+  }
+
+  /**
+   * Create a new table by cloning the snapshot content.
+   * @param snapshotName name of the snapshot to be cloned
+   * @param tableName name of the table where the snapshot will be restored
+   * @param restoreAcl <code>true</code> to clone acl into newly created table
+   * @param customSFT specify the StoreFileTracker used for the table
+   * @throws IOException if a remote or network exception occurs
+   * @throws TableExistsException if table to be created already exists
+   * @throws RestoreSnapshotException if snapshot failed to be cloned
+   * @throws IllegalArgumentException if the specified table has not a valid 
name
+   */
+  default void cloneSnapshot(String snapshotName, TableName tableName, boolean 
restoreAcl,
+    String customSFT)
+    throws IOException, TableExistsException, RestoreSnapshotException {
+    get(cloneSnapshotAsync(snapshotName, tableName, restoreAcl, customSFT), 
getSyncWaitTimeout(),
+      TimeUnit.MILLISECONDS);
   }
 
   /**
@@ -1680,8 +1699,25 @@ public interface Admin extends Abortable, Closeable {
    * @throws RestoreSnapshotException if snapshot failed to be cloned
    * @throws IllegalArgumentException if the specified table has not a valid 
name
    */
-  Future<Void> cloneSnapshotAsync(String snapshotName, TableName tableName, 
boolean restoreAcl)
-      throws IOException, TableExistsException, RestoreSnapshotException;
+  default Future<Void> cloneSnapshotAsync(String snapshotName, TableName 
tableName,
+    boolean restoreAcl)
+      throws IOException, TableExistsException, RestoreSnapshotException {
+    return cloneSnapshotAsync(snapshotName, tableName, restoreAcl, null);
+  }
+
+  /**
+   * Create a new table by cloning the snapshot content.
+   * @param snapshotName name of the snapshot to be cloned
+   * @param tableName name of the table where the snapshot will be restored
+   * @param restoreAcl <code>true</code> to clone acl into newly created table
+   * @param customSFT specify the StroreFileTracker used for the table
+   * @throws IOException if a remote or network exception occurs
+   * @throws TableExistsException if table to be created already exists
+   * @throws RestoreSnapshotException if snapshot failed to be cloned
+   * @throws IllegalArgumentException if the specified table has not a valid 
name
+   */
+  Future<Void> cloneSnapshotAsync(String snapshotName, TableName tableName, 
boolean restoreAcl,
+    String customSFT) throws IOException, TableExistsException, 
RestoreSnapshotException;
 
   /**
    * Execute a distributed procedure on a cluster.
diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java
 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java
index 08de979..161fe3d 100644
--- 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java
+++ 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java
@@ -644,14 +644,15 @@ class AdminOverAsyncAdmin implements Admin {
 
   @Override
   public void restoreSnapshot(String snapshotName, boolean 
takeFailSafeSnapshot, boolean restoreAcl)
-      throws IOException, RestoreSnapshotException {
+    throws IOException, RestoreSnapshotException {
     get(admin.restoreSnapshot(snapshotName, takeFailSafeSnapshot, restoreAcl));
   }
 
   @Override
   public Future<Void> cloneSnapshotAsync(String snapshotName, TableName 
tableName,
-      boolean restoreAcl) throws IOException, TableExistsException, 
RestoreSnapshotException {
-    return admin.cloneSnapshot(snapshotName, tableName, restoreAcl);
+    boolean restoreAcl, String customSFT)
+    throws IOException, TableExistsException, RestoreSnapshotException {
+    return admin.cloneSnapshot(snapshotName, tableName, restoreAcl, customSFT);
   }
 
   @Override
diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
index c366d3e..ba54bb7 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
@@ -895,8 +895,20 @@ public interface AsyncAdmin {
    * @param tableName name of the table where the snapshot will be restored
    * @param restoreAcl <code>true</code> to restore acl of snapshot
    */
+  default CompletableFuture<Void> cloneSnapshot(String snapshotName, TableName 
tableName,
+      boolean restoreAcl) {
+    return cloneSnapshot(snapshotName, tableName, restoreAcl, null);
+  }
+
+  /**
+   * Create a new table by cloning the snapshot content.
+   * @param snapshotName name of the snapshot to be cloned
+   * @param tableName name of the table where the snapshot will be restored
+   * @param restoreAcl <code>true</code> to restore acl of snapshot
+   * @param customSFT specify the StroreFileTracker used for the table
+   */
   CompletableFuture<Void> cloneSnapshot(String snapshotName, TableName 
tableName,
-      boolean restoreAcl);
+      boolean restoreAcl, String customSFT);
 
   /**
    * List completed snapshots.
diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
index f0f564b..64a13c7 100644
--- 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
+++ 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
@@ -488,14 +488,14 @@ class AsyncHBaseAdmin implements AsyncAdmin {
 
   @Override
   public CompletableFuture<Void> restoreSnapshot(String snapshotName, boolean 
takeFailSafeSnapshot,
-      boolean restoreAcl) {
+    boolean restoreAcl) {
     return wrap(rawAdmin.restoreSnapshot(snapshotName, takeFailSafeSnapshot, 
restoreAcl));
   }
 
   @Override
   public CompletableFuture<Void> cloneSnapshot(String snapshotName, TableName 
tableName,
-      boolean restoreAcl) {
-    return wrap(rawAdmin.cloneSnapshot(snapshotName, tableName, restoreAcl));
+      boolean restoreAcl, String customSFT) {
+    return wrap(rawAdmin.cloneSnapshot(snapshotName, tableName, restoreAcl, 
customSFT));
   }
 
   @Override
diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ColumnFamilyDescriptor.java
 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ColumnFamilyDescriptor.java
index 86d561d..001d672 100644
--- 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ColumnFamilyDescriptor.java
+++ 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ColumnFamilyDescriptor.java
@@ -199,6 +199,11 @@ public interface ColumnFamilyDescriptor {
    * @param key The key.
    * @return A clone value. Null if no mapping for the key
    */
+  String getValue(String key);
+  /**
+   * @param key The key.
+   * @return A clone value. Null if no mapping for the key
+   */
   byte[] getValue(byte[] key);
   /**
    * It clone all bytes of all elements.
diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ColumnFamilyDescriptorBuilder.java
 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ColumnFamilyDescriptorBuilder.java
index 06a2aec..6d85cb4 100644
--- 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ColumnFamilyDescriptorBuilder.java
+++ 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ColumnFamilyDescriptorBuilder.java
@@ -672,6 +672,12 @@ public class ColumnFamilyDescriptorBuilder {
     }
 
     @Override
+    public String getValue(String key) {
+      Bytes rval = values.get(new Bytes(Bytes.toBytes(key)));
+      return rval == null ? null : Bytes.toString(rval.get(), 
rval.getOffset(), rval.getLength());
+    }
+
+    @Override
     public Map<Bytes, Bytes> getValues() {
       return Collections.unmodifiableMap(values);
     }
diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java
 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java
index 292c9cb..f0895a0 100644
--- 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java
+++ 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java
@@ -1967,7 +1967,7 @@ class RawAsyncHBaseAdmin implements AsyncAdmin {
         } else if (!exists) {
           // if table does not exist, then just clone snapshot into new table.
           completeConditionalOnFuture(future,
-            internalRestoreSnapshot(snapshotName, finalTableName, restoreAcl));
+            internalRestoreSnapshot(snapshotName, finalTableName, restoreAcl, 
null));
         } else {
           addListener(isTableDisabled(finalTableName), (disabled, err4) -> {
             if (err4 != null) {
@@ -2003,12 +2003,13 @@ class RawAsyncHBaseAdmin implements AsyncAdmin {
           future.completeExceptionally(err);
         } else {
           // Step.2 Restore snapshot
-          addListener(internalRestoreSnapshot(snapshotName, tableName, 
restoreAcl),
+          addListener(internalRestoreSnapshot(snapshotName, tableName, 
restoreAcl, null),
             (void2, err2) -> {
               if (err2 != null) {
                 // Step.3.a Something went wrong during the restore and try to 
rollback.
                 addListener(
-                  internalRestoreSnapshot(failSafeSnapshotSnapshotName, 
tableName, restoreAcl),
+                  internalRestoreSnapshot(failSafeSnapshotSnapshotName, 
tableName, restoreAcl,
+                    null),
                   (void3, err3) -> {
                     if (err3 != null) {
                       future.completeExceptionally(err3);
@@ -2036,7 +2037,7 @@ class RawAsyncHBaseAdmin implements AsyncAdmin {
       });
       return future;
     } else {
-      return internalRestoreSnapshot(snapshotName, tableName, restoreAcl);
+      return internalRestoreSnapshot(snapshotName, tableName, restoreAcl, 
null);
     }
   }
 
@@ -2053,7 +2054,7 @@ class RawAsyncHBaseAdmin implements AsyncAdmin {
 
   @Override
   public CompletableFuture<Void> cloneSnapshot(String snapshotName, TableName 
tableName,
-      boolean restoreAcl) {
+      boolean restoreAcl, String customSFT) {
     CompletableFuture<Void> future = new CompletableFuture<>();
     addListener(tableExists(tableName), (exists, err) -> {
       if (err != null) {
@@ -2062,14 +2063,14 @@ class RawAsyncHBaseAdmin implements AsyncAdmin {
         future.completeExceptionally(new TableExistsException(tableName));
       } else {
         completeConditionalOnFuture(future,
-          internalRestoreSnapshot(snapshotName, tableName, restoreAcl));
+          internalRestoreSnapshot(snapshotName, tableName, restoreAcl, 
customSFT));
       }
     });
     return future;
   }
 
   private CompletableFuture<Void> internalRestoreSnapshot(String snapshotName, 
TableName tableName,
-      boolean restoreAcl) {
+      boolean restoreAcl, String customSFT) {
     SnapshotProtos.SnapshotDescription snapshot = 
SnapshotProtos.SnapshotDescription.newBuilder()
       .setName(snapshotName).setTable(tableName.getNameAsString()).build();
     try {
@@ -2077,10 +2078,15 @@ class RawAsyncHBaseAdmin implements AsyncAdmin {
     } catch (IllegalArgumentException e) {
       return failedFuture(e);
     }
+    RestoreSnapshotRequest.Builder builder =
+      
RestoreSnapshotRequest.newBuilder().setSnapshot(snapshot).setNonceGroup(ng.getNonceGroup())
+        .setNonce(ng.newNonce()).setRestoreACL(restoreAcl);
+    if(customSFT != null){
+      builder.setCustomSFT(customSFT);
+    }
     return waitProcedureResult(this.<Long> 
newMasterCaller().action((controller, stub) -> this
       .<RestoreSnapshotRequest, RestoreSnapshotResponse, Long> 
call(controller, stub,
-        
RestoreSnapshotRequest.newBuilder().setSnapshot(snapshot).setNonceGroup(ng.getNonceGroup())
-          .setNonce(ng.newNonce()).setRestoreACL(restoreAcl).build(),
+        builder.build(),
         (s, c, req, done) -> s.restoreSnapshot(c, req, done), (resp) -> 
resp.getProcId()))
       .call());
   }
diff --git a/hbase-protocol-shaded/src/main/protobuf/server/master/Master.proto 
b/hbase-protocol-shaded/src/main/protobuf/server/master/Master.proto
index ea09e11..c502f45 100644
--- a/hbase-protocol-shaded/src/main/protobuf/server/master/Master.proto
+++ b/hbase-protocol-shaded/src/main/protobuf/server/master/Master.proto
@@ -462,6 +462,7 @@ message RestoreSnapshotRequest {
   optional uint64 nonce_group = 2 [default = 0];
   optional uint64 nonce = 3 [default = 0];
   optional bool restoreACL = 4 [default = false];
+  optional string customSFT = 5;
 }
 
 message RestoreSnapshotResponse {
diff --git 
a/hbase-protocol-shaded/src/main/protobuf/server/master/MasterProcedure.proto 
b/hbase-protocol-shaded/src/main/protobuf/server/master/MasterProcedure.proto
index 6746e09..77f2c60 100644
--- 
a/hbase-protocol-shaded/src/main/protobuf/server/master/MasterProcedure.proto
+++ 
b/hbase-protocol-shaded/src/main/protobuf/server/master/MasterProcedure.proto
@@ -208,6 +208,7 @@ message CloneSnapshotStateData {
   repeated RegionInfo region_info = 4;
   repeated RestoreParentToChildRegionsPair parent_to_child_regions_pair_list = 
5;
   optional bool restore_acl = 6;
+  optional string customSFT = 7;
 }
 
 enum RestoreSnapshotState {
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
index b80a2e2..49c2f93 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
@@ -2619,8 +2619,8 @@ public class HMaster extends 
HBaseServerBase<MasterRpcServices> implements Maste
 
   }
 
-  public long restoreSnapshot(final SnapshotDescription snapshotDesc,
-      final long nonceGroup, final long nonce, final boolean restoreAcl) 
throws IOException {
+  public long restoreSnapshot(final SnapshotDescription snapshotDesc, final 
long nonceGroup,
+    final long nonce, final boolean restoreAcl, final String customSFT) throws 
IOException {
     checkInitialized();
     getSnapshotManager().checkSnapshotSupport();
 
@@ -2629,18 +2629,19 @@ public class HMaster extends 
HBaseServerBase<MasterRpcServices> implements Maste
     getClusterSchema().getNamespace(dstTable.getNamespaceAsString());
 
     return MasterProcedureUtil.submitProcedure(
-        new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, 
nonce) {
-      @Override
-      protected void run() throws IOException {
+      new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
+        @Override
+        protected void run() throws IOException {
           setProcId(
-            getSnapshotManager().restoreOrCloneSnapshot(snapshotDesc, 
getNonceKey(), restoreAcl));
-      }
+            getSnapshotManager().restoreOrCloneSnapshot(snapshotDesc, 
getNonceKey(), restoreAcl,
+              customSFT));
+        }
 
-      @Override
-      protected String getDescription() {
-        return "RestoreSnapshotProcedure";
-      }
-    });
+        @Override
+        protected String getDescription() {
+          return "RestoreSnapshotProcedure";
+        }
+      });
   }
 
   private void checkTableExists(final TableName tableName)
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
index 46bc8c2..9be3685 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
@@ -1586,7 +1586,7 @@ public class MasterRpcServices extends 
HBaseRpcServicesBase<HMaster>
       RestoreSnapshotRequest request) throws ServiceException {
     try {
       long procId = server.restoreSnapshot(request.getSnapshot(), 
request.getNonceGroup(),
-        request.getNonce(), request.getRestoreACL());
+        request.getNonce(), request.getRestoreACL(), request.getCustomSFT());
       return RestoreSnapshotResponse.newBuilder().setProcId(procId).build();
     } catch (ForeignException e) {
       throw new ServiceException(e.getCause());
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CloneSnapshotProcedure.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CloneSnapshotProcedure.java
index 7157fbf..f6185d1 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CloneSnapshotProcedure.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CloneSnapshotProcedure.java
@@ -30,6 +30,8 @@ import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hbase.TableExistsException;
 import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
+import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
 import org.apache.hadoop.hbase.client.RegionInfo;
 import org.apache.hadoop.hbase.client.TableDescriptor;
 import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
@@ -44,6 +46,8 @@ import 
org.apache.hadoop.hbase.master.procedure.CreateTableProcedure.CreateHdfsR
 import org.apache.hadoop.hbase.monitoring.MonitoredTask;
 import org.apache.hadoop.hbase.monitoring.TaskMonitor;
 import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
+import org.apache.hadoop.hbase.procedure2.util.StringUtils;
+import 
org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
 import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils;
 import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
 import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;
@@ -72,6 +76,7 @@ public class CloneSnapshotProcedure
   private TableDescriptor tableDescriptor;
   private SnapshotDescription snapshot;
   private boolean restoreAcl;
+  private String customSFT;
   private List<RegionInfo> newRegions = null;
   private Map<String, Pair<String, String> > parentsToChildrenPairMap = new 
HashMap<>();
 
@@ -96,12 +101,19 @@ public class CloneSnapshotProcedure
    * @param snapshot snapshot to clone from
    */
   public CloneSnapshotProcedure(final MasterProcedureEnv env,
+    final TableDescriptor tableDescriptor, final SnapshotDescription snapshot,
+    final boolean restoreAcl) {
+    this(env, tableDescriptor, snapshot, restoreAcl, null);
+  }
+
+  public CloneSnapshotProcedure(final MasterProcedureEnv env,
       final TableDescriptor tableDescriptor, final SnapshotDescription 
snapshot,
-      final boolean restoreAcl) {
+      final boolean restoreAcl, final String customSFT) {
     super(env);
     this.tableDescriptor = tableDescriptor;
     this.snapshot = snapshot;
     this.restoreAcl = restoreAcl;
+    this.customSFT = customSFT;
 
     getMonitorStatus();
   }
@@ -139,6 +151,7 @@ public class CloneSnapshotProcedure
           setNextState(CloneSnapshotState.CLONE_SNAPSHOT_WRITE_FS_LAYOUT);
           break;
         case CLONE_SNAPSHOT_WRITE_FS_LAYOUT:
+          updateTableDescriptorWithSFT();
           newRegions = createFilesystemLayout(env, tableDescriptor, 
newRegions);
           
env.getMasterServices().getTableDescriptors().update(tableDescriptor, true);
           setNextState(CloneSnapshotState.CLONE_SNAPSHOT_ADD_TO_META);
@@ -203,6 +216,37 @@ public class CloneSnapshotProcedure
     return Flow.HAS_MORE_STATE;
   }
 
+  /**
+   * If a StoreFileTracker is specified we strip the TableDescriptor from 
previous SFT config
+   * and set the specified SFT on the table level
+   */
+  private void updateTableDescriptorWithSFT() {
+    if (StringUtils.isEmpty(customSFT)){
+      return;
+    }
+
+    TableDescriptorBuilder builder = 
TableDescriptorBuilder.newBuilder(tableDescriptor);
+    builder.setValue(StoreFileTrackerFactory.TRACKER_IMPL, customSFT);
+    for (ColumnFamilyDescriptor family : tableDescriptor.getColumnFamilies()){
+      ColumnFamilyDescriptorBuilder cfBuilder = 
ColumnFamilyDescriptorBuilder.newBuilder(family);
+      cfBuilder.setConfiguration(StoreFileTrackerFactory.TRACKER_IMPL, null);
+      cfBuilder.setValue(StoreFileTrackerFactory.TRACKER_IMPL, null);
+      builder.modifyColumnFamily(cfBuilder.build());
+    }
+    tableDescriptor = builder.build();
+  }
+
+  private void validateSFT() {
+    if (StringUtils.isEmpty(customSFT)){
+      return;
+    }
+
+    //if customSFT is invalid getTrackerClass will throw a RuntimeException
+    Configuration sftConfig = new Configuration();
+    sftConfig.set(StoreFileTrackerFactory.TRACKER_IMPL, customSFT);
+    StoreFileTrackerFactory.getTrackerClass(sftConfig);
+  }
+
   @Override
   protected void rollbackState(final MasterProcedureEnv env, final 
CloneSnapshotState state)
       throws IOException {
@@ -292,6 +336,9 @@ public class CloneSnapshotProcedure
         cloneSnapshotMsg.addParentToChildRegionsPairList(parentToChildrenPair);
       }
     }
+    if (!StringUtils.isEmpty(customSFT)){
+      cloneSnapshotMsg.setCustomSFT(customSFT);
+    }
     serializer.serialize(cloneSnapshotMsg.build());
   }
 
@@ -327,6 +374,9 @@ public class CloneSnapshotProcedure
             parentToChildrenPair.getChild2RegionName()));
       }
     }
+    if (!StringUtils.isEmpty(cloneSnapshotMsg.getCustomSFT())){
+      customSFT = cloneSnapshotMsg.getCustomSFT();
+    }
     // Make sure that the monitor status is set up
     getMonitorStatus();
   }
@@ -340,6 +390,8 @@ public class CloneSnapshotProcedure
     if (env.getMasterServices().getTableDescriptors().exists(tableName)) {
       throw new TableExistsException(tableName);
     }
+
+    validateSFT();
   }
 
   /**
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RestoreSnapshotProcedure.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RestoreSnapshotProcedure.java
index a5a18b0..81b0aab 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RestoreSnapshotProcedure.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RestoreSnapshotProcedure.java
@@ -25,6 +25,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hbase.DoNotRetryIOException;
@@ -90,6 +91,7 @@ public class RestoreSnapshotProcedure
   throws HBaseIOException {
     this(env, tableDescriptor, snapshot, false);
   }
+
   /**
    * Constructor
    * @param env MasterProcedureEnv
@@ -387,14 +389,15 @@ public class RestoreSnapshotProcedure
     FileSystem fs = fileSystemManager.getFileSystem();
     Path rootDir = fileSystemManager.getRootDir();
     final ForeignExceptionDispatcher monitorException = new 
ForeignExceptionDispatcher();
+    final Configuration conf = new Configuration(env.getMasterConfiguration());
 
     LOG.info("Starting restore snapshot=" + 
ClientSnapshotDescriptionUtils.toString(snapshot));
     try {
       Path snapshotDir = 
SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshot, rootDir);
       SnapshotManifest manifest = SnapshotManifest.open(
-        env.getMasterServices().getConfiguration(), fs, snapshotDir, snapshot);
+        conf, fs, snapshotDir, snapshot);
       RestoreSnapshotHelper restoreHelper = new RestoreSnapshotHelper(
-        env.getMasterServices().getConfiguration(),
+        conf,
         fs,
         manifest,
         modifiedTableDescriptor,
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java
index cdacff7..76cd1bf 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java
@@ -66,6 +66,7 @@ import org.apache.hadoop.hbase.procedure.ProcedureCoordinator;
 import org.apache.hadoop.hbase.procedure.ProcedureCoordinatorRpcs;
 import org.apache.hadoop.hbase.procedure.ZKProcedureCoordinator;
 import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
+import 
org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
 import org.apache.hadoop.hbase.security.AccessDeniedException;
 import org.apache.hadoop.hbase.security.User;
 import org.apache.hadoop.hbase.security.access.AccessChecker;
@@ -751,8 +752,8 @@ public class SnapshotManager extends MasterProcedureManager 
implements Stoppable
    * @throws IOException
    */
   private long cloneSnapshot(final SnapshotDescription reqSnapshot, final 
TableName tableName,
-      final SnapshotDescription snapshot, final TableDescriptor 
snapshotTableDesc,
-      final NonceKey nonceKey, final boolean restoreAcl) throws IOException {
+    final SnapshotDescription snapshot, final TableDescriptor 
snapshotTableDesc,
+    final NonceKey nonceKey, final boolean restoreAcl, final String customSFT) 
throws IOException {
     MasterCoprocessorHost cpHost = master.getMasterCoprocessorHost();
     TableDescriptor htd = TableDescriptorBuilder.copy(tableName, 
snapshotTableDesc);
     org.apache.hadoop.hbase.client.SnapshotDescription snapshotPOJO = null;
@@ -762,7 +763,7 @@ public class SnapshotManager extends MasterProcedureManager 
implements Stoppable
     }
     long procId;
     try {
-      procId = cloneSnapshot(snapshot, htd, nonceKey, restoreAcl);
+      procId = cloneSnapshot(snapshot, htd, nonceKey, restoreAcl, customSFT);
     } catch (IOException e) {
       LOG.error("Exception occurred while cloning the snapshot " + 
snapshot.getName()
         + " as table " + tableName.getNameAsString(), e);
@@ -786,7 +787,8 @@ public class SnapshotManager extends MasterProcedureManager 
implements Stoppable
    * @return procId the ID of the clone snapshot procedure
    */
   synchronized long cloneSnapshot(final SnapshotDescription snapshot,
-      final TableDescriptor tableDescriptor, final NonceKey nonceKey, final 
boolean restoreAcl)
+    final TableDescriptor tableDescriptor, final NonceKey nonceKey, final 
boolean restoreAcl,
+    final String customSFT)
       throws HBaseSnapshotException {
     TableName tableName = tableDescriptor.getTableName();
 
@@ -803,7 +805,7 @@ public class SnapshotManager extends MasterProcedureManager 
implements Stoppable
     try {
       long procId = master.getMasterProcedureExecutor().submitProcedure(
         new 
CloneSnapshotProcedure(master.getMasterProcedureExecutor().getEnvironment(),
-                tableDescriptor, snapshot, restoreAcl),
+                tableDescriptor, snapshot, restoreAcl, customSFT),
         nonceKey);
       this.restoreTableToProcIdMap.put(tableName, procId);
       return procId;
@@ -822,7 +824,7 @@ public class SnapshotManager extends MasterProcedureManager 
implements Stoppable
    * @throws IOException
    */
   public long restoreOrCloneSnapshot(final SnapshotDescription reqSnapshot, 
final NonceKey nonceKey,
-      final boolean restoreAcl) throws IOException {
+      final boolean restoreAcl, String customSFT) throws IOException {
     FileSystem fs = master.getMasterFileSystem().getFileSystem();
     Path snapshotDir = 
SnapshotDescriptionUtils.getCompletedSnapshotDir(reqSnapshot, rootDir);
 
@@ -854,11 +856,12 @@ public class SnapshotManager extends 
MasterProcedureManager implements Stoppable
     // Execute the restore/clone operation
     long procId;
     if (master.getTableDescriptors().exists(tableName)) {
-      procId = restoreSnapshot(reqSnapshot, tableName, snapshot, 
snapshotTableDesc, nonceKey,
-        restoreAcl);
+      procId =
+        restoreSnapshot(reqSnapshot, tableName, snapshot, snapshotTableDesc, 
nonceKey, restoreAcl);
     } else {
       procId =
-          cloneSnapshot(reqSnapshot, tableName, snapshot, snapshotTableDesc, 
nonceKey, restoreAcl);
+        cloneSnapshot(reqSnapshot, tableName, snapshot, snapshotTableDesc, 
nonceKey, restoreAcl,
+          customSFT);
     }
     return procId;
   }
@@ -880,6 +883,10 @@ public class SnapshotManager extends 
MasterProcedureManager implements Stoppable
       final NonceKey nonceKey, final boolean restoreAcl) throws IOException {
     MasterCoprocessorHost cpHost = master.getMasterCoprocessorHost();
 
+    //have to check first if restoring the snapshot would break current SFT 
setup
+    
StoreFileTrackerFactory.validatePreRestoreSnapshot(master.getTableDescriptors().get(tableName),
+      snapshotTableDesc, master.getConfiguration());
+
     if (master.getTableStateManager().isTableState(
       TableName.valueOf(snapshot.getTable()), TableState.State.ENABLED)) {
       throw new UnsupportedOperationException("Table '" +
@@ -921,7 +928,7 @@ public class SnapshotManager extends MasterProcedureManager 
implements Stoppable
    * @return procId the ID of the restore snapshot procedure
    */
   private synchronized long restoreSnapshot(final SnapshotDescription snapshot,
-      final TableDescriptor tableDescriptor, final NonceKey nonceKey, final 
boolean restoreAcl)
+    final TableDescriptor tableDescriptor, final NonceKey nonceKey, final 
boolean restoreAcl)
       throws HBaseSnapshotException {
     final TableName tableName = tableDescriptor.getTableName();
 
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 1c683ae..61a71c2 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
@@ -29,6 +29,7 @@ import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
 import org.apache.hadoop.hbase.regionserver.StoreContext;
 
 import org.apache.hadoop.hbase.regionserver.StoreUtils;
+import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
 import org.apache.hadoop.hbase.util.ReflectionUtils;
 import org.apache.yetus.audience.InterfaceAudience;
 import org.slf4j.Logger;
@@ -92,7 +93,7 @@ public final class StoreFileTrackerFactory {
     return name != null ? name.name() : clazz.getName();
   }
 
-  private static Class<? extends StoreFileTracker> 
getTrackerClass(Configuration conf) {
+  public static Class<? extends StoreFileTracker> 
getTrackerClass(Configuration conf) {
     try {
       Trackers tracker = 
Trackers.valueOf(getStoreFileTrackerName(conf).toUpperCase());
       return tracker.clazz;
@@ -311,4 +312,40 @@ public final class StoreFileTrackerFactory {
       }
     }
   }
+
+  /**
+   * Makes sure restoring a snapshot does not break the current SFT setup
+   * follows StoreUtils.createStoreConfiguration
+   * @param currentTableDesc Existing Table's TableDescriptor
+   * @param snapshotTableDesc Snapshot's TableDescriptor
+   * @param baseConf Current global configuration
+   * @throws RestoreSnapshotException if restore would break the current SFT 
setup
+   */
+  public static void validatePreRestoreSnapshot(TableDescriptor 
currentTableDesc,
+    TableDescriptor snapshotTableDesc, Configuration baseConf) throws 
RestoreSnapshotException {
+
+    for (ColumnFamilyDescriptor cfDesc : currentTableDesc.getColumnFamilies()) 
{
+      ColumnFamilyDescriptor snapCFDesc = 
snapshotTableDesc.getColumnFamily(cfDesc.getName());
+      // if there is no counterpart in the snapshot it will be just deleted so 
the config does
+      // not matter
+      if (snapCFDesc != null) {
+        Configuration currentCompositeConf =
+          StoreUtils.createStoreConfiguration(baseConf, currentTableDesc, 
cfDesc);
+        Configuration snapCompositeConf =
+          StoreUtils.createStoreConfiguration(baseConf, snapshotTableDesc, 
snapCFDesc);
+        Class<? extends StoreFileTracker> currentSFT =
+          StoreFileTrackerFactory.getTrackerClass(currentCompositeConf);
+        Class<? extends StoreFileTracker> snapSFT =
+          StoreFileTrackerFactory.getTrackerClass(snapCompositeConf);
+
+        //restoration is not possible if there is an SFT mismatch
+        if (currentSFT != snapSFT) {
+          throw new RestoreSnapshotException(
+            "Restoring Snapshot is not possible because " + " the config for 
column family "
+              + cfDesc.getNameAsString() + " has incompatible configuration. 
Current SFT: "
+              + currentSFT + " SFT from snapshot: " + snapSFT);
+        }
+      }
+    }
+  }
 }
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 0f8a95f..1740644 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
@@ -57,6 +57,7 @@ import org.apache.hadoop.hbase.regionserver.HRegion;
 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.regionserver.StoreUtils;
 import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker;
 import 
org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
 import org.apache.hadoop.hbase.security.access.AccessControlClient;
@@ -72,9 +73,7 @@ import org.apache.hadoop.io.IOUtils;
 import org.apache.yetus.audience.InterfaceAudience;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
 import org.apache.hbase.thirdparty.com.google.common.collect.ListMultimap;
-
 import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotDescription;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;
@@ -200,8 +199,8 @@ public class RestoreSnapshotHelper {
 
     List<RegionInfo> tableRegions = getTableRegions();
 
-    RegionInfo mobRegion = 
MobUtils.getMobRegionInfo(snapshotManifest.getTableDescriptor()
-        .getTableName());
+    RegionInfo mobRegion =
+      
MobUtils.getMobRegionInfo(snapshotManifest.getTableDescriptor().getTableName());
     if (tableRegions != null) {
       // restore the mob region in case
       if (regionNames.contains(mobRegion.getEncodedName())) {
@@ -707,7 +706,9 @@ public class RestoreSnapshotHelper {
           HRegionFileSystem.openRegionFromFileSystem(conf, fs, tableDir, 
newRegionInfo, false) :
           HRegionFileSystem.createRegionOnFileSystem(conf, fs, tableDir, 
newRegionInfo);
 
-        StoreFileTracker tracker = StoreFileTrackerFactory.create(conf, true,
+        Configuration sftConf = StoreUtils.createStoreConfiguration(conf, 
tableDesc,
+          
tableDesc.getColumnFamily(familyFiles.getFamilyName().toByteArray()));
+        StoreFileTracker tracker = StoreFileTrackerFactory.create(sftConf, 
true,
           StoreContext.getBuilder().withFamilyStoreDirectoryPath(familyDir).
             withRegionFileSystem(regionFS).build());
         tracker.set(clonedFiles);
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestCloneSnapshotFromClientCustomSFT.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestCloneSnapshotFromClientCustomSFT.java
new file mode 100644
index 0000000..53b7f58
--- /dev/null
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestCloneSnapshotFromClientCustomSFT.java
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import java.io.IOException;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.TableName;
+import 
org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
+import org.apache.hadoop.hbase.testclassification.ClientTests;
+import org.apache.hadoop.hbase.testclassification.LargeTests;
+import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category({ LargeTests.class, ClientTests.class })
+public class TestCloneSnapshotFromClientCustomSFT extends 
CloneSnapshotFromClientTestBase{
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+    HBaseClassTestRule.forClass(TestCloneSnapshotFromClientCustomSFT.class);
+
+  public static final String CLONE_SFT = "FILE";
+
+  @Test
+  public void testCloneSnapshotWithCustomSFT() throws IOException, 
InterruptedException {
+    TableName clonedTableName =
+      TableName.valueOf(getValidMethodName() + "-" + 
EnvironmentEdgeManager.currentTime());
+
+    admin.cloneSnapshot(snapshotName1, clonedTableName, false, CLONE_SFT);
+    verifyRowCount(TEST_UTIL, clonedTableName, snapshot1Rows);
+
+    TableDescriptor td = admin.getDescriptor(clonedTableName);
+    assertEquals(CLONE_SFT, td.getValue(StoreFileTrackerFactory.TRACKER_IMPL));
+
+    TEST_UTIL.deleteTable(clonedTableName);
+  }
+
+  @Test
+  public void testCloneSnapshotWithIncorrectCustomSFT() throws IOException, 
InterruptedException {
+    TableName clonedTableName =
+      TableName.valueOf(getValidMethodName() + "-" + 
EnvironmentEdgeManager.currentTime());
+
+    IOException ioException = assertThrows(IOException.class, () -> {
+      admin.cloneSnapshot(snapshotName1, clonedTableName, false, 
"IncorrectSFT");
+    });
+
+    assertEquals(
+      "java.lang.RuntimeException: java.lang.RuntimeException: " +
+        "java.lang.ClassNotFoundException: Class IncorrectSFT not found",
+      ioException.getMessage());
+  }
+}
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/storefiletracker/TestStoreFileTracker.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/storefiletracker/TestStoreFileTracker.java
index 9818972..fc54eb0 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/storefiletracker/TestStoreFileTracker.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/storefiletracker/TestStoreFileTracker.java
@@ -20,7 +20,6 @@ package org.apache.hadoop.hbase.regionserver.storefiletracker;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -28,7 +27,6 @@ import java.util.Map;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.regionserver.StoreContext;
 import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
-import 
org.apache.hbase.thirdparty.org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/storefiletracker/TestStoreFileTrackerFactory.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/storefiletracker/TestStoreFileTrackerFactory.java
index 41f2afd..91038e9 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/storefiletracker/TestStoreFileTrackerFactory.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/storefiletracker/TestStoreFileTrackerFactory.java
@@ -22,9 +22,16 @@ import static org.junit.Assert.assertThrows;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.HBaseClassTestRule;
 import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
+import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
+import org.apache.hadoop.hbase.client.TableDescriptor;
+import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
 import org.apache.hadoop.hbase.regionserver.StoreContext;
+import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
 import org.apache.hadoop.hbase.testclassification.RegionServerTests;
 import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.apache.hadoop.hbase.util.Bytes;
 import org.junit.ClassRule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
@@ -55,4 +62,49 @@ public class TestStoreFileTrackerFactory {
     assertThrows(IllegalArgumentException.class, () -> StoreFileTrackerFactory
       .createForMigration(conf, configName, false, 
StoreContext.getBuilder().build()));
   }
+
+  @Test
+  public void testCheckSFTCompatibility() throws Exception {
+    //checking default value change on different configuration levels
+    Configuration conf = new Configuration();
+    conf.set(StoreFileTrackerFactory.TRACKER_IMPL, "DEFAULT");
+
+    //creating a TD with only TableDescriptor level config
+    TableDescriptorBuilder builder = 
TableDescriptorBuilder.newBuilder(TableName.valueOf("TableX"));
+    builder.setValue(StoreFileTrackerFactory.TRACKER_IMPL, "FILE");
+    ColumnFamilyDescriptor cf = ColumnFamilyDescriptorBuilder.of("cf");
+    builder.setColumnFamily(cf);
+    TableDescriptor td = builder.build();
+
+    //creating a TD with matching ColumnFamilyDescriptor level setting
+    TableDescriptorBuilder snapBuilder =
+      TableDescriptorBuilder.newBuilder(TableName.valueOf("TableY"));
+    snapBuilder.setValue(StoreFileTrackerFactory.TRACKER_IMPL, "FILE");
+    ColumnFamilyDescriptorBuilder snapCFBuilder =
+      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf"));
+    snapCFBuilder.setValue(StoreFileTrackerFactory.TRACKER_IMPL, "FILE");
+    snapBuilder.setColumnFamily(snapCFBuilder.build());
+    TableDescriptor snapTd = snapBuilder.build();
+
+    // adding a cf config that matches the td config is fine even when it does 
not match the default
+    StoreFileTrackerFactory.validatePreRestoreSnapshot(td, snapTd, conf);
+    // removing cf level config is fine when it matches the td config
+    StoreFileTrackerFactory.validatePreRestoreSnapshot(snapTd, td, conf);
+
+    TableDescriptorBuilder defaultBuilder =
+      TableDescriptorBuilder.newBuilder(TableName.valueOf("TableY"));
+    defaultBuilder.setValue(StoreFileTrackerFactory.TRACKER_IMPL, "FILE");
+    ColumnFamilyDescriptorBuilder defaultCFBuilder =
+      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf"));
+    defaultCFBuilder.setValue(StoreFileTrackerFactory.TRACKER_IMPL, "DEFAULT");
+    defaultBuilder.setColumnFamily(defaultCFBuilder.build());
+    TableDescriptor defaultTd = defaultBuilder.build();
+
+    assertThrows(RestoreSnapshotException.class, () -> {
+      StoreFileTrackerFactory.validatePreRestoreSnapshot(td, defaultTd, conf);
+    });
+    assertThrows(RestoreSnapshotException.class, () -> {
+      StoreFileTrackerFactory.validatePreRestoreSnapshot(snapTd, defaultTd, 
conf);
+    });
+  }
 }
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdmin.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdmin.java
index c5911f2..8b1a388 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdmin.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdmin.java
@@ -546,8 +546,9 @@ public class VerifyingRSGroupAdmin implements Admin, 
Closeable {
   }
 
   public Future<Void> cloneSnapshotAsync(String snapshotName, TableName 
tableName,
-    boolean restoreAcl) throws IOException, TableExistsException, 
RestoreSnapshotException {
-    return admin.cloneSnapshotAsync(snapshotName, tableName, restoreAcl);
+    boolean restoreAcl, String customSFT)
+    throws IOException, TableExistsException, RestoreSnapshotException {
+    return admin.cloneSnapshotAsync(snapshotName, tableName, restoreAcl, 
customSFT);
   }
 
   public void execProcedure(String signature, String instance, Map<String, 
String> props)
diff --git a/hbase-shell/src/main/ruby/hbase/admin.rb 
b/hbase-shell/src/main/ruby/hbase/admin.rb
index 53d262a..8f3a264 100644
--- a/hbase-shell/src/main/ruby/hbase/admin.rb
+++ b/hbase-shell/src/main/ruby/hbase/admin.rb
@@ -1259,8 +1259,8 @@ module Hbase
 
     
#----------------------------------------------------------------------------------------------
     # Create a new table by cloning the snapshot content
-    def clone_snapshot(snapshot_name, table, restore_acl = false)
-      @admin.cloneSnapshot(snapshot_name, TableName.valueOf(table), 
restore_acl)
+    def clone_snapshot(snapshot_name, table, restore_acl = false, clone_sft = 
nil)
+      @admin.cloneSnapshot(snapshot_name, TableName.valueOf(table), 
restore_acl, clone_sft)
     end
 
     
#----------------------------------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/hbase_constants.rb 
b/hbase-shell/src/main/ruby/hbase_constants.rb
index 1c79007..5f994c7 100644
--- a/hbase-shell/src/main/ruby/hbase_constants.rb
+++ b/hbase-shell/src/main/ruby/hbase_constants.rb
@@ -40,6 +40,7 @@ module HBaseConstants
   CACHE = 'CACHE'.freeze
   CACHE_BLOCKS = 'CACHE_BLOCKS'.freeze
   CLASSNAME = 'CLASSNAME'.freeze
+  CLONE_SFT = 'CLONE_SFT'.freeze
   CLUSTER_KEY = 'CLUSTER_KEY'.freeze
   COLUMN = 'COLUMN'.freeze
   COLUMNS = 'COLUMNS'.freeze
diff --git a/hbase-shell/src/main/ruby/shell/commands/clone_snapshot.rb 
b/hbase-shell/src/main/ruby/shell/commands/clone_snapshot.rb
index abc97591..3edd16d 100644
--- a/hbase-shell/src/main/ruby/shell/commands/clone_snapshot.rb
+++ b/hbase-shell/src/main/ruby/shell/commands/clone_snapshot.rb
@@ -33,13 +33,17 @@ Following command will restore all acl from origin snapshot 
table into the
 newly created table.
 
   hbase> clone_snapshot 'snapshotName', 'namespace:tableName', 
{RESTORE_ACL=>true}
+
+StoreFileTracker implementation used after restore can be set by the following 
command.
+  hbase> clone_snapshot 'snapshotName', 'namespace:tableName', 
{CLONE_SFT=>'FILE'}
 EOF
       end
 
       def command(snapshot_name, table, args = {})
         raise(ArgumentError, 'Arguments should be a Hash') unless 
args.is_a?(Hash)
         restore_acl = args.delete(::HBaseConstants::RESTORE_ACL) || false
-        admin.clone_snapshot(snapshot_name, table, restore_acl)
+        clone_sft = args.delete(::HBaseConstants::CLONE_SFT) || nil
+        admin.clone_snapshot(snapshot_name, table, restore_acl, clone_sft)
       end
 
       def handle_exceptions(cause, *args)
diff --git 
a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftAdmin.java
 
b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftAdmin.java
index 2b54525..71d3c9e 100644
--- 
a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftAdmin.java
+++ 
b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftAdmin.java
@@ -869,8 +869,8 @@ public class ThriftAdmin implements Admin {
   }
 
   @Override
-  public Future<Void> cloneSnapshotAsync(String snapshotName, TableName 
tableName, boolean cloneAcl)
-      throws IOException, TableExistsException, RestoreSnapshotException {
+  public Future<Void> cloneSnapshotAsync(String snapshotName, TableName 
tableName, boolean cloneAcl,
+    String customSFT) throws IOException, TableExistsException, 
RestoreSnapshotException {
     throw new NotImplementedException("cloneSnapshotAsync not supported in 
ThriftAdmin");
   }
 

Reply via email to