public async Task Should_route_to_redirect_route_if_exists() { const string redirectAddress = "a different destination"; var failedMessage = await CreateFailedMessage(); var message = CreateEditMessage(failedMessage.UniqueMessageId); using (var session = Store.OpenAsyncSession()) { var redirects = await MessageRedirectsCollection.GetOrCreate(session); redirects.Redirects.Add(new MessageRedirect { FromPhysicalAddress = failedMessage.ProcessingAttempts.Last().FailureDetails.AddressOfFailingEndpoint, ToPhysicalAddress = redirectAddress }); await redirects.Save(session); } await Handler.Handle(message, new TestableInvokeHandlerContext()); var sentMessage = Dispatcher.DispatchedMessages.Single().Item1; Assert.AreEqual(redirectAddress, sentMessage.Destination); }
public async Task <HttpResponseMessage> DeleteRedirect(Guid messageRedirectId) { using (var session = documentStore.OpenAsyncSession()) { var redirects = await MessageRedirectsCollection.GetOrCreate(session).ConfigureAwait(false); var messageRedirect = redirects[messageRedirectId]; if (messageRedirect == null) { return(Request.CreateResponse(HttpStatusCode.NoContent)); } redirects.Redirects.Remove(messageRedirect); await redirects.Save(session).ConfigureAwait(false); await domainEvents.Raise(new MessageRedirectRemoved { MessageRedirectId = messageRedirectId, FromPhysicalAddress = messageRedirect.FromPhysicalAddress, ToPhysicalAddress = messageRedirect.ToPhysicalAddress }).ConfigureAwait(false); } return(Request.CreateResponse(HttpStatusCode.NoContent)); }
public static IOrderedEnumerable <MessageRedirect> Sort(this MessageRedirectsCollection source, HttpRequestMessage request, string defaultSortDirection = "desc") { var query = request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value); if (!query.TryGetValue("direction", out var direction)) { direction = defaultSortDirection; } if (direction != "asc" && direction != "desc") { direction = defaultSortDirection; } if (!query.TryGetValue("sort", out var sort)) { sort = "from_physical_address"; } if (!SortOptions.Contains(sort)) { sort = "from_physical_address"; } if (sort == "to_physical_address") { return(direction == "asc" ? source.Redirects.OrderBy(r => r.ToPhysicalAddress) : source.Redirects.OrderByDescending(r => r.ToPhysicalAddress)); } return(direction == "asc" ? source.Redirects.OrderBy(r => r.FromPhysicalAddress) : source.Redirects.OrderByDescending(r => r.FromPhysicalAddress)); }
private bool MoveStagedBatchesToForwardingBatch(IDocumentSession session) { isRecoveringFromPrematureShutdown = false; var stagingBatch = session.Query <RetryBatch>() .Customize(q => q.Include <RetryBatch, FailedMessageRetry>(b => b.FailureRetries)) .FirstOrDefault(b => b.Status == RetryBatchStatus.Staging); if (stagingBatch != null) { redirects = MessageRedirectsCollection.GetOrCreate(session); var stagedMessages = Stage(stagingBatch, session); var skippedMessages = stagingBatch.InitialBatchSize - stagedMessages; retryingManager.Skip(stagingBatch.RequestId, stagingBatch.RetryType, skippedMessages); if (stagedMessages > 0) { session.Store(new RetryBatchNowForwarding { RetryBatchId = stagingBatch.Id }, RetryBatchNowForwarding.Id); } return(true); } return(false); }
public async Task <HttpResponseMessage> NewRedirects(MessageRedirectRequest request) { if (string.IsNullOrWhiteSpace(request.fromphysicaladdress) || string.IsNullOrWhiteSpace(request.tophysicaladdress)) { return(Request.CreateResponse(HttpStatusCode.BadRequest)); } var messageRedirect = new MessageRedirect { FromPhysicalAddress = request.fromphysicaladdress, ToPhysicalAddress = request.tophysicaladdress, LastModifiedTicks = DateTime.UtcNow.Ticks }; using (var session = documentStore.OpenAsyncSession()) { var collection = await MessageRedirectsCollection.GetOrCreate(session).ConfigureAwait(false); var existing = collection[messageRedirect.MessageRedirectId]; if (existing != null) { return(existing.ToPhysicalAddress == messageRedirect.ToPhysicalAddress ? Negotiator.FromModel(Request, messageRedirect, HttpStatusCode.Created) : Negotiator.FromModel(Request, existing, HttpStatusCode.Conflict).WithReasonPhrase("Duplicate")); } var dependents = collection.Redirects.Where(r => r.ToPhysicalAddress == request.fromphysicaladdress).ToList(); if (dependents.Any()) { return(Negotiator.FromModel(Request, dependents, HttpStatusCode.Conflict).WithReasonPhrase("Dependents")); } collection.Redirects.Add(messageRedirect); await collection.Save(session).ConfigureAwait(false); } await domainEvents.Raise(new MessageRedirectCreated { MessageRedirectId = messageRedirect.MessageRedirectId, FromPhysicalAddress = messageRedirect.FromPhysicalAddress, ToPhysicalAddress = messageRedirect.ToPhysicalAddress }).ConfigureAwait(false); if (request.retryexisting) { await messageSession.SendLocal(new RetryPendingMessages { QueueAddress = messageRedirect.FromPhysicalAddress, PeriodFrom = DateTime.MinValue, PeriodTo = DateTime.UtcNow }).ConfigureAwait(false); } return(Request.CreateResponse(HttpStatusCode.Created)); }
public async Task <HttpResponseMessage> CountRedirects() { using (var session = documentStore.OpenAsyncSession()) { var redirects = await MessageRedirectsCollection.GetOrCreate(session).ConfigureAwait(false); return(Request.CreateResponse(HttpStatusCode.OK) .WithEtag(redirects.ETag) .WithTotalCount(redirects.Redirects.Count)); } }
static string ApplyRedirect(string addressOfFailingEndpoint, MessageRedirectsCollection redirects) { var redirect = redirects[addressOfFailingEndpoint]; if (redirect != null) { addressOfFailingEndpoint = redirect.ToPhysicalAddress; } return(addressOfFailingEndpoint); }
private async Task <bool> MoveStagedBatchesToForwardingBatch(IAsyncDocumentSession session) { try { if (Log.IsDebugEnabled) { Log.Debug("Looking for batch to stage."); } isRecoveringFromPrematureShutdown = false; var stagingBatch = await session.Query <RetryBatch>() .Customize(q => q.Include <RetryBatch, FailedMessageRetry>(b => b.FailureRetries)) .FirstOrDefaultAsync(b => b.Status == RetryBatchStatus.Staging) .ConfigureAwait(false); if (stagingBatch != null) { Log.Info($"Staging batch {stagingBatch.Id}."); redirects = await MessageRedirectsCollection.GetOrCreate(session).ConfigureAwait(false); var stagedMessages = await Stage(stagingBatch, session).ConfigureAwait(false); var skippedMessages = stagingBatch.InitialBatchSize - stagedMessages; await retryingManager.Skip(stagingBatch.RequestId, stagingBatch.RetryType, skippedMessages) .ConfigureAwait(false); if (stagedMessages > 0) { Log.Info($"Batch {stagingBatch.Id} with {stagedMessages} messages staged and {skippedMessages} skipped ready to be forwarded."); await session.StoreAsync(new RetryBatchNowForwarding { RetryBatchId = stagingBatch.Id }, RetryBatchNowForwarding.Id) .ConfigureAwait(false); } return(true); } Log.Info("No batch found to stage."); return(false); } catch (RetryStagingException) { return(true); //Execute another staging attempt immediately } }
public async Task <HttpResponseMessage> UpdateRedirect(Guid messageRedirectId, MessageRedirectRequest request) { if (string.IsNullOrWhiteSpace(request.tophysicaladdress)) { return(Request.CreateResponse(HttpStatusCode.BadRequest)); } using (var session = documentStore.OpenAsyncSession()) { var redirects = await MessageRedirectsCollection.GetOrCreate(session).ConfigureAwait(false); var messageRedirect = redirects[messageRedirectId]; if (messageRedirect == null) { return(Request.CreateResponse(HttpStatusCode.NotFound)); } var toMessageRedirectId = DeterministicGuid.MakeId(request.tophysicaladdress); if (redirects[toMessageRedirectId] != null) { return(Request.CreateResponse(HttpStatusCode.Conflict)); } var messageRedirectChanged = new MessageRedirectChanged { MessageRedirectId = messageRedirectId, PreviousToPhysicalAddress = messageRedirect.ToPhysicalAddress, FromPhysicalAddress = messageRedirect.FromPhysicalAddress, ToPhysicalAddress = messageRedirect.ToPhysicalAddress = request.tophysicaladdress }; messageRedirect.LastModifiedTicks = DateTime.UtcNow.Ticks; await redirects.Save(session).ConfigureAwait(false); await domainEvents.Raise(messageRedirectChanged) .ConfigureAwait(false); return(Request.CreateResponse(HttpStatusCode.NoContent)); } }
public static IOrderedEnumerable <MessageRedirect> Sort(this MessageRedirectsCollection source, Request request, string defaultSortDirection = "desc") { var direction = defaultSortDirection; if (request.Query.direction.HasValue) { direction = (string)request.Query.direction; } if (direction != "asc" && direction != "desc") { direction = defaultSortDirection; } var sortOptions = new[] { "from_physical_address", "to_physical_address" }; var sort = "from_physical_address"; if (request.Query.sort.HasValue) { sort = (string)request.Query.sort; } if (!sortOptions.Contains(sort)) { sort = "from_physical_address"; } if (sort == "to_physical_address") { return(direction == "asc" ? source.Redirects.OrderBy(r => r.ToPhysicalAddress) : source.Redirects.OrderByDescending(r => r.ToPhysicalAddress)); } return(direction == "asc" ? source.Redirects.OrderBy(r => r.FromPhysicalAddress) : source.Redirects.OrderByDescending(r => r.FromPhysicalAddress)); }
public async Task <HttpResponseMessage> Redirects() { using (var session = documentStore.OpenAsyncSession()) { var redirects = await MessageRedirectsCollection.GetOrCreate(session).ConfigureAwait(false); var queryResult = redirects .Sort(Request) .Paging(Request) .Select(r => new { r.MessageRedirectId, r.FromPhysicalAddress, r.ToPhysicalAddress, LastModified = new DateTime(r.LastModifiedTicks) }); return(Negotiator .FromModel(Request, queryResult) .WithEtag(redirects.ETag) .WithPagingLinksAndTotalCount(redirects.Redirects.Count, Request)); } }
public MessageRedirectsModule() { Post["/redirects"] = parameters => { var request = this.Bind <MessageRedirectRequest>(); if (string.IsNullOrWhiteSpace(request.fromphysicaladdress) || string.IsNullOrWhiteSpace(request.tophysicaladdress)) { return(HttpStatusCode.BadRequest); } var messageRedirect = new MessageRedirect { FromPhysicalAddress = request.fromphysicaladdress, ToPhysicalAddress = request.tophysicaladdress, LastModifiedTicks = DateTime.UtcNow.Ticks }; using (var session = Store.OpenSession()) { var collection = MessageRedirectsCollection.GetOrCreate(session); var existing = collection[messageRedirect.MessageRedirectId]; if (existing != null) { return(existing.ToPhysicalAddress == messageRedirect.ToPhysicalAddress ? Negotiate.WithModel(messageRedirect).WithStatusCode(HttpStatusCode.Created) : Negotiate.WithReasonPhrase("Duplicate").WithModel(existing).WithStatusCode(HttpStatusCode.Conflict)); } var dependents = collection.Redirects.Where(r => r.ToPhysicalAddress == request.fromphysicaladdress).ToList(); if (dependents.Any()) { return(Negotiate.WithReasonPhrase("Dependents").WithModel(dependents).WithStatusCode(HttpStatusCode.Conflict)); } collection.Redirects.Add(messageRedirect); collection.Save(session); } Bus.Publish(new MessageRedirectCreated { MessageRedirectId = messageRedirect.MessageRedirectId, FromPhysicalAddress = messageRedirect.FromPhysicalAddress, ToPhysicalAddress = messageRedirect.ToPhysicalAddress }); if (request.retryexisting) { Bus.SendLocal(new RetryPendingMessages { QueueAddress = messageRedirect.FromPhysicalAddress, PeriodFrom = DateTime.MinValue, PeriodTo = DateTime.UtcNow }); } return(HttpStatusCode.Created); }; Put["/redirects/{messageredirectid:guid}/"] = parameters => { Guid messageRedirectId = parameters.messageredirectid; var request = this.Bind <MessageRedirectRequest>(); if (string.IsNullOrWhiteSpace(request.tophysicaladdress)) { return(HttpStatusCode.BadRequest); } using (var session = Store.OpenSession()) { var redirects = MessageRedirectsCollection.GetOrCreate(session); var messageRedirect = redirects[messageRedirectId]; if (messageRedirect == null) { return(HttpStatusCode.NotFound); } var toMessageRedirectId = DeterministicGuid.MakeId(request.tophysicaladdress); if (redirects[toMessageRedirectId] != null) { return(HttpStatusCode.Conflict); } var messageRedirectChanged = new MessageRedirectChanged { MessageRedirectId = messageRedirectId, PreviousToPhysicalAddress = messageRedirect.ToPhysicalAddress, FromPhysicalAddress = messageRedirect.FromPhysicalAddress, ToPhysicalAddress = messageRedirect.ToPhysicalAddress = request.tophysicaladdress, }; messageRedirect.LastModifiedTicks = DateTime.UtcNow.Ticks; redirects.Save(session); Bus.Publish(messageRedirectChanged); return(HttpStatusCode.NoContent); } }; Delete["/redirects/{messageredirectid:guid}/"] = parameters => { Guid messageRedirectId = parameters.messageredirectid; using (var session = Store.OpenSession()) { var redirects = MessageRedirectsCollection.GetOrCreate(session); var messageRedirect = redirects[messageRedirectId]; if (messageRedirect == null) { return(HttpStatusCode.NoContent); } redirects.Redirects.Remove(messageRedirect); redirects.Save(session); Bus.Publish <MessageRedirectRemoved>(evt => { evt.MessageRedirectId = messageRedirectId; evt.FromPhysicalAddress = messageRedirect.FromPhysicalAddress; evt.ToPhysicalAddress = messageRedirect.ToPhysicalAddress; }); } return(HttpStatusCode.NoContent); }; Head["/redirects"] = _ => { using (var session = Store.OpenSession()) { var redirects = MessageRedirectsCollection.GetOrCreate(session); return(Negotiate .WithEtagAndLastModified(redirects.ETag, redirects.LastModified) .WithTotalCount(redirects.Redirects.Count)); } }; Get["/redirects"] = _ => { using (var session = Store.OpenSession()) { var redirects = MessageRedirectsCollection.GetOrCreate(session); var queryResult = redirects .Sort(Request) .Paging(Request) .Select(r => new { r.MessageRedirectId, r.FromPhysicalAddress, r.ToPhysicalAddress, LastModified = new DateTime(r.LastModifiedTicks) }); return(Negotiate .WithModel(queryResult) .WithEtagAndLastModified(redirects.ETag, redirects.LastModified) .WithPagingLinksAndTotalCount(redirects.Redirects.Count, Request)); } }; }
public async Task Handle(EditAndSend message, IMessageHandlerContext context) { FailedMessage failedMessage; MessageRedirectsCollection redirects; using (var session = store.OpenAsyncSession()) { failedMessage = await session.LoadAsync <FailedMessage>(FailedMessage.MakeDocumentId(message.FailedMessageId)) .ConfigureAwait(false); if (failedMessage == null) { log.WarnFormat("Discarding edit {0} because no message failure for id {1} has been found.", context.MessageId, message.FailedMessageId); return; } var edit = await session.LoadAsync <FailedMessageEdit>(FailedMessageEdit.MakeDocumentId(message.FailedMessageId)) .ConfigureAwait(false); if (edit == null) { if (failedMessage.Status != FailedMessageStatus.Unresolved) { log.WarnFormat("Discarding edit {0} because message failure {1} doesn't have state 'Unresolved'.", context.MessageId, message.FailedMessageId); return; } // create a retries document to prevent concurrent edits await session.StoreAsync(new FailedMessageEdit { Id = FailedMessageEdit.MakeDocumentId(message.FailedMessageId), FailedMessageId = message.FailedMessageId, EditId = context.MessageId }, Etag.Empty).ConfigureAwait(false); } else if (edit.EditId != context.MessageId) { log.WarnFormat("Discarding edit {0} because the failure ({1}) has already been edited by edit {2}", context.MessageId, message.FailedMessageId, edit.EditId); return; } // the original failure is marked as resolved as any failures of the edited message are treated as a new message failure. failedMessage.Status = FailedMessageStatus.Resolved; redirects = await MessageRedirectsCollection.GetOrCreate(session) .ConfigureAwait(false); await session.SaveChangesAsync().ConfigureAwait(false); } var attempt = failedMessage.ProcessingAttempts.Last(); var outgoingMessage = BuildMessage(message); // mark the new message with a link to the original message id outgoingMessage.Headers.Add("ServiceControl.EditOf", message.FailedMessageId); var address = ApplyRedirect(attempt.FailureDetails.AddressOfFailingEndpoint, redirects); await DispatchEditedMessage(outgoingMessage, address, context) .ConfigureAwait(false); }