1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 package org.apache.hadoop.hbase.backup.example;
19
20 import java.io.IOException;
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.apache.hadoop.classification.InterfaceAudience;
25 import org.apache.hadoop.conf.Configuration;
26 import org.apache.hadoop.hbase.ZooKeeperConnectionException;
27 import org.apache.hadoop.hbase.client.HConnection;
28 import org.apache.hadoop.hbase.master.cleaner.HFileCleaner;
29 import org.apache.hadoop.hbase.util.Bytes;
30 import org.apache.hadoop.hbase.zookeeper.ZKUtil;
31 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
32 import org.apache.zookeeper.KeeperException;
33
34 /**
35 * Client-side manager for which table's hfiles should be preserved for long-term archive.
36 * @see ZKTableArchiveClient
37 * @see HFileArchiveTableMonitor
38 * @see LongTermArchivingHFileCleaner
39 */
40 @InterfaceAudience.Private
41 class HFileArchiveManager {
42
43 private final String archiveZnode;
44 private static final Log LOG = LogFactory.getLog(HFileArchiveManager.class);
45 private final ZooKeeperWatcher zooKeeper;
46 private volatile boolean stopped = false;
47
48 public HFileArchiveManager(HConnection connection, Configuration conf)
49 throws ZooKeeperConnectionException, IOException {
50 this.zooKeeper = new ZooKeeperWatcher(conf, "hfileArchiveManager-on-" + connection.toString(),
51 connection);
52 this.archiveZnode = ZKTableArchiveClient.getArchiveZNode(this.zooKeeper.getConfiguration(),
53 this.zooKeeper);
54 }
55
56 /**
57 * Turn on auto-backups of HFiles on the specified table.
58 * <p>
59 * When HFiles would be deleted from the hfile archive, they are instead preserved.
60 * @param table name of the table for which to preserve hfiles.
61 * @return <tt>this</tt> for chaining.
62 * @throws KeeperException if we can't reach zookeeper to update the hfile cleaner.
63 */
64 public HFileArchiveManager enableHFileBackup(byte[] table) throws KeeperException {
65 enable(this.zooKeeper, table);
66 return this;
67 }
68
69 /**
70 * Stop retaining HFiles for the given table in the archive. HFiles will be cleaned up on the next
71 * pass of the {@link HFileCleaner}, if the HFiles are retained by another cleaner.
72 * @param table name of the table for which to disable hfile retention.
73 * @return <tt>this</tt> for chaining.
74 * @throws KeeperException if if we can't reach zookeeper to update the hfile cleaner.
75 */
76 public HFileArchiveManager disableHFileBackup(byte[] table) throws KeeperException {
77 disable(this.zooKeeper, table);
78 return this;
79 }
80
81 /**
82 * Disable long-term archival of all hfiles for all tables in the cluster.
83 * @return <tt>this</tt> for chaining.
84 * @throws IOException if the number of attempts is exceeded
85 */
86 public HFileArchiveManager disableHFileBackup() throws IOException {
87 LOG.debug("Disabling backups on all tables.");
88 try {
89 ZKUtil.deleteNodeRecursively(this.zooKeeper, archiveZnode);
90 return this;
91 } catch (KeeperException e) {
92 throw new IOException("Unexpected ZK exception!", e);
93 }
94 }
95
96 /**
97 * Perform a best effort enable of hfile retention, which relies on zookeeper communicating the //
98 * * change back to the hfile cleaner.
99 * <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
105 */
106 private void enable(ZooKeeperWatcher zooKeeper, byte[] table)
107 throws KeeperException {
108 LOG.debug("Ensuring archiving znode exists");
109 ZKUtil.createAndFailSilent(zooKeeper, archiveZnode);
110
111 // then add the table to the list of znodes to archive
112 String tableNode = this.getTableNode(table);
113 LOG.debug("Creating: " + tableNode + ", data: []");
114 ZKUtil.createSetData(zooKeeper, tableNode, new byte[0]);
115 }
116
117 /**
118 * Disable all archiving of files for a given table
119 * <p>
120 * Inherently an <b>asynchronous operation</b>.
121 * @param zooKeeper watcher for the ZK cluster
122 * @param table name of the table to disable
123 * @throws KeeperException if an unexpected ZK connection issues occurs
124 */
125 private void disable(ZooKeeperWatcher zooKeeper, byte[] table) throws KeeperException {
126 // ensure the latest state of the archive node is found
127 zooKeeper.sync(archiveZnode);
128
129 // if the top-level archive node is gone, then we are done
130 if (ZKUtil.checkExists(zooKeeper, archiveZnode) < 0) {
131 return;
132 }
133 // delete the table node, from the archive
134 String tableNode = this.getTableNode(table);
135 // make sure the table is the latest version so the delete takes
136 zooKeeper.sync(tableNode);
137
138 LOG.debug("Attempting to delete table node:" + tableNode);
139 ZKUtil.deleteNodeRecursively(zooKeeper, tableNode);
140 }
141
142 public void stop() {
143 if (!this.stopped) {
144 this.stopped = true;
145 LOG.debug("Stopping HFileArchiveManager...");
146 this.zooKeeper.close();
147 }
148 }
149
150 /**
151 * Check to see if the table is currently marked for archiving
152 * @param table name of the table to check
153 * @return <tt>true</tt> if the archive znode for that table exists, <tt>false</tt> if not
154 * @throws KeeperException if an unexpected zookeeper error occurs
155 */
156 public boolean isArchivingEnabled(byte[] table) throws KeeperException {
157 String tableNode = this.getTableNode(table);
158 return ZKUtil.checkExists(zooKeeper, tableNode) >= 0;
159 }
160
161 /**
162 * Get the zookeeper node associated with archiving the given table
163 * @param table name of the table to check
164 * @return znode for the table's archive status
165 */
166 private String getTableNode(byte[] table) {
167 return ZKUtil.joinZNode(archiveZnode, Bytes.toString(table));
168 }
169 }