Code Monkey home page Code Monkey logo

php-resque's Introduction

php-resque: PHP Resque Worker (and Enqueue) Build Status

Resque is a Redis-backed library for creating background jobs, placing those jobs on one or more queues, and processing them later.

Background

Resque was pioneered and is developed by the fine folks at GitHub (yes, I am a kiss-ass), and written in Ruby. What you're seeing here is an almost direct port of the Resque worker and enqueue system to PHP.

For more information on Resque, visit the official GitHub project: https://github.com/resque/resque

For further information, see the launch post on the GitHub blog: http://github.com/blog/542-introducing-resque

The PHP port does NOT include its own web interface for viewing queue stats, as the data is stored in the exact same expected format as the Ruby version of Resque.

The PHP port provides much the same features as the Ruby version:

  • Workers can be distributed between multiple machines
  • Includes support for priorities (queues)
  • Resilient to memory leaks (forking)
  • Expects failure

It also supports the following additional features:

  • Has the ability to track the status of jobs
  • Will mark a job as failed, if a forked child running a job does not exit with a status code as 0
  • Has built in support for setUp and tearDown methods, called pre and post jobs

Requirements

  • PHP 5.3+
  • Redis 2.2+
  • Optional but Recommended: Composer

Getting Started

The easiest way to work with php-resque is when it's installed as a Composer package inside your project. Composer isn't strictly required, but makes life a lot easier.

If you're not familiar with Composer, please see http://getcomposer.org/.

  1. Add php-resque to your application's composer.json.
{
    "require": {
        "chrisboulton/php-resque": "1.2.x"
    }
}
  1. Run composer install.

  2. If you haven't already, add the Composer autoload to your project's initialization file. (example)

require 'vendor/autoload.php';

Jobs

Queueing Jobs

Jobs are queued as follows:

// Required if redis is located elsewhere
Resque::setBackend('localhost:6379');

$args = array(
        'name' => 'Chris'
        );
Resque::enqueue('default', 'My_Job', $args);

Defining Jobs

Each job should be in its own class, and include a perform method.

class My_Job
{
    public function perform()
    {
        // Work work work
        echo $this->args['name'];
    }
}

When the job is run, the class will be instantiated and any arguments will be set as an array on the instantiated object, and are accessible via $this->args.

Any exception thrown by a job will result in the job failing - be careful here and make sure you handle the exceptions that shouldn't result in a job failing.

Jobs can also have setUp and tearDown methods. If a setUp method is defined, it will be called before the perform method is run. The tearDown method, if defined, will be called after the job finishes.

class My_Job
{
    public function setUp()
    {
        // ... Set up environment for this job
    }

    public function perform()
    {
        // .. Run job
    }

    public function tearDown()
    {
        // ... Remove environment for this job
    }
}

Dequeueing Jobs

This method can be used to conveniently remove a job from a queue.

// Removes job class 'My_Job' of queue 'default'
Resque::dequeue('default', ['My_Job']);

// Removes job class 'My_Job' with Job ID '087df5819a790ac666c9608e2234b21e' of queue 'default'
Resque::dequeue('default', ['My_Job' => '087df5819a790ac666c9608e2234b21e']);

// Removes job class 'My_Job' with arguments of queue 'default'
Resque::dequeue('default', ['My_Job' => array('foo' => 1, 'bar' => 2)]);

// Removes multiple jobs
Resque::dequeue('default', ['My_Job', 'My_Job2']);

If no jobs are given, this method will dequeue all jobs matching the provided queue.

// Removes all jobs of queue 'default'
Resque::dequeue('default');

Tracking Job Statuses

php-resque has the ability to perform basic status tracking of a queued job. The status information will allow you to check if a job is in the queue, is currently being run, has finished, or has failed.

To track the status of a job, pass true as the fourth argument to Resque::enqueue. A token used for tracking the job status will be returned:

$token = Resque::enqueue('default', 'My_Job', $args, true);
echo $token;

To fetch the status of a job:

$status = new Resque_Job_Status($token);
echo $status->get(); // Outputs the status

Job statuses are defined as constants in the Resque_Job_Status class. Valid statuses include:

  • Resque_Job_Status::STATUS_WAITING - Job is still queued
  • Resque_Job_Status::STATUS_RUNNING - Job is currently running
  • Resque_Job_Status::STATUS_FAILED - Job has failed
  • Resque_Job_Status::STATUS_COMPLETE - Job is complete
  • false - Failed to fetch the status - is the token valid?

Statuses are available for up to 24 hours after a job has completed or failed, and are then automatically expired. A status can also forcefully be expired by calling the stop() method on a status class.

Workers

Workers work in the exact same way as the Ruby workers. For complete documentation on workers, see the original documentation.

A basic "up-and-running" bin/resque file is included that sets up a running worker environment. (vendor/bin/resque when installed via Composer)

The exception to the similarities with the Ruby version of resque is how a worker is initially setup. To work under all environments, not having a single environment such as with Ruby, the PHP port makes no assumptions about your setup.

