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.wal;
20  
21  import java.io.IOException;
22  import java.util.List;
23  import java.util.concurrent.atomic.AtomicBoolean;
24  import java.util.regex.Pattern;
25  
26  import com.google.common.annotations.VisibleForTesting;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.hbase.classification.InterfaceAudience;
30  import org.apache.hadoop.hbase.classification.InterfaceStability;
31  import org.apache.hadoop.conf.Configuration;
32  import org.apache.hadoop.fs.FSDataInputStream;
33  import org.apache.hadoop.fs.FileSystem;
34  import org.apache.hadoop.fs.Path;
35  import org.apache.hadoop.hbase.HConstants;
36  import org.apache.hadoop.hbase.ServerName;
37  import org.apache.hadoop.hbase.util.FSUtils;
38  
39  // imports for things that haven't moved from regionserver.wal yet.
40  import org.apache.hadoop.hbase.regionserver.wal.FSHLog;
41  import org.apache.hadoop.hbase.regionserver.wal.ProtobufLogWriter;
42  import org.apache.hadoop.hbase.regionserver.wal.WALActionsListener;
43  
44  /**
45   * A WAL Provider that returns a single thread safe WAL that writes to Hadoop FS.
46   * By default, this implementation picks a directory in Hadoop FS based on a combination of
47   * <ul>
48   *   <li>the HBase root WAL directory
49   *   <li>HConstants.HREGION_LOGDIR_NAME
50   *   <li>the given factory's factoryId (usually identifying the regionserver by host:port)
51   * </ul>
52   * It also uses the providerId to diffentiate among files.
53   *
54   */
55  @InterfaceAudience.Private
56  @InterfaceStability.Evolving
57  public class DefaultWALProvider implements WALProvider {
58    private static final Log LOG = LogFactory.getLog(DefaultWALProvider.class);
59  
60    // Only public so classes back in regionserver.wal can access
61    public interface Reader extends WAL.Reader {
62      /**
63       * @param fs File system.
64       * @param path Path.
65       * @param c Configuration.
66       * @param s Input stream that may have been pre-opened by the caller; may be null.
67       */
68      void init(FileSystem fs, Path path, Configuration c, FSDataInputStream s) throws IOException;
69    }
70  
71    // Only public so classes back in regionserver.wal can access
72    public interface Writer extends WALProvider.Writer {
73      void init(FileSystem fs, Path path, Configuration c, boolean overwritable) throws IOException;
74    }
75  
76    protected volatile FSHLog log = null;
77    private WALFactory factory = null;
78    private Configuration conf = null;
79    private List<WALActionsListener> listeners = null;
80    private String providerId = null;
81    private AtomicBoolean initialized = new AtomicBoolean(false);
82    // for default wal provider, logPrefix won't change
83    private String logPrefix = null;
84  
85    /**
86     * we synchronized on walCreateLock to prevent wal recreation in different threads
87     */
88    private final Object walCreateLock = new Object();
89  
90    /**
91     * @param factory factory that made us, identity used for FS layout. may not be null
92     * @param conf may not be null
93     * @param listeners may be null
94     * @param providerId differentiate between providers from one facotry, used for FS layout. may be
95     *                   null
96     */
97    @Override
98    public void init(final WALFactory factory, final Configuration conf,
99        final List<WALActionsListener> listeners, String providerId) throws IOException {
100     if (!initialized.compareAndSet(false, true)) {
101       throw new IllegalStateException("WALProvider.init should only be called once.");
102     }
103     this.factory = factory;
104     this.conf = conf;
105     this.listeners = listeners;
106     this.providerId = providerId;
107     // get log prefix
108     StringBuilder sb = new StringBuilder().append(factory.factoryId);
109     if (providerId != null) {
110       if (providerId.startsWith(WAL_FILE_NAME_DELIMITER)) {
111         sb.append(providerId);
112       } else {
113         sb.append(WAL_FILE_NAME_DELIMITER).append(providerId);
114       }
115     }
116     logPrefix = sb.toString();
117   }
118 
119   @Override
120   public WAL getWAL(final byte[] identifier, byte[] namespace) throws IOException {
121     if (log == null) {
122       // only lock when need to create wal, and need to lock since
123       // creating hlog on fs is time consuming
124       synchronized (walCreateLock) {
125         if (log == null) {
126           log = new FSHLog(FSUtils.getWALFileSystem(conf), FSUtils.getWALRootDir(conf),
127               getWALDirectoryName(factory.factoryId), HConstants.HREGION_OLDLOGDIR_NAME, conf,
128               listeners, true, logPrefix,
129               META_WAL_PROVIDER_ID.equals(providerId) ? META_WAL_PROVIDER_ID : null);
130         }
131       }
132     }
133     return log;
134   }
135 
136   @Override
137   public void close() throws IOException {
138     if (log != null) log.close();
139   }
140 
141   @Override
142   public void shutdown() throws IOException {
143     if (log != null) log.shutdown();
144   }
145 
146   // should be package private; more visible for use in FSHLog
147   public static final String WAL_FILE_NAME_DELIMITER = ".";
148   /** The hbase:meta region's WAL filename extension */
149   @VisibleForTesting
150   public static final String META_WAL_PROVIDER_ID = ".meta";
151   static final String DEFAULT_PROVIDER_ID = "default";
152 
153   // Implementation details that currently leak in tests or elsewhere follow
154   /** File Extension used while splitting an WAL into regions (HBASE-2312) */
155   public static final String SPLITTING_EXT = "-splitting";
156 
157   /**
158    * iff the given WALFactory is using the DefaultWALProvider for meta and/or non-meta,
159    * count the number of files (rolled and active). if either of them aren't, count 0
160    * for that provider.
161    */
162   @Override
163   public long getNumLogFiles() {
164     return log == null ? 0 : this.log.getNumLogFiles();
165   }
166 
167   /**
168    * iff the given WALFactory is using the DefaultWALProvider for meta and/or non-meta,
169    * count the size of files (rolled and active). if either of them aren't, count 0
170    * for that provider.
171    */
172   @Override
173   public long getLogFileSize() {
174     return log == null ? 0 : this.log.getLogFileSize();
175   }
176 
177   /**
178    * returns the number of rolled WAL files.
179    */
180   @VisibleForTesting
181   public static int getNumRolledLogFiles(WAL wal) {
182     return ((FSHLog)wal).getNumRolledLogFiles();
183   }
184 
185   /**
186    * return the current filename from the current wal.
187    */
188   @VisibleForTesting
189   public static Path getCurrentFileName(final WAL wal) {
190     return ((FSHLog)wal).getCurrentFileName();
191   }
192 
193   /**
194    * request a log roll, but don't actually do it.
195    */
196   @VisibleForTesting
197   static void requestLogRoll(final WAL wal) {
198     ((FSHLog)wal).requestLogRoll();
199   }
200 
201   /**
202    * It returns the file create timestamp from the file name.
203    * For name format see {@link #validateWALFilename(String)}
204    * public until remaining tests move to o.a.h.h.wal
205    * @param wal must not be null
206    * @return the file number that is part of the WAL file name
207    */
208   @VisibleForTesting
209   public static long extractFileNumFromWAL(final WAL wal) {
210     final Path walName = ((FSHLog)wal).getCurrentFileName();
211     if (walName == null) {
212       throw new IllegalArgumentException("The WAL path couldn't be null");
213     }
214     final String[] walPathStrs = walName.toString().split("\\" + WAL_FILE_NAME_DELIMITER);
215     return Long.parseLong(walPathStrs[walPathStrs.length - (isMetaFile(walName) ? 2:1)]);
216   }
217 
218   /**
219    * Pattern used to validate a WAL file name
220    * see {@link #validateWALFilename(String)} for description.
221    */
222   private static final Pattern pattern = Pattern.compile(".*\\.\\d*("+META_WAL_PROVIDER_ID+")*");
223 
224   /**
225    * A WAL file name is of the format:
226    * &lt;wal-name&gt;{@link #WAL_FILE_NAME_DELIMITER}&lt;file-creation-timestamp&gt;[.meta].
227    *
228    * provider-name is usually made up of a server-name and a provider-id
229    *
230    * @param filename name of the file to validate
231    * @return <tt>true</tt> if the filename matches an WAL, <tt>false</tt>
232    *         otherwise
233    */
234   public static boolean validateWALFilename(String filename) {
235     return pattern.matcher(filename).matches();
236   }
237 
238   /**
239    * Construct the directory name for all WALs on a given server.
240    *
241    * @param serverName
242    *          Server name formatted as described in {@link ServerName}
243    * @return the relative WAL directory name, e.g.
244    *         <code>.logs/1.example.org,60030,12345</code> if
245    *         <code>serverName</code> passed is
246    *         <code>1.example.org,60030,12345</code>
247    */
248   public static String getWALDirectoryName(final String serverName) {
249     StringBuilder dirName = new StringBuilder(HConstants.HREGION_LOGDIR_NAME);
250     dirName.append("/");
251     dirName.append(serverName);
252     return dirName.toString();
253   }
254 
255   /**
256    * Pulls a ServerName out of a Path generated according to our layout rules.
257    *
258    * In the below layouts, this method ignores the format of the logfile component.
259    *
260    * Current format:
261    *
262    * [base directory for hbase]/hbase/.logs/ServerName/logfile
263    *      or
264    * [base directory for hbase]/hbase/.logs/ServerName-splitting/logfile
265    *
266    * Expected to work for individual log files and server-specific directories.
267    *
268    * @return null if it's not a log file. Returns the ServerName of the region
269    *         server that created this log file otherwise.
270    */
271   public static ServerName getServerNameFromWALDirectoryName(Configuration conf, String path)
272       throws IOException {
273     if (path == null
274         || path.length() <= HConstants.HREGION_LOGDIR_NAME.length()) {
275       return null;
276     }
277 
278     if (conf == null) {
279       throw new IllegalArgumentException("parameter conf must be set");
280     }
281 
282     final String walDir = FSUtils.getWALRootDir(conf).toString();
283 
284     final StringBuilder startPathSB = new StringBuilder(walDir);
285     if (!walDir.endsWith("/"))
286       startPathSB.append('/');
287     startPathSB.append(HConstants.HREGION_LOGDIR_NAME);
288     if (!HConstants.HREGION_LOGDIR_NAME.endsWith("/"))
289       startPathSB.append('/');
290     final String startPath = startPathSB.toString();
291 
292     String fullPath;
293     try {
294       fullPath = FileSystem.get(conf).makeQualified(new Path(path)).toString();
295     } catch (IllegalArgumentException e) {
296       LOG.info("Call to makeQualified failed on " + path + " " + e.getMessage());
297       return null;
298     }
299 
300     if (!fullPath.startsWith(startPath)) {
301       return null;
302     }
303 
304     final String serverNameAndFile = fullPath.substring(startPath.length());
305 
306     if (serverNameAndFile.indexOf('/') < "a,0,0".length()) {
307       // Either it's a file (not a directory) or it's not a ServerName format
308       return null;
309     }
310 
311     Path p = new Path(path);
312     return getServerNameFromWALDirectoryName(p);
313   }
314 
315   /**
316    * This function returns region server name from a log file name which is in one of the following
317    * formats:
318    * <ul>
319    *   <li>hdfs://&lt;name node&gt;/hbase/.logs/&lt;server name&gt;-splitting/...</li>
320    *   <li>hdfs://&lt;name node&gt;/hbase/.logs/&lt;server name&gt;/...</li>
321    * </ul>
322    * @param logFile
323    * @return null if the passed in logFile isn't a valid WAL file path
324    */
325   public static ServerName getServerNameFromWALDirectoryName(Path logFile) {
326     String logDirName = logFile.getParent().getName();
327     // We were passed the directory and not a file in it.
328     if (logDirName.equals(HConstants.HREGION_LOGDIR_NAME)) {
329       logDirName = logFile.getName();
330     }
331     ServerName serverName = null;
332     if (logDirName.endsWith(SPLITTING_EXT)) {
333       logDirName = logDirName.substring(0, logDirName.length() - SPLITTING_EXT.length());
334     }
335     try {
336       serverName = ServerName.parseServerName(logDirName);
337     } catch (IllegalArgumentException ex) {
338       serverName = null;
339       LOG.warn("Cannot parse a server name from path=" + logFile + "; " + ex.getMessage());
340     }
341     if (serverName != null && serverName.getStartcode() < 0) {
342       LOG.warn("Invalid log file path=" + logFile);
343       serverName = null;
344     }
345     return serverName;
346   }
347 
348   public static boolean isMetaFile(Path p) {
349     return isMetaFile(p.getName());
350   }
351 
352   public static boolean isMetaFile(String p) {
353     if (p != null && p.endsWith(META_WAL_PROVIDER_ID)) {
354       return true;
355     }
356     return false;
357   }
358 
359   /**
360    * public because of FSHLog. Should be package-private
361    */
362   public static Writer createWriter(final Configuration conf, final FileSystem fs, final Path path,
363       final boolean overwritable)
364       throws IOException {
365     // Configuration already does caching for the Class lookup.
366     Class<? extends Writer> logWriterClass = conf.getClass("hbase.regionserver.hlog.writer.impl",
367         ProtobufLogWriter.class, Writer.class);
368     Writer writer = null;
369     try {
370       writer = logWriterClass.getDeclaredConstructor().newInstance();
371       FileSystem rootFs = FileSystem.get(path.toUri(), conf);
372       writer.init(rootFs, path, conf, overwritable);
373       return writer;
374     } catch (Exception e) {
375       LOG.debug("Error instantiating log writer.", e);
376       if (writer != null) {
377         try{
378           writer.close();
379         } catch(IOException ee){
380           LOG.error("cannot close log writer", ee);
381         }
382       }
383       throw new IOException("cannot get log writer", e);
384     }
385   }
386 
387   /**
388    * Get prefix of the log from its name, assuming WAL name in format of
389    * log_prefix.filenumber.log_suffix @see {@link FSHLog#getCurrentFileName()}
390    * @param name Name of the WAL to parse
391    * @return prefix of the log
392    */
393   public static String getWALPrefixFromWALName(String name) {
394     int endIndex = name.replaceAll(META_WAL_PROVIDER_ID, "").lastIndexOf(".");
395     return name.substring(0, endIndex);
396   }
397 
398   /*
399    * only public so WALSplitter can use.
400    * @return archived location of a WAL file with the given path p
401    */
402   public static Path getWALArchivePath(Path archiveDir, Path p) {
403     return new Path(archiveDir, p.getName());
404   }
405 
406 
407 }