Coder Social home page Coder Social logo

wp-background-processing's Introduction

WP Background Processing

WP Background Processing can be used to fire off non-blocking asynchronous requests or as a background processing tool, allowing you to queue tasks. Check out the example plugin or read the accompanying article.

Inspired by TechCrunch WP Asynchronous Tasks.

Requires PHP 5.6+

Install

The recommended way to install this library in your project is by loading it through Composer:

composer require deliciousbrains/wp-background-processing

It is highly recommended to prefix wrap the library class files using the Mozart package, to prevent collisions with other projects using this same library.

Usage

Async Request

Async requests are useful for pushing slow one-off tasks such as sending emails to a background process. Once the request has been dispatched it will process in the background instantly.

Extend the WP_Async_Request class:

class WP_Example_Request extends WP_Async_Request {

	/**
	 * @var string
	 */
	protected $prefix = 'my_plugin';

	/**
	 * @var string
	 */
	protected $action = 'example_request';

	/**
	 * Handle a dispatched request.
	 *
	 * Override this method to perform any actions required
	 * during the async request.
	 */
	protected function handle() {
		// Actions to perform.
	}

}

protected $prefix

Should be set to a unique prefix associated with your plugin, theme, or site's custom function prefix.

protected $action

Should be set to a unique name.

protected function handle()

Should contain any logic to perform during the non-blocking request. The data passed to the request will be accessible via $_POST.

Dispatching Requests

Instantiate your request:

$this->example_request = new WP_Example_Request();

Add data to the request if required:

$this->example_request->data( array( 'value1' => $value1, 'value2' => $value2 ) );

Fire off the request:

$this->example_request->dispatch();

Chaining is also supported:

$this->example_request->data( array( 'data' => $data ) )->dispatch();

Background Process

Background processes work in a similar fashion to async requests, but they allow you to queue tasks. Items pushed onto the queue will be processed in the background once the queue has been saved and dispatched. Queues will also scale based on available server resources, so higher end servers will process more items per batch. Once a batch has completed, the next batch will start instantly.

Health checks run by default every 5 minutes to ensure the queue is running when queued items exist. If the queue has failed it will be restarted.

Queues work on a first in first out basis, which allows additional items to be pushed to the queue even if it’s already processing. Saving a new batch of queued items and dispatching while another background processing instance is already running will result in the dispatch shortcutting out and the existing instance eventually picking up the new items and processing them when it is their turn.

Extend the WP_Background_Process class:

class WP_Example_Process extends WP_Background_Process {

	/**
	 * @var string
	 */
	protected $prefix = 'my_plugin';

	/**
	 * @var string
	 */
	protected $action = 'example_process';

	/**
	 * Perform task with queued item.
	 *
	 * Override this method to perform any actions required on each
	 * queue item. Return the modified item for further processing
	 * in the next pass through. Or, return false to remove the
	 * item from the queue.
	 *
	 * @param mixed $item Queue item to iterate over.
	 *
	 * @return mixed
	 */
	protected function task( $item ) {
		// Actions to perform.

		return false;
	}

	/**
	 * Complete processing.
	 *
	 * Override if applicable, but ensure that the below actions are
	 * performed, or, call parent::complete().
	 */
	protected function complete() {
		parent::complete();

		// Show notice to user or perform some other arbitrary task...
	}

}

protected $prefix

Should be set to a unique prefix associated with your plugin, theme, or site's custom function prefix.

protected $action

Should be set to a unique name.

protected function task( $item )

Should contain any logic to perform on the queued item. Return false to remove the item from the queue or return $item to push it back onto the queue for further processing. If the item has been modified and is pushed back onto the queue the current state will be saved before the batch is exited.

protected function complete()

Optionally contain any logic to perform once the queue has completed.

Dispatching Processes

Instantiate your process:

$this->example_process = new WP_Example_Process();

Note: You must instantiate your process unconditionally. All requests should do this, even if nothing is pushed to the queue.

Push items to the queue:

foreach ( $items as $item ) {
    $this->example_process->push_to_queue( $item );
}

An item can be any valid PHP value, string, integer, array or object. If needed, the $item is serialized when written to the database.

Save and dispatch the queue:

$this->example_process->save()->dispatch();

Handling serialized objects in queue items

Queue items that contain non-scalar values are serialized when stored in the database. To avoid potential security issues during unserialize, this library provides the option to set the allowed_classes option when calling unserialize() which limits which classes can be instantiated. It's kept internally as the protected $allowed_batch_data_classes property.

To maintain backward compatibility the default value is true, meaning that any serialized object will be instantiated. Please note that this default behavior may change in a future major release.

We encourage all users of this library to take advantage of setting a strict value for $allowed_batch_data_classes. If possible, set the value to false to disallow any objects from being instantiated, or a very limited list of class names, see examples below.

Objects in the serialized string that are not allowed to be instantiated will instead get the class type __PHP_Incomplete_Class.

Overriding the default $allowed_batch_data_classes

The default behavior can be overridden by passing an array of allowed classes to the constructor:

$allowed_batch_data_classes = array( MyCustomItem::class, MyItemHelper::class );
$this->example_process = new WP_Example_Process( $allowed_batch_data_classes );

Or, set the value to false:

$this->example_process = new WP_Example_Process( false );

Another way to change the default is to override the $allowed_batch_data_classes property in your process class:

class WP_Example_Process extends WP_Background_Process {

	/**
	 * @var string
	 */
	protected $prefix = 'my_plugin';

	/**
	 * @var string
	 */
	protected $action = 'example_process';

	/**
	 *
	 * @var bool|array
	 */
	protected $allowed_batch_data_classes = array( MyCustomItem::class, MyItemHelper::class );
	...

Background Process Status

A background process can be queued, processing, paused, cancelled, or none of the above (not started or has completed).

Queued

To check whether a background process has queued items use is_queued().

if ( $this->example_process->is_queued() ) {
    // Do something because background process has queued items, e.g. add notice in admin UI.
}
Processing

To check whether a background process is currently handling a queue of items use is_processing().

if ( $this->example_process->is_processing() ) {
    // Do something because background process is running, e.g. add notice in admin UI.
}
Paused

You can pause a background process with pause().

$this->example_process->pause();

The currently processing batch will continue until it either completes or reaches the time or memory limit. At that point it'll unlock the process and either complete the batch if the queue is empty, or perform a dispatch that will result in the handler removing the healthcheck cron and firing a "paused" action.

To check whether a background process is currently paused use is_paused().

if ( $this->example_process->is_paused() ) {
    // Do something because background process is paused, e.g. add notice in admin UI.
}

You can perform an action in response to background processing being paused by handling the "paused" action for the background process's identifier ($prefix + $action).

add_action( 'my_plugin_example_process_paused', function() {
    // Do something because background process is paused, e.g. add notice in admin UI.
});

You can resume a background process with resume().

$this->example_process->resume();

You can perform an action in response to background processing being resumed by handling the "resumed" action for the background process's identifier ($prefix + $action).

add_action( 'my_plugin_example_process_resumed', function() {
    // Do something because background process is resumed, e.g. add notice in admin UI.
});
Cancelled

You can cancel a background process with cancel().

$this->example_process->cancel();

The currently processing batch will continue until it either completes or reaches the time or memory limit. At that point it'll unlock the process and either complete the batch if the queue is empty, or perform a dispatch that will result in the handler removing the healthcheck cron, deleting all batches of queued items and firing a "cancelled" action.

To check whether a background process is currently cancelled use is_cancelled().

if ( $this->example_process->is_cancelled() ) {
    // Do something because background process is cancelled, e.g. add notice in admin UI.
}

You can perform an action in response to background processing being cancelled by handling the "cancelled" action for the background process's identifier ($prefix + $action).

add_action( 'my_plugin_example_process_cancelled', function() {
    // Do something because background process is paused, e.g. add notice in admin UI.
});

The "cancelled" action fires once the queue has been cleared down and cancelled status removed. After which is_cancelled() will no longer be true as the background process is now dormant.

Active

To check whether a background process has queued items, is processing, is paused, or is cancelling, use is_active().

if ( $this->example_process->is_active() ) {
    // Do something because background process is active, e.g. add notice in admin UI.
}

If a background process is not active, then it either has not had anything queued yet and not started, or has finished processing all queued items.

BasicAuth

If your site is behind BasicAuth, both async requests and background processes will fail to complete. This is because WP Background Processing relies on the WordPress HTTP API, which requires you to attach your BasicAuth credentials to requests. The easiest way to do this is using the following filter:

function wpbp_http_request_args( $r, $url ) {
	$r['headers']['Authorization'] = 'Basic ' . base64_encode( USERNAME . ':' . PASSWORD );

	return $r;
}
add_filter( 'http_request_args', 'wpbp_http_request_args', 10, 2);

Contributing

Contributions are welcome via Pull Requests, but please do raise an issue before working on anything to discuss the change if there isn't already an issue. If there is an approved issue you'd like to tackle, please post a comment on it to let people know you're going to have a go at it so that effort isn't wasted through duplicated work.

Unit & Style Tests

When working on the library, please add unit tests to the appropriate file in the tests directory that cover your changes.

Setting Up

We use the standard WordPress test libraries for running unit tests.

Please run the following command to set up the libraries:

bin/install-wp-tests.sh db_name db_user db_pass

Substitute db_name, db_user and db_pass as appropriate.

Please be aware that running the unit tests is a destructive operation, database tables will be cleared, so please use a database name dedicated to running unit tests. The standard database name usually used by the WordPress community is wordpress_test, e.g.

bin/install-wp-tests.sh wordpress_test root root

Please refer to the Initialize the testing environment locally section of the WordPress Handbook's Plugin Integration Tests entry should you run into any issues.

Running Unit Tests

To run the unit tests, simply run:

make test-unit

If the composer dependencies aren't in place, they'll be automatically installed first.

Running Style Tests

It's important that the code in the library use a consistent style to aid in quickly understanding it, and to avoid some common issues. PHP_Code_Sniffer is used with mostly standard WordPress rules to help check for consistency.

To run the style tests, simply run:

make test-style

If the composer dependencies aren't in place, they'll be automatically installed first.

Running All Tests

To make things super simple, just run the following to run all tests:

make

If the composer dependencies aren't in place, they'll be automatically installed first.

Creating a PR

When creating a PR, please make sure to mention which GitHub issue is being resolved at the top of the description, e.g.:

Resolves #123

The unit and style tests will be run automatically, the PR will not be eligible for merge unless they pass, and the branch is up-to-date with master.

License

GPLv2+

wp-background-processing's People

Contributors

a5hleyrich avatar ahmedthegeek avatar andrewkvalheim avatar bhardie avatar bhubbard avatar boonebgorges avatar coenjacobs avatar eriktorsner avatar ianmjones avatar kagg-design avatar leewillis77 avatar markjaquith avatar mikejolley avatar no3x avatar polevaultweb avatar thekhorshed avatar verygoodplugins avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wp-background-processing's Issues

Maximum execution time reached on a WP Background Processing

I've used WP Background Processing to execute really long request, for example inserting 1000 users.

I guess I was wrong by assuming that it split really long task in multiple WP-Crons because I reach the maximum execution time (30 seconds) in wp_hash_password() (called on user creation).

I have to split long task myself on multiple queue items ?

Do not use transients for locking

lock_process(), unlock_process() and is_process_running() use a transient for indicating whether the process is currently running.

I'm sure this is convenient as it allows you to specify an expiry time, helping avoid locks that get stale in the event of a job failing. However it's also unreliable since transients can disappear at any time. They're not guaranteed to persist until their expiration time which is a bad trait for a lock that's supposed to prevent double processing.

I'd suggest using normal options instead

Using on Plugin Activation/Deactivation

Hi there,

I want to call an asynchronous function upon Activation and Deactivation of my plugin (two different classes that extend "WP_Async_Request").

I'm finding however that these actions don't get called when dispatched.

Any idea if I'm missing something obvious, or whether execution would get halted for whatever reason?

Clarify version requirements?

The README.md says this plugin "Requires PHP 5.2."

Does this mean 5.2 is the minimum required version or the only compatible one?

Double dispatch loop?

It seems like WP_Background_Process calls method handle() which calls dispatch() where a parent::dispatch() is also done which in it's own turn fires handle() again. I can do more thorough testing on this but basically handle() is called twice in a single process and could cause some overload.

Request are not loading

Hello, I tried to implement your solution according to the main page using the WP_Background_Process
I was able to submit the job. But now, nothing else works. No requests work, even local ones. I can't access the admin page because that is also not working. Are there files that I could delete from the database to make it work again?

EDIT
Actually, the requests work. But after processing the cron job first. I had set a timeout of 2 minutes. That's why it was taking so long. I thought the cron jobs are supposed to happen asynchronously??

background processing is not working with plugin activation

Hello, I am using plugin boilerplate of DevinVinson/WordPress-Plugin-Boilerplate. I want to use the background processing when the plugin is activated. my code is

class Plugin_Name_Activator {

 private $background_process;
public function __construct() {
	require_once plugin_dir_path( __FILE__ ) . 'includes/class-background-processing.php';
	$this->background_process = new Plugin_Name_Background_Processing();
   }

public function activate() {

  	$names = array(
				'Grant Buel',
				'Bryon Pennywell',
				'Jarred Mccuiston',
			);

			foreach ( $names as $name ) {
				$this->background_process->push_to_queue( $name );
			}

			$this->background_process->save()->dispatch();

}

the WP table options have a new row:

wp_background_process_batch_1f8a9447c3ecec7570a4de37af

But

Plugin_Name_Background_Processing() task($item) is never called. What is the problem????

when When I instantiate the class on 'plugins_loaded' action, it works just fine.

I want to work the background processing when activate the plugin, not with 'plugins_loaded' action

Class 'WP_Background_Process' not found

Hi there,

I am currently using the Wordpress Plugin Boilerplate (https://github.com/DevinVinson/WordPress-Plugin-Boilerplate) to build a plugin as I have many times before however now have a requirement for background processing.

I am also using woocommerce and what im wanting to do is upon payment complete is push the order to our warehouse management system but do this as a background task as opposed to taking up the time on the user-facing side.

Unfortunately when I am running through a test transaction I am constantly receiving

Class 'WP_Background_Process' not found in WCKhaos/includes/tasks/SendOrderTask.php on line 3

Have you any experience with WooCommerce, WPPB and your plugin or any advice so I can look further?

Thanks in advance

When a job is released should it reserialize so state can be preserved?

I note in the master branch when using the task method the object passed around could be manipulated so multiple passes over the same object could save state and pick up where the previous task left off.

Moving to the queue and handle branch I see the job gets serialized to the db when it's pushed to the queue, but if I want to just do a part of the job and then release back to the queue I can't preserve state within the job because the next time the queue reaches it the job is deserialized from the original push.

So I'm wondering if I'm doing it wrong and should be preserving the state of the job outside of it, or should it be serialized and written back to db when released back to the queue?

function handle() not firing

Hello!

Not sure if I'm missing something. I'm adding an item for an async request :

$example->example_request = new WP_Example_Request();
$example->example_request->data( array( 'value1' => 'the value 1', 'value2' => 'the value 2' ) );
$example->example_request->dispatch();

Then my class extension looks like this :

class WP_Example_Request extends WP_Async_Request {

    /**
     * @var string
     */
    protected $action = 'example_request';

    /**
     * Handle
     *
     * Override this method to perform any actions required
     * during the async request.
     */
    protected function handle() {
        error_log('test handle');
        // Actions to perform
    }

}

Not seeing "test handle" in the logs at all. So handle() is not firing at all. Any idea why? I must be missing something.

Unexpected entry

Hello there,
I'm wondering what the firsts lines means in this batch, N?:

	i:6;N;
	i:52;N;
	i:55;N;
	i:56;N;
	i:59;N;
	i:225;s:25:"RESI-805759-update-463567";
	i:226;s:19:"RESI-805764-import-";
	i:227;s:19:"RESI-805765-import-";
	i:228;s:19:"RESI-805766-import-";
	i:229;s:19:"RESI-805772-import-";
	i:230;s:19:"RESI-805783-import-";
	}

What is creating this entries with "N". I think it's not me, cause if it was that should be like that : i:6;s:1:"N"; right?

Thanks for reading, have a good day !
Anthony

dispatch() method never gets called

Here's my code:

functions.php or similar:

