{"id":37228041,"url":"https://github.com/fornit1917/jobby","last_synced_at":"2026-04-11T14:16:24.048Z","repository":{"id":275223867,"uuid":"925332614","full_name":"fornit1917/jobby","owner":"fornit1917","description":"Highly efficient and reliable .net library for background jobs processing adapted for distributed services","archived":false,"fork":false,"pushed_at":"2026-04-07T20:26:35.000Z","size":2470,"stargazers_count":43,"open_issues_count":1,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-04-07T22:26:09.515Z","etag":null,"topics":["backend","background-jobs","cron-jobs","dotnet","scheduled-jobs"],"latest_commit_sha":null,"homepage":"","language":"C#","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/fornit1917.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-01-31T17:16:31.000Z","updated_at":"2026-02-20T09:41:09.000Z","dependencies_parsed_at":"2026-03-04T18:00:50.331Z","dependency_job_id":null,"html_url":"https://github.com/fornit1917/jobby","commit_stats":null,"previous_names":["fornit1917/jobby"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/fornit1917/jobby","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fornit1917%2Fjobby","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fornit1917%2Fjobby/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fornit1917%2Fjobby/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fornit1917%2Fjobby/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fornit1917","download_url":"https://codeload.github.com/fornit1917/jobby/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fornit1917%2Fjobby/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31683500,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-11T13:07:20.380Z","status":"ssl_error","status_checked_at":"2026-04-11T13:06:47.903Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["backend","background-jobs","cron-jobs","dotnet","scheduled-jobs"],"created_at":"2026-01-15T03:25:37.023Z","updated_at":"2026-04-11T14:16:24.041Z","avatar_url":"https://github.com/fornit1917.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Jobby  \n\n\u003cimg width=\"256\" src=\"./images/jobby_logo_256.png\" alt=\"Jobby Logo\"\u003e\n\nHigh-performance and reliable .NET library for background tasks, designed for distributed applications.\n\n- [Documentation](https://fornit1917.github.io/jobby/)\n- [Versions history](https://github.com/fornit1917/jobby/tree/master/versions.md)\n- [Samples](https://github.com/fornit1917/jobby/tree/master/samples)\n\n\n## Key Features  \n\n- Scheduled tasks with cron, intervals and any custom schedulers\n- Queue-based task execution  \n- Transactional creation of multiple tasks  \n- Configurable execution order for multiple tasks\n- Groups of tasks with sequential execution\n- Retry policies for failed tasks\n- Multi-Queues\n- Configurable middlewares pipeline for executing background tasks code\n- OpenTelemetry-compatible metrics and tracing\n- Proper operation in distributed applications\n- Fault tolerance and component failure resilience  \n- High performance  \n- Low resource consumption on both .NET application and database sides  \n\n## Usage Guide  \n\n### Installation  \n\nTo use Jobby, install the Jobby.Core package and a storage package (currently only PostgreSQL is supported):  \n\n```\ndotnet package add Jobby.Core  \ndotnet package add Jobby.Postgres  \n```\n\nFor ASP.NET Core integration, also install the Jobby.AspNetCore package:  \n\n```\ndotnet package add Jobby.AspNetCore  \n```\n\n### Defining Background Tasks  \n\nTo define a background task, implement the `IJobCommand` interface for the task parameters and `IJobCommandHandler` for the task logic:  \n\n```csharp\npublic class SendEmailCommand : IJobCommand  \n{  \n    // Properties can contain any parameters to be passed to the task  \n    public string Email { get; init; }  \n\n    // Return a unique name identifying the task  \n    public static string GetJobName() =\u003e \"SendEmail\";\n}  \n\npublic class SendEmailCommandHandler : IJobCommandHandler\u003cSendEmailCommand\u003e  \n{  \n    // Dependency injection is supported when using Jobby.AspNetCore  \n    private readonly IEmailService _emailService;  \n    public SendEmailCommandHandler(IEmailService logger)  \n    {  \n        _logger = logger;  \n    }  \n\n    public async Task ExecuteAsync(SendEmailCommand command, JobExecutionContext ctx)  \n    {  \n        // Implement your task logic here  \n        // command - task parameters  \n        // ctx - contains cancellationToken and additional task execution info  \n    }  \n}  \n```\n\n### Library Configuration  \n\n#### ASP.NET Core Configuration  \n\nTo add Jobby to an ASP.NET Core application, use the `AddJobbyServerAndClient` extension method:  \n\n```csharp\nbuilder.Services.AddSingleton\u003cNpgsqlDataSource\u003e(NpgsqlDataSource.Create(databaseConnectionString));\n\nbuilder.Services.AddJobbyServerAndClient(jobbyBuilder =\u003e  \n{\n    // Specify assemblies containing your IJobCommand and IJobCommandHandler implementations  \n    jobbyBuilder.AddJobsFromAssemblies(typeof(SendEmailCommand).Assembly);\n\n    // Configure jobby\n    jobbyBuilder.ConfigureJobby((serviceProvider, jobby) =\u003e {\n        jobby.UsePostgresql(serviceProvider.GetRequiredService\u003cNpgsqlDataSource\u003e());  \n        jobby.UseServerSettings(new JobbyServerSettings  \n        {  \n            // Maximum number of concurrently executing tasks  \n            MaxDegreeOfParallelism = 10,  \n\n            // Maximum number of tasks fetched from queue per query  \n            TakeToProcessingBatchSize = 10,  \n        });\n        jobby.UseDefaultRetryPolicy(new RetryPolicy  \n        {  \n            // Maximum number of task execution attempts  \n            MaxCount = 3,  \n\n            // Delays between retry attempts (in seconds)  \n            IntervalsSeconds = [1, 2]\n        });\n    }); \n});  \n```\n\nFull ASP.NET Core example: [Jobby.Samples.AspNet](https://github.com/fornit1917/jobby/tree/master/samples/Jobby.Samples.AspNet).  \n\n#### Non-ASP.NET Core Configuration  \n\nFor non-ASP.NET Core usage, create a `JobbyBuilder` instance:  \n\n```csharp\nvar jobbyBuilder = new JobbyBuilder();  \njobbyBuilder  \n        .UsePostgresql(dataSource)  \n        // scopeFactory - your custom scope factory implementation  \n        .UseExecutionScopeFactory(scopeFactory)  \n        .AddJobsFromAssemblies(typeof(SendEmailCommand).Assembly);  \n\n// Service for creating tasks  \nvar jobbyClient = builder.CreateJobbyClient();  \n\n// Background task execution service  \nvar jobbyServer = builder.CreateJobbyServer();  \njobbyServer.StartBackgroundService(); // Start background service  \n//...  \njobbyServer.SendStopSignal(); // Stop service  \n```\n\nFull console application example: [Jobby.Samples.CliJobsSample](https://github.com/fornit1917/jobby/tree/master/samples/Jobby.Samples.CliJobsSample).\n\n#### Creating Database Tables\n\nThe library provides the `IJobbyStorageMigrator` service to create the required tables in the database and automatically update their structure when transitioning to a new version.\n\nWhen using Jobby.AspNetCore, the service is available through the DI container and can be called at service startup:\n\n```csharp\n//...\n\napp.MapControllers();\n\n// Create or update jobby storage schema\nvar jobbyStorageMigrator = app.Services.GetRequiredService\u003cIJobbyStorageMigrator\u003e();\njobbyStorageMigrator.Migrate();\n```\n\nWithout using Jobby.AspNetCore, the `IJobbyStorageMigrator` service can be obtained from the `JobbyBuilder.GetStorageMigrator` method.\n\nFor full examples, refer to the links: [Jobby.Samples.AspNet](https://github.com/fornit1917/jobby/tree/master/samples/Jobby.Samples.AspNet) and [Jobby.Samples.CliJobsSample](https://github.com/fornit1917/jobby/tree/master/samples/Jobby.Samples.CliJobsSample).\n\n### Enqueueing Tasks  \n\nUse the `IJobbyClient` service to enqueue tasks (available via DI in ASP.NET Core or from `JobbyBuilder` otherwise).  \n\n#### Single Task  \n\n```csharp\nvar command = new SendEmailCommand { Email = \"some@email.com\" };  \n\n// Enqueue task for execution as soon as possible  \nawait jobbyClient.EnqueueCommandAsync(command);   \n\n// Enqueue task for execution no earlier than specified time  \nawait jobbyClient.EnqueueCommandAsync(command, DateTime.UtcNow.AddHours(1));  \n```\n\n#### Multiple Tasks  \n\nFor transactional creation of multiple tasks:  \n\n```csharp\nvar jobs = new List\u003cJobCreationModel\u003e  \n{  \n    jobbyClient.Factory\n        .Create(new SendEmailCommand { Email = \"first@email.com\" }),  \n    \n    jobbyClient.Factory\n        .Create(new SendEmailCommand { Email = \"second@email.com\" }),  \n};  \n\nawait jobbyClient.EnqueueBatchAsync(jobs);  \n```\n\nTo enforce strict execution order:  \n\n```csharp\nvar sequenceBuilder = jobbyClient.Factory.CreateSequenceBuilder();  \n\n// Tasks will execute in strict order  \nsequenceBuilder.Add(jobbyClient.Factory\n    .Create(new SendEmailCommand { Email = \"first@email.com\" }));  \n\nsequenceBuilder.Add(jobbyClient.Factory\n    .Create(new SendEmailCommand { Email = \"second@email.com\" }));  \n\nvar jobs = sequenceBuilder.GetJobs();  \n\nawait jobbyClient.EnqueueBatchAsync(jobs);  \n```\n\n#### Groups of tasks with sequential execution\n\nWhen creating a task, you can specify a group ID, and Jobby will ensure that no more than one task within each group is executed at any given time.\n\n```csharp\nawait jobbyClient.EnqueueCommandAsync(command, new JobOpts \n{\n    SerializableGroupId = \"SomeGroupId\"\n});\n```\n\nThe next task in the group will only start after the current task completes, whether successfully or unsuccessfully. If, in the event of an unsuccessful completion, you need to block the execution of any tasks from the same group, you must set the `LockGroupIfFailed` flag when creating the task:\n\n```csharp\nawait jobbyClient.EnqueueCommandAsync(command, new JobOpts \n{\n    SerializableGroupId = \"SomeGroupId\",\n    LockGroupIfFailed = true\n});\n```\n\n#### Using EntityFramework  \n\nFor EF Core integration:  \n\n```csharp\npublic class YourDbContext : DbContext  \n{  \n    // Add DbSet for JobCreationModel  \n    public DbSet\u003cJobCreationModel\u003e Jobs { get; set; }  \n\n    protected override void OnModelCreating(ModelBuilder modelBuilder)  \n    {  \n        modelBuilder.Entity\u003cJobCreationModel\u003e().ToTable(\"jobby_jobs\");  \n        modelBuilder.Entity\u003cJobCreationModel\u003e().HasKey(x =\u003e x.Id);  \n        // Apply snake_case naming convention for other configurations  \n    }  \n}  \n\n// Enqueue via EF  \nvar command = new SendEmailCommand { Email = \"some@email.com\" };  \nvar jobEntity = jobbyClient.Factory.Create(command);  \n_dbContext.Jobs.Add(job);  \nawait _dbContext.SaveChangesAsync();  \n```\n\nEF Core example: [Jobby.Samples.AspNet](https://github.com/fornit1917/jobby/tree/master/samples/Jobby.Samples.AspNet).  \n\n### Scheduled Tasks  \n\n```csharp\n// Scheduled tasks are defined similarly to regular tasks  \n\npublic class RecurrentJobCommand : IJobCommand  \n{  \n    public static string GetJobName() =\u003e \"SomeRecurrentJob\";  \n}  \n\npublic class RecurrentJobHandler : IJobCommandHandler\u003cRecurrentJobCommand\u003e  \n{  \n    public async Task ExecuteAsync(SendEmailCommand command, JobExecutionContext ctx)  \n    {  \n        // Your scheduled task logic  \n    }  \n}  \n\n// Schedule task using cron expression  \n// Will execute every 5 minutes  \nvar command = new RecurrentJobCommand();  \nawait jobbyClient.ScheduleRecurrentAsync(command, \"*/5 * * * *\");  \n```\n\n### Retry Policy Configuration  \n\nFailed tasks can be retried according to configured policies.  \n\nA `RetryPolicy` defines:  \n- Maximum total execution attempts  \n- Delays between retries (in seconds)  \n\n```csharp\nvar retryPolicy = new RetryPolicy  \n{  \n    // Maximum total execution attempts  \n    // Value of 3 means 1 initial attempt + 2 retries  \n    MaxCount = 3,  \n\n    // Delays between retry attempts  \n    // First retry after 1 second, second after 2 seconds  \n    IntervalsSeconds = [1, 2]  \n};  \n\n// IntervalSeconds doesn't require all values  \n// Example for 10 retries every 10 minutes:  \nretryPolicy = new RetryPolicy  \n{  \n    MaxCount = 11,  \n    IntervalsSeconds = [600]  \n};  \n```\n\nPolicies can be global or task-specific:  \n\n```csharp\njobbyBuilder  \n    // Default policy for all tasks  \n    .UseDefaultRetryPolicy(defaultPolicy)  \n    // Custom policy for SendEmailCommand  \n    .UseRetryPolicyForJob\u003cSendEmailCommand\u003e(specialRetryPolicy);  \n```\n\n### Multi-Queues  \n\nJobby allows you to distribute tasks across independent queues. For example, it can be useful to allocate tasks that need to be executed on time (such as recurrent jobs) to a separate queue, or tasks that are very heavy and require reduced parallelism.\n\nBy default, all tasks go to the `default` queue.\n\nThe queue can be specified when creating a task:\n\n```csharp\njobbyClient.Enqueue(command, new JobOpts { QueueName = \"special_queue\"});\n```\n\nOr defined in the command class by implementing the `IHasDefaultJobOptions` interface:\n\n```csharp\nclass SomeCommand : IJobCommand, IHasDefaultJobOptions\n{\n    public string GetJobName() =\u003e \"JobName\";\n\n    // Queue for non-recurrent jobs\n    public JobOpts GetOptionsForEnqueuedJob() =\u003e new() { QueueName = \"special_queue\" };\n    \n    // Queue for recurrent jobs\n    public RecurrentJobOptions GetOptionsForRecurrentJob() =\u003e new() { QueueName = \"special_queue\" };\n}\n```\n\nAdditionally, when configuring the library, you can specify a default queue for all recurrent jobs:\n\n```csharp\nbuilder.Services.AddJobbyServerAndClient((IAspNetCoreJobbyConfigurable jobbyBuilder) =\u003e\n{\n    jobbyBuilder\n        .AddJobsFromAssemblies(typeof(DemoJobCommand).Assembly)\n        // separate queue for all recurrent tasks\n        .UseQueueForAllRecurrent(\"recurrent\")\n```\n\nBy default, JobbyServer only executes tasks from the `default` queue. To run tasks from other queues, you need to specify them during configuration in the `UseServerSettings` method:  \n\n```csharp\nbuilder.Services.AddJobbyServerAndClient((IAspNetCoreJobbyConfigurable jobbyBuilder) =\u003e\n{\n    // ...\n    jobbyBuilder.ConfigureJobby((sp, jobby) =\u003e\n    {\n        jobby\n            .UseServerSettings(new JobbyServerSettings\n            {\n                // Here you specify the queues  \n                // from which tasks should be executed  \n                Queues = [\n                    new QueueSettings { QueueName = \"default\" },\n                    new QueueSettings { QueueName = \"recurrent\" },\n                    new QueueSettings\n                    {\n                        QueueName = \"heavy\",\n                        // If needed, you can reduce the degree of parallelism for a separate queue  \n                        MaxDegreeOfParallelism = 1,\n                    }\n                ]\n            })\n```\n\n### Using Middlewares\n\nIt is possible to wrap background task handler calls with your own middlewares.\n\nTo create a middleware, you need to implement the `IJobbyMiddleware` interface:\n\n```csharp\npublic class SomeMiddleware : IJobbyMiddleware\n{\n    public async Task ExecuteAsync\u003cTCommand\u003e(TCommand command, JobExecutionContext ctx, IJobCommandHandler\u003cTCommand\u003e handler)\n        where TCommand : IJobCommand\n    {\n        // Logic to be executed before the background task call can be placed here\n        // ....\n\n        await handler.ExecuteAsync(command, ctx);\n\n        // Logic to be executed after the background task call can be placed here\n        // .... \n    }\n}\n```\n\nMiddleware supports dependency injection through the constructor.\n\nConfiguration:\n\n```csharp\nbuilder.Services.AddJobbyServerAndClient(jobbyBuilder =\u003e  \n{\n    jobbyBuilder.ConfigureJobby((serviceProvider, jobby) =\u003e {\n        // ...\n        jobby.ConfigurePipeline(pipeline =\u003e {\n\n            // This is how a singleton middleware without dependencies is added\n            pipeline.Use(new SomeMiddleware());\n\n            // This is how a singleton middleware with non-scoped dependencies can be added\n            // In this case, the SomeMiddleware type must be registered in the DI container!\n            pipeline.Use(serviceProvider.GetRequiredService\u003cSomeMiddleware\u003e());\n\n            // This is how a scoped middleware or middleware with scoped dependencies can be added\n            // In this case, the SomeMiddleware type must be registered in the DI container!\n            pipeline.Use\u003cSomeMiddleware\u003e();\n        });\n    }); \n});\n```\n\nMore examples: [Jobby.Samples.AspNet](https://github.com/fornit1917/jobby/tree/master/samples/Jobby.Samples.AspNet).\n\n### Metrics\n\nJobby collects several metrics about background job execution on a given instance:\n\n- `jobby.inst.jobs.started` - number of started jobs\n- `jobby.inst.jobs.completed` - number of successfully completed jobs\n- `jobby.inst.jobs.retried` - number of job retries scheduled after a failure\n- `jobby.inst.jobs.failed` - number of jobs that failed after the last retry attempt plus the number of failed launches of recurrent jobs\n- `jobby.inst.jobs.duration` - execution time histogram of background jobs\n\nTo enable metric collection, you must call the `UseMetrics` method during configuration:\n\n```csharp\nbuilder.Services.AddJobbyServerAndClient((IAspNetCoreJobbyConfigurable jobbyBuilder) =\u003e\n{\n    jobbyBuilder.ConfigureJobby((sp, jobby) =\u003e\n    {\n        jobby\n            .UseMetrics() // Enable metric collection\n            // ...\n    });\n});\n```\n\nIn OpenTelemetry, Jobby metrics are added as follows:\n\n```csharp\nbuilder.Services\n    .AddOpenTelemetry()\n    .WithMetrics(builder =\u003e {\n        // Add all metrics from Jobby to OpenTelemetry\n        builder.AddMeter(JobbyMeterNames.GetAll());\n    });\n```\n\nIn the [Jobby.Samples.AspNet](https://github.com/fornit1917/jobby/tree/master/samples/Jobby.Samples.AspNet) example, metric collection is enabled with export to Prometheus format via the `/metrics` endpoint.\n\n### Tracing\n\nTo enable tracing, you should call the `UseTracing` method during configuration:\n\n```csharp\nbuilder.Services.AddJobbyServerAndClient((IAspNetCoreJobbyConfigurable jobbyBuilder) =\u003e\n{\n    jobbyBuilder.ConfigureJobby((sp, jobby) =\u003e\n    {\n        jobby\n            .UseTracing() // Execute jobs within an Activity\n            // ...\n    });\n});\n```\n\nYou can enable the export of Jobby job traces via OpenTelemetry as follows:\n\n```csharp\nbuilder.Services\n    .AddOpenTelemetry()\n    .ConfigureResource(resource =\u003e resource.AddService(serviceName: \"Jobby.Samples.AspNet\"))\n    .WithTracing(builder =\u003e\n    {\n        builder.AddConsoleExporter();\n\n        // Add Jobby job execution traces to OpenTelemetry\n        builder.AddSource(JobbyActivitySourceNames.JobsExecution);\n    });\n```\n\nIn the [Jobby.Samples.AspNet](https://github.com/fornit1917/jobby/tree/master/samples/Jobby.Samples.AspNet) example, metric collection with export to stdout is enabled.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffornit1917%2Fjobby","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffornit1917%2Fjobby","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffornit1917%2Fjobby/lists"}