Commit 645747a7 authored by Erick Hitter's avatar Erick Hitter Committed by GitHub
Browse files

Merge pull request #23 from Automattic/develop

Slow post ID growth and improve Internal Jobs
parents 594d5419 9b92d407
......@@ -197,7 +197,7 @@ class Cron_Options_CPT extends Singleton {
$job_exists = $this->job_exists( $event['timestamp'], $event['action'], $event['instance'] );
if ( ! $job_exists ) {
$this->create_job( $event['timestamp'], $event['action'], $event['args'] );
$this->create_or_update_job( $event['timestamp'], $event['action'], $event['args'] );
}
}
}
......@@ -232,7 +232,7 @@ class Cron_Options_CPT extends Singleton {
*
* Uses a direct query to avoid stale caches that result in duplicate events
*/
private function job_exists( $timestamp, $action, $instance, $return_id = false ) {
public function job_exists( $timestamp, $action, $instance, $return_id = false ) {
global $wpdb;
$exists = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_name = %s AND post_type = %s AND post_status = %s LIMIT 1;", $this->event_name( $timestamp, $action, $instance ), self::POST_TYPE, self::POST_STATUS_PENDING ) );
......@@ -250,7 +250,7 @@ class Cron_Options_CPT extends Singleton {
* Can't call `wp_insert_post()` because `wp_unique_post_slug()` breaks the plugin's expectations
* Also doesn't call `wp_insert_post()` because this function is needed before post types and capabilities are ready.
*/
public function create_job( $timestamp, $action, $args ) {
public function create_or_update_job( $timestamp, $action, $args, $update_id = null ) {
// Limit how many events to insert at once
if ( ! Lock::check_lock( self::LOCK, 5 ) ) {
return false;
......@@ -297,8 +297,13 @@ class Cron_Options_CPT extends Singleton {
// Set this so it isn't empty, even though it serves us no purpose
$job_post['guid'] = esc_url( add_query_arg( self::POST_TYPE, $job_post['post_name'], home_url( '/' ) ) );
// Create the post
// Create the post, or update an existing entry to run again in the future
if ( is_int( $update_id ) && $update_id > 0 ) {
$inserted = $wpdb->update( $wpdb->posts, $job_post, array( 'ID' => $update_id, ) );
$this->posts_to_clean[] = $update_id;
} else {
$inserted = $wpdb->insert( $wpdb->posts, $job_post );
}
// Clear caches for new posts once the post type is registered
if ( $inserted ) {
......
......@@ -164,6 +164,9 @@ class Events extends Singleton {
*/
private function update_event_record( $event ) {
if ( false !== $event['schedule'] ) {
// Get the existing ID
$job_id = Cron_Options_CPT::instance()->job_exists( $event['timestamp'], $event['action'], $event['instance'], true );
// Re-implements much of the logic from `wp_reschedule_event()`
$schedules = wp_get_schedules();
$interval = 0;
......@@ -178,7 +181,7 @@ class Events extends Singleton {
$interval = $event['interval'];
}
// If we have an interval, create a new event entry
// If we have an interval, update the existing event entry
if ( 0 != $interval ) {
// Determine new timestamp, according to how `wp_reschedule_event()` does
$now = time();
......@@ -197,10 +200,17 @@ class Events extends Singleton {
'interval' => $interval,
);
Cron_Options_CPT::instance()->create_job( $new_timestamp, $event['action'], $event_args );
// Update CPT store
Cron_Options_CPT::instance()->create_or_update_job( $new_timestamp, $event['action'], $event_args, $job_id );
// If the event could be rescheduled, don't then delete it :)
if ( is_int( $job_id ) && $job_id > 0 ) {
return;
}
}
}
// Either event doesn't recur, or the interval couldn't be determined
Cron_Options_CPT::instance()->mark_job_completed( $event['timestamp'], $event['action'], $event['instance'] );
}
}
......
......@@ -53,7 +53,8 @@ class Internal_Events extends Singleton {
);
// Register hooks
add_action( 'wp_loaded', array( $this, 'schedule_internal_events' ) );
add_action( 'admin_init', array( $this, 'schedule_internal_events' ) );
add_action( 'rest_api_init', array( $this, 'schedule_internal_events' ) );
add_filter( 'cron_schedules', array( $this, 'register_internal_events_schedules' ) );
foreach ( $this->internal_jobs as $internal_job ) {
......@@ -74,9 +75,19 @@ class Internal_Events extends Singleton {
public function schedule_internal_events() {
$when = strtotime( sprintf( '+%d seconds', JOB_QUEUE_WINDOW_IN_SECONDS ) );
$schedules = wp_get_schedules();
foreach ( $this->internal_jobs as $job_args ) {
if ( ! wp_next_scheduled( $job_args['action'] ) ) {
wp_schedule_event( $when, $job_args['schedule'], $job_args['action'] );
$interval = array_key_exists( $job_args['schedule'], $schedules ) ? $schedules[ $job_args['schedule'] ]['interval'] : 0;
$args = array(
'schedule' => $job_args['schedule'],
'args' => array(),
'interval' => $interval,
);
Cron_Options_CPT::instance()->create_or_update_job( $when, $job_args['action'], $args );
}
}
}
......
......@@ -37,6 +37,17 @@ class REST_API_Tests extends \WP_UnitTestCase {
public function test_get_items() {
$ev = Utils::create_test_event();
// Don't test internal events with this test
$internal_events = array(
'a8c_cron_control_force_publish_missed_schedules',
'a8c_cron_control_confirm_scheduled_posts',
'a8c_cron_control_delete_cron_option',
'a8c_cron_control_purge_completed_events',
);
foreach ( $internal_events as $internal_event ) {
wp_clear_scheduled_hook( $internal_event );
}
$request = new \WP_REST_Request( 'POST', '/' . \Automattic\WP\Cron_Control\REST_API::API_NAMESPACE . '/' . \Automattic\WP\Cron_Control\REST_API::ENDPOINT_LIST );
$request->set_body( wp_json_encode( array( 'secret' => \WP_CRON_CONTROL_SECRET, ) ) );
$request->set_header( 'content-type', 'application/json' );
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment