Skip to content

Commit

Permalink
Merge pull request #455 from woocommerce/allow_alternate_storage_engine
Browse files Browse the repository at this point in the history
* Increase default $args length limit to 8000 bytes
* Allow data stores to set $args length limit
  • Loading branch information
rrennick authored Feb 12, 2020
2 parents bcc501f + 1cff963 commit 90454ed
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 15 deletions.
12 changes: 6 additions & 6 deletions classes/abstracts/ActionScheduler_Store.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ abstract class ActionScheduler_Store extends ActionScheduler_Store_Deprecated {
private static $store = NULL;

/** @var int */
private static $max_index_length = 191;
protected static $max_args_length = 191;

/**
* @param ActionScheduler_Action $action
Expand Down Expand Up @@ -217,14 +217,14 @@ protected function validate_schedule( $schedule, $action_id ) {
* InnoDB indexes have a maximum size of 767 bytes by default, which is only 191 characters with utf8mb4.
*
* Previously, AS wasn't concerned about args length, as we used the (unindex) post_content column. However,
* as we prepare to move to custom tables, and can use an indexed VARCHAR column instead, we want to warn
* developers of this impending requirement.
* with custom tables, we use an indexed VARCHAR column instead.
*
* @param ActionScheduler_Action $action
* @param ActionScheduler_Action $action Action to be validated.
* @throws InvalidArgumentException When json encoded args is too long.
*/
protected function validate_action( ActionScheduler_Action $action ) {
if ( strlen( json_encode( $action->get_args() ) ) > self::$max_index_length ) {
throw new InvalidArgumentException( __( 'ActionScheduler_Action::$args too long. To ensure the args column can be indexed, action args should not be more than 191 characters when encoded as JSON.', 'action-scheduler' ) );
if ( strlen( json_encode( $action->get_args() ) ) > static::$max_args_length ) {
throw new InvalidArgumentException( sprintf( __( 'ActionScheduler_Action::$args too long. To ensure the args column can be indexed, action args should not be more than %d characters when encoded as JSON.', 'action-scheduler' ), static::$max_args_length ) );
}
}

Expand Down
51 changes: 46 additions & 5 deletions classes/data-stores/ActionScheduler_DBStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
*/
class ActionScheduler_DBStore extends ActionScheduler_Store {

/** @var int */
protected static $max_args_length = 8000;

/** @var int */
protected static $max_index_length = 191;

/**
* Initialize the data store
*
Expand Down Expand Up @@ -39,10 +45,17 @@ public function save_action( ActionScheduler_Action $action, \DateTime $date = n
'status' => ( $action->is_finished() ? self::STATUS_COMPLETE : self::STATUS_PENDING ),
'scheduled_date_gmt' => $this->get_scheduled_date_string( $action, $date ),
'scheduled_date_local' => $this->get_scheduled_date_string_local( $action, $date ),
'args' => json_encode( $action->get_args() ),
'schedule' => serialize( $action->get_schedule() ),
'group_id' => $this->get_group_id( $action->get_group() ),
];
$args = wp_json_encode( $action->get_args() );
if ( strlen( $args ) <= static::$max_index_length ) {
$data['args'] = $args;
} else {
$data['args'] = $this->hash_args( $args );
$data['extended_args'] = $args;
}

$wpdb->insert( $wpdb->actionscheduler_actions, $data );
$action_id = $wpdb->insert_id;

Expand All @@ -62,6 +75,29 @@ public function save_action( ActionScheduler_Action $action, \DateTime $date = n
}
}

/**
* Generate a hash from json_encoded $args using MD5 as this isn't for security.
*
* @param string $args JSON encoded action args.
* @return string
*/
protected function hash_args( $args ) {
return md5( $args );
}

/**
* Get action args query param value from action args.
*
* @param array $args Action args.
* @return string
*/
protected function get_args_for_query( $args ) {
$encoded = wp_json_encode( $args );
if ( strlen( $encoded ) <= static::$max_index_length ) {
return $encoded;
}
return $this->hash_args( $encoded );
}
/**
* Get a group's ID based on its name/slug.
*
Expand Down Expand Up @@ -118,6 +154,11 @@ public function fetch_action( $action_id ) {
return $this->get_null_action();
}

if ( ! empty( $data->extended_args ) ) {
$data->args = $data->extended_args;
unset( $data->extended_args );
}

try {
$action = $this->make_action_from_db_record( $data );
} catch ( ActionScheduler_InvalidActionException $exception ) {
Expand Down Expand Up @@ -188,7 +229,7 @@ public function find_action( $hook, $params = [] ) {
$args[] = $hook;
if ( ! is_null( $params[ 'args' ] ) ) {
$query .= " AND a.args=%s";
$args[] = json_encode( $params[ 'args' ] );
$args[] = $this->get_args_for_query( $params[ 'args' ] );
}

$order = 'ASC';
Expand Down Expand Up @@ -265,7 +306,7 @@ protected function get_query_actions_sql( array $query, $select_or_count = 'sele
}
if ( ! is_null( $query[ 'args' ] ) ) {
$sql .= " AND a.args=%s";
$sql_params[] = json_encode( $query[ 'args' ] );
$sql_params[] = $this->get_args_for_query( $query[ 'args' ] );
}

if ( $query[ 'status' ] ) {
Expand Down Expand Up @@ -301,8 +342,8 @@ protected function get_query_actions_sql( array $query, $select_or_count = 'sele
}

if ( ! empty( $query['search'] ) ) {
$sql .= " AND (a.hook LIKE %s OR a.args LIKE %s";
for( $i = 0; $i < 2; $i++ ) {
$sql .= " AND (a.hook LIKE %s OR (a.extended_args IS NULL AND a.args LIKE %s) OR a.extended_args LIKE %s";
for( $i = 0; $i < 3; $i++ ) {
$sql_params[] = sprintf( '%%%s%%', $query['search'] );
}

Expand Down
6 changes: 4 additions & 2 deletions classes/data-stores/ActionScheduler_wpPostStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -809,9 +809,11 @@ public function migration_dependencies_met( $setting ) {

$dependencies_met = get_transient( self::DEPENDENCIES_MET );
if ( empty( $dependencies_met ) ) {
$found_action = $wpdb->get_var(
$maximum_args_length = apply_filters( 'action_scheduler_maximum_args_length', 191 );
$found_action = $wpdb->get_var(
$wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND CHAR_LENGTH(post_content) > 191 LIMIT 1",
"SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND CHAR_LENGTH(post_content) > %d LIMIT 1",
$maximum_args_length,
self::POST_TYPE
)
);
Expand Down
1 change: 0 additions & 1 deletion classes/schema/ActionScheduler_LoggerSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ protected function get_table_definition( $table ) {
global $wpdb;
$table_name = $wpdb->$table;
$charset_collate = $wpdb->get_charset_collate();
$max_index_length = 191; // @see wp_get_db_schema()
switch ( $table ) {

case self::LOG_TABLE:
Expand Down
3 changes: 2 additions & 1 deletion classes/schema/ActionScheduler_StoreSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ActionScheduler_StoreSchema extends ActionScheduler_Abstract_Schema {
/**
* @var int Increment this value to trigger a schema update.
*/
protected $schema_version = 2;
protected $schema_version = 3;

public function __construct() {
$this->tables = [
Expand Down Expand Up @@ -47,6 +47,7 @@ protected function get_table_definition( $table ) {
last_attempt_gmt datetime NOT NULL default '0000-00-00 00:00:00',
last_attempt_local datetime NOT NULL default '0000-00-00 00:00:00',
claim_id bigint(20) unsigned NOT NULL default '0',
extended_args varchar(8000) DEFAULT NULL,
PRIMARY KEY (action_id),
KEY hook (hook($max_index_length)),
KEY status (status),
Expand Down

0 comments on commit 90454ed

Please sign in to comment.