    // Necessary autoloaders here
   // @hooked at plugins_loaded with priority PHP_INT_MAX
    $setup_database = new WP_Async_Setup_Database();
    $setup_database->data( ['user_id' => 17] );
    $setup_database->dispatch();

WP_Async_Setup_Database.php:

<?php
class WP_Async_Setup_Database extends WP_Async_Request {

    /**
     * @var string
     */
    protected $action = 'setup_database';

    /**
     * Handle
     *
     * Override this method to perform any actions required
     * during the async request.
     */
    protected function handle() {
        // Stuff here that never gets called
        error_log("test", 1, 'myemail.com');
    }

}

Var_dumping $setup_database shows it has action, prefix, identifier and everything. The problem is the dispatch() method that never gets called. I added var_dump('test');exit; to public funciton dispatch() in wp-async-request.php (and wp-background-process.php) but those never get called... I added the dispatch() method to WP_Async_Setup_Database.php and it does get called.

PHP Notice occurs if there are no results for get_batch()

When I run this locally (PHP 5.6), I get the following notices if there is no data for the class to process:

[25-Aug-2016 14:12:03 UTC] PHP Notice:  Trying to get property of non-object in wp-background-process.php on line 281
[25-Aug-2016 14:12:03 UTC] PHP Notice:  Trying to get property of non-object in wp-background-process.php on line 282
[25-Aug-2016 14:12:03 UTC] PHP Warning:  Invalid argument supplied for foreach() in wp-background-process.php on line 299

I can do a pull request if you find it worthwhile - put in an is_object at 281 and 299?

WP Cron

Hi there,

Great solution by the way :) Already using it in a plugin I'm developing.

I have two questions:

