/// <summary> /// Creates a new <see cref="UnicastTransportOperation" /> instance. /// </summary> public UnicastTransportOperation(OutgoingMessage message, string destination, DispatchProperties properties, DispatchConsistency requiredDispatchConsistency = DispatchConsistency.Default) { Message = message; Destination = destination; Properties = properties; RequiredDispatchConsistency = requiredDispatchConsistency; }
protected Task SendMessage( string address, Dictionary <string, string> headers = null, TransportTransaction transportTransaction = null, DispatchProperties dispatchProperties = null, DispatchConsistency dispatchConsistency = DispatchConsistency.Default, byte[] body = null) { var messageId = Guid.NewGuid().ToString(); var message = new OutgoingMessage(messageId, headers ?? new Dictionary <string, string>(), body ?? Array.Empty <byte>()); if (message.Headers.ContainsKey(TestIdHeaderName) == false) { message.Headers.Add(TestIdHeaderName, testId); } if (transportTransaction == null) { transportTransaction = new TransportTransaction(); } var transportOperation = new TransportOperation(message, new UnicastAddressTag(address), dispatchProperties, dispatchConsistency); return(transportInfrastructure.Dispatcher.Dispatch(new TransportOperations(transportOperation), transportTransaction)); }
static bool CalculateDelay(DispatchProperties dispatchProperties, out long delay) { delay = 0; var delayed = false; if (dispatchProperties.DoNotDeliverBefore != null) { delayed = true; delay = Convert.ToInt64(Math.Ceiling((dispatchProperties.DoNotDeliverBefore.At - DateTimeOffset.UtcNow).TotalSeconds)); if (delay > DelayInfrastructure.MaxDelayInSeconds) { throw new Exception($"Message cannot set to be delivered at '{dispatchProperties.DoNotDeliverBefore.At}' because the delay specified via {nameof(DelayedDeliveryOptionExtensions.DoNotDeliverBefore)} exceeds the maximum delay value '{TimeSpan.FromSeconds(DelayInfrastructure.MaxDelayInSeconds)}'."); } } else if (dispatchProperties.DelayDeliveryWith != null) { delayed = true; delay = Convert.ToInt64(Math.Ceiling(dispatchProperties.DelayDeliveryWith.Delay.TotalSeconds)); if (delay > DelayInfrastructure.MaxDelayInSeconds) { throw new Exception($"Message cannot be delayed by '{dispatchProperties.DelayDeliveryWith.Delay}' because the delay specified via {nameof(DelayedDeliveryOptionExtensions.DelayDeliveryWith)} exceeds the maximum delay value '{TimeSpan.FromSeconds(DelayInfrastructure.MaxDelayInSeconds)}'."); } } return(delayed); }
public override Task Invoke(IAuditContext context, Func <IRoutingContext, Task> stage) { var message = context.Message; if (context.Extensions.TryGet(out State state)) { //transfer audit values to the headers of the message to audit foreach (var kvp in state.AuditValues) { message.Headers[kvp.Key] = kvp.Value; } } var dispatchProperties = new DispatchProperties(); if (timeToBeReceived.HasValue) { dispatchProperties.DiscardIfNotReceivedBefore = new DiscardIfNotReceivedBefore(timeToBeReceived.Value); } var dispatchContext = this.CreateRoutingContext(context.Message, new UnicastRoutingStrategy(context.AuditAddress), context); dispatchContext.Extensions.Set(dispatchProperties); return(stage(dispatchContext)); }
public override Task Invoke(IRoutingContext context, Func <IDispatchContext, Task> stage) { var state = context.Extensions.GetOrCreate <State>(); var dispatchConsistency = state.ImmediateDispatch ? DispatchConsistency.Isolated : DispatchConsistency.Default; var operations = new TransportOperation[context.RoutingStrategies.Count]; var index = 0; foreach (var strategy in context.RoutingStrategies) { var addressLabel = strategy.Apply(context.Message.Headers); var message = new OutgoingMessage(context.Message.MessageId, context.Message.Headers, context.Message.Body); if (!context.Extensions.TryGet(out DispatchProperties dispatchProperties)) { dispatchProperties = new DispatchProperties(); } operations[index] = new TransportOperation(message, addressLabel, dispatchProperties, dispatchConsistency); index++; } if (isDebugEnabled) { LogOutgoingOperations(operations); } if (!state.ImmediateDispatch && context.Extensions.TryGet(out PendingTransportOperations pendingOperations)) { pendingOperations.AddRange(operations); return(Task.CompletedTask); } return(stage(this.CreateDispatchContext(operations, context))); }
public async Task Should_Reschedule_Timeouts() { var expectedDeliveryAt = new DateTimeOffset(new DateTime(2020, 1, 1)); var scenarioContext = new IntegrationScenarioContext(); scenarioContext.RegisterTimeoutRescheduleRule <AMessage>((msg, currentDelay) => { return(new DoNotDeliverBefore(expectedDeliveryAt)); }); var context = new TestableOutgoingSendContext { Message = new OutgoingLogicalMessage(typeof(AMessage), new AMessage()) }; context.Headers.Add(Headers.SagaId, "a-saga-id"); context.Headers.Add(Headers.SagaType, "a-saga-type"); context.Headers.Add(Headers.IsSagaTimeoutMessage, bool.TrueString); var properties = new DispatchProperties { DoNotDeliverBefore = new DoNotDeliverBefore(new DateTime(2030, 1, 1)) }; context.Extensions.Set(properties); var sut = new RescheduleTimeoutsBehavior(scenarioContext);; await sut.Invoke(context, () => Task.CompletedTask).ConfigureAwait(false); var rescheduledDoNotDeliverBefore = properties.DoNotDeliverBefore; Assert.AreEqual(expectedDeliveryAt, rescheduledDoNotDeliverBefore.At); }
internal static bool?ShouldUseDeadLetterQueue(this DispatchProperties dispatchProperties) { if (dispatchProperties.TryGetValue(KeyDeadLetterQueue, out var boolString)) { return(bool.Parse(boolString)); } return(null); }
/// <summary> /// Creates a new instance of a <see cref="TransportOperation" />. /// </summary> public TransportOperation(string messageId, DispatchProperties properties, byte[] body, Dictionary <string, string> headers) { Guard.AgainstNullAndEmpty(nameof(messageId), messageId); MessageId = messageId; Options = properties; Body = body; Headers = headers; }
public static Message Convert(OutgoingMessage message, DispatchProperties dispatchProperties) { var result = new Message(); if (message.Body != null) { result.BodyStream = new MemoryStream(message.Body); } AssignMsmqNativeCorrelationId(message, result); result.Recoverable = true; if (dispatchProperties.DiscardIfNotReceivedBefore?.MaxTime < MessageQueue.InfiniteTimeout) { result.TimeToBeReceived = dispatchProperties.DiscardIfNotReceivedBefore.MaxTime; } var addCorrIdHeader = !message.Headers.ContainsKey("CorrId"); using (var stream = new MemoryStream()) { var headers = message.Headers.Select(pair => new HeaderInfo { Key = pair.Key, Value = pair.Value }).ToList(); if (addCorrIdHeader) { headers.Add(new HeaderInfo { Key = "CorrId", Value = result.CorrelationId }); } headerSerializer.Serialize(stream, headers); result.Extension = stream.ToArray(); } var messageIntent = default(MessageIntentEnum); if (message.Headers.TryGetValue(Headers.MessageIntent, out var messageIntentString)) { Enum.TryParse(messageIntentString, true, out messageIntent); } result.AppSpecific = (int)messageIntent; return(result); }
public async Task Should_default_dlq_to_off_for_messages_with_ttbr() { var dispatchProperties = new DispatchProperties { DiscardIfNotReceivedBefore = new DiscardIfNotReceivedBefore(TimeSpan.FromMinutes(10)) }; var dispatchedMessage = await DispatchMessage("dlqOffForTTBR", dispatchProperties : dispatchProperties); Assert.False(dispatchedMessage.UseDeadLetterQueue); }
public void Should_use_the_TTBR_in_the_send_options_if_set() { var properties = new DispatchProperties { DiscardIfNotReceivedBefore = new DiscardIfNotReceivedBefore(TimeSpan.FromDays(1)) }; var message = MsmqUtilities.Convert(new OutgoingMessage("message id", new Dictionary <string, string>(), new byte[0]), properties); Assert.AreEqual(TimeSpan.FromDays(1), message.TimeToBeReceived); }
public async Task Should_allow_optin_for_dlq_on_ttbr_messages() { var transportSettings = new MsmqTransport { UseDeadLetterQueueForMessagesWithTimeToBeReceived = true }; var dispatchProperties = new DispatchProperties { DiscardIfNotReceivedBefore = new DiscardIfNotReceivedBefore(TimeSpan.FromMinutes(10)) }; var dispatchedMessage = await DispatchMessage("dlqOnForTTBR", transportSettings, dispatchProperties); Assert.True(dispatchedMessage.UseDeadLetterQueue); }
static void ApplyDeliveryConstraints(ServiceBusMessage message, DispatchProperties dispatchProperties) { if (dispatchProperties.DoNotDeliverBefore != null) { message.ScheduledEnqueueTime = dispatchProperties.DoNotDeliverBefore.At; } else if (dispatchProperties.DelayDeliveryWith != null) { // Delaying with TimeSpan is currently not supported, see https://github.com/Azure/azure-service-bus-dotnet/issues/160 message.ScheduledEnqueueTime = DateTimeOffset.UtcNow + dispatchProperties.DelayDeliveryWith.Delay; } if (dispatchProperties.DiscardIfNotReceivedBefore != null) { message.TimeToLive = dispatchProperties.DiscardIfNotReceivedBefore.MaxTime; } }
static TimeSpan?GetDeliveryDelay(DispatchProperties properties) { var doNotDeliverBefore = properties.DoNotDeliverBefore; if (doNotDeliverBefore != null) { return(ToNullIfNegative(doNotDeliverBefore.At - DateTimeOffset.UtcNow)); } var delay = properties.DelayDeliveryWith; if (delay != null) { return(ToNullIfNegative(delay.Delay)); } return(null); }
async Task BreakConnectionBySendingInvalidMessage(CancellationToken cancellationToken) { try { var outgoingMessage = new OutgoingMessage("Foo", new Dictionary <string, string>(), new byte[0]); var props = new DispatchProperties { DiscardIfNotReceivedBefore = new DiscardIfNotReceivedBefore(TimeSpan.FromMilliseconds(-1)) }; var operation = new TransportOperation(outgoingMessage, new UnicastAddressTag(settings.EndpointName()), props); await sender.Dispatch(new TransportOperations(operation), new TransportTransaction(), cancellationToken); } catch (Exception ex) when(!ex.IsCausedBy(cancellationToken)) { // Don't care } }
public async Task <int> Retry(IncomingMessage message, TimeSpan delay, TransportTransaction transportTransaction, CancellationToken cancellationToken = default) { var outgoingMessage = new OutgoingMessage(message.MessageId, new Dictionary <string, string>(message.Headers), message.Body); var currentDelayedRetriesAttempt = message.GetDelayedDeliveriesPerformed() + 1; outgoingMessage.SetCurrentDelayedDeliveries(currentDelayedRetriesAttempt); outgoingMessage.SetDelayedDeliveryTimestamp(DateTimeOffset.UtcNow); var dispatchProperties = new DispatchProperties { DelayDeliveryWith = new DelayDeliveryWith(delay) }; var messageDestination = new UnicastAddressTag(endpointInputQueue); var transportOperations = new TransportOperations(new TransportOperation(outgoingMessage, messageDestination, dispatchProperties)); await dispatcher.Dispatch(transportOperations, transportTransaction, cancellationToken).ConfigureAwait(false); return(currentDelayedRetriesAttempt); }
public async Task Should_honor_stored_delivery_constraints() { var messageId = "id"; var options = new DispatchProperties(); var deliverTime = DateTimeOffset.UtcNow.AddDays(1); var maxTime = TimeSpan.FromDays(1); options["Destination"] = "test"; options["DeliverAt"] = DateTimeOffsetHelper.ToWireFormattedString(deliverTime); options["DelayDeliveryFor"] = TimeSpan.FromSeconds(10).ToString(); options["TimeToBeReceived"] = maxTime.ToString(); fakeOutbox.ExistingMessage = new OutboxMessage(messageId, new[] { new NServiceBus.Outbox.TransportOperation("x", options, new byte[0], new Dictionary <string, string>()) }); var context = CreateContext(fakeBatchPipeline, messageId); await Invoke(context); var operationProperties = new DispatchProperties(fakeBatchPipeline.TransportOperations.First().Properties); var delayDeliveryWith = operationProperties.DelayDeliveryWith; Assert.NotNull(delayDeliveryWith); Assert.AreEqual(TimeSpan.FromSeconds(10), delayDeliveryWith.Delay); var doNotDeliverBefore = operationProperties.DoNotDeliverBefore; Assert.NotNull(doNotDeliverBefore); Assert.AreEqual(deliverTime.ToString(), doNotDeliverBefore.At.ToString()); var discard = operationProperties.DiscardIfNotReceivedBefore; Assert.NotNull(discard); Assert.AreEqual(maxTime, discard.MaxTime); Assert.Null(fakeOutbox.StoredMessage); }
public void DispatchDelayedMessage(string id, byte[] extension, ReadOnlyMemory <byte> body, string destination, TransportTransaction transportTransaction) { var headersAndProperties = MsmqUtilities.DeserializeMessageHeaders(extension); var headers = new Dictionary <string, string>(); var properties = new DispatchProperties(); foreach (var kvp in headersAndProperties) { if (kvp.Key.StartsWith(MsmqUtilities.PropertyHeaderPrefix)) { properties[kvp.Key] = kvp.Value; } else { headers[kvp.Key] = kvp.Value; } } var request = new OutgoingMessage(id, headers, body); SendToDestination(transportTransaction, new UnicastTransportOperation(request, destination, properties)); }
Task Send(byte[] body, string messageType, TimeSpan timeToBeReceived, IMessageDispatcher dispatcher, CancellationToken cancellationToken) { var headers = new Dictionary <string, string> { [Headers.EnclosedMessageTypes] = messageType, [Headers.ContentType] = ContentTypes.Json, [Headers.MessageIntent] = sendIntent }; if (localAddress != null) { headers[Headers.ReplyToAddress] = localAddress; } var outgoingMessage = new OutgoingMessage(Guid.NewGuid().ToString(), headers, body); var properties = new DispatchProperties { DiscardIfNotReceivedBefore = new DiscardIfNotReceivedBefore(timeToBeReceived) }; var operation = new TransportOperation(outgoingMessage, new UnicastAddressTag(destinationQueue), properties); return(dispatcher.Dispatch(new TransportOperations(operation), new TransportTransaction(), cancellationToken)); }
Task MessageReceivedOnChannel(MessageReceivedOnChannelArgs e, CancellationToken cancellationToken) { var body = e.Body; var headers = e.Headers; var id = e.Id; var timeToBeReceived = e.TimeToBeReceived; var destination = endpointRouter.GetDestinationFor(); Logger.Info("Sending message to " + destination); var outgoingMessage = new OutgoingMessage(id, headers, body); outgoingMessage.Headers[Headers.ReplyToAddress] = replyToAddress; var dispatchProperties = new DispatchProperties { DiscardIfNotReceivedBefore = new DiscardIfNotReceivedBefore(timeToBeReceived) }; var transportOperations = new TransportOperations(new TransportOperation(outgoingMessage, new UnicastAddressTag(destination), dispatchProperties, DispatchConsistency.Default)); return(dispatchMessages.Dispatch(transportOperations, new TransportTransaction(), cancellationToken)); }
public async Task Should_honor_stored_direct_routing() { var messageId = "id"; var properties = new DispatchProperties { ["Destination"] = "myEndpoint" }; fakeOutbox.ExistingMessage = new OutboxMessage(messageId, new[] { new NServiceBus.Outbox.TransportOperation("x", properties, new byte[0], new Dictionary <string, string>()) }); var context = CreateContext(fakeBatchPipeline, messageId); await Invoke(context); var routing = fakeBatchPipeline.TransportOperations.First().AddressTag as UnicastAddressTag; Assert.NotNull(routing); Assert.AreEqual("myEndpoint", routing.Destination); Assert.Null(fakeOutbox.StoredMessage); }
public async Task Should_honor_stored_pubsub_routing() { var messageId = "id"; var properties = new DispatchProperties { ["EventType"] = typeof(MyEvent).AssemblyQualifiedName }; fakeOutbox.ExistingMessage = new OutboxMessage(messageId, new[] { new NServiceBus.Outbox.TransportOperation("x", properties, new byte[0], new Dictionary <string, string>()) }); var context = CreateContext(fakeBatchPipeline, messageId); await Invoke(context); var routing = fakeBatchPipeline.TransportOperations.First().AddressTag as MulticastAddressTag; Assert.NotNull(routing); Assert.AreEqual(typeof(MyEvent), routing.MessageType); Assert.Null(fakeOutbox.StoredMessage); }
bool IsCombiningTimeToBeReceivedWithTransactions(TransportTransaction transaction, DispatchConsistency requiredDispatchConsistency, DispatchProperties dispatchProperties) { if (!transportSettings.UseTransactionalQueues) { return(false); } if (requiredDispatchConsistency == DispatchConsistency.Isolated) { return(false); } var timeToBeReceivedRequested = dispatchProperties.DiscardIfNotReceivedBefore?.MaxTime < MessageQueue.InfiniteTimeout; if (!timeToBeReceivedRequested) { return(false); } if (Transaction.Current != null) { return(true); } return(TryGetNativeTransaction(transaction, out _)); }
static void MergeDispatchProperties(ContextBag context, DispatchProperties dispatchProperties) { // we can't add the constraints directly to the SendOptions ContextBag as the options can be reused context.Set(new DispatchProperties(dispatchProperties)); }
public static void Fill(this IBasicProperties properties, OutgoingMessage message, DispatchProperties dispatchProperties) { if (message.MessageId != null) { properties.MessageId = message.MessageId; } var messageHeaders = message.Headers ?? new Dictionary <string, string>(); var delayed = CalculateDelay(dispatchProperties, out var delay); properties.Persistent = !messageHeaders.Remove(UseNonPersistentDeliveryHeader); properties.Headers = messageHeaders.ToDictionary(p => p.Key, p => (object)p.Value); if (delayed) { properties.Headers[DelayInfrastructure.DelayHeader] = Convert.ToInt32(delay); } if (dispatchProperties.DiscardIfNotReceivedBefore != null && dispatchProperties.DiscardIfNotReceivedBefore.MaxTime < TimeSpan.MaxValue) { // align with TimeoutManager behavior if (delayed) { throw new Exception("Postponed delivery of messages with TimeToBeReceived set is not supported. Remove the TimeToBeReceived attribute to postpone messages of this type."); } properties.Expiration = dispatchProperties.DiscardIfNotReceivedBefore.MaxTime.TotalMilliseconds.ToString(CultureInfo.InvariantCulture); } if (messageHeaders.TryGetValue(NServiceBus.Headers.CorrelationId, out var correlationId) && correlationId != null) { properties.CorrelationId = correlationId; } if (messageHeaders.TryGetValue(NServiceBus.Headers.EnclosedMessageTypes, out var enclosedMessageTypes) && enclosedMessageTypes != null) { var index = enclosedMessageTypes.IndexOf(','); if (index > -1) { properties.Type = enclosedMessageTypes.Substring(0, index); } else { properties.Type = enclosedMessageTypes; } } if (messageHeaders.TryGetValue(NServiceBus.Headers.ContentType, out var contentType) && contentType != null) { properties.ContentType = contentType; } else { properties.ContentType = "application/octet-stream"; } if (messageHeaders.TryGetValue(NServiceBus.Headers.ReplyToAddress, out var replyToAddress) && replyToAddress != null) { properties.ReplyTo = replyToAddress; } }
public static ServiceBusMessage ToAzureServiceBusMessage(this OutgoingMessage outgoingMessage, DispatchProperties dispatchProperties, string incomingQueuePartitionKey) { var message = new ServiceBusMessage(outgoingMessage.Body) { // Cannot re-use MessageId to be compatible with ASB transport that could have native de-dup enabled MessageId = Guid.NewGuid().ToString() }; // The value needs to be "application/octect-stream" and not "application/octet-stream" for interop with ASB transport message.ApplicationProperties[TransportMessageHeaders.TransportEncoding] = "application/octect-stream"; message.TransactionPartitionKey = incomingQueuePartitionKey; ApplyDeliveryConstraints(message, dispatchProperties); ApplyCorrelationId(message, outgoingMessage.Headers); ApplyContentType(message, outgoingMessage.Headers); SetReplyToAddress(message, outgoingMessage.Headers); CopyHeaders(message, outgoingMessage.Headers); return(message); }
static async Task <Message> DispatchMessage(string queueName, MsmqTransport settings = null, DispatchProperties dispatchProperties = null, CancellationToken cancellationToken = default) { if (settings == null) { settings = new MsmqTransport(); } var path = $@".\private$\{queueName}"; try { MsmqHelpers.DeleteQueue(path); MsmqHelpers.CreateQueue(path); var messageSender = new MsmqMessageDispatcher(settings, "timeouts"); var bytes = new byte[] { 1 }; var headers = new Dictionary <string, string>(); var outgoingMessage = new OutgoingMessage("1", headers, bytes); dispatchProperties = dispatchProperties ?? new DispatchProperties(); var transportOperation = new TransportOperation(outgoingMessage, new UnicastAddressTag(queueName), dispatchProperties); await messageSender.Dispatch(new TransportOperations(transportOperation), new TransportTransaction(), cancellationToken); using (var queue = new MessageQueue(path)) using (var message = queue.Receive(TimeSpan.FromSeconds(5))) { return(message); } } finally { MsmqHelpers.DeleteQueue(path); } }