View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.master;
20  
21  import com.google.common.collect.Sets;
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.fs.FileStatus;
26  import org.apache.hadoop.fs.FileSystem;
27  import org.apache.hadoop.fs.Path;
28  import org.apache.hadoop.hbase.Chore;
29  import org.apache.hadoop.hbase.HConstants;
30  import org.apache.hadoop.hbase.NamespaceDescriptor;
31  import org.apache.hadoop.hbase.util.FSUtils;
32  import org.apache.hadoop.hbase.zookeeper.ZKUtil;
33  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
34  import org.apache.zookeeper.KeeperException;
35  
36  import java.io.IOException;
37  import java.util.Set;
38  import java.util.concurrent.atomic.AtomicBoolean;
39  
40  /**
41   * A janitor for the namespace artifacts.
42   * Traverses hdfs and zk to remove orphaned directories/znodes
43   */
44  @InterfaceAudience.Private
45  public class NamespaceJanitor extends Chore {
46    private static final Log LOG = LogFactory.getLog(NamespaceJanitor.class.getName());
47    private final MasterServices services;
48    private AtomicBoolean enabled = new AtomicBoolean(true);
49    private AtomicBoolean alreadyRunning = new AtomicBoolean(false);
50  
51    public NamespaceJanitor(final MasterServices services) {
52      super("NamespaceJanitor-" + services.getServerName().toShortString(),
53        services.getConfiguration().getInt("hbase.namespacejanitor.interval", 30000),
54        services);
55      this.services = services;
56    }
57  
58    @Override
59    protected boolean initialChore() {
60      try {
61        if (this.enabled.get()) removeOrphans();
62      } catch (IOException e) {
63        LOG.warn("Failed NamespaceJanitor chore", e);
64        return false;
65      } catch (KeeperException e) {
66        LOG.warn("Failed NamespaceJanitor chore", e);
67        return false;
68      }
69      return true;
70    }
71  
72    /**
73     * @param enabled
74     */
75    public boolean setEnabled(final boolean enabled) {
76      return this.enabled.getAndSet(enabled);
77    }
78  
79    boolean getEnabled() {
80      return this.enabled.get();
81    }
82  
83    @Override
84    protected void chore() {
85      try {
86        if (this.enabled.get()) {
87          removeOrphans();
88        } else {
89          LOG.warn("NamepsaceJanitor disabled! Not running scan.");
90        }
91      } catch (IOException e) {
92        LOG.warn("Failed NamespaceJanitor chore", e);
93      } catch (KeeperException e) {
94        LOG.warn("Failed NamespaceJanitor chore", e);
95      }
96    }
97  
98    private void removeOrphans() throws IOException, KeeperException {
99      //cache the info so we don't need to keep the master nsLock for long
100     //and not be wasteful with rpc calls
101     FileSystem fs = services.getMasterFileSystem().getFileSystem();
102     Set<String> descs = Sets.newHashSet();
103     for(NamespaceDescriptor ns : services.listNamespaceDescriptors()) {
104       descs.add(ns.getName());
105     }
106 
107     //cleanup hdfs orphans
108     for (FileStatus nsStatus : FSUtils.listStatus(fs,
109         new Path(FSUtils.getRootDir(services.getConfiguration()), HConstants.BASE_NAMESPACE_DIR))) {
110       if (!descs.contains(nsStatus.getPath().getName()) &&
111           !NamespaceDescriptor.RESERVED_NAMESPACES.contains(nsStatus.getPath().getName())) {
112         boolean isEmpty = true;
113         for(FileStatus status : fs.listStatus(nsStatus.getPath())) {
114           if (!HConstants.HBASE_NON_TABLE_DIRS.contains(status.getPath().getName())) {
115             isEmpty = false;
116             break;
117           }
118         }
119         if(isEmpty) {
120           try {
121             if (!fs.delete(nsStatus.getPath(), true)) {
122               LOG.error("Failed to remove namespace directory: " + nsStatus.getPath());
123             }
124           } catch (IOException ex) {
125             LOG.error("Failed to remove namespace directory: " + nsStatus.getPath(),
126                 ex);
127           }
128           LOG.debug("Removed namespace directory: "+nsStatus.getPath());
129         } else {
130           LOG.debug("Skipping non-empty namespace directory: " + nsStatus.getPath());
131         }
132       }
133     }
134 
135     String baseZnode = ZooKeeperWatcher.namespaceZNode;
136     for(String child : ZKUtil.listChildrenNoWatch(services.getZooKeeper(), baseZnode)) {
137       if (!descs.contains(child) &&
138           !NamespaceDescriptor.RESERVED_NAMESPACES.contains(child)) {
139         String znode = ZKUtil.joinZNode(baseZnode, child);
140         try {
141           ZKUtil.deleteNode(services.getZooKeeper(), znode);
142           LOG.debug("Removed namespace znode: " + znode);
143         } catch (KeeperException ex) {
144           LOG.debug("Failed to remove namespace znode: " + znode, ex);
145         }
146       }
147     }
148 
149   }
150 }