Respond to MailGun's HTTP post and then process the message - c#

When receiving mail through MailGun they require a response within a limited time. I have two issues with this:
1) After receiving the message I need to process and record it in my CRM which takes some time. This causes MailGun to time out before I get to send a response. Then MailGun resends the message again and again as it continues to time out.
2) MailGun's post is not async but the api calls to my CRM are async.
So I need to send MailGun a 200 response and then continue to process the message. And that process needs to be in async.
The below code shows what I want to have happen. I tried using tasks and couldn't get it working. There are times when many emails can come in a once (like when initializing someone's account) if the solution requires some sort of parallel tasks or threads it would need to handle many of them.
public class HomeController : Controller
{
[HttpPost]
[Route("mail1")]
public ActionResult Mail()
{
var emailObj = MailGun.Receive(Request);
return Content("ok");
_ = await CRM.SendToEmailApp(emailObj);
}
}
Thank you for the help!

The easiest way to do what you are describing (which is not recommended, because you may lose some results if your app crash) is to use a fire & forget task:
var emailObj = MailGun.Receive(Request);
Task.Run(async () => await CRM.SendToEmailApp(emailObj));
return Content("ok");
But, I think what you really want is sort of a Message Queue, by using a message queue you put the message in the queue (which is fast enough) and return immediately, at the same time a processor is processing the message queue and saves the result in the CRM.
This is what it'll look like when you use a message queueing broker.

Related

I have a long running process which I call in my Service Bus Queue. I want it to continue beyond 5 minutes

I have a long running process which performs matches between millions of records I call this code using a Service Bus, However when my process passes the 5 minute limit Azure starts processing the already processed records from the start again.
How can I avoid this
Here is my code:
private static async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
long receivedMessageTrasactionId = 0;
try
{
IQueueClient queueClient = new QueueClient(serviceBusConnectionString, serviceBusQueueName, ReceiveMode.PeekLock);
// Process the message
receivedMessageTrasactionId = Convert.ToInt64(Encoding.UTF8.GetString(message.Body));
// My Very Long Running Method
await DataCleanse.PerformDataCleanse(receivedMessageTrasactionId);
//Get Transaction and Metric details
await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}
catch (Exception ex)
{
Log4NetErrorLogger(ex);
throw ex;
}
}
Messages are intended for notifications and not long running processing.
You've got a fewoptions:
Receive the message and rely on receiver's RenewLock() operation to extend the lock.
Use user-callback API and specify maximum processing time, if known, via MessageHandlerOptions.MaxAutoRenewDuration setting to auto-renew message's lock.
Record the processing started but do not complete the incoming message. Rather leverage message deferral feature, sending yourself a new delayed message with the reference to the deferred message SequenceNumber. This will allow you to periodically receive a "reminder" message to see if the work is finished. If it is, complete the deferred message by its SequenceNumber. Otherise, complete the "reminder" message along with sending a new one. This approach would require some level of your architecture redesign.
Similar to option 3, but offload processing to an external process that will report the status later. There are frameworks that can help you with that. MassTransit or NServiceBus. The latter has a sample you can download and play with.
Note that option 1 and 2 are not guaranteed as those are client-side initiated operations.

WebApi how to send asyncronous email after returning response to user?

Emails are taking too long to send (sometimes up to 7-8) seconds.
I want to send a response back to my clients without them having to wait for the email to send. Is this possible?
public async Task<IHttpActionResult> Action()
{
//Do something
await email.sendAsync(); //Can take up to 10 seconds...
return Ok();
}
I can remove the await, but I obviously get a An asynchronous module or handler completed while an asynchronous operation was still pending
How can I achieve this?
Ideally you would want to offload this work onto some other process, maybe a queue and then have some subscribers process off that queue to send the email but that is a lot of work and we live in the real world.
You can use
HostingEnvironment.QueueBackgroundWorkItem(ct => email.sendAsync());
to get something quite reliable up and running.

Asynchronous operation cannot be started at this time