To start a worker, it's very similar to the Ruby version:

$ QUEUE=file_serve php bin/resque

It's your responsibility to tell the worker which file to include to get your application underway. You do so by setting the APP_INCLUDE environment variable:

$ QUEUE=file_serve APP_INCLUDE=../application/init.php php bin/resque

Pro tip: Using Composer? More than likely, you don't need to worry about APP_INCLUDE, because hopefully Composer is responsible for autoloading your application too!

Getting your application underway also includes telling the worker your job classes, by means of either an autoloader or including them.

Alternately, you can always include('bin/resque') from your application and skip setting APP_INCLUDE altogether. Just be sure the various environment variables are set (setenv) before you do.

Logging

The port supports the same environment variables for logging to STDOUT. Setting VERBOSE will print basic debugging information and VVERBOSE will print detailed information.

$ VERBOSE=1 QUEUE=file_serve bin/resque
$ VVERBOSE=1 QUEUE=file_serve bin/resque

Priorities and Queue Lists

Similarly, priority and queue list functionality works exactly the same as the Ruby workers. Multiple queues should be separated with a comma, and the order that they're supplied in is the order that they're checked in.

As per the original example:

$ QUEUE=file_serve,warm_cache bin/resque

The file_serve queue will always be checked for new jobs on each iteration before the warm_cache queue is checked.

Running All Queues

All queues are supported in the same manner and processed in alphabetical order:

$ QUEUE='*' bin/resque

Running Multiple Workers

Multiple workers can be launched simultaneously by supplying the COUNT environment variable:

$ COUNT=5 bin/resque

Be aware, however, that each worker is its own fork, and the original process will shut down as soon as it has spawned COUNT forks. If you need to keep track of your workers using an external application such as monit, you'll need to work around this limitation.

Custom prefix

When you have multiple apps using the same Redis database it is better to use a custom prefix to separate the Resque data:

$ PREFIX=my-app-name bin/resque

Forking

Similarly to the Ruby versions, supported platforms will immediately fork after picking up a job. The forked child will exit as soon as the job finishes.

The difference with php-resque is that if a forked child does not exit nicely (PHP error or such), php-resque will automatically fail the job.

Signals

Signals also work on supported platforms exactly as in the Ruby version of Resque:

  • QUIT - Wait for job to finish processing then exit
  • TERM / INT - Immediately kill job then exit
  • USR1 - Immediately kill job but don't exit
  • USR2 - Pause worker, no new jobs will be processed
  • CONT - Resume worker.

Process Titles/Statuses

The Ruby version of Resque has a nifty feature whereby the process title of the worker is updated to indicate what the worker is doing, and any forked children also set their process title with the job being run. This helps identify running processes on the server and their resque status.

PHP does not have this functionality by default until 5.5.

