com.github.kagkarlsson.scheduler.LockAndFetchCandidates Maven / Gradle / Ivy
Show all versions of db-scheduler Show documentation
/*
* 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.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LockAndFetchCandidates implements PollStrategy {
private static final Logger LOG = LoggerFactory.getLogger(LockAndFetchCandidates.class);
private final Executor executor;
private final TaskRepository taskRepository;
private final SchedulerClient schedulerClient;
private SchedulerClientEventListener earlyExecutionListener;
private final StatsRegistry statsRegistry;
private final TaskResolver taskResolver;
private final SchedulerState schedulerState;
private final ConfigurableLogger failureLogger;
private final Clock clock;
private final PollingStrategyConfig pollingStrategyConfig;
private final Runnable triggerCheckForNewExecutions;
private HeartbeatConfig maxAgeBeforeConsideredDead;
private final int lowerLimit;
private final int upperLimit;
private AtomicBoolean moreExecutionsInDatabase = new AtomicBoolean(false);
public LockAndFetchCandidates(
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 maxAgeBeforeConsideredDead) {
this.executor = executor;
this.taskRepository = taskRepository;
this.schedulerClient = schedulerClient;
this.earlyExecutionListener = earlyExecutionListener;
this.statsRegistry = statsRegistry;
this.taskResolver = taskResolver;
this.schedulerState = schedulerState;
this.failureLogger = failureLogger;
this.clock = clock;
this.pollingStrategyConfig = pollingStrategyConfig;
this.triggerCheckForNewExecutions = triggerCheckForNewExecutions;
this.maxAgeBeforeConsideredDead = maxAgeBeforeConsideredDead;
lowerLimit = pollingStrategyConfig.getLowerLimit(threadpoolSize);
upperLimit = pollingStrategyConfig.getUpperLimit(threadpoolSize);
}
@Override
public void run() {
Instant now = clock.now();
int executionsToFetch = upperLimit - executor.getNumberInQueueOrProcessing();
// Might happen if upperLimit == threads and all threads are busy
if (executionsToFetch <= 0) {
LOG.trace("No executions to fetch.");
return;
}
// FIXLATER: should it fetch here if not under lowerLimit? probably
List pickedExecutions = taskRepository.lockAndGetDue(now, executionsToFetch);
LOG.trace("Picked {} taskinstances due for execution", pickedExecutions.size());
// Shared indicator for if there are more due executions in the database.
// As soon as we know there are not more executions in the database, we can stop triggering
// checks for more (and vice versa)
moreExecutionsInDatabase.set(pickedExecutions.size() == executionsToFetch);
if (pickedExecutions.size() == 0) {
// No picked executions to execute
LOG.trace("No executions due.");
return;
}
for (Execution picked : pickedExecutions) {
executor.addToQueue(
new ExecutePicked(
executor,
taskRepository,
earlyExecutionListener,
schedulerClient,
statsRegistry,
taskResolver,
schedulerState,
failureLogger,
clock,
maxAgeBeforeConsideredDead,
picked),
() -> {
if (moreExecutionsInDatabase.get()
&& executor.getNumberInQueueOrProcessing() <= lowerLimit) {
triggerCheckForNewExecutions.run();
}
});
}
statsRegistry.register(StatsRegistry.SchedulerStatsEvent.RAN_EXECUTE_DUE);
}
}