Review Board 1.7.22


HBASE-5509 MR based copier for copying HFiles (trunk version)

Review Request #4218 - Created March 7, 2012 and updated

Lars Hofhansl
trunk
Reviewers
hbase
hbase
This is Facebooks SnaphotMR for backup/restore. Thanks Karthik!

I made the following changes:
1. API changes for trunk.
2. Also copies and patches .tableinfo files as needed.
3. ImportTable can proceed even while HBase is down (HDFS needs to be up obviously). Regions will assigned upon HBase startup.
4. A ZK quorum can be optionally provided to assign imported regions right away (needed in trunk).
5. Some safety checks to verify the backup directory provided for import is in fact a backup directory (otherwise in some case adjacent directories were overwritten with temporary files).

Manual only so far.
http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/backups/ImportTable.java
New File

    
   
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.backups;

    
   
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.FSDataInputStream;

    
   
26
import org.apache.hadoop.fs.FSDataOutputStream;

    
   
27
import org.apache.hadoop.fs.FileStatus;

    
   
28
import org.apache.hadoop.fs.FileSystem;

    
   
29
import org.apache.hadoop.fs.Path;

    
   
30
import org.apache.hadoop.hbase.HBaseConfiguration;

    
   
31
import org.apache.hadoop.hbase.HConstants;

    
   
32
import org.apache.hadoop.hbase.HRegionInfo;

    
   
33
import org.apache.hadoop.hbase.HTableDescriptor;

    
   
34
import org.apache.hadoop.hbase.client.HBaseAdmin;

    
   
35
import org.apache.hadoop.hbase.client.HTable;

    
   
36
import org.apache.hadoop.hbase.client.Put;

    
   
37
import org.apache.hadoop.hbase.regionserver.HRegion;

    
   
38
import org.apache.hadoop.hbase.util.Bytes;

    
   
39
import org.apache.hadoop.hbase.util.FSTableDescriptors;

    
   
40
import org.apache.hadoop.hbase.util.Writables;

    
   
41

   

    
   
42
/**

    
   
43
 * Imports an HBase table from a backup directory created by the

    
   
44
 * {@link Snapshot} tool

    
   
45
 */

    
   