A PECL module (http://pecl.php.net/package/proctitle) exists that adds this functionality to PHP before 5.5, so if you'd like process titles updated, install the PECL module as well. php-resque will automatically detect and use it.

Event/Hook System

php-resque has a basic event system that can be used by your application to customize how some of the php-resque internals behave.

You listen in on events (as listed below) by registering with Resque_Event and supplying a callback that you would like triggered when the event is raised:

Resque_Event::listen('eventName', [callback]);

[callback] may be anything in PHP that is callable by call_user_func_array:

  • A string with the name of a function
  • An array containing an object and method to call
  • An array containing an object and a static method to call
  • A closure (PHP 5.3+)

Events may pass arguments (documented below), so your callback should accept these arguments.

You can stop listening to an event by calling Resque_Event::stopListening with the same arguments supplied to Resque_Event::listen.

It is up to your application to register event listeners. When enqueuing events in your application, it should be as easy as making sure php-resque is loaded and calling Resque_Event::listen.

When running workers, if you run workers via the default bin/resque script, your APP_INCLUDE script should initialize and register any listeners required for operation. If you have rolled your own worker manager, then it is again your responsibility to register listeners.

A sample plugin is included in the extras directory.

Events

beforeFirstFork

Called once, as a worker initializes. Argument passed is the instance of Resque_Worker that was just initialized.

beforeFork

Called before php-resque forks to run a job. Argument passed contains the instance of Resque_Job for the job about to be run.

beforeFork is triggered in the parent process. Any changes made will be permanent for as long as the worker lives.

afterFork

Called after php-resque forks to run a job (but before the job is run). Argument passed contains the instance of Resque_Job for the job about to be run.

afterFork is triggered in the child process after forking out to complete a job. Any changes made will only live as long as the job is being processed.

beforePerform

Called before the setUp and perform methods on a job are run. Argument passed contains the instance of Resque_Job for the job about to be run.

You can prevent execution of the job by throwing an exception of Resque_Job_DontPerform. Any other exceptions thrown will be treated as if they were thrown in a job, causing the job to fail.

afterPerform

Called after the perform and tearDown methods on a job are run. Argument passed contains the instance of Resque_Job that was just run.

Any exceptions thrown will be treated as if they were thrown in a job, causing the job to be marked as having failed.

onFailure

Called whenever a job fails. Arguments passed (in this order) include:

  • Exception - The exception that was thrown when the job failed
  • Resque_Job - The job that failed

beforeEnqueue

Called immediately before a job is enqueued using the Resque::enqueue method. Arguments passed (in this order) include:

  • Class - string containing the name of the job to be enqueued
  • Arguments - array of arguments for the job
  • Queue - string containing the name of the queue the job is to be enqueued in
  • ID - string containing the token of the job to be enqueued

You can prevent enqueing of the job by throwing an exception of Resque_Job_DontCreate.

afterEnqueue

Called after a job has been queued using the Resque::enqueue method. Arguments passed (in this order) include:

  • Class - string containing the name of scheduled job
  • Arguments - array of arguments supplied to the job
  • Queue - string containing the name of the queue the job was added to
  • ID - string containing the new token of the enqueued job

Step-By-Step

For a more in-depth look at what php-resque does under the hood (without needing to directly examine the code), have a look at HOWITWORKS.md.

Contributors

Project Lead

  • @chrisboulton

Others

  • @acinader
  • @ajbonner
  • @andrewjshults
  • @atorres757
  • @benjisg
  • @cballou
  • @chaitanyakuber
  • @charly22
  • @CyrilMazur
  • @d11wtq
  • @danhunsaker
  • @dceballos
  • @ebernhardson
  • @hlegius
  • @hobodave
  • @humancopy
  • @iskandar
  • @JesseObrien
  • @jjfrey
  • @jmathai
  • @joshhawthorne
  • @KevBurnsJr
  • @lboynton
  • @maetl
  • @matteosister
  • @MattHeath
  • @mickhrmweb
  • @Olden
  • @patrickbajao
  • @pedroarnal
  • @ptrofimov
  • @rajibahmed
  • @richardkmiller
  • @Rockstar04
  • @ruudk
  • @salimane
  • @scragg0x
  • @scraton
  • @thedotedge
  • @tonypiper
  • @trimbletodd
  • @warezthebeef

php-resque's People

Contributors

bc-vincent-zhao avatar bpdeployer avatar chrisboulton avatar danhunsaker avatar drahmel avatar ebernhardson avatar elazar avatar hobodave avatar humancopy avatar jaswdr avatar kevburnsjr avatar lauripiisang avatar lboynton avatar luishdez avatar maetl avatar mickhrmweb avatar nazo avatar patrickbajao avatar pedroarnal avatar petrkotek avatar richardkmiller avatar rockstar04 avatar rolfvreijdenberger avatar ruudk avatar salimane avatar sergeyklay avatar tburry avatar theaxel avatar warezthebeef avatar wedy 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  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

php-resque's Issues

Add method to determine job position in queue

It would be really useful if there were a way to determine where in the queue a particular job is. This information could be returned when calling "get()" on a Resque_Job_Status instance.

The only way I could figure out how to get the queue position at the moment would be call Resque::redis()->lrange("queue:QUEUE_NAME", 0, -1) and look for the job of interest.

Any possibility that this could be added?

Accents/special characters in payloads causing problems

Due to the way that JSON_ENCODE works, accents and some other special characters cause it to return null variables.

This poses a problem when passing messages that include these characters such as text in foreign languages and perhaps more... One work-around for this is to UTF8 encode all of the payload data.

I have tried this in development and it works fine. Is there any downside that you guys can point?

The function I am using was obtained in the php manpages and I havent even tweaked it yet, but the idea is to add something such as:

public static function array_utf8_encode_recursive($dat)
        { if (is_string($dat)) {
            return utf8_encode($dat);
          }
          if (is_object($dat)) {
            $ovs= get_object_vars($dat);
            $new=$dat;
            foreach ($ovs as $k =>$v)    {
                $new->$k=self::array_utf8_encode_recursive($new->$k);
            }
            return $new;
          }

          if (!is_array($dat)) return $dat;
          $ret = array();
          foreach($dat as $i=>$d) $ret[$i] = self::array_utf8_encode_recursive($d);
          return $ret;
        } 

and then encode the payload through this function (in order to retain the array structure).

I currently identified that this is necessary in resque.php (function push), in job.php (crica line 250) and worker.php (crica line 500)

Another idea is to UTF8 before sneding to resque (which I will do tomorrow for compatibility sakes since I use a "enque wrapper function" in my app). but I thought I'd contirbute this anyhow in case anyone has issues.

performance issues (pcntl_fork overhead?)

So I've started playing with a deployment of this in production and seem to be having performance issues with pcntl_fork. Processing an empty job (that just contain an error_log statement) takes over 50ms, and our queue needs to be able to process more than 1000 items per second.

I'm thinking I might have to fork the project and change the behavior so that instead of forking on every job, to fork on every X jobs (where X > 100). My concern is that I haven't figured out a way to communicate between processes in PHP that would let me pass back the job object about to be processed from the child to the parent. Ideas?

Defining CRLF within Redisent causes an exception

The line

define('CRLF', sprintf('%s%s', chr(13), chr(10)));

Within the Redisent.php causes an exception if CRLF is already defined.

In Redisent, this is already fixed by adding a namespace, but the version of Redisent included with php-resque is not up to date.

Would it be possible to update the Redisent lib? Otherwise, a simple check for definition before defining it again fixes it as well.

class 'Redis' does not have a method 'establishconnection'

Bug?

Method Resque_Worker::reestablishRedisConnection after SIGPIPE signal calls Resque::redis()->establishConnection(); that causes error

call_user_func_array() expects parameter 1 to be a valid callback, class 'Redis' does not have a method 'establishconnection'

piece of trace

chrisboulton/php-resque/lib/Resque/Worker.php(365): Resque_Redis->establishConnection()

colinmollenhour/credis/Client.php(627): call_user_func_array(Array, Array)

Terminate Worker/Queue

Hi,

Is it possible to grab a queue/worker by name and stop (kill) it?

Many thanks,

CJ

Multiple Workers = Lost jobs?

As per a comment on issue #30, I'm opening this issue in order to help gather more info and find a fix for the bug.

I observed that when deploying multiple workers, some jobs dont get processed...

Basically the test I did was:

watch output and number of jobs, launch 5 workers.
Enqueue 10 jobs. number of jobs in redis gets augmented to 10 (so 10 jobs queued), but only a few get really processed. Nonetheless, they all get pulled out of the queue...

So basically it is "loosing" jobs.

I will try and test more tomorrow, time permitting, and post back.

Chris said he had already noticed this, so if anyone else has noticed and/or fixed this, please let us know.

Imo this is crucial for any production environment, no? Is anyone using phpresque in a production environment? Are you loosing jobs? Maybe you havent noticed? Can you please check and revert?

thanks!

APP_INCLUDE does not get picked up

Hi Chris!
I'm not sure how to send you patches, so I'm just including it below.

From 83a9efeec35602bc74182246b3609f7deec96958 Mon Sep 17 00:00:00 2001
From: Oleg Topchiy 
Date: Wed, 12 May 2010 17:19:50 +0300
Subject: [PATCH] fixed APP_INCUDE

---
 resque.php |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/resque.php b/resque.php
index eb6aec9..4b7ab44 100644
--- a/resque.php
+++ b/resque.php
@@ -12,7 +12,7 @@ if(!empty($_ENV['APP_INCLUDE'])) {
                die('APP_INCLUDE ('.$_ENV['APP_INCLUDE'].") does not exist.\n");
        }
 
-       require_once APP_INCLUDE;
+       require_once $_ENV['APP_INCLUDE'];
 }
 
 require 'lib/Resque.php';
