private Task ContinueTaskQueue(Task task, BasicMessage message, TKey key, TValue item) { return(task.ContinueWith(async t => { if (!t.IsCompletedSuccessfully) { message.Nack(); throw new Exception($"Processing chain aborted for {key}"); } try { await ProcessAsync(item); message.Ack(); } catch (Exception e) { _logger.LogError(e, $"Couldn't process: {e.Message} key: {key} tag: ({message.DeliveryTag})"); if (e is AggregateException agg) { foreach (var ex in agg.InnerExceptions) { _logger.LogError(ex, ex.Message); } } message.ErrorAction(); throw; } }).Unwrap()); }
/// <summary> /// This method is run sequentially. The number of tasks will be dependent on the prefetch setting in Rabbit. /// </summary> /// <param name="message"></param> /// <returns></returns> public bool Process(BasicMessage message) { CleanUpTasks(); try { var item = Get(message); var key = GetKey(item); _logger.LogDebug($"Processing message for {key}"); if (key == null) { _logger.LogInformation($"Message ignored {message.Properties?.MessageId} -> {message.Body}, no key"); return(true); } if (!_tasks.TryGetValue(key, out var task)) { task = Task.CompletedTask; } // save the task at the end of the queues. var tailTask = ContinueTaskQueue(task, message, key, item); _tasks[key] = tailTask; // add completed task to the to be used. return(false); } catch (Exception e) { _logger.LogError(e, $"Error Processing message {message.Body}, {e.Message}"); throw; } }
private void ReceiveEvent(object sender, BasicDeliverEventArgs args) { var channel = (sender as EventingBasicConsumer)?.Model; if (channel == null) { throw new ArgumentNullException(nameof(sender), "Model null in received consumer event."); } var message = new BasicMessage(args, channel, _queueServiceParams.QueueName, () => OnError(sender, args)); try { if (_handler.Process(message)) { message.Ack(); } _retryCount = 0; } catch (Exception ex) { // error processing message _logger.LogError(ex, $"{ex.Message} -> {args.DeliveryTag}: {args.BasicProperties.MessageId}"); message?.ErrorAction?.Invoke(); } }
/// <summary> /// Must be provided to decompose the message to a TValue e.g. perform any deserialisation or object creation. /// </summary> /// <param name="message"></param> /// <returns>The decomposed message</returns> protected abstract TValue Get(BasicMessage message);