View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.master;
21  
22  import java.io.IOException;
23  import java.util.Map;
24  import java.util.concurrent.locks.Lock;
25  import java.util.concurrent.locks.ReentrantLock;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.conf.Configuration;
30  import org.apache.hadoop.fs.FileStatus;
31  import org.apache.hadoop.fs.FileSystem;
32  import org.apache.hadoop.fs.Path;
33  import org.apache.hadoop.hbase.HColumnDescriptor;
34  import org.apache.hadoop.hbase.HConstants;
35  import org.apache.hadoop.hbase.HRegionInfo;
36  import org.apache.hadoop.hbase.HServerInfo;
37  import org.apache.hadoop.hbase.RemoteExceptionHandler;
38  import org.apache.hadoop.hbase.Server;
39  import org.apache.hadoop.hbase.master.metrics.MasterMetrics;
40  import org.apache.hadoop.hbase.regionserver.HRegion;
41  import org.apache.hadoop.hbase.regionserver.Store;
42  import org.apache.hadoop.hbase.regionserver.wal.HLog;
43  import org.apache.hadoop.hbase.regionserver.wal.HLogSplitter;
44  import org.apache.hadoop.hbase.regionserver.wal.OrphanHLogAfterSplitException;
45  import org.apache.hadoop.hbase.util.Bytes;
46  import org.apache.hadoop.hbase.util.FSUtils;
47  
48  /**
49   * This class abstracts a bunch of operations the HMaster needs to interact with
50   * the underlying file system, including splitting log files, checking file
51   * system status, etc.
52   */
53  public class MasterFileSystem {
54    private static final Log LOG = LogFactory.getLog(MasterFileSystem.class.getName());
55    // HBase configuration
56    Configuration conf;
57    // master status
58    Server master;
59    // metrics for master
60    MasterMetrics metrics;
61    // Keep around for convenience.
62    private final FileSystem fs;
63    // Is the fileystem ok?
64    private volatile boolean fsOk = true;
65    // The Path to the old logs dir
66    private final Path oldLogDir;
67    // root hbase directory on the FS
68    private final Path rootdir;
69    // create the split log lock
70    final Lock splitLogLock = new ReentrantLock();
71  
72    public MasterFileSystem(Server master, MasterMetrics metrics)
73    throws IOException {
74      this.conf = master.getConfiguration();
75      this.master = master;
76      this.metrics = metrics;
77      // Set filesystem to be that of this.rootdir else we get complaints about
78      // mismatched filesystems if hbase.rootdir is hdfs and fs.defaultFS is
79      // default localfs.  Presumption is that rootdir is fully-qualified before
80      // we get to here with appropriate fs scheme.
81      this.rootdir = FSUtils.getRootDir(conf);
82      // Cover both bases, the old way of setting default fs and the new.
83      // We're supposed to run on 0.20 and 0.21 anyways.
84      conf.set("fs.default.name", this.rootdir.toString());
85      conf.set("fs.defaultFS", this.rootdir.toString());
86      // setup the filesystem variable
87      this.fs = FileSystem.get(conf);
88      // set up the archived logs path
89      this.oldLogDir = new Path(this.rootdir, HConstants.HREGION_OLDLOGDIR_NAME);
90      createInitialFileSystemLayout();
91    }
92  
93    /**
94     * Create initial layout in filesystem.
95     * <ol>
96     * <li>Check if the root region exists and is readable, if not create it.
97     * Create hbase.version and the -ROOT- directory if not one.
98     * </li>
99     * <li>Create a log archive directory for RS to put archived logs</li>
100    * </ol>
101    * Idempotent.
102    */
103   private void createInitialFileSystemLayout() throws IOException {
104     // check if the root directory exists
105     checkRootDir(this.rootdir, conf, this.fs);
106 
107     // Make sure the region servers can archive their old logs
108     if(!this.fs.exists(this.oldLogDir)) {
109       this.fs.mkdirs(this.oldLogDir);
110     }
111   }
112 
113   public FileSystem getFileSystem() {
114     return this.fs;
115   }
116 
117   /**
118    * Get the directory where old logs go
119    * @return the dir
120    */
121   public Path getOldLogDir() {
122     return this.oldLogDir;
123   }
124 
125   /**
126    * Checks to see if the file system is still accessible.
127    * If not, sets closed
128    * @return false if file system is not available
129    */
130   public boolean checkFileSystem() {
131     if (this.fsOk) {
132       try {
133         FSUtils.checkFileSystemAvailable(this.fs);
134       } catch (IOException e) {
135         master.abort("Shutting down HBase cluster: file system not available", e);
136         this.fsOk = false;
137       }
138     }
139     return this.fsOk;
140   }
141 
142   /**
143    * @return HBase root dir.
144    * @throws IOException
145    */
146   public Path getRootDir() {
147     return this.rootdir;
148   }
149 
150   /**
151    * Inspect the log directory to recover any log file without
152    * an active region server.
153    * @param onlineServers Map of online servers keyed by
154    * {@link HServerInfo#getServerName()}
155    */
156   void splitLogAfterStartup(final Map<String, HServerInfo> onlineServers) {
157     Path logsDirPath = new Path(this.rootdir, HConstants.HREGION_LOGDIR_NAME);
158     try {
159       if (!this.fs.exists(logsDirPath)) {
160         return;
161       }
162     } catch (IOException e) {
163       throw new RuntimeException("Failed exists test on " + logsDirPath, e);
164     }
165     FileStatus[] logFolders;
166     try {
167       logFolders = this.fs.listStatus(logsDirPath);
168     } catch (IOException e) {
169       throw new RuntimeException("Failed listing " + logsDirPath.toString(), e);
170     }
171     if (logFolders == null || logFolders.length == 0) {
172       LOG.debug("No log files to split, proceeding...");
173       return;
174     }
175     for (FileStatus status : logFolders) {
176       String serverName = status.getPath().getName();
177       if (onlineServers.get(serverName) == null) {
178         LOG.info("Log folder " + status.getPath() + " doesn't belong " +
179           "to a known region server, splitting");
180         splitLog(serverName);
181       } else {
182         LOG.info("Log folder " + status.getPath() +
183           " belongs to an existing region server");
184       }
185     }
186   }
187 
188   public void splitLog(final String serverName) {
189     this.splitLogLock.lock();
190     long splitTime = 0, splitLogSize = 0;
191     Path logDir = new Path(this.rootdir, HLog.getHLogDirectoryName(serverName));
192     try {
193       HLogSplitter splitter = HLogSplitter.createLogSplitter(
194         conf, rootdir, logDir, oldLogDir, this.fs);
195       try {
196         splitter.splitLog();
197       } catch (OrphanHLogAfterSplitException e) {
198         LOG.warn("Retrying splitting because of:", e);
199         // An HLogSplitter instance can only be used once.  Get new instance.
200         splitter = HLogSplitter.createLogSplitter(conf, rootdir, logDir,
201           oldLogDir, this.fs);
202         splitter.splitLog();
203       }
204       splitTime = splitter.getTime();
205       splitLogSize = splitter.getSize();
206     } catch (IOException e) {
207       LOG.error("Failed splitting " + logDir.toString(), e);
208     } finally {
209       this.splitLogLock.unlock();
210     }
211     if (this.metrics != null) {
212       this.metrics.addSplit(splitTime, splitLogSize);
213     }
214   }
215 
216   /**
217    * Get the rootdir.  Make sure its wholesome and exists before returning.
218    * @param rd
219    * @param conf
220    * @param fs
221    * @return hbase.rootdir (after checks for existence and bootstrapping if
222    * needed populating the directory with necessary bootup files).
223    * @throws IOException
224    */
225   private static Path checkRootDir(final Path rd, final Configuration c,
226     final FileSystem fs)
227   throws IOException {
228     // If FS is in safe mode wait till out of it.
229     FSUtils.waitOnSafeMode(c, c.getInt(HConstants.THREAD_WAKE_FREQUENCY,
230         10 * 1000));
231     // Filesystem is good. Go ahead and check for hbase.rootdir.
232     if (!fs.exists(rd)) {
233       fs.mkdirs(rd);
234       FSUtils.setVersion(fs, rd);
235     } else {
236       FSUtils.checkVersion(fs, rd, true);
237     }
238     // Make sure the root region directory exists!
239     if (!FSUtils.rootRegionExists(fs, rd)) {
240       bootstrap(rd, c);
241     }
242     return rd;
243   }
244 
245   private static void bootstrap(final Path rd, final Configuration c)
246   throws IOException {
247     LOG.info("BOOTSTRAP: creating ROOT and first META regions");
248     try {
249       // Bootstrapping, make sure blockcache is off.  Else, one will be
250       // created here in bootstap and it'll need to be cleaned up.  Better to
251       // not make it in first place.  Turn off block caching for bootstrap.
252       // Enable after.
253       HRegionInfo rootHRI = new HRegionInfo(HRegionInfo.ROOT_REGIONINFO);
254       setInfoFamilyCaching(rootHRI, false);
255       HRegionInfo metaHRI = new HRegionInfo(HRegionInfo.FIRST_META_REGIONINFO);
256       setInfoFamilyCaching(metaHRI, false);
257       HRegion root = HRegion.createHRegion(rootHRI, rd, c);
258       HRegion meta = HRegion.createHRegion(metaHRI, rd, c);
259       setInfoFamilyCaching(rootHRI, true);
260       setInfoFamilyCaching(metaHRI, true);
261       // Add first region from the META table to the ROOT region.
262       HRegion.addRegionToMETA(root, meta);
263       root.close();
264       root.getLog().closeAndDelete();
265       meta.close();
266       meta.getLog().closeAndDelete();
267     } catch (IOException e) {
268       e = RemoteExceptionHandler.checkIOException(e);
269       LOG.error("bootstrap", e);
270       throw e;
271     }
272   }
273 
274   /**
275    * @param hri Set all family block caching to <code>b</code>
276    * @param b
277    */
278   private static void setInfoFamilyCaching(final HRegionInfo hri, final boolean b) {
279     for (HColumnDescriptor hcd: hri.getTableDesc().families.values()) {
280       if (Bytes.equals(hcd.getName(), HConstants.CATALOG_FAMILY)) {
281         hcd.setBlockCacheEnabled(b);
282         hcd.setInMemory(b);
283       }
284     }
285   }
286 
287   public void deleteRegion(HRegionInfo region) throws IOException {
288     fs.delete(HRegion.getRegionDir(rootdir, region), true);
289   }
290 
291   public void deleteTable(byte[] tableName) throws IOException {
292     fs.delete(new Path(rootdir, Bytes.toString(tableName)), true);
293   }
294 
295   public void updateRegionInfo(HRegionInfo region) {
296     // TODO implement this.  i think this is currently broken in trunk i don't
297     //      see this getting updated.
298     //      @see HRegion.checkRegioninfoOnFilesystem()
299   }
300 
301   public void deleteFamily(HRegionInfo region, byte[] familyName)
302   throws IOException {
303     fs.delete(Store.getStoreHomedir(
304         new Path(rootdir, region.getTableDesc().getNameAsString()),
305         region.getEncodedName(), familyName), true);
306   }
307 }