{"id":24288558,"url":"https://github.com/nielspilgaard/pilgaard.backgroundjobs","last_synced_at":"2025-04-05T06:07:45.926Z","repository":{"id":37569116,"uuid":"484149164","full_name":"NielsPilgaard/Pilgaard.BackgroundJobs","owner":"NielsPilgaard","description":"A dotnet library for running background jobs in a scalable and performant manner. The jobs can trigger based on cron expressions, intervals or absolute datetime.","archived":false,"fork":false,"pushed_at":"2025-04-02T16:50:36.000Z","size":482,"stargazers_count":54,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-05T06:07:37.844Z","etag":null,"topics":["cronjob","cronjob-schedule","csharp","dotnet","dotnet-core","dotnet-farmework","dotnet-standard","nuget","recurring","scheduled-tasks","scheduling"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NielsPilgaard.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"NielsPilgaard"}},"created_at":"2022-04-21T17:43:32.000Z","updated_at":"2025-04-02T16:47:42.000Z","dependencies_parsed_at":"2022-08-29T06:31:14.290Z","dependency_job_id":"5873cf8d-411c-4ac5-a0e9-bc3c65005df1","html_url":"https://github.com/NielsPilgaard/Pilgaard.BackgroundJobs","commit_stats":{"total_commits":226,"total_committers":3,"mean_commits":75.33333333333333,"dds":0.1504424778761062,"last_synced_commit":"c1c48da553a65fd56ed66c163c1dee056ed7f854"},"previous_names":["nillermeddild/pilgaard.cronjobs"],"tags_count":82,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NielsPilgaard%2FPilgaard.BackgroundJobs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NielsPilgaard%2FPilgaard.BackgroundJobs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NielsPilgaard%2FPilgaard.BackgroundJobs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NielsPilgaard%2FPilgaard.BackgroundJobs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NielsPilgaard","download_url":"https://codeload.github.com/NielsPilgaard/Pilgaard.BackgroundJobs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247294539,"owners_count":20915340,"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":["cronjob","cronjob-schedule","csharp","dotnet","dotnet-core","dotnet-farmework","dotnet-standard","nuget","recurring","scheduled-tasks","scheduling"],"created_at":"2025-01-16T10:16:47.418Z","updated_at":"2025-04-05T06:07:45.904Z","avatar_url":"https://github.com/NielsPilgaard.png","language":"C#","funding_links":["https://github.com/sponsors/NielsPilgaard"],"categories":[],"sub_categories":[],"readme":"![Pilgaard BackgroundJobs Banner](https://user-images.githubusercontent.com/21295394/212175105-80087d36-42e3-436e-afbe-28c56173be60.png)\r\n\u003cdiv style=\"text-align: center\"\u003e\r\n\r\n[![CI](https://github.com/NielsPilgaard/Pilgaard.BackgroundJobs/actions/workflows/backgroundjobs_ci.yml/badge.svg)](https://github.com/NielsPilgaard/Pilgaard.BackgroundJobs/actions/workflows/backgroundjobs_ci.yml)\r\n[![Downloads](https://img.shields.io/nuget/dt/pilgaard.backgroundjobs.svg)](https://www.nuget.org/packages/Pilgaard.BackgroundJobs)\r\n[![Version](https://img.shields.io/nuget/vpre/pilgaard.backgroundjobs.svg)](https://www.nuget.org/packages/Pilgaard.BackgroundJobs)\r\n\r\n\u003c/div\u003e\r\nA dotnet library for running background jobs in a scalable and performant manner.\r\n\r\n## Features\r\n- Implement background jobs through interfaces\r\n- Centralized host to manage and run jobs, keeping memory and thread usage low.\r\n- Dependency Injection support\r\n- Read and update job schedules at runtime through `IConfiguration` or `IOptionsMonitor`\r\n- Monitoring jobs using logs and metrics, both compatible with OpenTelemetry\r\n\r\n## Scheduling Methods\r\n- Cron expressions using `ICronJob`\r\n- Recurringly at a set interval using `IRecurringJob`\r\n- Recurringly at a set interval after an initial delay using `IRecurringJobWithInitialDelay`\r\n- Once at an absolute time using `IOneTimeJob`\r\n\r\n## Use Case examples\r\n- Sending emails \r\n- Processing data\r\n- Enforcing data retention\r\n\r\n\r\n# Getting Started\r\nMake BackgroundJobs by implementing one of these interfaces:\r\n\r\n```csharp\r\npublic class CronJob : ICronJob\r\n{\r\n    public Task RunJobAsync(CancellationToken cancellationToken = default)\r\n    {\r\n        Console.WriteLine(\"Time to backup your databases!\");\r\n\r\n        return Task.CompletedTask;\r\n    }\r\n    public CronExpression CronExpression =\u003e CronExpression.Parse(\"0 3 * * *\");\r\n}\r\n```\r\n```csharp\r\npublic class RecurringJob : IRecurringJob\r\n{\r\n    public Task RunJobAsync(CancellationToken cancellationToken = default)\r\n    {\r\n        Console.WriteLine(\"This is your hourly reminder to stay hydrated.\");\r\n\r\n        return Task.CompletedTask;\r\n    }\r\n    public TimeSpan Interval =\u003e TimeSpan.FromHours(1);\r\n}\r\n```\r\n```csharp\r\npublic class RecurringJobWithInitialDelay : IRecurringJobWithInitialDelay\r\n{\r\n    public Task RunJobAsync(CancellationToken cancellationToken = default)\r\n    {\r\n        Console.WriteLine(\"This is your hourly reminder to stay hydrated.\");\r\n\r\n        return Task.CompletedTask;\r\n    }\r\n    public TimeSpan Interval =\u003e TimeSpan.FromHours(1);\r\n    public TimeSpan InitialDelay =\u003e TimeSpan.Zero;\r\n}\r\n```\r\n```csharp\r\npublic class OneTimeJob : IOneTimeJob\r\n{\r\n    public Task RunJobAsync(CancellationToken cancellationToken = default)\r\n    {\r\n        Console.WriteLine(\"Happy New Year!\");\r\n\r\n        return Task.CompletedTask;\r\n    }\r\n    public DateTime ScheduledTimeUtc =\u003e new(year: 2023, month: 12, day: 31, hour: 23, minute: 59, second: 59);\r\n}\r\n```\r\n\r\n\r\n# Registration\r\n\r\nCall `AddBackgroundJobs()` on an `IServiceCollection`, and then add jobs:\r\n\r\n```csharp\r\nbuilder.Services.AddBackgroundJobs()\r\n    .AddJob\u003cCronJob\u003e()\r\n    .AddJob\u003cRecurringJob\u003e()\r\n    .AddJob\u003cRecurringJobWithInitialDelay\u003e()\r\n    .AddJob\u003cOneTimeJob\u003e();\r\n```\r\n\r\nYou can also register jobs in-line for simple use cases:\r\n\r\n```csharp\r\nbuilder.Services.AddBackgroundJobs()\r\n    .AddJob(\r\n        name: \"basic-cronjob\",\r\n        job: () =\u003e {},\r\n        cronExpression: CronExpression.Parse(\"* * * * *\"))\r\n    .AddJob(\r\n        name: \"basic-recurringjob\",\r\n        job: () =\u003e {},\r\n        interval: TimeSpan.FromSeconds(3))\r\n    .AddJob(\r\n        name: \"basic-recurringjob-withinitialdelay\",\r\n        job: () =\u003e {},\r\n        interval: TimeSpan.FromSeconds(3),\r\n        initialDelay: TimeSpan.Zero)\r\n    .AddJob(\r\n        name: \"basic-onetimejob\",\r\n        job: () =\u003e {},\r\n        scheduledTimeUtc: DateTime.UtcNow.AddHours(1))\r\n    .AddAsyncJob(\r\n        name: \"async-cronjob\",\r\n        job: cancellationToken =\u003e Task.CompletedTask,\r\n        cronExpression: CronExpression.Parse(\"* * * * *\"))\r\n    .AddAsyncJob(\r\n        name: \"async-recurringjob\",\r\n        job: cancellationToken =\u003e Task.CompletedTask,\r\n        interval: TimeSpan.FromSeconds(3))\r\n    .AddAsyncJob(\r\n        name: \"async-recurringjob-withinitialdelay\",\r\n        job: cancellationToken =\u003e Task.CompletedTask,\r\n        interval: TimeSpan.FromSeconds(3),\r\n        initialDelay: TimeSpan.Zero)\r\n    .AddAsyncJob(\r\n        name: \"async-onetimejob\",\r\n        job: cancellationToken =\u003e Task.CompletedTask,\r\n        scheduledTimeUtc: DateTime.UtcNow.AddHours(1));\r\n```\r\n\r\n\r\n# Samples\r\n\r\n| Sample 🔗 | Tags |\r\n| -- | -- |\r\n| [BackgroundJobs.Configuration](https://github.com/NielsPilgaard/Pilgaard.BackgroundJobs/tree/master/samples/BackgroundJobs.Configuration) | ASP.NET, Reloading, Configuration\r\n| [BackgroundJobs.MinimalAPI](https://github.com/NielsPilgaard/Pilgaard.BackgroundJobs/tree/master/samples/BackgroundJobs.MinimalAPI) | ASP.NET, MinimalAPI\r\n| [BackgroundJobs.OpenTelemetry](https://github.com/NielsPilgaard/Pilgaard.BackgroundJobs/tree/master/samples/BackgroundJobs.OpenTelemetry) | ASP.NET, Open Telemetry, Metrics, Logs\r\n| [BackgroundJobs.WorkerService](https://github.com/NielsPilgaard/Pilgaard.BackgroundJobs/tree/master/samples/BackgroundJobs.WorkerService) | Console, Worker Service\r\n\r\n---\r\n\r\n# Open Telemetry Compatibility\r\n\r\nEach project exposes histogram metrics, which allow monitoring the duration and count of jobs.\r\n\r\nThe meter names match the project names.\r\n\r\nThe [Open Telemetry Sample](https://github.com/NielsPilgaard/Pilgaard.BackgroundJobs/tree/master/samples/BackgroundJobs.OpenTelemetry) shows how to collect CronJob metrics using the Prometheus Open Telemetry exporter.\r\n\r\n---\r\n\r\n## Roadmap\r\n\r\n- ~~Replace Assembly Scanning with registration similar to that of HealthChecks~~\r\n- A separate UI project to help visualize when jobs trigger\r\n- Utilize dotnet 8's new TimeProvider instead of `DateTime.UtcNow`\r\n- More samples\r\n  - Using Blazor Server\r\n  - ~~Using a Worker Service~~\r\n  - Using IConfiguration to reload job schedule\r\n  - Using OneTimeJobs to control feature flags\r\n  - Using RecurringJobs to manage data retention\r\n\r\n---\r\n\r\n## Thanks to\r\n\r\n- The developers of [Cronos](https://github.com/HangfireIO/Cronos) for their excellent Cron expression library.\r\n- JetBrains for providing me with a free license to their products, through their [Open Source Support program](https://jb.gg/OpenSourceSupport).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnielspilgaard%2Fpilgaard.backgroundjobs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnielspilgaard%2Fpilgaard.backgroundjobs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnielspilgaard%2Fpilgaard.backgroundjobs/lists"}