private void enqueueHealthAndNewTaskChecks() { final long start = System.currentTimeMillis(); final List<SingularityTask> activeTasks = taskManager.getActiveTasks(); final Map<SingularityTaskId, SingularityTask> activeTaskMap = Maps.uniqueIndex(activeTasks, SingularityTaskIdHolder.getTaskIdFunction()); final Map<SingularityTaskId, List<SingularityTaskHistoryUpdate>> taskUpdates = taskManager.getTaskHistoryUpdates(activeTaskMap.keySet()); final Map<SingularityDeployKey, SingularityPendingDeploy> pendingDeploys = Maps.uniqueIndex( deployManager.getPendingDeploys(), SingularityDeployKey.FROM_PENDING_TO_DEPLOY_KEY); final Map<String, SingularityRequestWithState> idToRequest = Maps.uniqueIndex( requestManager.getRequests(), SingularityRequestWithState.REQUEST_STATE_TO_REQUEST_ID); requestManager.getActiveRequests(); int enqueuedNewTaskChecks = 0; int enqueuedHealthchecks = 0; for (Map.Entry<SingularityTaskId, SingularityTask> entry : activeTaskMap.entrySet()) { SingularityTaskId taskId = entry.getKey(); SingularityTask task = entry.getValue(); SimplifiedTaskState simplifiedTaskState = SingularityTaskHistoryUpdate.getCurrentState(taskUpdates.get(taskId)); if (simplifiedTaskState != SimplifiedTaskState.DONE) { SingularityDeployKey deployKey = new SingularityDeployKey(taskId.getRequestId(), taskId.getDeployId()); Optional<SingularityPendingDeploy> pendingDeploy = Optional.fromNullable(pendingDeploys.get(deployKey)); Optional<SingularityRequestWithState> request = Optional.fromNullable(idToRequest.get(taskId.getRequestId())); if (!pendingDeploy.isPresent()) { newTaskChecker.enqueueNewTaskCheck(task, request, healthchecker); enqueuedNewTaskChecks++; } if (simplifiedTaskState == SimplifiedTaskState.RUNNING) { if (healthchecker.enqueueHealthcheck(task, pendingDeploy, request)) { enqueuedHealthchecks++; } } } } LOG.info( "Enqueued {} health checks and {} new task checks (out of {} active tasks) in {}", enqueuedHealthchecks, enqueuedNewTaskChecks, activeTasks.size(), JavaUtils.duration(start)); }
private void readInitialFiles() throws IOException { final long start = System.currentTimeMillis(); LOG.info( "Scanning for metadata files (*{}) in {}", configuration.getS3MetadataSuffix(), configuration.getS3MetadataDirectory()); int foundFiles = 0; for (Path file : JavaUtils.iterable(configuration.getS3MetadataDirectory())) { if (!isS3MetadataFile(file)) { continue; } if (handleNewOrModifiedS3Metadata(file)) { foundFiles++; } } LOG.info("Found {} file(s) in {}", foundFiles, JavaUtils.duration(start)); }
private boolean handleChunk( S3Artifact s3Artifact, Future<Path> future, Path downloadTo, int chunk, long start, long remainingMillis) { if (remainingMillis <= 0) { remainingMillis = 1; } try { Path path = future.get(remainingMillis, TimeUnit.MILLISECONDS); if (chunk > 0) { combineChunk(downloadTo, path); } return true; } catch (TimeoutException te) { log.error( "Chunk {} for {} timed out after {} - had {} remaining", chunk, s3Artifact.getFilename(), JavaUtils.duration(start), JavaUtils.durationFromMillis(remainingMillis)); future.cancel(true); exceptionNotifier.notify( te, ImmutableMap.of("filename", s3Artifact.getFilename(), "chunk", Integer.toString(chunk))); } catch (Throwable t) { log.error("Error while handling chunk {} for {}", chunk, s3Artifact.getFilename(), t); exceptionNotifier.notify( t, ImmutableMap.of("filename", s3Artifact.getFilename(), "chunk", Integer.toString(chunk))); } return false; }
private <T extends SingularityId> List<T> getChildrenAsIdsForParentsThrows( final String pathNameforLogs, final Collection<String> parents, final IdTranscoder<T> idTranscoder) throws Exception { if (parents.isEmpty()) { return Collections.emptyList(); } final List<T> objects = Lists.newArrayListWithExpectedSize(parents.size()); final CountDownLatch latch = new CountDownLatch(parents.size()); final AtomicInteger missing = new AtomicInteger(); final BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if (event.getChildren() == null || event.getChildren().size() == 0) { LOG.trace("Expected children for node {} - but found none", event.getPath()); missing.incrementAndGet(); latch.countDown(); return; } objects.addAll(Lists.transform(event.getChildren(), idTranscoder)); latch.countDown(); } }; final long start = System.currentTimeMillis(); for (String parent : parents) { curator.getChildren().inBackground(callback).forPath(parent); } checkLatch(latch, pathNameforLogs); LOG.trace( "Fetched {} objects from {} (missing {}) in {}", objects.size(), pathNameforLogs, missing.intValue(), JavaUtils.duration(start)); return objects; }
private <T> List<T> getAsyncThrows( final String pathNameForLogs, final Collection<String> paths, final Transcoder<T> transcoder) throws Exception { final List<T> objects = Lists.newArrayListWithCapacity(paths.size()); if (paths.isEmpty()) { return objects; } final CountDownLatch latch = new CountDownLatch(paths.size()); final AtomicInteger missing = new AtomicInteger(); final BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if (event.getData() == null || event.getData().length == 0) { LOG.trace("Expected active node {} but it wasn't there", event.getPath()); missing.incrementAndGet(); latch.countDown(); return; } objects.add(transcoder.transcode(event.getData())); latch.countDown(); } }; final long start = System.currentTimeMillis(); for (String path : paths) { curator.getData().inBackground(callback).forPath(path); } checkLatch(latch, pathNameForLogs); LOG.trace( "Fetched {} objects from {} (missing {}) in {}", objects.size(), pathNameForLogs, missing.intValue(), JavaUtils.duration(start)); return objects; }
private <T extends SingularityId> List<T> existsThrows( final String pathNameforLogs, final Collection<String> paths, final IdTranscoder<T> idTranscoder) throws Exception { if (paths.isEmpty()) { return Collections.emptyList(); } final List<T> objects = Lists.newArrayListWithCapacity(paths.size()); final CountDownLatch latch = new CountDownLatch(paths.size()); final BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if (event.getStat() == null) { latch.countDown(); return; } objects.add(idTranscoder.apply(ZKPaths.getNodeFromPath(event.getPath()))); latch.countDown(); } }; final long start = System.currentTimeMillis(); for (String path : paths) { curator.checkExists().inBackground(callback).forPath(path); } checkLatch(latch, pathNameforLogs); LOG.trace( "Found {} objects out of {} from {} in {}", objects.size(), paths.size(), pathNameforLogs, JavaUtils.duration(start)); return objects; }
@Inject public SingularityS3UploaderDriver( SingularityS3UploaderConfiguration configuration, SingularityS3Configuration s3Configuration, SingularityS3UploaderMetrics metrics, JsonObjectFileHelper jsonObjectFileHelper) { super( configuration.getPollForShutDownMillis(), configuration.getS3MetadataDirectory(), ImmutableList.of( StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE)); this.metrics = metrics; this.fileSystem = FileSystems.getDefault(); try { this.s3Service = new RestS3Service( new AWSCredentials( s3Configuration.getS3AccessKey(), s3Configuration.getS3SecretKey())); } catch (Throwable t) { throw Throwables.propagate(t); } this.jsonObjectFileHelper = jsonObjectFileHelper; this.configuration = configuration; this.metadataToUploader = Maps.newHashMap(); this.uploaderLastHadFilesAt = Maps.newHashMap(); this.expiring = Sets.newHashSet(); this.metrics.setExpiringCollection(expiring); this.runLock = new ReentrantLock(); this.executorService = JavaUtils.newFixedTimingOutThreadPool( configuration.getExecutorMaxUploadThreads(), TimeUnit.SECONDS.toMillis(30), "SingularityS3Uploader-%d"); this.scheduler = Executors.newScheduledThreadPool( 1, new ThreadFactoryBuilder().setNameFormat("SingularityS3Driver-%d").build()); }
public void download(S3Artifact s3Artifact, Path downloadTo) { final long start = System.currentTimeMillis(); boolean success = false; try { downloadThrows(s3Artifact, downloadTo); success = true; } catch (Throwable t) { throw Throwables.propagate(t); } finally { log.info( "S3 Download {}/{} finished {} after {}", s3Artifact.getS3Bucket(), s3Artifact.getS3ObjectKey(), success ? "successfully" : "with error", JavaUtils.duration(start)); } }
private void combineChunk(Path downloadTo, Path path) throws Exception { final long start = System.currentTimeMillis(); long bytes = 0; log.info("Writing {} to {}", path, downloadTo); try (WritableByteChannel wbs = Files.newByteChannel( downloadTo, EnumSet.of(StandardOpenOption.APPEND, StandardOpenOption.WRITE))) { try (FileChannel readChannel = FileChannel.open( path, EnumSet.of(StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE))) { bytes = readChannel.size(); readChannel.transferTo(0, bytes, wbs); } } log.info("Finished writing {} bytes in {}", bytes, JavaUtils.duration(start)); }
public void startup(MasterInfo masterInfo, SchedulerDriver driver) throws Exception { final long start = System.currentTimeMillis(); final String uri = mesosClient.getMasterUri(MesosUtils.getMasterHostAndPort(masterInfo)); LOG.info("Starting up... fetching state data from: " + uri); zkDataMigrationRunner.checkMigrations(); MesosMasterStateObject state = mesosClient.getMasterState(uri); slaveAndRackManager.loadSlavesAndRacksFromMaster(state); checkSchedulerForInconsistentState(); enqueueHealthAndNewTaskChecks(); taskReconciliation.startReconciliation(); LOG.info("Finished startup after {}", JavaUtils.duration(start)); }
@Override public void shutdown() { final long start = System.currentTimeMillis(); LOG.info("Gracefully shutting down S3Uploader, this may take a few moments..."); runLock.lock(); try { if (!super.stop()) { LOG.info("Already shutting down, ignoring request"); return; } } finally { runLock.unlock(); } future.cancel(false); scheduler.shutdown(); executorService.shutdown(); LOG.info("Shut down in {}", JavaUtils.duration(start)); }
private Optional<Long> getNextRunAt( SingularityRequest request, RequestState state, SingularityDeployStatistics deployStatistics, PendingType pendingType) { 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 { Date scheduleFrom = new Date(now); CronExpression cronExpression = new CronExpression(request.getQuartzScheduleSafe()); 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); // don't create a schedule that is overdue as this is used to indicate that // singularity is not fulfilling requests. 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); }
public void drainPendingQueue(final SingularitySchedulerStateCache stateCache) { final long start = System.currentTimeMillis(); final ImmutableList<SingularityPendingRequest> pendingRequests = ImmutableList.copyOf(requestManager.getPendingRequests()); if (pendingRequests.isEmpty()) { LOG.trace("Pending queue was empty"); return; } LOG.info("Pending queue had {} requests", pendingRequests.size()); int totalNewScheduledTasks = 0; int heldForScheduledActiveTask = 0; int obsoleteRequests = 0; for (SingularityPendingRequest pendingRequest : pendingRequests) { Optional<SingularityRequestWithState> maybeRequest = requestManager.getRequest(pendingRequest.getRequestId()); if (shouldScheduleTasks(pendingRequest, maybeRequest)) { final List<SingularityTaskId> matchingTaskIds = getMatchingTaskIds(stateCache, maybeRequest.get().getRequest(), pendingRequest); final SingularityDeployStatistics deployStatistics = getDeployStatistics(pendingRequest.getRequestId(), pendingRequest.getDeployId()); final RequestState requestState = checkCooldown(maybeRequest.get(), deployStatistics); int numScheduledTasks = scheduleTasks( stateCache, maybeRequest.get().getRequest(), requestState, deployStatistics, pendingRequest, matchingTaskIds); if (numScheduledTasks == 0 && !matchingTaskIds.isEmpty() && maybeRequest.get().getRequest().isScheduled() && pendingRequest.getPendingType() == PendingType.NEW_DEPLOY) { LOG.trace( "Holding pending request {} because it is scheduled and has an active task", pendingRequest); heldForScheduledActiveTask++; continue; } LOG.debug( "Pending request {} resulted in {} new scheduled tasks", pendingRequest, numScheduledTasks); totalNewScheduledTasks += numScheduledTasks; } else { LOG.debug( "Pending request {} was obsolete (request {})", pendingRequest, SingularityRequestWithState.getRequestState(maybeRequest)); obsoleteRequests++; } requestManager.deletePendingRequest(pendingRequest); } LOG.info( "Scheduled {} new tasks ({} obsolete requests, {} held) in {}", totalNewScheduledTasks, obsoleteRequests, heldForScheduledActiveTask, JavaUtils.duration(start)); }
public void checkForDecomissions(SingularitySchedulerStateCache stateCache) { final long start = System.currentTimeMillis(); final Set<String> requestIdsToReschedule = Sets.newHashSet(); final Set<SingularityTaskId> matchingTaskIds = Sets.newHashSet(); final Collection<SingularityTaskId> activeTaskIds = stateCache.getActiveTaskIds(); final Map<SingularitySlave, MachineState> slaves = getDefaultMap(slaveManager.getObjectsFiltered(MachineState.STARTING_DECOMMISSION)); for (SingularitySlave slave : slaves.keySet()) { boolean foundTask = false; for (SingularityTask activeTask : taskManager.getTasksOnSlave(activeTaskIds, slave)) { cleanupTaskDueToDecomission(requestIdsToReschedule, matchingTaskIds, activeTask, slave); foundTask = true; } if (!foundTask) { slaves.put(slave, MachineState.DECOMMISSIONED); } } final Map<SingularityRack, MachineState> racks = getDefaultMap(rackManager.getObjectsFiltered(MachineState.STARTING_DECOMMISSION)); for (SingularityRack rack : racks.keySet()) { boolean foundTask = false; for (SingularityTaskId activeTaskId : activeTaskIds) { if (rack.getId().equals(activeTaskId.getRackId())) { foundTask = true; } if (matchingTaskIds.contains(activeTaskId)) { continue; } if (rack.getId().equals(activeTaskId.getRackId())) { Optional<SingularityTask> maybeTask = taskManager.getTask(activeTaskId); cleanupTaskDueToDecomission( requestIdsToReschedule, matchingTaskIds, maybeTask.get(), rack); } } if (!foundTask) { racks.put(rack, MachineState.DECOMMISSIONED); } } for (String requestId : requestIdsToReschedule) { LOG.trace("Rescheduling request {} due to decomissions", requestId); Optional<String> maybeDeployId = deployManager.getInUseDeployId(requestId); if (maybeDeployId.isPresent()) { requestManager.addToPendingQueue( new SingularityPendingRequest( requestId, maybeDeployId.get(), start, PendingType.DECOMISSIONED_SLAVE_OR_RACK)); } else { LOG.warn("Not rescheduling a request ({}) because of no active deploy", requestId); } } changeState(slaves, slaveManager); changeState(racks, rackManager); if (slaves.isEmpty() && racks.isEmpty() && requestIdsToReschedule.isEmpty() && matchingTaskIds.isEmpty()) { LOG.trace("Decomission check found nothing"); } else { LOG.info( "Found {} decomissioning slaves, {} decomissioning racks, rescheduling {} requests and scheduling {} tasks for cleanup in {}", slaves.size(), racks.size(), requestIdsToReschedule.size(), matchingTaskIds.size(), JavaUtils.duration(start)); } }
public SlaveMatchState doesOfferMatch( Protos.Offer offer, SingularityTaskRequest taskRequest, SingularitySchedulerStateCache stateCache) { final String host = offer.getHostname(); final String rackId = slaveAndRackHelper.getRackIdOrDefault(offer); final String slaveId = offer.getSlaveId().getValue(); final MachineState currentSlaveState = stateCache.getSlave(slaveId).get().getCurrentState().getState(); if (currentSlaveState == MachineState.FROZEN) { return SlaveMatchState.SLAVE_FROZEN; } if (currentSlaveState.isDecommissioning()) { return SlaveMatchState.SLAVE_DECOMMISSIONING; } final MachineState currentRackState = stateCache.getRack(rackId).get().getCurrentState().getState(); if (currentRackState == MachineState.FROZEN) { return SlaveMatchState.RACK_FROZEN; } if (currentRackState.isDecommissioning()) { return SlaveMatchState.RACK_DECOMMISSIONING; } if (!taskRequest.getRequest().getRackAffinity().or(Collections.<String>emptyList()).isEmpty()) { if (!taskRequest.getRequest().getRackAffinity().get().contains(rackId)) { LOG.trace( "Task {} requires a rack in {} (current rack {})", taskRequest.getPendingTask().getPendingTaskId(), taskRequest.getRequest().getRackAffinity().get(), rackId); return SlaveMatchState.RACK_AFFINITY_NOT_MATCHING; } } final SlavePlacement slavePlacement = taskRequest.getRequest().getSlavePlacement().or(configuration.getDefaultSlavePlacement()); if (!taskRequest.getRequest().isRackSensitive() && slavePlacement == SlavePlacement.GREEDY) { return SlaveMatchState.NOT_RACK_OR_SLAVE_PARTICULAR; } final int numDesiredInstances = taskRequest.getRequest().getInstancesSafe(); double numOnRack = 0; double numOnSlave = 0; double numCleaningOnSlave = 0; double numOtherDeploysOnSlave = 0; final String sanitizedHost = JavaUtils.getReplaceHyphensWithUnderscores(host); final String sanitizedRackId = JavaUtils.getReplaceHyphensWithUnderscores(rackId); Collection<SingularityTaskId> cleaningTasks = stateCache.getCleaningTasks(); for (SingularityTaskId taskId : SingularityTaskId.matchingAndNotIn( stateCache.getActiveTaskIds(), taskRequest.getRequest().getId(), Collections.<SingularityTaskId>emptyList())) { // TODO consider using executorIds if (taskId.getSanitizedHost().equals(sanitizedHost)) { if (taskRequest.getDeploy().getId().equals(taskId.getDeployId())) { if (cleaningTasks.contains(taskId)) { numCleaningOnSlave++; } else { numOnSlave++; } } else { numOtherDeploysOnSlave++; } } if (taskId.getSanitizedRackId().equals(sanitizedRackId) && !cleaningTasks.contains(taskId) && taskRequest.getDeploy().getId().equals(taskId.getDeployId())) { numOnRack++; } } if (taskRequest.getRequest().isRackSensitive()) { final double numPerRack = numDesiredInstances / (double) stateCache.getNumActiveRacks(); final boolean isRackOk = numOnRack < numPerRack; if (!isRackOk) { LOG.trace( "Rejecting RackSensitive task {} from slave {} ({}) due to numOnRack {} and cleaningOnSlave {}", taskRequest.getRequest().getId(), slaveId, host, numOnRack, numCleaningOnSlave); return SlaveMatchState.RACK_SATURATED; } } switch (slavePlacement) { case SEPARATE: case SEPARATE_BY_DEPLOY: if (numOnSlave > 0 || numCleaningOnSlave > 0) { LOG.trace( "Rejecting SEPARATE task {} from slave {} ({}) due to numOnSlave {} numCleaningOnSlave {}", taskRequest.getRequest().getId(), slaveId, host, numOnSlave, numCleaningOnSlave); return SlaveMatchState.SLAVE_SATURATED; } break; case SEPARATE_BY_REQUEST: if (numOnSlave > 0 || numCleaningOnSlave > 0 || numOtherDeploysOnSlave > 0) { LOG.trace( "Rejecting SEPARATE task {} from slave {} ({}) due to numOnSlave {} numCleaningOnSlave {} numOtherDeploysOnSlave {}", taskRequest.getRequest().getId(), slaveId, host, numOnSlave, numCleaningOnSlave, numOtherDeploysOnSlave); return SlaveMatchState.SLAVE_SATURATED; } break; case OPTIMISTIC: final double numPerSlave = numDesiredInstances / (double) stateCache.getNumActiveSlaves(); final boolean isSlaveOk = numOnSlave < numPerSlave; if (!isSlaveOk) { LOG.trace( "Rejecting OPTIMISTIC task {} from slave {} ({}) due to numOnSlave {}", taskRequest.getRequest().getId(), slaveId, host, numOnSlave); return SlaveMatchState.SLAVE_SATURATED; } break; case GREEDY: } return SlaveMatchState.OK; }