46
public class ImportTable {

    
   
47
  public static final Log LOG = LogFactory.getLog(ImportTable.class);

    
   
48

   

    
   
49
  static Configuration conf = null;

    
   
50
  static String tableName = null;

    
   
51
  static String zkQuorum = null;

    
   
52
  static String backupDir = null;

    
   
53
  static String dfs = null;

    
   
54
  static HBaseAdmin admin = null;

    
   
55

   

    
   
56
  public static void main(String[] args) throws Exception {

    
   
57
    parseArgs(args);

    
   
58

   

    
   
59
    conf = HBaseConfiguration.create();

    
   
60
    conf.set("fs.default.name", dfs);

    
   
61
    if (zkQuorum != null) {

    
   
62
      conf.set("hbase.zookeeper.quorum", zkQuorum);

    
   
63
      admin = new HBaseAdmin(conf);

    
   
64
    }

    
   
65
    importTable();

    
   
66

   

    
   
67
  }

    
   
68

   

    
   
69
  public static void importTable() throws IOException {

    
   
70
    LOG.info("Starting import of table: " + tableName);

    
   
71

   

    
   
72
    FileSystem fs = FileSystem.get(conf);

    
   
73
    String hbaseDir = conf.get(HConstants.HBASE_DIR);

    
   
74
    LOG.info("The hbase base dir: " + hbaseDir);

    
   
75
    Path tableDir = new Path(hbaseDir, tableName);

    
   
76

   

    
   
77
    if (fs.exists(tableDir)) {

    
   
78
      // Not sure what to do if table exists - maybe move it aside

    
   
79
      throw new IOException("Table "+tableName+" already exists.");

    
   
80
    }

    
   
81

   

    
   
82
    Path backupDirPath = new Path(backupDir);

    
   
83
    if (!FSTableDescriptors.isTableInfoExists(fs, backupDirPath)) {

    
   
84
      throw new IOException(".tableinfo is missing in '"+backupDir+"'.");

    
   
85
    }

    
   
86
    if (!tableName.equals(backupDirPath.getName())) {

    
   
87
      LOG.info("Rename - Fix region names / .regioninfo files");

    
   
88
      backupDirPath = fixRegionNames(backupDirPath, tableName);

    
   
89
    }

    
   
90

   

    
   
91
    fs.rename(backupDirPath, tableDir);

    
   
92
    LOG.info("Renamed dir: " + backupDirPath.toString() + " to dir: " + tableDir.toString());

    
   
93

   

    
   
94
    LOG.info("Copying/fixing tableinfo from "+new Path(backupDir)+" to "+hbaseDir+" ("+tableName+")");

    
   
95
    HTableDescriptor htd = FSTableDescriptors.getTableDescriptor(fs, new Path(backupDir));

    
   
96
    htd.setName(Bytes.toBytes(tableName));

    
   
97
    FSTableDescriptors.createTableDescriptor(fs, new Path(hbaseDir), htd);

    
   
98

   

    
   
99
    addToMeta(tableDir);

    
   
100
  }

    
   
101

   

    
   
102
  private static void addToMeta(Path tableDir) throws IOException {

    
   
103
    FileSystem fs = FileSystem.get(conf);

    
   
104
    HTable metaTable = new HTable(conf, HConstants.META_TABLE_NAME);

    
   
105
    FileStatus[] files = fs.listStatus(tableDir);

    
   
106
    for (FileStatus file : files) {

    
   
107
      LOG.debug("Checking file for addToMeta: " + file.getPath().toString());

    
   
108
      if (! file.isDir()) continue;

    
   
109
      if (file.getPath().getName().equals(HConstants.HREGION_COMPACTIONDIR_NAME)) continue;

    
   
110
      if (file.getPath().getName().equals(".tmp")) continue;

    
   
111
      Path regionInfoPath = new Path(file.getPath(), HRegion.REGIONINFO_FILE);

    
   
112
      if (! fs.exists(regionInfoPath)) {

    
   
113
        throw new IOException("Missing regioninfo file for: " + file.getPath().toString());

    
   
114
      }

    
   
115

   

    
   
116
      FSDataInputStream regionInfoIn = fs.open(regionInfoPath);

    
   
117
      HRegionInfo hRegionInfo = new HRegionInfo();

    
   
118
      hRegionInfo.readFields(regionInfoIn);

    
   
119
      regionInfoIn.close();

    
   
120

   

    
   
121
      Put put = new Put(hRegionInfo.getRegionName());

    
   
122
      put.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,

    
   
123
          Writables.getBytes(hRegionInfo));

    
   
124
      metaTable.put(put);

    
   
125

   

    
   
126
      if (admin != null) {

    
   
127
        // anything more "bulky" one can do here?

    
   
128
        LOG.info("Assigning: " + Bytes.toStringBinary(hRegionInfo.getRegionName()));

    
   
129
        admin.assign(hRegionInfo.getRegionName());

    
   
130
      }

    
   
131
    }

    
   
132
  }

    
   
133

   

    
   
134
  /**

    
   
135
   * Fix up the directories before importing to match the region names of the

    
   
136
   * target table

    
   
137
   */

    
   
138
  private static Path fixRegionNames(Path oldTableDir, String newTableName)

    
   
