/// <summary> /// call this from your client projects to queue a job /// </summary> public static async Task <TKey> QueueJobAsync <TJob, TRequest, TKey>(this QueueClient queueClient, string userName, TRequest request, JobRepositoryBase <TJob, TKey> repository, ILogger logger) where TJob : BackgroundJobInfo <TKey>, new() { // this enables visibility of the job over its lifetime (enabling dashboards, notifications, retry and tracking features) var job = await SaveJobAsync(repository, userName, request); logger.LogDebug($"Job Id {job.Id} was saved with request data {job.RequestData}"); try { // this queues the job for execution await SendJsonAsync(queueClient, job.Id); logger.LogDebug($"Job Id {job.Id} was added to queue {queueClient.Name}"); } catch (Exception exc) { // if queuing failed, then we need to indicate that in the stored record logger.LogError($"Error queuing Job Id {job.Id} on {queueClient.Name}: {exc.Message}"); job.Status = JobStatus.Aborted; await repository.SaveAsync(job); } return(job.Id); }
public static async Task <TJob> SaveJobAsync <TJob, TRequest, TKey>( this JobRepositoryBase <TJob, TKey> repository, string userName, TRequest request) where TJob : BackgroundJobInfo <TKey>, new() { var job = new TJob(); job.UserName = userName; job.RequestType = typeof(TRequest).Name; job.RequestData = JsonSerializer.Serialize(request); job.Status = JobStatus.Pending; job.Started = null; job.Completed = null; job.Created = DateTime.UtcNow; return(await repository.SaveAsync(job)); }
/// <summary> /// call this in your QueueTrigger Azure Function. The id will be the queue message data /// </summary> public async Task <TResult> ExecuteAsync(TKey id) { TResult result = default; TJob job = default; var errorContext = "starting"; try { job = await _repository.GetAsync(id); if (job == null) { throw new Exception($"Job Id {id} not found."); } if (!job.RequestType.Equals(typeof(TRequest).Name)) { throw new Exception($"Job Id {id} request type {job.RequestType} does not match job runner request type {typeof(TRequest).Name}"); } var request = JsonSerializer.Deserialize <TRequest>(job.RequestData); try { errorContext = "executing"; job.RetryCount++; job.ExceptionData = null; job.Status = JobStatus.Running; job.Started = DateTime.UtcNow; await _repository.SaveAsync(job); if (PostStatusUpdates) { await OnStatusUpdatedAsync(id, job.Status); } result = await OnExecuteAsync(request); job.ResultData = JsonSerializer.Serialize(result); job.Status = JobStatus.Succeeded; } catch (Exception exc) { errorContext = "failing"; Logger.LogError(exc, $"Job Id {job.Id} failed: {exc.Message}"); job.Status = JobStatus.Failed; job.ExceptionData = JsonSerializer.Serialize(new { message = exc.FullMessage(), data = exc.Data, stackTrace = exc.StackTrace }); } finally { errorContext = "finishing"; job.Completed = DateTime.UtcNow; job.Duration = Convert.ToInt32(job.Completed.Value.Subtract(job.Started.Value).TotalSeconds); await _repository.SaveAsync(job); if (PostStatusUpdates) { await OnStatusUpdatedAsync(id, job.Status); } } } catch (Exception exc) { if (job != null) { job.Status = JobStatus.Aborted; job.ExceptionData = JsonSerializer.Serialize(new { errorContext, message = exc.FullMessage() }); await _repository.SaveAsync(job); if (PostStatusUpdates) { await OnStatusUpdatedAsync(id, job.Status); } } Logger.LogError(exc, $"While {errorContext}: {exc.Message}"); } return(result); }