Metronome
A Nim scheduler library that lets you kick off jobs at regular intervals.
Example usage::
metronome:
every(seconds=1, id="tick", throttle=1, async=true):
echo("async tick ", now())
await sleepAsync(2000)
every(seconds=1, id="sync tick", throttle=1):
echo("sync tick ", now())
Types
Beater = ref object of RootObj case kind*: BeaterKind of bkInterval: interval*: TimeInterval jitter*: TimeInterval of bkCron: cron*: Cron timezone*: Option[Timezone] of bkCustom: nextRunProc*: NextRunProc of bkOnce: nil
- Beater generates beats for the next runs.
BeaterAsyncProc = proc (): Future[void] {.closure.}
BeaterKind {.pure.} = enum bkInterval, bkCron, bkCustom, bkOnce
BeaterState {.pure.} = enum bsRunning, bsPaused, bsStopped
BeaterThreadProc = proc (): void {....gcsafe, thread.}
JobErrorHandler = proc (fut: Future[void]) {.closure, ...gcsafe.}
NextRunProc = proc (current: DateTime): Option[DateTime] {.closure, ...gcsafe, raises: [].}
Scheduler = ref object
Settings = ref object appName*: string errorHandler*: JobErrorHandler
Throttler = ref object
- Throttle the total number of beats.
Vars
logger = newConsoleLogger(lvlAll, defaultFmtStr, false, defaultFlushThreshold)
- By default, the logger is attached to no handlers. If you want to show logs, please call addHandler(logger).
Procs
proc fire(self: Beater; errorHandler: JobErrorHandler = nil): owned( Future[void]) {....stackTrace: false, raises: [Exception, ValueError, KeyError], tags: [RootEffect, TimeEffect], forbids: [].}
- Fire beats as async loop until no beats can be scheduled.
proc fireTime(self: Beater; prev: Option[DateTime]; now: DateTime): Option[ DateTime] {....raises: [KeyError, Exception], tags: [RootEffect], forbids: [].}
-
Returns the next fire time of a task execution.
For bkInterval, it uses these rules:
- For the 1st run,
- Choose startTime when it is equal to or later than now.
- Choose the next future startTime + N * interval when startTime is earlier than now.
- For the rest of runs,
- Choose prev + interval.
Cron beaters delegate to cron matching, optionally in their configured timezone. Custom beaters delegate to their strictly-future next-run callback. One-shot beaters return their scheduled time only while prev is none.
If self.endTime is set and the computed fire time is later than it, none(DateTime) is returned. A fire time exactly equal to endTime is still allowed.
- For the 1st run,
proc initBeater(cron: Cron; asyncProc: BeaterAsyncProc; startTime: Option[DateTime] = none(DateTime); endTime: Option[DateTime] = none(DateTime); id: string = ""; throttleNum: int = 1; errorHandler: JobErrorHandler = nil; timezone: Option[Timezone] = none(Timezone)): Beater {. ...raises: [ValueError], tags: [TimeEffect], forbids: [].}
-
Initialize a Beater, which kind is bkCron.
startTime, endTime, and timezone are optional. When timezone is set, cron matching is evaluated in that timezone and converted back to the caller's timezone for scheduling.
proc initBeater(cron: Cron; threadProc: BeaterThreadProc; startTime: Option[DateTime] = none(DateTime); endTime: Option[DateTime] = none(DateTime); id: string = ""; throttleNum: int = 1; errorHandler: JobErrorHandler = nil; timezone: Option[Timezone] = none(Timezone)): Beater {. ...raises: [ValueError], tags: [TimeEffect], forbids: [].}
-
Initialize a Beater, which kind is bkCron.
startTime, endTime, and timezone are optional. When timezone is set, cron matching is evaluated in that timezone and converted back to the caller's timezone for scheduling.
proc initBeater(interval: TimeInterval; asyncProc: BeaterAsyncProc; startTime: Option[DateTime] = none(DateTime); endTime: Option[DateTime] = none(DateTime); id: string = ""; throttleNum: int = 1; errorHandler: JobErrorHandler = nil; jitter: TimeInterval = initTimeInterval()): Beater {. ...raises: [ValueError], tags: [TimeEffect], forbids: [].}
-
Initialize a Beater, which kind is bkInterval.
startTime, endTime, and jitter are optional. Jitter adds a random non-negative delay to each interval launch without changing the base interval cadence.
proc initBeater(interval: TimeInterval; threadProc: BeaterThreadProc; startTime: Option[DateTime] = none(DateTime); endTime: Option[DateTime] = none(DateTime); id: string = ""; throttleNum: int = 1; errorHandler: JobErrorHandler = nil; jitter: TimeInterval = initTimeInterval()): Beater {. ...raises: [ValueError], tags: [TimeEffect], forbids: [].}
-
Initialize a Beater, which kind is bkInterval.
startTime, endTime, and jitter are optional. Jitter adds a random non-negative delay to each interval launch without changing the base interval cadence.
proc initBeater(nextRunProc: NextRunProc; asyncProc: BeaterAsyncProc; startTime: Option[DateTime] = none(DateTime); endTime: Option[DateTime] = none(DateTime); id: string = ""; throttleNum: int = 1; errorHandler: JobErrorHandler = nil): Beater {. ...raises: [ValueError], tags: [TimeEffect], forbids: [].}
-
Initialize a custom-schedule Beater.
The callback must return the first instant strictly later than its input. startTime and endTime constrain the resulting schedule.
proc initBeater(nextRunProc: NextRunProc; threadProc: BeaterThreadProc; startTime: Option[DateTime] = none(DateTime); endTime: Option[DateTime] = none(DateTime); id: string = ""; throttleNum: int = 1; errorHandler: JobErrorHandler = nil): Beater {. ...raises: [ValueError], tags: [TimeEffect], forbids: [].}
- Initialize a thread-backed custom-schedule Beater.
proc initBeater(time: DateTime; asyncProc: BeaterAsyncProc; id: string = ""; throttleNum: int = 1; errorHandler: JobErrorHandler = nil): Beater {. ...raises: [ValueError], tags: [TimeEffect], forbids: [].}
- Initialize a one-shot Beater, which launches once at time.
proc initBeater(time: DateTime; threadProc: BeaterThreadProc; id: string = ""; throttleNum: int = 1; errorHandler: JobErrorHandler = nil): Beater {. ...raises: [ValueError], tags: [TimeEffect], forbids: [].}
- Initialize a one-shot Beater, which launches once at time.
proc initScheduler(settings: Settings): Scheduler {....raises: [], tags: [], forbids: [].}
- Initialize a scheduler.
proc initThrottler(num: int = 1): Throttler {....raises: [ValueError], tags: [], forbids: [].}
- Initialize the total number of beats allowed to be scheduled. By default, it's 1. If it's greater than 1, then more than one beats can be scheduled simultaneously.
proc jobState(self: Scheduler; id: string): Option[BeaterState] {....raises: [], tags: [], forbids: [].}
- Return the lifecycle state for a registered job.
proc lastErrorAt(beater: Beater): Option[DateTime] {....raises: [], tags: [], forbids: [].}
- Return when this beater most recently recorded a job error.
proc lastErrorAt(self: Scheduler; id: string): Option[DateTime] {....raises: [], tags: [], forbids: [].}
- Return when a registered job most recently recorded an error.
proc newSettings(appName = ""; errorHandler: JobErrorHandler = nil): Settings {. ...raises: [], tags: [], forbids: [].}
proc runningCount(beater: Beater): int {....raises: [], tags: [], forbids: [].}
- Return the number of currently running jobs for this beater.
proc runningCount(self: Scheduler; id: string): int {....raises: [], tags: [], forbids: [].}
- Return the number of currently running jobs for a registered job.
proc state(beater: Beater): BeaterState {....raises: [], tags: [], forbids: [].}
- Return the current lifecycle state for this beater.
Macros
macro metronome(body: untyped): untyped
-
Initialize a scheduler, register code blocks as beats, and run it as a blocking application.
You'll use it when the scheduled jobs are the only thing your programm will need to handle.
macro scheduler(sched: untyped; body: untyped)
-
Initialize a scheduler and register code blocks as beats.
Use it when running Metronome alongside another event-driven library, such as a web framework.