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