@Override
  public void start(final IJobUpdate update, final AuditData auditData)
      throws UpdateStateException {

    requireNonNull(update);
    requireNonNull(auditData);

    storage.write(
        (NoResult<UpdateStateException>)
            storeProvider -> {
              IJobUpdateSummary summary = update.getSummary();
              IJobUpdateInstructions instructions = update.getInstructions();
              IJobKey job = summary.getKey().getJob();

              // Validate the update configuration by making sure we can create an updater for it.
              updateFactory.newUpdate(update.getInstructions(), true);

              if (instructions.getInitialState().isEmpty() && !instructions.isSetDesiredState()) {
                throw new IllegalArgumentException("Update instruction is a no-op.");
              }

              List<IJobUpdateSummary> activeJobUpdates =
                  storeProvider.getJobUpdateStore().fetchJobUpdateSummaries(queryActiveByJob(job));
              if (!activeJobUpdates.isEmpty()) {
                throw new UpdateStateException(
                    "An active update already exists for this job, "
                        + "please terminate it before starting another. "
                        + "Active updates are those in states "
                        + Updates.ACTIVE_JOB_UPDATE_STATES);
              }

              LOG.info("Starting update for job " + job);
              ILock lock;
              try {
                lock =
                    lockManager.acquireLock(
                        ILockKey.build(LockKey.job(job.newBuilder())), auditData.getUser());
              } catch (LockException e) {
                throw new UpdateStateException(e.getMessage(), e);
              }

              storeProvider
                  .getJobUpdateStore()
                  .saveJobUpdate(update, Optional.of(requireNonNull(lock.getToken())));

              JobUpdateStatus status = ROLLING_FORWARD;
              if (isCoordinatedUpdate(instructions)) {
                status = ROLL_FORWARD_AWAITING_PULSE;
                pulseHandler.initializePulseState(update, status);
              }

              recordAndChangeJobUpdateStatus(
                  storeProvider, summary.getKey(), addAuditData(newEvent(status), auditData));
            });
  }
    private static ITaskConfig getTargetConfig(
        IJobUpdateInstructions instructions, boolean rollingForward, int instanceId) {

      if (rollingForward) {
        // Desired state is assumed to be non-null when AddTask is used.
        return instructions.getDesiredState().getTask();
      } else {
        for (IInstanceTaskConfig config : instructions.getInitialState()) {
          for (IRange range : config.getInstances()) {
            if (Range.closed(range.getFirst(), range.getLast()).contains(instanceId)) {
              return config.getTask();
            }
          }
        }

        throw new IllegalStateException("Failed to find instance " + instanceId);
      }
    }
    @Override
    public Amount<Long, Time> getReevaluationDelay(
        IInstanceKey instance,
        IJobUpdateInstructions instructions,
        MutableStoreProvider storeProvider,
        StateManager stateManager,
        JobUpdateStatus status) {

      return Amount.of(
          (long) instructions.getSettings().getMinWaitInInstanceRunningMs(), Time.MILLISECONDS);
    }
    @Override
    public Amount<Long, Time> getReevaluationDelay(
        IInstanceKey instance,
        IJobUpdateInstructions instructions,
        MutableStoreProvider storeProvider,
        StateManager stateManager,
        JobUpdateStatus status) {

      LOG.info("Adding instance " + instance + " while " + status);
      ITaskConfig replacement =
          getTargetConfig(instructions, status == ROLLING_FORWARD, instance.getInstanceId());
      stateManager.insertPendingTasks(
          storeProvider, replacement, ImmutableSet.of(instance.getInstanceId()));
      return Amount.of(
          (long) instructions.getSettings().getMaxWaitToInstanceRunningMs(), Time.MILLISECONDS);
    }
    @Override
    public Amount<Long, Time> getReevaluationDelay(
        IInstanceKey instance,
        IJobUpdateInstructions instructions,
        MutableStoreProvider storeProvider,
        StateManager stateManager,
        JobUpdateStatus status) {

      String taskId =
          Tasks.id(
              Iterables.getOnlyElement(
                  storeProvider
                      .getTaskStore()
                      .fetchTasks(Query.instanceScoped(instance).active())));
      LOG.info("Killing " + instance + " while " + status);
      stateManager.changeState(
          storeProvider,
          taskId,
          Optional.absent(),
          ScheduleStatus.KILLING,
          Optional.of("Killed for job update."));
      return Amount.of(
          (long) instructions.getSettings().getMaxWaitToInstanceRunningMs(), Time.MILLISECONDS);
    }
 private static boolean isCoordinatedUpdate(IJobUpdateInstructions instructions) {
   return instructions.getSettings().getBlockIfNoPulsesAfterMs() > 0;
 }