Masstransit - Can we use Send req and res for valiating - c#

As per myunderstanding on masstransit
Publish: Sends a message to subscribers .
Send: Used to send messages in fire and forget fashion
Requests: uses request/reply pattern to just send a message and get a response
In my requirement i need to validate my request before calling the Send method. Here the validation should occur at DB level to check for say duplicate records.
I tried to use publish before my send method , but send method doesnt wait for the publish consumer to execute.
My scenario is if (validation is success) proceed with saving data ie the send request job to save data.
So should i use request response pattern here for doing the validation. I am a newbie to masstransit and microservice.
MyTestController{
if(validation success) // how to validate here
Send request to save data.
}

It sounds like you want to validate the data before it is sent out. Something conceptually like this.
class MyTestController
{
// ..
public async Task<IActionResult> Post(SomeData data)
{
if(DataIsValid(data))
_publishEndpoint.Publish(new Message())
}
}
You can validate the data (like null checking) like any other code before you publish. Nothing special here, so I'm guessing its something else.
You want to validate the data using some other data in a database. If its the same database that the website/api is using - that is also not a special thing, so I'm guessing that is not it either.
You want to, some how, validate the data is correct before sending the message. But that you need to use the data of the application that the message is going to. That is typically where I see people get tripped up.
Assuming its something like number three. Let's call the sending service "Service A" and the receiving service "Service B". Today it sounds like you are trying to put the validation in "Service A" but it really has to be in "Service B". What I would do is implement a Saga in "Service B". The first step would be to take request (creating an instance of a saga), then validate the data, then if it passes validation, the saga can take the next step that you want in the process. That should give you what you want in terms of validation before action (we just need to move it to "Service B").
Now "Service B" can expose the state of the Saga at an endpoint like /saga-instance/42 where the controller takes the 42, digs into the database, grabs the saga data and converts it into an API response. Service A can poll that endpoint to get updated status details.
Ultimately, I hope you see that there are a lot of variables at play, but that there is a path forward. You may have to simply adjust where certain actions are taken.

Related

Deferring and re-receiving a deferred message in an IHostBuilder hosted service

