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 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.NoSuchElementException;
26 import java.util.Random;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.fs.FileSystem;
32 import org.apache.hadoop.fs.Path;
33 import org.apache.hadoop.hbase.HConstants;
34 import org.apache.hadoop.hbase.HRegionInfo;
35 import org.apache.hadoop.hbase.HTableDescriptor;
36 import org.apache.hadoop.hbase.KeyValue;
37 import org.apache.hadoop.hbase.RemoteExceptionHandler;
38 import org.apache.hadoop.hbase.TableNotDisabledException;
39 import org.apache.hadoop.hbase.client.Delete;
40 import org.apache.hadoop.hbase.client.HBaseAdmin;
41 import org.apache.hadoop.hbase.client.HConnection;
42 import org.apache.hadoop.hbase.client.HConnectionManager;
43 import org.apache.hadoop.hbase.client.HConnectionManager.HConnectable;
44 import org.apache.hadoop.hbase.client.HTable;
45 import org.apache.hadoop.hbase.client.Put;
46 import org.apache.hadoop.hbase.client.Result;
47 import org.apache.hadoop.hbase.client.ResultScanner;
48 import org.apache.hadoop.hbase.client.Scan;
49 import org.apache.hadoop.hbase.regionserver.HRegion;
50 import org.apache.hadoop.hbase.regionserver.InternalScanner;
51 import org.apache.hadoop.hbase.regionserver.wal.HLog;
52
53
54
55
56
57 class HMerge {
58
59 static final Log LOG = LogFactory.getLog(HMerge.class);
60 static final Random rand = new Random();
61
62
63
64
65 private HMerge() {
66 super();
67 }
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public static void merge(Configuration conf, FileSystem fs,
83 final byte [] tableName)
84 throws IOException {
85 merge(conf, fs, tableName, true);
86 }
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 public static void merge(Configuration conf, FileSystem fs,
104 final byte [] tableName, final boolean testMasterRunning)
105 throws IOException {
106 boolean masterIsRunning = false;
107 if (testMasterRunning) {
108 masterIsRunning = HConnectionManager
109 .execute(new HConnectable<Boolean>(conf) {
110 @Override
111 public Boolean connect(HConnection connection) throws IOException {
112 return connection.isMasterRunning();
113 }
114 });
115 }
116 if (Bytes.equals(tableName, HConstants.META_TABLE_NAME)) {
117 if (masterIsRunning) {
118 throw new IllegalStateException(
119 "Can not compact META table if instance is on-line");
120 }
121 new OfflineMerger(conf, fs).process();
122 } else {
123 if(!masterIsRunning) {
124 throw new IllegalStateException(
125 "HBase instance must be running to merge a normal table");
126 }
127 HBaseAdmin admin = new HBaseAdmin(conf);
128 if (!admin.isTableDisabled(tableName)) {
129 throw new TableNotDisabledException(tableName);
130 }
131 new OnlineMerger(conf, fs, tableName).process();
132 }
133 }
134
135 private static abstract class Merger {
136 protected final Configuration conf;
137 protected final FileSystem fs;
138 protected final Path tabledir;
139 protected final HTableDescriptor htd;
140 protected final HLog hlog;
141 private final long maxFilesize;
142
143
144 protected Merger(Configuration conf, FileSystem fs, final byte [] tableName)
145 throws IOException {
146 this.conf = conf;
147 this.fs = fs;
148 this.maxFilesize = conf.getLong(HConstants.HREGION_MAX_FILESIZE,
149 HConstants.DEFAULT_MAX_FILE_SIZE);
150
151 this.tabledir = new Path(
152 fs.makeQualified(new Path(conf.get(HConstants.HBASE_DIR))),
153 Bytes.toString(tableName)
154 );
155 this.htd = FSTableDescriptors.getTableDescriptor(this.fs, this.tabledir);
156 Path logdir = new Path(tabledir, "merge_" + System.currentTimeMillis() +
157 HConstants.HREGION_LOGDIR_NAME);
158 Path oldLogDir = new Path(tabledir, HConstants.HREGION_OLDLOGDIR_NAME);
159 this.hlog = new HLog(fs, logdir, oldLogDir, conf);
160 }
161
162 void process() throws IOException {
163 try {
164 for (HRegionInfo[] regionsToMerge = next();
165 regionsToMerge != null;
166 regionsToMerge = next()) {
167 if (!merge(regionsToMerge)) {
168 return;
169 }
170 }
171 } finally {
172 try {
173 hlog.closeAndDelete();
174
175 } catch(IOException e) {
176 LOG.error(e);
177 }
178 }
179 }
180
181 protected boolean merge(final HRegionInfo[] info) throws IOException {
182 if (info.length < 2) {
183 LOG.info("only one region - nothing to merge");
184 return false;
185 }
186
187 HRegion currentRegion = null;
188 long currentSize = 0;
189 HRegion nextRegion = null;
190 long nextSize = 0;
191 for (int i = 0; i < info.length - 1; i++) {
192 if (currentRegion == null) {
193 currentRegion = HRegion.newHRegion(tabledir, hlog, fs, conf, info[i],
194 this.htd, null);
195 currentRegion.initialize();
196 currentSize = currentRegion.getLargestHStoreSize();
197 }
198 nextRegion = HRegion.newHRegion(tabledir, hlog, fs, conf, info[i + 1],
199 this.htd, null);
200 nextRegion.initialize();
201 nextSize = nextRegion.getLargestHStoreSize();
202
203 if ((currentSize + nextSize) <= (maxFilesize / 2)) {
204
205
206 LOG.info("Merging regions " + currentRegion.getRegionNameAsString() +
207 " and " + nextRegion.getRegionNameAsString());
208 HRegion mergedRegion =
209 HRegion.mergeAdjacent(currentRegion, nextRegion);
210 updateMeta(currentRegion.getRegionName(), nextRegion.getRegionName(),
211 mergedRegion);
212 break;
213 }
214 LOG.info("not merging regions " + Bytes.toStringBinary(currentRegion.getRegionName())
215 + " and " + Bytes.toStringBinary(nextRegion.getRegionName()));
216 currentRegion.close();
217 currentRegion = nextRegion;
218 currentSize = nextSize;
219 }
220 if(currentRegion != null) {
221 currentRegion.close();
222 }
223 return true;
224 }
225
226 protected abstract HRegionInfo[] next() throws IOException;
227
228 protected abstract void updateMeta(final byte [] oldRegion1,
229 final byte [] oldRegion2, HRegion newRegion)
230 throws IOException;
231
232 }
233
234
235 private static class OnlineMerger extends Merger {
236 private final byte [] tableName;
237 private final HTable table;
238 private final ResultScanner metaScanner;
239 private HRegionInfo latestRegion;
240
241 OnlineMerger(Configuration conf, FileSystem fs,
242 final byte [] tableName)
243 throws IOException {
244 super(conf, fs, tableName);
245 this.tableName = tableName;
246 this.table = new HTable(conf, HConstants.META_TABLE_NAME);
247 this.metaScanner = table.getScanner(HConstants.CATALOG_FAMILY,
248 HConstants.REGIONINFO_QUALIFIER);
249 this.latestRegion = null;
250 }
251
252 private HRegionInfo nextRegion() throws IOException {
253 try {
254 Result results = getMetaRow();
255 if (results == null) {
256 return null;
257 }
258 byte[] regionInfoValue = results.getValue(HConstants.CATALOG_FAMILY,
259 HConstants.REGIONINFO_QUALIFIER);
260 if (regionInfoValue == null || regionInfoValue.length == 0) {
261 throw new NoSuchElementException("meta region entry missing " +
262 Bytes.toString(HConstants.CATALOG_FAMILY) + ":" +
263 Bytes.toString(HConstants.REGIONINFO_QUALIFIER));
264 }
265 HRegionInfo region = Writables.getHRegionInfo(regionInfoValue);
266 if (!Bytes.equals(region.getTableName(), this.tableName)) {
267 return null;
268 }
269 return region;
270 } catch (IOException e) {
271 e = RemoteExceptionHandler.checkIOException(e);
272 LOG.error("meta scanner error", e);
273 metaScanner.close();
274 throw e;
275 }
276 }
277
278
279
280
281
282
283 private Result getMetaRow() throws IOException {
284 Result currentRow = metaScanner.next();
285 boolean foundResult = false;
286 while (currentRow != null) {
287 LOG.info("Row: <" + Bytes.toStringBinary(currentRow.getRow()) + ">");
288 byte[] regionInfoValue = currentRow.getValue(HConstants.CATALOG_FAMILY,
289 HConstants.REGIONINFO_QUALIFIER);
290 if (regionInfoValue == null || regionInfoValue.length == 0) {
291 currentRow = metaScanner.next();
292 continue;
293 }
294 foundResult = true;
295 break;
296 }
297 return foundResult ? currentRow : null;
298 }
299
300 @Override
301 protected HRegionInfo[] next() throws IOException {
302 List<HRegionInfo> regions = new ArrayList<HRegionInfo>();
303 if(latestRegion == null) {
304 latestRegion = nextRegion();
305 }
306 if(latestRegion != null) {
307 regions.add(latestRegion);
308 }
309 latestRegion = nextRegion();
310 if(latestRegion != null) {
311 regions.add(latestRegion);
312 }
313 return regions.toArray(new HRegionInfo[regions.size()]);
314 }
315
316 @Override
317 protected void updateMeta(final byte [] oldRegion1,
318 final byte [] oldRegion2,
319 HRegion newRegion)
320 throws IOException {
321 byte[][] regionsToDelete = {oldRegion1, oldRegion2};
322 for (int r = 0; r < regionsToDelete.length; r++) {
323 if(Bytes.equals(regionsToDelete[r], latestRegion.getRegionName())) {
324 latestRegion = null;
325 }
326 Delete delete = new Delete(regionsToDelete[r]);
327 table.delete(delete);
328 if(LOG.isDebugEnabled()) {
329 LOG.debug("updated columns in row: " + Bytes.toStringBinary(regionsToDelete[r]));
330 }
331 }
332 newRegion.getRegionInfo().setOffline(true);
333
334 Put put = new Put(newRegion.getRegionName());
335 put.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
336 Writables.getBytes(newRegion.getRegionInfo()));
337 table.put(put);
338
339 if(LOG.isDebugEnabled()) {
340 LOG.debug("updated columns in row: "
341 + Bytes.toStringBinary(newRegion.getRegionName()));
342 }
343 }
344 }
345
346
347 private static class OfflineMerger extends Merger {
348 private final List<HRegionInfo> metaRegions = new ArrayList<HRegionInfo>();
349 private final HRegion root;
350
351 OfflineMerger(Configuration conf, FileSystem fs)
352 throws IOException {
353 super(conf, fs, HConstants.META_TABLE_NAME);
354
355 Path rootTableDir = HTableDescriptor.getTableDir(
356 fs.makeQualified(new Path(conf.get(HConstants.HBASE_DIR))),
357 HConstants.ROOT_TABLE_NAME);
358
359
360
361 root = HRegion.newHRegion(rootTableDir, hlog, fs, conf,
362 HRegionInfo.ROOT_REGIONINFO, HTableDescriptor.ROOT_TABLEDESC, null);
363 root.initialize();
364
365 Scan scan = new Scan();
366 scan.addColumn(HConstants.CATALOG_FAMILY,
367 HConstants.REGIONINFO_QUALIFIER);
368 InternalScanner rootScanner =
369 root.getScanner(scan);
370
371 try {
372 List<KeyValue> results = new ArrayList<KeyValue>();
373 boolean hasMore;
374 do {
375 hasMore = rootScanner.next(results);
376 for(KeyValue kv: results) {
377 HRegionInfo info = Writables.getHRegionInfoOrNull(kv.getValue());
378 if (info != null) {
379 metaRegions.add(info);
380 }
381 }
382 } while (hasMore);
383 } finally {
384 rootScanner.close();
385 try {
386 root.close();
387
388 } catch(IOException e) {
389 LOG.error(e);
390 }
391 }
392 }
393
394 @Override
395 protected HRegionInfo[] next() {
396 HRegionInfo[] results = null;
397 if (metaRegions.size() > 0) {
398 results = metaRegions.toArray(new HRegionInfo[metaRegions.size()]);
399 metaRegions.clear();
400 }
401 return results;
402 }
403
404 @Override
405 protected void updateMeta(final byte [] oldRegion1,
406 final byte [] oldRegion2, HRegion newRegion)
407 throws IOException {
408 byte[][] regionsToDelete = {oldRegion1, oldRegion2};
409 for(int r = 0; r < regionsToDelete.length; r++) {
410 Delete delete = new Delete(regionsToDelete[r]);
411 delete.deleteColumns(HConstants.CATALOG_FAMILY,
412 HConstants.REGIONINFO_QUALIFIER);
413 delete.deleteColumns(HConstants.CATALOG_FAMILY,
414 HConstants.SERVER_QUALIFIER);
415 delete.deleteColumns(HConstants.CATALOG_FAMILY,
416 HConstants.STARTCODE_QUALIFIER);
417 delete.deleteColumns(HConstants.CATALOG_FAMILY,
418 HConstants.SPLITA_QUALIFIER);
419 delete.deleteColumns(HConstants.CATALOG_FAMILY,
420 HConstants.SPLITB_QUALIFIER);
421 root.delete(delete, null, true);
422
423 if(LOG.isDebugEnabled()) {
424 LOG.debug("updated columns in row: " + Bytes.toStringBinary(regionsToDelete[r]));
425 }
426 }
427 HRegionInfo newInfo = newRegion.getRegionInfo();
428 newInfo.setOffline(true);
429 Put put = new Put(newRegion.getRegionName());
430 put.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
431 Writables.getBytes(newInfo));
432 root.put(put);
433 if(LOG.isDebugEnabled()) {
434 LOG.debug("updated columns in row: " + Bytes.toStringBinary(newRegion.getRegionName()));
435 }
436 }
437 }
438 }