-- 
1.7.0.2

Resque_Worker shutdown() is not working?

I tried to call shutdown() on a Resque_Worker Object but it's not working.
like:

            $workers = Resque_Worker::all();
            foreach($workers as $worker) {
                $worker->shutdown();
            }  

I tried to print $worker->shutdown
it's always false.
is this normal?

Dealing with memory leaks?

Hi All,

I am developing a job which scans an uploaded file for a virus. Nonetheless, running 10 workers is causing the machine to become unresposnive due to the amount of memory it is consuming...

I assume it is a problem with the phpclam extension itself (any other suggestions for deploying virus scanning in a phpresque job? I thought this would be better than an exec command?) and perhaps all the forking that is going on is making the memory usage go through the roof...

Nonetheless, I can deal with that on a more individual level, what worries me is more general and slower leaks which might not become so evident. Have any of you guys dealt with problems on similar level? I suppose monit restarting the workers every 12 or 24hrs may help by providing some cleanup, but I'd like to hear others experiences...

thanks!

json_decode error in workers

Getting the following warning in the workers:

PHP Warning:  json_decode() expects parameter 1 to be string, array given in /var/www/server/releases/20111223123758/vendor/resque/lib/Resque.php on line 85

Which seems to trace back to Resident::__call. Not sure what's going on there, and I don't know the internals of Redis enough to know why it's trying to json_decode a string rather than an array. Fwiw, jovs are added by doing Resque::enqueue($queue, $class, $args); where $args is always an array.

Thoughts?

Implement a real logging engine

Implementing a robust logging engine, like Monolog.

It'll allow logging to various database, like Mysql, Redis, File, MongoDB, Amazon, etc ... and open the door for metric collection.

Working with namespaces

I am trying to set up PHP-Resque but i can't manage to get it to work using namespaces. Here is an example:

<?php
require_once(__DIR__ . "/bootstrap.php");

$x = new \Company\Tasks\ProcessOrder();
$x->perform();

The above code works, but when using PHP-Resque like this:

$job = Resque::enqueue('default', "\\Company\\Tasks\\ProcessOrder", array(), true);

Task is not excecuted :(

What i see with vverbose=1:

** [21:40:13 2013-04-28] Sleeping for 5
** [21:40:18 2013-04-28] Checking default
** [21:40:18 2013-04-28] Found job on default
** [21:40:18 2013-04-28] got (Job{default} | ID: ca025826774df52033260720ec0c9459 | \Company\Tasks\ProcessOrder | [])

Anyone knows what could be happening? I run resque from console with:

QUEUE=* VVERBOSE=1 APP_INCLUDE=./bootstrap.php php vendor/bin/resque

Thank you!

Job::recreate() encapsulates args array inside another array

Job::recreate is calling Job::create, which enqueues the job like this

Resque::push($queue, array(
'class' => $class,
'args' => array($args),
'id' => $id,
));

It encapsulates array($args) (don't really know why?)

Anyways, if you call recreate, that will pass array($args) to create() and then encapsulate into another array. So if you all recreate() once, you end up with

array(array($args))

if you call it twice, you end up with

array(array(array($args)))

and so on... thus when performing a job that was enqueued using recreate(), the $args array is wrongly encapsulated into another array.

I fixed this by changing the line 234 of Job.php like this:

return self::create($this->queue, $this->payload['class'], $this->payload['args'][0], $monitor);

(jus added [0] to $this->payload['args'])

Job classes with namespaces

I'm using php-resque Symfony 2.1 but I could not make it work. Job classes are not found, with or without their namespaces. What is the potential cause ?

Resque_Job_DirtyExitException

Hello guys

I have successfully add job to queue, however once worker try to execute the job. It throws exception "Resque_Job_DirtyExitException", Job exited with exit code 255

Do you know why I get this exception

Please help

Rate limit a queue?

Is it possible to limit the rate at which jobs are processed in a particular queue?

Worker->fork() not working when pcntl isn't installed

I just found out that my production server doesn't have PCNTL installed. Now every job is accepted but not executed:

// Resque/Worker.php

// On my server, this returns -1 because it isn't installed
$this->child = Resque::fork();

// So here, it isn't executed, because it isn't 0 nor false
if ($this->child === 0 || $this->child === false) {
    $status = 'Processing ' . $job->queue . ' since ' . strftime('%F %T');
    $this->updateProcLine($status);
    $this->log($status, self::LOG_VERBOSE);
    $this->perform($job);
    if ($this->child === 0) {
        exit(0);
    }
}

// This isn't executed either... because its below 0 
if($this->child > 0) {
    // Parent process, sit and wait
    $status = 'Forked ' . $this->child . ' at ' . strftime('%F %T');
    $this->updateProcLine($status);
    $this->log($status, self::LOG_VERBOSE);

    // Wait until the child process finishes before continuing
    pcntl_wait($status);
    $exitStatus = pcntl_wexitstatus($status);
    if($exitStatus !== 0) {
        $job->fail(new Resque_Job_DirtyExitException(
            'Job exited with exit code ' . $exitStatus
        ));
    }
}

$this->child = null;

// Wtf?? Didn't do anythin' ;)
$this->doneWorking();

So what would be the best way to fix this?

Job perform method should be static, and job classes should not be instantiated

php-resque behaves differently to the Ruby equivalent in that it currently instantiated job classes when running jobs(e.g $job = new $jobName(); $job->perform()). It also has setUp and tearDown methods which the Ruby version does not.

The problem is, php-resque does NOT know how to instantiate your classes, which means if you have classes that require arguments to the constructor, you are SOL.

It makes sense for php-resque to statically call the perform method on classes, and then leave it up to your class to then instantiate itself if required. There is also no need for the setUp and tearDown methods as these would be handled in the statically called perform method.

This change is going to break compatibility with all current jobs, and require people to update their code.

Thoughts on a filter system?

Hi Chris et. al. Thanks for your work on porting this to PHP. I'm implementing it here at my work, and I'm really liking it so far.

I ran into one issue which I'm having trouble solving. We use the Yii framework, which has its own way of serializing its Active Record class files. Resque json_encodes data before pushing it to the queue, which prevents us from properly unserializing it when the worker reserves the job. Taking a cue from Delayed Job's performable_method.rb, my idea is to create a "signature" of the AR object using the class name and id.

I then ran into issues about where to put this logic. The only solution that I think will work is an awkward one where I extend the Resque class to override the enqueue function and create the signature before pushing the job to Redis, then putting my reconstitution logic in the setup method of every job class I create. I then thought that a better solution would be to try to add to Resque's event system, creating a pair of events that are run before and after the arguments are encoded/decoded. However, I found myself working against Resque_Event, as it really doesn't like you to change the arguments once they've been pulled from the queue.

So, I think the ideal system would be to have something like a Resque_Filter class, which would be similar to the Event system, where at certain points within the process Resque allows you to run arbitrary callbacks that filter the data. Lithium uses this sort of Aspect-Oriented filter system to great effect throughout its framework, and I think Resque could benefit from it as well. I think because Ruby and RoR have very standardized ways of dealing with serialization of data, a filter system isn't needed in the original Resque. But since every PHP framework has its own standard, a filter system is necessary to provide php-Resque with customized ways of dealing with data.

I'd love to take a crack at writing this filter system, but I wanted to check with you and see if there would be any interest in it. What do you think?

Thanks,
Dan

minor perform method adjustment in Resque_Job class

Any harm adding:

$instance->id = $this->payload['id'];

...to the perform method in the Resque_Job class?

In addition to just passing worker args by reference, I have use for the actual job id in a worker class. Similar to a Resque_Job_Status class, I have a Resque_Job_Message class that populates and updates a key/value. Throughout the course of a job when a worker has it, I update the value for this key to provide some human-readable feedback (e.g. "Job has started" / "Job is now doing 'x'" / "Job is now doing 'y'" / etc.). Thus, it is handy for a worker to know what is the job id.

json_decode error when REDIS_BACKEND is not localhost

everything is fine when I didn't set REDIS_BACKEND
but when it came to production, I set REDIS_BACKEND to other machine and bang it exploded.

Here's what I did:

export REDIS_BACKEND=queue.mydomain.com:6379
php resque.php

And all I got was json_decode error.

I tried to modify lib/Resque.php

    public static function redis()
    {
        if(is_null(self::$redis)) {
            self::setBackend('localhost:6379');
        }

        return self::$redis;
    }

changed it to queue.mydomain.com:6379 and it worked fine.

So I figure that maybe my env var dropped somewhere and crashed my resque?

How to you guys set up REDIS_BACKEND other than localhost?

Tag a 1.3 release

Would be great if you could tag a 1.3 release so that a versioned release with the changes since 1.2 is available on packagist.

Thanks!

PERFORMANCE OF RESQUE

I AM NOT ABLE TO CHECK THE PERFORMANCE OF RESQUE. IT CAN BE DONE THROUGH CLI ONLY?
CAN RESQUE WORKER BE STARTED FROM A PHP SCRIPT?

Delayed Jobs?

Hi,

What's the best way to delay jobs? Say there was an error the first time because the api host is down, so I don't want to re-add the job and have it run right a way... I want to be able to add the job and say try this in 1 hour.

Many thanks,

CJ

Redisent does not handle errors when the socket connection has been interrupted for whatever reason

First of all, kudos for porting this project to PHP. It's made our life a lot easier migrating a large PHP application to Rails, since we can run both apps side-by-side during the (N years) transition and delegate bits and pieces of work in either direction.

We ran into a small issue overnight, with our error logs swelling to 380GB (yes, GB's) in the space of about 12 hours. The cause was a runaway Resque worker process, that had processed a job and was trying to mark itself as doneWorking(), then continuing its event loop. I think we must have had a brief network hiccup, since what was happening was it was getting "broken pipe" trying to write to Redis:

Exception:
exception 'ErrorException' with message 'fwrite(): send of 90 bytes failed with errno=32 Broken pipe' in /mnt/flippa/flippa/lib/php-resque/lib/Redisent/Redisent.php:70
Stack trace:
#0 [internal function]: Sitepoint_ErrorHandler->handleError(8, 'fwrite(): send ...', '/mnt/flippa/fli...', 70, Array)
#1 /mnt/flippa/flippa/lib/php-resque/lib/Redisent/Redisent.php(70): fwrite(Resource id #107, '*3??$6??INCRBY?...')
#2 /mnt/flippa/flippa/lib/php-resque/lib/Resque/Redis.php(94): Redisent->__call('incrby', Array)
#3 [internal function]: Resque_Redis->__call('incrby', Array)
#4 /mnt/flippa/flippa/lib/php-resque/lib/Resque/Stat.php(32): Resque_Redis->incrby('stat:processed:...', 1)
#5 /mnt/flippa/flippa/lib/php-resque/lib/Resque/Worker.php(520): Resque_Stat::incr('processed:ip-10...')
#6 /mnt/flippa/flippa/lib/php-resque/lib/Resque/Worker.php(226): Resque_Worker->doneWorking()
#7 /mnt/flippa/flippa/scripts/resque.php(54): Resque_Worker->work(5)

However, since this was not a fatal PHP error, it was continuing onto the next iteration of the loop, skipping over the body of the loop (including the sleep call) and trying to register itself as done, again, just spinning extremely quickly out of control.

I think that there are two possible solutions here:

  1. Open and close a new connection for each __call()
  2. Check if the connection is up, and re-connect if it's not, on each __call(), throwing an Exception if it can't re-connect, thus halting the worker.

We're running a slightly patched version (of Resque, not Redisent) but I'll try and fix this on a clean branch off master and submit a pull request when I get a spare moment later today.

Job::updateStatus() never works

This empty check always returns true, thus job statuses are never updated. The failing unit tests also blatantly indicate this.

    public function updateStatus($status)
    {
        if(empty($this->payload->id)) {
            return;
        }

        $statusInstance = new Resque_Job_Status($this->payload['id']);
        $statusInstance->update($status);
    }

Jobs

Is it possible to pass variables to a job ?

Process exited with status 0 , running COUNT > 1 on heroku worker process

i can realize running multiple worker with COUNT enviroment > 1
here's my worker log from heroku

2012-05-08T07:25:17+00:00 heroku[worker.1]: Starting process with command `QUEUE=* APP_INCLUDE=/app/www/htdocs/_bootstrap.php /app/bin/php /app/www/htdocs/worker.php`
2012-05-08T07:25:18+00:00 app[worker.1]: *** Starting worker 9138fa24-ab02-40e9-bcdd-e19803e4a4fe:4:*
2012-05-08T07:25:18+00:00 app[worker.1]: *** Starting worker 9138fa24-ab02-40e9-bcdd-e19803e4a4fe:6:*
2012-05-08T07:25:18+00:00 heroku[worker.1]: State changed from starting to crashed
2012-05-08T07:25:18+00:00 heroku[worker.1]: State changed from crashed to created
2012-05-08T07:25:18+00:00 heroku[worker.1]: State changed from created to starting
2012-05-08T07:25:19+00:00 heroku[worker.1]: Process exited with status 0

and i try to investigate, run my app locally and my redis-server gitve this log output repeatly

[29684] 08 May 06:18:28 * 10 changes in 300 seconds. Saving...
[29684] 08 May 06:18:28 * Background saving started by pid 29911
[29911] 08 May 06:18:28 # Failed saving the DB: Permission denied
[29684] 08 May 06:18:28 # Background saving error

Works in conjunction with ruby resque?

This is probably a dumb question, but does this use the same redis schema as the "normal" resque? That is, can I create jobs in PHP and jobs in Ruby in the same database?

Bonus question:
Is there anyway to create jobs in PHP which are then executed by Ruby workers?

Thanks,
Jacob

Pub/Sub option?

Hi,

Has anyone tried getting a pub/sub queue working under resque? any comment?

I have one feature that i need to add to my app that calls a webservice for another application we interface. Nonetheless, i need this to be done as quickly as possible, currently using resque using the normal 5 second interval is a bit laggy... 1 second interval would probably ok, nonetheless i think it may be overkill to run the queue across a cluster of server iterating every second, etc, without any activity? (not all the time there will be things on the queue)...

The other option will be to just tie these particular webservice calls to the user-activated script (kinda not-elegant because if the webservice does lag at all we will have a laggy interface as well as laggy functionality...)

Anyways, any suggestions about this? I do suppose that if I put the workers throughout 10 or so servers, and maybe a few workers per server, eventually the lag will be quite small? But still, there will be a ton of hits on Redis, wouldnt a pub/sub mechanism be somewhat better?

thanks for ideas/feedbak

Constant CRLF already defined

Laravel already defines CRLF constant in the core, so I get this warning from Redisent.php.

Constant CRLF already defined

Might be worth adding a check for this?

Clean redis

Is there anyway to clean up redis for executed job?

bug in demo

in demo/resque.php:

$APP_INCLUDE = getenv('APP_INCLUDE');
if(!$APP_INCLUDE) {
if(!file_exists($APP_INCLUDE)) {
die('APP_INCLUDE ('.$APP_INCLUDE.") does not exist.\n");
}

    require_once $APP_INCLUDE;

}

The second line shoud be: if($APP_INCLUDE) {. The ! does not make sense here,

fwrite infinite loop on broken pipe

In an application I have where tasks may take up to 5-6 minutes the workers end up going in infinite loops with an error message to the effect of:

PHP Notice: fwrite(): send of 898 bytes failed with errno=32 Broken pipe in /var/www/lib/Redisent/Redisent.php on line 74

It looks like the same issue was reported upstream and a pull request submitted (jdp/redisent#10).

PHP Warnings after jobs

PHP Warning: Error while sending STMT_CLOSE packet. PID=31710 in Unknown on line 0

Warning: Error while sending STMT_CLOSE packet. PID=31710 in Unknown on line 0
PHP Warning: Unknown: Error occured while closing statement in Unknown on line 0

I get the above warnings after every processed job. I'm not sure what is causing them. They don't seem to be causing any problems. Anyone have an idea of what it could be?

Mix of false and workers returned by Resque_Worker::all()

When calling Resque_Worker::all(), the returned array can contain a mix of worker instances and false. This will be either because redis threw an exception in smembers or because the worker could not be found (because find() returns false in that case).

I think it makes sense to be able to loop over the contents of the array expecting its contents to consist solely of worker objects, so it seems like false shouldn't be added to the array in the first place.

I have patched the code for myself, so I'm happy to submit a pull request if you agree that this is the way to go.

Command Line Tool

Hi there,

I've created a command line tool to manager resque workers.
It provides some commands like start, stop, restart, stats and takes various option flag to start a queue with the parameters you want.

Maybe it can help someone here.

Prefix (aka namespace)

Just stumbled upon this port of the Ruby version of resque and by reading the code that you don't support changing namespace or prefix (whatever you want to call it) as the Ruby version does. I might be wrong so just asking before submitting a pull request w/ the necessary changes. It can be a small problem if you want to use both the Ruby and the PHP version, especially if you're using a custom namespace on the Ruby side, which is my case because I need to enqueue some tasks on a server where most of the code base is in PHP and empty the queue on another server where it's more convenient to use Ruby.

Execute jobs via php-fpm rather than forking

After a discussion with a co-worker i decided to write up a quick hack which executes jobs over php5-fpm instead of forking. This provides all the same protections as forking in regards to failures while reducing job execution overhead and providing a more natural php execution environment(vs fork). The Resque_Worker::fork method was adjusted to always return false when running the fastcgi tests.

This is just a POC, not intended for usage outside benchmarking but i think it shows promise.

https://gist.github.com/4253706

Initial benchmarks on an intel i5-2500k (4 cores no HT), ubuntu 12.04, php 5.4.8

WITH FORK:

COUNT=1 INTERVAL=0.01 QUEUE=default APP_INCLUDE=examples/worker_include.php php vendor/.../resque.php
1 Worker, no arguments
Completed 2000 jobs in 9.704s ~4.8ms per job per core
Completed 2000 jobs in 9.960s
Completed 2000 jobs in 10.014s
Completed 2000 jobs in 10.022s
Completed 2000 jobs in 10.000s
Completed 2000 jobs in 9.992s
Completed 2000 jobs in 10.310s
Completed 2000 jobs in 9.991s
Completed 2000 jobs in 10.002s
Completed 2000 jobs in 10.016s

COUNT=4 INTERVAL=0.01 QUEUE=default APP_INCLUDE=examples/worker_include.php php vendor/.../resque.php
4 Workers, no arguments, ~6.4ms per job per core
Completed 2000 jobs in 3.235s
Completed 2000 jobs in 3.250s
Completed 2000 jobs in 3.252s
Completed 2000 jobs in 3.247s
Completed 2000 jobs in 3.278s
Completed 2000 jobs in 3.282s
Completed 2000 jobs in 3.298s
Completed 2000 jobs in 3.292s
Completed 2000 jobs in 3.297s
Completed 2000 jobs in 3.304s

WITH FASTCGI:

COUNT=1 INTERVAL=0.01 QUEUE=default APP_INCLUDE=examples/worker_fcgi_include.php php vendor/.../resque.php
1 worker, no arguments
Completed 2000 jobs in 3.082s ~1.5ms per job per core
Completed 2000 jobs in 3.098s
Completed 2000 jobs in 3.102s
Completed 2000 jobs in 3.084s
Completed 2000 jobs in 3.093s
Completed 2000 jobs in 3.081s
Completed 2000 jobs in 3.111s
Completed 2000 jobs in 3.098s
Completed 2000 jobs in 3.101s
Completed 2000 jobs in 3.079s

COUNT=4 INTERVAL=0.01 QUEUE=default APP_INCLUDE=examples/worker_fcgi_include.php php vendor/.../resque.php
4 workers, no arguments
Completed 2000 jobs in 0.982s ~1.9ms per job per core
Completed 2000 jobs in 0.981s
Completed 2000 jobs in 0.988s
Completed 2000 jobs in 0.988s
Completed 2000 jobs in 0.989s
Completed 2000 jobs in 0.995s
Completed 2000 jobs in 0.987s
Completed 2000 jobs in 0.996s
Completed 2000 jobs in 0.983s
Completed 2000 jobs in 0.986s

To summarize:

FORK one core: 4.8ms/job/core four cores: 6.4ms/job/core
FASTCGI one core: 1.5ms/job/core four cores: 1.9ms/job/core

Naturally this is a synthetic benchmark with the actual job doing nothing. It is intended only to measure the amount of overhead involved in starting resque jobs. Additionally this was run over the loopback interface, as the provided FCGIClient class does not support unix sockets. It is unknown at this point how much overhead there is between tcp and unix sockets for our usage.

Thoughts? Would there be any interest in flushing out to a proper implementation?

EDIT: fixed bad math, 4 * 0.982/2000 makes for 1.9ms/job/core on 4 core fastcgi

Why queue is not a member variable of Job class?

In Resque queue is set as a member variable of Job class
but in PHP-Resque queue is something you just put whatever you like when you try to enqueue a job.

May I ask why?

And my real problem is, the event AfterEnqueue got 2 argvs, $class and $arg

But I need to know which queue this job belongs.

Can't do.

Of course I could modify lib/Resque.php to pass the $queue into AfterEnqueue,
but I still want to know why it's different from the origin Resque

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.