private void scheduleRedundancyRecovery(Object failedMemberId) {
    boolean isStartup = failedMemberId == null;
    long delay;
    boolean movePrimaries;

    if (isStartup) {
      delay = partitionedRegion.getPartitionAttributes().getStartupRecoveryDelay();
      movePrimaries = !Boolean
          .getBoolean(GEMFIRE_PREFIX + "DISABLE_MOVE_PRIMARIES_ON_STARTUP");
    } else {
      delay = partitionedRegion.getPartitionAttributes().getRecoveryDelay();
      movePrimaries = false;
    }

    final boolean requiresRedundancyRecovery = delay >= 0;

    if (!requiresRedundancyRecovery) {
      return;
    }
    if (!partitionedRegion.isDataStore()) {
      return;
    }

    Runnable task = new RecoveryRunnable(this) {
      @Override
      public void run2() {
        try {
          boolean isFixedPartitionedRegion = partitionedRegion.isFixedPartitionedRegion();

          // always replace offline data for fixed partitioned regions -
          // this guarantees we create the buckets we are supposed to create on this node.
          boolean replaceOfflineData = isFixedPartitionedRegion || !isStartup;

          RebalanceDirector director;
          if (isFixedPartitionedRegion) {
            director = new FPRDirector(true, movePrimaries);
          } else {
            director = new CompositeDirector(true, true, false, movePrimaries);
          }

          PartitionedRegionRebalanceOp rebalance = rebalanceOpFactory.create(
              partitionedRegion, false, director, replaceOfflineData, false);

          long start = partitionedRegion.getPrStats().startRecovery();

          if (isFixedPartitionedRegion) {
            rebalance.executeFPA();
          } else {
            rebalance.execute();
          }

          partitionedRegion.getPrStats().endRecovery(start);
          recoveryFuture = null;
          providerStartupTask.complete(null);
        } catch (CancelException e) {
          logger.debug("Cache closed while recovery in progress");
          providerStartupTask.completeExceptionally(e);
        } catch (RegionDestroyedException e) {
          logger.debug("Region destroyed while recovery in progress");
          providerStartupTask.completeExceptionally(e);
        } catch (Exception e) {
          logger.error("Unexpected exception during bucket recovery", e);
          providerStartupTask.completeExceptionally(e);
        }
      }
    };

    synchronized (shutdownLock) {
      if (!shutdown) {
        try {
          if (logger.isDebugEnabled()) {
            if (isStartup) {
              logger.debug("{} scheduling redundancy recovery in {} ms", partitionedRegion, delay);
            } else {
              logger.debug(
                  "partitionedRegion scheduling redundancy recovery after departure/crash/error in {} in {} ms",
                  failedMemberId, delay);
            }
          }
          recoveryFuture = recoveryExecutor.schedule(task, delay, MILLISECONDS);
          resourceManager.addStartupTask(providerStartupTask);
        } catch (RejectedExecutionException e) {
          // ok, the executor is shutting down.
        }
      }
    }
  }
Schedule a task to perform redundancy recovery for a new node or for the node departed.
geode-core/src/main/java/org/apache/geode/internal/cache/PRHARedundancyProvider.java