SingularityScheduler { private Optional<Long> getNextRunAt(SingularityRequest request, RequestState state, SingularityDeployStatistics deployStatistics, PendingType pendingType, Optional<SingularityPendingDeploy> maybePendingDeploy) { final long now = System.currentTimeMillis(); long nextRunAt = now; if (request.isScheduled()) { if (pendingType == PendingType.IMMEDIATE || pendingType == PendingType.RETRY) { LOG.info("Scheduling requested immediate run of {}", request.getId()); } else { try { final CronExpression cronExpression = new CronExpression(request.getQuartzScheduleSafe()); final Date scheduleFrom = new Date(now); final Date nextRunAtDate = cronExpression.getNextValidTimeAfter(scheduleFrom); if (nextRunAtDate == null) { return Optional.absent(); } LOG.trace("Calculating nextRunAtDate for {} (schedule: {}): {} (from: {})", request.getId(), request.getSchedule(), nextRunAtDate, scheduleFrom); nextRunAt = Math.max(nextRunAtDate.getTime(), now); LOG.trace("Scheduling next run of {} (schedule: {}) at {} (from: {})", request.getId(), request.getSchedule(), nextRunAtDate, scheduleFrom); } catch (ParseException pe) { throw Throwables.propagate(pe); } } } if (pendingType == PendingType.TASK_DONE && request.getWaitAtLeastMillisAfterTaskFinishesForReschedule().or(0L) > 0) { nextRunAt = Math.max(nextRunAt, now + request.getWaitAtLeastMillisAfterTaskFinishesForReschedule().get()); LOG.trace("Adjusted next run of {} to {} (by {}) due to waitAtLeastMillisAfterTaskFinishesForReschedule", request.getId(), nextRunAt, JavaUtils.durationFromMillis(request.getWaitAtLeastMillisAfterTaskFinishesForReschedule().get())); } if (state == RequestState.SYSTEM_COOLDOWN && pendingType != PendingType.NEW_DEPLOY) { final long prevNextRunAt = nextRunAt; nextRunAt = Math.max(nextRunAt, now + TimeUnit.SECONDS.toMillis(configuration.getCooldownMinScheduleSeconds())); LOG.trace("Adjusted next run of {} to {} (from: {}) due to cooldown", request.getId(), nextRunAt, prevNextRunAt); } return Optional.of(nextRunAt); } @Inject  SingularityScheduler(TaskRequestManager taskRequestManager, SingularityConfiguration configuration, SingularityCooldown cooldown, DeployManager deployManager,
    TaskManager taskManager, RequestManager requestManager, SlaveManager slaveManager, RackManager rackManager, SingularityMailer mailer); @Timed void checkForDecomissions(SingularitySchedulerStateCache stateCache); @Timed void drainPendingQueue(final SingularitySchedulerStateCache stateCache); @Timed List<SingularityTaskRequest> getDueTasks(); @Timed void handleCompletedTask(Optional<SingularityTask> task, SingularityTaskId taskId, boolean wasActive, long timestamp, ExtendedTaskState state,
    SingularityCreateResult taskHistoryUpdateCreateResult, SingularitySchedulerStateCache stateCache);  }
@Test public void testWaitAfterTaskWorks() { initRequest(); initFirstDeploy(); SingularityTask task = launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); statusUpdate(task, TaskState.TASK_FAILED); Assert.assertTrue(taskManager.getPendingTaskIds().get(0).getNextRunAt() - System.currentTimeMillis() < 1000L); resourceOffers(); long extraWait = 100000L; saveAndSchedule(request.toBuilder().setWaitAtLeastMillisAfterTaskFinishesForReschedule(Optional.of(extraWait)).setInstances(Optional.of(2))); resourceOffers(); statusUpdate(taskManager.getActiveTasks().get(0), TaskState.TASK_FAILED); Assert.assertTrue(taskManager.getPendingTaskIds().get(0).getNextRunAt() - System.currentTimeMillis() > 1000L); Assert.assertEquals(1, taskManager.getActiveTaskIds().size()); }