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.backup.example;
019
020import java.io.IOException;
021import org.apache.hadoop.conf.Configuration;
022import org.apache.hadoop.hbase.ZooKeeperConnectionException;
023import org.apache.hadoop.hbase.client.Connection;
024import org.apache.hadoop.hbase.util.Bytes;
025import org.apache.hadoop.hbase.zookeeper.ZKUtil;
026import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
027import org.apache.hadoop.hbase.zookeeper.ZNodePaths;
028import org.apache.yetus.audience.InterfaceAudience;
029import org.apache.zookeeper.KeeperException;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * Client-side manager for which table's hfiles should be preserved for long-term archive.
035 * @see ZKTableArchiveClient
036 * @see HFileArchiveTableMonitor
037 * @see LongTermArchivingHFileCleaner
038 */
039@InterfaceAudience.Private
040class HFileArchiveManager {
041
042  private final String archiveZnode;
043  private static final Logger LOG = LoggerFactory.getLogger(HFileArchiveManager.class);
044  private final ZKWatcher zooKeeper;
045  private volatile boolean stopped = false;
046
047  public HFileArchiveManager(Connection connection, Configuration conf)
048    throws ZooKeeperConnectionException, IOException {
049    this.zooKeeper =
050      new ZKWatcher(conf, "hfileArchiveManager-on-" + connection.toString(), connection);
051    this.archiveZnode =
052      ZKTableArchiveClient.getArchiveZNode(this.zooKeeper.getConfiguration(), this.zooKeeper);
053  }
054
055  /**
056   * Turn on auto-backups of HFiles on the specified table.
057   * <p>
058   * When HFiles would be deleted from the hfile archive, they are instead preserved.
059   * @param table name of the table for which to preserve hfiles.
060   * @return <tt>this</tt> for chaining.
061   * @throws KeeperException if we can't reach zookeeper to update the hfile cleaner.
062   */
063  public HFileArchiveManager enableHFileBackup(byte[] table) throws KeeperException {
064    enable(this.zooKeeper, table);
065    return this;
066  }
067
068  /**
069   * Stop retaining HFiles for the given table in the archive. HFiles will be cleaned up on the next
070   * pass of the {@link org.apache.hadoop.hbase.master.cleaner.HFileCleaner}, if the HFiles are
071   * retained by another cleaner.
072   * @param table name of the table for which to disable hfile retention.
073   * @return <tt>this</tt> for chaining.
074   * @throws KeeperException if if we can't reach zookeeper to update the hfile cleaner.
075   */
076  public HFileArchiveManager disableHFileBackup(byte[] table) throws KeeperException {
077    disable(this.zooKeeper, table);
078    return this;
079  }
080
081  /**
082   * Disable long-term archival of all hfiles for all tables in the cluster.
083   * @return <tt>this</tt> for chaining.
084   * @throws IOException if the number of attempts is exceeded
085   */
086  public HFileArchiveManager disableHFileBackup() throws IOException {
087    LOG.debug("Disabling backups on all tables.");
088    try {
089      ZKUtil.deleteNodeRecursively(this.zooKeeper, archiveZnode);
090      return this;
091    } catch (KeeperException e) {
092      throw new IOException("Unexpected ZK exception!", e);
093    }
094  }
095
096  /**
097   * Perform a best effort enable of hfile retention, which relies on zookeeper communicating the
098   * change back to the hfile cleaner.
099   * <p>
100   * No attempt is made to make sure that backups are successfully created - it is inherently an
101   * <b>asynchronous operation</b>.
102   * @param zooKeeper watcher connection to zk cluster
103   * @param table     table name on which to enable archiving
104   * @throws KeeperException if a ZooKeeper operation fails
105   */
106  private void enable(ZKWatcher zooKeeper, byte[] table) throws KeeperException {
107    LOG.debug("Ensuring archiving znode exists");
108    ZKUtil.createAndFailSilent(zooKeeper, archiveZnode);
109
110    // then add the table to the list of znodes to archive
111    String tableNode = this.getTableNode(table);
112    LOG.debug("Creating: " + tableNode + ", data: []");
113    ZKUtil.createSetData(zooKeeper, tableNode, new byte[0]);
114  }
115
116  /**
117   * Disable all archiving of files for a given table
118   * <p>
119   * Inherently an <b>asynchronous operation</b>.
120   * @param zooKeeper watcher for the ZK cluster
121   * @param table     name of the table to disable
122   * @throws KeeperException if an unexpected ZK connection issues occurs
123   */
124  private void disable(ZKWatcher zooKeeper, byte[] table) throws KeeperException {
125    // ensure the latest state of the archive node is found
126    zooKeeper.syncOrTimeout(archiveZnode);
127
128    // if the top-level archive node is gone, then we are done
129    if (ZKUtil.checkExists(zooKeeper, archiveZnode) < 0) {
130      return;
131    }
132    // delete the table node, from the archive
133    String tableNode = this.getTableNode(table);
134    // make sure the table is the latest version so the delete takes
135    zooKeeper.syncOrTimeout(tableNode);
136
137    LOG.debug("Attempting to delete table node:" + tableNode);
138    ZKUtil.deleteNodeRecursively(zooKeeper, tableNode);
139  }
140
141  public void stop() {
142    if (!this.stopped) {
143      this.stopped = true;
144      LOG.debug("Stopping HFileArchiveManager...");
145      this.zooKeeper.close();
146    }
147  }
148
149  /**
150   * Check to see if the table is currently marked for archiving
151   * @param table name of the table to check
152   * @return <tt>true</tt> if the archive znode for that table exists, <tt>false</tt> if not
153   * @throws KeeperException if an unexpected zookeeper error occurs
154   */
155  public boolean isArchivingEnabled(byte[] table) throws KeeperException {
156    String tableNode = this.getTableNode(table);
157    return ZKUtil.checkExists(zooKeeper, tableNode) >= 0;
158  }
159
160  /**
161   * Get the zookeeper node associated with archiving the given table
162   * @param table name of the table to check
163   * @return znode for the table's archive status
164   */
165  private String getTableNode(byte[] table) {
166    return ZNodePaths.joinZNode(archiveZnode, Bytes.toString(table));
167  }
168}