View Javadoc

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;
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.conf.Configuration;
25  import org.apache.hadoop.fs.FSDataOutputStream;
26  import org.apache.hadoop.fs.FileSystem;
27  import org.apache.hadoop.fs.Path;
28  import org.apache.hadoop.fs.permission.FsPermission;
29  import org.apache.hadoop.hbase.regionserver.wal.HLogFileSystem;
30  import org.apache.hadoop.hbase.util.Threads;
31  
32  /**
33   * An abstraction of the underlying filesystem. This is used by other entities such as
34   * {@link HLogFileSystem}, to make calls to the underlying filesystem.
35   *
36   */
37  public abstract class HBaseFileSystem {
38  
39    public static final Log LOG = LogFactory.getLog(HBaseFileSystem.class);
40  
41    /**
42     * In order to handle NN connectivity hiccups, one need to retry non-idempotent operation at the
43     * client level.
44     */
45    protected static int hdfsClientRetriesNumber;
46    private static int baseSleepBeforeRetries;
47    private static final int DEFAULT_HDFS_CLIENT_RETRIES_NUMBER = 10;
48    private static final int DEFAULT_BASE_SLEEP_BEFORE_RETRIES = 1000;
49    // This static block is added for performance reasons. This is to ensure we are not checking
50    // in the method calls whether retry properties are set or not. Refer to HBase-8288 for more 
51    // context.
52    static {
53      setRetryCounts(HBaseConfiguration.create());
54    }
55  
56    /**
57     * Deletes a file. Assumes the user has already checked for this directory existence.
58     * @param fs
59     * @param dir
60     * @return true if the directory is deleted.
61     * @throws IOException
62     */
63    public static boolean deleteFileFromFileSystem(FileSystem fs, Path dir)
64        throws IOException {
65      IOException lastIOE = null;
66      int i = 0;
67      do {
68        try {
69          return fs.delete(dir, false);
70        } catch (IOException ioe) {
71          lastIOE = ioe;
72          if (!fs.exists(dir)) return true;
73          // dir is there, retry deleting after some time.
74          sleepBeforeRetry("Delete File", i + 1);
75        }
76      } while (++i <= hdfsClientRetriesNumber);
77      throw new IOException("Exception in deleteFileFromFileSystem", lastIOE);
78    }
79    
80    
81    /**
82     * Deletes a directory. Assumes the user has already checked for this directory existence.
83     * @param fs
84     * @param dir
85     * @return true if the directory is deleted.
86     * @throws IOException
87     */
88    public static boolean deleteDirFromFileSystem(FileSystem fs, Path dir)
89        throws IOException {
90      IOException lastIOE = null;
91      int i = 0;
92      do {
93        try {
94          return fs.delete(dir, true);
95        } catch (IOException ioe) {
96          lastIOE = ioe;
97          if (!fs.exists(dir)) return true;
98          // dir is there, retry deleting after some time.
99          sleepBeforeRetry("Delete Dir", i + 1);
100       }
101     } while (++i <= hdfsClientRetriesNumber);
102     throw new IOException("Exception in deleteDirFromFileSystem", lastIOE);
103   }
104 
105   protected static void setRetryCounts(Configuration conf) {
106       hdfsClientRetriesNumber = conf.getInt("hdfs.client.retries.number",
107         DEFAULT_HDFS_CLIENT_RETRIES_NUMBER);
108       baseSleepBeforeRetries = conf.getInt("hdfs.client.sleep.before.retries",
109         DEFAULT_BASE_SLEEP_BEFORE_RETRIES);
110   }
111   
112   /**
113    * Creates a directory for a filesystem and configuration object. Assumes the user has already
114    * checked for this directory existence.
115    * @param fs
116    * @param dir
117    * @return the result of fs.mkdirs(). In case underlying fs throws an IOException, it checks
118    *         whether the directory exists or not, and returns true if it exists.
119    * @throws IOException
120    */
121   public static boolean makeDirOnFileSystem(FileSystem fs, Path dir)
122       throws IOException {
123     int i = 0;
124     IOException lastIOE = null;
125     do {
126       try {
127         return fs.mkdirs(dir);
128       } catch (IOException ioe) {
129         lastIOE = ioe;
130         if (fs.exists(dir)) return true; // directory is present
131         sleepBeforeRetry("Create Directory", i+1);
132       }
133     } while (++i <= hdfsClientRetriesNumber);
134     throw new IOException("Exception in makeDirOnFileSystem", lastIOE);
135   }
136   
137   /**
138    * Renames a directory. Assumes the user has already checked for this directory existence.
139    * @param fs
140    * @param src
141    * @param dst
142    * @return true if the directory is renamed.
143    * @throws IOException
144    */
145   public static boolean renameDirForFileSystem(FileSystem fs, Path src, Path dst)
146       throws IOException {
147     IOException lastIOE = null;
148     int i = 0;
149     do {
150       try {
151         return fs.rename(src, dst);
152       } catch (IOException ioe) {
153         lastIOE = ioe;
154         if (!fs.exists(src) && fs.exists(dst)) return true;
155         // src is there, retry renaming after some time.
156         sleepBeforeRetry("Rename Directory", i + 1);
157       }
158     } while (++i <= hdfsClientRetriesNumber);
159     throw new IOException("Exception in renameDirForFileSystem", lastIOE);
160   }
161 
162   /**
163    * Creates a path on the file system. Checks whether the path exists already or not, and use it
164    * for retrying in case underlying fs throws an exception. 
165    * If the dir already exists and overwrite flag is false, the underlying FileSystem throws
166    *  an IOE. It is not retried and the IOE is re-thrown to the caller.
167    * @param fs
168    * @param dir
169    * @param overwrite
170    * @return
171    * @throws IOException
172    */
173   public static FSDataOutputStream createPathOnFileSystem(FileSystem fs, Path dir,
174       boolean overwrite) throws IOException {
175     int i = 0;
176     boolean existsBefore = fs.exists(dir);
177     IOException lastIOE = null;
178     do {
179       try {
180         return fs.create(dir, overwrite);
181       } catch (IOException ioe) {
182         lastIOE = ioe;
183         if (existsBefore && !overwrite) throw ioe;// a legitimate exception
184         sleepBeforeRetry("Create Path", i + 1);
185       }
186     } while (++i <= hdfsClientRetriesNumber);
187     throw new IOException("Exception in createPathOnFileSystem", lastIOE);
188   }
189 
190   /**
191    * Creates the specified file with the given permission. 
192    * If the dir already exists and the overwrite flag is false, underlying FileSystem throws
193    * an IOE. It is not retried and the IOE is re-thrown to the caller.
194    * @param fs
195    * @param path
196    * @param perm
197    * @param overwrite
198    * @return
199    * @throws IOException
200    */
201   public static FSDataOutputStream createPathWithPermsOnFileSystem(FileSystem fs,
202       Path path, FsPermission perm, boolean overwrite) throws IOException {
203     int i = 0;
204     IOException lastIOE = null;
205     boolean existsBefore = fs.exists(path);
206     do {
207       try {
208         return fs.create(path, perm, overwrite, fs.getConf().getInt("io.file.buffer.size", 4096),
209           fs.getDefaultReplication(), fs.getDefaultBlockSize(), null);
210       } catch (IOException ioe) {
211         lastIOE = ioe;
212         if (existsBefore && !overwrite) throw ioe;// a legitimate exception
213         sleepBeforeRetry("Create Path with Perms", i + 1);
214       }
215     } while (++i <= hdfsClientRetriesNumber);
216     throw new IOException("Exception in createPathWithPermsOnFileSystem", lastIOE);
217   }
218 
219 /**
220  * Creates the file. Assumes the user has already checked for this file existence.
221  * @param fs
222  * @param dir
223  * @return result true if the file is created with this call, false otherwise.
224  * @throws IOException
225  */
226   public static boolean createNewFileOnFileSystem(FileSystem fs, Path file)
227       throws IOException {
228     int i = 0;
229     IOException lastIOE = null;
230     do {
231       try {
232         return fs.createNewFile(file);
233       } catch (IOException ioe) {
234         lastIOE = ioe;
235         if (fs.exists(file)) return true; // file exists now, return true.
236         sleepBeforeRetry("Create NewFile", i + 1);
237       }
238     } while (++i <= hdfsClientRetriesNumber);
239     throw new IOException("Exception in createNewFileOnFileSystem", lastIOE);
240   }
241   
242   /**
243    * sleeping logic for static methods; handles the interrupt exception. Keeping a static version
244    * for this to avoid re-looking for the integer values.
245    */
246   protected static void sleepBeforeRetry(String msg, int sleepMultiplier) {
247     if (sleepMultiplier > hdfsClientRetriesNumber) {
248       LOG.warn(msg + ", retries exhausted");
249       return;
250     }
251     LOG.info(msg + ", sleeping " + baseSleepBeforeRetries + " times " + sleepMultiplier);
252     Threads.sleep(baseSleepBeforeRetries * sleepMultiplier);
253   }
254 }