Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#75 WP 5.1 Pre-flight filters #91

Merged
merged 55 commits into from
Feb 18, 2020
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
c407351
Cache individual jobs.
peterwilsoncc Apr 22, 2019
ab1c8d3
Add argument to `Job::get_by_site()` to limit to due jobs only.
peterwilsoncc Apr 24, 2019
0fd0a65
Fix getting cache by ID.
peterwilsoncc Apr 24, 2019
cd9a758
Function to query jobs based on event parameters.
peterwilsoncc Apr 24, 2019
7047af2
Remove all trace of querying via schedule and interval.
peterwilsoncc Apr 24, 2019
c3c3ba9
Include waiting and running jobs in default query.
peterwilsoncc Apr 24, 2019
f90672e
Improve timestamp for searching.
peterwilsoncc Apr 26, 2019
d327960
Allow for unspecified hook.
peterwilsoncc Apr 26, 2019
4b39a6c
Allow for unlimited results.
peterwilsoncc Apr 26, 2019
bccbd98
Allow for raw results to be returned.
peterwilsoncc Apr 26, 2019
6d6c3d4
Use internal query for get_by_site.
peterwilsoncc Apr 26, 2019
2cb8260
Args is an array.
peterwilsoncc Apr 26, 2019
6d6e3a7
Query is unlimited when getting full cron array.
peterwilsoncc Apr 26, 2019
6cd340a
$results is now raw.
peterwilsoncc Apr 26, 2019
01d3d01
Timestamp is epoch timestamp already.
peterwilsoncc Apr 26, 2019
ab2ebfe
Remove the var_dump!
peterwilsoncc Apr 26, 2019
a4afe73
Add todo for time window.
peterwilsoncc Apr 26, 2019
f2cc1af
Preflight filter for `pre_schedule_event` covering single and recurri…
peterwilsoncc Apr 26, 2019
0ad5d9b
Remove call to undefined function.
peterwilsoncc Apr 26, 2019
af3a148
Preflight `wp_unschedule_event()`.
peterwilsoncc Apr 26, 2019
843e713
Allow public access to the cavalcade table name.
peterwilsoncc Apr 26, 2019
d4aef6b
Preflight `wp_clear_scheduled_hook()`.
peterwilsoncc Apr 26, 2019
be926c4
Preflight `wp_unschedule_hook()`.
peterwilsoncc Apr 26, 2019
922f9e6
Preflight `wp_get_scheduled_event()`.
peterwilsoncc Apr 26, 2019
37d1151
Fix past and future shortcuts.
peterwilsoncc Apr 26, 2019
876bba9
Preflight `wp_get_ready_cron_jobs()`.
peterwilsoncc Apr 26, 2019
ad3d14a
Preflight `wp_reschedule_event()`.
peterwilsoncc Apr 26, 2019
fdf72b7
Exclude preflight filters from core.
peterwilsoncc Apr 26, 2019
bbb489d
Allow for timestamp range.
peterwilsoncc Apr 26, 2019
1d40578
Ensure duplicate single events are not scheduled.
peterwilsoncc Apr 26, 2019
2f0554e
Improve comment.
peterwilsoncc Apr 26, 2019
e5bc052
Improve setting time range for testing duplicate events.
peterwilsoncc Apr 26, 2019
e5f8b29
merge master
roborourke Jan 16, 2020
33c68d0
merge master
roborourke Jan 16, 2020
4fe14d7
fix constant name
roborourke Jan 16, 2020
b50905f
use reset to get first result in connector
roborourke Jan 21, 2020
a5b54d2
use wp_cache_delete_group if supported
roborourke Jan 21, 2020
e95c1c3
add global cache group
roborourke Jan 21, 2020
c069644
use cache last changed method to invalidate group
roborourke Jan 23, 2020
601c8a2
Only require plugin once.
peterwilsoncc Jan 25, 2020
adecadc
Cast timestamps to integer.
peterwilsoncc Jan 25, 2020
6831d85
Remove Cavalcade job object from return value of `wp_get_scheduled_ev…
peterwilsoncc Jan 25, 2020
40e0ae4
Reset array rather than assume indexing.
peterwilsoncc Jan 25, 2020
ffd5fd1
CS Fixes: whitespace.
peterwilsoncc Jan 25, 2020
f863e9b
Add note explaining design difference in reschedule function.
peterwilsoncc Jan 25, 2020
6d1130d
Merge pull request #94 from peterwilsoncc/75-wp51-filters-fork-2
roborourke Jan 27, 2020
cf3028f
re introduce test group 45976
roborourke Jan 27, 2020
b6fcd73
Merge branch 'master' of github.com:humanmade/Cavalcade into 75-wp51-…
roborourke Jan 27, 2020
edca203
Merge branch 'master' of github.com:humanmade/Cavalcade into 75-wp51-…
roborourke Feb 14, 2020
0c8ad2f
review updates, simplify the timestamp logic
roborourke Feb 14, 2020
47b6fdf
remove use of reset & check for WP_Error
roborourke Feb 14, 2020
298ea91
fix mah boob
roborourke Feb 14, 2020
2c86202
Fire `schedule_event` filter before scheduling.
peterwilsoncc Feb 17, 2020
e22ef32
filter respection
roborourke Feb 17, 2020
10fc940
priority to 10
roborourke Feb 18, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ install:
- composer install
- bash tests/install-tests.sh wordpress_test root '' 127.0.0.1 $WP_VERSION
script:
- vendor/bin/phpunit --exclude-group cron
# 32656 tests are related to the pre_*_event filters which we're using already
- vendor/bin/phpunit --exclude-group 32656
notifications:
email: false
222 changes: 194 additions & 28 deletions inc/class-job.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ public function save() {
}
}

wp_cache_delete( 'jobs', 'cavalcade-jobs' );

if ( $this->is_created() ) {
$where = [
'id' => $this->id,
Expand All @@ -69,6 +67,9 @@ public function save() {
$result = $wpdb->insert( $this->get_table(), $data, $this->row_format( $data ) );
$this->id = $wpdb->insert_id;
}

self::flush_query_cache();
wp_cache_set( "job::{$this->id}", $this, 'cavalcade-jobs' );
}

public function delete( $options = [] ) {
Expand All @@ -89,13 +90,13 @@ public function delete( $options = [] ) {
];
$result = $wpdb->delete( $this->get_table(), $where, $this->row_format( $where ) );

wp_cache_delete( 'jobs', 'cavalcade-jobs' );
self::flush_query_cache();
wp_cache_delete( "job::{$this->id}", 'cavalcade-jobs' );

return (bool) $result;

}

protected static function get_table() {
public static function get_table() {
global $wpdb;
return $wpdb->base_prefix . 'cavalcade_jobs';
}
Expand Down Expand Up @@ -127,6 +128,7 @@ protected static function to_instance( $row ) {
$job->schedule = get_schedule_by_interval( $row->interval );
}

wp_cache_set( "job::{$job->id}", $job, 'cavalcade-jobs' );
return $job;
}

Expand Down Expand Up @@ -155,6 +157,11 @@ public static function get( $job ) {

$job = absint( $job );

$cached_job = wp_cache_get( "job::{$job}", 'cavalcade-jobs' );
if ( $cached_job ) {
return $cached_job;
}

$suppress = $wpdb->suppress_errors();
$job = $wpdb->get_row( $wpdb->prepare( 'SELECT * FROM ' . static::get_table() . ' WHERE id = %d', $job ) );
$wpdb->suppress_errors( $suppress );
Expand All @@ -172,10 +179,10 @@ public static function get( $job ) {
* @param int|stdClass $site Site ID, or site object from {@see get_blog_details}
* @param bool $include_completed Should we include completed jobs?
* @param bool $include_failed Should we include failed jobs?
* @param bool $exclude_future Should we exclude future (not ready) jobs?
* @return Job[]|WP_Error Jobs on success, error otherwise.
*/
public static function get_by_site( $site, $include_completed = false, $include_failed = false ) {
global $wpdb;
public static function get_by_site( $site, $include_completed = false, $include_failed = false, $exclude_future = false ) {

// Allow passing a site object in
if ( is_object( $site ) && isset( $site->blog_id ) ) {
Expand All @@ -186,39 +193,198 @@ public static function get_by_site( $site, $include_completed = false, $include_
return new WP_Error( 'cavalcade.job.invalid_site_id' );
}

if ( ! $include_completed && ! $include_failed ) {
$results = wp_cache_get( 'jobs', 'cavalcade-jobs' );
$args = [
'site' => $site,
'args' => null,
'statuses' => [ 'waiting', 'running' ],
'limit' => 0,
'__raw' => true,
];

if ( $include_completed ) {
$args['statuses'][] = 'completed';
}
if ( $include_failed ) {
$args['statuses'][] = 'failed';
}
if ( $exclude_future ) {
$args['timestamp'] = 'past';
}

if ( isset( $results ) && ! $results ) {
$statuses = [ 'waiting', 'running' ];
if ( $include_completed ) {
$statuses[] = 'completed';
}
if ( $include_failed ) {
$statuses[] = 'failed';
}
$results = static::get_jobs_by_query( $args );

// Find all scheduled events for this site
$table = static::get_table();
if ( empty( $results ) ) {
return [];
}

$sql = "SELECT * FROM `{$table}` WHERE site = %d";
$sql .= ' AND status IN(' . implode( ',', array_fill( 0, count( $statuses ), '%s' ) ) . ')';
$query = $wpdb->prepare( $sql, array_merge( [ $site ], $statuses ) );
$results = $wpdb->get_results( $query );
return static::to_instances( $results );
}

if ( ! $include_completed && ! $include_failed ) {
wp_cache_set( 'jobs', $results, 'cavalcade-jobs' );
}
/**
* Query jobs database.
*
* Returns an array of Job instances for the current site based
* on the paramaters.
*
* @todo: allow searching within time window for duplicate events.
*
* @param array|\stdClass $args {
* @param string $hook Jobs hook to return. Optional.
* @param int|string|null $timestamp Timestamp to search for. Optional.
* String shortcuts `future`: > NOW(); `past`: <= NOW()
* @param array $args Cron job arguments.
* @param int|object $site Site to query. Default current site.
* @param array $statuses Job statuses to query. Default to waiting and running.
* @param int $limit Max number of jobs to return. Default 1.
* @param string $order ASC or DESC. Default ASC.
* }
* @return Job[]|WP_Error Jobs on success, error otherwise.
*/
public static function get_jobs_by_query( $args = [] ) {
global $wpdb;
$args = (array) $args;
$results = [];

$defaults = [
'timestamp' => null,
'hook' => null,
'args' => [],
'site' => get_current_blog_id(),
'statuses' => [ 'waiting', 'running' ],
'limit' => 1,
'order' => 'ASC',
'__raw' => false,
];

$args = wp_parse_args( $args, $defaults );

/**
* Filters the get_jobs_by_query() arguments.
*
* An example use case would be to enforce limits on the number of results
* returned if you run into performance problems.
*
* @param array $args {
* @param string $hook Jobs hook to return. Optional.
* @param int|string|array $timestamp Timestamp to search for. Optional.
* String shortcuts `future`: > NOW(); `past`: <= NOW()
* Array of 2 time stamps will search between those dates.
* @param array $args Cron job arguments.
* @param int|object $site Site to query. Default current site.
* @param array $statuses Job statuses to query. Default to waiting and running.
* Possible values are 'waiting', 'running', 'completed' and 'failed'.
* @param int $limit Max number of jobs to return. Default 1.
* @param string $order ASC or DESC. Default ASC.
* @param bool $__raw If true return the raw array of data rather than Job objects.
* }
*/
$args = apply_filters( 'cavalcade.get_jobs_by_query.args', $args );

// Allow passing a site object in
if ( is_object( $args['site'] ) && isset( $args['site']->blog_id ) ) {
$args['site'] = $args['site']->blog_id;
}

if ( empty( $results ) ) {
return [];
if ( ! is_numeric( $args['site'] ) ) {
return new WP_Error( 'cavalcade.job.invalid_site_id' );
}

if ( ! empty( $args['hook'] ) && ! is_string( $args['hook'] ) ) {
return new WP_Error( 'cavalcade.job.invalid_hook_name' );
}

if ( ! is_array( $args['args'] ) && ! is_null( $args['args'] ) ) {
return new WP_Error( 'cavalcade.job.invalid_event_arguments' );
}

if ( ! is_numeric( $args['limit'] ) ) {
return new WP_Error( 'cavalcade.job.invalid_limit' );
}

$args['limit'] = absint( $args['limit'] );

// Find all scheduled events for this site
$table = static::get_table();

$sql = "SELECT * FROM `{$table}` WHERE site = %d";
$sql_params[] = $args['site'];

if ( is_string( $args['hook'] ) ) {
$sql .= ' AND hook = %s';
$sql_params[] = $args['hook'];
}

if ( ! is_null( $args['args'] ) ) {
$sql .= ' AND args = %s';
$sql_params[] = serialize( $args['args'] );
}

// Timestamp 'future' shortcut.
if ( $args['timestamp'] === 'future' ) {
$sql .= " AND nextrun > %s";
$sql_params[] = date( DATE_FORMAT );
}

// Timestamp past shortcut.
if ( $args['timestamp'] === 'past' ) {
$sql .= " AND nextrun <= %s";
$sql_params[] = date( DATE_FORMAT );
}

// Timestamp array range.
if ( is_array( $args['timestamp'] ) && count( $args['timestamp'] ) === 2 ) {
$sql .= ' AND nextrun BETWEEN %s AND %s';
$sql_params[] = date( DATE_FORMAT, (int) $args['timestamp'][0] );
$sql_params[] = date( DATE_FORMAT, (int) $args['timestamp'][1] );
}

// Default integer timestamp.
if ( is_int( $args['timestamp'] ) ) {
$sql .= ' AND nextrun = %s';
$sql_params[] = date( DATE_FORMAT, (int) $args['timestamp'] );
}

$sql .= ' AND status IN(' . implode( ',', array_fill( 0, count( $args['statuses'] ), '%s' ) ) . ')';
$sql_params = array_merge( $sql_params, $args['statuses'] );

$sql .= ' ORDER BY nextrun';
if ( $args['order'] === 'DESC' ) {
$sql .= ' DESC';
} else {
$sql .= ' ASC';
}

if ( $args['limit'] > 0 ) {
$sql .= ' LIMIT %d';
$sql_params[] = $args['limit'];
}

// Cache results.
$last_changed = wp_cache_get_last_changed( 'cavalcade-jobs' );
$query_hash = sha1( serialize( [ $sql, $sql_params ] ) ) . "::{$last_changed}";
$found = null;
$results = wp_cache_get( "jobs::{$query_hash}", 'cavalcade-jobs', true, $found );

if ( ! $found ) {
$query = $wpdb->prepare( $sql, $sql_params );
$results = $wpdb->get_results( $query );
wp_cache_set( "jobs::{$query_hash}", $results, 'cavalcade-jobs' );
}

if ( $args['__raw'] === true ) {
return $results;
}

return static::to_instances( $results );
}

/**
* Invalidates existing query cache keys by updating last changed time.
*/
public static function flush_query_cache() {
wp_cache_set( 'last_changed', microtime(), 'cavalcade-jobs' );
}

/**
* Get the (printf-style) format for a given column.
*
Expand Down
Loading