  • Does this rely on WP Cron being enabled on a user's site to work properly?
  • Could this somehow act as a WP Cron replacement? I'm trying to think of how I could get it to do that.. If it could, it'd be amazing

Thanks a lot!

Callback in task function?

Hi there,

First of all thanks for this plugin, I almost got it working in a project I'm working on. I'm following the example of your example-plugin and that works flawless with your dummy really_long_running_task in the task function:

public function really_long_running_task() {
	return sleep( 5 );
}

However, I obviously want to place my own (memory heavy) function in there that uses the class Leafo\ScssPhp\Compiler.

But the result is not consistent, it never finishes the whole queue, while the data is pushed to the queue correctly.

My function with an exception handler looks something like this:

public function compileScss($value = null) {
	$compiler     = new Compiler();
	$compiler->setFormatter('Leafo\ScssPhp\Formatter\Crunched');

	try {
		$style             = $compiler->compile($value);
		return $style;
	} catch (\Exception $e) {
		error_log($e);
	}
}

There are no errors catched.
If I just return the $value (even with a sleep), it returns everything correctly.

How can I check if the function is done with one item, before passing the next one?
Any help is appreciated!

Thank you!

WordPress database error Got a packet bigger than 'max_allowed_packet' bytes

Hi,

WordPress database error Got a packet bigger than 'max_allowed_packet' bytes

I ran into this error when working with a huge amount of data (like a 20MB JSON of data).
Since the save() function saves all the $data in a single row of the wp_options table, if the data is actually a lot of data, it throws this error.

I solved it by following @No3x advice in #7 and doing the following:

foreach($jsonData as $data) {
    $this->wp_background_processing_instance->push_to_queue($data);
    $this->wp_background_processing_instance->save();
}
$this->wp_background_processing_instance->dispatch();

I put the dispatch function outside the loop, looks cleaner.

Then in my wp_background_processing class:

public function save() {
    parent::save();
    $this->data = [];
    return $this;
}

As stated in the referenced issue, this creates an option in the database for each item in the queue, and maybe this is not ideal. But at least this prevent the database error.

Is there a better way to prevent this?

cancel_process() has no effect

Have tried this several ways - on page reload and via ajax call. No matter what I do I cannot seem to stop the queue once it starts running.

Do background processes have to be created in the context wp-admin?

I am trying to set up a remote trigger for importing a large amount of information from a remote data source. When a certain url is hit with the right parameters, I fetch a file from ftp, and parse it into rows. I chunk the rows in groups of 750. Each row is a json string representing an associative array. I create a background process for each group of 750 rows. The processes do some actions (updating post meta fields with the array values) for every item. However, the task method never happens, I don't think it gets past the dispatch method.

The processes do get saved in the database successfully.

I've been var_dump-ing the wp_remote_post and getting an "http_request_failed" timeout response every time I dispatch.
So I'm wondering if it needs to be triggered from a logged in user context? Is there some dependency I am missing?

This is a pretty perfect plugin for what I need, if I can get it working -- thanks for taking the time on it @A5hleyRich

Please tell us max queue insert to database

To day i check wp_queue Put in insert only 3 queue database

exmple
I have 4 user : A , B , C ,D

wp_queue(userA)
wp_queue(userB)
wp_queue(userC)
wp_queue(userD) // D not work

Realy code

wp_quue(New NOTIFICATIONALLUSER(users));

In notificationAllUser

foreach(users as user)
wp_queue(new WP_Job_Notification($this->key, $this->message, $this->data,$user->ID,'promotion'));

but only three queue add to database

cancel_process not working

Hi,

cancel_process doesn't work as expected: it never stops processing queue items.

Am I missing something?

Call WP_Background_Process into functions.php

First thank for this great job. I just try to use this but not inside any plugin. I have my own class to send emails. and i want to use WP_Background_Process into my classes directly in my theme folder. I need to push and dispatch inside some action ? $this->process_all->push_to_queue( $name ); $this->process_all->save()->dispatch();

i put this directly on my functions fallback

Tutorial - How to get it working

Hey,

First of all I would like to thank the creators of this library. It is simply awesome!

However, it might be really tricky to get this up and running.

I would like to provide a detailed breakdown of common issues I faced implementing this and how to solve them.

The first thing to understand about this package is that it only consists of TWO files:

  • wp-async-request.php
  • wp-background-process.php (extends wp-async-request.php)

The first one is used to dispatch a single async request. The second one is a powerful batch processor of long-running tasks.

That said, you can either install this library as a plugin by cloning the repository and installing it as a plugin, or you can just add these two files to your project. I recommend the second approach, as you will have more control on when the classes will be available for you to use.

If you install it as a plugin, you have to be careful, because your async class will have to be hooked at plugins_loaded, and the class that calls your async class will have to load after that.

Minimal example with plugin:

<?php
/**
* Plugin name: Async Test with wp-brackground-processing as a plugin
*/

// We hook this at plugins_loaded, since WP_Background_Process is in wp-background-process.php
add_action('plugins_loaded', function() {
class BackgroundAsync extends WP_Background_Process
    {
        protected $action = 'just_testing';
        protected function task( $item ) {
            sleep(1);
            error_log($item);
            return false;
        }
    }
});

class BackgroundTest
{
    public function __construct() {
        $this->task = new BackgroundAsync;
        if (isset($_GET['async_test'])) {
            $names = ['name1', 'name2', 'name3', 'name4'];

            foreach ( $names as $name ) {
                $this->task->push_to_queue( $name );
            }

            $this->task->save()->dispatch();
        }
    }
}

// We hook this at plugins_loaded with priority 11, so that BackgroundAsync will already be loaded then.
add_action('plugins_loaded', function() {
    new BackgroundTest();
}, 11);

If you load the classes in your project, it gets easier:

<?php
require_once __DIR__ . '/wp-async-request.php';
require_once __DIR__ . '/wp-background-process.php';

class BackgroundAsync extends WP_Background_Process
{
    protected $action = 'just_testing';
    protected function task( $item ) {
        sleep(1);
        error_log($item);
        return false;
    }
}

class BackgroundTest
{
    public function __construct() {
        $this->task = new BackgroundAsync;
        if (isset($_GET['async_test'])) {
            $names = ['name1', 'name2', 'name3', 'name4'];

            foreach ( $names as $name ) {
                $this->task->push_to_queue( $name );
            }

            $this->task->save()->dispatch();
        }
    }
}
// Now we just need plugins_loaded here, because otherwise it throws a "Call to undefined function wp_create_nonce()"
add_action('plugins_loaded', function() {
    new BackgroundTest();
});

This should get you up and running for most cases. Just remember that BackgroundAsync class must be called somewhere during the execution of your script, because it is the instantiation of the class that registers the callback to the cron!! That's probably why a lot of people can't get this to work.

That means, you can then use something like:

// Somewhere in the execution of your plugin
add_action('plugins_loaded', function() {
    $backgroundAsync = new BackgroundAsync();
    $background = new BackgroundTest($backgroundAsync);
    $background->doSomethingThatWillPushToQueueThenSaveAndDispatch();
});

// BackgroundAsync.php
class BackgroundAsync { ... }

// BackgroundTest.php
class BackgroundTest {
    protected $async;
    public function __construct(BackgroundAsync $async) {
        $this->async = $async;
    }
    // ...
}

Other tips:

wp-cli is your friend debugging crons.

Example:
wp cron event list
wp cron event run wp_some_cron_event

That's it, I hope this tutorial helps people get up and running with this awesome library!

Debug Async request with PHPStorm?

Hi,

Thanks for your contribution to the community!

Is it possible to debug async classes with PHPStorm? I add my breakpoints but they don't stop when making async requests.

Tasks duplicated on a multiple app server environment

I have a site on AWS Elastic Beanstalk, and I've written a custom API integration for Gravity Forms, using their feed addon with background processing enabled. We're seeing an issue where when the site is under significant load, the same background process will get fired off twice (as seen due to duplicate/conflicting info on GF entries). When there are duplicate requests to the API, they come from 2 different IP addresses, so more than one app server is trying to process the task. My background task consists of a API request, that once a response is received writes data onto the entry.

Let me know what info can help diagnose, and I will provide what I can.

Question: sub-processes

Are there any constraints or limitations that could make impossible designing a background process that contains a set of background processes?
I've made two attempts to make and had a problem with initiating subprocesses.
Did anyone try anything similar?

wp-background-process do not dispatch

Hi Ashley,

Im trying yo use your lib to sync data from WP to external system but I could not make it work...im only trying to write to logs...

require_once plugin_dir_path( FILE ) . 'classes/wp-async-request.php';
require_once plugin_dir_path( FILE ) . 'classes/wp-background-process.php';

class EE_sfSDK extends WP_Background_Process {
protected $action = 'attendees_background_process';
protected function task( $registration ) {
//$this->really_long_running_task();
error_log("This is the task RUNNING of the WP_Background_Process");
return false;
}
protected function complete() {
parent::complete();
// Show notice to user or perform some other arbitrary task...
error_log("This is the task COMPLETED WP_Background_Process");
}

the elements are push correctly inside the queue:

foreach ($registrations as $registration) {
if ($registration instanceof EE_Registration) {
error_log("Event: ext_sync_to_salesforce push_to_queue!!");
$sfSDK->push_to_queue($registration->ID());
}
}
$sfSDK->save()->dispatch();

the WP table options have a new row:

wp_attendees_background_process_batch_1f8a9447c3ecec7570a4de37af

with the values...

a:7:{i:0;i:2664;i:1;i:2692;i:2;i:2693;i:3;i:2714;i:4;i:2729;i:5;i:2769;i:6;i:2770;}

but looks like the server is never dispatching the BG process....

there is any configuration missing to make it work?

thanks

Possible to schedule an action?

I'm trying to set it up so one of these background processes can be scheduled ahead of time.

This is my code:

add_action('wp_ajax_my_action_routes', 'my_action_callback_routes');

function my_action_callback_routes() {
deleteposts();
runposts();
cleardate();
echo "Routes Created";
die(); // this is required to return a proper result & exit is faster than die();
}

class WP_create_routes extends WP_Async_Request {
protected $action = 'create_routes';
protected function handle() {
my_action_callback_routes();
}

};

if ( get_option( 'schedule_gtfs' ) ){
$date = get_option('schedule_date');
$time = get_option('schedule_time');
$timestamp = strtotime( $date.' '.$time );
wp_schedule_single_event( $timestamp, 'create_routes' );
};

Have to declare handle_cron_healthcheck() for it to process

I've been having some issues with the Background process calling handle and activating the cron schedules needed for the fallback. Right now, I am utilizing something I saw in WooCommerce to force a healthcheck, which will start the handle process. I have also hooked that into the heartbeat function to continue polling for background processes, in the event the original timesout. This code starts the process, and this code is where I declare the handle_cron_healthcheck().

Is there a better way to make sure the background process has started? I want this to be pretty painless, from a user perspective, but I also want to make sure I understand what I am doing!

Really appreciate the help and for creating this awesome batch of code! I was really bummed when I moved from a Laravel project to a WP project and lost my queue process.

Class 'WP_Async_Request' not found

Hi

I have installed the example plugin, but getting the following error:

Fatal error: Class 'WP_Async_Request' not found in C:\xampp\htdocs\WP-CRM\wp-content\plugins\wp-background-processing-example-master\async-requests\class-example-request.php on line 3

Any help would be great as I am new to this. There are not other errors in the logs, but whole site stops working.

Thanks in advance.

Please add Github Topics

Please add several github topics, makes it easier to find the project when I start searching for it ;)

Add batch_complete callback method

Currently there is no way to know when a certain batch has completed, as the complete() method is only called after the whole queue for a background process is complete. However, in certain scenarios, it's desirable to know when a certain batch of related items has completed, such as when running imports or exports on background.

It would be great if instead of simply calling $this->delete( $batch->key ) when a batch is complete, it would also call something like $this->batch_complete( $batch ).

Process data is in the database, but does not get processed

I'm trying to offload a call to wp_mail to a process (tried async request first with no luck)

When I call dispatch() i get an entry like this in wp_options:

wp_wp_mail_batch_666155c47196b967c51a6a3419be51ed (wp_mail is the value of the action in my class)

After that, nothing happens. This holds true even with the sample plugin

Update Readme

Since #8 the readme should mention this method, or better the background process should automatically clear itself before running the same queue. Shouldn't it?

Add possibility to add metadata to each batch

I'm implementing this a second time, and have run into the same thing twice now - I usually need to save some common data that is shared between all the items in a batch. For example, when generating a CSV file with all the users, I would need to make sure that all the generated lines are saved to the same file. This means I need to add the path to the output file to the batch data.

Right now, I would need to save that file path to every single item, which is quite pointless (same information repeated over & over again) and it can make the option field size quite large in case of thousands of items.

Ideally, I could do something like this:

$this->example_process = new WP_Example_Process();
foreach ( $user_ids as $user_id ) {
    $this->example_process->push_to_queue( $user_id );
}
$this->example_process->set_options( array( 'file_path' => '/myfile.csv' ) ); 
// and in the handler
function task( $item, $options ) {
  // $options will be array( 'file_path' => '/myfile.csv' )
}

But at the moment I need to do this:

$this->example_process = new WP_Example_Process();
foreach ( $user_ids as $user_id ) {
    $this->example_process->push_to_queue( array( 'user_id' => $user_id, 'file_path' => '/myfile.csv' ) );
}
// will result in something like
array(
 array( 'user_id' => 1, 'file_path' => '/myfile.csv' ),
 array( 'user_id' => 2, 'file_path' => '/myfile.csv' ),
 array( 'user_id' => 3, 'file_path' => '/myfile.csv' ),
 array( 'user_id' => 4, 'file_path' => '/myfile.csv' ),
)

As you can see, I am saving a lot more info about each item than I really need to. And it gets much worse quickly when handling thousands of items. PHP serialize/unserialize will max out server memory pretty quicklly when serializing huge arrays like this.

Cron not updating to options table

There is an issue with the scheduler. Sometimes when we initiate the background processing class, it saves the cron scheduler to the options table option_name "cron" and sometimes, it doesn't. What could be the issue?

Proper way to initialize batch

In the example, you are using an Admin Menu Bar Link, which has a nonce connected with it and is executed when clicked. When trying to implement this class into a more practical use-case plugin, I'm having a hard time figuring out how to properly feed the queue once every X minutes. I tried an action that occurs on 'plugins_loaded' or 'admin_init' that retrieves all my "items" and loops them and uses "push_to_queue" however what I end up is 100s of "batches" with arrays of my "items", then my computer nearly explodes trying to process them all. I even tried to stop the process by checking for a transient that is set to expire after 30 minutes and if the transient exists, it's supposed to bail.

The processing seems to be working great, however when it processes the queue, since there are 100s of them stored, it just keeps processing the same queue over and over and I'm not sure why it doesn't stop.

Here's basically what I'm doing

<?php
add_action( 'admin_init', 'sync' );

$processor = new Sync_Data();

function sync() {
    if( get_transient( 'data-sync' ) )
        return;

    global $sync_db, $processor;

    $data = $sync_db->get_data(); // (returns a database resultset of objects with 1 property "id")

    foreach ( $data as $item ) {
        $processor->push_to_queue( $item->id );
    }

    set_transient( 'data-sync', '1', MINUTE_IN_SECONDS * 30 );

    $processor->save()->dispatch();
}

What am I doing wrong that's causing this to repeat infinitely?

The dispatch method not working 'wp_loaded' some action

Hi... I want some help from someone who can help me... I don't understand why, but the async function is working fine if I create the object and use it in an early action, like creating directly in functions.php or called via 'wp_loaded' action or 'init'. However if I call it from 'wp' action it won't work.

class WP_Example_Request extends WP_Async_Request {

