public override void StartWorking(Func <IQueueEntry <T>, CancellationToken, Task> handler, bool autoComplete = false, CancellationToken cancellationToken = default(CancellationToken)) { if (handler == null) { throw new ArgumentNullException(nameof(handler)); } _logger.Trace("Queue {0} start working", typeof(T).Name); var linkedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(_disposeTokenSource.Token, cancellationToken).Token; Task.Run(async() => { _logger.Trace("WorkerLoop Start {0}", typeof(T).Name); while (!linkedCancellationToken.IsCancellationRequested) { _logger.Trace("WorkerLoop Signaled {0}", typeof(T).Name); IQueueEntry <T> queueEntry = null; try { queueEntry = await DequeueAsync(cancellationToken: cancellationToken).AnyContext(); } catch (Exception ex) { _logger.Error(ex, "Error on Dequeue: " + ex.Message); } if (queueEntry == null) { return; } try { await handler(queueEntry, linkedCancellationToken).AnyContext(); if (autoComplete) { await queueEntry.CompleteAsync().AnyContext(); } } catch (Exception ex) { _logger.Error(ex, "Worker error: {0}", ex.Message); await queueEntry.AbandonAsync().AnyContext(); Interlocked.Increment(ref _workerErrorCount); } } _logger.Trace("WorkLoop End"); }, linkedCancellationToken); }
protected virtual Task OnLockRenewedAsync(IQueueEntry <T> entry) { LastDequeueActivity = SystemClock.UtcNow; var lockRenewed = LockRenewed; if (lockRenewed == null) { return(Task.CompletedTask); } var args = new LockRenewedEventArgs <T> { Queue = this, Entry = entry }; return(lockRenewed.InvokeAsync(this, args)); }
protected virtual void StopProcessQueueEntryActivity(IQueueEntry <T> entry) { if (!entry.Properties.TryGetValue("@Activity", out object a) || !(a is Activity activity)) { return; } entry.Properties.Remove("@Activity"); if (QueuesDiagnosticSource.Logger.IsEnabled("ProcessQueueEntry")) { QueuesDiagnosticSource.Logger.StopActivity(activity, entry); } else { activity.Stop(); } }
public override async Task CompleteAsync(IQueueEntry <T> entry) { _logger.Trace("Queue {0} complete item: {1}", typeof(T).Name, entry.Id); QueueEntry <T> info = null; if (!_dequeued.TryRemove(entry.Id, out info) || info == null) { throw new ApplicationException("Unable to remove item from the dequeued list."); } Interlocked.Increment(ref _completedCount); await OnCompletedAsync(entry).AnyContext(); _logger.Trace("Complete done: {0}", entry.Id); }
protected override void StartWorkingImpl(Func <IQueueEntry <T>, CancellationToken, Task> handler, bool autoComplete, CancellationToken cancellationToken) { if (handler == null) { throw new ArgumentNullException(nameof(handler)); } var linkedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(_queueDisposedCancellationTokenSource.Token, cancellationToken).Token; Task.Run(async() => { _logger.Trace("WorkerLoop Start {_queueName}", _queueName); while (!linkedCancellationToken.IsCancellationRequested) { _logger.Trace("WorkerLoop Signaled {_queueName}", _queueName); IQueueEntry <T> queueEntry = null; try { queueEntry = await DequeueImplAsync(cancellationToken: cancellationToken).AnyContext(); } catch (TimeoutException) { } if (linkedCancellationToken.IsCancellationRequested || queueEntry == null) { continue; } try { await handler(queueEntry, linkedCancellationToken).AnyContext(); if (autoComplete && !queueEntry.IsAbandoned && !queueEntry.IsCompleted) { await queueEntry.CompleteAsync().AnyContext(); } } catch (Exception ex) { Interlocked.Increment(ref _workerErrorCount); _logger.Error(ex, "Worker error: {0}", ex.Message); if (!queueEntry.IsAbandoned && !queueEntry.IsCompleted) { await queueEntry.AbandonAsync().AnyContext(); } } } _logger.Trace("Worker exiting: {0} Cancel Requested: {1}", _queueName, linkedCancellationToken.IsCancellationRequested); }, linkedCancellationToken); }
private async Task <QueueMessageRecoveryStrategy> OnProcessMessageAsync(IQueueEntry <TMessage> queueEntry) { try { await OnMessageReceivedAsync(queueEntry).ConfigureAwait(false); return(QueueMessageRecoveryStrategy.Complete); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception e) #pragma warning restore CA1031 // Do not catch general exception types { return(await TaskHelpers.DefaultIfExceptionAsync( GetRecoveryStrategyAsync, e, QueueMessageRecoveryStrategy.Abandon).ConfigureAwait(false)); } }
public override async Task RenewLockAsync(IQueueEntry <T> entry) { _logger.Debug("Queue {0} renew lock item: {1}", _options.Name, entry.Id); var item = entry as QueueEntry <T>; _dequeued.AddOrUpdate(entry.Id, item, (key, value) => { if (item != null) { value.RenewedTimeUtc = item.RenewedTimeUtc; } return(value); }); await OnLockRenewedAsync(entry).AnyContext(); _logger.Trace("Renew lock done: {0}", entry.Id); }
protected virtual async Task OnAbandonedAsync(IQueueEntry <T> entry) { LastDequeueActivity = SystemClock.UtcNow; if (entry is QueueEntry <T> metadata && metadata.DequeuedTimeUtc > DateTime.MinValue) { metadata.ProcessingTime = SystemClock.UtcNow.Subtract(metadata.DequeuedTimeUtc); } if (Abandoned != null) { var args = new AbandonedEventArgs <T> { Queue = this, Entry = entry }; await Abandoned.InvokeAsync(this, args).AnyContext(); } StopProcessQueueEntryActivity(entry); }
protected virtual Task OnDequeuedAsync(IQueueEntry <T> entry) { LastDequeueActivity = SystemClock.UtcNow; StartProcessQueueEntryActivity(entry); var dequeued = Dequeued; if (dequeued == null) { return(Task.CompletedTask); } var args = new DequeuedEventArgs <T> { Queue = this, Entry = entry }; return(dequeued.InvokeAsync(this, args)); }
public override async Task AbandonAsync(IQueueEntry <T> queueEntry) { var azureQueueEntry = ToAzureEntryWithCheck(queueEntry); if (azureQueueEntry.Attempts > _retries) { await _queueReference.DeleteMessageAsync(azureQueueEntry.UnderlyingMessage).AnyContext(); await _deadletterQueueReference.AddMessageAsync(azureQueueEntry.UnderlyingMessage).AnyContext(); } else { // Make the item visible immediately await _queueReference.UpdateMessageAsync(azureQueueEntry.UnderlyingMessage, TimeSpan.Zero, MessageUpdateFields.Visibility).AnyContext(); } Interlocked.Increment(ref _abandonedCount); await OnAbandonedAsync(queueEntry).AnyContext(); }
public override async Task CompleteAsync(IQueueEntry <T> entry) { _logger.LogDebug("Queue {Name} complete item: {Id}", _options.Name, entry.Id); if (entry.IsAbandoned || entry.IsCompleted) { throw new InvalidOperationException("Queue entry has already been completed or abandoned"); } if (!_dequeued.TryRemove(entry.Id, out var info) || info == null) { throw new Exception("Unable to remove item from the dequeued list"); } entry.MarkCompleted(); Interlocked.Increment(ref _completedCount); await OnCompletedAsync(entry).AnyContext(); _logger.LogTrace("Complete done: {Id}", entry.Id); }
protected virtual void StartProcessQueueEntryActivity(IQueueEntry <T> entry) { if (!QueuesDiagnosticSource.Logger.IsEnabled("ProcessQueueEntry") || !QueuesDiagnosticSource.Logger.IsEnabled("ProcessQueueEntry", entry)) { return; } var activity = new Activity("ProcessQueueEntry"); activity.AddTag("Id", entry.Id); if (!String.IsNullOrEmpty(entry.CorrelationId)) { activity.SetParentId(entry.CorrelationId); } EnrichProcessQueueEntryActivity(activity, entry); QueuesDiagnosticSource.Logger.StartActivity(activity, entry); entry.Properties["@Activity"] = activity; }
protected virtual Task OnAbandonedAsync(IQueueEntry <T> entry) { if (entry is QueueEntry <T> metadata && metadata.DequeuedTimeUtc > DateTime.MinValue) { metadata.ProcessingTime = SystemClock.UtcNow.Subtract(metadata.DequeuedTimeUtc); } var abandoned = Abandoned; if (abandoned == null) { return(Task.CompletedTask); } var args = new AbandonedEventArgs <T> { Queue = this, Entry = entry }; return(abandoned.InvokeAsync(this, args)); }
private static async Task HandleRenewalAsync( IQueueEntry <TMessage> queueEntry, CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { await queueEntry.RenewLockAsync().ConfigureAwait(false); await Task.Delay(5000, cancellationToken).ConfigureAwait(false); } } #pragma warning disable CA1031 // Do not catch general exception types catch #pragma warning restore CA1031 // Do not catch general exception types { // no op } }
public override async Task AbandonAsync(IQueueEntry <T> entry) { _logger.LogDebug("Queue {Name}:{QueueId} abandon item: {Id}", _options.Name, QueueId, entry.Id); if (entry.IsAbandoned || entry.IsCompleted) { throw new InvalidOperationException("Queue entry has already been completed or abandoned"); } if (!_dequeued.TryRemove(entry.Id, out var targetEntry) || targetEntry == null) { throw new Exception("Unable to remove item from the dequeued list"); } entry.MarkAbandoned(); Interlocked.Increment(ref _abandonedCount); _logger.LogTrace("Abandon complete: {Id}", entry.Id); try { await OnAbandonedAsync(entry).AnyContext(); } finally { if (targetEntry.Attempts < _options.Retries + 1) { if (_options.RetryDelay > TimeSpan.Zero) { _logger.LogTrace("Adding item to wait list for future retry: {Id}", entry.Id); var unawaited = Run.DelayedAsync(GetRetryDelay(targetEntry.Attempts), () => RetryAsync(targetEntry), _queueDisposedCancellationTokenSource.Token); } else { _logger.LogTrace("Adding item back to queue for retry: {Id}", entry.Id); var unawaited = Task.Run(() => RetryAsync(targetEntry)); } } else { _logger.LogTrace("Exceeded retry limit moving to deadletter: {Id}", entry.Id); _deadletterQueue.Enqueue(targetEntry); } } }
public override async Task RenewLockAsync(IQueueEntry <T> queueEntry) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Queue {Name} renew lock item: {EntryId}", _options.Name, queueEntry.Id); } var entry = ToQueueEntry(queueEntry); var request = new ChangeMessageVisibilityRequest { QueueUrl = _queueUrl, VisibilityTimeout = (int)_options.WorkItemTimeout.TotalSeconds, ReceiptHandle = entry.UnderlyingMessage.ReceiptHandle }; await _client.Value.ChangeMessageVisibilityAsync(request).AnyContext(); if (_logger.IsEnabled(LogLevel.Trace)) { _logger.LogTrace("Renew lock done: {EntryId}", queueEntry.Id); } }
public override void StartWorking(Func <IQueueEntry <T>, CancellationToken, Task> handler, bool autoComplete = false, CancellationToken cancellationToken = new CancellationToken()) { if (handler == null) { throw new ArgumentNullException(nameof(handler)); } var linkedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(_queueDisposedCancellationTokenSource.Token, cancellationToken).Token; Task.Run(async() => { while (!linkedCancellationToken.IsCancellationRequested) { IQueueEntry <T> queueEntry = null; try { queueEntry = await DequeueAsync(cancellationToken).AnyContext(); } catch (TaskCanceledException) { } if (linkedCancellationToken.IsCancellationRequested || queueEntry == null) { continue; } try { await handler(queueEntry, cancellationToken); if (autoComplete) { await queueEntry.CompleteAsync().AnyContext(); } } catch (Exception ex) { _logger.Error(ex, "Worker error: {0}", ex.Message); await queueEntry.AbandonAsync().AnyContext(); Interlocked.Increment(ref _workerErrorCount); } } _logger.Trace("Worker exiting: {0} Cancel Requested: {1}", _queueReference.Name, linkedCancellationToken.IsCancellationRequested); }, linkedCancellationToken); }
public override async Task CompleteAsync(IQueueEntry <T> queueEntry) { _logger.Debug("Queue {0} complete item: {1}", _options.Name, queueEntry.Id); if (queueEntry.IsAbandoned || queueEntry.IsCompleted) { throw new InvalidOperationException("Queue entry has already been completed or abandoned."); } var entry = ToQueueEntry(queueEntry); var request = new DeleteMessageRequest { QueueUrl = _queueUrl, ReceiptHandle = entry.UnderlyingMessage.ReceiptHandle, }; await _client.Value.DeleteMessageAsync(request).AnyContext(); Interlocked.Increment(ref _completedCount); queueEntry.MarkCompleted(); await OnCompletedAsync(queueEntry).AnyContext(); _logger.Trace("Complete done: {0}", queueEntry.Id); }
public override async Task CompleteAsync(IQueueEntry <T> entry) { _logger.Debug("Queue {0} complete item: {1}", _queueName, entry.Id); var tasks = new List <Task>(); var batch = Database.CreateBatch(); tasks.Add(batch.ListRemoveAsync(WorkListName, entry.Id)); tasks.Add(batch.KeyDeleteAsync(GetPayloadKey(entry.Id))); tasks.Add(batch.KeyDeleteAsync(GetAttemptsKey(entry.Id))); tasks.Add(batch.KeyDeleteAsync(GetEnqueuedTimeKey(entry.Id))); tasks.Add(batch.KeyDeleteAsync(GetDequeuedTimeKey(entry.Id))); tasks.Add(batch.KeyDeleteAsync(GetWaitTimeKey(entry.Id))); batch.Execute(); await Task.WhenAll(tasks.ToArray()).AnyContext(); Interlocked.Increment(ref _completedCount); await OnCompletedAsync(entry).AnyContext(); _logger.Trace("Complete done: {0}", entry.Id); }
protected virtual Task OnCompletedAsync(IQueueEntry <T> entry) { var metadata = entry as QueueEntry <T>; if (metadata != null && metadata.DequeuedTimeUtc > DateTime.MinValue) { metadata.ProcessingTime = SystemClock.UtcNow.Subtract(metadata.DequeuedTimeUtc); } var completed = Completed; if (completed == null) { return(Task.CompletedTask); } var args = new CompletedEventArgs <T> { Queue = this, Entry = entry }; return(completed.InvokeAsync(this, args)); }
public override async Task CompleteAsync(IQueueEntry <T> entry) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Queue {Name} complete item: {EntryId}", _options.Name, entry.Id); } if (entry.IsAbandoned || entry.IsCompleted) { //_logger.LogDebug("Queue {Name} item already abandoned or completed: {EntryId}", _options.Name, entry.Id); throw new InvalidOperationException("Queue entry has already been completed or abandoned."); } long result = await Run.WithRetriesAsync(() => Database.ListRemoveAsync(_workListName, entry.Id), logger : _logger).AnyContext(); if (result == 0) { _logger.LogDebug("Queue {Name} item not in work list: {EntryId}", _options.Name, entry.Id); throw new InvalidOperationException("Queue entry not in work list, it may have been auto abandoned."); } await Run.WithRetriesAsync(() => Task.WhenAll( Database.KeyDeleteAsync(GetPayloadKey(entry.Id)), Database.KeyDeleteAsync(GetAttemptsKey(entry.Id)), Database.KeyDeleteAsync(GetEnqueuedTimeKey(entry.Id)), Database.KeyDeleteAsync(GetDequeuedTimeKey(entry.Id)), Database.KeyDeleteAsync(GetRenewedTimeKey(entry.Id)), Database.KeyDeleteAsync(GetWaitTimeKey(entry.Id)) ), logger : _logger).AnyContext(); Interlocked.Increment(ref _completedCount); entry.MarkCompleted(); await OnCompletedAsync(entry).AnyContext(); if (_logger.IsEnabled(LogLevel.Trace)) { _logger.LogTrace("Complete done: {EntryId}", entry.Id); } }
public override async Task AbandonAsync(IQueueEntry <T> entry) { _logger.Debug("Queue {_options.Name}:{QueueId} abandon item: {entryId}", _options.Name, QueueId, entry.Id); if (entry.IsAbandoned || entry.IsCompleted) { throw new InvalidOperationException("Queue entry has already been completed or abandoned."); } if (!_dequeued.TryRemove(entry.Id, out QueueEntry <T> info) || info == null) { throw new Exception("Unable to remove item from the dequeued list."); } if (info.Attempts < _options.Retries + 1) { if (_options.RetryDelay > TimeSpan.Zero) { _logger.Trace("Adding item to wait list for future retry: {0}", entry.Id); var unawaited = Run.DelayedAsync(GetRetryDelay(info.Attempts), () => RetryAsync(info)); } else { _logger.Trace("Adding item back to queue for retry: {0}", entry.Id); var unawaited = Task.Run(() => RetryAsync(info)); } } else { _logger.Trace("Exceeded retry limit moving to deadletter: {0}", entry.Id); _deadletterQueue.Enqueue(info); } Interlocked.Increment(ref _abandonedCount); entry.MarkAbandoned(); await OnAbandonedAsync(entry).AnyContext(); _logger.Trace("Abandon complete: {entryId}", entry.Id); }
public override async Task CompleteAsync(IQueueEntry <T> entry) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Queue {Name} complete item: {EntryId}", _options.Name, entry.Id); } if (entry.IsAbandoned || entry.IsCompleted) { throw new InvalidOperationException("Queue entry has already been completed or abandoned."); } var azureQueueEntry = ToAzureEntryWithCheck(entry); await _queueReference.DeleteMessageAsync(azureQueueEntry.UnderlyingMessage).AnyContext(); Interlocked.Increment(ref _completedCount); entry.MarkCompleted(); await OnCompletedAsync(entry).AnyContext(); if (_logger.IsEnabled(LogLevel.Trace)) { _logger.LogTrace("Complete done: {EntryId}", entry.Id); } }
protected virtual Activity StartProcessQueueEntryActivity(IQueueEntry <T> entry) { var activity = FoundatioDiagnostics.ActivitySource.StartActivity("ProcessQueueEntry", ActivityKind.Server, entry.CorrelationId); if (activity == null) { return(activity); } if (entry.Properties != null && entry.Properties.TryGetValue("TraceState", out var traceState)) { activity.TraceStateString = traceState.ToString(); } activity.DisplayName = $"Queue: {entry.EntryType.Name}"; if (entry.GetValue() is WorkItemData workItem && !String.IsNullOrEmpty(workItem.SubMetricName)) { activity.DisplayName = $"Queue Work Item: {workItem.SubMetricName}"; } EnrichProcessQueueEntryActivity(activity, entry); return(activity); }
public override async Task RenewLockAsync(IQueueEntry <T> entry) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Queue {Name} renew lock item: {Id}", _options.Name, entry.Id); } if (entry is QueueEntry <T> val && val.Data["Pull-Strategy"].Equals(true)) { var newLockedUntilUtc = await _messageReceiver.RenewLockAsync(entry.Id).AnyContext(); if (_logger.IsEnabled(LogLevel.Trace)) { _logger.LogTrace("Renew lock done: {Id} - {newLockedUntilUtc}", entry.Id, newLockedUntilUtc); } } await OnLockRenewedAsync(entry).AnyContext(); if (_logger.IsEnabled(LogLevel.Trace)) { _logger.LogTrace("Renew lock done: {Id}", entry.Id); } }
protected virtual void EnrichProcessQueueEntryActivity(Activity activity, IQueueEntry <T> entry) { if (!activity.IsAllDataRequested) { return; } activity.AddTag("EntryType", entry.EntryType.FullName); activity.AddTag("Id", entry.Id); activity.AddTag("CorrelationId", entry.CorrelationId); if (entry.Properties == null || entry.Properties.Count <= 0) { return; } foreach (var p in entry.Properties) { if (p.Key != "TraceState") { activity.AddTag(p.Key, p.Value); } } }
public bool DispatchHandleQueueResponse(IQueueEntry entry) { switch (entry.Type) { case QueueEntryType.HOTEL: return(HandleHotelEntry(new Hotel(entry.Hotel))); case QueueEntryType.RESERVATION: return(HandleReservationEntry(new Reservation(entry.Reservation))); case QueueEntryType.ROOM: return(HandleRoomEntry(new Room(entry.Room))); case QueueEntryType.USER: return(HandleUserEntry(new User(entry.User))); case QueueEntryType.NONE: Console.Write("Got None type entry"); return(false); default: return(false); } }
private async Task DoRecoveryStrategyAsync( QueueMessageRecoveryStrategy recoveryStrategy, IQueueEntry <TMessage> queueEntry) { switch (recoveryStrategy) { case QueueMessageRecoveryStrategy.Abandon: await DoAbandonMessageAsync(queueEntry) .ConfigureAwait(false); break; case QueueMessageRecoveryStrategy.Complete: await DoCompleteMessageAsync(queueEntry) .ConfigureAwait(false); break; case QueueMessageRecoveryStrategy.DeadLetter: await DoDeadLetterMessageAsync(queueEntry) .ConfigureAwait(false); break; case QueueMessageRecoveryStrategy.Requeue: await DoRequeueMessageAsync(queueEntry) .ConfigureAwait(false); break; default: throw new ArgumentOutOfRangeException( nameof(recoveryStrategy), $"Unexpected recovery strategy: {recoveryStrategy}"); } }
protected async Task DoWorkAsync(IQueueEntry<SimpleWorkItem> w, AsyncCountdownEvent countdown, WorkInfo info) { _logger.Trace($"Starting: {w.Value.Id}"); Assert.Equal("Hello", w.Value.Data); try { // randomly complete, abandon or blowup. if (RandomData.GetBool()) { _logger.Trace($"Completing: {w.Value.Id}"); await w.CompleteAsync(); info.IncrementCompletedCount(); } else if (RandomData.GetBool()) { _logger.Trace($"Abandoning: {w.Value.Id}"); await w.AbandonAsync(); info.IncrementAbandonCount(); } else { _logger.Trace($"Erroring: {w.Value.Id}"); info.IncrementErrorCount(); throw new Exception(); } } finally { _logger.Trace($"Signal {countdown.CurrentCount}"); countdown.Signal(); } }
public override Task AbandonAsync(IQueueEntry <T> entry) { // delay first abandon from maintenance (simulate timing issues which may occur to demonstrate the problem) return(base.AbandonAsync(new QueueEntry_Issue239 <T>(entry))); }
public QueueEntry_Issue239(IQueueEntry <T> queueEntry) { _queueEntry = queueEntry; }
protected async Task ReportProgress(IWorkItemHandler handler, IQueueEntry<WorkItemData> queueEntry, int progress = 0, string message = null) { try { await _messageBus.PublishAsync(new WorkItemStatus { WorkItemId = queueEntry.Value.WorkItemId, Type = queueEntry.Value.Type, Progress = progress, Message = message }).AnyContext(); } catch (Exception ex) { handler.Log.Error(ex, "Error sending progress report: {0}", ex.Message); } }