All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.kagkarlsson.scheduler.FetchCandidates Maven / Gradle / Ivy

There is a newer version: 15.0.0
Show newest version
/*
 * Copyright (C) Gustav Karlsson
 *
 * 

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * *

http://www.apache.org/licenses/LICENSE-2.0 * *

Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package com.github.kagkarlsson.scheduler; import com.github.kagkarlsson.scheduler.logging.ConfigurableLogger; import com.github.kagkarlsson.scheduler.stats.StatsRegistry; import com.github.kagkarlsson.scheduler.task.Execution; import java.time.Instant; import java.util.List; import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FetchCandidates implements PollStrategy { private static final Logger LOG = LoggerFactory.getLogger(FetchCandidates.class); private final Executor executor; private final TaskRepository taskRepository; private final SchedulerClient schedulerClient; private SchedulerClientEventListener earlyExecutionListener; private final StatsRegistry statsRegistry; private final SchedulerState schedulerState; private final ConfigurableLogger failureLogger; private final TaskResolver taskResolver; private final Clock clock; private final PollingStrategyConfig pollingStrategyConfig; private final Runnable triggerCheckForNewExecutions; private HeartbeatConfig heartbeatConfig; AtomicInteger currentGenerationNumber = new AtomicInteger(0); private final int lowerLimit; private final int upperLimit; public FetchCandidates( Executor executor, TaskRepository taskRepository, SchedulerClient schedulerClient, SchedulerClientEventListener earlyExecutionListener, int threadpoolSize, StatsRegistry statsRegistry, SchedulerState schedulerState, ConfigurableLogger failureLogger, TaskResolver taskResolver, Clock clock, PollingStrategyConfig pollingStrategyConfig, Runnable triggerCheckForNewExecutions, HeartbeatConfig heartbeatConfig) { this.executor = executor; this.taskRepository = taskRepository; this.schedulerClient = schedulerClient; this.earlyExecutionListener = earlyExecutionListener; this.statsRegistry = statsRegistry; this.schedulerState = schedulerState; this.failureLogger = failureLogger; this.taskResolver = taskResolver; this.clock = clock; this.pollingStrategyConfig = pollingStrategyConfig; this.triggerCheckForNewExecutions = triggerCheckForNewExecutions; this.heartbeatConfig = heartbeatConfig; lowerLimit = pollingStrategyConfig.getLowerLimit(threadpoolSize); // FIXLATER: this is not "upper limit", but rather nr of executions to get. those already in // queue will become stale upperLimit = pollingStrategyConfig.getUpperLimit(threadpoolSize); } @Override public void run() { Instant now = clock.now(); // Fetch new candidates for execution. Old ones still in ExecutorService will become stale and // be discarded final int executionsToFetch = upperLimit; List fetchedDueExecutions = taskRepository.getDue(now, executionsToFetch); LOG.trace( "Fetched {} task instances due for execution at {}", fetchedDueExecutions.size(), now); currentGenerationNumber.incrementAndGet(); DueExecutionsBatch newDueBatch = new DueExecutionsBatch( currentGenerationNumber.get(), fetchedDueExecutions.size(), executionsToFetch == fetchedDueExecutions.size(), (Integer leftInBatch) -> leftInBatch <= lowerLimit); for (Execution e : fetchedDueExecutions) { executor.addToQueue( () -> { final Optional candidate = new PickDue(e, newDueBatch).call(); candidate.ifPresent( picked -> new ExecutePicked( executor, taskRepository, earlyExecutionListener, schedulerClient, statsRegistry, taskResolver, schedulerState, failureLogger, clock, heartbeatConfig, picked) .run()); }, () -> { newDueBatch.oneExecutionDone(triggerCheckForNewExecutions::run); }); } statsRegistry.register(StatsRegistry.SchedulerStatsEvent.RAN_EXECUTE_DUE); } private class PickDue implements Callable> { private final Execution candidate; private final DueExecutionsBatch addedDueExecutionsBatch; public PickDue(Execution candidate, DueExecutionsBatch dueExecutionsBatch) { this.candidate = candidate; this.addedDueExecutionsBatch = dueExecutionsBatch; } @Override public Optional call() { if (schedulerState.isShuttingDown()) { LOG.info( "Scheduler has been shutdown. Skipping fetched due execution: " + candidate.taskInstance.getTaskAndInstance()); return Optional.empty(); } if (addedDueExecutionsBatch.isOlderGenerationThan(currentGenerationNumber.get())) { // skipping execution due to it being stale addedDueExecutionsBatch.markBatchAsStale(); statsRegistry.register(StatsRegistry.CandidateStatsEvent.STALE); LOG.trace( "Skipping queued execution (current generationNumber: {}, execution generationNumber: {})", currentGenerationNumber, addedDueExecutionsBatch.getGenerationNumber()); return Optional.empty(); } final Optional pickedExecution = taskRepository.pick(candidate, clock.now()); if (!pickedExecution.isPresent()) { // someone else picked id LOG.debug("Execution picked by another scheduler. Continuing to next due execution."); statsRegistry.register(StatsRegistry.CandidateStatsEvent.ALREADY_PICKED); return Optional.empty(); } return pickedExecution; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy