ByteBard.GUSTO
1.2.0
Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
See the version list below for details.
dotnet add package ByteBard.GUSTO --version 1.2.0
NuGet\Install-Package ByteBard.GUSTO -Version 1.2.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ByteBard.GUSTO" Version="1.2.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ByteBard.GUSTO" Version="1.2.0" />
<PackageReference Include="ByteBard.GUSTO" />
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ByteBard.GUSTO --version 1.2.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: ByteBard.GUSTO, 1.2.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ByteBard.GUSTO@1.2.0
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ByteBard.GUSTO&version=1.2.0
#tool nuget:?package=ByteBard.GUSTO&version=1.2.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
GUSTO
Generic Utility for Scheduling & Task Orchestration.
A lightweight background job processing library for .NET. GUSTO provides a simple way to queue and execute background tasks with just a background service and two interfaces to implement.
Features
- Minimal Setup: Implement two interfaces and you're ready
- Storage Agnostic: Use any database (SQL Server, MongoDB, Redis, etc.)
- Concurrent Processing: Configurable parallel job execution
- Job Scheduling: Schedule jobs for future execution
- Failure Handling: Built-in retry logic with customizable strategies
Quick Start
1. Install the Package
dotnet add package ByteBard.GUSTO
2. Implement the Storage Record
public class JobRecord : IJobStorageRecord
{
public Guid TrackingId { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime? ExecuteAfter { get; set; }
public DateTime? ExpireOn { get; set; }
public bool IsComplete { get; set; }
public string JobType { get; set; }
public string MethodName { get; set; }
public string ArgumentsJson { get; set; }
}
3. Implement the Storage Provider
public class InMemoryJobStorageProvider : IJobStorageProvider<JobRecord>
{
private readonly List<JobRecord> _jobs = new();
private readonly object _lock = new();
public Task StoreJobAsync(JobRecord jobStorageRecord, CancellationToken cancellationToken)
{
lock (_lock)
{
_jobs.Add(jobStorageRecord);
}
return Task.CompletedTask;
}
public Task<IEnumerable<JobRecord>> GetBatchAsync(JobSearchParams<JobRecord> parameters, CancellationToken cancellationToken)
{
lock (_lock)
{
var results = _jobs.Where(parameters.Match.Compile()).Take(parameters.Limit);
return Task.FromResult(results);
}
}
public Task MarkJobAsCompleteAsync(JobRecord jobStorageRecord, CancellationToken cancellationToken)
{
lock (_lock)
{
var job = _jobs.FirstOrDefault(j => j.TrackingId == jobStorageRecord.TrackingId);
if (job != null)
{
job.IsComplete = true;
}
}
return Task.CompletedTask;
}
public Task CancelJobAsync(Guid trackingId, CancellationToken cancellationToken)
{
lock (_lock)
{
_jobs.RemoveAll(j => j.TrackingId == trackingId);
}
return Task.CompletedTask;
}
public Task OnHandlerExecutionFailureAsync(JobRecord jobStorageRecord, Exception exception, CancellationToken cancellationToken)
{
// Simple retry: reschedule for 5 minutes later
jobStorageRecord.ExecuteAfter = DateTime.UtcNow.AddMinutes(5);
return Task.CompletedTask;
}
}
4. Configure Your Application
// Program.cs
services.AddGusto<JobRecord, InMemoryJobStorageProvider>(configuration);
// appsettings.json
{
"Gusto": {
"BatchSize": 50,
"Concurrency": 8,
"PollInterval": "00:00:10"
}
}
5. Create and Queue Jobs
public class EmailService
{
private readonly IEmailProvider _emailProvider;
public EmailService(IEmailProvider emailProvider)
{
_emailProvider = emailProvider;
}
public async Task SendWelcomeEmailAsync(string email, string userName)
{
await _emailProvider.SendAsync(email, "Welcome!", $"Hello {userName}!");
}
}
// Queue jobs
public class UserController : ControllerBase
{
private readonly JobQueue<JobRecord> _jobQueue;
public UserController(JobQueue<JobRecord> jobQueue)
{
_jobQueue = jobQueue;
}
[HttpPost("register")]
public async Task<IActionResult> Register(RegisterRequest request)
{
// Queue immediate job
await _jobQueue.EnqueueAsync<EmailService>(
service => service.SendWelcomeEmailAsync(request.Email, request.UserName));
// Queue scheduled job with expiration
var trackingId = await _jobQueue.EnqueueAsync<EmailService>(
service => service.SendWelcomeEmailAsync(request.Email, request.UserName),
executeAfter: DateTime.UtcNow.AddHours(1),
expireOn: DateTime.UtcNow.AddDays(1));
return Ok(new { TrackingId = trackingId });
}
}
Configuration
- BatchSize: Jobs to process per batch (default: 10)
- Concurrency: Max parallel jobs (default: Environment.ProcessorCount)
- PollInterval: Polling frequency (default: 10 seconds)
Advanced Patterns
MongoDB Storage Provider
public class MongoJobRecord : IJobStorageRecord
{
public string Id { get; set; }
public Guid TrackingId { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime? ExecuteAfter { get; set; }
public DateTime? ExpireOn { get; set; }
public bool IsComplete { get; set; }
public string JobType { get; set; }
public string MethodName { get; set; }
public string ArgumentsJson { get; set; }
public int? RetryCount { get; set; }
}
public class MongoDbJobStorageProvider : IJobStorageProvider<MongoJobRecord>
{
private readonly IMongoCollection<MongoJobRecord> _collection;
public MongoDbJobStorageProvider(IMongoDatabase database)
{
_collection = database.GetCollection<MongoJobRecord>("jobs");
}
public async Task StoreJobAsync(MongoJobRecord jobStorageRecord, CancellationToken cancellationToken)
{
await _collection.InsertOneAsync(jobStorageRecord, cancellationToken: cancellationToken);
}
public async Task<IEnumerable<MongoJobRecord>> GetBatchAsync(JobSearchParams<MongoJobRecord> parameters, CancellationToken cancellationToken)
{
var filter = Builders<MongoJobRecord>.Filter.Where(parameters.Match);
return await _collection.Find(filter).Limit(parameters.Limit).ToListAsync(cancellationToken);
}
public async Task MarkJobAsCompleteAsync(MongoJobRecord jobStorageRecord, CancellationToken cancellationToken)
{
var filter = Builders<MongoJobRecord>.Filter.Eq(x => x.TrackingId, jobStorageRecord.TrackingId);
var update = Builders<MongoJobRecord>.Update.Set(x => x.IsComplete, true);
await _collection.UpdateOneAsync(filter, update, cancellationToken: cancellationToken);
}
public async Task CancelJobAsync(Guid trackingId, CancellationToken cancellationToken)
{
var filter = Builders<MongoJobRecord>.Filter.Eq(x => x.TrackingId, trackingId);
await _collection.DeleteOneAsync(filter, cancellationToken);
}
public async Task OnHandlerExecutionFailureAsync(MongoJobRecord jobStorageRecord, Exception exception, CancellationToken cancellationToken)
{
// Simple retry: reschedule for 5 minutes later
var filter = Builders<MongoJobRecord>.Filter.Eq(x => x.TrackingId, jobStorageRecord.TrackingId);
var update = Builders<MongoJobRecord>.Update.Set(x => x.ExecuteAfter, DateTime.UtcNow.AddMinutes(5));
await _collection.UpdateOneAsync(filter, update, cancellationToken: cancellationToken);
}
}
Exponential Backoff Retry
public async Task OnHandlerExecutionFailureAsync(MongoJobRecord jobStorageRecord, Exception exception, CancellationToken cancellationToken)
{
// Track retry attempts
jobStorageRecord.RetryCount = (jobStorageRecord.RetryCount ?? 0) + 1;
if (jobStorageRecord.RetryCount > 5)
{
// Max retries exceeded - mark as failed
await MarkJobAsCompleteAsync(jobStorageRecord, cancellationToken);
return;
}
// Exponential backoff: 2^retryCount minutes
var delayMinutes = Math.Pow(2, jobStorageRecord.RetryCount.Value);
jobStorageRecord.ExecuteAfter = DateTime.UtcNow.AddMinutes(delayMinutes);
// Update the record with new retry info
var filter = Builders<MongoJobRecord>.Filter.Eq(x => x.TrackingId, jobStorageRecord.TrackingId);
var update = Builders<MongoJobRecord>.Update
.Set(x => x.ExecuteAfter, jobStorageRecord.ExecuteAfter)
.Set(x => x.RetryCount, jobStorageRecord.RetryCount);
await _collection.UpdateOneAsync(filter, update, cancellationToken: cancellationToken);
}
Dead Letter Queue
public async Task OnHandlerExecutionFailureAsync(MongoJobRecord jobStorageRecord, Exception exception, CancellationToken cancellationToken)
{
jobStorageRecord.RetryCount = (jobStorageRecord.RetryCount ?? 0) + 1;
if (jobStorageRecord.RetryCount > 3)
{
// Move to dead letter collection for manual inspection
await _deadLetterCollection.InsertOneAsync(new DeadLetterJob
{
OriginalJob = jobStorageRecord,
FailureReason = exception.Message,
FailedAt = DateTime.UtcNow
}, cancellationToken: cancellationToken);
await MarkJobAsCompleteAsync(jobStorageRecord, cancellationToken);
return;
}
// Continue with retry logic...
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
net8.0
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.7)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.7)
- Newtonsoft.Json (>= 13.0.3)
-
net9.0
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.7)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.7)
- Newtonsoft.Json (>= 13.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.