How can I make email transactional in PHP and MySQL? - php

I want to send email to my app users ensuring that every email is sent only once.
I will be recording the email transmission in a database.
If I use this order of operations:
Insert into database
Send email
Commit
There is a chance that script times out just after sending the email (step #2) and before doing the commit (step #3). In that case the change in step #1 will not be committed and email sending job won't be able to know that the email was successfully sent last time so the same email will be sent again.
Is there a way out or do I have to live with occasional duplicate emails?

To make a long story short: you can't make email transactional.
At best you know that your smtp server recieved your request to sent the message. You have however no way of knowing whether it got sent, was received or bounced.
So your best bet, as you already suggested, is to live occasional duplicate emails. It will be a very rare event anyway.

You can use MySQL transactions for this, basically it prepares all your queries and executes them when you tell it to do so.
So you prepare the queries before sending the mail, then commit it after sending it.
More info
http://dev.mysql.com/doc/refman/5.0/en/commit.html
Alternatively you could set the mail as pending, and afterwards update it as completed. Then run a cron job that kills pending jobs that have been running for a certain amount of time, or try to reprocess them.

well you can make a column status which can be enum or int with values representing sending, failed, sent_successfully
Now, you do this:
Insert the data before sending the mail. And set status to sending
Send the mail
based on the result in step (2), update the row either failed or sent_successfully.
You might also like to have a column tries and a batch process that sends failed mails at every 30 minutes, if tries < TRY_THRESHOLD. And then, set status to failed_permanently or sent_successfully and log the error.

Just got some time to think over my own question. Here is what I think now:
The email sending steps in question were:
Insert into database
Send email
Commit
Email cannot be made transactional because the step #2 (above) is not part of the transaction even though it is done while the transaction is active.
These steps can help ensure that any failed email-sending attempts is retried but cannot guarantee that an email is not sent more than once. This situation could only be improved if the email sending engine is transaction-aware. Such an engine would (atleast) do the following:
Accept email job submission but don't send the email until the commit is performed
Make the commit fail if the email is not sent.
Cancel the submitted job if the transaction is rolled-back
I am not aware of any such email server.
Nishant, in his answer suggested following steps:
Insert the data before sending the mail. And set status to sending
Send the mail
based on the result in step (2), update the row either failed or sent_successfully.
These steps also cannot ensure duplicate sending of email due to script timeout for the same reason as noted above.
So far, I think, I will just have to live with occasional duplicate emails.

Related

Laravel email conditional send - checking when last email of this type was sent to a user

I run a scheduled task each day to send emails to customers who have outstanding invoice and need to add a condition to check whether the email has been sent to this customer within the last 72 hours.
I guess I would need to:
Send the mail.
Log the message type and user to the database
Check the database for when the last mail was sent.
I have done a fair bit of reading and wonder if anything like this is already included in Laravel? I have not been able to find much so far.
This isn't built in to the platform, but it should be fairly straightforward to add. Sending an email using Laravel's mail functions triggers a MessageSent event. Attach a listener to that event and you can do whatever you like with it. The event will include the message object as well as any data passed to it.

How to tackle emails failed to send after commit database php

The case is I am calling a function which makes changes in DB and return list of users to whom I have to send emails.
If my db have made changes and selected users and due to some reason emails were not send and users are not informed from changes.
How to make sure that emails were sent after making changes in DB and if emails were failed to send what action should be taken.
You can use mysqli transaction commit & rollback when fails
here is another thread that may help you
Mysql transaction : commit and rollback
You should restructure your code in order to SELECT the users you wanted to update, try to email, and update the successfully emailed contacts. Maybe add a flag to denote contacts that had a successful email.
This way on your next SELECT you will have the left over ones that failed to email and were not updated.

Do I need to whitelist any IPs to use Mandrill API?

I am trying to send mail using Mandrill API and I have got following responses :
[{"email":"_emailid_","status":"queued","_id":"772237d78ce74e5c9bd8ccbd9a1cfb8c"}]
{"email":"_emailid_","status":"scheduled","_id":"10a0b596a95e44ddb5e8cf504e3899ce","reject_reason":null},
{"email":"_emailid_","status":"scheduled","_id":"b699d58f021b43b2a0b25d801d1b4f91","reject_reason":null}
But I am not getting any mail on the email addresses sent. Please help.
Do I need to whitelist the IP of the system from where I am doing the curl call ?
You do not need to whitelist any ip addresses, you however do have to configure the DNS records for your email domain properly, head over to the domain settings page in the mandrill control panel for more information.
Everything seems fine.Either your you are not providing correct email to api or mail are coming to your spam folder.You need to check mail from address which you give to the api.
It looks like at least the last two were scheduled, which means they won't send immediately, but rather at the scheduled time in the future. You'll probably want to double check the time and date that they were scheduled to be sure.
For the first one, there are a number of reasons that emails are queued, including if they have attachments that need to be scanned, or if they're part of a large/batch API call. We'd need more information about the API parameters that you passed to determine the reason for it being queued and/or what happened once it was queued.
You can search your Outbound Activity by the recipient email address, subject line, sender, and a number of other criteria for emails that have been sent. Messages that are scheduled but haven't sent yet cannot be searched since they've not been processed. For any messages that have sent, click on the word "Delivered" on the Outbound Activity page to see more details about the response that Mandrill got from the receiving server when delivering the message.

Slow mail() function workarounds

I have a php script which sends user a mail if his purchase is successful. The problem is it the page loads slow due to mail(). I know there are ways like putting the mails in a database table and then using a cron job to send them but the frequency of purchase can be high and I want the mail to be sent right away, so that doesn't look like a good option.
The purchase request gets processed by the same page from where he purchased and he can only do so once. The user doesn't control any part of mail other than the purchase details. I thought of using Ajax, the script would send the data to client side and call a ajax function which then calls another mail script but this would let user know what is being sent and can be tampered. Is there any other way I can use Ajax safely without letting user know whats being sent and where? And are there any better workarounds?
mail() should return immediately upon queuing the message to your mail server, which (generally) should take no time at all, so I think your problem here is definitely the mail server and not your PHP. I'm guessing that the mail server you're submitting to is running some anti-spam checks, like reverse DNS lookups. It might also be throttling you based on usage. Can you try sending through another mail server to verify?
Also, if you have shell access, try sending a message from the shell (e.g., echo test | mail -v -s test me#whatever.com) to see what it's doing and how long it takes.
Edit: Just noticed your comment re: Windows. In this case, at least you can try a telnet from the PHP server to port 25 on the mail host to see how long it takes to connect and get the 220 greeting header. (I bet you'll connect immediately but won't get the 220 header for a while.)
You're on to the right idea. Here's what I would do in this scenario:
The user makes a purchase (no ajax unless you want to at this point)
In processing that purchase an email is inserted into the email table with an "id" & "sent" column plus all the other stuff.
User is brought to a success page
The success page kicks off ajax in the background to send the email with that id from the db - and doesn't care about the result
The php page in charge of emailing sends the email and marks the sent column
If someone ever makes a call to your database with an id that's not sent, that email needed to get sent out anyway, so it's ok. If the id doesn't match or the sent column is marked true, you can ignore it. No worries about people trying to send spam using your system.

PHP Bulk Email - Dedicated IP Max?

Is there any way i can send out about 3000+ emails from one php script request without overloading a dedicated IP... the max would be 500 per hour?
If you dont get me.. here is detailed :)
I can only send out 500 emails via the mail() function in PHP per hour via my dedicated IP, is there any way i could send out for example 3000 rows of emails pulled from an email address but stagger the mail() functions out for 500 per hour...
Thanks already!
Create 2 tables, one for the email message and one for the list of recipients.
Then create a script to be run by cron that checks if there is a new message in the message table and if so sends a batch of email to the next set of recipients. Marking each recipient after the mail is sent.
Then you create a web interface for your client to create a message and attach recipients to the message once the user marks the message as ready to go your cron job picks it up and processes it.
If there aren't any messages to be sent your cron job doesn't do anything.
You could sleep between the calls, or, if they're already in a database, put a field in there that says when they were sent. Then you select the ones that haven't been sent, and start from there.
I would put a field in the DB to show when the last email was sent to each user and what email it was. I would also have another DB table to show each email you sent and if it has been sent to all users yet.
User Table:
Id, UserName, Email, etc, DateTimeOfLastEmail, LastEmailId
Email Table:
Id, EmailSubject, EmailContent, DateTimeSent, SentToAll(True/False), DateTimeOfFinish
Thanks for all the answers!
The best way i found was actually to simply sleep() between calls using the sleep() as i tested 400 mails, this took 17 seconds :)
It is unlikely the user will send more than the 450 limit ... but if they do i have an if statement before the while() ends checking if there are more than 450 rows, if so it will sleep between each... this works without fiddly databases :)
Thanks!
well after doing some math you could send an email every 8.3 seconds (498/hr) but it doesn't solve the problem. I think another approach would be to use a DB, query for the 500 and have a CRON job run the script every hour.
So in the DB table you could have the script update a field after the email has been sent so the next cron job will query and get the next 500 emails that need to be sent.

Categories