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.regionserver;
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.ListIterator;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.conf.Configuration;
30 import org.apache.hadoop.fs.FileStatus;
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.client.HTable;
36 import org.apache.hadoop.hbase.client.Put;
37 import org.apache.hadoop.hbase.io.Reference.Range;
38 import org.apache.hadoop.hbase.util.Bytes;
39 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
40 import org.apache.hadoop.hbase.util.FSUtils;
41 import org.apache.hadoop.hbase.util.PairOfSameType;
42 import org.apache.hadoop.hbase.util.Writables;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 class SplitTransaction {
68 private static final Log LOG = LogFactory.getLog(SplitTransaction.class);
69 private static final String SPLITDIR = "splits";
70
71
72
73
74 private final HRegion parent;
75 private HRegionInfo hri_a;
76 private HRegionInfo hri_b;
77 private Path splitdir;
78
79
80
81
82 private final byte [] splitrow;
83
84
85
86
87 enum JournalEntry {
88
89
90
91 CREATE_SPLIT_DIR,
92
93
94
95 CLOSED_PARENT_REGION,
96
97
98
99 OFFLINED_PARENT,
100
101
102
103 STARTED_REGION_A_CREATION,
104
105
106
107 STARTED_REGION_B_CREATION
108 }
109
110
111
112
113 private final List<JournalEntry> journal = new ArrayList<JournalEntry>();
114
115
116
117
118
119
120
121 SplitTransaction(final HRegion r, final byte [] splitrow) {
122 this.parent = r;
123 this.splitrow = splitrow;
124 this.splitdir = getSplitDir(this.parent);
125 }
126
127
128
129
130
131
132 public boolean prepare() {
133 if (this.parent.isClosed() || this.parent.isClosing()) return false;
134 HRegionInfo hri = this.parent.getRegionInfo();
135
136 byte [] startKey = hri.getStartKey();
137 byte [] endKey = hri.getEndKey();
138 if (Bytes.equals(startKey, splitrow) ||
139 !this.parent.getRegionInfo().containsRow(splitrow)) {
140 LOG.info("Split row is not inside region key range or is equal to " +
141 "startkey: " + Bytes.toString(this.splitrow));
142 return false;
143 }
144 long rid = getDaughterRegionIdTimestamp(hri);
145 this.hri_a = new HRegionInfo(hri.getTableDesc(), startKey, this.splitrow,
146 false, rid);
147 this.hri_b = new HRegionInfo(hri.getTableDesc(), this.splitrow, endKey,
148 false, rid);
149 return true;
150 }
151
152
153
154
155
156
157 private static long getDaughterRegionIdTimestamp(final HRegionInfo hri) {
158 long rid = EnvironmentEdgeManager.currentTimeMillis();
159
160
161 if (rid < hri.getRegionId()) {
162 LOG.warn("Clock skew; parent regions id is " + hri.getRegionId() +
163 " but current time here is " + rid);
164 rid = hri.getRegionId() + 1;
165 }
166 return rid;
167 }
168
169
170
171
172
173
174
175
176 public PairOfSameType<HRegion> execute(final OnlineRegions or) throws IOException {
177 return execute(or, or != null);
178 }
179
180
181
182
183
184
185
186
187
188
189 PairOfSameType<HRegion> execute(final OnlineRegions or, final boolean updateMeta)
190 throws IOException {
191 LOG.info("Starting split of region " + this.parent);
192 assert !this.parent.lock.writeLock().isHeldByCurrentThread() : "Unsafe to hold write lock while performing RPCs";
193
194
195
196 HTable t = null;
197 if (updateMeta) t = getTable(this.parent.getConf());
198
199 createSplitDir(this.parent.getFilesystem(), this.splitdir);
200 this.journal.add(JournalEntry.CREATE_SPLIT_DIR);
201
202 List<StoreFile> hstoreFilesToSplit = this.parent.close(false);
203 this.journal.add(JournalEntry.CLOSED_PARENT_REGION);
204
205 if (or != null) or.removeFromOnlineRegions(this.parent.getRegionInfo());
206 this.journal.add(JournalEntry.OFFLINED_PARENT);
207
208 splitStoreFiles(this.splitdir, hstoreFilesToSplit);
209
210
211
212
213
214
215
216
217 this.journal.add(JournalEntry.STARTED_REGION_A_CREATION);
218 HRegion a = createDaughterRegion(this.hri_a);
219
220
221 this.journal.add(JournalEntry.STARTED_REGION_B_CREATION);
222 HRegion b = createDaughterRegion(this.hri_b);
223
224 Put editParentPut = createOfflineParentPut();
225 if (t != null) t.put(editParentPut);
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252 HRegionInfo hri = this.hri_a;
253 try {
254 if (t != null) t.put(createDaughterPut(hri));
255 hri = this.hri_b;
256 if (t != null) t.put(createDaughterPut(hri));
257 } catch (IOException e) {
258
259 LOG.warn("Failed adding daughter " + hri.toString());
260 }
261
262
263 if (t != null) t.close();
264
265
266
267
268 return new PairOfSameType<HRegion>(a, b);
269 }
270
271 private static Path getSplitDir(final HRegion r) {
272 return new Path(r.getRegionDir(), SPLITDIR);
273 }
274
275
276
277
278
279
280
281
282 private static void createSplitDir(final FileSystem fs, final Path splitdir)
283 throws IOException {
284 if (fs.exists(splitdir)) throw new IOException("Splitdir already exits? " + splitdir);
285 if (!fs.mkdirs(splitdir)) throw new IOException("Failed create of " + splitdir);
286 }
287
288 private static void cleanupSplitDir(final FileSystem fs, final Path splitdir)
289 throws IOException {
290
291 deleteDir(fs, splitdir, false);
292 }
293
294
295
296
297
298
299
300
301 private static void deleteDir(final FileSystem fs, final Path dir,
302 final boolean mustPreExist)
303 throws IOException {
304 if (!fs.exists(dir)) {
305 if (mustPreExist) throw new IOException(dir.toString() + " does not exist!");
306 } else if (!fs.delete(dir, true)) {
307 throw new IOException("Failed delete of " + dir);
308 }
309 }
310
311 private void splitStoreFiles(final Path splitdir,
312 final List<StoreFile> hstoreFilesToSplit)
313 throws IOException {
314 if (hstoreFilesToSplit == null) {
315
316 throw new IOException("Close returned empty list of StoreFiles");
317 }
318
319
320 for (StoreFile sf: hstoreFilesToSplit) {
321 splitStoreFile(sf, splitdir);
322 }
323 }
324
325 private void splitStoreFile(final StoreFile sf, final Path splitdir)
326 throws IOException {
327 FileSystem fs = this.parent.getFilesystem();
328 byte [] family = sf.getFamily();
329 String encoded = this.hri_a.getEncodedName();
330 Path storedir = Store.getStoreHomedir(splitdir, encoded, family);
331 StoreFile.split(fs, storedir, sf, this.splitrow, Range.bottom);
332 encoded = this.hri_b.getEncodedName();
333 storedir = Store.getStoreHomedir(splitdir, encoded, family);
334 StoreFile.split(fs, storedir, sf, this.splitrow, Range.top);
335 }
336
337
338
339
340
341
342
343 HRegion createDaughterRegion(final HRegionInfo hri)
344 throws IOException {
345
346 FileSystem fs = this.parent.getFilesystem();
347 Path regionDir = getSplitDirForDaughter(this.parent.getFilesystem(),
348 this.splitdir, hri);
349 HRegion r = HRegion.newHRegion(this.parent.getTableDir(),
350 this.parent.getLog(), fs, this.parent.getConf(),
351 hri, null);
352 HRegion.moveInitialFilesIntoPlace(fs, regionDir, r.getRegionDir());
353 return r;
354 }
355
356 private static void cleanupDaughterRegion(final FileSystem fs,
357 final Path tabledir, final String encodedName)
358 throws IOException {
359 Path regiondir = HRegion.getRegionDir(tabledir, encodedName);
360
361 deleteDir(fs, regiondir, false);
362 }
363
364
365
366
367
368
369
370
371
372
373 private static Path getSplitDirForDaughter(final FileSystem fs,
374 final Path splitdir, final HRegionInfo hri)
375 throws IOException {
376 return new Path(splitdir, hri.getEncodedName());
377 }
378
379
380
381
382
383
384
385
386 private HTable getTable(final Configuration conf) throws IOException {
387
388
389
390 HTable t = null;
391 if (this.parent.getRegionInfo().isMetaTable()) {
392 t = new HTable(conf, HConstants.ROOT_TABLE_NAME);
393 } else {
394 t = new HTable(conf, HConstants.META_TABLE_NAME);
395 }
396
397 t.setAutoFlush(true);
398 return t;
399 }
400
401
402 private Put createOfflineParentPut() throws IOException {
403 HRegionInfo editedParentRegionInfo =
404 new HRegionInfo(this.parent.getRegionInfo());
405 editedParentRegionInfo.setOffline(true);
406 editedParentRegionInfo.setSplit(true);
407 Put put = new Put(editedParentRegionInfo.getRegionName());
408 put.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
409 Writables.getBytes(editedParentRegionInfo));
410 put.add(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER,
411 HConstants.EMPTY_BYTE_ARRAY);
412 put.add(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER,
413 HConstants.EMPTY_BYTE_ARRAY);
414 put.add(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER,
415 Writables.getBytes(this.hri_a));
416 put.add(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER,
417 Writables.getBytes(this.hri_b));
418 return put;
419 }
420
421 private Put createDaughterPut(final HRegionInfo daughter)
422 throws IOException {
423 Put p = new Put(daughter.getRegionName());
424 p.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
425 Writables.getBytes(daughter));
426 return p;
427 }
428
429
430
431
432
433
434
435 public void rollback(final OnlineRegions or) throws IOException {
436 FileSystem fs = this.parent.getFilesystem();
437 ListIterator<JournalEntry> iterator =
438 this.journal.listIterator(this.journal.size());
439 while (iterator.hasPrevious()) {
440 JournalEntry je = iterator.previous();
441 switch(je) {
442 case CREATE_SPLIT_DIR:
443 cleanupSplitDir(fs, this.splitdir);
444 break;
445
446 case CLOSED_PARENT_REGION:
447
448
449
450
451
452 this.parent.initialize();
453 break;
454
455 case STARTED_REGION_A_CREATION:
456 cleanupDaughterRegion(fs, this.parent.getTableDir(),
457 this.hri_a.getEncodedName());
458 break;
459
460 case STARTED_REGION_B_CREATION:
461 cleanupDaughterRegion(fs, this.parent.getTableDir(),
462 this.hri_b.getEncodedName());
463 break;
464
465 case OFFLINED_PARENT:
466 if (or != null) or.addToOnlineRegions(this.parent);
467 break;
468
469 default:
470 throw new RuntimeException("Unhandled journal entry: " + je);
471 }
472 }
473 }
474
475 HRegionInfo getFirstDaughter() {
476 return hri_a;
477 }
478
479 HRegionInfo getSecondDaughter() {
480 return hri_b;
481 }
482
483
484 Path getSplitDir() {
485 return this.splitdir;
486 }
487
488
489
490
491
492
493
494
495
496 static void cleanupAnySplitDetritus(final HRegion r) throws IOException {
497 Path splitdir = getSplitDir(r);
498 FileSystem fs = r.getFilesystem();
499 if (!fs.exists(splitdir)) return;
500
501
502
503
504
505
506
507 FileStatus [] daughters = fs.listStatus(splitdir, new FSUtils.DirFilter(fs));
508 for (int i = 0; i < daughters.length; i++) {
509 cleanupDaughterRegion(fs, r.getTableDir(),
510 daughters[i].getPath().getName());
511 }
512 cleanupSplitDir(r.getFilesystem(), splitdir);
513 LOG.info("Cleaned up old failed split transaction detritus: " + splitdir);
514 }
515 }