protected override Task UpdateQueuedMessage(SQLQueuedMessage queuedMessage, DateTime? acknowledged, DateTime? abandoned, int attempts) { CheckDisposed(); var op = new SQLiteOperation(() => base.UpdateQueuedMessage(queuedMessage, acknowledged, abandoned, attempts)); _operationQueue.Post(op); return op.Task; }
public SQLQueuedMessageContext(SQLQueuedMessage queuedMessage) { _queuedMessage = queuedMessage; }
protected virtual Task UpdateQueuedMessage(SQLQueuedMessage queuedMessage, DateTime? acknowledged, DateTime? abandoned, int attempts) { var connection = _connectionProvider.GetConnection(); try { using (var scope = new TransactionScope(TransactionScopeOption.Required)) { using (var command = connection.CreateCommand()) { command.CommandType = CommandType.Text; command.CommandText = _dialect.UpdateQueuedMessageCommand; command.SetParameter(_dialect.MessageIdParameterName, (Guid) queuedMessage.Message.Headers.MessageId); command.SetParameter(_dialect.QueueNameParameterName, (string) _queueName); command.SetParameter(_dialect.AcknowledgedParameterName, acknowledged); command.SetParameter(_dialect.AbandonedParameterName, abandoned); command.SetParameter(_dialect.AttemptsParameterName, attempts); command.ExecuteNonQuery(); } scope.Complete(); } } finally { _connectionProvider.ReleaseConnection(connection); } // SQL calls are not async to avoid the need for TransactionAsyncFlowOption // and dependency on .NET 4.5.1 and later return Task.FromResult(true); }
protected virtual Task<IEnumerable<SQLQueuedMessage>> SelectQueuedMessages() { var queuedMessages = new List<SQLQueuedMessage>(); var connection = _connectionProvider.GetConnection(); try { using (var scope = new TransactionScope(TransactionScopeOption.Required)) { using (var command = connection.CreateCommand()) { command.CommandType = CommandType.Text; command.CommandText = _dialect.SelectQueuedMessagesCommand; command.SetParameter(_dialect.QueueNameParameterName, (string) _queueName); using (var reader = command.ExecuteReader()) { while (reader.Read()) { var messageContent = reader.GetString("MessageContent"); var headers = DeserializeHeaders(reader.GetString("Headers")); var senderPrincipal = DeserializePrincipal(reader.GetString("SenderPrincipal")); var message = new Message(headers, messageContent); var attempts = reader.GetInt("Attempts").GetValueOrDefault(0); var queuedMessage = new SQLQueuedMessage(message, senderPrincipal, attempts); queuedMessages.Add(queuedMessage); } } } scope.Complete(); } } finally { _connectionProvider.ReleaseConnection(connection); } // SQL calls are not async to avoid the need for TransactionAsyncFlowOption // and dependency on .NET 4.5.1 and later return Task.FromResult(queuedMessages.AsEnumerable()); }
protected virtual Task<SQLQueuedMessage> InsertQueuedMessage(Message message, IPrincipal senderPrincipal) { SQLQueuedMessage queuedMessage; var connection = _connectionProvider.GetConnection(); try { using (var scope = new TransactionScope(TransactionScopeOption.Required)) { using (var command = connection.CreateCommand()) { command.CommandType = CommandType.Text; command.CommandText = _dialect.InsertQueuedMessageCommand; var headers = message.Headers; command.SetParameter(_dialect.MessageIdParameterName, (Guid) headers.MessageId); command.SetParameter(_dialect.QueueNameParameterName, (string) _queueName); command.SetParameter(_dialect.MessageNameParameterName, (string) headers.MessageName); command.SetParameter(_dialect.OriginationParameterName, headers.Origination == null ? null : headers.Origination.ToString()); command.SetParameter(_dialect.DestinationParameterName, headers.Destination == null ? null : headers.Destination.ToString()); command.SetParameter(_dialect.ReplyToParameterName, headers.ReplyTo == null ? null : headers.ReplyTo.ToString()); command.SetParameter(_dialect.ExpiresParameterName, headers.Expires); command.SetParameter(_dialect.ContentTypeParameterName, headers.ContentType); command.SetParameter(_dialect.SenderPrincipalParameterName, SerializePrincipal(senderPrincipal)); command.SetParameter(_dialect.HeadersParameterName, SerializeHeaders(headers)); command.SetParameter(_dialect.MessageContentParameterName, message.Content); command.ExecuteNonQuery(); } scope.Complete(); } queuedMessage = new SQLQueuedMessage(message, senderPrincipal); } finally { _connectionProvider.ReleaseConnection(connection); } // SQL calls are not async to avoid the need for TransactionAsyncFlowOption // and dependency on .NET 4.5.1 and later return Task.FromResult(queuedMessage); }
// ReSharper disable once UnusedMethodReturnValue.Local protected async Task ProcessQueuedMessage(SQLQueuedMessage queuedMessage, CancellationToken cancellationToken) { var messageId = queuedMessage.Message.Headers.MessageId; var attemptCount = queuedMessage.Attempts; var abandoned = false; // ReSharper disable once ConditionIsAlwaysTrueOrFalse while (!abandoned && attemptCount < _maxAttempts) { attemptCount++; Log.DebugFormat("Processing queued message {0} (attempt {1} of {2})...", messageId, attemptCount, _maxAttempts); var context = new SQLQueuedMessageContext(queuedMessage); cancellationToken.ThrowIfCancellationRequested(); await _concurrentMessageProcessingSlot.WaitAsync(cancellationToken); cancellationToken.ThrowIfCancellationRequested(); try { var message = queuedMessage.Message; await _listener.MessageReceived(message, context, cancellationToken); if (_autoAcknowledge && !context.Acknowledged) { await context.Acknowledge(); } } catch (Exception ex) { Log.WarnFormat("Unhandled exception handling queued message {0}", ex, messageId); } finally { _concurrentMessageProcessingSlot.Release(); } if (context.Acknowledged) { Log.DebugFormat("Message acknowledged. Marking message {0} as acknowledged...", messageId); // TODO: Implement journaling await UpdateQueuedMessage(queuedMessage, DateTime.UtcNow, null, attemptCount); Log.DebugFormat("Message {0} acknowledged successfully", messageId); return; } if (attemptCount >= _maxAttempts) { Log.WarnFormat("Maximum attempts to proces message {0} exceeded", messageId); abandoned = true; } if (abandoned) { await UpdateQueuedMessage(queuedMessage, null, DateTime.UtcNow, attemptCount); return; } await UpdateQueuedMessage(queuedMessage, null, null, attemptCount); Log.DebugFormat("Message not acknowledged. Retrying in {0}...", _retryDelay); await Task.Delay(_retryDelay, cancellationToken); } }