1

I have a question about the wp_mail() function. Right now I'm having the following button to send e-mail with:

echo '<input type="submit" name="send_button" value="verzenden" id="'.$post_id.'" class="send_post">';

which triggers a jQuery request:

jQuery(document).ready(function() {

jQuery(".send_post").click(function() {

    var post_id = $(this).attr('id');

    jQuery.ajax({
        async: false,
        type: 'get',
        url: nieuwsbrief.sendmail_url,
        data: 'postid=' + post_id,
            success: function(data) {
                alert(data);
                return false;
            }
    });
});

});

jQuery sends the request to sendmail.php. Which includes the following:

<?php
 include_once $_SERVER['DOCUMENT_ROOT'] . '/www/wordpress/wp-config.php';
 include_once $_SERVER['DOCUMENT_ROOT'] . '/www/wordpress/wp-load.php';
 include_once $_SERVER['DOCUMENT_ROOT'] . '/www/wordpress/wp-includes/wp-db.php';

 global $wpdb;

$subscribers = $wpdb->get_results( 
$wpdb->prepare(

"SELECT * FROM 
            wp_nwsbrf_subscribers 
                INNER JOIN 
                    wp_nwsbrf_couple
                        INNER JOIN 
                            wp_nwsbrf_categories
                                INNER JOIN
                                    wp_posts
                                    WHERE 
                                        wp_nwsbrf_subscribers.category_id = wp_nwsbrf_categories.category_id
                                        AND 
                                        wp_nwsbrf_couple.category_id = wp_nwsbrf_categories.category_id
                                        AND 
                                        wp_nwsbrf_subscribers.category_id = wp_nwsbrf_couple.category_id
                                        AND
                                        wp_posts.ID = wp_nwsbrf_couple.ID
                                        AND
                                        wp_nwsbrf_couple.ID = %d", $_GET['postid']) );


foreach ($subscribers as $subscriber) {

    $to = $subscriber->subscriber_mail.",";
    $title = $subscriber->post_title;
    $message = $subscriber->post_content;

    wp_mail($to, $title, $message);
}

?>

As you can see, the mail has to be sent in sendmail.php (url: nieuwsbrief.sendmail_url). But when I click on the send_button it takes minutes (3 or 4) to send the e-mail!? What am I doing wrong here?

Thanks for helping me out!

  • 1
    How are you timing when email is sent? Had you considered using proper Ajax endpoint in WP? That custom loader doesn't look right to me. – Rarst May 29 '13 at 11:25
  • At the moment i ain't timing it. How can i achieve that on a proper way? But even when i sent 1 mail at a time wp_mail() is very slow. – user2237168 May 29 '13 at 11:37
  • 2
    You should use the standard AJAX API rather than going to all that effort to reinvent it – Tom J Nowell May 29 '13 at 11:43
5

What am i doing wrong here?

Many, many things, not all performance related. Lets begin with the critical parts, then conclude on your performance issues and what can be done to mitigate and help

AJAX API

Firstly, you're not using the AJAX API, and reinventing the wheel. It's quite simple:

JS:

$.post( do_mail_sending.ajaxurl, {
    action: 'do_mail_sending',
    post_id: post_id

}, function(response) {
    alert( response );
});

PHP:

wp_localize_script( 'do_mail_sending', 'do_mail_sending', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );
add_action( 'wp_ajax_do_mail_sending',  'do_mail_sending' );
add_action( 'wp_ajax_nopriv_do_mail_sending',  'do_mail_sending' ); // and for logged out users
function do_mail_sending() {
    $post_id = $_POST['post_id'];
    die( 'response data' );
}

Closing PHP Tag and Trailing Space

PHP can be used as a scripting language, but you've veered into app development, so I'd recommend not having a closing PHP tag at the end of the document. Your code snippet above does this and it has a new line break space afterwards, which can cause issues.

Security

It would appear that although you're running a prepare statement, not once are you checking if the user is actually allowed to do this.

E.g. I can flood all your subscribers, and bring down your server with the following javascript snippet:

while ( true ) {
    jQuery.ajax({
        async: false,
        type: 'get',
        url: nieuwsbrief.sendmail_url,
        data: 'postid=' + post_id,
            success: function(data) {
                return false;
            }
    });
}

You don't check that:

  • the user is logged in
  • the user has the necessary roles and capabilities
  • that the request came from your site
  • that the request came from the page with the email send form
  • make use nonces
  • check that the post itself exists

Performance

Your performance woes are a result of what you're doing. Namely:

  • An expensive SQL query including multiple joins
  • Sending an email

Both are inherently expensive, and the last one you're doing a lot of. Emails themselves are costly to send server-wise, as it has to contact the SMTP server, and negotiate, and any remote requests are going to take time.

There is little you can do to avoid this expense, as the core parts that make this up are inherently expensive.

But that's okay. You aren't the first person to have to do something expensive, so your goal should be mitigation and redesign. You cant reduce the cost of the process, but you can modify the process so that it appears fast for the UI, and still gets done. Banks solved this in the early days by doing their hard number crunching overnight while the branch was closed in batch runs.

Here are some ways of mitigating it:

  • Split the process up into multiple steps, instead of 1 AJAX request, do several, 1 to count how many emails need to be sent, then several afterwards, using a paging style technique to send them in batches of 10 or 20 depending on your servers response times.
  • Dont send emails at all, instead calculate what work needs to be done, then save it in the database as a task that needs doing. Then have a cron job run at a regular interval that sends the emails in the background.
  • Add notes to a queue of emails to be sent, and send them one by one, removing the item from the queue. Your newsletter may not be sent all at once, but it will be sent, and it won't bring your server down while it happens. It also enables you to create a status page showing the mailing progress in realtime.

Data Note

That you need an SQL statement like that makes me wonder if your categories table and query cant be replaced with a custom taxonomy and a call to WP_Query. This way you could take advantage of the built in object and query caching

4

In my case wp_mail() took over 90 seconds per email on form submits, bringing my site to a crawl. Totally forgot to correct the /etc/hosts config for my site, per the instructions below, now sendmail is < 3 seconds.

https://www.digitalocean.com/community/questions/sendmail-is-slow-to-send-mail

If you check your /var/log/mail.log you will probably find something like this: My unqualified host name (localhost) unknown; sleeping for retry. This is because sendmail requires "hostname" to be a fully qualified domain name. update your "/etc/hosts" file: nano /etc/hosts to look like this: 127.0.0.1 localhost.localdomain localhost yourhostname boom, fast sendmail.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.