If the processing of an Azure Service Bus message depends on another resource, e.g. an API or a database service, and this resource is not available, not calling CompleteMessageAsync() is not an option, because the message will be immediately received again until the Max Delivery Count is reached, and then put into the DLQ. If an API is down for maintenance, we want to wait a bit before retrying.
One of the answers to this question has the general steps for deferring and receiving deferred messages. This is a little better than Microsoft's documentation, but not enough for me to understand the intent of the API, and how it is to be implemented in a hosted service that basically sits in ServiceBusProcessor.StartProcessingAsync all day long.
This is the basic structure of my service:
public class ServiceBusWatcher : IHostedService, IDisposable
{
public Task StartAsync(CancellationToken stoppingToken)
{
ReceiveMessagesAsync();
return Task.CompletedTask;
}
private async void ReceiveMessagesAsync()
{
ServiceBusClient client = new ServiceBusClient(connectionString);
processor = client.CreateProcessor(queueName, new ServiceBusProcessorOptions());
processor.ProcessMessageAsync += MessageHandler;
await processor.StartProcessingAsync();
}
async Task MessageHandler(ProcessMessageEventArgs args)
{
// a dependency is not available that allows me to process a message. so:
await args.DeferMessageAsync(args.Message);
Once the message is deferred, it is my understanding that the processor will not get to it anymore (or will it?). Instead, I have to use ReceiveDeferredMessageAsync() to receive it, along with the sequence number of the originally received message.
In my case, it will make sense to wait minutes or hours before trying again.
This could be done with a separate service that uses a timer and an explicit call to ReceiveDeferredMessageAsync(), as opposed to using a ServiceBusProcessor. I also suppose that the deferred message sequence numbers will have to be persisted in non-volatile storage so that they don't get lost.
Does this sound like a viable approach? I don't like having to remember its sequence numbers so that I can get to a message later. It goes against everything that using a message queue brings to the table in the first place.
Or, instead of deferring, I could just post a new "internal" message with the sequence number and use the ScheduledEnqueueTimeUtc property to delay receiving it. Once I receive this message, I could call ReceiveDeferredMessageAsync() with that sequence number to get to the original message. This seems elegant at the surface, but messages could quickly multiply if there is a longer outage of a dependency.
Another idea that could work without another service: I could complete and repost the payload of the message and set ScheduledEnqueueTimeUtc to a time in the future, as described in another answer to the question I mentioned earlier. Assuming that this works (Microsoft's documentation does not mention what this property is for), it seems simple and clean, and I like simple.
How have you solved this? Is there a better/preferred way that balances low complexity with high robustness without requiring a large amount of code?
Deferring a message works when you know what message you want to retrieve later and your receiver will have the message sequence number saved to retrieve the deferred message. If the receiver has no ability to save message sequence number, the delaying the message is a better option. Delaying a message will mean to copy the original message data into a newly scheduled one and completing the original message. That way the consumer doesn't have to neither hold on to the message sequence number nor initiate the retrieval of a specific message.

Reliable/idempotent mailing from Azure Function app

I'm currently evaluating Azure Functions and I'm trying to find a way/pattern to reliable and idempotent send Emails (and store them in a db). I already read a lot about Sagas, 2PC, Eventual Consistency, but I don't know how to apply these concepts to my situation.
I already have a few business objects stored in a database. Now I would like to add an endpoint which e.g. sends a project summary based on a template. Therefore I created a http triggered function and a CreateEmail method. This is the pseudo code of it
public static async void CreateEmail(QueueClient queue, Guid id)
{
// add the message to the queue, but keep it hidden for 3 min
var sendReceipt = await queue.SendMessageAsync(id.ToString(), TimeSpan.FromSeconds(180))
.ConfigureAwait(false);
//message.PopReceipt is now populated, and only this client can operate on the message until visibility timeout expires
try
{
//Create the mail entity in the db and commit
CreateEmailEntityAndCommit(id);
}
catch (Exception)
{
// Delete the SendMail queue message, because an error occured in db operations
queue.DeleteMessage(sendReceipt.Value.MessageId, sendReceipt.Value.PopReceipt);
throw;
}
// Everything is fine. Mark the message as visible to the email send function
queue.UpdateMessage(sendReceipt.Value.MessageId, sendReceipt.Value.PopReceipt,
visibilityTimeout: TimeSpan.Zero);
}
The code actually does not send the mail, but only creates a database entity and queues a message to the Azure Queue Storage. Another, queue triggered function picks up the messages, sends the mail and updates the status in the db:
public void Run([QueueTrigger("myqueue-items")]string id, ILogger log)
{
if (CheckEmailStatus() == Status.Sent)
{
// Message received twice
return;
}
SendEmail();
UpdateEmailStatus(Status.Sent); // How do we deal with exceptions here? email sent successfully, but status not updated...
}
And here is my problem: If anything goes wrong immediately after sending the mail, the status is not updated. When azure delivers the message again, the Mail would be send again. I guess there is a pattern to avoid such a situation.
Since you are using storage queue, you need to handle idempotency or deduplication at the receiver function end using some identifier of the entity. For example, you can maintain a cache of ids which you can look up to check if currently received id exists, the cache can be set with a reasonable TTL of your desired time window.
Note: Duplicate detection is out of the box in Service Bus queue.
Also you might want to look at Durable Functions.

How to process a message with PossDupFlag = Y. Actually it doesn't reach the FromApp method

We're receiving a ResendRequest message from our acceptor, but the messages that are coming with PossDupFlag = Y, aren't being processed.
We've seen that in quickfixn validates it in the method "void DoPossUp(Message msg)" on Session class, but we don't know how quickfixn manage this message if it hasn't being processed before and needs to be processed again to ensure the good data quality.
We've deleted the files where body and headers are stored but messages aren't processed.
What's the right behaviour and how we can test a right escenario in a data loss scenario?

Tasks which contains smaller tasks with ACK from Consumer

I am working on some POC project and trying to solve the following problem.
I have a Publisher which is sending a messages to the Queue:
bus.PublishAsync<IBaseScenario>(new TestScenario())
.ContinueWith(task =>
{
if (task.IsCompleted && !task.IsFaulted)
Console.WriteLine("TestScenario queued with success.");
else
Console.WriteLine(task.Exception.Message);
});
And some Consumers which are consuming a messages:
bus.SubscribeAsync<IBaseScenario>("test_1_consumer",
message => Task.Factory.StartNew(() =>
{
var testScenario = message as TestScenario;
var anotherTestScenario = message as AnotherTestScenario;
ResolveScenario(testScenario);
ResolveScenario(anotherTestScenario);
}).ContinueWith(task =>
{
if (task.IsCompleted && !task.IsFaulted)
Console.WriteLine("Task ended up with success.");
else
Console.WriteLine(task.Exception.Message);
}));
At this point everything is working as needed, but here is what I would like to achieve.
My Message is some kind of Scenario which contains steps, each Scenario is sent to the Queue and then maintained by a Consumer.
I would like to get a some kind of ACK info from Consumer sent to Publisher everytime when the each Step is done on the Consumer site (for example if its ended up with success or not.
I would like to get also an info about which Consumer got the Message.
Every Message (Scenario) should be treated as atomic operation, so there should not be possible to doing steps on different Consumers and if some Step will end without success, then the whole scenario should be treated as failed.
Are these 2 requirements possible to solve using the following architecture or do I need to use something more?
The easiest thing to do would be to use EasyNetQ's request response model described here https://github.com/EasyNetQ/EasyNetQ/wiki/Request-Response
In the response you can put the identity of the consumer that processed the message and the final status of the scenario. If one scenario is sent in one message, and that scenario contains all the steps necessary then all steps would be processed by a single consumer.
That said, message duplication is always a problem due to either sending the message twice or a message being requeued after a consumer fails. If it is critical that a scenario NEVER be processed more than once, then you will need to implement message deduplication or make each scenario idempotent. That is a general fact of life when working with RabbitMQ.

Serial processing of a certain message type in Rebus

We have a Rebus message handler that talks to a third party webservice. Due to reasons beyond our immediate control, this WCF service frequently throws an exception because it encountered a database deadlock in its own database. Rebus will then try to process this message five times, which in most cases means that one of those five times will be lucky and not get a deadlock. But it frequently happens that a message does get deadlock after deadlock and ends up in our error queue.
Besides fixing the source of the deadlocks, which would be a longterm goal, I can think of two options:
Keep trying with only this particular message type until it succeeds. Preferably I would be able to set a timeout, so "if five deadlocks then try again in 5 minutes" rather than choke the process up even more by trying continuously. I already do a Thread.Sleep(random) to spread the messages somewhat, but it will still give up after five tries.
Send this particular message type to a different queue that has only one worker that processes the message, so that this happens serially rather than in parallel. Our current configuration uses 8 worker threads, but this just makes the deadlock situation worse as the webservice now gets called concurrently and the messages get in each other's way.
Option #2 has my preference, but I'm not sure if this is possible. Our configuration on the receiving side currently looks like this:
var adapter = new Rebus.Ninject.NinjectContainerAdapter(this.Kernel);
var bus = Rebus.Configuration.Configure.With(adapter)
.Logging(x => x.Log4Net())
.Transport(t => t.UseMsmqAndGetInputQueueNameFromAppConfig())
.MessageOwnership(d => d.FromRebusConfigurationSection())
.CreateBus().Start();
And the .config for the receiving side:
<rebus inputQueue="app.msg.input" errorQueue="app.msg.error" workers="8">
<endpoints>
</endpoints>
</rebus>
From what I can tell from the config, it's only possible to set one input queue to 'listen' to. I can't really find a way to do this via the fluent mapping API either. That seems to take only one input- and error queue as well:
.Transport(t =>t.UseMsmq("input", "error"))
Basically, what I'm looking for is something along the lines of:
<rebus workers="8">
<input name="app.msg.input" error="app.msg.error" />
<input name="another.input.queue" error="app.msg.error" />
</rebus>
Any tips on how to handle my requirements?
I suggest you make use of a saga and Rebus' timeout service to implement a retry strategy that fits your needs. This way, in your Rebus-enabled web service facade, you could do something like this:
public void Handle(TryMakeWebServiceCall message)
{
try
{
var result = client.MakeWebServiceCall(whatever);
bus.Reply(new ResponseWithTheResult{ ... });
}
catch(Exception e)
{
Data.FailedAttempts++;
if (Data.FailedAttempts < 10)
{
bus.Defer(TimeSpan.FromSeconds(1), message);
return;
}
// oh no! we failed 10 times... this is probably where we'd
// go and do something like this:
emailService.NotifyAdministrator("Something went wrong!");
}
}
where Data is the saga data that is made magically available to you and persisted between calls.
For inspiration on how to create a saga, check out the wiki page on coordinating stuff that happens over time where you can see an example on how a service might have some state (i.e. number of failed attempts in your case) stored locally that is made available between handling messages.
When the time comes to make bus.Defer work, you have two options: 1) use an external timeout service (which I usually have installed one of on each server), or 2) just use "yourself" as a timeout service.
At configuration time, you go
Configure.With(...)
.(...)
.Timeouts(t => // configure it here)
where you can either StoreInMemory, StoreInSqlServer, StoreInMongoDb, StoreInRavenDb, or UseExternalTimeoutManager.
If you choose (1), you need to check out the Rebus code and build Rebus.Timeout yourself - it's basically just a configurable, Topshelf-enabled console application that has a Rebus endpoint inside.
Please let me know if you need more help making this work - bus.Defer is where your system becomes awesome, and will be capable of overcoming all of the little glitches that make all others' go down :)

Categories

Resources