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 { ...@@ -197,7 +197,7 @@ class Cron_Options_CPT extends Singleton {
$job_exists = $this->job_exists( $event['timestamp'], $event['action'], $event['instance'] ); $job_exists = $this->job_exists( $event['timestamp'], $event['action'], $event['instance'] );
if ( ! $job_exists ) { 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 { ...@@ -232,7 +232,7 @@ class Cron_Options_CPT extends Singleton {
* *
* Uses a direct query to avoid stale caches that result in duplicate events * 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; 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 ) ); $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 { ...@@ -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 * 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. * 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 // Limit how many events to insert at once
if ( ! Lock::check_lock( self::LOCK, 5 ) ) { if ( ! Lock::check_lock( self::LOCK, 5 ) ) {
return false; return false;
...@@ -297,8 +297,13 @@ class Cron_Options_CPT extends Singleton { ...@@ -297,8 +297,13 @@ class Cron_Options_CPT extends Singleton {
// Set this so it isn't empty, even though it serves us no purpose // 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( '/' ) ) ); $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
$inserted = $wpdb->insert( $wpdb->posts, $job_post ); 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 // Clear caches for new posts once the post type is registered
if ( $inserted ) { if ( $inserted ) {
......
...@@ -164,6 +164,9 @@ class Events extends Singleton { ...@@ -164,6 +164,9 @@ class Events extends Singleton {
*/ */
private function update_event_record( $event ) { private function update_event_record( $event ) {
if ( false !== $event['schedule'] ) { 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()` // Re-implements much of the logic from `wp_reschedule_event()`
$schedules = wp_get_schedules(); $schedules = wp_get_schedules();
$interval = 0; $interval = 0;
...@@ -178,7 +181,7 @@ class Events extends Singleton { ...@@ -178,7 +181,7 @@ class Events extends Singleton {
$interval = $event['interval']; $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 ) { if ( 0 != $interval ) {
// Determine new timestamp, according to how `wp_reschedule_event()` does // Determine new timestamp, according to how `wp_reschedule_event()` does
$now = time(); $now = time();
...@@ -197,10 +200,17 @@ class Events extends Singleton { ...@@ -197,10 +200,17 @@ class Events extends Singleton {
'interval' => $interval, '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'] ); Cron_Options_CPT::instance()->mark_job_completed( $event['timestamp'], $event['action'], $event['instance'] );
} }
} }
......
...@@ -53,7 +53,8 @@ class Internal_Events extends Singleton { ...@@ -53,7 +53,8 @@ class Internal_Events extends Singleton {
); );
// Register hooks // 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' ) ); add_filter( 'cron_schedules', array( $this, 'register_internal_events_schedules' ) );
foreach ( $this->internal_jobs as $internal_job ) { foreach ( $this->internal_jobs as $internal_job ) {
...@@ -74,9 +75,19 @@ class Internal_Events extends Singleton { ...@@ -74,9 +75,19 @@ class Internal_Events extends Singleton {
public function schedule_internal_events() { public function schedule_internal_events() {
$when = strtotime( sprintf( '+%d seconds', JOB_QUEUE_WINDOW_IN_SECONDS ) ); $when = strtotime( sprintf( '+%d seconds', JOB_QUEUE_WINDOW_IN_SECONDS ) );
$schedules = wp_get_schedules();
foreach ( $this->internal_jobs as $job_args ) { foreach ( $this->internal_jobs as $job_args ) {
if ( ! wp_next_scheduled( $job_args['action'] ) ) { 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 { ...@@ -37,6 +37,17 @@ class REST_API_Tests extends \WP_UnitTestCase {
public function test_get_items() { public function test_get_items() {
$ev = Utils::create_test_event(); $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 = 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_body( wp_json_encode( array( 'secret' => \WP_CRON_CONTROL_SECRET, ) ) );
$request->set_header( 'content-type', 'application/json' ); $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