From 425e3a4fc7225138e8266309e5956a2b94cd7652 Mon Sep 17 00:00:00 2001 From: zalgonoise Date: Thu, 23 Nov 2023 20:13:19 +0100 Subject: [PATCH] micron/README.md: second pass on the README's hyperlinks, to reference the correct lines in the codebase --- README.md | 174 +++++++++++++++++++++++++++--------------------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 26fae35..1440f41 100644 --- a/README.md +++ b/README.md @@ -93,17 +93,17 @@ _______ The runtime is the component that will control (like the name implies) how the module runs -- that is, controlling the flow of job selection and execution. The runtime will allow cron to be executed as a goroutine, as its -[`Runtime.Run`](./cron.go#L60) method has no returns, and errors are channeled via its [`Runtime.Err`](./cron.go#L77) +[`Runtime.Run`](./cron.go#L61) method has no returns, and errors are channeled via its [`Runtime.Err`](./cron.go#L78) method (which returns an error channel). The actual runtime of the cron is still managed with a `context.Context` that -is provided when calling [`Runtime.Run`](./cron.go#L60) -- which can impose a cancellation or timeout strategy. +is provided when calling [`Runtime.Run`](./cron.go#L61) -- which can impose a cancellation or timeout strategy. Just like the simple example above, creating a cron runtime starts with the -[`cron.New` constructor function](./cron.go#L86). +[`cron.New` constructor function](./cron.go#L87). -This function only has [a variadic parameter for `cfg.Option[cron.Config]`](./cron.go#L86). This allows full modularity +This function only has [a variadic parameter for `cfg.Option[cron.Config]`](./cron.go#L87). This allows full modularity on the way you build your cron runtime, to be as simple or as detailed as you want it to be -- provided that it complies with the minimum requirements to create one; to supply either: -- a [`selector.Selector`](./selector/selector.go#L36) +- a [`selector.Selector`](./selector/selector.go#L37) - or, a (set of) [`executor.Runner`](./executor/executor.go#L40). This can be supplied as [`executor.Runnable`](./executor/executor.go#L53) as well. @@ -115,39 +115,39 @@ Below is a table with all the options available for creating a cron runtime: | Function | Input Parameters | Description | |:---------------------------------------------:|:--------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| -| [`WithSelector`](./cron_config.go#L31) | [`sel selector.Selector`](./selector/selector.go#L36) | Configures the [`Runtime`](./cron.go#L33) with the input [`selector.Selector`](./selector/selector.go#L36). | -| [`WithJob`](./cron_config.go#L53) | `id string`, `cronString string`, [`runners ...executor.Runner`](./executor/executor.go#L40) | Adds a new [`executor.Executor`](./executor/executor.go#L84) to the [`Runtime`](./cron.go#L33) configuration from the input ID, cron string and set of [`executor.Runner`](./executor/executor.go#L40). | -| [`WithErrorBufferSize`](./cron_config.go#L83) | `size int` | Defines the capacity of the error channel that the [`Runtime`](./cron.go#L33) exposes in its [`Runtime.Err`](./cron.go#L77) method. | -| [`WithMetrics`](./cron_config.go#L96) | [`m cron.Metrics`](./cron_with_metrics.go#L10) | Decorates the [`Runtime`](./cron.go#L33) with the input metrics registry. | -| [`WithLogger`](./cron_config.go#L109) | [`logger *slog.Logger`](https://pkg.go.dev/log/slog#Logger) | Decorates the [`Runtime`](./cron.go#L33) with the input logger. | -| [`WithLogHandler`](./cron_config.go#L122) | [`handler slog.Handler`](https://pkg.go.dev/log/slog#Handler) | Decorates the [`Runtime`](./cron.go#L33) with logging using the input log handler. | -| [`WithTrace`](./cron_config.go#L135) | [`tracer trace.Tracer`](https://pkg.go.dev/go.opentelemetry.io/otel/trace#Tracer) | Decorates the [`Runtime`](./cron.go#L33) with the input trace.Tracer. | - -The simplest possible cron runtime could be the result of a call to [`cron.New`](./cron.go#L86) with a single -[`cron.WithJob`](./cron_config.go#L53) option. This creates all the components that a cron runtime needs with the most +| [`WithSelector`](./cron_config.go#L32) | [`sel selector.Selector`](./selector/selector.go#L37) | Configures the with the input [`selector.Selector`](./selector/selector.go#L37). | +| [`WithJob`](./cron_config.go#L54) | `id string`, `cronString string`, [`runners ...executor.Runner`](./executor/executor.go#L40) | Adds a new [`executor.Executor`](./executor/executor.go#L84) to the [`Runtime`](./cron.go#L34) configuration from the input ID, cron string and set of [`executor.Runner`](./executor/executor.go#L40). | +| [`WithErrorBufferSize`](./cron_config.go#L84) | `size int` | Defines the capacity of the error channel that the [`Runtime`](./cron.go#L34) exposes in its [`Runtime.Err`](./cron.go#L77) method. | +| [`WithMetrics`](./cron_config.go#L97) | [`m cron.Metrics`](./cron_with_metrics.go#L10) | Decorates the [`Runtime`](./cron.go#L34) with the input metrics registry. | +| [`WithLogger`](./cron_config.go#L110) | [`logger *slog.Logger`](https://pkg.go.dev/log/slog#Logger) | Decorates the [`Runtime`](./cron.go#L34) with the input logger. | +| [`WithLogHandler`](./cron_config.go#L123) | [`handler slog.Handler`](https://pkg.go.dev/log/slog#Handler) | Decorates the [`Runtime`](./cron.go#L34) with logging using the input log handler. | +| [`WithTrace`](./cron_config.go#L136) | [`tracer trace.Tracer`](https://pkg.go.dev/go.opentelemetry.io/otel/trace#Tracer) | Decorates the [`Runtime`](./cron.go#L34) with the input trace.Tracer. | + +The simplest possible cron runtime could be the result of a call to [`cron.New`](./cron.go#L87) with a single +[`cron.WithJob`](./cron_config.go#L54) option. This creates all the components that a cron runtime needs with the most minimal setup. It creates the underlying selector and executors. -Otherwise, the caller must use the [`WithSelector`](./cron_config.go#L31) option, and configure a -[`selector.Selector`](./selector/selector.go#L36) manually when doing so. This results in more _boilerplate_ to get the +Otherwise, the caller must use the [`WithSelector`](./cron_config.go#L32) option, and configure a +[`selector.Selector`](./selector/selector.go#L37) manually when doing so. This results in more _boilerplate_ to get the runtime set up, but provides deeper control on how the cron should be composed. The next chapter covers what is a -[`selector.Selector`](./selector/selector.go#L36) and how to create one. +[`selector.Selector`](./selector/selector.go#L37) and how to create one. _______ #### Cron Selector This component is responsible for picking up the next job to execute, according to their schedule frequency. For this, -the [`Selector`](./selector/selector.go#L36) is configured with a set of +the [`Selector`](./selector/selector.go#L37) is configured with a set of [`executor.Executor`](./executor/executor.go#L84), which in turn will expose a -[`Next` method](./executor/executor.go#L92). With this information, the [`Selector`](./selector/selector.go#L36) cycles +[`Next` method](./executor/executor.go#L92). With this information, the [`Selector`](./selector/selector.go#L37) cycles through its [`executor.Executor`](./executor/executor.go#L84) and picks up the next task(s) to run. -While the [`Selector`](./selector/selector.go#L36) calls the +While the [`Selector`](./selector/selector.go#L37) calls the [`executor.Executor`'s `Exec` method](./executor/executor.go#L90), the actual waiting is within the [`executor.Executor`'s](./executor/executor.go#L84) logic. -You're able to create a [`Selector`](./selector/selector.go#L36) through -[its constructor function](./selector/selector.go#L115): +You're able to create a [`Selector`](./selector/selector.go#L37) through +[its constructor function](./selector/selector.go#L142): ```go func New(options ...cfg.Option[Config]) (Selector, error) @@ -157,24 +157,24 @@ func New(options ...cfg.Option[Config]) (Selector, error) Below is a table with all the options available for creating a cron job selector: -| Function | Input Parameters | Description | -|:-----------------------------------------------------:|:---------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| -| [`WithExecutors`](./selector/selector_config.go#L23) | [`executors ...executor.Executor`](./executor/executor.go#L84) | Configures the [`Selector`](./selector/selector.go#L36) with the input [`executor.Executor`(s)](./executor/executor.go#L84). | -| [`WithBlock`](./selector/selector_config.go#L23) | | Configures the [`Selector`](./selector/selector.go#L36) to block (wait) for the underlying [`executor.Executor`(s)](./executor/executor.go#L84) to complete the task. | -| [`WithTimeout`](./selector/selector_config.go#L23) | `dur time.Duration` | Configures a (non-blocking) [`Selector`](./selector/selector.go#L36) to wait a certain duration before detaching of the executable task, before continuing to select the next one. | -| [`WithMetrics`](./selector/selector_config.go#L51) | [`m selector.Metrics`](./selector/selector_with_metrics.go#L10) | Decorates the [`Selector`](./selector/selector.go#L36) with the input metrics registry. | -| [`WithLogger`](./selector/selector_config.go#L64) | [`logger *slog.Logger`](https://pkg.go.dev/log/slog#Logger) | Decorates the [`Selector`](./selector/selector.go#L36) with the input logger. | -| [`WithLogHandler`](./selector/selector_config.go#L77) | [`handler slog.Handler`](https://pkg.go.dev/log/slog#Handler) | Decorates the [`Selector`](./selector/selector.go#L36) with logging using the input log handler. | -| [`WithTrace`](./selector/selector_config.go#L90) | [`tracer trace.Tracer`](https://pkg.go.dev/go.opentelemetry.io/otel/trace#Tracer) | Decorates the [`Selector`](./selector/selector.go#L36) with the input trace.Tracer. | +| Function | Input Parameters | Description | +|:------------------------------------------------------:|:---------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| +| [`WithExecutors`](./selector/selector_config.go#L27) | [`executors ...executor.Executor`](./executor/executor.go#L84) | Configures the [`Selector`](./selector/selector.go#L37) with the input [`executor.Executor`(s)](./executor/executor.go#L84). | +| [`WithBlock`](./selector/selector_config.go#L61) | | Configures the [`Selector`](./selector/selector.go#L37) to block (wait) for the underlying [`executor.Executor`(s)](./executor/executor.go#L84) to complete the task. | +| [`WithTimeout`](./selector/selector_config.go#L74) | `dur time.Duration` | Configures a (non-blocking) [`Selector`](./selector/selector.go#L37) to wait a certain duration before detaching of the executable task, before continuing to select the next one. | +| [`WithMetrics`](./selector/selector_config.go#L87) | [`m selector.Metrics`](./selector/selector_with_metrics.go#L10) | Decorates the [`Selector`](./selector/selector.go#L37) with the input metrics registry. | +| [`WithLogger`](./selector/selector_config.go#L100) | [`logger *slog.Logger`](https://pkg.go.dev/log/slog#Logger) | Decorates the [`Selector`](./selector/selector.go#L37) with the input logger. | +| [`WithLogHandler`](./selector/selector_config.go#L113) | [`handler slog.Handler`](https://pkg.go.dev/log/slog#Handler) | Decorates the [`Selector`](./selector/selector.go#L37) with logging using the input log handler. | +| [`WithTrace`](./selector/selector_config.go#L126) | [`tracer trace.Tracer`](https://pkg.go.dev/go.opentelemetry.io/otel/trace#Tracer) | Decorates the [`Selector`](./selector/selector.go#L37) with the input trace.Tracer. | -There is a catch to the [`Selector`](./selector/selector.go#L36), which is the actual job's execution time. While the -[`Selector`](./selector/selector.go#L36) cycles through its [`executor.Executor`](./executor/executor.go#L84) list, it +There is a catch to the [`Selector`](./selector/selector.go#L37), which is the actual job's execution time. While the +[`Selector`](./selector/selector.go#L37) cycles through its [`executor.Executor`](./executor/executor.go#L84) list, it will execute the task while waiting for it to return with or without an error. This may cause issues when a given running task takes too long to complete when there are other, very frequent tasks. If there is a situation where the long-running task overlaps the execution time for another scheduled job, that job's execution is potentially skipped -- as the next task would only be picked up and waited for once the long-running one exits. -For this reason, there are two implementations of [`Selector`](./selector/selector.go#L36): +For this reason, there are two implementations of [`Selector`](./selector/selector.go#L37): - A blocking one, that waits for every job to run and return an error, accurately returning the correct outcome in its `Next` call. This implementation is great for fast and snappy jobs, or less frequent / non-overlapping schedules and executions. There is less resource overhead to it, and the error returns are fully accurate with the actual outcome. @@ -182,7 +182,7 @@ executions. There is less resource overhead to it, and the error returns are ful caller or a default one). This implementation is great if the jobs are too frequent and / or the tasks too long, when it risks skipping executions due to stuck long-running tasks. It relies more heavily on having configured Observability at least on the [`executor.Executor`](./executor/executor.go#L84) level to underline those events (which get detached from -the [`Selector`](./selector/selector.go#L36) after timing out). +the [`Selector`](./selector/selector.go#L37) after timing out). It is important to have a good idea of how your cron jobs will execute and how often, or simply ensure that there is at least logging enabled for the configured [`executor.Executor`(s)](./executor/executor.go#L84). @@ -213,9 +213,9 @@ duration until the next job, and sleeps until that time is reached (instead of, To create an [`Executor`](./executor/executor.go#L84), you can use the [`New`](./executor/executor.go#L160) function that serves as a constructor. Note that the minimum requirements to creating an [`Executor`](./executor/executor.go#L84) are to include both a [`schedule.Scheduler`](./schedule/scheduler.go#L24) with the -[`WithScheduler`](./executor/executor_config.go#L60) option (or a cron string, using the -[`WithSchedule`](./executor/executor_config.go#L77) option), -and at least one [`Runner`](./executor/executor.go#L40) with the [`WithRunners`](./executor/executor_config.go#L28) +[`WithScheduler`](./executor/executor_config.go#L61) option (or a cron string, using the +[`WithSchedule`](./executor/executor_config.go#L78) option), +and at least one [`Runner`](./executor/executor.go#L40) with the [`WithRunners`](./executor/executor_config.go#L29) option. The [`Runner`](./executor/executor.go#L40) itself is an interface with a single method @@ -223,7 +223,7 @@ The [`Runner`](./executor/executor.go#L40) itself is an interface with a single so simple that you have it as a function and don't need to create a type for this [`Runner`](./executor/executor.go#L40), then you can use the [`Runnable` type](./executor/executor.go#L53) instead, which is a type alias to a function of the same signature, but implements [`Runner`](./executor/executor.go#L40) by -calling itself as a function, in its `Run` method. +calling itself as a function, in its [`Run`](./executor/executor.go#L61) method. Creating an [`Executor`](./executor/executor.go#L84) is as easy as calling [its constructor function](./executor/executor.go#L160): @@ -239,14 +239,14 @@ Below is a table with all the options available for creating a cron job executor | Function | Input Parameters | Description | |:------------------------------------------------------:|:---------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------:| -| [`WithRunners`](./executor/executor_config.go#L28) | [`runners ...Runner`](./executor/executor.go#L40) | Configures the [`Executor`](./executor/executor.go#L84) with the input [`Runner`(s)](./executor/executor.go#L40). | -| [`WithScheduler`](./executor/executor_config.go#L60) | [`sched schedule.Scheduler`](./schedule/scheduler.go#L24) | Configures the [`Executor`](./executor/executor.go#L84) with the input [`schedule.Scheduler`](./schedule/scheduler.go#L24). | -| [`WithSchedule`](./executor/executor_config.go#L77) | `cronString string` | Configures the [`Executor`](./executor/executor.go#L84) with a [`schedule.Scheduler`](./schedule/scheduler.go#L24) using the input cron string. | -| [`WithLocation`](./executor/executor_config.go#L95) | `loc *time.Location` | Configures the [`Executor`](./executor/executor.go#L84) with a [`schedule.Scheduler`](./schedule/scheduler.go#L24) using the input `time.Location`. | -| [`WithMetrics`](./executor/executor_config.go#L108) | [`m executor.Metrics`](./executor/executor_with_metrics.go#L11) | Decorates the [`Executor`](./executor/executor.go#L84) with the input metrics registry. | -| [`WithLogger`](./executor/executor_config.go#L121) | [`logger *slog.Logger`](https://pkg.go.dev/log/slog#Logger) | Decorates the [`Executor`](./executor/executor.go#L84) with the input logger. | -| [`WithLogHandler`](./executor/executor_config.go#L134) | [`handler slog.Handler`](https://pkg.go.dev/log/slog#Handler) | Decorates the [`Executor`](./executor/executor.go#L84) with logging using the input log handler. | -| [`WithTrace`](./executor/executor_config.go#L147) | [`tracer trace.Tracer`](https://pkg.go.dev/go.opentelemetry.io/otel/trace#Tracer) | Decorates the [`Executor`](./executor/executor.go#L84) with the input trace.Tracer. | +| [`WithRunners`](./executor/executor_config.go#L29) | [`runners ...Runner`](./executor/executor.go#L40) | Configures the [`Executor`](./executor/executor.go#L84) with the input [`Runner`(s)](./executor/executor.go#L40). | +| [`WithScheduler`](./executor/executor_config.go#L61) | [`sched schedule.Scheduler`](./schedule/scheduler.go#L24) | Configures the [`Executor`](./executor/executor.go#L84) with the input [`schedule.Scheduler`](./schedule/scheduler.go#L24). | +| [`WithSchedule`](./executor/executor_config.go#L78) | `cronString string` | Configures the [`Executor`](./executor/executor.go#L84) with a [`schedule.Scheduler`](./schedule/scheduler.go#L24) using the input cron string. | +| [`WithLocation`](./executor/executor_config.go#L96) | `loc *time.Location` | Configures the [`Executor`](./executor/executor.go#L84) with a [`schedule.Scheduler`](./schedule/scheduler.go#L24) using the input `time.Location`. | +| [`WithMetrics`](./executor/executor_config.go#L109) | [`m executor.Metrics`](./executor/executor_with_metrics.go#L11) | Decorates the [`Executor`](./executor/executor.go#L84) with the input metrics registry. | +| [`WithLogger`](./executor/executor_config.go#L122) | [`logger *slog.Logger`](https://pkg.go.dev/log/slog#Logger) | Decorates the [`Executor`](./executor/executor.go#L84) with the input logger. | +| [`WithLogHandler`](./executor/executor_config.go#L135) | [`handler slog.Handler`](https://pkg.go.dev/log/slog#Handler) | Decorates the [`Executor`](./executor/executor.go#L84) with logging using the input log handler. | +| [`WithTrace`](./executor/executor_config.go#L148) | [`tracer trace.Tracer`](https://pkg.go.dev/go.opentelemetry.io/otel/trace#Tracer) | Decorates the [`Executor`](./executor/executor.go#L84) with the input trace.Tracer. | _______ @@ -260,10 +260,10 @@ calculations. The default implementation of [`Scheduler`](./schedule/scheduler.go#L24), [`CronSchedule`](./schedule/scheduler.go#L32), will be created from parsing a cron string, and is nothing but a data structure with a -[`cronlex.Schedule`](./schedule/cronlex/process.go#L32) bounded to a `time.Location`. +[`cronlex.Schedule`](./schedule/cronlex/process.go#L33) bounded to a `time.Location`. While the [`CronSchedule`](./schedule/scheduler.go#L32) leverages different schedule elements with -[`cronlex.Resolver` interfaces](./schedule/cronlex/process.go#L25), the [`Scheduler`](./schedule/scheduler.go#L24) uses +[`cronlex.Resolver` interfaces](./schedule/cronlex/process.go#L26), the [`Scheduler`](./schedule/scheduler.go#L24) uses these values as a difference from the input timestamp, to create a new date with a [`time.Date()`](https://pkg.go.dev/time#Date) call. This call merely adds the difference until the next job to the current time, on different elements of the timestamp, with added logic to calculate weekdays if set. @@ -298,25 +298,25 @@ _______ ##### Cron Schedule -[`Schedule`](./schedule/cronlex/process.go#L32) is a data structure that holds a set of -[`Resolver`s](./schedule/cronlex/process.go#L25), for each node, segment or unit of the schedule. This implementation +[`Schedule`](./schedule/cronlex/process.go#L33) is a data structure that holds a set of +[`Resolver`s](./schedule/cronlex/process.go#L26), for each node, segment or unit of the schedule. This implementation focuses on the cron string specification with added support for a seconds definition (instead of the usual minutes, hours, days-of-the-month, months and weekdays). Each of these elements are -[`Resolver`s](./schedule/cronlex/process.go#L25) interfaces that will calculate the difference until the target value(s) -is reached. More information on [`Resolver`s](./schedule/cronlex/process.go#L25) in +[`Resolver`s](./schedule/cronlex/process.go#L26) interfaces that will calculate the difference until the target value(s) +is reached. More information on [`Resolver`s](./schedule/cronlex/process.go#L26) in [its own section](#schedule-resolver). -The [`Schedule`](./schedule/cronlex/process.go#L32) only holds the state of a parsed cron string, and its elements are +The [`Schedule`](./schedule/cronlex/process.go#L33) only holds the state of a parsed cron string, and its elements are made public so that implementations of [`Scheduler`](./schedule/scheduler.go#L24) can leverage it to calculate the job's next execution time. -To create a new [`Schedule`](./schedule/cronlex/process.go#L32) type, you're able to use the -[`Parse` function](./schedule/cronlex/process.go#L45), that consumes the input cron string, returning a -[`Schedule`](./schedule/cronlex/process.go#L32) and an error if raised. More details on the actual parsing of the string +To create a new [`Schedule`](./schedule/cronlex/process.go#L33) type, you're able to use the +[`Parse` function](./schedule/cronlex/process.go#L46), that consumes the input cron string, returning a +[`Schedule`](./schedule/cronlex/process.go#L33) and an error if raised. More details on the actual parsing of the string [in its own section](#schedule-parser). -Once created, the elements of [`Schedule`](./schedule/cronlex/process.go#L32) are accessed directly, where the caller -can use the [`Resolver`](./schedule/cronlex/process.go#L25) interface: +Once created, the elements of [`Schedule`](./schedule/cronlex/process.go#L33) are accessed directly, where the caller +can use the [`Resolver`](./schedule/cronlex/process.go#L26) interface: ```go type Schedule struct { @@ -333,27 +333,27 @@ _______ ##### Schedule Resolver This component calculates the difference between the input value and the (set of) value(s) it is configured to be -triggered on, also given a certain maximum value for that [`Resolver`'s](./schedule/cronlex/process.go#L25) range. +triggered on, also given a certain maximum value for that [`Resolver`'s](./schedule/cronlex/process.go#L26) range. This difference is useful on a [`Scheduler`](./schedule/scheduler.go#L24), where [`time.Date()`](https://pkg.go.dev/time#Date) sums the input time to the difference until the next execution. As such, -given each node in a [`Schedule`](./schedule/cronlex/process.go#L32), it is possible to derive the next time per node, +given each node in a [`Schedule`](./schedule/cronlex/process.go#L33), it is possible to derive the next time per node, following this logic. Take a minute as an example. It is an element that spans from 0 to 59; and consider all elements start at zero. A -minutes [`Resolver`](./schedule/cronlex/process.go#L25) is configured with a maximum value of 59, and this example is -configured to trigger at minute 15. If the input time is 2:03 PM, the [`Resolver`](./schedule/cronlex/process.go#L25) +minutes [`Resolver`](./schedule/cronlex/process.go#L26) is configured with a maximum value of 59, and this example is +configured to trigger at minute 15. If the input time is 2:03 PM, the [`Resolver`](./schedule/cronlex/process.go#L26) returns a difference of 12. When the [`Scheduler`](./schedule/scheduler.go#L24) gets this information, it adds up the 12 minutes to the input time, and returns the resulting datetime value. -This also makes the [`Resolver`](./schedule/cronlex/process.go#L25) very flexible, in tandem with the corresponding -[`Schedule`](./schedule/cronlex/process.go#L32) and [`Scheduler`](./schedule/scheduler.go#L24), providing customizable -levels of precision. The seconds [`Resolver`](./schedule/cronlex/process.go#L25) in -[`Schedule`](./schedule/cronlex/process.go#L32) is an example of this. +This also makes the [`Resolver`](./schedule/cronlex/process.go#L26) very flexible, in tandem with the corresponding +[`Schedule`](./schedule/cronlex/process.go#L33) and [`Scheduler`](./schedule/scheduler.go#L24), providing customizable +levels of precision. The seconds [`Resolver`](./schedule/cronlex/process.go#L26) in +[`Schedule`](./schedule/cronlex/process.go#L33) is an example of this. -The implementations of [`Resolver`](./schedule/cronlex/process.go#L25) can be found in the +The implementations of [`Resolver`](./schedule/cronlex/process.go#L26) can be found in the [`resolve` package](./schedule/resolve/resolve.go). These are derived from parsing a cron string and assigned -automatically when calling the [`Parse` function](./schedule/cronlex/process.go#L45). +automatically when calling the [`Parse` function](./schedule/cronlex/process.go#L46). To explore the different implementations, it's good to have in mind the modularity in cron schedules: - it supports "every value" sequences when using a star (`*`) @@ -363,9 +363,9 @@ To explore the different implementations, it's good to have in mind the modulari - it supports a combination of range values and steps (e.g. `0-15/3`) - it supports overrides, for certain configurations (e.g. `@weekly`) -Having this in mind, this could technically be achieved with a single [`Resolver`](./schedule/cronlex/process.go#L25) +Having this in mind, this could technically be achieved with a single [`Resolver`](./schedule/cronlex/process.go#L26) type (that you will find for step values), but to maximize performance and reduce complexity where it is not needed, -there are four types of [`Resolver`](./schedule/cronlex/process.go#L25): +there are four types of [`Resolver`](./schedule/cronlex/process.go#L26): ###### `Everytime` Resolver @@ -375,7 +375,7 @@ type Everytime struct{} ``` The [`Everytime Resolver`](./schedule/resolve/resolve.go#L4) always returns zero, meaning that it resolves to the current time -always (_trigger now_ type of action). This is the default [`Resolver`](./schedule/cronlex/process.go#L25) whenever a +always (_trigger now_ type of action). This is the default [`Resolver`](./schedule/cronlex/process.go#L26) whenever a star (`*`) node is found, for example. @@ -422,7 +422,7 @@ type StepSchedule struct { The [`StepSchedule Resolver`](./schedule/resolve/resolve.go#L42) is the most complex of all -- that could potentially serve all the other implementations, however it can be the most resource-expensive of all -[`Resolver`s](./schedule/cronlex/process.go#L25). +[`Resolver`s](./schedule/cronlex/process.go#L26). This implementation stores the defined values for the schedule and returns the difference of the closest value ahead of it. This involves scanning all the steps in the sequence as it requires looking into values that are less than the @@ -434,7 +434,7 @@ or contains a given frequency delimited by a slash (`/`), it surely will have an [`StepSchedule Resolver`](./schedule/resolve/resolve.go#L42) to resolve that / those node(s). The [`StepSchedule Resolver`](./schedule/resolve/resolve.go#L42) is the only -[`Resolver`s](./schedule/cronlex/process.go#L25) which exposes a constructor, with the +[`Resolver`s](./schedule/cronlex/process.go#L26) which exposes a constructor, with the [`NewStepSchedule` function](./schedule/resolve/resolve.go#L77), that takes _from_ and _to_ values, a maximum, and a certain frequency (which should be 1 if no custom frequency is desired). Any further additions to the `Steps` in the [`StepSchedule Resolver`](./schedule/resolve/resolve.go#L42), should be added to the data structure, @@ -445,7 +445,7 @@ func NewStepSchedule(from, to, maximum, frequency int) StepSchedule ``` In the [Schedule Parser section](#schedule-parser), we explore how its processor will create the -[`Schedule`](./schedule/cronlex/process.go#L32) types following some rules, when working with the abstract syntax tree +[`Schedule`](./schedule/cronlex/process.go#L33) types following some rules, when working with the abstract syntax tree from parsing the cron string. _______ @@ -456,8 +456,8 @@ nail down accurately as it is the main source of user input within this library' caller's responsibility as they pass into it whatever they want. But having a correct understanding of the input schedule as well as calculating the times for the jobs' execution is fundamentally most important. -As mentioned before, this package exposes a [`Parse` function](./schedule/cronlex/process.go#L45) that consumes a cron -string returning a [`Schedule`](./schedule/cronlex/process.go#L32) and an error: +As mentioned before, this package exposes a [`Parse` function](./schedule/cronlex/process.go#L46) that consumes a cron +string returning a [`Schedule`](./schedule/cronlex/process.go#L33) and an error: ```go func Parse(cronString string) (s Schedule, err error) @@ -470,7 +470,7 @@ The tokens also hold the value (bytes) that represent them. - A parser, which consumes the tokens as they are emitted from the lexer, and progressively builds an abstract syntax tree that is the cron schedule and its different nodes. - A processor, which consumes the finalized abstract syntax tree created by the parser, validates its contents and -creates the appropriate [`Schedule`](./schedule/cronlex/process.go#L32). +creates the appropriate [`Schedule`](./schedule/cronlex/process.go#L33). The implementations of the underlying lexer and parser logic are taken from Go's standard library, from its [`go/token`](https://pkg.go.dev/go/token) and [`text/template`](https://pkg.go.dev/text/template) packages. There is @@ -514,7 +514,7 @@ look like: ``` Having created the cron's abstract syntax tree we arrive to the last phase, the -[`ProcessFunc` function](./schedule/cronlex/process.go#L58). It starts off by validating the contents in the abstract +[`ProcessFunc` function](./schedule/cronlex/process.go#L59). It starts off by validating the contents in the abstract syntax tree to ensure there are no unsupported values like greater than the maximum, etc. Once ensured it is valid, the function checks how many nodes are children of the root node in the tree, with support for @@ -526,16 +526,16 @@ Once ensured it is valid, the function checks how many nodes are children of the Handling the exceptions is very simple as the function only switches on the supported values looking for a match. The switch statement is the fastest algorithm to perform this check. -A _classic_ cron string with 5 nodes will still have a seconds [`Resolver`](./schedule/cronlex/process.go#L25) in its -[`Schedule`](./schedule/cronlex/process.go#L32), by configuring it as a +A _classic_ cron string with 5 nodes will still have a seconds [`Resolver`](./schedule/cronlex/process.go#L26) in its +[`Schedule`](./schedule/cronlex/process.go#L33), by configuring it as a [`FixedSchedule` type](./schedule/resolve/resolve.go#L13), triggering at value 0 with a maximum of 59 (for seconds). Generally, the Resolver types for each node from both _classic_ and _extended_ cron strings are built by checking -if it's a star (`*`) or alphanumeric node, creating the appropriate [`Resolver`](./schedule/cronlex/process.go#L25). +if it's a star (`*`) or alphanumeric node, creating the appropriate [`Resolver`](./schedule/cronlex/process.go#L26). Note that step values are sorted and compacted before being returned, for optimal efficiency when being used. Lastly, considering the weekday support for 0 and 7 as Sundays, if the weekday -[`Resolver`](./schedule/cronlex/process.go#L25) is a +[`Resolver`](./schedule/cronlex/process.go#L26) is a [`StepSchedule` type](./schedule/resolve/resolve.go#L42), it is normalized as a 0 value. @@ -548,9 +548,9 @@ A working example is the [Steam CLI app](https://github.com/zalgonoise/x/tree/ma [`monitor`](https://github.com/zalgonoise/x/blob/master/steam/cmd/steam/monitor/monitor.go). This file provides some insight on how the cron service is set up from a `main.go` / script-like approach. -You can also take a look [at its -`runner.go` file](https://github.com/zalgonoise/x/blob/master/steam/cmd/steam/monitor/runner.go), that implements the -`executor.Runner` interface. +You can also take a look +[at its `runner.go` file](https://github.com/zalgonoise/x/blob/master/steam/cmd/steam/monitor/runner.go), that +implements the [`executor.Runner`](./executor/executor.go#L40) interface. _______