Cute is a minimalistic queuing system for beanstalkd.

Installs: 610

Dependents: 0

Stars: 6

Watchers: 2

Language: PHP

0.2.0 2015-01-13 13:32 UTC


,-. . . |- ,-.
|   | | |  |-'
`-' `-^ `' `-'
-- PHP version of the beanstalkd-backed job queuing system.

First of all is cute a concept of a coherent and feature complete job
system. Its small API surface and flat class hierarchy allows to easily
understand and reimplement it in other languages.

In many ways it can be thought of a hybrid between resque[0] and gearman[1]
while using beanstalkd[2] as its backend. Resque inspired how worker
processes behave, the overall feature set and finally the concept
of creating an implementation that through its simplicity becomes
specification-like. The way how handlers are registered centrally has been
borrowed from gearman.

While one of the goals for cute is to keep it as slim and lean as possible,
it should provide a feature set that fulfills all common requirements of
web applications. Cute expects jobs to fail[3] and provides extensive
logging throughout its codebase.


=== This is an early release and not yet ready for production.
=== Some features are still being implemented (delayed and scheduled jobs).
=== You may still try it, anybody who's testing helps making cute better.

- Administer and debug by using standard beanstalkd tooling. No
  special "cute admin" tooling needed.
- Extensive logging to ease debugging, builtin process title support for
  workers without the need of additional extensions.
- Native job persistence.
- Native job priorities and TTR.
- Native delayed jobs. (not yet implemented)
- Recurring jobs - your very own cron. (not yet implemented)
- Blocking and non-blocking jobs.
- Fallback to inline processing if job server goes offline.
- Resilient workers.
- Back off strategies builtin.
- Namespaced connection operations allow for sharing a single job server
  instance with multiple apps.

The preferred installation method is via composer. You can add
the library as a dependency via:

$ composer require atelierdisko/cute

First we create a `handlers.php` file in which we'll register handlers. In
this example we register a handler that resizes images. Handlers must be
available to both workers and producers so its a good idea to keep them in
a dedicated file.

Handlers are identified by a name and have one handler function. This
function does the actual work when processing a job.

<?php // handlers.php

use Cute\Handlers;

Handlers::register('thumbnail', function($data) {
	$image = new Image($data['source']);
	$image->fit(100, 150);

	// Returning `false` will fail and bury the job, `true`
	// to signal success.
	return $image->store($data['target']);


Regular Jobs
<?php // somewhere in your application

use Cute\Connection;
use Cute\Job;

// Create connection. This should be done at best once in your application.
// Then the connection instance can be shared between job instances.
$connection = new Connection();

// Create the instance; optionally pass a PSR-3 compatible logger as the
// second parameter to enable logging.
$job = new Job($connection);

// Enqueue a job using the "thumbnail" handler we registered in the section
// above. Any data we pass to run() will get passed to the handler.
$job->run('thumbnail', [
	'from' => '/path/to/cat.png',
	'to' => '/tmp/cat_thumb.png'


Delayed Jobs
<?php // somewhere in your application

// Setup $connection as we did for regular jobs.
// ...

// Delay job processing by 2 minutes.
$job->in('+2 minutes', 'sendFollowUpMail', '');


Recurring Jobs
<?php // schedule.php

// Setup $connection as we did for regular jobs.
// ...

// Specifying a recurring task using crontab format.
$job->recur('30 6 * * 1', 'updateStream');

// Specifying a recurring task with a delay of 2 minutes until
// first execution.
$job->recur(['30 6 * * 1', '+2 minutes'], 'updateStream');


Workers & Scheduler
Workers can be started using the following command. Workers must have
access to registered handlers.

$ bin/cute-worker --queue=image --require=./handlers.php

Cute workers are very similar to resque workers and can be controlled
through signals in the exact same way:

QUIT - Wait for child to finish processing then exit.
TERM / INT - Immediately kill child then exit.
USR2 - Don't start to process any new jobs.
CONT - Start to process new jobs again after a USR2.

Every time the scheduler is started it clears the scheduled queue and
installs its "crontab".

$ bin/cute-scheduler --require=./handlers.php --require=./schedule.php

Job Options
You may - as a last parameter - pass a number of options to the handlers
and enqueuing methods. Options passed on an enqueue call take precendence
over the ones defined on handlers.


// These methods all take the same options.
Handlers::register(/* name, callback, options */);
$job->run(/* handler, data, options */);
$job->in(/* delay, handler, data, options */);
$job->recur(/* expression, handler, data, options */);

// The following options are available.
$options = [
	// Prioritize job. _HIGH, _NORMAL, _LOW are available; defaults
	// to _NORMAL. Alternatively an integer between 0 (highest) and
	// 4294967295 (lowest).
	'priority' => Job::PRIORITY_NORMAL,

	// Makes the call block until worker has processed the job.
	// Defaults to `false`.
	'wait' => false,

	// If we can't access the job server, should we process inline
	// instead? Note that inline operations will block. Defaults to
	// `false`.
	'fallback' => false,

	// Time the job is allowed to run in seconds; by default
	// 10 minutes.
	'ttr' => 600,

	// The name of the queue to put the job in. Defaults to `default`.
	'queue' => 'default'


Design Choices
Why not Resque?
- Resque is great. Especially that its simple "specification"
  spawned many implementations in different languages.

  However one of the original reasons why resque was created, was that
  beanstalkd didn't provide persistence. That doesn't hold true anymore.

  We also wanted a simpler way to perform jobs. We didn't want to create
  a class for each "Performance" and prefer simple anonymous functions

Why not Redis as a Backend?
- We've been very tempted to use redis as the job server backend as it's
  already part of ours (and probably many others) infrastructure and could
  simply be reused for this purpose.

  But we also had lots of very good experience with beanstalkd and
  enjoy its semantics and minimalism. We also think we can benefit from
  beanstalkd being specialized as a work queue and its feature set. Using
  redis would've meant reimplementing a poor beanstalkd instead.

Why not Gearman?
- Gearman is great and provides lots of valuable features. We didn't like
  the semantics (do vs doBackound vs doTask) and the sheer size of it. We
  wanted to define our own minimal semantics.

Copyright & License
Cute is Copyright (c) 2014 Atelier Disko if not otherwise stated. The code
is distributed under the terms of the BSD 3-clause License. For the full
license text see the LICENSE file.