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  
19  package org.apache.hadoop.hbase.io;
20  
21  import java.io.IOException;
22  import java.util.regex.Matcher;
23  import java.util.regex.Pattern;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.hbase.classification.InterfaceAudience;
28  import org.apache.hadoop.conf.Configuration;
29  import org.apache.hadoop.fs.FileSystem;
30  import org.apache.hadoop.fs.Path;
31  import org.apache.hadoop.hbase.TableName;
32  import org.apache.hadoop.hbase.HConstants;
33  import org.apache.hadoop.hbase.HRegionInfo;
34  import org.apache.hadoop.hbase.mob.MobConstants;
35  import org.apache.hadoop.hbase.regionserver.HRegion;
36  import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
37  import org.apache.hadoop.hbase.util.FSUtils;
38  import org.apache.hadoop.hbase.util.HFileArchiveUtil;
39  import org.apache.hadoop.hbase.util.Pair;
40  
41  /**
42   * HFileLink describes a link to an hfile.
43   *
44   * An hfile can be served from a region or from the hfile archive directory (/hbase/.archive)
45   * HFileLink allows to access the referenced hfile regardless of the location where it is.
46   *
47   * <p>Searches for hfiles in the following order and locations:
48   * <ul>
49   *  <li>/hbase/table/region/cf/hfile</li>
50   *  <li>/hbase/.archive/table/region/cf/hfile</li>
51   * </ul>
52   *
53   * The link checks first in the original path if it is not present
54   * it fallbacks to the archived path.
55   */
56  @InterfaceAudience.Private
57  public class HFileLink extends FileLink {
58    private static final Log LOG = LogFactory.getLog(HFileLink.class);
59  
60    /**
61     * A non-capture group, for HFileLink, so that this can be embedded.
62     * The HFileLink describe a link to an hfile in a different table/region
63     * and the name is in the form: table=region-hfile.
64     * <p>
65     * Table name is ([\p{IsAlphabetic}\p{Digit}][\p{IsAlphabetic}\p{Digit}.-]*), so '=' is an invalid
66     * character for the table name.
67     * Region name is ([a-f0-9]+), so '-' is an invalid character for the region name.
68     * HFile is ([0-9a-f]+(?:_SeqId_[0-9]+_)?) covering the plain hfiles (uuid)
69     * and the bulk loaded (_SeqId_[0-9]+_) hfiles.
70     */
71    public static final String LINK_NAME_REGEX =
72      String.format("(?:(?:%s=)?)%s=%s-%s",
73        TableName.VALID_NAMESPACE_REGEX, TableName.VALID_TABLE_QUALIFIER_REGEX,
74        HRegionInfo.ENCODED_REGION_NAME_REGEX, StoreFileInfo.HFILE_NAME_REGEX);
75  
76    /** Define the HFile Link name parser in the form of: table=region-hfile */
77    //made package private for testing
78    static final Pattern LINK_NAME_PATTERN =
79      Pattern.compile(String.format("^(?:(%s)(?:\\=))?(%s)=(%s)-(%s)$",
80        TableName.VALID_NAMESPACE_REGEX, TableName.VALID_TABLE_QUALIFIER_REGEX,
81        HRegionInfo.ENCODED_REGION_NAME_REGEX, StoreFileInfo.HFILE_NAME_REGEX));
82  
83    /**
84     * The pattern should be used for hfile and reference links
85     * that can be found in /hbase/table/region/family/
86     */
87    private static final Pattern REF_OR_HFILE_LINK_PATTERN =
88      Pattern.compile(String.format("^(?:(%s)(?:=))?(%s)=(%s)-(.+)$",
89        TableName.VALID_NAMESPACE_REGEX, TableName.VALID_TABLE_QUALIFIER_REGEX,
90        HRegionInfo.ENCODED_REGION_NAME_REGEX));
91  
92    private final Path archivePath;
93    private final Path originPath;
94    private final Path mobPath;
95    private final Path tempPath;
96  
97    /**
98     * Dead simple hfile link constructor
99     */
100   public HFileLink(final Path originPath, final Path tempPath, final Path mobPath,
101                    final Path archivePath) {
102     this.tempPath = tempPath;
103     this.originPath = originPath;
104     this.mobPath = mobPath;
105     this.archivePath = archivePath;
106     setLocations(originPath, tempPath, mobPath, archivePath);
107   }
108 
109 
110   /**
111    * @param conf {@link Configuration} from which to extract specific archive locations
112    * @param hFileLinkPattern The path ending with a HFileLink pattern. (table=region-hfile)
113    * @throws IOException on unexpected error.
114    */
115   public static final HFileLink buildFromHFileLinkPattern(Configuration conf, Path hFileLinkPattern)
116           throws IOException {
117     return buildFromHFileLinkPattern(FSUtils.getRootDir(conf),
118             HFileArchiveUtil.getArchivePath(conf), hFileLinkPattern);
119   }
120 
121 
122 
123   /**
124    * @param rootDir Path to the root directory where hbase files are stored
125    * @param archiveDir Path to the hbase archive directory
126    * @param hFileLinkPattern The path of the HFile Link.
127    */
128   public final static HFileLink buildFromHFileLinkPattern(final Path rootDir,
129                                                           final Path archiveDir,
130                                                           final Path hFileLinkPattern) {
131     Path hfilePath = getHFileLinkPatternRelativePath(hFileLinkPattern);
132     Path tempPath = new Path(new Path(rootDir, HConstants.HBASE_TEMP_DIRECTORY), hfilePath);
133     Path originPath = new Path(rootDir, hfilePath);
134     Path mobPath = new Path(new Path(rootDir, MobConstants.MOB_DIR_NAME), hfilePath);
135     Path archivePath = new Path(archiveDir, hfilePath);
136     return new HFileLink(originPath, tempPath, mobPath, archivePath);
137   }
138 
139   /**
140    * Create an HFileLink relative path for the table/region/family/hfile location
141    * @param table Table name
142    * @param region Region Name
143    * @param family Family Name
144    * @param hfile HFile Name
145    * @return the relative Path to open the specified table/region/family/hfile link
146    */
147   public static Path createPath(final TableName table, final String region,
148                                 final String family, final String hfile) {
149     if (HFileLink.isHFileLink(hfile)) {
150       return new Path(family, hfile);
151     }
152     return new Path(family, HFileLink.createHFileLinkName(table, region, hfile));
153   }
154 
155   /**
156    * Create an HFileLink instance from table/region/family/hfile location
157    * @param conf {@link Configuration} from which to extract specific archive locations
158    * @param table Table name
159    * @param region Region Name
160    * @param family Family Name
161    * @param hfile HFile Name
162    * @return Link to the file with the specified table/region/family/hfile location
163    * @throws IOException on unexpected error.
164    */
165   public static HFileLink build(final Configuration conf, final TableName table,
166                                  final String region, final String family, final String hfile)
167           throws IOException {
168     return HFileLink.buildFromHFileLinkPattern(conf, createPath(table, region, family, hfile));
169   }
170 
171   /**
172    * @return the origin path of the hfile.
173    */
174   public Path getOriginPath() {
175     return this.originPath;
176   }
177 
178   /**
179    * @return the path of the archived hfile.
180    */
181   public Path getArchivePath() {
182     return this.archivePath;
183   }
184 
185   /**
186    * @return the path of the mob hfiles.
187    */
188   public Path getMobPath() {
189     return this.mobPath;
190   }
191 
192     /**
193    * @param path Path to check.
194    * @return True if the path is a HFileLink.
195    */
196   public static boolean isHFileLink(final Path path) {
197     return isHFileLink(path.getName());
198   }
199 
200 
201   /**
202    * @param fileName File name to check.
203    * @return True if the path is a HFileLink.
204    */
205   public static boolean isHFileLink(String fileName) {
206     Matcher m = LINK_NAME_PATTERN.matcher(fileName);
207     if (!m.matches()) return false;
208     return m.groupCount() > 2 && m.group(4) != null && m.group(3) != null && m.group(2) != null;
209   }
210 
211   /**
212    * Convert a HFileLink path to a table relative path.
213    * e.g. the link: /hbase/test/0123/cf/testtb=4567-abcd
214    *      becomes: /hbase/testtb/4567/cf/abcd
215    *
216    * @param path HFileLink path
217    * @return Relative table path
218    * @throws IOException on unexpected error.
219    */
220   private static Path getHFileLinkPatternRelativePath(final Path path) {
221     // table=region-hfile
222     Matcher m = REF_OR_HFILE_LINK_PATTERN.matcher(path.getName());
223     if (!m.matches()) {
224       throw new IllegalArgumentException(path.getName() + " is not a valid HFileLink pattern!");
225     }
226 
227     // Convert the HFileLink name into a real table/region/cf/hfile path.
228     TableName tableName = TableName.valueOf(m.group(1), m.group(2));
229     String regionName = m.group(3);
230     String hfileName = m.group(4);
231     String familyName = path.getParent().getName();
232     Path tableDir = FSUtils.getTableDir(new Path("./"), tableName);
233     return new Path(tableDir, new Path(regionName, new Path(familyName,
234         hfileName)));
235   }
236 
237   /**
238    * Get the HFile name of the referenced link
239    *
240    * @param fileName HFileLink file name
241    * @return the name of the referenced HFile
242    */
243   public static String getReferencedHFileName(final String fileName) {
244     Matcher m = REF_OR_HFILE_LINK_PATTERN.matcher(fileName);
245     if (!m.matches()) {
246       throw new IllegalArgumentException(fileName + " is not a valid HFileLink name!");
247     }
248     return(m.group(4));
249   }
250 
251   /**
252    * Get the Region name of the referenced link
253    *
254    * @param fileName HFileLink file name
255    * @return the name of the referenced Region
256    */
257   public static String getReferencedRegionName(final String fileName) {
258     Matcher m = REF_OR_HFILE_LINK_PATTERN.matcher(fileName);
259     if (!m.matches()) {
260       throw new IllegalArgumentException(fileName + " is not a valid HFileLink name!");
261     }
262     return(m.group(3));
263   }
264 
265   /**
266    * Get the Table name of the referenced link
267    *
268    * @param fileName HFileLink file name
269    * @return the name of the referenced Table
270    */
271   public static TableName getReferencedTableName(final String fileName) {
272     Matcher m = REF_OR_HFILE_LINK_PATTERN.matcher(fileName);
273     if (!m.matches()) {
274       throw new IllegalArgumentException(fileName + " is not a valid HFileLink name!");
275     }
276     return(TableName.valueOf(m.group(1), m.group(2)));
277   }
278 
279   /**
280    * Create a new HFileLink name
281    *
282    * @param hfileRegionInfo - Linked HFile Region Info
283    * @param hfileName - Linked HFile name
284    * @return file name of the HFile Link
285    */
286   public static String createHFileLinkName(final HRegionInfo hfileRegionInfo,
287       final String hfileName) {
288     return createHFileLinkName(hfileRegionInfo.getTable(),
289             hfileRegionInfo.getEncodedName(), hfileName);
290   }
291 
292   /**
293    * Create a new HFileLink name
294    *
295    * @param tableName - Linked HFile table name
296    * @param regionName - Linked HFile region name
297    * @param hfileName - Linked HFile name
298    * @return file name of the HFile Link
299    */
300   public static String createHFileLinkName(final TableName tableName,
301       final String regionName, final String hfileName) {
302     String s = String.format("%s=%s-%s",
303         tableName.getNameAsString().replace(TableName.NAMESPACE_DELIM, '='),
304         regionName, hfileName);
305     return s;
306   }
307 
308   /**
309    * Create a new HFileLink
310    *
311    * <p>It also adds a back-reference to the hfile back-reference directory
312    * to simplify the reference-count and the cleaning process.
313    *
314    * @param conf {@link Configuration} to read for the archive directory name
315    * @param fs {@link FileSystem} on which to write the HFileLink
316    * @param dstFamilyPath - Destination path (table/region/cf/)
317    * @param hfileRegionInfo - Linked HFile Region Info
318    * @param hfileName - Linked HFile name
319    * @return true if the file is created, otherwise the file exists.
320    * @throws IOException on file or parent directory creation failure
321    */
322   public static boolean create(final Configuration conf, final FileSystem fs,
323       final Path dstFamilyPath, final HRegionInfo hfileRegionInfo,
324       final String hfileName) throws IOException {
325     return create(conf, fs, dstFamilyPath, hfileRegionInfo, hfileName, true);
326   }
327 
328   /**
329    * Create a new HFileLink
330    *
331    * <p>It also adds a back-reference to the hfile back-reference directory
332    * to simplify the reference-count and the cleaning process.
333    *
334    * @param conf {@link Configuration} to read for the archive directory name
335    * @param fs {@link FileSystem} on which to write the HFileLink
336    * @param dstFamilyPath - Destination path (table/region/cf/)
337    * @param hfileRegionInfo - Linked HFile Region Info
338    * @param hfileName - Linked HFile name
339    * @param createBackRef - Whether back reference should be created. Defaults to true.
340    * @return true if the file is created, otherwise the file exists.
341    * @throws IOException on file or parent directory creation failure
342    */
343   public static boolean create(final Configuration conf, final FileSystem fs,
344       final Path dstFamilyPath, final HRegionInfo hfileRegionInfo,
345       final String hfileName, final boolean createBackRef) throws IOException {
346     TableName linkedTable = hfileRegionInfo.getTable();
347     String linkedRegion = hfileRegionInfo.getEncodedName();
348     return create(conf, fs, dstFamilyPath, linkedTable, linkedRegion, hfileName, createBackRef);
349   }
350 
351   /**
352    * Create a new HFileLink
353    *
354    * <p>It also adds a back-reference to the hfile back-reference directory
355    * to simplify the reference-count and the cleaning process.
356    *
357    * @param conf {@link Configuration} to read for the archive directory name
358    * @param fs {@link FileSystem} on which to write the HFileLink
359    * @param dstFamilyPath - Destination path (table/region/cf/)
360    * @param linkedTable - Linked Table Name
361    * @param linkedRegion - Linked Region Name
362    * @param hfileName - Linked HFile name
363    * @return true if the file is created, otherwise the file exists.
364    * @throws IOException on file or parent directory creation failure
365    */
366   public static boolean create(final Configuration conf, final FileSystem fs,
367       final Path dstFamilyPath, final TableName linkedTable, final String linkedRegion,
368       final String hfileName) throws IOException {
369     return create(conf, fs, dstFamilyPath, linkedTable, linkedRegion, hfileName, true);
370   }
371 
372   /**
373    * Create a new HFileLink
374    *
375    * <p>It also adds a back-reference to the hfile back-reference directory
376    * to simplify the reference-count and the cleaning process.
377    *
378    * @param conf {@link Configuration} to read for the archive directory name
379    * @param fs {@link FileSystem} on which to write the HFileLink
380    * @param dstFamilyPath - Destination path (table/region/cf/)
381    * @param linkedTable - Linked Table Name
382    * @param linkedRegion - Linked Region Name
383    * @param hfileName - Linked HFile name
384    * @param createBackRef - Whether back reference should be created. Defaults to true.
385    * @return true if the file is created, otherwise the file exists.
386    * @throws IOException on file or parent directory creation failure
387    */
388   public static boolean create(final Configuration conf, final FileSystem fs,
389       final Path dstFamilyPath, final TableName linkedTable, final String linkedRegion,
390       final String hfileName, final boolean createBackRef) throws IOException {
391     String familyName = dstFamilyPath.getName();
392     String regionName = dstFamilyPath.getParent().getName();
393     String tableName = FSUtils.getTableName(dstFamilyPath.getParent().getParent())
394         .getNameAsString();
395 
396     String name = createHFileLinkName(linkedTable, linkedRegion, hfileName);
397     String refName = createBackReferenceName(tableName, regionName);
398 
399     // Make sure the destination directory exists
400     fs.mkdirs(dstFamilyPath);
401 
402     // Make sure the FileLink reference directory exists
403     Path archiveStoreDir = HFileArchiveUtil.getStoreArchivePath(conf,
404           linkedTable, linkedRegion, familyName);
405     Path backRefPath = null;
406     if (createBackRef) {
407       Path backRefssDir = getBackReferencesDir(archiveStoreDir, hfileName);
408       fs.mkdirs(backRefssDir);
409 
410       // Create the reference for the link
411       backRefPath = new Path(backRefssDir, refName);
412       fs.createNewFile(backRefPath);
413     }
414     try {
415       // Create the link
416       return fs.createNewFile(new Path(dstFamilyPath, name));
417     } catch (IOException e) {
418       LOG.error("couldn't create the link=" + name + " for " + dstFamilyPath, e);
419       // Revert the reference if the link creation failed
420       if (createBackRef) {
421         fs.delete(backRefPath, false);
422       }
423       throw e;
424     }
425   }
426 
427   /**
428    * Create a new HFileLink starting from a hfileLink name
429    *
430    * <p>It also adds a back-reference to the hfile back-reference directory
431    * to simplify the reference-count and the cleaning process.
432    *
433    * @param conf {@link Configuration} to read for the archive directory name
434    * @param fs {@link FileSystem} on which to write the HFileLink
435    * @param dstFamilyPath - Destination path (table/region/cf/)
436    * @param hfileLinkName - HFileLink name (it contains hfile-region-table)
437    * @return true if the file is created, otherwise the file exists.
438    * @throws IOException on file or parent directory creation failure
439    */
440   public static boolean createFromHFileLink(final Configuration conf, final FileSystem fs,
441       final Path dstFamilyPath, final String hfileLinkName)
442           throws IOException {
443     return createFromHFileLink(conf, fs, dstFamilyPath, hfileLinkName, true);
444   }
445 
446   /**
447    * Create a new HFileLink starting from a hfileLink name
448    *
449    * <p>It also adds a back-reference to the hfile back-reference directory
450    * to simplify the reference-count and the cleaning process.
451    *
452    * @param conf {@link Configuration} to read for the archive directory name
453    * @param fs {@link FileSystem} on which to write the HFileLink
454    * @param dstFamilyPath - Destination path (table/region/cf/)
455    * @param hfileLinkName - HFileLink name (it contains hfile-region-table)
456    * @param createBackRef - Whether back reference should be created. Defaults to true.
457    * @return true if the file is created, otherwise the file exists.
458    * @throws IOException on file or parent directory creation failure
459    */
460   public static boolean createFromHFileLink(final Configuration conf, final FileSystem fs,
461       final Path dstFamilyPath, final String hfileLinkName, final boolean createBackRef)
462           throws IOException {
463     Matcher m = LINK_NAME_PATTERN.matcher(hfileLinkName);
464     if (!m.matches()) {
465       throw new IllegalArgumentException(hfileLinkName + " is not a valid HFileLink name!");
466     }
467     return create(conf, fs, dstFamilyPath, TableName.valueOf(m.group(1), m.group(2)),
468         m.group(3), m.group(4), createBackRef);
469   }
470 
471   /**
472    * Create the back reference name
473    */
474   //package-private for testing
475   static String createBackReferenceName(final String tableNameStr,
476                                         final String regionName) {
477 
478     return regionName + "." + tableNameStr.replace(TableName.NAMESPACE_DELIM, '=');
479   }
480 
481   /**
482    * Get the full path of the HFile referenced by the back reference
483    *
484    * @param rootDir root hbase directory
485    * @param linkRefPath Link Back Reference path
486    * @return full path of the referenced hfile
487    * @throws IOException on unexpected error.
488    */
489   public static Path getHFileFromBackReference(final Path rootDir, final Path linkRefPath) {
490     Pair<TableName, String> p = parseBackReferenceName(linkRefPath.getName());
491     TableName linkTableName = p.getFirst();
492     String linkRegionName = p.getSecond();
493 
494     String hfileName = getBackReferenceFileName(linkRefPath.getParent());
495     Path familyPath = linkRefPath.getParent().getParent();
496     Path regionPath = familyPath.getParent();
497     Path tablePath = regionPath.getParent();
498 
499     String linkName = createHFileLinkName(FSUtils.getTableName(tablePath),
500             regionPath.getName(), hfileName);
501     Path linkTableDir = FSUtils.getTableDir(rootDir, linkTableName);
502     Path regionDir = HRegion.getRegionDir(linkTableDir, linkRegionName);
503     return new Path(new Path(regionDir, familyPath.getName()), linkName);
504   }
505 
506   static Pair<TableName, String> parseBackReferenceName(String name) {
507     int separatorIndex = name.indexOf('.');
508     String linkRegionName = name.substring(0, separatorIndex);
509     String tableSubstr = name.substring(separatorIndex + 1)
510         .replace('=', TableName.NAMESPACE_DELIM);
511     TableName linkTableName = TableName.valueOf(tableSubstr);
512     return new Pair<TableName, String>(linkTableName, linkRegionName);
513   }
514 
515   /**
516    * Get the full path of the HFile referenced by the back reference
517    *
518    * @param conf {@link Configuration} to read for the archive directory name
519    * @param linkRefPath Link Back Reference path
520    * @return full path of the referenced hfile
521    * @throws IOException on unexpected error.
522    */
523   public static Path getHFileFromBackReference(final Configuration conf, final Path linkRefPath)
524       throws IOException {
525     return getHFileFromBackReference(FSUtils.getRootDir(conf), linkRefPath);
526   }
527 
528 }