private void ProcessQueueMessage(CloudQueueMessage msg) { // Log and delete if this is a "poison" queue message (repeatedly processed // and always causes an error that prevents processing from completing). // Production applications should move the "poison" message to a "dead message" // queue for analysis rather than deleting the message. if (msg.DequeueCount > 5) { Trace.TraceError("Deleting poison message: message {0} Role Instance {1}.", msg.ToString(), GetRoleInstance()); sendEmailQueue.DeleteMessage(msg); return; } // Parse message retrieved from queue. // Example: 2012-01-01,[email protected],0 var messageParts = msg.AsString.Split(new char[] { ',' }); var partitionKey = messageParts[0]; var rowKey = messageParts[1]; var restartFlag = messageParts[2]; Trace.TraceInformation("ProcessQueueMessage start: partitionKey {0} rowKey {1} Role Instance {2}.", partitionKey, rowKey, GetRoleInstance()); // If this is a restart, verify that the email hasn't already been sent. if (restartFlag == "1") { var retrieveOperationForRestart = TableOperation.Retrieve<SendEmail>(partitionKey, rowKey); var retrievedResultForRestart = messagearchiveTable.Execute(retrieveOperationForRestart); var messagearchiveRow = retrievedResultForRestart.Result as SendEmail; if (messagearchiveRow != null) { // SendEmail row is in archive, so email is already sent. // If there's a SendEmail Row in message table, delete it, // and delete the queue message. Trace.TraceInformation("Email already sent: partitionKey=" + partitionKey + " rowKey= " + rowKey); var deleteOperation = TableOperation.Delete(new SendEmail { PartitionKey = partitionKey, RowKey = rowKey, ETag = "*" }); try { messageTable.Execute(deleteOperation); } catch { } sendEmailQueue.DeleteMessage(msg); return; } } // Get the row in the Message table that has data we need to send the email. var retrieveOperation = TableOperation.Retrieve<SendEmail>(partitionKey, rowKey); var retrievedResult = messageTable.Execute(retrieveOperation); var emailRowInMessageTable = retrievedResult.Result as SendEmail; if (emailRowInMessageTable == null) { Trace.TraceError("SendEmail row not found: partitionKey {0} rowKey {1} Role Instance {2}.", partitionKey, rowKey, GetRoleInstance()); return; } // Derive blob names from the MessageRef. var htmlMessageBodyRef = emailRowInMessageTable.MessageRef + ".htm"; var textMessageBodyRef = emailRowInMessageTable.MessageRef + ".txt"; // If the email hasn't already been sent, send email and archive the table row. if (emailRowInMessageTable.EmailSent != true) { SendEmailToList(emailRowInMessageTable, htmlMessageBodyRef, textMessageBodyRef); var emailRowToDelete = new SendEmail { PartitionKey = partitionKey, RowKey = rowKey, ETag = "*" }; emailRowInMessageTable.EmailSent = true; var upsertOperation = TableOperation.InsertOrReplace(emailRowInMessageTable); messagearchiveTable.Execute(upsertOperation); var deleteOperation = TableOperation.Delete(emailRowToDelete); messageTable.Execute(deleteOperation); } // Delete the queue message. sendEmailQueue.DeleteMessage(msg); Trace.TraceInformation("ProcessQueueMessage complete: partitionKey {0} rowKey {1} Role Instance {2}.", partitionKey, rowKey, GetRoleInstance()); }
private string GetPlainTextUnsubscribeLink(SendEmail emailRowInMessageTable) { return String.Format("\nCopy the following URL into your browser unsubscribe.\n{0}/unsubscribe?id={1}&listName={2}", RoleEnvironment.GetConfigurationSettingValue("AzureMailServiceURL"), emailRowInMessageTable.SubscriberGUID, emailRowInMessageTable.ListName); }
private string GetHTMLUnsubscribeLink(SendEmail emailRowInMessageTable) { return String.Format("<p>Click the following link to unsubscribe. <a href=\"{0}/unsubscribe?id={1}&listName={2}\">Unsubscribe</a></p>", RoleEnvironment.GetConfigurationSettingValue("AzureMailServiceURL"), emailRowInMessageTable.SubscriberGUID, emailRowInMessageTable.ListName); }
private void SendEmailToList(SendEmail emailRowInMessageTable, string htmlMessageBodyRef, string textMessageBodyRef) { var email = SendGrid.GetInstance(); email.From = new MailAddress(emailRowInMessageTable.FromEmailAddress); email.AddTo(emailRowInMessageTable.EmailAddress); email.Html = GetBlobText(htmlMessageBodyRef) + GetHTMLUnsubscribeLink(emailRowInMessageTable); email.Text = GetBlobText(textMessageBodyRef) + GetPlainTextUnsubscribeLink(emailRowInMessageTable); email.Subject = emailRowInMessageTable.SubjectLine; var credentials = new NetworkCredential(RoleEnvironment.GetConfigurationSettingValue("SendGridUserName"), RoleEnvironment.GetConfigurationSettingValue("SendGridPassword")); var transportREST = Web.GetInstance(credentials); transportREST.Deliver(email); }
private void ProcessQueueMessage(CloudQueueMessage msg) { Stopwatch requestTimer = Stopwatch.StartNew(); var request = RequestTelemetryHelper.StartNewRequest("ProcessEmailQueueMessage", DateTimeOffset.UtcNow); CallContext.LogicalSetData(CORRELATION_SLOT, request.Id); //Thread.SetData(Thread.GetNamedDataSlot(CORRELATION_SLOT), request.Id); try { // Log and delete if this is a "poison" queue message (repeatedly processed // and always causes an error that prevents processing from completing). // Production applications should move the "poison" message to a "dead message" // queue for analysis rather than deleting the message. if (msg.DequeueCount > 5) { Trace.TraceError("Deleting poison message: message {0} Role Instance {1}.", msg.ToString(), GetRoleInstance()); sendEmailQueue.DeleteMessage(msg); request.Properties.Add(new KeyValuePair<string,string>("FailureCode","PoisonMessage")); request.Properties.Add(new KeyValuePair<string, string>("DequeueCount", msg.DequeueCount.ToString())); if (msg.InsertionTime != null) { request.Metrics.Add(new KeyValuePair<string, double>("EmailProcessingTimeMs", ((TimeSpan)(DateTimeOffset.Now - msg.InsertionTime)).Milliseconds)); } RequestTelemetryHelper.DispatchRequest(request, requestTimer.Elapsed, false); return; } // Parse message retrieved from queue. // Example: 2012-01-01,[email protected],0 var messageParts = msg.AsString.Split(new char[] { ',' }); var partitionKey = messageParts[0]; var rowKey = messageParts[1]; var restartFlag = messageParts[2]; Trace.TraceInformation("ProcessQueueMessage start: partitionKey {0} rowKey {1} Role Instance {2}.", partitionKey, rowKey, GetRoleInstance()); // If this is a restart, verify that the email hasn't already been sent. if (restartFlag == "1") { var retrieveOperationForRestart = TableOperation.Retrieve<SendEmail>(partitionKey, rowKey); var retrievedResultForRestart = messagearchiveTable.Execute(retrieveOperationForRestart); var messagearchiveRow = retrievedResultForRestart.Result as SendEmail; if (messagearchiveRow != null) { // SendEmail row is in archive, so email is already sent. // If there's a SendEmail Row in message table, delete it, // and delete the queue message. Trace.TraceInformation("Email already sent: partitionKey=" + partitionKey + " rowKey= " + rowKey); var deleteOperation = TableOperation.Delete(new SendEmail { PartitionKey = partitionKey, RowKey = rowKey, ETag = "*" }); try { messageTable.Execute(deleteOperation); } catch(Exception ex) { aiClient.TrackException(ex); } sendEmailQueue.DeleteMessage(msg); request.Properties.Add(new KeyValuePair<string, string>("SuccessCode", "NoOp-MessageAlreadySent")); if (msg.InsertionTime != null) { request.Metrics.Add(new KeyValuePair<string, double>("EmailProcessingTimeMs", ((TimeSpan)(DateTimeOffset.Now - msg.InsertionTime)).Milliseconds)); } RequestTelemetryHelper.DispatchRequest(request, requestTimer.Elapsed, true); return; } } // Get the row in the Message table that has data we need to send the email. var retrieveOperation = TableOperation.Retrieve<SendEmail>(partitionKey, rowKey); var retrievedResult = messageTable.Execute(retrieveOperation); var emailRowInMessageTable = retrievedResult.Result as SendEmail; if (emailRowInMessageTable == null) { Trace.TraceError("SendEmail row not found: partitionKey {0} rowKey {1} Role Instance {2}.",partitionKey, rowKey, GetRoleInstance()); request.Properties.Add(new KeyValuePair<string, string>("FailureCode", "SendEmailRowNotFound")); if (msg.InsertionTime != null) { request.Metrics.Add(new KeyValuePair<string, double>("EmailProcessingTimeMs", ((TimeSpan)(DateTimeOffset.Now - msg.InsertionTime)).Milliseconds)); } RequestTelemetryHelper.DispatchRequest(request, requestTimer.Elapsed, false); return; } // Derive blob names from the MessageRef. var htmlMessageBodyRef = emailRowInMessageTable.MessageRef + ".htm"; var textMessageBodyRef = emailRowInMessageTable.MessageRef + ".txt"; // If the email hasn't already been sent, send email and archive the table row. if (emailRowInMessageTable.EmailSent != true) { SendEmailToList(emailRowInMessageTable, htmlMessageBodyRef, textMessageBodyRef); var emailRowToDelete = new SendEmail { PartitionKey = partitionKey, RowKey = rowKey, ETag = "*" }; emailRowInMessageTable.EmailSent = true; var upsertOperation = TableOperation.InsertOrReplace(emailRowInMessageTable); messagearchiveTable.Execute(upsertOperation); var deleteOperation = TableOperation.Delete(emailRowToDelete); messageTable.Execute(deleteOperation); } // Delete the queue message. sendEmailQueue.DeleteMessage(msg); Trace.TraceInformation("ProcessQueueMessage complete: partitionKey {0} rowKey {1} Role Instance {2}.", partitionKey, rowKey, GetRoleInstance()); request.Properties.Add(new KeyValuePair<string, string>("SuccessCode", "EmailSent")); if (msg.InsertionTime != null) { request.Metrics.Add(new KeyValuePair<string, double>("EmailProcessingTimeMs", ((TimeSpan)(DateTimeOffset.Now - msg.InsertionTime)).Milliseconds)); } RequestTelemetryHelper.DispatchRequest(request, requestTimer.Elapsed, true); } catch (Exception ex) { request.Properties.Add(new KeyValuePair<string, string>("FailureCode", "Exception")); if (msg.InsertionTime != null) { request.Metrics.Add(new KeyValuePair<string, double>("FailedEmailProcessingTimeMs", ((TimeSpan)(DateTimeOffset.Now - msg.InsertionTime)).Milliseconds)); } RequestTelemetryHelper.DispatchRequest(request, requestTimer.Elapsed, false); throw ex; } }
private void ProcessMessage(Message messageToProcess, string restartFlag) { // Get Mailing List info to get the "From" email address. var retrieveOperation = TableOperation.Retrieve<MailingList>(messageToProcess.ListName, "mailinglist"); var retrievedResult = mailingListTable.Execute(retrieveOperation); var mailingList = retrievedResult.Result as MailingList; if (mailingList == null) { Trace.TraceError("Mailing list not found: " + messageToProcess.ListName + " for message: " + messageToProcess.MessageRef); return; } // Get email addresses for this Mailing List. string filter = TableQuery.CombineFilters( TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, messageToProcess.ListName), TableOperators.And, TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.NotEqual, "mailinglist")); var query = new TableQuery<Subscriber>().Where(filter); var subscribers = mailingListTable.ExecuteQuery(query).ToList(); aiClient.TrackMetric("SubscriberCount", subscribers.Count); foreach (Subscriber subscriber in subscribers) { // Verify that the subscriber email address has been verified. if (subscriber.Verified == false) { Trace.TraceInformation("Subscriber " + subscriber.EmailAddress + " not Verified, so not queuing "); continue; } // Create a SendEmail entity for this email. var sendEmailRow = new SendEmail { PartitionKey = messageToProcess.PartitionKey, RowKey = messageToProcess.MessageRef.ToString() + subscriber.EmailAddress, EmailAddress = subscriber.EmailAddress, EmailSent = false, MessageRef = messageToProcess.MessageRef, ScheduledDate = messageToProcess.ScheduledDate, FromEmailAddress = mailingList.FromEmailAddress, SubjectLine = messageToProcess.SubjectLine, SubscriberGUID = subscriber.SubscriberGUID, ListName = mailingList.ListName }; // When we try to add the entity to the SendEmail table, // an exception might happen if this worker role went // down after processing some of the email addresses and then restarted. // In that case the row might already be present, so we do an Upsert operation. try { var upsertOperation = TableOperation.InsertOrReplace(sendEmailRow); messageTable.Execute(upsertOperation); } catch (Exception ex) { string err = "Error creating SendEmail row: " + ex.Message; if (ex.InnerException != null) { err += " Inner Exception: " + ex.InnerException; } Trace.TraceError(err, ex); } // Create the queue message. string queueMessageString = sendEmailRow.PartitionKey + "," + sendEmailRow.RowKey + "," + restartFlag; var queueMessage = new CloudQueueMessage(queueMessageString); sendEmailQueue.AddMessage(queueMessage); } Trace.TraceInformation("ProcessMessage end PK: " + messageToProcess.PartitionKey); }