@samveys/scheduler
From packages/scheduler/README.md in the framework repository.
Define scheduled tasks in code and run them from one cron entry.
* * * * * cd /srv/app && svs schedule:runDefine tasks
import { cache, defineCacheConfig, memoryStore } from '@samveys/cache'
import { scheduler } from '@samveys/scheduler'
export const plugins = [
cache(defineCacheConfig({ default: 'main', stores: { main: memoryStore() } })),
scheduler({
define(schedule) {
schedule
.call('reports', async () => {
console.log('building reports')
})
.dailyAt('02:00')
.timezone('Asia/Jakarta')
.withoutOverlapping()
.onOneServer()
.pingBefore('https://monitor.example.com/start')
.thenPing('https://monitor.example.com/done')
schedule.call('heartbeat', () => {}).cron('*/5 * * * *')
schedule.call('cleanup', () => {}).hourly()
schedule.call('tick', () => {}).everyMinute()
},
}),
]- Cron: five fields (
minute hour day-of-month month day-of-week) with*, lists, ranges and steps;7is Sunday. When both day fields are restricted, either may match (as in Vixie cron). Fields are evaluated withTemporalin the task's zone (defaultUTC), sodailyAt('02:00')withAsia/Jakartaruns at 19:00 UTC; a wall-clock time that does not exist on a spring-forward day does not run that day. withoutOverlapping(ttlMs?): a cache lock; a run that starts while the previous one still holds it is skipped (skipped-overlap). The lock is released when the task ends and expires afterttlMs(default 24 h) if the process dies.onOneServer(): each due minute is claimed withcache.lock(...). Servers that share a cache store (a database store, not a memory store) never both run the task for that minute.pingBefore/thenPing: GET requests; a failed ping never fails the task, andthenPingonly follows success.
Locks need the cache() plugin; without it the task fails with SVS_E102 and a message saying so.
Commands
svs schedule:run # run what is due this minute, once, and exit (exit code 1 if a task failed)
svs schedule:list # name, cron, zone, next runParsing and next run
import { nextCronRun, parseCron } from '@samveys/scheduler'
const cron = parseCron('0 9 * * 1-5')
const next = nextCronRun(cron, Temporal.Now.instant(), 'Europe/Berlin')
console.log(next.toString())