{"id":16443465,"url":"https://github.com/sherifabdlnaby/sched","last_synced_at":"2025-03-21T05:30:31.476Z","repository":{"id":52686387,"uuid":"354200546","full_name":"sherifabdlnaby/sched","owner":"sherifabdlnaby","description":"In-process Go Job Scheduler. Supports Fixed, Timely, and Cron Expression Intervals. Instrument and Expose Scheduler's Job Metrics.","archived":false,"fork":false,"pushed_at":"2021-04-24T15:39:43.000Z","size":107,"stargazers_count":57,"open_issues_count":4,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-17T21:43:28.676Z","etag":null,"topics":["go","in-process","metrics","prometheus","scheduler"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sherifabdlnaby.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":null}},"created_at":"2021-04-03T04:40:07.000Z","updated_at":"2023-09-07T03:27:21.000Z","dependencies_parsed_at":"2022-08-22T05:21:57.267Z","dependency_job_id":null,"html_url":"https://github.com/sherifabdlnaby/sched","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sherifabdlnaby%2Fsched","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sherifabdlnaby%2Fsched/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sherifabdlnaby%2Fsched/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sherifabdlnaby%2Fsched/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sherifabdlnaby","download_url":"https://codeload.github.com/sherifabdlnaby/sched/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244745619,"owners_count":20503042,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","in-process","metrics","prometheus","scheduler"],"created_at":"2024-10-11T09:20:36.582Z","updated_at":"2025-03-21T05:30:30.925Z","avatar_url":"https://github.com/sherifabdlnaby.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003ch1 align=\"center\"\u003e 🕰 Sched\u003c/h1\u003e\n\u003c/p\u003e\n\u003ch3 align=\"center\"\u003eGo In-Process Scheduler with Cron Expression Support\u003c/h3\u003e\n\u003ch6 align=\"center\"\u003eRun Jobs on a schedule, supports fixed interval, timely, and cron-expression timers; Instrument your processes and expose metrics for each job. \u003c/h4\u003e\n\u003cp align=\"center\"\u003e\n   \u003ca href=\"http://godoc.org/github.com/sherifabdlnaby/sched\"\u003e\n      \u003cimg src=\"https://godoc.org/github.com/sherifabdlnaby/sched?status.svg\" alt=\"Go Doc\"\u003e\n   \u003c/a\u003e\n   \u003ca\u003e\n      \u003cimg src=\"https://img.shields.io/github/v/tag/sherifabdlnaby/sched?label=release\u0026amp;sort=semver\"\u003e\n    \u003c/a\u003e\n   \u003ca\u003e\n      \u003cimg src=\"https://img.shields.io/badge/Go-%3E=v1.13-blue?style=flat\u0026logo=go\" alt=\"Go Version\"\u003e\n   \u003c/a\u003e\n    \u003ca\u003e\n      \u003cimg src=\"https://github.com/sherifabdlnaby/sched/workflows/Build/badge.svg\"\u003e\n    \u003c/a\u003e\n   \u003ca href=\"https://goreportcard.com/report/github.com/sherifabdlnaby/sched\"\u003e\n      \u003cimg src=\"https://goreportcard.com/badge/github.com/sherifabdlnaby/sched\" alt=\"Go Report\"\u003e\n   \u003c/a\u003e\n   \u003ca href=\"https://raw.githubusercontent.com/sherifabdlnaby/sched/blob/master/LICENSE\"\u003e\n      \u003cimg src=\"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"GitHub license\"\u003e\n   \u003c/a\u003e\n\u003c/p\u003e\n\n# Introduction\n\nA simple process manager that allows you to specify a Schedule that execute a Job based on a Timer. Schedule manage the\nstate of this job allowing you to start/stop/restart in concurrent safe way. Schedule also instrument this Job and\ngather metrics and optionally expose them via [uber-go/tally](https://github.com/uber-go/tally#report-your-metrics)\nscope.\n\n# Install\n\n``` bash\ngo get github.com/sherifabdlnaby/sched\n```\n\n``` go\nimport \"github.com/sherifabdlnaby/sched\"\n```\n\n# Requirements\n\nGo 1.13 \u003e=\n\n-----\n\n# Concepts\n\n## Job\n\nSimply a `func(){}` implementation that is the schedule goal to run, and instrument.\n\n## Timer\n\nAn Object that Implements\nthe [type Timer interface{}](https://pkg.go.dev/github.com/sherifabdlnaby/sched?utm_source=godoc#Timer). A Timer is\nresponsible for providing a schedule with the **next time the job should run** and if there will be subsequent runs.\n\nPackaged Implementations:\n\n1. [Fixed](https://pkg.go.dev/github.com/sherifabdlnaby/sched?utm_source=godoc#Fixed) :- Infinitely Fires a job at a\n   Fixed Interval (`time.Duration`)\n2. [Cron](https://pkg.go.dev/github.com/sherifabdlnaby/sched?utm_source=godoc#Cron) :- Infinitely Fires a job based on\n   a [Cron Expression](https://en.wikipedia.org/wiki/Cron#CRON_expression), all Expressions supported\n   by [gorhill/cronexpr](https://github.com/gorhill/cronexpr) are supported.\n2. [Once](https://pkg.go.dev/github.com/sherifabdlnaby/sched?utm_source=godoc#Once) :- A Timer that run **ONCE** after\n   an optional specific delay or at a specified time, schedule will stop after it fires.\n\nYou can Implement your own Timer for your specific scheduling needs by implementing\n\n```go\ntype Timer interface {\n\t// done indicated that there will be no more runs.\n    Next() (next time.Time, done bool)\n}\n```\n\n## Schedule\n\nA Schedule wraps a Job and fires it according to Timer.\n\n```go\n\tfixedTimer30second, _ := sched.NewFixed(30 * time.Second)\n\n\tjob := func() {\n\t\tlog.Println(\"Doing some work...\")\n\t\ttime.Sleep(1 * time.Second)\n\t\tlog.Println(\"Finished Work.\")\n\t}\n\n\t// Create Schedule\n\tschedule := sched.NewSchedule(\"every30s\", fixedTimer30second, job)\n\n\t// Start\n\tschedule.Start()\n```\n\n### Options\n\nAdditional Options can be passed to Schedule to change its behavior.\n\n```go\n// Create Schedule\nschedule := sched.NewSchedule(\"every30s\", fixedTimer30second, job,\n\tsched.WithLogger(sched.DefaultLogger()),\n\topt2,\n\topt3,\n\t....,\n)\n```\n\n#### Logger Option\n\n`WithLogger( logger Logger)` -\u003e Supply the Schedule the Logger it is going to use for logging.\n1. [func DefaultLogger() Logger](https://pkg.go.dev/github.com/sherifabdlnaby/sched?utm_source=godoc#DefaultLogger) :\nProvide a Default Logging Interface to be used Instead of Implementing your own.\n1. [func NopLogger() Logger](https://pkg.go.dev/github.com/sherifabdlnaby/sched?utm_source=godoc#NopLogger) : A nop\nLogger that will not output anything to stdout.\n\n#### Metrics Option\n\n`WithMetrics( scope tally.Scope)` -\u003e Supply the Schedule with a metrics scope it can use to export metrics.\n\n1. Use any of `uber-go/tally` implementations (Prometheus, statsd, etc)\n1.\nUse [`func WithConsoleMetrics(printEvery time.Duration) Option`](https://pkg.go.dev/github.com/sherifabdlnaby/sched?utm_source=godoc#WithConsoleMetrics)\nImplementation to Output Metrics to stdout (good for debugging)\n\n#### Expected Runtime\n\n`WithExpectedRunTime(d time.Duration)` -\u003e Supply the Schedule with the expected duration for the job to run, schedule\nwill output corresponding logs and metrics if job run exceeded expected.\n\n## Schedule(r)\n\nScheduler manage one or more Schedule creating them using common options, enforcing unique IDs, and supply methods to\nStart / Stop all schedule(s).\n\n\n----\n\n# Exported Metrics\n\n| Metric                   | Type     | Desc                                                                                                                                                   |\n|--------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------|\n| up                       | Gauge    | If the schedule is Running / Stopped                                                                                                                   |\n| runs                     | Counter  | Number of Runs Since Starting                                                                                                                          |\n| runs_overlapping         |  Counter | Number of times more than one job was running together. (Overlapped)                                                                                   |\n| run_actual_elapsed_time  | Time     | Elapsed Time between Starting and Ending of Job Execution                                                                                              |\n| run_total_elapsed_time   | Time     | Total Elapsed Time between Creating the Job and Ending of Job Execution, This differ from Actual Elapsed time when Overlapping blocking is Implemented |\n| run_errors               | Counter  | Count Number of Times a Job error'd(Panicked) during execution.                                                                                        |\n| run_exceed_expected_time | Counter  | Count Number of Times a Job Execution Time exceeded the Expected Time                                                                                  |\n\n### In Prometheus Format\n\n```\n# HELP sched_run_actual_elapsed_time sched_run_actual_elapsed_time summary\n# TYPE sched_run_actual_elapsed_time summary\nsched_run_actual_elapsed_time{id=\"every5s\",quantile=\"0.5\"} 0.203843151\nsched_run_actual_elapsed_time{id=\"every5s\",quantile=\"0.75\"} 1.104031623\nsched_run_actual_elapsed_time{id=\"every5s\",quantile=\"0.95\"} 1.104031623\nsched_run_actual_elapsed_time{id=\"every5s\",quantile=\"0.99\"} 1.104031623\nsched_run_actual_elapsed_time{id=\"every5s\",quantile=\"0.999\"} 1.104031623\nsched_run_actual_elapsed_time_sum{id=\"every5s\"} 1.307874774\nsched_run_actual_elapsed_time_count{id=\"every5s\"} 2\n# HELP sched_run_errors sched_run_errors counter\n# TYPE sched_run_errors counter\nsched_run_errors{id=\"every5s\"} 0\n# HELP sched_run_exceed_expected_time sched_run_exceed_expected_time counter\n# TYPE sched_run_exceed_expected_time counter\nsched_run_exceed_expected_time{id=\"every5s\"} 0\n# HELP sched_run_total_elapsed_time sched_run_total_elapsed_time summary\n# TYPE sched_run_total_elapsed_time summary\nsched_run_total_elapsed_time{id=\"every5s\",quantile=\"0.5\"} 0.203880714\nsched_run_total_elapsed_time{id=\"every5s\",quantile=\"0.75\"} 1.104065614\nsched_run_total_elapsed_time{id=\"every5s\",quantile=\"0.95\"} 1.104065614\nsched_run_total_elapsed_time{id=\"every5s\",quantile=\"0.99\"} 1.104065614\nsched_run_total_elapsed_time{id=\"every5s\",quantile=\"0.999\"} 1.104065614\nsched_run_total_elapsed_time_sum{id=\"every5s\"} 1.307946328\nsched_run_total_elapsed_time_count{id=\"every5s\"} 2\n# HELP sched_runs sched_runs counter\n# TYPE sched_runs counter\nsched_runs{id=\"every5s\"} 2\n# HELP sched_runs_overlapping sched_runs_overlapping counter\n# TYPE sched_runs_overlapping counter\nsched_runs_overlapping{id=\"every5s\"} 0\n# HELP sched_up sched_up gauge\n# TYPE sched_up gauge\nsched_up{id=\"every5s\"} 1\n```\n\n# Examples\n\n1. [schedule-console-metrics](https://github.com/sherifabdlnaby/sched/tree/main/examples/schedule-console-metrics)\n1. [schedule-cron](https://github.com/sherifabdlnaby/sched/tree/main/examples/schedule-cron)\n1. [schedule-fixed](https://github.com/sherifabdlnaby/sched/tree/main/examples/schedule-fixed)\n1. [schedule-four-mixed-timers](https://github.com/sherifabdlnaby/sched/tree/main/examples/schedule-four-mixed-timers)\n1. [schedule-once](https://github.com/sherifabdlnaby/sched/tree/main/examples/schedule-console-metrics)\n1. [schedule-overlapping](https://github.com/sherifabdlnaby/sched/tree/main/examples/schedule-overlapping)\n1. [schedule-panic](https://github.com/sherifabdlnaby/sched/tree/main/examples/schedule-panic)\n1. [schedule-prom-metrics](https://github.com/sherifabdlnaby/sched/tree/main/examples/schedule-prom-metrics)\n1. [schedule-warn-expected](https://github.com/sherifabdlnaby/sched/tree/main/examples/schedule-warn-expected)\n1. [scheduler](https://github.com/sherifabdlnaby/sched/tree/main/examples/scheduler)\n1. [scheduler-extra-opts](https://github.com/sherifabdlnaby/sched/tree/main/examples/scheduler-extra-opts)\n\n## Inline Example\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/sherifabdlnaby/sched\"\n    \"log\"\n    \"os\"\n    \"os/signal\"\n    \"syscall\"\n    \"time\"\n)\n\nfunc main() {\n\n    cronTimer, err := sched.NewCron(\"* * * * *\")\n    if err != nil {\n        panic(fmt.Sprintf(\"invalid cron expression: %s\", err.Error()))\n    }\n\n    job := func() {\n        log.Println(\"Doing some work...\")\n        time.Sleep(1 * time.Second)\n        log.Println(\"Finished Work.\")\n    }\n\n    // Create Schedule\n    schedule := sched.NewSchedule(\"cron\", cronTimer, job, sched.WithLogger(sched.DefaultLogger()))\n\n    // Start Schedule\n    schedule.Start()\n\n    // Stop schedule after 5 Minutes\n    time.AfterFunc(5*time.Minute, func() {\n        schedule.Stop()\n    })\n\n    // Listen to CTRL + C\n    signalChan := make(chan os.Signal, 1)\n    signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)\n    _ = \u003c-signalChan\n\n    // Stop before shutting down.\n    schedule.Stop()\n\n    return\n}\n\n```\n\n### Output for 3 minutes\n\n```bash\n2021-04-10T12:30: 13.132+0200    INFO    sched   sched/schedule.go: 96    Job Schedule Started    {\"id\": \"cron\"}\n2021-04-10T12:30: 13.132+0200    INFO    sched   sched/schedule.go: 168   Job Next Run Scheduled  {\"id\": \"cron\", \"After\": \"47s\", \"At\": \"2021-04-10T12:31:00+02:00\"}\n2021-04-10T12: 31: 00.000+0200    INFO    sched   sched/schedule.go: 168   Job Next Run Scheduled  {\"id\": \"cron\", \"After\": \"1m0s\", \"At\": \"2021-04-10T12:32:00+02:00\"}\n2021-04-10T12: 31:00.000+0200    INFO    sched   sched/schedule.go: 193   Job Run Starting        {\"id\": \"cron\", \"Instance\": \"8e1044ab-20b6-4acf-8a15-e06c0418522c\"}\n2021/04/10 12: 31: 00 Doing some work...\n2021/04/10 12: 31: 01 Finished Work.\n2021-04-10T12: 31: 01.001+0200    INFO    sched   sched/schedule.go: 208   Job Finished    {\"id\": \"cron\", \"Instance\": \"8e1044ab-20b6-4acf-8a15-e06c0418522c\", \"Duration\": \"1.001s\", \"State\": \"FINISHED\"}\n2021-04-10T12:32: 00.002+0200    INFO    sched   sched/schedule.go: 168   Job Next Run Scheduled  {\"id\": \"cron\", \"After\": \"1m0s\", \"At\": \"2021-04-10T12:33:00+02:00\"}\n2021-04-10T12: 32: 00.002+0200    INFO    sched   sched/schedule.go: 193   Job Run Starting        {\"id\": \"cron\", \"Instance\": \"baae94eb-f818-4b34-a1f4-45b521a360a1\"}\n2021/04/10 12: 32: 00 Doing some work...\n2021/04/10 12: 32: 01 Finished Work.\n2021-04-10T12:32: 01.005+0200    INFO    sched   sched/schedule.go: 208   Job Finished    {\"id\": \"cron\", \"Instance\": \"baae94eb-f818-4b34-a1f4-45b521a360a1\", \"Duration\": \"1.003s\", \"State\": \"FINISHED\"}\n2021-04-10T12: 33: 00.001+0200    INFO    sched   sched/schedule.go:168   Job Next Run Scheduled  {\"id\": \"cron\", \"After\": \"1m0s\", \"At\": \"2021-04-10T12:34:00+02:00\"}\n2021-04-10T12:33: 00.001+0200    INFO    sched   sched/schedule.go: 193   Job Run Starting        {\"id\": \"cron\", \"Instance\": \"71c8f0bf-3624-4a92-909c-b4149f3c62a3\"}\n2021/04/10 12: 33: 00 Doing some work...\n2021/04/10 12: 33: 01 Finished Work.\n2021-04-10T12: 33: 01.004+0200    INFO    sched   sched/schedule.go:208   Job Finished    {\"id\": \"cron\", \"Instance\": \"71c8f0bf-3624-4a92-909c-b4149f3c62a3\", \"Duration\": \"1.003s\", \"State\": \"FINISHED\"}\n\n\n```\n\n### Output With CTRL+C\n\n```bash\n2021-04-10T12:28: 45.591+0200    INFO    sched   sched/schedule.go: 96    Job Schedule Started    {\"id\": \"cron\"}\n2021-04-10T12:28: 45.592+0200    INFO    sched   sched/schedule.go: 168   Job Next Run Scheduled  {\"id\": \"cron\", \"After\": \"14s\", \"At\": \"2021-04-10T12:29:00+02:00\"}\n2021-04-10T12: 29: 00.000+0200    INFO    sched   sched/schedule.go: 168   Job Next Run Scheduled  {\"id\": \"cron\", \"After\": \"1m0s\", \"At\": \"2021-04-10T12:30:00+02:00\"}\n2021-04-10T12: 29:00.000+0200    INFO    sched   sched/schedule.go: 193   Job Run Starting        {\"id\": \"cron\", \"Instance\": \"786540f1-594b-44a0-9a66-7181619e38a6\"}\n2021/04/10 12: 29: 00 Doing some work...\nCTRL+C\n2021-04-10T12: 29: 00.567+0200    INFO    sched   sched/schedule.go: 125   Stopping Schedule...    {\"id\": \"cron\"}\n2021-04-10T12: 29: 00.567+0200    INFO    sched   sched/schedule.go: 130   Waiting active jobs to finish...        {\"id\": \"cron\"}\n2021-04-10T12: 29: 00.567+0200    INFO    sched   sched/schedule.go: 171   Job Next Run Canceled   {\"id\": \"cron\", \"At\": \"2021-04-10T12:30:00+02:00\"}\n2021/04/10 12: 29: 01 Finished Work.\n2021-04-10T12: 29:01.000+0200    INFO    sched   sched/schedule.go: 208   Job Finished    {\"id\": \"cron\", \"Instance\": \"786540f1-594b-44a0-9a66-7181619e38a6\", \"Duration\": \"1s\", \"State\": \"FINISHED\"}\n2021-04-10T12: 29: 01.000+0200    INFO    sched   sched/schedule.go: 133   Job Schedule Stopped {\"id\": \"cron\" }\n```\n\n# Todo(s) and Enhancements\n\n- [ ] Control Logging Verbosity\n- [ ] Make Panic Recovery Optional\n- [ ] Make Job a func() error and allow retry(s), backoff, and collect errors and their metrics\n- [ ] Make Jobs context aware and support canceling Jobs Context.\n- [ ] Make allow Overlapping Optional and Configure How Overlapping is handled/denied.\n- [ ] Global Package-Level Metrics\n\n# License\n\n[MIT License](https://raw.githubusercontent.com/sherifabdlnaby/sched/master/LICENSE)\nCopyright (c) 2021 Sherif Abdel-Naby\n\n# Contribution\n\nPR(s) are Open and Welcomed. ❤️\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsherifabdlnaby%2Fsched","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsherifabdlnaby%2Fsched","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsherifabdlnaby%2Fsched/lists"}