	protected $action = 'example_request';

	protected function handle() {
		error_log('is working');
	}

}

function makeItWork(){
	$obj = new WP_Example_Request();
	$obj->dispatch();
}

If i use add_action( 'wp_loaded', 'makeItWork' ); will work, if i change to add_action( 'wp', 'makeItWork' ); will not work. Even if i change to 'parse_request' action, the one after 'wp_loaded' will not work, so wp_loaded is the last of what will work. ... anyone know what I'm doing wrong?

I also changed the timeout and the return of the dispatch() method to something like this:

			$output = wp_remote_post( esc_url_raw( $url ), $args );
			error_log( serialize($output) );
			return $output;

The error log will show something like this(so the dispatch method work, however it will not run my function ):
[Sun Feb 10 22:58:40.447691 2019] [php7:notice] [pid 15924:tid 1896] [client 192.168.0.200:58319] a:5:{s:7:"headers";a:0:{}s:4:"body";s:0:"";s:8:"response";a:2:{s:4:"code";b:0;s:7:"message";b:0;}s:7:"cookies";a:0:{}s:13:"http_response";N;}

Note: My server is on the same machine I code, but I connect via private IP under router, and not localhost.

Licensing

Before using this, I'd like to know how this is licensed? If it's mentioned somewhere there, I failed to find it.

Cheers
antti

wp_remote_post empty or timed out

Hi there,

I'm trying to use wp-background-processing, but it never enters task function (or even maybe_handle).
I debugged wp_remote_post but the response is always empty, and if I changed blocked to true on post parameters I get a timeout.

Any help/ideas debugging this?

Thanks

How Does WP Background Processing Compare To Sidekiq?

I'm a little confused as to how this works. I have set up queueing systems using sidekiq before and this, wp-background-processing seems very different. In Sidekiq, there's a redis queue and my application enqueues messages to a queue in redis. Along side the web processes there's another forever running process that consumes messages off of the queue and runs them, assigning certain messages to certain background Jobs (Ruby Classes). In short there are two processes: a web process and a background process.

  1. How many processes are there with wp-background-processing?
  2. Is the background process and the web process that powers my wordpress site the same PHP process?
  3. What queue is being used? In Sidekiq, usually the queue technology is Redis.
  4. If I have a 1500 expensive HTTP requests hitting my wordpress app and I want to enqueue, let's say, 1500 background jobs -- one for each request -- to save off an expensive operation to an API or a database, is my Apache process still blocked? In the sidekiq example, it would not be blocked because the web process (Puma / Nginx) would just offload a message to a redis queue and let the sidekiq process deal with it when it needs to, one message at a time (well actually multiple messages at a time because Ruby is multi threaded).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.