Exception Handling
TickerQ provides a robust exception handling system that allows you to customize how job failures are managed.
ITickerExceptionHandler Interfaceâ
Implement the ITickerExceptionHandler interface to handle job exceptions:
using TickerQ.Utilities.Base;
using TickerQ.Utilities.Enums;
using TickerQ.Utilities.Interfaces;
public class TickerExceptionHandler(ILogger<TickerExceptionHandler> logger) : ITickerExceptionHandler
{
public async Task HandleExceptionAsync(Exception exception, Guid tickerId, TickerType tickerType)
{
Console.WriteLine($"đ¨ EXCEPTION - ID: {tickerId}, Type: {tickerType}, Error: {exception.Message}");
logger.LogError(exception, "TickerQ job failed - ID: {TickerId}, Type: {TickerType}", tickerId, tickerType);
await Task.CompletedTask;
}
public async Task HandleCanceledExceptionAsync(Exception exception, Guid tickerId, TickerType tickerType)
{
Console.WriteLine($"âšī¸ CANCELLED - ID: {tickerId}, Type: {tickerType}, Reason: {exception.Message}");
logger.LogWarning("TickerQ job cancelled - ID: {TickerId}, Type: {TickerType}", tickerId, tickerType);
await Task.CompletedTask;
}
}
Handler Methodsâ
HandleExceptionAsyncâ
Called when a job throws an exception:
public async Task HandleExceptionAsync(Exception exception, Guid tickerId, TickerType tickerType)
{
// Log the exception
logger.LogError(exception, "Job {TickerId} of type {TickerType} failed", tickerId, tickerType);
// Send notifications
await SendFailureNotification(tickerId, exception);
// Update database status
await UpdateJobStatus(tickerId, JobStatus.Failed);
// Custom business logic
if (exception is DatabaseConnectionException)
{
await ScheduleRetry(tickerId);
}
}
HandleCanceledExceptionAsyncâ
Called when a job is cancelled:
public async Task HandleCanceledExceptionAsync(Exception exception, Guid tickerId, TickerType tickerType)
{
logger.LogWarning("Job {TickerId} was cancelled", tickerId);
// Clean up resources
await CleanupJobResources(tickerId);
// Update status
await UpdateJobStatus(tickerId, JobStatus.Cancelled);
}
Registrationâ
Register your exception handler in Program.cs:
builder.Services.AddTickerQ(options =>
{
// ... other options
options.SetExceptionHandler<TickerExceptionHandler>();
});
Advanced Exception Handlingâ
Custom Exception Typesâ
Handle different exception types differently:
public async Task HandleExceptionAsync(Exception exception, Guid tickerId, TickerType tickerType)
{
switch (exception)
{
case DatabaseConnectionException dbEx:
await HandleDatabaseException(dbEx, tickerId);
break;
case TimeoutException timeoutEx:
await HandleTimeoutException(timeoutEx, tickerId);
break;
case ValidationException validationEx:
await HandleValidationException(validationEx, tickerId);
break;
default:
await HandleGenericException(exception, tickerId);
break;
}
}
Notification Systemâ
Send notifications for critical failures:
public async Task HandleExceptionAsync(Exception exception, Guid tickerId, TickerType tickerType)
{
// Log the exception
logger.LogError(exception, "Critical job failure: {TickerId}", tickerId);
// Send email notification for critical jobs
if (IsCriticalJob(tickerId))
{
await emailService.SendCriticalFailureAlert(tickerId, exception);
}
// Send Slack notification
await slackService.SendJobFailureNotification(tickerId, exception.Message);
// Create support ticket
await supportService.CreateTicket(tickerId, exception);
}
Retry Logicâ
Implement custom retry strategies:
public async Task HandleExceptionAsync(Exception exception, Guid tickerId, TickerType tickerType)
{
var retryCount = await GetRetryCount(tickerId);
if (retryCount < MaxRetries && IsRetryableException(exception))
{
var delay = CalculateRetryDelay(retryCount);
await ScheduleRetry(tickerId, delay);
}
else
{
await MarkJobAsFailed(tickerId);
}
}
private bool IsRetryableException(Exception exception)
{
return exception is DatabaseConnectionException ||
exception is TimeoutException ||
exception is HttpRequestException;
}
private TimeSpan CalculateRetryDelay(int retryCount)
{
return TimeSpan.FromMinutes(Math.Pow(2, retryCount)); // Exponential backoff
}
Testing Exception Handlingâ
Create Test Jobsâ
[TickerFunction("TestException", "*/1 * * * *")]
public async Task TestException(TickerFunctionContext context, CancellationToken cancellationToken)
{
Console.WriteLine($"đĨ Exception test started at {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}");
await Task.Delay(1000, cancellationToken);
throw new InvalidOperationException("Test exception for TickerQ exception handler!");
}
Monitor in Dashboardâ
- Navigate to
/dashboard - View job execution history
- Check exception details
- Monitor retry attempts
Best Practicesâ
- Log all exceptions with sufficient context
- Use structured logging for better searchability
- Handle different exception types appropriately
- Implement retry logic for transient failures
- Send notifications for critical failures
- Clean up resources on cancellation
- Update job status in your database
- Test exception scenarios thoroughly
Common Patternsâ
Database Exception Handlingâ
if (exception is SqlException sqlEx)
{
if (sqlEx.Number == 2) // Timeout
{
await ScheduleRetry(tickerId, TimeSpan.FromMinutes(5));
}
else if (sqlEx.Number == 1205) // Deadlock
{
await ScheduleRetry(tickerId, TimeSpan.FromSeconds(30));
}
}
HTTP Exception Handlingâ
if (exception is HttpRequestException httpEx)
{
if (httpEx.Message.Contains("timeout"))
{
await ScheduleRetry(tickerId, TimeSpan.FromMinutes(2));
}
}
Next Stepsâ
Learn about the Dashboard for monitoring and managing your jobs.