1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21 import static org.apache.hadoop.hbase.executor.EventType.RS_ZK_REGION_MERGED;
22 import static org.apache.hadoop.hbase.executor.EventType.RS_ZK_REGION_MERGING;
23 import static org.apache.hadoop.hbase.executor.EventType.RS_ZK_REQUEST_REGION_MERGE;
24
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.ListIterator;
29 import java.util.Map;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.hadoop.hbase.classification.InterfaceAudience;
34 import org.apache.hadoop.fs.Path;
35 import org.apache.hadoop.hbase.HConstants;
36 import org.apache.hadoop.hbase.HRegionInfo;
37 import org.apache.hadoop.hbase.MetaMutationAnnotation;
38 import org.apache.hadoop.hbase.RegionTransition;
39 import org.apache.hadoop.hbase.Server;
40 import org.apache.hadoop.hbase.ServerName;
41 import org.apache.hadoop.hbase.catalog.CatalogTracker;
42 import org.apache.hadoop.hbase.catalog.MetaEditor;
43 import org.apache.hadoop.hbase.catalog.MetaReader;
44 import org.apache.hadoop.hbase.client.Delete;
45 import org.apache.hadoop.hbase.client.HTable;
46 import org.apache.hadoop.hbase.client.Mutation;
47 import org.apache.hadoop.hbase.client.Put;
48 import org.apache.hadoop.hbase.executor.EventType;
49 import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionStateTransition.TransitionCode;
50 import org.apache.hadoop.hbase.regionserver.SplitTransaction.LoggingProgressable;
51 import org.apache.hadoop.hbase.util.Bytes;
52 import org.apache.hadoop.hbase.util.ConfigUtil;
53 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
54 import org.apache.hadoop.hbase.util.Pair;
55 import org.apache.hadoop.hbase.zookeeper.ZKAssign;
56 import org.apache.hadoop.hbase.zookeeper.ZKUtil;
57 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
58 import org.apache.zookeeper.KeeperException;
59 import org.apache.zookeeper.KeeperException.NodeExistsException;
60 import org.apache.zookeeper.data.Stat;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 @InterfaceAudience.Private
91 public class RegionMergeTransaction {
92 private static final Log LOG = LogFactory.getLog(RegionMergeTransaction.class);
93
94
95 private HRegionInfo mergedRegionInfo;
96
97 private final HRegion region_a;
98 private final HRegion region_b;
99
100 private final Path mergesdir;
101 private int znodeVersion = -1;
102
103 private final boolean forcible;
104 private boolean useZKForAssignment;
105
106
107
108
109
110 enum JournalEntry {
111
112
113
114 SET_MERGING_IN_ZK,
115
116
117
118 CREATED_MERGE_DIR,
119
120
121
122 CLOSED_REGION_A,
123
124
125
126 OFFLINED_REGION_A,
127
128
129
130 CLOSED_REGION_B,
131
132
133
134 OFFLINED_REGION_B,
135
136
137
138 STARTED_MERGED_REGION_CREATION,
139
140
141
142
143 PONR
144 }
145
146
147
148
149 private final List<JournalEntry> journal = new ArrayList<JournalEntry>();
150
151 private static IOException closedByOtherException = new IOException(
152 "Failed to close region: already closed by another thread");
153
154 private RegionServerCoprocessorHost rsCoprocessorHost = null;
155
156
157
158
159
160
161
162 public RegionMergeTransaction(final HRegion a, final HRegion b,
163 final boolean forcible) {
164 if (a.getRegionInfo().compareTo(b.getRegionInfo()) <= 0) {
165 this.region_a = a;
166 this.region_b = b;
167 } else {
168 this.region_a = b;
169 this.region_b = a;
170 }
171 this.forcible = forcible;
172 this.mergesdir = region_a.getRegionFileSystem().getMergesDir();
173 }
174
175
176
177
178
179
180
181 public boolean prepare(final RegionServerServices services) {
182 if (!region_a.getTableDesc().getTableName()
183 .equals(region_b.getTableDesc().getTableName())) {
184 LOG.info("Can't merge regions " + region_a + "," + region_b
185 + " because they do not belong to the same table");
186 return false;
187 }
188 if (region_a.getRegionInfo().equals(region_b.getRegionInfo())) {
189 LOG.info("Can't merge the same region " + region_a);
190 return false;
191 }
192 if (!forcible && !HRegionInfo.areAdjacent(region_a.getRegionInfo(),
193 region_b.getRegionInfo())) {
194 String msg = "Skip merging " + this.region_a.getRegionNameAsString()
195 + " and " + this.region_b.getRegionNameAsString()
196 + ", because they are not adjacent.";
197 LOG.info(msg);
198 return false;
199 }
200 if (!this.region_a.isMergeable() || !this.region_b.isMergeable()) {
201 return false;
202 }
203 try {
204 boolean regionAHasMergeQualifier = hasMergeQualifierInMeta(services,
205 region_a.getRegionName());
206 if (regionAHasMergeQualifier ||
207 hasMergeQualifierInMeta(services, region_b.getRegionName())) {
208 LOG.debug("Region " + (regionAHasMergeQualifier ? region_a.getRegionNameAsString()
209 : region_b.getRegionNameAsString())
210 + " is not mergeable because it has merge qualifier in META");
211 return false;
212 }
213 } catch (IOException e) {
214 LOG.warn("Failed judging whether merge transaction is available for "
215 + region_a.getRegionNameAsString() + " and "
216 + region_b.getRegionNameAsString(), e);
217 return false;
218 }
219
220
221
222
223
224
225
226
227 this.mergedRegionInfo = getMergedRegionInfo(region_a.getRegionInfo(),
228 region_b.getRegionInfo());
229 return true;
230 }
231
232
233
234
235
236
237
238
239
240
241
242
243 public HRegion execute(final Server server,
244 final RegionServerServices services) throws IOException {
245 useZKForAssignment = server == null ? true :
246 ConfigUtil.useZKForAssignment(server.getConfiguration());
247 if (rsCoprocessorHost == null) {
248 rsCoprocessorHost = server != null ? ((HRegionServer) server).getCoprocessorHost() : null;
249 }
250 HRegion mergedRegion = createMergedRegion(server, services);
251 if (rsCoprocessorHost != null) {
252 rsCoprocessorHost.postMergeCommit(this.region_a, this.region_b, mergedRegion);
253 }
254 return stepsAfterPONR(server, services, mergedRegion);
255 }
256
257 public HRegion stepsAfterPONR(final Server server, final RegionServerServices services,
258 HRegion mergedRegion) throws IOException {
259 openMergedRegion(server, services, mergedRegion);
260 transitionZKNode(server, services, mergedRegion);
261 return mergedRegion;
262 }
263
264
265
266
267
268
269
270
271
272
273 HRegion createMergedRegion(final Server server,
274 final RegionServerServices services) throws IOException {
275 LOG.info("Starting merge of " + region_a + " and "
276 + region_b.getRegionNameAsString() + ", forcible=" + forcible);
277 if ((server != null && server.isStopped())
278 || (services != null && services.isStopping())) {
279 throw new IOException("Server is stopped or stopping");
280 }
281
282 if (rsCoprocessorHost != null) {
283 if (rsCoprocessorHost.preMerge(this.region_a, this.region_b)) {
284 throw new IOException("Coprocessor bypassing regions " + this.region_a + " "
285 + this.region_b + " merge.");
286 }
287 }
288
289
290 boolean testing = server == null ? true : server.getConfiguration()
291 .getBoolean("hbase.testing.nocluster", false);
292
293 HRegion mergedRegion = stepsBeforePONR(server, services, testing);
294
295 @MetaMutationAnnotation
296 List<Mutation> metaEntries = new ArrayList<Mutation>();
297 if (rsCoprocessorHost != null) {
298 if (rsCoprocessorHost.preMergeCommit(this.region_a, this.region_b, metaEntries)) {
299 throw new IOException("Coprocessor bypassing regions " + this.region_a + " "
300 + this.region_b + " merge.");
301 }
302 try {
303 for (Mutation p : metaEntries) {
304 HRegionInfo.parseRegionName(p.getRow());
305 }
306 } catch (IOException e) {
307 LOG.error("Row key of mutation from coprocessor is not parsable as region name."
308 + "Mutations from coprocessor should only be for hbase:meta table.", e);
309 throw e;
310 }
311 }
312
313
314
315
316 this.journal.add(JournalEntry.PONR);
317
318
319
320
321
322
323 if (!testing && useZKForAssignment) {
324 if (metaEntries.isEmpty()) {
325 MetaEditor.mergeRegions(server.getCatalogTracker(), mergedRegion.getRegionInfo(), region_a
326 .getRegionInfo(), region_b.getRegionInfo(), server.getServerName());
327 } else {
328 mergeRegionsAndPutMetaEntries(server.getCatalogTracker(), mergedRegion.getRegionInfo(),
329 region_a.getRegionInfo(), region_b.getRegionInfo(), server.getServerName(), metaEntries);
330 }
331 } else if (services != null && !useZKForAssignment) {
332 if (!services.reportRegionStateTransition(TransitionCode.MERGE_PONR,
333 mergedRegionInfo, region_a.getRegionInfo(), region_b.getRegionInfo())) {
334
335 throw new IOException("Failed to notify master that merge passed PONR: "
336 + region_a.getRegionInfo().getRegionNameAsString() + " and "
337 + region_b.getRegionInfo().getRegionNameAsString());
338 }
339 }
340 return mergedRegion;
341 }
342
343 private void mergeRegionsAndPutMetaEntries(CatalogTracker catalogTracker,
344 HRegionInfo mergedRegion, HRegionInfo regionA, HRegionInfo regionB, ServerName serverName,
345 List<Mutation> metaEntries) throws IOException {
346 prepareMutationsForMerge(mergedRegion, regionA, regionB, serverName, metaEntries);
347 MetaEditor.mutateMetaTable(catalogTracker, metaEntries);
348 }
349
350 public void prepareMutationsForMerge(HRegionInfo mergedRegion, HRegionInfo regionA,
351 HRegionInfo regionB, ServerName serverName, List<Mutation> mutations) throws IOException {
352 HRegionInfo copyOfMerged = new HRegionInfo(mergedRegion);
353
354
355 Put putOfMerged = MetaEditor.makePutFromRegionInfo(copyOfMerged);
356 putOfMerged.add(HConstants.CATALOG_FAMILY, HConstants.MERGEA_QUALIFIER, regionA.toByteArray());
357 putOfMerged.add(HConstants.CATALOG_FAMILY, HConstants.MERGEB_QUALIFIER, regionB.toByteArray());
358 mutations.add(putOfMerged);
359
360 Delete deleteA = MetaEditor.makeDeleteFromRegionInfo(regionA);
361 Delete deleteB = MetaEditor.makeDeleteFromRegionInfo(regionB);
362 mutations.add(deleteA);
363 mutations.add(deleteB);
364
365 addLocation(putOfMerged, serverName, 1);
366 }
367
368 @SuppressWarnings("deprecation")
369 public Put addLocation(final Put p, final ServerName sn, long openSeqNum) {
370 p.add(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER, Bytes
371 .toBytes(sn.getHostAndPort()));
372 p.add(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER, Bytes.toBytes(sn
373 .getStartcode()));
374 p.add(HConstants.CATALOG_FAMILY, HConstants.SEQNUM_QUALIFIER, Bytes.toBytes(openSeqNum));
375 return p;
376 }
377
378 public HRegion stepsBeforePONR(final Server server, final RegionServerServices services,
379 boolean testing) throws IOException {
380
381
382 if (useZKAndZKIsSet(server)) {
383 try {
384 createNodeMerging(server.getZooKeeper(), this.mergedRegionInfo,
385 server.getServerName(), region_a.getRegionInfo(), region_b.getRegionInfo());
386 } catch (KeeperException e) {
387 throw new IOException("Failed creating PENDING_MERGE znode on "
388 + this.mergedRegionInfo.getRegionNameAsString(), e);
389 }
390 } else if (services != null && !useZKForAssignment) {
391 if (!services.reportRegionStateTransition(TransitionCode.READY_TO_MERGE,
392 mergedRegionInfo, region_a.getRegionInfo(), region_b.getRegionInfo())) {
393 throw new IOException("Failed to get ok from master to merge "
394 + region_a.getRegionInfo().getRegionNameAsString() + " and "
395 + region_b.getRegionInfo().getRegionNameAsString());
396 }
397 }
398 this.journal.add(JournalEntry.SET_MERGING_IN_ZK);
399 if (useZKAndZKIsSet(server)) {
400
401
402
403 znodeVersion = getZKNode(server, services);
404 }
405
406 this.region_a.getRegionFileSystem().createMergesDir();
407 this.journal.add(JournalEntry.CREATED_MERGE_DIR);
408
409 Map<byte[], List<StoreFile>> hstoreFilesOfRegionA = closeAndOfflineRegion(
410 services, this.region_a, true, testing);
411 Map<byte[], List<StoreFile>> hstoreFilesOfRegionB = closeAndOfflineRegion(
412 services, this.region_b, false, testing);
413
414 assert hstoreFilesOfRegionA != null && hstoreFilesOfRegionB != null;
415
416
417
418
419
420
421 mergeStoreFiles(hstoreFilesOfRegionA, hstoreFilesOfRegionB);
422
423 if (server != null && useZKAndZKIsSet(server)) {
424 try {
425
426
427 this.znodeVersion = transitionMergingNode(server.getZooKeeper(),
428 this.mergedRegionInfo, region_a.getRegionInfo(), region_b.getRegionInfo(),
429 server.getServerName(), this.znodeVersion,
430 RS_ZK_REGION_MERGING, RS_ZK_REGION_MERGING);
431 } catch (KeeperException e) {
432 throw new IOException("Failed setting MERGING znode on "
433 + this.mergedRegionInfo.getRegionNameAsString(), e);
434 }
435 }
436
437
438
439
440
441 this.journal.add(JournalEntry.STARTED_MERGED_REGION_CREATION);
442 HRegion mergedRegion = createMergedRegionFromMerges(this.region_a,
443 this.region_b, this.mergedRegionInfo);
444 return mergedRegion;
445 }
446
447
448
449
450
451
452
453
454
455
456 HRegion createMergedRegionFromMerges(final HRegion a, final HRegion b,
457 final HRegionInfo mergedRegion) throws IOException {
458 return a.createMergedRegionFromMerges(mergedRegion, b);
459 }
460
461
462
463
464
465
466
467
468
469
470 private Map<byte[], List<StoreFile>> closeAndOfflineRegion(
471 final RegionServerServices services, final HRegion region,
472 final boolean isRegionA, final boolean testing) throws IOException {
473 Map<byte[], List<StoreFile>> hstoreFilesToMerge = null;
474 Exception exceptionToThrow = null;
475 try {
476 hstoreFilesToMerge = region.close(false);
477 } catch (Exception e) {
478 exceptionToThrow = e;
479 }
480 if (exceptionToThrow == null && hstoreFilesToMerge == null) {
481
482
483
484
485
486 exceptionToThrow = closedByOtherException;
487 }
488 if (exceptionToThrow != closedByOtherException) {
489 this.journal.add(isRegionA ? JournalEntry.CLOSED_REGION_A
490 : JournalEntry.CLOSED_REGION_B);
491 }
492 if (exceptionToThrow != null) {
493 if (exceptionToThrow instanceof IOException)
494 throw (IOException) exceptionToThrow;
495 throw new IOException(exceptionToThrow);
496 }
497
498 if (!testing) {
499 services.removeFromOnlineRegions(region, null);
500 }
501 this.journal.add(isRegionA ? JournalEntry.OFFLINED_REGION_A
502 : JournalEntry.OFFLINED_REGION_B);
503 return hstoreFilesToMerge;
504 }
505
506
507
508
509
510
511
512 public static HRegionInfo getMergedRegionInfo(final HRegionInfo a,
513 final HRegionInfo b) {
514 long rid = EnvironmentEdgeManager.currentTimeMillis();
515
516
517 if (rid < a.getRegionId() || rid < b.getRegionId()) {
518 LOG.warn("Clock skew; merging regions id are " + a.getRegionId()
519 + " and " + b.getRegionId() + ", but current time here is " + rid);
520 rid = Math.max(a.getRegionId(), b.getRegionId()) + 1;
521 }
522
523 byte[] startKey = null;
524 byte[] endKey = null;
525
526 if (a.compareTo(b) <= 0) {
527 startKey = a.getStartKey();
528 } else {
529 startKey = b.getStartKey();
530 }
531
532 if (Bytes.equals(a.getEndKey(), HConstants.EMPTY_BYTE_ARRAY)
533 || (!Bytes.equals(b.getEndKey(), HConstants.EMPTY_BYTE_ARRAY)
534 && Bytes.compareTo(a.getEndKey(), b.getEndKey()) > 0)) {
535 endKey = a.getEndKey();
536 } else {
537 endKey = b.getEndKey();
538 }
539
540
541 HRegionInfo mergedRegionInfo = new HRegionInfo(a.getTable(), startKey,
542 endKey, false, rid);
543 return mergedRegionInfo;
544 }
545
546
547
548
549
550
551
552
553
554
555 void openMergedRegion(final Server server,
556 final RegionServerServices services, HRegion merged) throws IOException {
557 boolean stopped = server != null && server.isStopped();
558 boolean stopping = services != null && services.isStopping();
559 if (stopped || stopping) {
560 LOG.info("Not opening merged region " + merged.getRegionNameAsString()
561 + " because stopping=" + stopping + ", stopped=" + stopped);
562 return;
563 }
564 HRegionInfo hri = merged.getRegionInfo();
565 LoggingProgressable reporter = server == null ? null
566 : new LoggingProgressable(hri, server.getConfiguration().getLong(
567 "hbase.regionserver.regionmerge.open.log.interval", 10000));
568 merged.openHRegion(reporter);
569
570 if (services != null) {
571 try {
572 if (useZKForAssignment) {
573 services.postOpenDeployTasks(merged, server.getCatalogTracker());
574 } else if (!services.reportRegionStateTransition(TransitionCode.MERGED,
575 mergedRegionInfo, region_a.getRegionInfo(), region_b.getRegionInfo())) {
576 throw new IOException("Failed to report merged region to master: "
577 + mergedRegionInfo.getShortNameToLog());
578 }
579 services.addToOnlineRegions(merged);
580 } catch (KeeperException ke) {
581 throw new IOException(ke);
582 }
583 }
584
585 }
586
587
588
589
590
591
592
593
594
595 void transitionZKNode(final Server server, final RegionServerServices services,
596 HRegion mergedRegion) throws IOException {
597 if (useZKAndZKIsSet(server)) {
598
599 try {
600 this.znodeVersion = transitionMergingNode(server.getZooKeeper(),
601 this.mergedRegionInfo, region_a.getRegionInfo(),
602 region_b.getRegionInfo(), server.getServerName(), this.znodeVersion,
603 RS_ZK_REGION_MERGING, RS_ZK_REGION_MERGED);
604
605 long startTime = EnvironmentEdgeManager.currentTimeMillis();
606 int spins = 0;
607
608
609
610 do {
611 if (spins % 10 == 0) {
612 LOG.debug("Still waiting on the master to process the merge for "
613 + this.mergedRegionInfo.getEncodedName() + ", waited "
614 + (EnvironmentEdgeManager.currentTimeMillis() - startTime) + "ms");
615 }
616 Thread.sleep(100);
617
618 this.znodeVersion = transitionMergingNode(server.getZooKeeper(),
619 this.mergedRegionInfo, region_a.getRegionInfo(),
620 region_b.getRegionInfo(), server.getServerName(), this.znodeVersion,
621 RS_ZK_REGION_MERGED, RS_ZK_REGION_MERGED);
622 spins++;
623 } while (this.znodeVersion != -1 && !server.isStopped()
624 && !services.isStopping());
625 } catch (Exception e) {
626 if (e instanceof InterruptedException) {
627 Thread.currentThread().interrupt();
628 }
629 throw new IOException("Failed telling master about merge "
630 + mergedRegionInfo.getEncodedName(), e);
631 }
632 }
633
634 if (rsCoprocessorHost != null) {
635 rsCoprocessorHost.postMerge(this.region_a, this.region_b, mergedRegion);
636 }
637
638
639
640
641 }
642
643
644
645
646
647
648
649
650
651 private int getZKNode(final Server server,
652 final RegionServerServices services) throws IOException {
653
654 try {
655 int spins = 0;
656 Stat stat = new Stat();
657 ZooKeeperWatcher zkw = server.getZooKeeper();
658 ServerName expectedServer = server.getServerName();
659 String node = mergedRegionInfo.getEncodedName();
660 while (!(server.isStopped() || services.isStopping())) {
661 if (spins % 5 == 0) {
662 LOG.debug("Still waiting for master to process "
663 + "the pending_merge for " + node);
664 transitionMergingNode(zkw, mergedRegionInfo, region_a.getRegionInfo(),
665 region_b.getRegionInfo(), expectedServer, -1, RS_ZK_REQUEST_REGION_MERGE,
666 RS_ZK_REQUEST_REGION_MERGE);
667 }
668 Thread.sleep(100);
669 spins++;
670 byte [] data = ZKAssign.getDataNoWatch(zkw, node, stat);
671 if (data == null) {
672 throw new IOException("Data is null, merging node "
673 + node + " no longer exists");
674 }
675 RegionTransition rt = RegionTransition.parseFrom(data);
676 EventType et = rt.getEventType();
677 if (et == RS_ZK_REGION_MERGING) {
678 ServerName serverName = rt.getServerName();
679 if (!serverName.equals(expectedServer)) {
680 throw new IOException("Merging node " + node + " is for "
681 + serverName + ", not us " + expectedServer);
682 }
683 byte [] payloadOfMerging = rt.getPayload();
684 List<HRegionInfo> mergingRegions = HRegionInfo.parseDelimitedFrom(
685 payloadOfMerging, 0, payloadOfMerging.length);
686 assert mergingRegions.size() == 3;
687 HRegionInfo a = mergingRegions.get(1);
688 HRegionInfo b = mergingRegions.get(2);
689 HRegionInfo hri_a = region_a.getRegionInfo();
690 HRegionInfo hri_b = region_b.getRegionInfo();
691 if (!(hri_a.equals(a) && hri_b.equals(b))) {
692 throw new IOException("Merging node " + node + " is for " + a + ", "
693 + b + ", not expected regions: " + hri_a + ", " + hri_b);
694 }
695
696 return stat.getVersion();
697 }
698 if (et != RS_ZK_REQUEST_REGION_MERGE) {
699 throw new IOException("Merging node " + node
700 + " moved out of merging to " + et);
701 }
702 }
703
704 throw new IOException("Server is "
705 + (services.isStopping() ? "stopping" : "stopped"));
706 } catch (Exception e) {
707 if (e instanceof InterruptedException) {
708 Thread.currentThread().interrupt();
709 }
710 throw new IOException("Failed getting MERGING znode on "
711 + mergedRegionInfo.getRegionNameAsString(), e);
712 }
713 }
714
715
716
717
718
719
720
721 private void mergeStoreFiles(
722 Map<byte[], List<StoreFile>> hstoreFilesOfRegionA,
723 Map<byte[], List<StoreFile>> hstoreFilesOfRegionB)
724 throws IOException {
725
726 HRegionFileSystem fs_a = this.region_a.getRegionFileSystem();
727 for (Map.Entry<byte[], List<StoreFile>> entry : hstoreFilesOfRegionA
728 .entrySet()) {
729 String familyName = Bytes.toString(entry.getKey());
730 for (StoreFile storeFile : entry.getValue()) {
731 fs_a.mergeStoreFile(this.mergedRegionInfo, familyName, storeFile,
732 this.mergesdir);
733 }
734 }
735
736 HRegionFileSystem fs_b = this.region_b.getRegionFileSystem();
737 for (Map.Entry<byte[], List<StoreFile>> entry : hstoreFilesOfRegionB
738 .entrySet()) {
739 String familyName = Bytes.toString(entry.getKey());
740 for (StoreFile storeFile : entry.getValue()) {
741 fs_b.mergeStoreFile(this.mergedRegionInfo, familyName, storeFile,
742 this.mergesdir);
743 }
744 }
745 }
746
747
748
749
750
751
752
753
754
755 @SuppressWarnings("deprecation")
756 public boolean rollback(final Server server,
757 final RegionServerServices services) throws IOException {
758 assert this.mergedRegionInfo != null;
759
760 if (rsCoprocessorHost != null) {
761 rsCoprocessorHost.preRollBackMerge(this.region_a, this.region_b);
762 }
763
764 boolean result = true;
765 ListIterator<JournalEntry> iterator = this.journal
766 .listIterator(this.journal.size());
767
768 while (iterator.hasPrevious()) {
769 JournalEntry je = iterator.previous();
770 switch (je) {
771
772 case SET_MERGING_IN_ZK:
773 if (useZKAndZKIsSet(server)) {
774 cleanZK(server, this.mergedRegionInfo);
775 } else if (services != null && !useZKForAssignment
776 && !services.reportRegionStateTransition(TransitionCode.MERGE_REVERTED,
777 mergedRegionInfo, region_a.getRegionInfo(), region_b.getRegionInfo())) {
778 return false;
779 }
780 break;
781
782 case CREATED_MERGE_DIR:
783 this.region_a.writestate.writesEnabled = true;
784 this.region_b.writestate.writesEnabled = true;
785 this.region_a.getRegionFileSystem().cleanupMergesDir();
786 break;
787
788 case CLOSED_REGION_A:
789 try {
790
791
792
793
794
795 this.region_a.initialize();
796 } catch (IOException e) {
797 LOG.error("Failed rollbacking CLOSED_REGION_A of region "
798 + this.region_a.getRegionNameAsString(), e);
799 throw new RuntimeException(e);
800 }
801 break;
802
803 case OFFLINED_REGION_A:
804 if (services != null)
805 services.addToOnlineRegions(this.region_a);
806 break;
807
808 case CLOSED_REGION_B:
809 try {
810 this.region_b.initialize();
811 } catch (IOException e) {
812 LOG.error("Failed rollbacking CLOSED_REGION_A of region "
813 + this.region_b.getRegionNameAsString(), e);
814 throw new RuntimeException(e);
815 }
816 break;
817
818 case OFFLINED_REGION_B:
819 if (services != null)
820 services.addToOnlineRegions(this.region_b);
821 break;
822
823 case STARTED_MERGED_REGION_CREATION:
824 this.region_a.getRegionFileSystem().cleanupMergedRegion(
825 this.mergedRegionInfo);
826 break;
827
828 case PONR:
829
830
831 return false;
832
833 default:
834 throw new RuntimeException("Unhandled journal entry: " + je);
835 }
836 }
837
838 if (rsCoprocessorHost != null) {
839 rsCoprocessorHost.postRollBackMerge(this.region_a, this.region_b);
840 }
841
842 return result;
843 }
844
845 HRegionInfo getMergedRegionInfo() {
846 return this.mergedRegionInfo;
847 }
848
849
850 Path getMergesDir() {
851 return this.mergesdir;
852 }
853
854 private boolean useZKAndZKIsSet(final Server server) {
855 return server != null && useZKForAssignment && server.getZooKeeper() != null;
856 }
857
858 private static void cleanZK(final Server server, final HRegionInfo hri) {
859 try {
860
861 if (!ZKAssign.deleteNode(server.getZooKeeper(), hri.getEncodedName(),
862 RS_ZK_REQUEST_REGION_MERGE, server.getServerName())) {
863 ZKAssign.deleteNode(server.getZooKeeper(), hri.getEncodedName(),
864 RS_ZK_REGION_MERGING, server.getServerName());
865 }
866 } catch (KeeperException.NoNodeException e) {
867 LOG.info("Failed cleanup zk node of " + hri.getRegionNameAsString(), e);
868 } catch (KeeperException e) {
869 server.abort("Failed cleanup zk node of " + hri.getRegionNameAsString(),e);
870 }
871 }
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887 public static void createNodeMerging(final ZooKeeperWatcher zkw, final HRegionInfo region,
888 final ServerName serverName, final HRegionInfo a,
889 final HRegionInfo b) throws KeeperException, IOException {
890 LOG.debug(zkw.prefix("Creating ephemeral node for "
891 + region.getEncodedName() + " in PENDING_MERGE state"));
892 byte [] payload = HRegionInfo.toDelimitedByteArray(region, a, b);
893 RegionTransition rt = RegionTransition.createRegionTransition(
894 RS_ZK_REQUEST_REGION_MERGE, region.getRegionName(), serverName, payload);
895 String node = ZKAssign.getNodeName(zkw, region.getEncodedName());
896 if (!ZKUtil.createEphemeralNodeAndWatch(zkw, node, rt.toByteArray())) {
897 throw new IOException("Failed create of ephemeral " + node);
898 }
899 }
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940 public static int transitionMergingNode(ZooKeeperWatcher zkw,
941 HRegionInfo merged, HRegionInfo a, HRegionInfo b, ServerName serverName,
942 final int znodeVersion, final EventType beginState,
943 final EventType endState) throws KeeperException, IOException {
944 byte[] payload = HRegionInfo.toDelimitedByteArray(merged, a, b);
945 return ZKAssign.transitionNode(zkw, merged, serverName,
946 beginState, endState, znodeVersion, payload);
947 }
948
949
950
951
952
953
954
955
956
957 boolean hasMergeQualifierInMeta(final RegionServerServices services,
958 final byte[] regionName) throws IOException {
959 if (services == null) return false;
960
961
962 Pair<HRegionInfo, HRegionInfo> mergeRegions = MetaReader
963 .getRegionsFromMergeQualifier(services.getCatalogTracker(), regionName);
964 if (mergeRegions != null &&
965 (mergeRegions.getFirst() != null || mergeRegions.getSecond() != null)) {
966
967 return true;
968 }
969 return false;
970 }
971 }