I am here just trying to download the emails in "INBOX" from the server using IMAP with ComponentPro .net component (Asynchronous Task-Based Approach) but it throws the error every time when I try Asynchronous method.
I already got Inbox emails downloaded using IMAP with Synchronous approach but this takes nearly 3-4 minutes to download atleast 80+ emails, So I want to try asynchronous approach any suggestions on this as I am trying asynchronous approach for the first time.
Error:
An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle.
Controller Code:
public ActionResult ImportEmailDemo()
{
var sImportedEmails = ARepository.ImportEmailForDemo();
return null;
}
Repository Code:
public async Task<string> ImportEmailForDemo()
{
//Async
// Create a new instance of the Imap class.
Imap client1 = new Imap();
// Connect to the server.
client1.Connect("Server Address");
// Or you can specify the IMAP port with
// client.Connect("myserver", 143);
// Login to the server.
client1.Authenticate("EmailID", "Password");
// Select 'INBOX' mailbox.
client1.Select("INBOX");
// Download a mail message with sequence number 1.
ComponentPro.Net.Mail.MailMessage msg = await client1.DownloadMailMessageAsync(1);
// ...
Console.WriteLine("Message downloaded successfully.");
Console.WriteLine("Message ID: {0}, Subject: {1}", msg.MessageIdentifier, msg.Subject);
// Disconnect.
client1.Disconnect();
return null;
}
I tried with different ways to resolve the error looking in internet but nothing worked for me, Can any one help me out.
Cheers!!
I already got Inbox emails downloaded using IMAP with Synchronous approach but this takes nearly 3-4 minutes to download atleast 80+ emails, So I want to try asynchronous approach
Asynchronous won't be any faster.
An asynchronous operation cannot be started at this time.
This error occurs when your code starts an asynchronous operation within a synchronous handler. You should not be using Wait or Result; instead, you need to await the returned task, which makes that method async, etc., until your handler is changed to be asynchronous.
But as noted above, using async is not going to make it faster anyway.

Sending multiple async emails

I want to send multiple emails asynchronously. Emails do not depend on each other and no code will run after emails send, so i want to fire multiple and forget. I am following this article for sending async emails but i am not sure if i am doing the right thing for sending multiple emails.
A few of my concerns;
Sending of second email should not wait for first one.
Should i use QueueBackgroundWorkItem for each email sending task or use one QueueBackgroundWorkItem?
public class MyController : Controller
{
public async Task SendEmailAsync(IdentityMessage message)
{
var mailMessage = new MailMessage
("abc#domain.com", message.Destination, message.Subject, message.Body);
using(var client = new SmtpClient())
{
await client.SendMailAsync(mailMessage);
}
}
public ActionResult Create()
{
Action<CancellationToken> workItem = SendEmails;
HostingEnvironment.QueueBackgroundWorkItem(workItem);
return View();
}
private async void SendEmails(CancellationToken cancellationToken)
{
await SendEmailAsync(new IdentityMessage(...));
await SendEmailAsync(new IdentityMessage(...));
}
}
In SendEmails every next email is waiting for completion of previous, you should rewrite it like this:
private async Task SendEmails(CancellationToken cancellationToken)
{
await Task.WhenAll(SendEmailAsync(new IdentityMessage(...)),
SendEmailAsync(new IdentityMessage(...)));
}
By doing so all email will be send in parallel.
Also you should not QueueBackgroundWorkItem in Create. You can just do next:
public async Task<ActionResult> Create()
{
HostingEnvironment.QueueBackgroundWorkItem(async (token)=> await SendEmails() );
return View();
}
But please keep in mind that if any exception occur in SendEmails you will not be notified about this. Let me know if this can be an issue for you, so i will provide solution for this.
But i prefer another approach of sending emails from web application. It is more complicated but more reliable. Web app should never send messages to smtp server but store request to queue or db. Than we have background application that listen to queue and every time new request was detected it try to send it to smtp. if attempt fail system will return request back to the queue and will try few more times later. This background app may be windows service of webjob if your app is hosted in azure.
Advantages of this approach:
no UI thread blocking - to put something in queue is short running operation
even if smtp server is not accessible for some period of time you are sure that all emails will be delivered
Disadvantages:
You have to maintain additional component of your system

How to handle a reply for long-running REST request?

Background: We have import-functions that can take anywhere from a few seconds to 1-2 hours to run depending on the file being imported. We want to expose a new way of triggering imports, via a REST request.
Ideally the REST service would be called, trigger the import and reply with a result when done. My question is: since it can take up to two hours to run, is it possible to reply or will the request timeout for the caller? Is there a better way for this kind of operation?
What I use in these cases is an asynchronous operation that returns no result (void function result in case of c# Web API), then send the result asynchronously using a message queue.
E.g.
[HttpPut]
[Route("update")]
public void Update()
{
var task = Task.Run(() => this.engine.Update());
task.ContinueWith(t => publish(t, "Update()"));
}

Categories

Resources