139
      throws IOException {

    
   
140
    FileSystem fs = FileSystem.get(conf);

    
   
141
    Path newTableDir = new Path(oldTableDir.getParent(), tableName);

    
   
142
    if (!fs.mkdirs(newTableDir)) {

    
   
143
      throw new IOException("Mkdir failed for: " + newTableDir);

    
   
144
    }

    
   
145

   

    
   
146
    FileStatus[] oldFiles = fs.listStatus(oldTableDir);

    
   
147
    for (FileStatus oldFile : oldFiles) {

    
   
148
      LOG.debug("Checking file for renaming: " + oldFile.getPath().toString());

    
   
149

   

    
   
150
      if (!oldFile.isDir()) continue;

    
   
151
      if (oldFile.getPath().getName().equals(HConstants.HREGION_COMPACTIONDIR_NAME)) continue;

    
   
152
      if (oldFile.getPath().getName().equals(".tmp")) continue;

    
   
153

   

    
   
154
      Path oldRegionInfoPath = new Path(oldFile.getPath(), HRegion.REGIONINFO_FILE);

    
   
155
      if (!fs.exists(oldRegionInfoPath)) {

    
   
156
        throw new IOException("Missing regioninfo file for: " + oldFile.getPath().toString());

    
   
157
      }

    
   
158
      // read .regioninfo file

    
   
159
      FSDataInputStream regionInfoIn = fs.open(oldRegionInfoPath);

    
   
160
      HRegionInfo oldHRegionInfo = new HRegionInfo();

    
   
161
      oldHRegionInfo.readFields(regionInfoIn);

    
   
162
      regionInfoIn.close();

    
   
163
      fs.delete(oldRegionInfoPath, false);

    
   
164

   

    
   
165
      // get new RegionName

    
   
166
      HRegionInfo tableHRegionInfo = new HRegionInfo(Bytes.toBytes(tableName), oldHRegionInfo.getStartKey(), oldHRegionInfo.getEndKey());

    
   
167
      String tableEncodedRegionName = tableHRegionInfo.getEncodedName();

    
   
168
      Path tableFilePath = new Path(newTableDir, tableEncodedRegionName);

    
   
169
      if (!fs.rename(oldFile.getPath(), tableFilePath)) {

    
   
170
        throw new IOException("Rename failed for : " + oldFile.getPath().toString());

    
   
171
      }

    
   
172
      LOG.info("Renamed file: " + oldFile.getPath().toString() + " to file: " + tableFilePath.toString());

    
   
173

   

    
   
174
      // write out new regioninfo file

    
   
175
      Path tableRegionInfoPath = new Path(tableFilePath, HRegion.REGIONINFO_FILE);

    
   
176
      FSDataOutputStream regionInfoOut = fs.create(tableRegionInfoPath);

    
   
177
      tableHRegionInfo.write(regionInfoOut);

    
   
178
      regionInfoOut.close();

    
   
179
    }

    
   
180
    return newTableDir;

    
   
181
  }

    
   
182

   

    
   
183
  private static void parseArgs(String[] args) {

    
   
184
    try {

    
   
185
      for (int i = 0; i < args.length; i++) {

    
   
186
        if (args[i].equals("--table")) {

    
   
187
          tableName = args[++i];

    
   
188
        } else if (args[i].equals("--zookeeper")) {

    
   
189
          zkQuorum = args[++i];

    
   
190
        } else if (args[i].equals("--backup-dir")) {

    
   
191
          backupDir = args[++i];

    
   
192
        } else if (args[i].equals("--hdfs")) {

    
   
193
          dfs = args[++i];

    
   
194
        } else {

    
   
195
          printUsageAndExit("Unrecognized option: " + args[i]);

    
   
196
          System.exit(1);

    
   
197
        }

    
   
198
      }

    
   
199
      if (tableName == null || backupDir == null || dfs == null) {

    
   
200
        printUsageAndExit("Too few options, or option not set!");

    
   
201
      }

    
   
202
    } catch (ArrayIndexOutOfBoundsException e) {

    
   
203
      printUsageAndExit("Too few options, or option not set!");

    
   
204
    }

    
   
205
  }

    
   
206

   

    
   
207
  protected static void printUsageAndExit(String msg) {

    
   
208
    System.out.println("Usage: ImportTable\n"

    
   
209
        + "--table <name of new table>\n"

    
   
210
        + "--hdfs <hdfs://<namenode>:9000>\n"

    
   
211
        + "--backup-dir <backup-directory>\n"

    
   
212
        + "[--zookeeper <zookeeper quorum>] "

    
   
213
        + "(used to assign the regions in a life HBase cluster)\n"

    
   
214
        + "*Note*: The backup files are *moved* into position under the HBase "

    
   
215
        + "root directory, i.e. the backup is destroyed in the process!\n"

    
   
216
        + "If this is not desired the backup directory should be copied "

    
   
217
        + "before the import is initiated.");

    
   
218
    System.exit(1);

    
   
219
  }

    
   
220

   

    
   
221
}
http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/backups/Snapshot.java
New File
 
http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/backups/SnapshotInputFormat.java
New File
 
http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/backups/SnapshotMR.java
New File
 
http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/backups/SnapshotUtilities.java
New File
 
http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/ipc/HRegionInterface.java
Revision 1301687 New Change
 
http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/util/FSTableDescriptors.java
Revision 1301687 New Change
 
  1. http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/backups/ImportTable.java: Loading...
  2. http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/backups/Snapshot.java: Loading...
  3. http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/backups/SnapshotInputFormat.java: Loading...
  4. http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/backups/SnapshotMR.java: Loading...
  5. http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/backups/SnapshotUtilities.java: Loading...
  6. http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/ipc/HRegionInterface.java: Loading...
  7. http://svn.apache.org/repos/asf/hbase/trunk/src/main/java/org/apache/hadoop/hbase/util/FSTableDescriptors.java: Loading...