@Override public void resume(final IJobUpdateKey key, final AuditData auditData) throws UpdateStateException { requireNonNull(key); requireNonNull(auditData); LOG.info("Attempting to resume update " + key); storage.write( (NoResult<UpdateStateException>) storeProvider -> { IJobUpdateDetails details = Iterables.getOnlyElement( storeProvider.getJobUpdateStore().fetchJobUpdateDetails(queryByUpdate(key)), null); if (details == null) { throw new UpdateStateException("Update does not exist: " + key); } IJobUpdate update = details.getUpdate(); IJobUpdateKey key1 = update.getSummary().getKey(); Function<JobUpdateStatus, JobUpdateStatus> stateChange = isCoordinatedAndPulseExpired(key1, update.getInstructions()) ? GET_BLOCKED_RESUME_STATE : GET_ACTIVE_RESUME_STATE; JobUpdateStatus newStatus = stateChange.apply(update.getSummary().getState().getStatus()); changeUpdateStatus( storeProvider, update.getSummary(), addAuditData(newEvent(newStatus), auditData)); }); }
@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)); }); }
synchronized void initializePulseState(IJobUpdate update, JobUpdateStatus status) { pulseStates.put( update.getSummary().getKey(), new PulseState( status, update.getInstructions().getSettings().getBlockIfNoPulsesAfterMs(), 0L)); }
private void changeJobUpdateStatus( MutableStoreProvider storeProvider, IJobUpdateKey key, JobUpdateEvent proposedEvent, boolean recordChange) throws UpdateStateException { JobUpdateStatus status; boolean record; JobUpdateStore.Mutable updateStore = storeProvider.getJobUpdateStore(); Optional<String> updateLock = updateStore.getLockToken(key); if (updateLock.isPresent()) { status = proposedEvent.getStatus(); record = recordChange; } else { LOG.severe("Update " + key + " does not have a lock"); status = ERROR; record = true; } LOG.info(String.format("Update %s is now in state %s", key, status)); if (record) { updateStore.saveJobUpdateEvent( key, IJobUpdateEvent.build(proposedEvent.setTimestampMs(clock.nowMillis()).setStatus(status))); } if (TERMINAL_STATES.contains(status)) { if (updateLock.isPresent()) { lockManager.releaseLock( ILock.build( new Lock() .setKey(LockKey.job(key.getJob().newBuilder())) .setToken(updateLock.get()))); } pulseHandler.remove(key); } else { pulseHandler.updatePulseStatus(key, status); } MonitorAction action = JobUpdateStateMachine.getActionForStatus(status); IJobKey job = key.getJob(); if (action == STOP_WATCHING) { updates.remove(job); } else if (action == ROLL_FORWARD || action == ROLL_BACK) { if (action == ROLL_BACK) { updates.remove(job); } else { checkState(!updates.containsKey(job), "Updater already exists for " + job); } IJobUpdate jobUpdate = updateStore.fetchJobUpdate(key).get(); UpdateFactory.Update update; try { update = updateFactory.newUpdate(jobUpdate.getInstructions(), action == ROLL_FORWARD); } catch (RuntimeException e) { LOG.log(Level.WARNING, "Uncaught exception: " + e, e); changeJobUpdateStatus( storeProvider, key, newEvent(ERROR).setMessage("Internal scheduler error: " + e.getMessage()), true); return; } updates.put(job, update); evaluateUpdater(storeProvider, update, jobUpdate.getSummary(), ImmutableMap.of()); } }