Skip to content

OpenCron Library

Francois Normandin edited this page Mar 6, 2021 · 2 revisions

Presentation

The OpenCron library from LabVIEW Open Source project is an extensible library that proposes to handle custom-defined cron expressions by introducing an intermediary representation that allows to map any expression syntax to a unique engine.

Instead of defining a LabVIEW-centric cron syntax based on one of the "standards" widely used in the world, this library proposes three cron syntaxes that are widely accepted and maps them all to the same intermediary representation. All subsequent computations are therefore completely decoupled from the syntax used. This schema allows the LabVIEW developers to invent their own syntax or to choose from an existing one. It will also make it easy to map any other existing syntax without recreating a specific engine. Engine optimization can then be completely agnostic to the source syntax, making it possible to improve with less risk on impacting existing deployments.

Why and intermediary representation?

All cron syntaxes out there have the same principle of operation, but differ slightly in their representations. For example, some will map with a 1-second resolution, others with a 1-minute resolution. Some make use of hash variables to spread the processing load happening at the beginning of some intervals (for example, many jobs could be triggered once an hour... producing a large spike in resource utilization if they are all synchronized to the :00 minute) Furthermore, some syntaxes differ only in the use of 0-based or 1-based days of the week. It is rather poor strategy to have an engine for each when they have so much in common.

Cron syntaxes supported

There are currently three syntaxes supported:

How does it work?

Each cron syntax class simply has to overwrite two methods:

  • onValidateExpression.vi
  • onComputeIntervals.vi

The first one is to make sure the expression matches the syntax. The second one transforms the expression into the intermediary representation.

Expression validation

Regular expression are a convenient way to parse and validate an input expression string. Any other means is acceptable, of course. The expression validation happens only once before the expression is stored in the object. It is not called every time a computation is requested, so it is not extremely important to make this method super fast and optimized.

Intermediary representation

The Job base class will call the "onComputeIntervals.vi" once for every interval supported (seconds, minutes, hours, day of month, month, day of week and years). For each of these seven intervals, the override will need to interpret the input string (based on its own understanding of the syntactic expression), extract the region of interest corresponding to the current interval (ex: hours might be the 3rd item of the list) and finally output to the intermediary representation in the form of a cluster containing three informations:

  1. A boolean flag that informs if all values are included (ex: "*" means all values are valid)
  2. A boolean flag that informs if any value is included (ex: "?" means any of the values in the interval are susceptible to be valid)
  3. Array of discrete values allowed in the interval (or empty array of either of the above-mentioned two flags are set to True)

Examples

Considering a Quartz-compatible expression: " 0 0/5 14 * * ? 2021-2024"

This expression would read as follows: Every five minutes between 14:00 and 14:55, of every 14th of every month in the years 2021 through 2024.

And its intermediary representation would be:

  • seconds: {false, false, [0]}
  • minutes: {false, false, [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]}
  • hours: {false, false, [14]}
  • day of month: {true, false, []}
  • month: {true, false, []}
  • day of week: {false, true, []}
  • year: {false, false, [2021, 2022. 2023, 2024]}

Considering a Jenkins-compatible expression: " H/15 8 * * FRI *"

This expression would read as follows: Every fifteen minutes between 08:00 and 08:59 of every Friday. Use a hash (based on job name) to start the first job between 0 and 14 minutes after 8:00 to distribute load.

And its intermediary representation would be:

  • seconds: {false, false, [0]} // Note that the seconds are not specific in Jenkins expressions, so we set them to 0 by default.
  • minutes: {false, false, [4, 19, 34, 49]} // provided that the Hash of "JobName" would be determined to be 4 (in the range of 0 to 14).
  • hours: {false, false, [8]}
  • day of month: {true, false, []}
  • month: {true, false, []}
  • day of week: {false, false, [6]}
  • year: {true, false, []}