mirror of
https://gitee.com/jmix/cuba.git
synced 2024-12-01 02:38:21 +08:00
PL-6818 Support fixed-delay in Scheduled Tasks mechanism
This commit is contained in:
parent
5049caa263
commit
b982798bfe
@ -144,6 +144,7 @@ create table SYS_SCHEDULED_EXECUTION (
|
||||
)^
|
||||
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_START_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, START_TIME)^
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
||||
|
||||
------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
@ -142,6 +142,7 @@ create table SYS_SCHEDULED_EXECUTION (
|
||||
)^
|
||||
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_START_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, START_TIME)^
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
||||
|
||||
create clustered index IDX_SYS_SCHEDULED_EXECUTION_CREATE_TS on SYS_SCHEDULED_EXECUTION (CREATE_TS)^
|
||||
|
||||
|
@ -141,6 +141,7 @@ create table SYS_SCHEDULED_EXECUTION (
|
||||
)^
|
||||
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_START_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, START_TIME)^
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
||||
|
||||
/**********************************************************************************************/
|
||||
|
||||
|
@ -233,6 +233,7 @@ create table SYS_SCHEDULED_EXECUTION (
|
||||
primary key(ID)
|
||||
)^
|
||||
create index IDX_SYS_SCH_EXE_TAS_STA_TIM on SYS_SCHEDULED_EXECUTION(TASK_ID, START_TIME)^
|
||||
create index IDX_SYS_SCH_EXE_TAS_FI_TIM on SYS_SCHEDULED_EXECUTION(TASK_ID, FINISH_TIME)^
|
||||
|
||||
create table SYS_SCHEDULED_TASK (
|
||||
ID varchar2(32) not null,
|
||||
|
@ -141,6 +141,7 @@ create table SYS_SCHEDULED_EXECUTION (
|
||||
)^
|
||||
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_START_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, START_TIME)^
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
||||
|
||||
------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
@ -0,0 +1 @@
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
@ -0,0 +1 @@
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
@ -0,0 +1 @@
|
||||
create index IDX_SYS_SCH_EXE_TAS_FI_TIM on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)
|
@ -0,0 +1 @@
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
@ -19,6 +19,7 @@ package com.haulmont.cuba.core.app.scheduling;
|
||||
|
||||
import com.haulmont.cuba.core.entity.ScheduledTask;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -40,4 +41,6 @@ public interface Coordinator {
|
||||
void end(Context context);
|
||||
|
||||
boolean isLastExecutionFinished(ScheduledTask task, long now);
|
||||
|
||||
long getLastFinished(ScheduledTask task);
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ import com.haulmont.cuba.core.Transaction;
|
||||
import com.haulmont.cuba.core.entity.ScheduledTask;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.LockModeType;
|
||||
import java.util.Date;
|
||||
@ -35,7 +35,6 @@ import java.util.List;
|
||||
* Implementation of {@link Coordinator} interface, performing synchronization of singleton schedulers on the main
|
||||
* database.
|
||||
* <p>This implementation should not be used if the database is overloaded.</p>
|
||||
*
|
||||
*/
|
||||
@Component(Coordinator.NAME)
|
||||
public class DbBasedCoordinator implements Coordinator {
|
||||
@ -108,6 +107,16 @@ public class DbBasedCoordinator implements Coordinator {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastFinished(ScheduledTask task) {
|
||||
EntityManager em = persistence.getEntityManager();
|
||||
Query query = em.createQuery(
|
||||
"select max(e.finishTime) from sys$ScheduledExecution e where e.task.id = ?1")
|
||||
.setParameter(1, task.getId());
|
||||
Date date = (Date) query.getFirstResult();
|
||||
return date == null ? 0 : date.getTime();
|
||||
}
|
||||
|
||||
protected synchronized List<ScheduledTask> getTasks() {
|
||||
log.trace("Read all active tasks from DB and lock them");
|
||||
EntityManager em = persistence.getEntityManager();
|
||||
|
@ -25,6 +25,7 @@ import com.haulmont.cuba.core.app.ServerInfoAPI;
|
||||
import com.haulmont.cuba.core.app.scheduled.MethodParameterInfo;
|
||||
import com.haulmont.cuba.core.entity.ScheduledExecution;
|
||||
import com.haulmont.cuba.core.entity.ScheduledTask;
|
||||
import com.haulmont.cuba.core.entity.SchedulingType;
|
||||
import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.Metadata;
|
||||
import com.haulmont.cuba.core.global.Scripting;
|
||||
@ -104,33 +105,31 @@ public class RunnerBean implements Runner {
|
||||
// It's better not to pass an entity instance in managed state to another thread
|
||||
final ScheduledTask taskCopy = metadata.getTools().copy(task);
|
||||
|
||||
executorService.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
log.debug(taskCopy + ": running");
|
||||
try {
|
||||
boolean runConcurrent = scheduling.setRunning(taskCopy, true);
|
||||
if (!runConcurrent) {
|
||||
executorService.submit(() -> {
|
||||
log.debug("{}: running", taskCopy);
|
||||
try {
|
||||
boolean runConcurrent = scheduling.setRunning(taskCopy, true);
|
||||
if (!runConcurrent) {
|
||||
try {
|
||||
setSecurityContext(taskCopy, userSession);
|
||||
ScheduledExecution execution = registerExecutionStart(taskCopy, now);
|
||||
statisticsCounter.incCubaScheduledTasksCount();
|
||||
try {
|
||||
setSecurityContext(taskCopy, userSession);
|
||||
ScheduledExecution execution = registerExecutionStart(taskCopy, now);
|
||||
statisticsCounter.incCubaScheduledTasksCount();
|
||||
try {
|
||||
Object result = executeTask(taskCopy);
|
||||
registerExecutionFinish(taskCopy, execution, result);
|
||||
} catch (Throwable throwable) {
|
||||
registerExecutionFinish(taskCopy, execution, throwable);
|
||||
throw throwable;
|
||||
}
|
||||
} finally {
|
||||
scheduling.setRunning(taskCopy, false);
|
||||
Object result = executeTask(taskCopy);
|
||||
registerExecutionFinish(taskCopy, execution, result);
|
||||
} catch (Throwable throwable) {
|
||||
registerExecutionFinish(taskCopy, execution, throwable);
|
||||
throw throwable;
|
||||
}
|
||||
} else {
|
||||
log.info("Detected concurrent task execution: {}, skip it", taskCopy);
|
||||
} finally {
|
||||
scheduling.setRunning(taskCopy, false);
|
||||
scheduling.setFinished(task);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
log.error("Error running " + taskCopy, throwable);
|
||||
} else {
|
||||
log.info("Detected concurrent task execution: {}, skip it", taskCopy);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
log.error("Error running {}", taskCopy, throwable);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -148,10 +147,10 @@ public class RunnerBean implements Runner {
|
||||
}
|
||||
|
||||
protected ScheduledExecution registerExecutionStart(ScheduledTask task, long now) {
|
||||
if (!BooleanUtils.isTrue(task.getLogStart()) && !BooleanUtils.isTrue(task.getSingleton()))
|
||||
if (!BooleanUtils.isTrue(task.getLogStart()) && !BooleanUtils.isTrue(task.getSingleton()) && task.getSchedulingType() != SchedulingType.FIXED_DELAY)
|
||||
return null;
|
||||
|
||||
log.trace(task + ": registering execution start");
|
||||
log.trace("{}: registering execution start", task);
|
||||
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
@ -172,10 +171,11 @@ public class RunnerBean implements Runner {
|
||||
}
|
||||
|
||||
protected void registerExecutionFinish(ScheduledTask task, ScheduledExecution execution, Object result) {
|
||||
if ((!BooleanUtils.isTrue(task.getLogFinish()) && !BooleanUtils.isTrue(task.getSingleton())) || execution == null)
|
||||
if ((!BooleanUtils.isTrue(task.getLogFinish()) && !BooleanUtils.isTrue(task.getSingleton()) && task.getSchedulingType() != SchedulingType.FIXED_DELAY)
|
||||
|| execution == null)
|
||||
return;
|
||||
|
||||
log.trace(task + ": registering execution finish");
|
||||
log.trace("{}: registering execution finish", task);
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
EntityManager em = persistence.getEntityManager();
|
||||
@ -193,7 +193,7 @@ public class RunnerBean implements Runner {
|
||||
protected Object executeTask(ScheduledTask task) {
|
||||
switch (task.getDefinedBy()) {
|
||||
case BEAN: {
|
||||
log.trace(task + ": invoking bean");
|
||||
log.trace("{}: invoking bean", task);
|
||||
Object bean = AppBeans.get(task.getBeanName());
|
||||
try {
|
||||
List<MethodParameterInfo> methodParams = task.getMethodParameters();
|
||||
|
@ -33,21 +33,23 @@ import com.haulmont.cuba.security.sys.UserSessionManager;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.perf4j.StopWatch;
|
||||
import org.perf4j.log4j.Log4JStopWatch;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.support.CronSequenceGenerator;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.*;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* Class that manages {@link ScheduledTask}s in distributed environment.
|
||||
*
|
||||
*/
|
||||
@Component(SchedulingAPI.NAME)
|
||||
public class Scheduling implements SchedulingAPI {
|
||||
@ -81,7 +83,9 @@ public class Scheduling implements SchedulingAPI {
|
||||
|
||||
protected ConcurrentMap<ScheduledTask, Long> runningTasks = new ConcurrentHashMap<>();
|
||||
|
||||
protected Map<ScheduledTask, Long> lastStartCache = new HashMap<>();
|
||||
protected Map<ScheduledTask, Long> lastStartCache = new ConcurrentHashMap<>();
|
||||
|
||||
protected Map<ScheduledTask, Long> lastFinishCache = new ConcurrentHashMap<>();
|
||||
|
||||
protected volatile long schedulingStartTime;
|
||||
|
||||
@ -136,6 +140,11 @@ public class Scheduling implements SchedulingAPI {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFinished(ScheduledTask task) {
|
||||
lastFinishCache.put(task, timeSource.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return configuration.getConfig(ServerConfig.class).getSchedulingActive();
|
||||
@ -168,7 +177,7 @@ public class Scheduling implements SchedulingAPI {
|
||||
|
||||
protected void processTask(ScheduledTask task) {
|
||||
if (isRunning(task)) {
|
||||
log.trace(task + " is running");
|
||||
log.trace("{} is running", task);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -189,12 +198,18 @@ public class Scheduling implements SchedulingAPI {
|
||||
|
||||
if (BooleanUtils.isTrue(task.getSingleton())) {
|
||||
if (task.getStartDate() != null || SchedulingType.CRON == task.getSchedulingType()) {
|
||||
long currentStart = calculateCurrentStart(task, task.getLastStart(), now, period, frame);
|
||||
|
||||
if (needToStartNow(now, frame, task.getLastStart(), currentStart)) {
|
||||
long currentStart;
|
||||
if (SchedulingType.FIXED_DELAY == task.getSchedulingType()) {
|
||||
currentStart = calculateNextDelayDate(task, task.getLastStart(), coordinator.getLastFinished(task), now, frame, period);
|
||||
} else if (SchedulingType.CRON == task.getSchedulingType()) {
|
||||
currentStart = calculateNextCronDate(task, task.getLastStart(), now, frame);
|
||||
} else {
|
||||
currentStart = calculateNextPeriodDate(task, task.getLastStart(), now, frame, period);
|
||||
}
|
||||
if (needToStartInTimeFrame(now, frame, task.getLastStart(), currentStart)) {
|
||||
runSingletonTask(task, now, me);
|
||||
} else {
|
||||
log.trace(task + "\n not in time frame to start");
|
||||
log.trace("{}\n not in time frame to start", task);
|
||||
}
|
||||
} else {
|
||||
Integer lastServerPriority = task.getLastStartServer() == null ?
|
||||
@ -208,40 +223,56 @@ public class Scheduling implements SchedulingAPI {
|
||||
boolean giveChanceToPreviousHost = lastServerWasNotMe(task, me)
|
||||
&& (lastServerPriority != null && serverPriority.compareTo(lastServerPriority) > 0);
|
||||
|
||||
if (log.isTraceEnabled())
|
||||
log.trace(task + "\n now=" + now + " lastStart=" + task.getLastStart()
|
||||
+ " lastServer=" + task.getLastStartServer() + " shouldSwitch=" + shouldSwitch
|
||||
+ " giveChanceToPreviousHost=" + giveChanceToPreviousHost);
|
||||
log.trace("{}\n now={} lastStart={} lastServer={} shouldSwitch={} giveChanceToPreviousHost={}",
|
||||
task, now, task.getLastStart(), task.getLastStartServer(), shouldSwitch, giveChanceToPreviousHost);
|
||||
|
||||
if (task.getLastStart() == 0
|
||||
|| shouldSwitch
|
||||
|| (task.getLastStart() + (giveChanceToPreviousHost ? period + period / 2 : period) <= now)) {
|
||||
if (task.getLastStart() == 0 || shouldSwitch) {
|
||||
runSingletonTask(task, now, me);
|
||||
} else {
|
||||
log.trace(task + "\n time has not come and we shouldn't switch");
|
||||
long delay = giveChanceToPreviousHost ? period + period / 2 : period;
|
||||
if (SchedulingType.FIXED_DELAY == task.getSchedulingType()) {
|
||||
long lastFinish = coordinator.getLastFinished(task);
|
||||
if ((task.getLastStart() < lastFinish || !lastFinishCache.containsKey(task)) && lastFinish + delay < now) {
|
||||
runSingletonTask(task, now, me);
|
||||
} else {
|
||||
log.trace("{}\n time has not come and we shouldn't switch", task);
|
||||
}
|
||||
} else if (task.getLastStart() + delay <= now) {
|
||||
runSingletonTask(task, now, me);
|
||||
} else {
|
||||
log.trace("{}\n time has not come and we shouldn't switch", task);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Long lastStart = lastStartCache.get(task);
|
||||
if (lastStart == null) {
|
||||
lastStart = 0L;
|
||||
}
|
||||
Long lastStart = lastStartCache.getOrDefault(task, 0L);
|
||||
Long lastFinish = lastFinishCache.getOrDefault(task, 0L);
|
||||
if (task.getStartDate() != null || SchedulingType.CRON == task.getSchedulingType()) {
|
||||
long currentStart = calculateCurrentStart(task, lastStart, now, period, frame);
|
||||
|
||||
if (needToStartNow(now, frame, lastStart, currentStart)) {
|
||||
long currentStart;
|
||||
if (SchedulingType.FIXED_DELAY == task.getSchedulingType()) {
|
||||
currentStart = calculateNextDelayDate(task, lastStart, lastFinish, now, frame, period);
|
||||
} else if (SchedulingType.CRON == task.getSchedulingType()) {
|
||||
currentStart = calculateNextCronDate(task, lastStart, now, frame);
|
||||
} else {
|
||||
currentStart = calculateNextPeriodDate(task, lastStart, now, frame, period);
|
||||
}
|
||||
if (needToStartInTimeFrame(now, frame, lastStart, currentStart)) {
|
||||
runTask(task, now);
|
||||
} else {
|
||||
log.trace(task + "\n not in time frame to start");
|
||||
log.trace("{}\n not in time frame to start", task);
|
||||
}
|
||||
} else {
|
||||
if (log.isTraceEnabled())
|
||||
log.trace(task + "\n now=" + now + " lastStart= " + lastStart);
|
||||
|
||||
if (now >= lastStart + period) {
|
||||
log.trace("{}\n now={} lastStart={} lastFinish={}", task, now, lastStart, lastFinish);
|
||||
if (SchedulingType.FIXED_DELAY == task.getSchedulingType()) {
|
||||
if ((lastStart == 0 || lastStart < lastFinish) && now >= lastFinish + period) {
|
||||
runTask(task, now);
|
||||
} else {
|
||||
log.trace("{}\n time has not come", task);
|
||||
}
|
||||
} else if (now >= lastStart + period) {
|
||||
runTask(task, now);
|
||||
} else {
|
||||
log.trace(task + "\n time has not come");
|
||||
log.trace("{}\n time has not come", task);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -250,42 +281,48 @@ public class Scheduling implements SchedulingAPI {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean needToStartNow(long now, long frame, long lastStart, long currentStart) {
|
||||
protected boolean needToStartInTimeFrame(long now, long frame, long lastStart, long currentStart) {
|
||||
return currentStart <= now && now < currentStart + frame && lastStart < currentStart;
|
||||
}
|
||||
|
||||
protected long calculateCurrentStart(ScheduledTask task, long lastStart, long now, long period, long frame) {
|
||||
String cron = task.getCron();
|
||||
if (SchedulingType.CRON == task.getSchedulingType()) {
|
||||
StopWatch sw = new Log4JStopWatch("Cron next date calculations");
|
||||
CronSequenceGenerator cronSequenceGenerator = new CronSequenceGenerator(cron, getCurrentTimeZone());
|
||||
//if last start = 0 (task never has run) or to far in the past, we use (NOW - FRAME) timestamp for pivot time
|
||||
//this approach should work fine cause cron works with absolute time
|
||||
long pivotPreviousTime = Math.max(lastStart, now - frame);
|
||||
protected long calculateNextCronDate(ScheduledTask task, long date, long currentDate, long frame) {
|
||||
StopWatch sw = new Log4JStopWatch("Cron next date calculations");
|
||||
CronSequenceGenerator cronSequenceGenerator = new CronSequenceGenerator(task.getCron(), getCurrentTimeZone());
|
||||
//if last start = 0 (task never has run) or to far in the past, we use (NOW - FRAME) timestamp for pivot time
|
||||
//this approach should work fine cause cron works with absolute time
|
||||
long pivotPreviousTime = Math.max(date, currentDate - frame);
|
||||
|
||||
Date currentStart = null;
|
||||
Date nextDate = cronSequenceGenerator.next(new Date(pivotPreviousTime));
|
||||
while (nextDate.getTime() < now) {//if next date is in past try to find next date nearest to now
|
||||
currentStart = nextDate;
|
||||
nextDate = cronSequenceGenerator.next(nextDate);
|
||||
}
|
||||
|
||||
if (currentStart == null) {
|
||||
currentStart = nextDate;
|
||||
}
|
||||
|
||||
log.trace(task + "\n now=" + now + " frame=" + frame
|
||||
+ " currentStart=" + currentStart + " lastStart=" + lastStart + " cron=" + cron);
|
||||
sw.stop();
|
||||
return currentStart.getTime();
|
||||
} else {
|
||||
long repetitions = (now - task.getStartDate().getTime()) / period;
|
||||
long currentStart = task.getStartDate().getTime() + repetitions * period;
|
||||
|
||||
log.trace(task + "\n now=" + now + " frame=" + frame + " repetitions=" + repetitions +
|
||||
" currentStart=" + currentStart + " lastStart=" + lastStart);
|
||||
return currentStart;
|
||||
Date currentStart = null;
|
||||
Date nextDate = cronSequenceGenerator.next(new Date(pivotPreviousTime));
|
||||
while (nextDate.getTime() < currentDate) {//if next date is in past try to find next date nearest to now
|
||||
currentStart = nextDate;
|
||||
nextDate = cronSequenceGenerator.next(nextDate);
|
||||
}
|
||||
|
||||
if (currentStart == null) {
|
||||
currentStart = nextDate;
|
||||
}
|
||||
log.trace("{}\n now={} frame={} currentStart={} lastStart={} cron={}",
|
||||
task, currentDate, frame, currentStart, task.getCron());
|
||||
sw.stop();
|
||||
return currentStart.getTime();
|
||||
}
|
||||
|
||||
protected long calculateNextPeriodDate(ScheduledTask task, long date, long currentDate, long frame, long period) {
|
||||
long repetitions = (currentDate - task.getStartDate().getTime()) / period;
|
||||
long currentStart = task.getStartDate().getTime() + repetitions * period;
|
||||
log.trace("{}\n now={} frame={} repetitions={} currentStart={} lastStart={}",
|
||||
task, currentDate, frame, repetitions, currentStart, date);
|
||||
return currentStart;
|
||||
}
|
||||
|
||||
protected long calculateNextDelayDate(ScheduledTask task, long lastStart, long lastFinish, long currentDate, long frame, long period) {
|
||||
long fromDate = lastFinish != 0 ? lastFinish : task.getStartDate().getTime();
|
||||
long repetitions = (currentDate - fromDate) / period;
|
||||
long currentStart = fromDate + repetitions * period;
|
||||
log.trace("{}\n now={} frame={} repetitions={} currentStart={} lastStart={} lastFinish={}",
|
||||
task, currentDate, frame, repetitions, currentStart, lastStart, lastFinish);
|
||||
return currentStart;
|
||||
}
|
||||
|
||||
protected TimeZone getCurrentTimeZone() {
|
||||
|
@ -55,7 +55,7 @@ public interface SchedulingAPI {
|
||||
void processScheduledTasks(boolean onlyIfActive);
|
||||
|
||||
/**
|
||||
* Mark the sheduled task as running/not running in the internal list. This method should not be used in the
|
||||
* Mark the scheduled task as running/not running in the internal list. This method should not be used in the
|
||||
* application code.
|
||||
* @param task task instance
|
||||
* @param running true to mark as running, false to mark as not running
|
||||
@ -63,6 +63,13 @@ public interface SchedulingAPI {
|
||||
*/
|
||||
boolean setRunning(ScheduledTask task, boolean running);
|
||||
|
||||
/**
|
||||
* Mark the scheduled task as finished in the internal list. This method should not be used in the
|
||||
* application code.
|
||||
* @param task task instance
|
||||
*/
|
||||
void setFinished(ScheduledTask task);
|
||||
|
||||
/**
|
||||
* @return a list of active task instances in detached state
|
||||
*/
|
||||
|
@ -60,42 +60,42 @@ public class SchedulingTest {
|
||||
scheduledTask.setCron("*/5 * * * * *");
|
||||
|
||||
//scheduler has failed couple of runs and now we should run it
|
||||
long currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-11-13 15:29:00").getTime(), date("2013-11-13 15:30:00").getTime(), 0, 10000l);
|
||||
long currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-11-13 15:29:00").getTime(), date("2013-11-13 15:30:00").getTime(), 10000l);
|
||||
assertEquals(date("2013-11-13 15:29:55"), new Date(currentStart));
|
||||
|
||||
//last run was year ago, so now-frame should be considered
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2012-11-13 15:29:00").getTime(), date("2013-11-13 15:30:00").getTime(), 0, 10000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2012-11-13 15:29:00").getTime(), date("2013-11-13 15:30:00").getTime(), 10000l);
|
||||
assertEquals(date("2013-11-13 15:29:55"), new Date(currentStart));
|
||||
|
||||
//last run was very close to now, last start date should be considered
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-11-13 15:29:59").getTime(), date("2013-11-13 15:30:01").getTime(), 0, 10000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-11-13 15:29:59").getTime(), date("2013-11-13 15:30:01").getTime(), 10000l);
|
||||
assertEquals(date("2013-11-13 15:30:00"), new Date(currentStart));
|
||||
|
||||
scheduledTask.setCron("0 0 0 * * FRI");
|
||||
|
||||
//task should run in next friday
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-11-08 01:01:01").getTime(), date("2013-11-13 15:30:00").getTime(), 0, 10000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-11-08 01:01:01").getTime(), date("2013-11-13 15:30:00").getTime(), 10000l);
|
||||
assertEquals(date("2013-11-15 00:00:00"), new Date(currentStart));
|
||||
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-11-08 01:01:01").getTime(), date("2013-11-08 01:01:02").getTime(), 0, 600000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-11-08 01:01:01").getTime(), date("2013-11-08 01:01:02").getTime(), 600000l);
|
||||
assertEquals(date("2013-11-15 00:00:00"), new Date(currentStart));
|
||||
|
||||
//task is late but matches frame
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-11-07 23:59:59").getTime(), date("2013-11-08 00:01:00").getTime(), 0, 600000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-11-07 23:59:59").getTime(), date("2013-11-08 00:01:00").getTime(), 600000l);
|
||||
assertEquals(date("2013-11-8 00:00:00"), new Date(currentStart));
|
||||
|
||||
//task is late and does not match frame
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-11-07 23:59:59").getTime(), date("2013-11-08 00:11:00").getTime(), 0, 600000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-11-07 23:59:59").getTime(), date("2013-11-08 00:11:00").getTime(), 600000l);
|
||||
assertEquals(date("2013-11-15 00:00:00"), new Date(currentStart));
|
||||
|
||||
scheduledTask.setCron("0 59 1 * * *");
|
||||
|
||||
//time shift forward
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-10-26 1:59:59").getTime(), date("2013-10-27 00:00:00").getTime(), 0, 600000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-10-26 1:59:59").getTime(), date("2013-10-27 00:00:00").getTime(), 600000l);
|
||||
assertEquals(date("2013-10-27 01:59:00"), new Date(currentStart));
|
||||
|
||||
//time shift backward
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-03-30 1:59:00").getTime(), date("2013-03-31 00:00:00").getTime(), 0, 600000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-03-30 1:59:00").getTime(), date("2013-03-31 00:00:00").getTime(), 600000l);
|
||||
assertEquals(date("2013-03-31 01:59:00"), new Date(currentStart));
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,8 @@ import com.haulmont.chile.core.datatypes.impl.EnumClass;
|
||||
|
||||
public enum SchedulingType implements EnumClass<String> {
|
||||
CRON("C"),
|
||||
PERIOD("P");
|
||||
PERIOD("P"),
|
||||
FIXED_DELAY("D");
|
||||
|
||||
private final String id;
|
||||
|
||||
|
@ -132,6 +132,7 @@ ScheduledTask.methodParamsXml = XML
|
||||
|
||||
SchedulingType.CRON=Cron
|
||||
SchedulingType.PERIOD=Period
|
||||
SchedulingType.FIXED_DELAY=Fixed Delay
|
||||
|
||||
ScheduledExecution = Scheduled Execution
|
||||
ScheduledExecution.task = Task
|
||||
|
@ -135,6 +135,7 @@ ScheduledTask.methodParamsXml = XML
|
||||
|
||||
SchedulingType.CRON=Cron
|
||||
SchedulingType.PERIOD=Period
|
||||
SchedulingType.FIXED_DELAY=Fixed Delay
|
||||
|
||||
ScheduledExecution = Выполнение задания
|
||||
ScheduledExecution.task = Task
|
||||
|
Loading…
Reference in New Issue
Block a user