/**
  * Run the transaction.
  *
  * @param server Hosting server instance. Can be null when testing
  * @param services Used to online/offline regions.
  * @throws IOException If thrown, transaction failed. Call {@link #rollback(Server,
  *     RegionServerServices)}
  * @return merged region
  * @throws IOException
  * @see #rollback(Server, RegionServerServices)
  */
 public HRegion execute(final Server server, final RegionServerServices services)
     throws IOException {
   useCoordinationForAssignment =
       server == null ? true : ConfigUtil.useZKForAssignment(server.getConfiguration());
   if (rmd == null) {
     rmd =
         server != null && server.getCoordinatedStateManager() != null
             ? ((BaseCoordinatedStateManager) server.getCoordinatedStateManager())
                 .getRegionMergeCoordination()
                 .getDefaultDetails()
             : null;
   }
   if (rsCoprocessorHost == null) {
     rsCoprocessorHost =
         server != null ? ((HRegionServer) server).getRegionServerCoprocessorHost() : null;
   }
   HRegion mergedRegion = createMergedRegion(server, services);
   if (rsCoprocessorHost != null) {
     rsCoprocessorHost.postMergeCommit(this.region_a, this.region_b, mergedRegion);
   }
   return stepsAfterPONR(server, services, mergedRegion);
 }
 public HRegion stepsAfterPONR(
     final Server server, final RegionServerServices services, HRegion mergedRegion)
     throws IOException {
   openMergedRegion(server, services, mergedRegion);
   if (useCoordination(server)) {
     ((BaseCoordinatedStateManager) server.getCoordinatedStateManager())
         .getRegionMergeCoordination()
         .completeRegionMergeTransaction(
             services, mergedRegionInfo, region_a, region_b, rmd, mergedRegion);
   }
   if (rsCoprocessorHost != null) {
     rsCoprocessorHost.postMerge(this.region_a, this.region_b, mergedRegion);
   }
   return mergedRegion;
 }
 private boolean useCoordination(final Server server) {
   return server != null
       && useCoordinationForAssignment
       && server.getCoordinatedStateManager() != null;
 }
  /**
   * @param server Hosting server instance (May be null when testing).
   * @param services Services of regionserver, used to online regions.
   * @throws IOException If thrown, rollback failed. Take drastic action.
   * @return True if we successfully rolled back, false if we got to the point of no return and so
   *     now need to abort the server to minimize damage.
   */
  @SuppressWarnings("deprecation")
  public boolean rollback(final Server server, final RegionServerServices services)
      throws IOException {
    assert this.mergedRegionInfo != null;
    // Coprocessor callback
    if (rsCoprocessorHost != null) {
      rsCoprocessorHost.preRollBackMerge(this.region_a, this.region_b);
    }

    boolean result = true;
    ListIterator<JournalEntry> iterator = this.journal.listIterator(this.journal.size());
    // Iterate in reverse.
    while (iterator.hasPrevious()) {
      JournalEntry je = iterator.previous();
      switch (je) {
        case SET_MERGING:
          if (useCoordination(server)) {
            ((BaseCoordinatedStateManager) server.getCoordinatedStateManager())
                .getRegionMergeCoordination()
                .clean(this.mergedRegionInfo);
          } else if (services != null
              && !useCoordinationForAssignment
              && !services.reportRegionStateTransition(
                  TransitionCode.MERGE_REVERTED,
                  mergedRegionInfo,
                  region_a.getRegionInfo(),
                  region_b.getRegionInfo())) {
            return false;
          }
          break;

        case CREATED_MERGE_DIR:
          this.region_a.writestate.writesEnabled = true;
          this.region_b.writestate.writesEnabled = true;
          this.region_a.getRegionFileSystem().cleanupMergesDir();
          break;

        case CLOSED_REGION_A:
          try {
            // So, this returns a seqid but if we just closed and then reopened,
            // we should be ok. On close, we flushed using sequenceid obtained
            // from hosting regionserver so no need to propagate the sequenceid
            // returned out of initialize below up into regionserver as we
            // normally do.
            this.region_a.initialize();
          } catch (IOException e) {
            LOG.error(
                "Failed rollbacking CLOSED_REGION_A of region "
                    + this.region_a.getRegionNameAsString(),
                e);
            throw new RuntimeException(e);
          }
          break;

        case OFFLINED_REGION_A:
          if (services != null) services.addToOnlineRegions(this.region_a);
          break;

        case CLOSED_REGION_B:
          try {
            this.region_b.initialize();
          } catch (IOException e) {
            LOG.error(
                "Failed rollbacking CLOSED_REGION_A of region "
                    + this.region_b.getRegionNameAsString(),
                e);
            throw new RuntimeException(e);
          }
          break;

        case OFFLINED_REGION_B:
          if (services != null) services.addToOnlineRegions(this.region_b);
          break;

        case STARTED_MERGED_REGION_CREATION:
          this.region_a.getRegionFileSystem().cleanupMergedRegion(this.mergedRegionInfo);
          break;

        case PONR:
          // We got to the point-of-no-return so we need to just abort. Return
          // immediately. Do not clean up created merged regions.
          return false;

        default:
          throw new RuntimeException("Unhandled journal entry: " + je);
      }
    }
    // Coprocessor callback
    if (rsCoprocessorHost != null) {
      rsCoprocessorHost.postRollBackMerge(this.region_a, this.region_b);
    }

    return result;
  }
  public HRegion stepsBeforePONR(
      final Server server, final RegionServerServices services, boolean testing)
      throws IOException {
    if (rmd == null) {
      rmd =
          server != null && server.getCoordinatedStateManager() != null
              ? ((BaseCoordinatedStateManager) server.getCoordinatedStateManager())
                  .getRegionMergeCoordination()
                  .getDefaultDetails()
              : null;
    }

    // If server doesn't have a coordination state manager, don't do coordination actions.
    if (useCoordination(server)) {
      try {
        ((BaseCoordinatedStateManager) server.getCoordinatedStateManager())
            .getRegionMergeCoordination()
            .startRegionMergeTransaction(
                mergedRegionInfo,
                server.getServerName(),
                region_a.getRegionInfo(),
                region_b.getRegionInfo());
      } catch (IOException e) {
        throw new IOException(
            "Failed to start region merge transaction for "
                + this.mergedRegionInfo.getRegionNameAsString(),
            e);
      }
    } else if (services != null && !useCoordinationForAssignment) {
      if (!services.reportRegionStateTransition(
          TransitionCode.READY_TO_MERGE,
          mergedRegionInfo,
          region_a.getRegionInfo(),
          region_b.getRegionInfo())) {
        throw new IOException(
            "Failed to get ok from master to merge "
                + region_a.getRegionInfo().getRegionNameAsString()
                + " and "
                + region_b.getRegionInfo().getRegionNameAsString());
      }
    }
    this.journal.add(JournalEntry.SET_MERGING);
    if (useCoordination(server)) {
      // After creating the merge node, wait for master to transition it
      // from PENDING_MERGE to MERGING so that we can move on. We want master
      // knows about it and won't transition any region which is merging.
      ((BaseCoordinatedStateManager) server.getCoordinatedStateManager())
          .getRegionMergeCoordination()
          .waitForRegionMergeTransaction(services, mergedRegionInfo, region_a, region_b, rmd);
    }

    this.region_a.getRegionFileSystem().createMergesDir();
    this.journal.add(JournalEntry.CREATED_MERGE_DIR);

    Map<byte[], List<StoreFile>> hstoreFilesOfRegionA =
        closeAndOfflineRegion(services, this.region_a, true, testing);
    Map<byte[], List<StoreFile>> hstoreFilesOfRegionB =
        closeAndOfflineRegion(services, this.region_b, false, testing);

    assert hstoreFilesOfRegionA != null && hstoreFilesOfRegionB != null;

    //
    // mergeStoreFiles creates merged region dirs under the region_a merges dir
    // Nothing to unroll here if failure -- clean up of CREATE_MERGE_DIR will
    // clean this up.
    mergeStoreFiles(hstoreFilesOfRegionA, hstoreFilesOfRegionB);

    if (useCoordination(server)) {
      try {
        // Do the final check in case any merging region is moved somehow. If so, the transition
        // will fail.
        ((BaseCoordinatedStateManager) server.getCoordinatedStateManager())
            .getRegionMergeCoordination()
            .confirmRegionMergeTransaction(
                this.mergedRegionInfo,
                region_a.getRegionInfo(),
                region_b.getRegionInfo(),
                server.getServerName(),
                rmd);
      } catch (IOException e) {
        throw new IOException(
            "Failed setting MERGING on " + this.mergedRegionInfo.getRegionNameAsString(), e);
      }
    }

    // Log to the journal that we are creating merged region. We could fail
    // halfway through. If we do, we could have left
    // stuff in fs that needs cleanup -- a storefile or two. Thats why we
    // add entry to journal BEFORE rather than AFTER the change.
    this.journal.add(JournalEntry.STARTED_MERGED_REGION_CREATION);
    HRegion mergedRegion =
        createMergedRegionFromMerges(this.region_a, this.region_b, this.mergedRegionInfo);
    return mergedRegion;
  }