org.apache.hadoop.hbase.backup.example.TableHFileArchiveTracker Maven / Gradle / Ivy
Show all versions of hbase-server Show documentation
/*
* 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.backup.example;
import java.io.IOException;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.zookeeper.ZKListener;
import org.apache.hadoop.hbase.zookeeper.ZKUtil;
import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
import org.apache.yetus.audience.InterfaceAudience;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Track HFile archiving state changes in ZooKeeper. Keeps track of the tables whose HFiles should
* be kept in the archive.
*
* {@link TableHFileArchiveTracker#start()} needs to be called to start monitoring for tables to
* archive.
*/
@InterfaceAudience.Private
public final class TableHFileArchiveTracker extends ZKListener {
private static final Logger LOG = LoggerFactory.getLogger(TableHFileArchiveTracker.class);
public static final String HFILE_ARCHIVE_ZNODE_PARENT = "hfilearchive";
private HFileArchiveTableMonitor monitor;
private String archiveHFileZNode;
private boolean stopped = false;
private TableHFileArchiveTracker(ZKWatcher watcher, HFileArchiveTableMonitor monitor) {
super(watcher);
watcher.registerListener(this);
this.monitor = monitor;
this.archiveHFileZNode =
ZKTableArchiveClient.getArchiveZNode(watcher.getConfiguration(), watcher);
}
/**
* Start monitoring for archive updates
* @throws KeeperException on failure to find/create nodes
*/
public void start() throws KeeperException {
// if archiving is enabled, then read in the list of tables to archive
LOG.debug("Starting hfile archive tracker...");
this.checkEnabledAndUpdate();
LOG.debug("Finished starting hfile archive tracker!");
}
@Override
public void nodeCreated(String path) {
// if it is the archive path
if (!path.startsWith(archiveHFileZNode)) {
return;
}
LOG.debug("Archive node: " + path + " created");
// since we are already enabled, just update a single table
String table = path.substring(archiveHFileZNode.length());
// the top level node has come up, so read in all the tables
if (table.length() == 0) {
checkEnabledAndUpdate();
return;
}
// find the table that needs to be archived
try {
addAndReWatchTable(path);
} catch (KeeperException e) {
LOG.warn(
"Couldn't read zookeeper data for table for path:" + path + ", not preserving a table.", e);
}
}
@Override
public void nodeChildrenChanged(String path) {
if (!path.startsWith(archiveHFileZNode)) {
return;
}
LOG.debug("Archive node: " + path + " children changed.");
// a table was added to the archive
try {
updateWatchedTables();
} catch (KeeperException e) {
LOG.error("Failed to update tables to archive", e);
}
}
/**
* Add this table to the tracker and then read a watch on that node.
*
* Handles situation where table is deleted in the time between the update and resetting the watch
* by deleting the table via {@link #safeStopTrackingTable(String)}
* @param tableZnode full zookeeper path to the table to be added
* @throws KeeperException if an unexpected zk exception occurs
*/
private void addAndReWatchTable(String tableZnode) throws KeeperException {
getMonitor().addTable(ZKUtil.getNodeName(tableZnode));
// re-add a watch to the table created
// and check to make sure it wasn't deleted
if (!ZKUtil.watchAndCheckExists(watcher, tableZnode)) {
safeStopTrackingTable(tableZnode);
}
}
/**
* Stop tracking a table. Ensures that the table doesn't exist, but if it does, it attempts to add
* the table back via {@link #addAndReWatchTable(String)} - its a 'safe' removal.
* @param tableZnode full zookeeper path to the table to be added
* @throws KeeperException if an unexpected zk exception occurs
*/
private void safeStopTrackingTable(String tableZnode) throws KeeperException {
getMonitor().removeTable(ZKUtil.getNodeName(tableZnode));
// if the table exists, then add and rewatch it
if (ZKUtil.checkExists(watcher, tableZnode) >= 0) {
addAndReWatchTable(tableZnode);
}
}
@Override
public void nodeDeleted(String path) {
if (!path.startsWith(archiveHFileZNode)) {
return;
}
LOG.debug("Archive node: " + path + " deleted");
String table = path.substring(archiveHFileZNode.length());
// if we stop archiving all tables
if (table.length() == 0) {
// make sure we have the tracker before deleting the archive
// but if we don't, we don't care about delete
clearTables();
// watches are one-time events, so we need to renew our subscription to
// the archive node and might as well check to make sure archiving
// didn't come back on at the same time
checkEnabledAndUpdate();
return;
}
// just stop archiving one table
// note that we don't attempt to add another watch for that table into zk.
// We have no assurances that the table will be archived again (or even
// exists for that matter), so its better not to add unnecessary load to
// zk for watches. If the table is created again, then we will get the
// notification in childrenChanaged.
getMonitor().removeTable(ZKUtil.getNodeName(path));
}
/**
* Sets the watch on the top-level archive znode, and then updates the monitor with the current
* tables that should be archived (and ensures that those nodes are watched as well).
*/
private void checkEnabledAndUpdate() {
try {
if (ZKUtil.watchAndCheckExists(watcher, archiveHFileZNode)) {
LOG.debug(archiveHFileZNode + " znode does exist, checking for tables to archive");
// update the tables we should backup, to get the most recent state.
// This is safer than also watching for children and then hoping we get
// all the updates as it makes sure we get and watch all the children
updateWatchedTables();
} else {
LOG.debug("Archiving not currently enabled, waiting");
}
} catch (KeeperException e) {
LOG.warn("Failed to watch for archiving znode", e);
}
}
/**
* Read the list of children under the archive znode as table names and then sets those tables to
* the list of tables that we should archive
* @throws KeeperException if there is an unexpected zk exception
*/
private void updateWatchedTables() throws KeeperException {
// get the children and watch for new children
LOG.debug("Updating watches on tables to archive.");
// get the children and add watches for each of the children
List tables = ZKUtil.listChildrenAndWatchThem(watcher, archiveHFileZNode);
LOG.debug("Starting archive for tables:" + tables);
// if archiving is still enabled
if (tables != null && tables.size() > 0) {
getMonitor().setArchiveTables(tables);
} else {
LOG.debug("No tables to archive.");
// only if we currently have a tracker, then clear the archive
clearTables();
}
}
/**
* Remove the currently archived tables.
*
* Does some intelligent checking to make sure we don't prematurely create an archive tracker.
*/
private void clearTables() {
getMonitor().clearArchive();
}
/**
* Determine if the given table should or should not allow its hfiles to be deleted
* @param tableName name of the table to check
* @return true if its store files should be retained, false otherwise
*/
public boolean keepHFiles(String tableName) {
return getMonitor().shouldArchiveTable(tableName);
}
/** Returns the tracker for which tables should be archived. */
public final HFileArchiveTableMonitor getMonitor() {
return this.monitor;
}
/**
* Create an archive tracker for the passed in server
* @param conf to read for zookeeper connection information
* @return ZooKeeper tracker to monitor for this server if this server should archive hfiles for a
* given table
* @throws IOException If a unexpected exception occurs
* @throws ZooKeeperConnectionException if we can't reach zookeeper
*/
public static TableHFileArchiveTracker create(Configuration conf)
throws ZooKeeperConnectionException, IOException {
ZKWatcher zkw = new ZKWatcher(conf, "hfileArchiveCleaner", null);
return create(zkw, new HFileArchiveTableMonitor());
}
/**
* Create an archive tracker with the special passed in table monitor. Should only be used in
* special cases (e.g. testing)
* @param zkw Watcher for the ZooKeeper cluster that we should track
* @param monitor Monitor for which tables need hfile archiving
* @return ZooKeeper tracker to monitor for this server if this server should archive hfiles for a
* given table
*/
private static TableHFileArchiveTracker create(ZKWatcher zkw, HFileArchiveTableMonitor monitor) {
return new TableHFileArchiveTracker(zkw, monitor);
}
public ZKWatcher getZooKeeperWatcher() {
return this.watcher;
}
/**
* Stop this tracker and the passed zookeeper
*/
public void stop() {
if (this.stopped) {
return;
}
this.stopped = true;
this.watcher.close();
}
}