This is an automated email from the ASF dual-hosted git repository.
apurtell pushed a commit to branch branch-2
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/branch-2 by this push:
new 3a72cce HBASE-22648 Snapshot TTL (#371)
3a72cce is described below
commit 3a72ccefbda5ab0bdb7bc171a115504127a2d1f6
Author: Viraj Jasani <[email protected]>
AuthorDate: Tue Jul 23 03:33:44 2019 +0530
HBASE-22648 Snapshot TTL (#371)
Signed-off-by: Reid Chan <[email protected]>
Signed-off-by: Andrew Purtell <[email protected]>
Conflicts:
hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/SnapshotDescriptionUtils.java
---
.../java/org/apache/hadoop/hbase/client/Admin.java | 60 ++++++-
.../hadoop/hbase/client/SnapshotDescription.java | 72 ++++++--
.../hadoop/hbase/shaded/protobuf/ProtobufUtil.java | 16 +-
.../snapshot/ClientSnapshotDescriptionUtils.java | 13 +-
.../java/org/apache/hadoop/hbase/HConstants.java | 9 +
hbase-common/src/main/resources/hbase-default.xml | 19 ++
.../src/main/protobuf/Snapshot.proto | 1 +
hbase-protocol/src/main/protobuf/HBase.proto | 1 +
.../org/apache/hadoop/hbase/master/HMaster.java | 13 ++
.../hbase/master/cleaner/SnapshotCleanerChore.java | 110 ++++++++++++
.../hbase/snapshot/SnapshotDescriptionUtils.java | 19 ++
.../apache/hadoop/hbase/snapshot/SnapshotInfo.java | 11 +-
.../resources/hbase-webapps/master/snapshot.jsp | 10 ++
.../hbase-webapps/master/snapshotsStats.jsp | 8 +
.../hbase/client/TestSnapshotFromClient.java | 2 +-
.../client/TestSnapshotTemporaryDirectory.java | 5 +-
.../master/cleaner/TestSnapshotCleanerChore.java | 199 +++++++++++++++++++++
hbase-shell/src/main/ruby/hbase/admin.rb | 8 +-
src/main/asciidoc/_chapters/hbase-default.adoc | 30 ++++
src/main/asciidoc/_chapters/ops_mgt.adoc | 41 +++++
20 files changed, 608 insertions(+), 39 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 e6ccc77..35e1a95 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
@@ -2069,13 +2069,59 @@ public interface Admin extends Abortable, Closeable {
}
/**
- * Take a snapshot and wait for the server to complete that snapshot
(blocking). Only a single
- * snapshot should be taken at a time for an instance of HBase, or results
may be undefined (you
- * can tell multiple HBase clusters to snapshot at the same time, but only
one at a time for a
- * single cluster). Snapshots are considered unique based on <b>the name of
the snapshot</b>.
- * Attempts to take a snapshot with the same name (even a different type or
with different
- * parameters) will fail with a {@link SnapshotCreationException} indicating
the duplicate naming.
- * Snapshot names follow the same naming constraints as tables in HBase. See
+ * Create typed snapshot of the table. Snapshots are considered unique based
on <b>the name of the
+ * snapshot</b>. Snapshots are taken sequentially even when requested
concurrently, across
+ * all tables. Attempts to take a snapshot with the same name (even a
different type or with
+ * different parameters) will fail with a {@link SnapshotCreationException}
indicating the
+ * duplicate naming. Snapshot names follow the same naming constraints as
tables in HBase. See
+ * {@link
org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}.
+ * Snapshot can live with ttl seconds.
+ *
+ * @param snapshotName name to give the snapshot on the filesystem. Must be
unique from all other
+ * snapshots stored on the cluster
+ * @param tableName name of the table to snapshot
+ * @param type type of snapshot to take
+ * @param snapshotProps snapshot additional properties e.g. TTL
+ * @throws IOException we fail to reach the master
+ * @throws SnapshotCreationException if snapshot creation failed
+ * @throws IllegalArgumentException if the snapshot request is formatted
incorrectly
+ */
+ default void snapshot(String snapshotName, TableName tableName, SnapshotType
type,
+ Map<String, Object> snapshotProps) throws IOException,
+ SnapshotCreationException, IllegalArgumentException {
+ snapshot(new SnapshotDescription(snapshotName, tableName, type,
snapshotProps));
+ }
+
+ /**
+ * Create typed snapshot of the table. Snapshots are considered unique based
on <b>the name of the
+ * snapshot</b>. Snapshots are taken sequentially even when requested
concurrently, across
+ * all tables. Attempts to take a snapshot with the same name (even a
different type or with
+ * different parameters) will fail with a {@link SnapshotCreationException}
indicating the
+ * duplicate naming. Snapshot names follow the same naming constraints as
tables in HBase. See
+ * {@link
org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}.
+ * Snapshot can live with ttl seconds.
+ *
+ * @param snapshotName name to give the snapshot on the filesystem. Must be
unique from all other
+ * snapshots stored on the cluster
+ * @param tableName name of the table to snapshot
+ * @param snapshotProps snapshot additional properties e.g. TTL
+ * @throws IOException we fail to reach the master
+ * @throws SnapshotCreationException if snapshot creation failed
+ * @throws IllegalArgumentException if the snapshot request is formatted
incorrectly
+ */
+ default void snapshot(String snapshotName, TableName tableName,
+ Map<String, Object> snapshotProps) throws IOException,
+ SnapshotCreationException, IllegalArgumentException {
+ snapshot(new SnapshotDescription(snapshotName, tableName,
SnapshotType.FLUSH, snapshotProps));
+ }
+
+ /**
+ * Take a snapshot and wait for the server to complete that snapshot
(blocking). Snapshots are
+ * considered unique based on <b>the name of the snapshot</b>. Snapshots are
taken sequentially
+ * even when requested concurrently, across all tables. Attempts to take a
snapshot with the same
+ * name (even a different type or with different parameters) will fail with a
+ * {@link SnapshotCreationException} indicating the duplicate naming.
Snapshot names follow the
+ * same naming constraints as tables in HBase. See
* {@link
org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}. You
should
* probably use {@link #snapshot(String, org.apache.hadoop.hbase.TableName)}
or
* {@link #snapshot(byte[], org.apache.hadoop.hbase.TableName)} unless you
are sure about the type
diff --git
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/SnapshotDescription.java
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/SnapshotDescription.java
index 0b6f196..4fa825e 100644
---
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/SnapshotDescription.java
+++
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/SnapshotDescription.java
@@ -17,9 +17,13 @@
*/
package org.apache.hadoop.hbase.client;
+import java.util.Map;
+
import org.apache.hadoop.hbase.TableName;
import org.apache.yetus.audience.InterfaceAudience;
+import org.apache.hbase.thirdparty.org.apache.commons.collections4.MapUtils;
+
/**
* The POJO equivalent of HBaseProtos.SnapshotDescription
*/
@@ -30,6 +34,7 @@ public class SnapshotDescription {
private final SnapshotType snapShotType;
private final String owner;
private final long creationTime;
+ private final long ttl;
private final int version;
public SnapshotDescription(String name) {
@@ -48,7 +53,7 @@ public class SnapshotDescription {
}
public SnapshotDescription(String name, TableName table) {
- this(name, table, SnapshotType.DISABLED, null);
+ this(name, table, SnapshotType.DISABLED, null, -1, -1, null);
}
/**
@@ -63,14 +68,14 @@ public class SnapshotDescription {
}
public SnapshotDescription(String name, TableName table, SnapshotType type) {
- this(name, table, type, null);
+ this(name, table, type, null, -1, -1, null);
}
/**
- * @deprecated since 2.0.0 and will be removed in 3.0.0. Use the version
with the TableName
- * instance instead.
* @see #SnapshotDescription(String, TableName, SnapshotType, String)
* @see <a
href="https://issues.apache.org/jira/browse/HBASE-16892">HBASE-16892</a>
+ * @deprecated since 2.0.0 and will be removed in 3.0.0. Use the version
with the TableName
+ * instance instead.
*/
@Deprecated
public SnapshotDescription(String name, String table, SnapshotType type,
String owner) {
@@ -78,31 +83,60 @@ public class SnapshotDescription {
}
public SnapshotDescription(String name, TableName table, SnapshotType type,
String owner) {
- this(name, table, type, owner, -1, -1);
+ this(name, table, type, owner, -1, -1, null);
}
/**
+ * @see #SnapshotDescription(String, TableName, SnapshotType, String, long,
int, Map)
+ * @see <a
href="https://issues.apache.org/jira/browse/HBASE-16892">HBASE-16892</a>
* @deprecated since 2.0.0 and will be removed in 3.0.0. Use the version
with the TableName
* instance instead.
- * @see #SnapshotDescription(String, TableName, SnapshotType, String, long,
int)
- * @see <a
href="https://issues.apache.org/jira/browse/HBASE-16892">HBASE-16892</a>
*/
@Deprecated
public SnapshotDescription(String name, String table, SnapshotType type,
String owner,
long creationTime, int version) {
- this(name, TableName.valueOf(table), type, owner, creationTime, version);
+ this(name, TableName.valueOf(table), type, owner, creationTime, version,
null);
}
+ /**
+ * SnapshotDescription Parameterized Constructor
+ *
+ * @param name Name of the snapshot
+ * @param table TableName associated with the snapshot
+ * @param type Type of the snapshot - enum SnapshotType
+ * @param owner Snapshot Owner
+ * @param creationTime Creation time for Snapshot
+ * @param version Snapshot Version
+ * @param snapshotProps Additional properties for snapshot e.g. TTL
+ */
public SnapshotDescription(String name, TableName table, SnapshotType type,
String owner,
- long creationTime, int version) {
+ long creationTime, int version, Map<String,
Object> snapshotProps) {
this.name = name;
this.table = table;
this.snapShotType = type;
this.owner = owner;
this.creationTime = creationTime;
+ this.ttl = getTtlFromSnapshotProps(snapshotProps);
this.version = version;
}
+ private long getTtlFromSnapshotProps(Map<String, Object> snapshotProps) {
+ return MapUtils.getLongValue(snapshotProps, "TTL", -1);
+ }
+
+ /**
+ * SnapshotDescription Parameterized Constructor
+ *
+ * @param snapshotName Name of the snapshot
+ * @param tableName TableName associated with the snapshot
+ * @param type Type of the snapshot - enum SnapshotType
+ * @param snapshotProps Additional properties for snapshot e.g. TTL
+ */
+ public SnapshotDescription(String snapshotName, TableName tableName,
SnapshotType type,
+ Map<String, Object> snapshotProps) {
+ this(snapshotName, tableName, type, null, -1, -1, snapshotProps);
+ }
+
public String getName() {
return this.name;
}
@@ -139,15 +173,27 @@ public class SnapshotDescription {
return this.creationTime;
}
+ // get snapshot ttl in sec
+ public long getTtl() {
+ return ttl;
+ }
+
public int getVersion() {
return this.version;
}
@Override
public String toString() {
- return "SnapshotDescription: name = " + ((name != null) ? name : null) +
"/table = "
- + ((table != null) ? table : null) + " /owner = " + ((owner != null) ?
owner : null)
- + (creationTime != -1 ? ("/creationtime = " + creationTime) : "")
- + (version != -1 ? ("/version = " + version) : "");
+ return new StringBuilder("SnapshotDescription: ")
+ .append("name = ")
+ .append(name)
+ .append("/table = ")
+ .append(table)
+ .append(" /owner = ")
+ .append(owner)
+ .append(creationTime != -1 ? ("/creationtime = " + creationTime) :
"")
+ .append(ttl != -1 ? ("/ttl = " + ttl) : "")
+ .append(version != -1 ? ("/version = " + version) : "")
+ .toString();
}
}
diff --git
a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java
b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java
index 336c59c..b3e6e20 100644
---
a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java
+++
b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java
@@ -29,6 +29,7 @@ import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -2933,12 +2934,15 @@ public final class ProtobufUtil {
if (snapshotDesc.getCreationTime() != -1L) {
builder.setCreationTime(snapshotDesc.getCreationTime());
}
+ if (snapshotDesc.getTtl() != -1L &&
+ snapshotDesc.getTtl() <
TimeUnit.MILLISECONDS.toSeconds(Long.MAX_VALUE)) {
+ builder.setTtl(snapshotDesc.getTtl());
+ }
if (snapshotDesc.getVersion() != -1) {
builder.setVersion(snapshotDesc.getVersion());
}
builder.setType(ProtobufUtil.createProtosSnapShotDescType(snapshotDesc.getType()));
- SnapshotProtos.SnapshotDescription snapshot = builder.build();
- return snapshot;
+ return builder.build();
}
/**
@@ -2950,10 +2954,12 @@ public final class ProtobufUtil {
*/
public static SnapshotDescription
createSnapshotDesc(SnapshotProtos.SnapshotDescription snapshotDesc) {
+ final Map<String, Object> snapshotProps = new HashMap<>();
+ snapshotProps.put("TTL", snapshotDesc.getTtl());
return new SnapshotDescription(snapshotDesc.getName(),
- snapshotDesc.hasTable() ? TableName.valueOf(snapshotDesc.getTable()) :
null,
- createSnapshotType(snapshotDesc.getType()), snapshotDesc.getOwner(),
- snapshotDesc.getCreationTime(), snapshotDesc.getVersion());
+ snapshotDesc.hasTable() ?
TableName.valueOf(snapshotDesc.getTable()) : null,
+ createSnapshotType(snapshotDesc.getType()),
snapshotDesc.getOwner(),
+ snapshotDesc.getCreationTime(), snapshotDesc.getVersion(),
snapshotProps);
}
public static RegionLoadStats
createRegionLoadStats(ClientProtos.RegionLoadStats stats) {
diff --git
a/hbase-client/src/main/java/org/apache/hadoop/hbase/snapshot/ClientSnapshotDescriptionUtils.java
b/hbase-client/src/main/java/org/apache/hadoop/hbase/snapshot/ClientSnapshotDescriptionUtils.java
index 5a25c52..106836c 100644
---
a/hbase-client/src/main/java/org/apache/hadoop/hbase/snapshot/ClientSnapshotDescriptionUtils.java
+++
b/hbase-client/src/main/java/org/apache/hadoop/hbase/snapshot/ClientSnapshotDescriptionUtils.java
@@ -61,8 +61,15 @@ public class ClientSnapshotDescriptionUtils {
if (ssd == null) {
return null;
}
- return "{ ss=" + ssd.getName() +
- " table=" + (ssd.hasTable()?TableName.valueOf(ssd.getTable()):"") +
- " type=" + ssd.getType() + " }";
+ return new StringBuilder("{ ss=")
+ .append(ssd.getName())
+ .append(" table=")
+ .append(ssd.hasTable() ? TableName.valueOf(ssd.getTable()) : "")
+ .append(" type=")
+ .append(ssd.getType())
+ .append(" ttl=")
+ .append(ssd.getTtl())
+ .append(" }")
+ .toString();
}
}
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java
b/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java
index 5b3c478..e1977d4 100644
--- a/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java
@@ -1475,6 +1475,15 @@ public final class HConstants {
"hbase.util.default.lossycounting.errorrate";
public static final String NOT_IMPLEMENTED = "Not implemented";
+ // Default TTL - FOREVER
+ public static final long DEFAULT_SNAPSHOT_TTL = 0;
+
+ // User defined Default TTL config key
+ public static final String DEFAULT_SNAPSHOT_TTL_CONFIG_KEY =
"hbase.master.snapshot.ttl";
+
+ public static final String SNAPSHOT_CLEANER_DISABLE =
"hbase.master.cleaner.snapshot.disable";
+
+
private HConstants() {
// Can't be instantiated with this ctor.
}
diff --git a/hbase-common/src/main/resources/hbase-default.xml
b/hbase-common/src/main/resources/hbase-default.xml
index 9135fdf..0ed4ab9 100644
--- a/hbase-common/src/main/resources/hbase-default.xml
+++ b/hbase-common/src/main/resources/hbase-default.xml
@@ -1858,4 +1858,23 @@ possible configurations would overwhelm and obscure the
important.
<description>Default is 5 minutes. Make it 30 seconds for tests. See
HBASE-19794 for some context.</description>
</property>
+ <property>
+ <name>hbase.master.cleaner.snapshot.interval</name>
+ <value>1800000</value>
+ <description>
+ Snapshot Cleanup chore interval in milliseconds.
+ The cleanup thread keeps running at this interval
+ to find all snapshots that are expired based on TTL
+ and delete them.
+ </description>
+ </property>
+ <property>
+ <name>hbase.master.snapshot.ttl</name>
+ <value>0</value>
+ <description>
+ Default Snapshot TTL to be considered when the user does not specify TTL
while
+ creating snapshot. Default value 0 indicates FOREVERE - snapshot should
not be
+ automatically deleted until it is manually deleted
+ </description>
+ </property>
</configuration>
diff --git a/hbase-protocol-shaded/src/main/protobuf/Snapshot.proto
b/hbase-protocol-shaded/src/main/protobuf/Snapshot.proto
index 479e33e..5faa7ab 100644
--- a/hbase-protocol-shaded/src/main/protobuf/Snapshot.proto
+++ b/hbase-protocol-shaded/src/main/protobuf/Snapshot.proto
@@ -44,6 +44,7 @@ message SnapshotDescription {
optional int32 version = 5;
optional string owner = 6;
optional UsersAndPermissions users_and_permissions = 7;
+ optional int64 ttl = 8 [default = 0];
}
message SnapshotFileInfo {
diff --git a/hbase-protocol/src/main/protobuf/HBase.proto
b/hbase-protocol/src/main/protobuf/HBase.proto
index 0dcf363..5cf509d 100644
--- a/hbase-protocol/src/main/protobuf/HBase.proto
+++ b/hbase-protocol/src/main/protobuf/HBase.proto
@@ -177,6 +177,7 @@ message SnapshotDescription {
optional Type type = 4 [default = FLUSH];
optional int32 version = 5;
optional string owner = 6;
+ optional int64 ttl = 7 [default = 0];
}
/**
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 d450aba..78cf549 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
@@ -114,6 +114,7 @@ import org.apache.hadoop.hbase.master.cleaner.CleanerChore;
import org.apache.hadoop.hbase.master.cleaner.HFileCleaner;
import org.apache.hadoop.hbase.master.cleaner.LogCleaner;
import org.apache.hadoop.hbase.master.cleaner.ReplicationBarrierCleaner;
+import org.apache.hadoop.hbase.master.cleaner.SnapshotCleanerChore;
import org.apache.hadoop.hbase.master.locking.LockManager;
import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan;
import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan.PlanType;
@@ -375,6 +376,7 @@ public class HMaster extends HRegionServer implements
MasterServices {
private RegionNormalizerChore normalizerChore;
private ClusterStatusChore clusterStatusChore;
private ClusterStatusPublisher clusterStatusPublisherChore = null;
+ private SnapshotCleanerChore snapshotCleanerChore = null;
CatalogJanitor catalogJanitorChore;
private LogCleaner logCleaner;
@@ -1434,6 +1436,16 @@ public class HMaster extends HRegionServer implements
MasterServices {
replicationPeerManager);
getChoreService().scheduleChore(replicationBarrierCleaner);
+ final boolean isSnapshotChoreDisabled =
conf.getBoolean(HConstants.SNAPSHOT_CLEANER_DISABLE,
+ false);
+ if (isSnapshotChoreDisabled) {
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("Snapshot Cleaner Chore is disabled. Not starting up the
chore..");
+ }
+ } else {
+ this.snapshotCleanerChore = new SnapshotCleanerChore(this, conf,
getSnapshotManager());
+ getChoreService().scheduleChore(this.snapshotCleanerChore);
+ }
serviceStarted = true;
if (LOG.isTraceEnabled()) {
LOG.trace("Started service threads");
@@ -1551,6 +1563,7 @@ public class HMaster extends HRegionServer implements
MasterServices {
choreService.cancelChore(this.logCleaner);
choreService.cancelChore(this.hfileCleaner);
choreService.cancelChore(this.replicationBarrierCleaner);
+ choreService.cancelChore(this.snapshotCleanerChore);
}
}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/cleaner/SnapshotCleanerChore.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/cleaner/SnapshotCleanerChore.java
new file mode 100644
index 0000000..7e12acd
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/cleaner/SnapshotCleanerChore.java
@@ -0,0 +1,110 @@
+/*
+ * 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.master.cleaner;
+
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.ScheduledChore;
+import org.apache.hadoop.hbase.Stoppable;
+import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
+import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;
+
+/**
+ * This chore, every time it runs, will try to delete snapshots that are
expired based on TTL in
+ * seconds configured for each Snapshot
+ */
[email protected]
+public class SnapshotCleanerChore extends ScheduledChore {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(SnapshotCleanerChore.class);
+ private static final String SNAPSHOT_CLEANER_CHORE_NAME = "SnapshotCleaner";
+ private static final String SNAPSHOT_CLEANER_INTERVAL =
"hbase.master.cleaner.snapshot.interval";
+ private static final int SNAPSHOT_CLEANER_DEFAULT_INTERVAL = 1800 * 1000; //
Default 30 min
+ private static final String DELETE_SNAPSHOT_EVENT =
+ "Eligible Snapshot for cleanup due to expired TTL.";
+
+ private final SnapshotManager snapshotManager;
+
+ /**
+ * Construct Snapshot Cleaner Chore with parameterized constructor
+ *
+ * @param stopper When {@link Stoppable#isStopped()} is true, this chore
will cancel and cleanup
+ * @param configuration The configuration to set
+ * @param snapshotManager SnapshotManager instance to manage lifecycle of
snapshot
+ */
+ public SnapshotCleanerChore(Stoppable stopper, Configuration configuration,
+ SnapshotManager snapshotManager) {
+ super(SNAPSHOT_CLEANER_CHORE_NAME, stopper,
configuration.getInt(SNAPSHOT_CLEANER_INTERVAL,
+ SNAPSHOT_CLEANER_DEFAULT_INTERVAL));
+ this.snapshotManager = snapshotManager;
+ }
+
+ @Override
+ protected void chore() {
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("Snapshot Cleaner Chore is starting up...");
+ }
+ try {
+ List<SnapshotProtos.SnapshotDescription> completedSnapshotsList =
+ this.snapshotManager.getCompletedSnapshots();
+ for (SnapshotProtos.SnapshotDescription snapshotDescription :
completedSnapshotsList) {
+ long snapshotCreatedTime = snapshotDescription.getCreationTime();
+ long snapshotTtl = snapshotDescription.getTtl();
+ /*
+ * Backward compatibility after the patch deployment on HMaster
+ * Any snapshot with ttl 0 is to be considered as snapshot to keep
FOREVER
+ * Default ttl value specified by {@HConstants.DEFAULT_SNAPSHOT_TTL}
+ */
+ if (snapshotCreatedTime > 0 && snapshotTtl > 0 &&
+ snapshotTtl < TimeUnit.MILLISECONDS.toSeconds(Long.MAX_VALUE))
{
+ long currentTime = EnvironmentEdgeManager.currentTime();
+ if ((snapshotCreatedTime + TimeUnit.SECONDS.toMillis(snapshotTtl)) <
currentTime) {
+ LOG.info("Event: {} Name: {}, CreatedTime: {}, TTL: {},
currentTime: {}",
+ DELETE_SNAPSHOT_EVENT, snapshotDescription.getName(),
snapshotCreatedTime,
+ snapshotTtl, currentTime);
+ deleteExpiredSnapshot(snapshotDescription);
+ }
+ }
+ }
+ } catch (IOException e) {
+ LOG.error("Error while cleaning up Snapshots...", e);
+ }
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("Snapshot Cleaner Chore is closing...");
+ }
+ }
+
+ private void deleteExpiredSnapshot(SnapshotProtos.SnapshotDescription
snapshotDescription) {
+ try {
+ this.snapshotManager.deleteSnapshot(snapshotDescription);
+ } catch (Exception e) {
+ LOG.error("Error while deleting Snapshot: {}",
snapshotDescription.getName(), e);
+ }
+ }
+
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/SnapshotDescriptionUtils.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/SnapshotDescriptionUtils.java
index 5c25526..bbe2042 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/SnapshotDescriptionUtils.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/SnapshotDescriptionUtils.java
@@ -20,6 +20,7 @@ package org.apache.hadoop.hbase.snapshot;
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
+import java.util.concurrent.TimeUnit;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
@@ -124,6 +125,8 @@ public final class SnapshotDescriptionUtils {
/** Default value if no start time is specified */
public static final long NO_SNAPSHOT_START_TIME_SPECIFIED = 0;
+ // Default value if no ttl is specified for Snapshot
+ private static final long NO_SNAPSHOT_TTL_SPECIFIED = 0;
public static final String MASTER_SNAPSHOT_TIMEOUT_MILLIS =
"hbase.snapshot.master.timeout.millis";
@@ -317,6 +320,22 @@ public final class SnapshotDescriptionUtils {
snapshot = builder.build();
}
+ long ttl = snapshot.getTtl();
+ // set default ttl(sec) if it is not set already or the value is out of
the range
+ if (ttl == SnapshotDescriptionUtils.NO_SNAPSHOT_TTL_SPECIFIED ||
+ ttl > TimeUnit.MILLISECONDS.toSeconds(Long.MAX_VALUE)) {
+ final long defaultSnapshotTtl =
conf.getLong(HConstants.DEFAULT_SNAPSHOT_TTL_CONFIG_KEY,
+ HConstants.DEFAULT_SNAPSHOT_TTL);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Snapshot current TTL value: {} resetting it to default
value: {}", ttl,
+ defaultSnapshotTtl);
+ }
+ ttl = defaultSnapshotTtl;
+ }
+ SnapshotDescription.Builder builder = snapshot.toBuilder();
+ builder.setTtl(ttl);
+ snapshot = builder.build();
+
// set the acl to snapshot if security feature is enabled.
if (isSecurityAvailable(conf)) {
snapshot = writeAclToSnapshotDescription(snapshot, conf);
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/SnapshotInfo.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/SnapshotInfo.java
index 85a45ee..78c7050 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/SnapshotInfo.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/SnapshotInfo.java
@@ -374,12 +374,12 @@ public final class SnapshotInfo extends AbstractHBaseTool
{
// List Available Snapshots
if (listSnapshots) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
- System.out.printf("%-20s | %-20s | %s%n", "SNAPSHOT", "CREATION TIME",
"TABLE NAME");
+ System.out.printf("%-20s | %-20s | %-20s | %s%n", "SNAPSHOT", "CREATION
TIME", "TTL IN SEC",
+ "TABLE NAME");
for (SnapshotDescription desc: getSnapshotList(conf)) {
- System.out.printf("%-20s | %20s | %s%n",
- desc.getName(),
- df.format(new Date(desc.getCreationTime())),
- desc.getTableNameAsString());
+ System.out.printf("%-20s | %20s | %20s | %s%n", desc.getName(),
+ df.format(new Date(desc.getCreationTime())), desc.getTtl(),
+ desc.getTableNameAsString());
}
return 0;
}
@@ -432,6 +432,7 @@ public final class SnapshotInfo extends AbstractHBaseTool {
System.out.println(" Table: " + snapshotDesc.getTable());
System.out.println(" Format: " + snapshotDesc.getVersion());
System.out.println("Created: " + df.format(new
Date(snapshotDesc.getCreationTime())));
+ System.out.println(" Ttl: " + snapshotDesc.getTtl());
System.out.println(" Owner: " + snapshotDesc.getOwner());
System.out.println();
}
diff --git a/hbase-server/src/main/resources/hbase-webapps/master/snapshot.jsp
b/hbase-server/src/main/resources/hbase-webapps/master/snapshot.jsp
index 1ea2b40..fc75ca7 100644
--- a/hbase-server/src/main/resources/hbase-webapps/master/snapshot.jsp
+++ b/hbase-server/src/main/resources/hbase-webapps/master/snapshot.jsp
@@ -36,6 +36,7 @@
SnapshotInfo.SnapshotStats stats = null;
TableName snapshotTable = null;
boolean tableExists = false;
+ long snapshotTtl = 0;
if(snapshotName != null && master.isInitialized()) {
try (Admin admin = master.getConnection().getAdmin()) {
for (SnapshotDescription snapshotDesc: admin.listSnapshots()) {
@@ -43,6 +44,7 @@
snapshot = snapshotDesc;
stats = SnapshotInfo.getSnapshotStats(conf, snapshot);
snapshotTable = snapshot.getTableName();
+ snapshotTtl = snapshot.getTtl();
tableExists = admin.tableExists(snapshotTable);
break;
}
@@ -91,6 +93,7 @@
<tr>
<th>Table</th>
<th>Creation Time</th>
+ <th>Time To Live(Sec)</th>
<th>Type</th>
<th>Format Version</th>
<th>State</th>
@@ -106,6 +109,13 @@
<% } %>
</td>
<td><%= new Date(snapshot.getCreationTime()) %></td>
+ <td>
+ <% if (snapshotTtl == 0) { %>
+ FOREVER
+ <% } else { %>
+ <%= snapshotTtl %>
+ <% } %>
+ </td>
<td><%= snapshot.getType() %></td>
<td><%= snapshot.getVersion() %></td>
<% if (stats.isSnapshotCorrupted()) { %>
diff --git
a/hbase-server/src/main/resources/hbase-webapps/master/snapshotsStats.jsp
b/hbase-server/src/main/resources/hbase-webapps/master/snapshotsStats.jsp
index 719ab4f..d270eb8 100644
--- a/hbase-server/src/main/resources/hbase-webapps/master/snapshotsStats.jsp
+++ b/hbase-server/src/main/resources/hbase-webapps/master/snapshotsStats.jsp
@@ -65,6 +65,7 @@
<th>Snapshot Name</th>
<th>Table</th>
<th>Creation Time</th>
+ <th>TTL(Sec)</th>
<th>Shared Storefile Size</th>
<th>Mob Storefile Size</th>
<th>Archived Storefile Size</th>
@@ -82,6 +83,13 @@
<td><a href="/table.jsp?name=<%= snapshotTable.getNameAsString() %>">
<%= snapshotTable.getNameAsString() %></a></td>
<td><%= new Date(snapshotDesc.getCreationTime()) %></td>
+ <td>
+ <% if (snapshotDesc.getTtl() == 0) { %>
+ FOREVER
+ <% } else { %>
+ <%= snapshotDesc.getTtl() %>
+ <% } %>
+ </td>
<td><%= StringUtils.humanReadableInt(stats.getSharedStoreFilesSize())
%></td>
<td><%= StringUtils.humanReadableInt(stats.getMobStoreFilesSize())
%></td>
<td><%= StringUtils.humanReadableInt(stats.getArchivedStoreFileSize()) %>
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestSnapshotFromClient.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestSnapshotFromClient.java
index b46404f..d2a0eae 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestSnapshotFromClient.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestSnapshotFromClient.java
@@ -228,7 +228,7 @@ public class TestSnapshotFromClient {
byte[] snapshot = Bytes.toBytes(SNAPSHOT_NAME);
admin.snapshot(new SnapshotDescription(SNAPSHOT_NAME, TABLE_NAME,
- SnapshotType.DISABLED, null, -1,
SnapshotManifestV1.DESCRIPTOR_VERSION));
+ SnapshotType.DISABLED, null, -1,
SnapshotManifestV1.DESCRIPTOR_VERSION, null));
LOG.debug("Snapshot completed.");
// make sure we have the snapshot
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestSnapshotTemporaryDirectory.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestSnapshotTemporaryDirectory.java
index 6da06ac..c590215 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestSnapshotTemporaryDirectory.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestSnapshotTemporaryDirectory.java
@@ -473,9 +473,8 @@ public class TestSnapshotTemporaryDirectory {
private void takeSnapshot(TableName tableName, String snapshotName, boolean
disabled)
throws IOException {
SnapshotType type = disabled ? SnapshotType.DISABLED : SnapshotType.FLUSH;
- SnapshotDescription desc =
- new SnapshotDescription(snapshotName, tableName.getNameAsString(),
type, null, -1,
- manifestVersion);
+ SnapshotDescription desc = new SnapshotDescription(snapshotName,
tableName, type, null, -1,
+ manifestVersion, null);
admin.snapshot(desc);
}
}
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/cleaner/TestSnapshotCleanerChore.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/cleaner/TestSnapshotCleanerChore.java
new file mode 100644
index 0000000..e05213d
--- /dev/null
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/cleaner/TestSnapshotCleanerChore.java
@@ -0,0 +1,199 @@
+/*
+ * 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.master.cleaner;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.Stoppable;
+import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
+import org.apache.hadoop.hbase.testclassification.MasterTests;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;
+
+
+/**
+ * Tests for SnapshotsCleanerChore
+ */
+@Category({MasterTests.class, SmallTests.class})
+public class TestSnapshotCleanerChore {
+
+ @ClassRule
+ public static final HBaseClassTestRule CLASS_RULE =
+ HBaseClassTestRule.forClass(TestSnapshotCleanerChore.class);
+
+ private static final Logger LOG =
LoggerFactory.getLogger(TestSnapshotCleanerChore.class);
+
+ private static final HBaseTestingUtility HBASE_TESTING_UTILITY = new
HBaseTestingUtility();
+
+ private SnapshotManager snapshotManager;
+
+ private Configuration getSnapshotCleanerConf() {
+ Configuration conf = HBASE_TESTING_UTILITY.getConfiguration();
+ conf.setInt("hbase.master.cleaner.snapshot.interval", 100);
+ return conf;
+ }
+
+
+ @Test
+ public void testSnapshotCleanerWithoutAnyCompletedSnapshot() throws
IOException {
+ snapshotManager = Mockito.mock(SnapshotManager.class);
+ Stoppable stopper = new StoppableImplementation();
+ Configuration conf = getSnapshotCleanerConf();
+ SnapshotCleanerChore snapshotCleanerChore =
+ new SnapshotCleanerChore(stopper, conf, snapshotManager);
+ try {
+ snapshotCleanerChore.chore();
+ } finally {
+ stopper.stop("Stopping Test Stopper");
+ }
+ Mockito.verify(snapshotManager,
Mockito.times(0)).deleteSnapshot(Mockito.any());
+ }
+
+ @Test
+ public void testSnapshotCleanerWithNoTtlExpired() throws IOException {
+ snapshotManager = Mockito.mock(SnapshotManager.class);
+ Stoppable stopper = new StoppableImplementation();
+ Configuration conf = getSnapshotCleanerConf();
+ SnapshotCleanerChore snapshotCleanerChore =
+ new SnapshotCleanerChore(stopper, conf, snapshotManager);
+ List<SnapshotProtos.SnapshotDescription> snapshotDescriptionList = new
ArrayList<>();
+ snapshotDescriptionList.add(getSnapshotDescription(-2, "snapshot01",
"table01",
+ EnvironmentEdgeManager.currentTime() - 100000));
+ snapshotDescriptionList.add(getSnapshotDescription(10, "snapshot02",
"table02",
+ EnvironmentEdgeManager.currentTime()));
+
Mockito.when(snapshotManager.getCompletedSnapshots()).thenReturn(snapshotDescriptionList);
+ try {
+ LOG.info("2 Snapshots are completed but TTL is not expired for any of
them");
+ snapshotCleanerChore.chore();
+ } finally {
+ stopper.stop("Stopping Test Stopper");
+ }
+ Mockito.verify(snapshotManager,
Mockito.times(0)).deleteSnapshot(Mockito.any());
+ }
+
+ @Test
+ public void testSnapshotCleanerWithSomeTtlExpired() throws IOException {
+ snapshotManager = Mockito.mock(SnapshotManager.class);
+ Stoppable stopper = new StoppableImplementation();
+ Configuration conf = getSnapshotCleanerConf();
+ conf.setStrings("hbase.master.cleaner.snapshot.disable", "false");
+ SnapshotCleanerChore snapshotCleanerChore =
+ new SnapshotCleanerChore(stopper, conf, snapshotManager);
+ List<SnapshotProtos.SnapshotDescription> snapshotDescriptionList = new
ArrayList<>();
+ snapshotDescriptionList.add(getSnapshotDescription(10, "snapshot01",
"table01", 1));
+ snapshotDescriptionList.add(getSnapshotDescription(5, "snapshot02",
"table02", 2));
+ snapshotDescriptionList.add(getSnapshotDescription(30, "snapshot01",
"table01",
+ EnvironmentEdgeManager.currentTime()));
+ snapshotDescriptionList.add(getSnapshotDescription(0, "snapshot02",
"table02",
+ EnvironmentEdgeManager.currentTime()));
+ snapshotDescriptionList.add(getSnapshotDescription(40, "snapshot03",
"table03",
+ EnvironmentEdgeManager.currentTime()));
+
Mockito.when(snapshotManager.getCompletedSnapshots()).thenReturn(snapshotDescriptionList);
+ try {
+ LOG.info("5 Snapshots are completed. TTL is expired for 2 them. Going to
delete them");
+ snapshotCleanerChore.chore();
+ } finally {
+ stopper.stop("Stopping Test Stopper");
+ }
+ Mockito.verify(snapshotManager,
Mockito.times(2)).deleteSnapshot(Mockito.any());
+ }
+
+ @Test
+ public void testSnapshotCleanerWithReadIOE() throws IOException {
+ snapshotManager = Mockito.mock(SnapshotManager.class);
+ Stoppable stopper = new StoppableImplementation();
+ Configuration conf = new HBaseTestingUtility().getConfiguration();
+ SnapshotCleanerChore snapshotCleanerChore =
+ new SnapshotCleanerChore(stopper, conf, snapshotManager);
+
Mockito.when(snapshotManager.getCompletedSnapshots()).thenThrow(IOException.class);
+ try {
+ LOG.info("While getting completed Snapshots, IOException would occur.
Hence, No Snapshot"
+ + " should be deleted");
+ snapshotCleanerChore.chore();
+ } finally {
+ stopper.stop("Stopping Test Stopper");
+ }
+ Mockito.verify(snapshotManager,
Mockito.times(0)).deleteSnapshot(Mockito.any());
+ }
+
+ @Test
+ public void testSnapshotChoreWithTtlOutOfRange() throws IOException {
+ snapshotManager = Mockito.mock(SnapshotManager.class);
+ Stoppable stopper = new StoppableImplementation();
+ Configuration conf = getSnapshotCleanerConf();
+ List<SnapshotProtos.SnapshotDescription> snapshotDescriptionList = new
ArrayList<>();
+ snapshotDescriptionList.add(getSnapshotDescription(Long.MAX_VALUE,
"snapshot01", "table01", 1));
+ snapshotDescriptionList.add(getSnapshotDescription(5, "snapshot02",
"table02", 2));
+
Mockito.when(snapshotManager.getCompletedSnapshots()).thenReturn(snapshotDescriptionList);
+ SnapshotCleanerChore snapshotCleanerChore =
+ new SnapshotCleanerChore(stopper, conf, snapshotManager);
+ try {
+ LOG.info("Snapshot Chore is disabled. No cleanup performed for Expired
Snapshots");
+ snapshotCleanerChore.chore();
+ } finally {
+ stopper.stop("Stopping Test Stopper");
+ }
+ Mockito.verify(snapshotManager, Mockito.times(1)).getCompletedSnapshots();
+ }
+
+ private SnapshotProtos.SnapshotDescription getSnapshotDescription(final long
ttl,
+ final String snapshotName, final String tableName, final long
snapshotCreationTime) {
+ SnapshotProtos.SnapshotDescription.Builder snapshotDescriptionBuilder =
+ SnapshotProtos.SnapshotDescription.newBuilder();
+ snapshotDescriptionBuilder.setTtl(ttl);
+ snapshotDescriptionBuilder.setName(snapshotName);
+ snapshotDescriptionBuilder.setTable(tableName);
+
snapshotDescriptionBuilder.setType(SnapshotProtos.SnapshotDescription.Type.FLUSH);
+ snapshotDescriptionBuilder.setCreationTime(snapshotCreationTime);
+ return snapshotDescriptionBuilder.build();
+ }
+
+ /**
+ * Simple helper class that just keeps track of whether or not its stopped.
+ */
+ private static class StoppableImplementation implements Stoppable {
+
+ private volatile boolean stop = false;
+
+ @Override
+ public void stop(String why) {
+ this.stop = true;
+ }
+
+ @Override
+ public boolean isStopped() {
+ return this.stop;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/hbase-shell/src/main/ruby/hbase/admin.rb
b/hbase-shell/src/main/ruby/hbase/admin.rb
index ddce0d9..65a6061 100644
--- a/hbase-shell/src/main/ruby/hbase/admin.rb
+++ b/hbase-shell/src/main/ruby/hbase/admin.rb
@@ -1065,11 +1065,15 @@ module Hbase
@admin.snapshot(snapshot_name, table_name)
else
args.each do |arg|
+ ttl = arg[TTL]
+ ttl = ttl ? ttl.to_java(:long) : -1
+ snapshot_props = java.util.HashMap.new
+ snapshot_props.put("TTL", ttl)
if arg[SKIP_FLUSH] == true
@admin.snapshot(snapshot_name, table_name,
-
org.apache.hadoop.hbase.client.SnapshotType::SKIPFLUSH)
+
org.apache.hadoop.hbase.client.SnapshotType::SKIPFLUSH, snapshot_props)
else
- @admin.snapshot(snapshot_name, table_name)
+ @admin.snapshot(snapshot_name, table_name, snapshot_props)
end
end
end
diff --git a/src/main/asciidoc/_chapters/hbase-default.adoc
b/src/main/asciidoc/_chapters/hbase-default.adoc
index 2bc7a7f..7a5fc25 100644
--- a/src/main/asciidoc/_chapters/hbase-default.adoc
+++ b/src/main/asciidoc/_chapters/hbase-default.adoc
@@ -2131,3 +2131,33 @@ The percent of region server RPC threads failed to abort
RS.
.Default
`0.5`
+
+[[hbase.master.cleaner.snapshot.interval]]
+*`hbase.master.cleaner.snapshot.interval`*::
++
+.Description
+
+ Snapshot Cleanup chore interval in milliseconds.
+ The cleanup thread keeps running at this interval
+ to find all snapshots that are expired based on TTL
+ and delete them.
+
++
+.Default
+`1800000`
+
+
+[[hbase.master.snapshot.ttl]]
+*`hbase.master.snapshot.ttl`*::
++
+.Description
+
+ Default Snapshot TTL to be considered when the user
+ does not specify TTL while creating snapshot.
+ Default value 0 indicates FOREVERE - snapshot should not be
+ automatically deleted until it is manually deleted
+
+
++
+.Default
+`0`
diff --git a/src/main/asciidoc/_chapters/ops_mgt.adoc
b/src/main/asciidoc/_chapters/ops_mgt.adoc
index 878d1f3..e6668d2 100644
--- a/src/main/asciidoc/_chapters/ops_mgt.adoc
+++ b/src/main/asciidoc/_chapters/ops_mgt.adoc
@@ -2726,6 +2726,47 @@ A snapshot is only a representation of a table during a
window of time.
The amount of time the snapshot operation will take to reach each Region
Server may vary from a few seconds to a minute, depending on the resource load
and speed of the hardware or network, among other factors.
There is also no way to know whether a given insert or update is in memory or
has been flushed.
+
+.Take a Snapshot With TTL
+Snapshots have a lifecycle that is independent from the table from which they
are created.
+Although data in a table may be stored with TTL the data files containing them
become
+frozen by the snapshot. Space consumed by expired cells will not be reclaimed
by normal
+table housekeeping like compaction. While this is expected it can be
inconvenient at scale.
+When many snapshots are under management and the data in various tables is
expired by
+TTL some notion of optional TTL (and optional default TTL) for snapshots could
be useful.
+
+
+----
+hbase> snapshot 'mytable', 'snapshot1234', {TTL => 86400}
+----
+
+The above command creates snapshot `snapshot1234` with TTL of 86400 sec(24
hours)
+and hence, the snapshot is supposed to be cleaned up after 24 hours
+
+
+
+.Default Snapshot TTL:
+
+- FOREVER by default
+- User specified Default TTL with config `hbase.master.snapshot.ttl`
+
+
+While creating a Snapshot, if TTL in seconds is not specified, by default the
snapshot
+would not be deleted automatically. i.e. it would be retained forever until it
is
+manually deleted. However, the user can update this default TTL behavior by
+providing default TTL in sec for key: `hbase.master.snapshot.ttl`.
+Value 0 for this config indicates TTL: FOREVER
+
+
+
+
+At any point of time, if Snapshot cleanup is supposed to be stopped due to
+some snapshot restore activity, it is advisable to disable Snapshot Cleaner
with
+ config:
+
+`hbase.master.cleaner.snapshot.disable`: "true"
+
+
[[ops.snapshots.list]]
=== Listing Snapshots