1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.util;
20
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.NoSuchElementException;
25 import java.util.Random;
26
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.conf.Configuration;
31 import org.apache.hadoop.fs.FileSystem;
32 import org.apache.hadoop.fs.Path;
33 import org.apache.hadoop.hbase.TableName;
34 import org.apache.hadoop.hbase.HConstants;
35 import org.apache.hadoop.hbase.HRegionInfo;
36 import org.apache.hadoop.hbase.HTableDescriptor;
37 import org.apache.hadoop.hbase.RemoteExceptionHandler;
38 import org.apache.hadoop.hbase.TableNotDisabledException;
39 import org.apache.hadoop.hbase.MetaTableAccessor;
40 import org.apache.hadoop.hbase.client.Admin;
41 import org.apache.hadoop.hbase.client.Delete;
42 import org.apache.hadoop.hbase.client.HBaseAdmin;
43 import org.apache.hadoop.hbase.client.HConnectable;
44 import org.apache.hadoop.hbase.client.HConnection;
45 import org.apache.hadoop.hbase.client.HConnectionManager;
46 import org.apache.hadoop.hbase.client.HTable;
47 import org.apache.hadoop.hbase.client.Result;
48 import org.apache.hadoop.hbase.client.ResultScanner;
49 import org.apache.hadoop.hbase.client.Table;
50 import org.apache.hadoop.hbase.regionserver.HRegion;
51 import org.apache.hadoop.hbase.wal.WALFactory;
52
53
54
55
56
57 @InterfaceAudience.Private
58 class HMerge {
59
60 private static final Log LOG = LogFactory.getLog(HMerge.class);
61 static final Random rand = new Random();
62
63
64
65
66 private HMerge() {
67 super();
68 }
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 public static void merge(Configuration conf, FileSystem fs,
84 final TableName tableName)
85 throws IOException {
86 merge(conf, fs, tableName, true);
87 }
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 public static void merge(Configuration conf, FileSystem fs,
105 final TableName tableName, final boolean testMasterRunning)
106 throws IOException {
107 boolean masterIsRunning = false;
108 if (testMasterRunning) {
109 masterIsRunning = HConnectionManager
110 .execute(new HConnectable<Boolean>(conf) {
111 @Override
112 public Boolean connect(HConnection connection) throws IOException {
113 return connection.isMasterRunning();
114 }
115 });
116 }
117 if (tableName.equals(TableName.META_TABLE_NAME)) {
118 if (masterIsRunning) {
119 throw new IllegalStateException(
120 "Can not compact hbase:meta table if instance is on-line");
121 }
122
123 } else {
124 if(!masterIsRunning) {
125 throw new IllegalStateException(
126 "HBase instance must be running to merge a normal table");
127 }
128 Admin admin = new HBaseAdmin(conf);
129 try {
130 if (!admin.isTableDisabled(tableName)) {
131 throw new TableNotDisabledException(tableName);
132 }
133 } finally {
134 admin.close();
135 }
136 new OnlineMerger(conf, fs, tableName).process();
137 }
138 }
139
140 private static abstract class Merger {
141 protected final Configuration conf;
142 protected final FileSystem fs;
143 protected final Path rootDir;
144 protected final HTableDescriptor htd;
145 protected final WALFactory walFactory;
146 private final long maxFilesize;
147
148
149 protected Merger(Configuration conf, FileSystem fs, final TableName tableName)
150 throws IOException {
151 this.conf = conf;
152 this.fs = fs;
153 this.maxFilesize = conf.getLong(HConstants.HREGION_MAX_FILESIZE,
154 HConstants.DEFAULT_MAX_FILE_SIZE);
155
156 this.rootDir = FSUtils.getRootDir(conf);
157 Path tabledir = FSUtils.getTableDir(this.rootDir, tableName);
158 this.htd = FSTableDescriptors.getTableDescriptorFromFs(this.fs, tabledir);
159 String logname = "merge_" + System.currentTimeMillis() + HConstants.HREGION_LOGDIR_NAME;
160
161 final Configuration walConf = new Configuration(conf);
162 FSUtils.setRootDir(walConf, tabledir);
163 this.walFactory = new WALFactory(walConf, null, logname);
164 }
165
166 void process() throws IOException {
167 try {
168 for (HRegionInfo[] regionsToMerge = next();
169 regionsToMerge != null;
170 regionsToMerge = next()) {
171 if (!merge(regionsToMerge)) {
172 return;
173 }
174 }
175 } finally {
176 try {
177 walFactory.close();
178 } catch(IOException e) {
179 LOG.error(e);
180 }
181 }
182 }
183
184 protected boolean merge(final HRegionInfo[] info) throws IOException {
185 if (info.length < 2) {
186 LOG.info("only one region - nothing to merge");
187 return false;
188 }
189
190 HRegion currentRegion = null;
191 long currentSize = 0;
192 HRegion nextRegion = null;
193 long nextSize = 0;
194 for (int i = 0; i < info.length - 1; i++) {
195 if (currentRegion == null) {
196 currentRegion = HRegion.openHRegion(conf, fs, this.rootDir, info[i], this.htd,
197 walFactory.getWAL(info[i].getEncodedNameAsBytes(),
198 info[i].getTable().getNamespace()));
199 currentSize = currentRegion.getLargestHStoreSize();
200 }
201 nextRegion = HRegion.openHRegion(conf, fs, this.rootDir, info[i + 1], this.htd,
202 walFactory.getWAL(info[i + 1].getEncodedNameAsBytes(),
203 info[i + 1].getTable().getNamespace()));
204 nextSize = nextRegion.getLargestHStoreSize();
205
206 if ((currentSize + nextSize) <= (maxFilesize / 2)) {
207
208
209 LOG.info("Merging regions " + currentRegion.getRegionInfo().getRegionNameAsString() +
210 " and " + nextRegion.getRegionInfo().getRegionNameAsString());
211 HRegion mergedRegion =
212 HRegion.mergeAdjacent(currentRegion, nextRegion);
213 updateMeta(currentRegion.getRegionInfo().getRegionName(),
214 nextRegion.getRegionInfo().getRegionName(), mergedRegion);
215 break;
216 }
217 LOG.info("not merging regions " +
218 Bytes.toStringBinary(currentRegion.getRegionInfo().getRegionName()) +
219 " and " + Bytes.toStringBinary(nextRegion.getRegionInfo().getRegionName()));
220 currentRegion.close();
221 currentRegion = nextRegion;
222 currentSize = nextSize;
223 }
224 if(currentRegion != null) {
225 currentRegion.close();
226 }
227 return true;
228 }
229
230 protected abstract HRegionInfo[] next() throws IOException;
231
232 protected abstract void updateMeta(final byte [] oldRegion1,
233 final byte [] oldRegion2, HRegion newRegion)
234 throws IOException;
235
236 }
237
238
239 private static class OnlineMerger extends Merger {
240 private final TableName tableName;
241 private final Table table;
242 private final ResultScanner metaScanner;
243 private HRegionInfo latestRegion;
244
245 OnlineMerger(Configuration conf, FileSystem fs,
246 final TableName tableName)
247 throws IOException {
248 super(conf, fs, tableName);
249 this.tableName = tableName;
250 this.table = new HTable(conf, TableName.META_TABLE_NAME);
251 this.metaScanner = table.getScanner(HConstants.CATALOG_FAMILY,
252 HConstants.REGIONINFO_QUALIFIER);
253 this.latestRegion = null;
254 }
255
256 private HRegionInfo nextRegion() throws IOException {
257 try {
258 Result results = getMetaRow();
259 if (results == null) {
260 return null;
261 }
262 HRegionInfo region = HRegionInfo.getHRegionInfo(results);
263 if (region == null) {
264 throw new NoSuchElementException("meta region entry missing " +
265 Bytes.toString(HConstants.CATALOG_FAMILY) + ":" +
266 Bytes.toString(HConstants.REGIONINFO_QUALIFIER));
267 }
268 if (!region.getTable().equals(this.tableName)) {
269 return null;
270 }
271 return region;
272 } catch (IOException e) {
273 e = RemoteExceptionHandler.checkIOException(e);
274 LOG.error("meta scanner error", e);
275 metaScanner.close();
276 throw e;
277 }
278 }
279
280
281
282
283
284
285 private Result getMetaRow() throws IOException {
286 Result currentRow = metaScanner.next();
287 boolean foundResult = false;
288 while (currentRow != null) {
289 LOG.info("Row: <" + Bytes.toStringBinary(currentRow.getRow()) + ">");
290 byte[] regionInfoValue = currentRow.getValue(HConstants.CATALOG_FAMILY,
291 HConstants.REGIONINFO_QUALIFIER);
292 if (regionInfoValue == null || regionInfoValue.length == 0) {
293 currentRow = metaScanner.next();
294 continue;
295 }
296 HRegionInfo region = HRegionInfo.getHRegionInfo(currentRow);
297 if (!region.getTable().equals(this.tableName)) {
298 currentRow = metaScanner.next();
299 continue;
300 }
301 foundResult = true;
302 break;
303 }
304 return foundResult ? currentRow : null;
305 }
306
307 @Override
308 protected HRegionInfo[] next() throws IOException {
309 List<HRegionInfo> regions = new ArrayList<HRegionInfo>();
310 if(latestRegion == null) {
311 latestRegion = nextRegion();
312 }
313 if(latestRegion != null) {
314 regions.add(latestRegion);
315 }
316 latestRegion = nextRegion();
317 if(latestRegion != null) {
318 regions.add(latestRegion);
319 }
320 return regions.toArray(new HRegionInfo[regions.size()]);
321 }
322
323 @Override
324 protected void updateMeta(final byte [] oldRegion1,
325 final byte [] oldRegion2,
326 HRegion newRegion)
327 throws IOException {
328 byte[][] regionsToDelete = {oldRegion1, oldRegion2};
329 for (int r = 0; r < regionsToDelete.length; r++) {
330 if(Bytes.equals(regionsToDelete[r], latestRegion.getRegionName())) {
331 latestRegion = null;
332 }
333 Delete delete = new Delete(regionsToDelete[r]);
334 table.delete(delete);
335 if(LOG.isDebugEnabled()) {
336 LOG.debug("updated columns in row: " + Bytes.toStringBinary(regionsToDelete[r]));
337 }
338 }
339 newRegion.getRegionInfo().setOffline(true);
340
341 MetaTableAccessor.addRegionToMeta(table, newRegion.getRegionInfo());
342
343 if(LOG.isDebugEnabled()) {
344 LOG.debug("updated columns in row: "
345 + Bytes.toStringBinary(newRegion.getRegionInfo().getRegionName()));
346 }
347 }
348 }
349 }