admin管理员组

文章数量:1122832

I have a custom plugin that pulls in data from an API once per day at 2 AM. This mostly works fine, except that because I need to pull in the data in chunks, I keep running in to issues when I try to fetch subsequent chunks after the first.

My first thought was just to run do_action("myplugin_cron", $offset + 50); until no more data was retrieved. That works fine, except that if you have too much data come in, eventually the page will crash, preventing the rest of the data from being imported.

My second attempt was to use a query string to redirect the user to a URL with &offset=($offset + 50), but this lead to a "this page isn't redirecting properly" error in the browser.

My third attempt is to try to schedule the next offset as a cron event, and then immediately trigger it, looping until it finishes. This kind of works, except that wp_cron() doesn't seem to trigger the event, nor does pinging /wp-cron.php with cURL. Instead, I have to wait for users to come to the site naturally, slowly building out the import. Occasionally, even this seems to fail at some point, though I haven't been able to point point exactly when or why.

What am I doing wrong here? How can I modify this import logic so that it will continue importing in chunks without crashing?

/**
 * Run the cron job
 */
add_action("myplugin_cron", function (int $offset = 0) {
    global $wpdb;

    /**
     * POSTS
     */

    // Delete all posts older than 12 hours
    $wpdb->query("DELETE FROM `{$wpdb->prefix}myplugin_posts` WHERE timestamp < DATE_SUB(NOW(), INTERVAL 12 HOUR)");

    // Fetch the next page of activities
    $activities = \MyPlugin\request_activities($offset);

    if ($activities) {
        // Store posts in the database, if they don't already exist
        foreach ($activities as $activity) {
            if ($activity && ! $wpdb->get_row("SELECT * FROM `{$wpdb->prefix}myplugin_posts` WHERE api_id = '{$activity["api_id"]}'")) {
                $wpdb->insert("{$wpdb->prefix}myplugin_posts", $activity);
            }
        }

        // Clear memory
        unset($activities);

        // Schedule the next iteration
        wp_schedule_single_event(time(), "myplugin_cron", [$offset + 50]);

        // Sleep to ensure job is queued
        sleep(2);

        // Trigger cron again
        wp_cron();

        // Ping /wp-cron.php to keep the job running
        // $curl = curl_init(site_url("/wp-cron.php"));
        // curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        // curl_exec($curl);
        // curl_close($curl);
    }
});

/**
 * Every day at 2 AM run the cron job
 */
add_action("init", function () {
    if (! wp_next_scheduled("myplugin_cron")) {
        wp_schedule_event(strtotime("02:00:00"), "daily", "myplugin_cron");
    }
});

I have a custom plugin that pulls in data from an API once per day at 2 AM. This mostly works fine, except that because I need to pull in the data in chunks, I keep running in to issues when I try to fetch subsequent chunks after the first.

My first thought was just to run do_action("myplugin_cron", $offset + 50); until no more data was retrieved. That works fine, except that if you have too much data come in, eventually the page will crash, preventing the rest of the data from being imported.

My second attempt was to use a query string to redirect the user to a URL with &offset=($offset + 50), but this lead to a "this page isn't redirecting properly" error in the browser.

My third attempt is to try to schedule the next offset as a cron event, and then immediately trigger it, looping until it finishes. This kind of works, except that wp_cron() doesn't seem to trigger the event, nor does pinging /wp-cron.php with cURL. Instead, I have to wait for users to come to the site naturally, slowly building out the import. Occasionally, even this seems to fail at some point, though I haven't been able to point point exactly when or why.

What am I doing wrong here? How can I modify this import logic so that it will continue importing in chunks without crashing?

/**
 * Run the cron job
 */
add_action("myplugin_cron", function (int $offset = 0) {
    global $wpdb;

    /**
     * POSTS
     */

    // Delete all posts older than 12 hours
    $wpdb->query("DELETE FROM `{$wpdb->prefix}myplugin_posts` WHERE timestamp < DATE_SUB(NOW(), INTERVAL 12 HOUR)");

    // Fetch the next page of activities
    $activities = \MyPlugin\request_activities($offset);

    if ($activities) {
        // Store posts in the database, if they don't already exist
        foreach ($activities as $activity) {
            if ($activity && ! $wpdb->get_row("SELECT * FROM `{$wpdb->prefix}myplugin_posts` WHERE api_id = '{$activity["api_id"]}'")) {
                $wpdb->insert("{$wpdb->prefix}myplugin_posts", $activity);
            }
        }

        // Clear memory
        unset($activities);

        // Schedule the next iteration
        wp_schedule_single_event(time(), "myplugin_cron", [$offset + 50]);

        // Sleep to ensure job is queued
        sleep(2);

        // Trigger cron again
        wp_cron();

        // Ping /wp-cron.php to keep the job running
        // $curl = curl_init(site_url("/wp-cron.php"));
        // curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        // curl_exec($curl);
        // curl_close($curl);
    }
});

/**
 * Every day at 2 AM run the cron job
 */
add_action("init", function () {
    if (! wp_next_scheduled("myplugin_cron")) {
        wp_schedule_event(strtotime("02:00:00"), "daily", "myplugin_cron");
    }
});
Share Improve this question edited Sep 16, 2024 at 19:50 JacobTheDev asked Sep 16, 2024 at 19:41 JacobTheDevJacobTheDev 1,2535 gold badges18 silver badges37 bronze badges 1
  • add a log on fetch and when storing in db to trace exactly what appends. – mmm Commented Sep 17, 2024 at 1:10
Add a comment  | 

2 Answers 2

Reset to default 1

If you want to trigger the WP Cron without having to wait for a visitor to visit your website, you can setup a cronjob to "visit" your cron file at http://www.example.com/wp-cron.php after a duration, like every 15 minutes.

If you use cPanel, you can follow this guide

If your server does not support cron job, you can use service like Easy Crone. I think their free membership is good enough for a website. They also provide a guide on how to setup real cron job for WordPress here. Just one thing that you need to renew your Free plan every month.

If you use another type of hosting, let me know so I can help.

Your attempt with new CRON task is fine, but calling wp_cron() do not look like a good idea.

Instead, you can call the task with for example 10 seconds delay. So you will have:

 wp_schedule_single_event( strtotime( '+10 seconds' ), "myplugin_cron", [$offset + 50]);

But this will create infinite loop. So in the first run, you should gather max number of jobs that you expect and then put schedule into if:

 if ( $executed_activities < $max_activities ) {
     wp_schedule_single_event( strtotime( '+10 seconds' ), "myplugin_cron", [$offset + 50]);
 }

And you can keep $executed_activities in transient to finally have code:

 $per_run             = 50;
 $executed_activities = get_transient( 'executed_activities' );
 $max_activities      = get_transient( 'max_activities' ); // To do not count this at every run

 if ( $executed_activities < $max_activities ) {
     wp_schedule_single_event( strtotime( '+10 seconds' ), "myplugin_cron", [$offset + $per_run]);

     // Hour to do not lose it during process, but to be sure it will clear itself in case of trouble
     set_transient( 'executed_activities', $executed_activities + $per_run, HOUR_IN_SECONDS ) 
 } else {
     // Day job is done, you do not need this.
     delete_transient( 'executed_activities' );
     delete_transient( 'max_activities' );
 }

本文标签: phpHow can I cause run wpcron to trigger sequentially