/** * 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; }