001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.master.cleaner;
019
020import java.io.IOException;
021import java.util.List;
022import java.util.concurrent.TimeUnit;
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.hbase.ScheduledChore;
025import org.apache.hadoop.hbase.Stoppable;
026import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
027import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
028import org.apache.yetus.audience.InterfaceAudience;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;
033
034/**
035 * This chore, every time it runs, will try to delete snapshots that are expired based on TTL in
036 * seconds configured for each Snapshot
037 */
038@InterfaceAudience.Private
039public class SnapshotCleanerChore extends ScheduledChore {
040
041  private static final Logger LOG = LoggerFactory.getLogger(SnapshotCleanerChore.class);
042  private static final String SNAPSHOT_CLEANER_CHORE_NAME = "SnapshotCleaner";
043  private static final String SNAPSHOT_CLEANER_INTERVAL = "hbase.master.cleaner.snapshot.interval";
044  private static final int SNAPSHOT_CLEANER_DEFAULT_INTERVAL = 1800 * 1000; // Default 30 min
045  private static final String DELETE_SNAPSHOT_EVENT =
046    "Eligible Snapshot for cleanup due to expired TTL.";
047
048  private final SnapshotManager snapshotManager;
049
050  /**
051   * Construct Snapshot Cleaner Chore with parameterized constructor
052   * @param stopper         When {@link Stoppable#isStopped()} is true, this chore will cancel and
053   *                        cleanup
054   * @param configuration   The configuration to set
055   * @param snapshotManager SnapshotManager instance to manage lifecycle of snapshot
056   */
057  public SnapshotCleanerChore(Stoppable stopper, Configuration configuration,
058    SnapshotManager snapshotManager) {
059    super(SNAPSHOT_CLEANER_CHORE_NAME, stopper,
060      configuration.getInt(SNAPSHOT_CLEANER_INTERVAL, SNAPSHOT_CLEANER_DEFAULT_INTERVAL));
061    this.snapshotManager = snapshotManager;
062  }
063
064  @Override
065  protected void chore() {
066    if (LOG.isTraceEnabled()) {
067      LOG.trace("Snapshot Cleaner Chore is starting up...");
068    }
069    try {
070      List<SnapshotProtos.SnapshotDescription> completedSnapshotsList =
071        this.snapshotManager.getCompletedSnapshots();
072      for (SnapshotProtos.SnapshotDescription snapshotDescription : completedSnapshotsList) {
073        long snapshotCreatedTime = snapshotDescription.getCreationTime();
074        long snapshotTtl = snapshotDescription.getTtl();
075        /*
076         * Backward compatibility after the patch deployment on HMaster Any snapshot with ttl 0 is
077         * to be considered as snapshot to keep FOREVER Default ttl value specified by
078         * {@HConstants.DEFAULT_SNAPSHOT_TTL}
079         */
080        if (
081          snapshotCreatedTime > 0 && snapshotTtl > 0
082            && snapshotTtl < TimeUnit.MILLISECONDS.toSeconds(Long.MAX_VALUE)
083        ) {
084          long currentTime = EnvironmentEdgeManager.currentTime();
085          if ((snapshotCreatedTime + TimeUnit.SECONDS.toMillis(snapshotTtl)) < currentTime) {
086            LOG.info("Event: {} Name: {}, CreatedTime: {}, TTL: {}, currentTime: {}",
087              DELETE_SNAPSHOT_EVENT, snapshotDescription.getName(), snapshotCreatedTime,
088              snapshotTtl, currentTime);
089            deleteExpiredSnapshot(snapshotDescription);
090          }
091        }
092      }
093    } catch (IOException e) {
094      LOG.error("Error while cleaning up Snapshots...", e);
095    }
096    if (LOG.isTraceEnabled()) {
097      LOG.trace("Snapshot Cleaner Chore is closing...");
098    }
099  }
100
101  private void deleteExpiredSnapshot(SnapshotProtos.SnapshotDescription snapshotDescription) {
102    try {
103      this.snapshotManager.deleteSnapshot(snapshotDescription);
104    } catch (Exception e) {
105      LOG.error("Error while deleting Snapshot: {}", snapshotDescription.getName(), e);
106    }
107  }
108
109}