|
1 | | -<!-- markdownlint-disable MD013 --> |
2 | | -# ecron [![GitHub Actions][action-img]][action] [![codeCov-img]][codeCov] [![hex-img]][hex] [![tag]][tag] [![License]][License] |
3 | | -[action]: https://github.com/zhongwencool/ecron |
4 | | -[action-img]: https://github.com/zhongwencool/ecron/actions/workflows/ci.yml/badge.svg |
5 | | -[codeCov]: https://codecov.io/gh/zhongwencool/ecron |
6 | | -[codeCov-img]: https://codecov.io/gh/zhongwencool/ecron/branch/master/graph/badge.svg?token=FI9WAQ6UG5 |
7 | | -[hex]: https://hex.pm/packages/ecron |
8 | | -[hex-img]: https://img.shields.io/hexpm/v/ecron.svg?style=flat |
9 | | -[tag]: https://img.shields.io/github/tag/zhongwencool/ecron.svg |
10 | | -[License]: https://img.shields.io/hexpm/l/ecron.svg |
| 1 | +# ecron [](https://github.com/zhongwencool/ecron) [](https://codecov.io/gh/zhongwencool/ecron) [](https://hex.pm/packages/ecron) [](https://img.shields.io/github/tag/zhongwencool/ecron.svg) [](https://img.shields.io/hexpm/l/ecron.svg) [](https://hexdocs.pm/ecron/) |
11 | 2 |
|
12 | 3 | A lightweight and efficient cron-like job scheduling library for Erlang. |
13 | 4 |
|
14 | | -## Overview |
| 5 | +# Overview |
15 | 6 | Ecron is designed to manage scheduled jobs within a single gen_server process, similar to the standard library's [stdlib's timer](http://erlang.org/doc/man/timer.html). It uses an ordered_set ETS table to organize jobs by the next run time, ensuring efficient execution. Unlike traditional cron, Ecoron does not poll the system every second, which reduces message overhead and process usage. |
16 | 7 |
|
17 | | -more detail see [Implementation](#Implementation). |
| 8 | +## Key Features |
18 | 9 |
|
19 | | - |
20 | | -### Key Features |
21 | 10 | - Supports both cron-like and interval-based scheduling. |
22 | 11 | - Well-tested with [PropTest](https://github.com/proper-testing/proper) [](https://codecov.io/gh/zhongwencool/ecron). |
23 | 12 | - Utilizes gen_server timeout mechanism for precise timing. |
24 | 13 | - Efficient process management, avoiding high memory usage. |
25 | 14 |
|
26 | | -You can find a collection of general practices in [Full Erlang Examples](https://github.com/zhongwencool/ecron/blob/master/examples/titan_erlang) and [Full Elixir Examples](https://github.com/zhongwencool/ecron/blob/master/examples/titan_elixir). |
27 | | - |
28 | 15 | ## Installation |
29 | | - |
30 | | -**Erlang** |
31 | | - ```erlang |
| 16 | + |
| 17 | +<!-- tabs-open --> |
| 18 | + |
| 19 | +### Erlang |
| 20 | + |
| 21 | +```erlang |
32 | 22 | %% rebar.config |
33 | 23 | {deps, [ecron]} |
34 | | - ``` |
| 24 | +``` |
| 25 | +### Elixir |
35 | 26 |
|
36 | | -**Elixir** |
37 | | - ```elixir |
| 27 | +```elixir |
38 | 28 | # mix.exs |
39 | 29 | def deps do |
40 | 30 | [{:ecron, "~> 1.0.1"}] |
41 | 31 | end |
42 | 32 | ``` |
43 | 33 |
|
| 34 | +<!-- tabs-close --> |
| 35 | + |
44 | 36 | ## Basic Usage |
45 | 37 |
|
46 | 38 | Configure Ecoron in your sys.config file with timezone and job specifications: |
@@ -146,6 +138,9 @@ ok = ecron:delete(crontabuniqueName), |
146 | 138 | EveryMFA = {io, format, ["Runs every 120 second.~n"]}, |
147 | 139 | {ok, _} = ecron:add(everyUniqueName, 120, EveryMFA), |
148 | 140 | ``` |
| 141 | +
|
| 142 | +You can find a collection of general practices in [Full Erlang Examples](https://github.com/zhongwencool/ecron/blob/master/examples/titan_erlang) and [Full Elixir Examples](https://github.com/zhongwencool/ecron/blob/master/examples/titan_elixir). |
| 143 | +
|
149 | 144 | ## Debug Support |
150 | 145 |
|
151 | 146 | Ecron provides functions to assist with debugging: |
@@ -225,97 +220,98 @@ Additionally, you can use `ecron:statistic(Name)` to see the job's latest 16 res |
225 | 220 | |<-------| |
226 | 221 | ``` |
227 | 222 |
|
228 | | -[Check this for global_jobs workflow](https://github.com/zhongwencool/ecron/blob/master/doc/global.md#Implementation). |
| 223 | +[Check this for global_jobs workflow](global.html#Implementation). |
229 | 224 |
|
230 | | -## Telemetry |
| 225 | +## CRON Expression Guide |
231 | 226 |
|
232 | | -Ecron publishes events through telemetry, allowing for monitoring and alerting, |
233 | | -You can handle those events by [this guide](https://github.com/zhongwencool/ecron/blob/master/doc/telemetry.md). |
| 227 | +### Basic Format |
234 | 228 |
|
235 | | -## CRON Expression Format |
| 229 | +A cron expression consists of 5-6 fields representing time units: |
236 | 230 |
|
237 | | -A [cron expression](https://www.wikiwand.com/en/Cron) represents a set of times, using 5-6 space-separated fields. |
238 | | -Currently, W (nearest weekday), L (last day of month/week), and # (nth weekday of the month) are not supported. |
239 | | -
|
240 | | -Most other features supported by popular cron implementations should work just fine. |
241 | | -```shell script |
242 | | - # ┌────────────── second (optional) |
243 | | - # │ ┌──────────── minute |
244 | | - # │ │ ┌────────── hour |
245 | | - # │ │ │ ┌──────── day of month |
246 | | - # │ │ │ │ ┌────── month |
247 | | - # │ │ │ │ │ ┌──── day of week |
248 | | - # │ │ │ │ │ │ |
249 | | - # │ │ │ │ │ │ |
250 | | - # 0 * * * * * |
| 231 | +``` |
| 232 | +# ┌────────────── second (optional) |
| 233 | +# │ ┌──────────── minute |
| 234 | +# │ │ ┌────────── hour |
| 235 | +# │ │ │ ┌──────── day of month |
| 236 | +# │ │ │ │ ┌────── month |
| 237 | +# │ │ │ │ │ ┌──── day of week |
| 238 | +# │ │ │ │ │ │ |
| 239 | +# │ │ │ │ │ │ |
| 240 | +# 0 * * * * * |
251 | 241 | ``` |
252 | 242 |
|
253 | | -Field name | Mandatory? | Allowed values | Allowed special characters |
254 | | ----------- | ---------- | -------------- | -------------------------- |
255 | | -Seconds | No | 0-59 | * / , - |
256 | | -Minutes | Yes | 0-59 | * / , - |
257 | | -Hours | Yes | 0-23 | * / , - |
258 | | -Day of month | Yes | 1-31 | * / , - |
259 | | -Month | Yes | 1-12 or JAN-DEC | * / , - |
260 | | -Day of week | Yes | 0-6 or SUN-SAT | * / , - |
| 243 | +### Field Values |
261 | 244 |
|
262 | | -Note: Month and Day-of-week field values are case-insensitive. "SUN", "Sun", and "sun" are equally accepted. |
| 245 | +Field | Required | Values | Special Characters |
| 246 | +Second | No | 0-59 | `* / , -` |
| 247 | +Minute | Yes | 0-59 | `* / , -` |
| 248 | +Hour | Yes | 0-23 | `* / , -` |
| 249 | +Day of Month | Yes | 1-31 | `* / , -` |
| 250 | +Month | Yes | 1-12 or JAN-DEC | `* / , -` |
| 251 | +Day of Week | Yes | 0-6 or SUN-SAT | `* / , -` |
263 | 252 |
|
264 | | -When specifying your cron values you'll need to make sure that your values fall within the ranges. |
265 | | -For instance, some cron's use a 0-7 range for the day of week where both 0 and 7 represent Sunday. We do not. |
| 253 | +> **Note**: Month and Day-of-week values are case-insensitive. |
266 | 254 |
|
267 | 255 | ### Special Characters |
268 | | -#### Asterisk ( * ) |
269 | | -The asterisk indicates that the cron expression will match for all values of the field. |
270 | | -For example, using an asterisk in the `month` field would indicate every month. |
271 | 256 |
|
272 | | -#### Slash ( / ) |
273 | | -Slashes are used to describe increments of ranges. |
274 | | -For example, "3-59/15" in the `minutes` field would indicate the 3rd minute of the hour and every 15 minutes thereafter. |
275 | | -The form "*/..." is equivalent to the form "First-Last/...", that is, an increment over the largest possible range of the field. |
276 | | -The form "N/..." is accepted as meaning "N-Max/...", that is, starting at N, use the increment until the end of that specific range. |
277 | | -It does not wrap around. |
| 257 | +- `*` - Any value |
| 258 | +- `/` - Step values (e.g., `*/15` - every 15 units) |
| 259 | +- `,` - Value list (e.g., `1,3,5`) |
| 260 | +- `-` - Range (e.g., `1-5`) |
278 | 261 |
|
279 | | -#### Comma ( , ) |
280 | | -Commas are used to separate items of a list. |
281 | | -For example, using "MON,WED,FRI" in the `day_of_week` field would mean Mondays, Wednesdays and Fridays. |
| 262 | +### Predefined Schedules |
282 | 263 |
|
283 | | -#### Hyphen ( - ) |
284 | | -Hyphens are used to define ranges. |
285 | | -For example, using "9-17" in the `hours`field would indicate every hour between 9am and 5pm inclusive. |
| 264 | +Expression | Description | Equivalent |
| 265 | +`@yearly` | Once a year (midnight, Jan 1) | `0 0 0 1 1 *` |
| 266 | +`@monthly` | Once a month (midnight, first day) | `0 0 0 1 * *` |
| 267 | +`@weekly` | Once a week (midnight, Sunday) | `0 0 0 * * 0` |
| 268 | +`@daily` | Once a day (midnight) | `0 0 0 * * *` |
| 269 | +`@hourly` | Once an hour | `0 0 * * * *` |
| 270 | +`@minutely` | Once a minute | `0 * * * * *` |
286 | 271 |
|
287 | | -## Predefined crontab |
| 272 | +### Fixed Intervals |
288 | 273 |
|
289 | | -You may use one of several pre-defined crontab in place of a cron expression. |
| 274 | +For simpler scheduling needs, use `@every` with a duration: |
290 | 275 |
|
291 | | -Entry | Description | Equivalent To |
292 | | ------ | ----------- | ------------- |
293 | | -@yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 * |
294 | | -@monthly | Run once a month, midnight, first of month | 0 0 0 1 * * |
295 | | -@weekly | Run once a week, midnight between Sat/Sun | 0 0 0 * * 0 |
296 | | -@daily (or @midnight) | Run once a day, midnight | 0 0 0 * * * |
297 | | -@hourly | Run once an hour, beginning of hour | 0 0 * * * * |
298 | | -@minutely | Run once an minute, beginning of minute | 0 * * * * * |
| 276 | +```erlang |
| 277 | +% Run every 30 minutes |
| 278 | +"@every 30m" |
299 | 279 |
|
300 | | ->There are tools that help when constructing your cronjobs. |
301 | | ->You might find something like [https://crontab.guru/](https://crontab.guru/) or [https://cronjob.xyz/](https://cronjob.xyz/) helpful. |
302 | | ->But, note that these don't necessarily accept the exact same syntax as this library, |
303 | | ->for instance, it doesn't accept the seconds field, so keep that in mind. |
304 | | ->The best way to verify the spec format is `ecron:parse_spec("0 0 1 1 1-6 1", 10).`. |
| 280 | +% Run every 1 hour and 30 minutes |
| 281 | +"@every 1h30m" |
| 282 | +``` |
305 | 283 |
|
306 | | -## Intervals |
| 284 | +Duration units: `d`(days), `h`(hours), `m`(minutes), `s`(seconds) |
307 | 285 |
|
308 | | -Ecron also supports scheduling jobs at fixed intervals: |
| 286 | +### Error Handling |
309 | 287 |
|
310 | | -```shell |
311 | | -@every <duration> |
312 | | -``` |
313 | | -For example, @every 1h30m10s schedules a job to run every 1 hour, 30 minutes, and 10 seconds. |
| 288 | +- Failed jobs don't affect other jobs |
| 289 | +- Execution results and timing are stored (last 16 runs) |
| 290 | +- Jobs can be configured as singleton or parallel |
| 291 | +- System time changes are handled gracefully |
| 292 | +
|
| 293 | +## Best Practices |
| 294 | +
|
| 295 | +1. **Job Names** |
| 296 | + - Use descriptive, unique names |
| 297 | + - Consider adding prefixes for different job types |
| 298 | +
|
| 299 | +2. **Time Windows** |
| 300 | + - Use start/end times for temporary jobs |
| 301 | + - Consider time zone implications |
| 302 | +
|
| 303 | +3. **Error Handling** |
| 304 | + - Implement proper error handling in job functions |
| 305 | + - Monitor job execution through telemetry |
| 306 | +
|
| 307 | +4. **Resource Management** |
| 308 | + - Group related jobs under same supervisor |
| 309 | + - Use `singleton: false` only when needed |
| 310 | +
|
| 311 | +5. **Testing** |
| 312 | + - Validate cron expressions before deployment |
| 313 | + - Test jobs with different time scenarios |
314 | 314 |
|
315 | | ->Note: The interval doesn't take the job runtime into account. |
316 | | ->For example, if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes, |
317 | | ->it only has 2 minutes of idle time between each run. |
318 | | - |
319 | 315 | ## Test |
320 | 316 |
|
321 | 317 | This command will run property-based tests, common tests, and generate a coverage report with verbose output. |
|
0 commit comments