1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.util;
21
22 import java.io.IOException;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.hadoop.hbase.MetaTableAccessor;
27 import org.apache.hadoop.hbase.classification.InterfaceAudience;
28 import org.apache.hadoop.conf.Configured;
29 import org.apache.hadoop.fs.FileSystem;
30 import org.apache.hadoop.fs.Path;
31 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
32 import org.apache.hadoop.hbase.TableDescriptor;
33 import org.apache.hadoop.hbase.TableName;
34 import org.apache.hadoop.hbase.HBaseConfiguration;
35 import org.apache.hadoop.hbase.HConstants;
36 import org.apache.hadoop.hbase.HRegionInfo;
37 import org.apache.hadoop.hbase.HTableDescriptor;
38 import org.apache.hadoop.hbase.MasterNotRunningException;
39 import org.apache.hadoop.hbase.ZooKeeperConnectionException;
40 import org.apache.hadoop.hbase.client.Delete;
41 import org.apache.hadoop.hbase.client.Get;
42 import org.apache.hadoop.hbase.client.HBaseAdmin;
43 import org.apache.hadoop.hbase.client.Result;
44 import org.apache.hadoop.hbase.regionserver.HRegion;
45 import org.apache.hadoop.io.WritableComparator;
46 import org.apache.hadoop.util.Tool;
47 import org.apache.hadoop.util.ToolRunner;
48
49 import com.google.common.base.Preconditions;
50
51
52
53
54
55 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
56 public class Merge extends Configured implements Tool {
57 private static final Log LOG = LogFactory.getLog(Merge.class);
58 private Path rootdir;
59 private volatile MetaUtils utils;
60 private TableName tableName;
61 private volatile byte [] region1;
62 private volatile byte [] region2;
63 private volatile HRegionInfo mergeInfo = null;
64
65 @Override
66 public int run(String[] args) throws Exception {
67 if (parseArgs(args) != 0) {
68 return -1;
69 }
70
71
72 FileSystem fs = FileSystem.get(getConf());
73 LOG.info("Verifying that file system is available...");
74 try {
75 FSUtils.checkFileSystemAvailable(fs);
76 } catch (IOException e) {
77 LOG.fatal("File system is not available", e);
78 return -1;
79 }
80
81
82 LOG.info("Verifying that HBase is not running...");
83 try {
84 HBaseAdmin.checkHBaseAvailable(getConf());
85 LOG.fatal("HBase cluster must be off-line, and is not. Aborting.");
86 return -1;
87 } catch (ZooKeeperConnectionException zkce) {
88
89 } catch (MasterNotRunningException e) {
90
91 }
92
93
94
95 this.utils = new MetaUtils(getConf());
96 this.rootdir = FSUtils.getRootDir(getConf());
97 try {
98 mergeTwoRegions();
99 return 0;
100 } catch (IOException e) {
101 LOG.fatal("Merge failed", e);
102 return -1;
103
104 } finally {
105 if (this.utils != null) {
106 this.utils.shutdown();
107 }
108 }
109 }
110
111
112 HRegionInfo getMergedHRegionInfo() {
113 return this.mergeInfo;
114 }
115
116
117
118
119 private void mergeTwoRegions() throws IOException {
120 LOG.info("Merging regions " + Bytes.toStringBinary(this.region1) + " and " +
121 Bytes.toStringBinary(this.region2) + " in table " + this.tableName);
122 HRegion meta = this.utils.getMetaRegion();
123 Get get = new Get(region1);
124 get.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
125 Result result1 = meta.get(get);
126 Preconditions.checkState(!result1.isEmpty(),
127 "First region cells can not be null");
128 HRegionInfo info1 = MetaTableAccessor.getHRegionInfo(result1);
129 if (info1 == null) {
130 throw new NullPointerException("info1 is null using key " +
131 Bytes.toStringBinary(region1) + " in " + meta);
132 }
133 get = new Get(region2);
134 get.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
135 Result result2 = meta.get(get);
136 Preconditions.checkState(!result2.isEmpty(),
137 "Second region cells can not be null");
138 HRegionInfo info2 = MetaTableAccessor.getHRegionInfo(result2);
139 if (info2 == null) {
140 throw new NullPointerException("info2 is null using key " + meta);
141 }
142 TableDescriptor htd = FSTableDescriptors.getTableDescriptorFromFs(FileSystem.get(getConf()),
143 this.rootdir, this.tableName);
144 HRegion merged = merge(htd.getHTableDescriptor(), meta, info1, info2);
145
146 LOG.info("Adding " + merged.getRegionInfo() + " to " +
147 meta.getRegionInfo());
148
149 HRegion.addRegionToMETA(meta, merged);
150 merged.close();
151 }
152
153
154
155
156
157 private HRegion merge(final HTableDescriptor htd, HRegion meta,
158 HRegionInfo info1, HRegionInfo info2)
159 throws IOException {
160 if (info1 == null) {
161 throw new IOException("Could not find " + Bytes.toStringBinary(region1) + " in " +
162 Bytes.toStringBinary(meta.getRegionInfo().getRegionName()));
163 }
164 if (info2 == null) {
165 throw new IOException("Could not find " + Bytes.toStringBinary(region2) + " in " +
166 Bytes.toStringBinary(meta.getRegionInfo().getRegionName()));
167 }
168 HRegion merged = null;
169 HRegion r1 = HRegion.openHRegion(info1, htd, utils.getLog(info1), getConf());
170 try {
171 HRegion r2 = HRegion.openHRegion(info2, htd, utils.getLog(info2), getConf());
172 try {
173 merged = HRegion.merge(r1, r2);
174 } finally {
175 if (!r2.isClosed()) {
176 r2.close();
177 }
178 }
179 } finally {
180 if (!r1.isClosed()) {
181 r1.close();
182 }
183 }
184
185
186
187
188 removeRegionFromMeta(meta, info1);
189 removeRegionFromMeta(meta, info2);
190
191 this.mergeInfo = merged.getRegionInfo();
192 return merged;
193 }
194
195
196
197
198
199
200
201
202
203
204 private void removeRegionFromMeta(HRegion meta, HRegionInfo regioninfo)
205 throws IOException {
206 if (LOG.isDebugEnabled()) {
207 LOG.debug("Removing region: " + regioninfo + " from " + meta);
208 }
209
210 Delete delete = new Delete(regioninfo.getRegionName(),
211 System.currentTimeMillis());
212 meta.delete(delete);
213 }
214
215
216
217
218
219
220
221
222
223 private int parseArgs(String[] args) throws IOException {
224 if (args.length != 3) {
225 usage();
226 return -1;
227 }
228 tableName = TableName.valueOf(args[0]);
229
230 region1 = Bytes.toBytesBinary(args[1]);
231 region2 = Bytes.toBytesBinary(args[2]);
232 int status = 0;
233 if (notInTable(tableName, region1) || notInTable(tableName, region2)) {
234 status = -1;
235 } else if (Bytes.equals(region1, region2)) {
236 LOG.error("Can't merge a region with itself");
237 status = -1;
238 }
239 return status;
240 }
241
242 private boolean notInTable(final TableName tn, final byte [] rn) {
243 if (WritableComparator.compareBytes(tn.getName(), 0, tn.getName().length,
244 rn, 0, tn.getName().length) != 0) {
245 LOG.error("Region " + Bytes.toStringBinary(rn) + " does not belong to table " +
246 tn);
247 return true;
248 }
249 return false;
250 }
251
252 private void usage() {
253 System.err
254 .println("For hadoop 0.21+, Usage: bin/hbase org.apache.hadoop.hbase.util.Merge "
255 + "[-Dfs.defaultFS=hdfs://nn:port] <table-name> <region-1> <region-2>\n");
256 }
257
258 public static void main(String[] args) {
259 int status;
260 try {
261 status = ToolRunner.run(HBaseConfiguration.create(), new Merge(), args);
262 } catch (Exception e) {
263 LOG.error("exiting due to error", e);
264 status = -1;
265 }
266 System.exit(status);
267 }
268 }