src/metronome/scheduler

Search:
Group by:

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 `$`(beater: Beater): string {....raises: [], tags: [], forbids: [].}
proc failures(beater: Beater): int {....raises: [], tags: [], forbids: [].}
Return the number of job failures recorded for this beater.
proc failures(self: Scheduler; id: string): int {....raises: [], tags: [],
    forbids: [].}
Return the number of failures recorded for a registered job.
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.

proc id(beater: Beater): string {....raises: [], tags: [], forbids: [].}
Return the identifier assigned to this beater.
proc idle(self: Scheduler): owned(Future[void]) {....stackTrace: false,
    raises: [Exception, ValueError], tags: [RootEffect, TimeEffect], forbids: [].}
Idle the scheduler. It prevents the scheduler from shutdown when no beats is running.
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 lastError(beater: Beater): ref Exception {....raises: [], tags: [],
    forbids: [].}
Return the most recent job error, or nil if no job has failed.
proc lastError(self: Scheduler; id: string): ref Exception {....raises: [],
    tags: [], forbids: [].}
Return the most recent error for a registered job, or nil.
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 lastRun(beater: Beater): Option[DateTime] {....raises: [], tags: [],
    forbids: [].}
Return the last time this beater launched a job.
proc lastRun(self: Scheduler; id: string): Option[DateTime] {....raises: [],
    tags: [], forbids: [].}
Return the last run time for a registered job.
proc listJobs(self: Scheduler): seq[string] {....raises: [], tags: [], forbids: [].}
Return all non-empty registered job identifiers.
proc newSettings(appName = ""; errorHandler: JobErrorHandler = nil): Settings {.
    ...raises: [], tags: [], forbids: [].}
proc nextRun(beater: Beater): Option[DateTime] {....raises: [], tags: [],
    forbids: [].}
Return the next scheduled run time for this beater.
proc nextRun(self: Scheduler; id: string): Option[DateTime] {....raises: [],
    tags: [], forbids: [].}
Return the next run time for a registered job.
proc pause(beater: Beater) {....raises: [], tags: [], forbids: [].}
Pause future job launches for this beater.
proc pause(self: Scheduler; id: string): bool {....raises: [], tags: [],
    forbids: [].}
Pause a registered job by identifier.
proc register(self: Scheduler; beater: Beater) {....raises: [], tags: [],
    forbids: [].}
Register a beater.
proc resume(beater: Beater) {....raises: [], tags: [TimeEffect], forbids: [].}
Resume job launches for this beater.
proc resume(self: Scheduler; id: string): bool {....raises: [], tags: [TimeEffect],
    forbids: [].}
Resume a registered job by identifier.
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 serve(self: Scheduler) {....raises: [Exception, ValueError, KeyError, OSError],
                              tags: [RootEffect, TimeEffect], forbids: [].}
Serve the scheduler. It's a blocking function.
proc start(self: Scheduler): owned(Future[void]) {....stackTrace: false,
    raises: [Exception, ValueError, KeyError], tags: [RootEffect, TimeEffect],
    forbids: [].}
Start the scheduler.
proc state(beater: Beater): BeaterState {....raises: [], tags: [], forbids: [].}
Return the current lifecycle state for this beater.
proc stop(beater: Beater) {....raises: [], tags: [], forbids: [].}
Stop this beater permanently.
proc stop(self: Scheduler; id: string): bool {....raises: [], tags: [], forbids: [].}
Stop a registered job by identifier.
proc stopAll(self: Scheduler) {....raises: [], tags: [], forbids: [].}
Stop all registered jobs.
proc submit(self: Throttler; fut: Future[void]) {....raises: [], tags: [],
    forbids: [].}
Submit a new future to the throttler. WARNING: this function does not perform throttling check.
proc throttled(self: Throttler): bool {....raises: [], tags: [], forbids: [].}
Whether the throttler is allowed to schedule more beats.
proc waitFor(self: Scheduler) {....raises: [ValueError, Exception, OSError,
    KeyError], tags: [TimeEffect, RootEffect], forbids: [].}
Run all beats til they're completed.

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.