Author: bicou Date: 2010-05-16 23:16:50 +0200 (Sun, 16 May 2010) New Revision: 29486
Added: plugins/sfGearmanPlugin/tags/0.9.0/ plugins/sfGearmanPlugin/tags/0.9.0/LICENSE plugins/sfGearmanPlugin/tags/0.9.0/README plugins/sfGearmanPlugin/tags/0.9.0/config/ plugins/sfGearmanPlugin/tags/0.9.0/config/gearman.yml plugins/sfGearmanPlugin/tags/0.9.0/lib/ plugins/sfGearmanPlugin/tags/0.9.0/package.xml.tmpl plugins/sfGearmanPlugin/tags/0.9.0/test/ Removed: plugins/sfGearmanPlugin/tags/0.9.0/config/gearman.yml plugins/sfGearmanPlugin/tags/0.9.0/test/fixtures/project/cache/project_autoload.cache Log: [sfGearmanPlugin] tag version 0.9.0 Copied: plugins/sfGearmanPlugin/tags/0.9.0/LICENSE (from rev 29482, plugins/sfGearmanPlugin/trunk/LICENSE) =================================================================== --- plugins/sfGearmanPlugin/tags/0.9.0/LICENSE (rev 0) +++ plugins/sfGearmanPlugin/tags/0.9.0/LICENSE 2010-05-16 21:16:50 UTC (rev 29486) @@ -0,0 +1,19 @@ +Copyright (c) 2010 Benjamin VIELLARD, [email protected] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. Copied: plugins/sfGearmanPlugin/tags/0.9.0/README (from rev 29485, plugins/sfGearmanPlugin/trunk/README) =================================================================== --- plugins/sfGearmanPlugin/tags/0.9.0/README (rev 0) +++ plugins/sfGearmanPlugin/tags/0.9.0/README 2010-05-16 21:16:50 UTC (rev 29486) @@ -0,0 +1,407 @@ +sfGearmanPlugin +=============== + +The sfGearman plugin provides a symfony wrapper to gearman pecl module. + +Features: + +* configuration of servers and workers in a yaml config file +* run workers in a symfony task +* auto (un)serialization of job workloads/results +* a simple message queue manager +* a Doctrine Template to trigger tasks on record/table events +* a worker for gearman jobs based on MySQL TRIGGER (gearman UDF) + + +Installation +------------ + +First you need to install the gearman pecl module, version 0.6.0 minimum. + +Then you can install this plugin the usual way (RTFM), or if you want to work with the trunk: + + $ cd plugins + $ svn co http://svn.symfony-project.com/plugins/sfGearmanPlugin/trunk/ sfGearmanPlugin + +Then activate the plugin in the `config/ProjectConfiguration.class.php` file. + + +Configuration +------------- + +By default, there is a connection named "default" which targets a local gearman server. + +Edit or create `config/gearman.yml` to suit your gearman server installation : + + [yml] + all: + server: + default: + host: 192.168.0.1 + port: 4730 + +You can also use host:port based notation : + + [yml] + all: + server: + default: 127.0.0.1:4730 + +If you have more than one gearman job server, you can list them and mix notations this way : + + [yml] + all: + server: + default: + - 192.168.0.1:4730 + - { host: 192.168.0.2, port: 4730 } + + +Create a worker +--------------- + +Edit `config/gearman.yml` to define worker functions and callbacks, grouped by a key name : + + [yml] + all: + worker: + example1: + reverse: [Worker1, reverse] + +We defined a worker named "example1" registering gearman function "reverse" with `Worker1::reverse()` callback. + +You can register multiple function for one worker : + + [yml] + all: + worker: + example1: + reverse: [Worker1, reverse] + hash: [Worker1, hash] + + +Next implement you callback: + + [php] + /** + * Gearman worker example1 + */ + class Worker1 + { + /** + * reverse work handler + * + * @param GearmanJob $job Gearman job + * @return string Result sent to client + */ + public static function reverse($job) + { + return strrev($job->workload()); + } + } + + +To understand `GearmanJob` and `$job->workload()`, read [gearman php module documentation](http://php.net/gearman). + +To start your worker, use the symfony task `gearman:worker` with `--config=example1` : + + $ symfony gearman:worker --config=example1 + + +This command starts a gearman worker, and exit after processing 100 jobs or waiting 20 secs for a job. +You can tweak this with `--count=100` and `--timeout=20` options (use 0 for count and a negative value for timeout to never end worker). + +If you want your worker to restart automatically, use [supervisord](http://supervisord.org/) or [daemon-tools](http://cr.yp.to/daemontools.html), or any process control tool. + +To see what happens, use `--verbose` option : + + $ symfony gearman:worker --config=example1 --verbose + + +See all options with : + + $ symfony help gearman:worker + + +If you want to trace jobs and workloads as well, you need to notify symfony that a job is processed : + + [php] + /** + * Gearman worker example1 + */ + class Worker1 + { + /** + * reverse work handler + * + * @param GearmanJob $job Gearman job + * @param sfGearmanWorker $worker sfGearmanWorker + * @return string Result sent to client + */ + public static function reverse($job, $worker) + { + // sfGearman worker is passed as the 2nd parameter of the method + // notifyEventJob() displays a trace in symfony task output + // if --verbose is set, workload is logged too + $worker->notifyEventJob($job); + + return strrev($job->workload()); + } + } + + +Gearman protocol only handles strings in workloads, if you need to return an array or object as a worker result, use `sfGearman::serialize` : + + [php] + /** + * Gearman worker example1 + */ + class Worker1 + { + /** + * reverse work handler + * + * @param GearmanJob $job Gearman job + * @param sfGearmanWorker $worker sfGearmanWorker + * @return string Result sent to client + */ + public static function hash($job, $worker) + { + $worker->notifyEventJob($job); + + $workload = $job->workload(); + + $result = array(md5($workload), sha1($workload)); + + return sfGearman::serialize($result); + } + } + +The sfGearmanClient will automatically unserialize the result if needed. + + +Use a client +------------ + +To create a gearman client, use `sfGearmanClient::getInstance` : + + [php] + // client connecting to default server + $client = sfGearmanClient::getInstance(); + + // client connecting to a different server defined in gearman.yml + $client = sfGearmanClient::getInstance('local'); + + +You have 2 shorthands methods to send a job to gearman server : `task('function' [, 'workload'])` and `background('function' [, 'workload'])`, example: + + [php] + // this blocks until a worker do the job and return result + $result = sfGearmanClient::getInstance()->task('reverse', 'Hello!'); + // $result == '!olleH' + + // this sends an asynchronous job to gearman server, the return value is a gearman handle + $handle = sfGearmanClient::getInstance()->background('async'); + // $handle == 'H:host:id' + + +If you need priorities for your jobs, pass as 3rd parameter the level you want : + + [php] + // this job has high priority + $result = sfGearmanClient::getInstance()->task('reverse', 'Hello!', sfGearman::HIGH); + + // this job has low priority + $result = sfGearmanClient::getInstance()->task('reverse', 'Hello!', sfGearman::LOW); + + +Message queue manager +--------------------- + +The plugin provides a `sfGearmanQueue` class to put and get messages in queues, usage : + + [php] + // put a message in a queue named "q1" + sfGearmanQueue::put('q1', 'a message'); + + // later or elsewhere, get a message from queue + $message = sfGearmanQueue::get('q1'); + + // put a message with high priority (will be fetched first) + sfGearmanQueue::put('q1', 'urgent', sfGearman::HIGH); + + // ::get() blocks forever until a message arrives, if you want to timeout, use 2nd parameter (in ms) + try + { + $message = sfGearmanQueue::get('q1', 10000); + } + catch(sfGearmanTimeoutException $e) + { + // waited 10 secs but no message in queue + } + + +Internally, this sends messages as serialized workloads of "queue.%name%" jobs. + + +Doctrine integration +-------------------- + +The sfGearmanPlugin provides a Doctrine Template which listens to insert/update/delete events and sends background jobs to gearman server. + +Add the Gearmanable template to `doctrine/schema.yml`, for example we want to listen to the update events of articles: + + [yml] + Article: + actAs: + Gearmanable: {events: [update]} + columns: + title: string(200) + +Update your models, then configure `gearman.yml` to create a doctrine worker : + + [yml] + all: + doctrine: + example2: + Article: ~ + +Unlike the classic worker configuration, the doctrine one is made of the model name as key and the list of events as value, ~ is an alias to all events defined in `schema.yml`. + +Then implement worker callback, they need to be located in the model class and named "trigger%Event%" (to avoid overlapping), so : + + [php] + class Article extends BaseArticle + { + public function triggerUpdate($modified) + { + if (in_array('title', $modified)) + { + // update a search index, refresh symfony cache, ... + } + } + } + +Note that the only parameter for doctrine gearman work handler is an array of modified properties. + +Then launches a doctrine worker with symfony `gearman:worker-doctrine` task : + + $ symfony gearman:worker-doctrine --config=example2 + +You could omit the `--config=` option, this loads all doctrine models and register all events defined in `schema.yml`, but this merge all jobs in same workers. + + +Then save as usual your records : + + [php] + $article = Doctrine_Core::getTable('Article')->find($id); + $article->title = 'new title'; + $article->save(); + +This is what happens : + +1. The `->save()` sends a background job to gearman server containing serialized record +2. The gearman server sends the job to the worker +3. The worker unserializes the record and call the trigger + +Note: + +The object transits through gearman server, and the trigger is called in another php process. + +So when the record arrives to symfony worker task, it may not exists anymore in the database, or may be out-of-date. + +If you want a fresh copy, use doctrine `refresh()` in the handler : + + [php] + class Article extends BaseArticle + { + public function triggerUpdate($modified) + { + try { $this->refresh(); } + catch (Doctrine_Record_Exception $e) { return; } + + // here the record is reloaded from db + } + } + +Don't use the previous snippet in `triggerDelete()` because what you have in the worker task is only a ghost of your record. + + +To access the gearman job object, use the method `->getGearmanJob()` : + + [php] + class Article extends BaseArticle + { + public function triggerUpdate($modified) + { + $job = $this->getGearmanJob(); + $job->sendFail(); + } + } + + +Custom doctrine jobs +-------------------- + +You can have custom jobs for Doctrine records, register them in `gearman.yml`: + + [yml] + all: + doctrine: + example2: + Article: [publish, ~] + +Implement them in your model class: + + [php] + class Article extends BaseArticle + { + public function publish($tweet, $ping) + { + try { $this->refresh(); } + catch (Doctrine_Record_Exception $e) { return; } + + // heavy code to publish your article + } + } + +Reload your worker task to work this new function. + +Then launch tasks with `->task('function' [, ...])` for synchronous jobs, or `->taskBackground('function' [, ...])` for asynchronous ones. + + [php] + $article = Doctrine_Core::getTable('Article')->find($id); + $article->taskBackground('publish', false, true); + // this returns immediately and let the symfony task do the heavy code for ->publish(false, true) + + +Doctrine table jobs +------------------- + +You can have function for Doctrine table, example : + + [yml] + all: + doctrine: + example2: + Article: [buildFeed, publish, ~] + + +Define the job handler in the table class : + + [php] + class TestArticleTable extends Doctrine_Table + { + public function buildFeed() + { + // build the feeds + } + } + + +Call `task()` or `taskBackground()` on the table : + + [php] + Doctrine_Core::getTable('Article')->taskBackground('buildFeed'); + + Deleted: plugins/sfGearmanPlugin/tags/0.9.0/config/gearman.yml =================================================================== --- plugins/sfGearmanPlugin/trunk/config/gearman.yml 2010-05-16 17:11:45 UTC (rev 29482) +++ plugins/sfGearmanPlugin/tags/0.9.0/config/gearman.yml 2010-05-16 21:16:50 UTC (rev 29486) @@ -1,3 +0,0 @@ -all: - server: - default: ~ Copied: plugins/sfGearmanPlugin/tags/0.9.0/config/gearman.yml (from rev 29485, plugins/sfGearmanPlugin/trunk/config/gearman.yml) =================================================================== --- plugins/sfGearmanPlugin/tags/0.9.0/config/gearman.yml (rev 0) +++ plugins/sfGearmanPlugin/tags/0.9.0/config/gearman.yml 2010-05-16 21:16:50 UTC (rev 29486) @@ -0,0 +1,5 @@ +# default server configuration +all: + server: + default: ~ # = GEARMAN_DEFAULT_TCP_HOST:GEARMAN_DEFAULT_TCP_PORT + Copied: plugins/sfGearmanPlugin/tags/0.9.0/package.xml.tmpl (from rev 29482, plugins/sfGearmanPlugin/trunk/package.xml.tmpl) =================================================================== --- plugins/sfGearmanPlugin/tags/0.9.0/package.xml.tmpl (rev 0) +++ plugins/sfGearmanPlugin/tags/0.9.0/package.xml.tmpl 2010-05-16 21:16:50 UTC (rev 29486) @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="##ENCODING##"?> +<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.4.1" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> + <name>sfGearmanPlugin</name> + <channel>plugins.symfony-project.org</channel> + <summary>Gearman plugin for symfony</summary> + <description>sfGearmanPlugin provides gearman support to symfony (pecl/gearman)</description> + <lead> + <name>Benjamin VIELLARD</name> + <user>bicou</user> + <email>[email protected]</email> + <active>yes</active> + </lead> + <date>##CURRENT_DATE##</date> + <version> + <release>##PLUGIN_VERSION##</release> + <api>##API_VERSION##</api> + </version> + <stability> + <release>##STABILITY##</release> + <api>##STABILITY##</api> + </stability> + <license uri="http://www.symfony-project.org/license">MIT license</license> + <notes>-</notes> + <contents> + ##CONTENTS## + </contents> + <dependencies> + <required> + <php> + <min>5.2.4</min> + </php> + <pearinstaller> + <min>1.4.1</min> + </pearinstaller> + <package> + <name>symfony</name> + <channel>pear.symfony-project.com</channel> + <min>1.2.0</min> + <max>2.0.0</max> + <exclude>2.0.0</exclude> + </package> + </required> + </dependencies> + <phprelease></phprelease> + <changelog> + <release> + <version> + <release>0.9.0</release> + <api>0.9.0</api> + </version> + <stability> + <release>beta</release> + <api>stable</api> + </stability> + <license uri="http://www.symfony-project.org/license">MIT license</license> + <date>2010-05-11</date> + <notes> + * First public release + </notes> + </release> + </changelog> +</package> Deleted: plugins/sfGearmanPlugin/tags/0.9.0/test/fixtures/project/cache/project_autoload.cache =================================================================== --- plugins/sfGearmanPlugin/trunk/test/fixtures/project/cache/project_autoload.cache 2010-05-16 17:11:45 UTC (rev 29482) +++ plugins/sfGearmanPlugin/tags/0.9.0/test/fixtures/project/cache/project_autoload.cache 2010-05-16 21:16:50 UTC (rev 29486) @@ -1 +0,0 @@ -a:3:{i:0;a:16:{s:9:"sfgearman";s:57:"/home/bicou/sfGearmanPlugin/trunk/lib/sfGearman.class.php";s:36:"doctrine_record_listener_gearmanable";s:84:"/home/bicou/sfGearmanPlugin/trunk/lib/Doctrine/Record/Listener/Gearmanable.class.php";s:29:"doctrine_template_gearmanable";s:77:"/home/bicou/sfGearmanPlugin/trunk/lib/Doctrine/Template/Gearmanable.class.php";s:14:"sfgearmanqueue";s:62:"/home/bicou/sfGearmanPlugin/trunk/lib/sfGearmanQueue.class.php";s:24:"gearmanworkertriggertask";s:77:"/home/bicou/sfGearmanPlugin/trunk/lib/task/gearmanWorkertriggerTask.class.php";s:23:"sfgearmanworkerbasetask";s:76:"/home/bicou/sfGearmanPlugin/trunk/lib/task/sfGearmanWorkerBaseTask.class.php";s:24:"gearmancreatetriggertask";s:77:"/home/bicou/sfGearmanPlugin/trunk/lib/task/gearmanCreateTriggerTask.class.php";s:25:"gearmanworkerdoctrinetask";s:78:"/home/bicou/sfGearmanPlugin/trunk/lib/task/gearmanWorkerdoctrineTask.class.php";s:17:"gearmanworkertask";s:70:"/home/bicou/sfGearmanPlugin/trunk/lib/task/gearmanWorkerTask.class.php";s:15:"sfgearmanclient";s:63:"/home/bicou/sfGearmanPlugin/trunk/lib/sfGearmanClient.class.php";s:15:"sfgearmanworker";s:63:"/home/bicou/sfGearmanPlugin/trunk/lib/sfGearmanWorker.class.php";s:22:"sfgearmanconfighandler";s:77:"/home/bicou/sfGearmanPlugin/trunk/lib/config/sfGearmanConfigHandler.class.php";s:18:"sfgearmanexception";s:76:"/home/bicou/sfGearmanPlugin/trunk/lib/exception/sfGearmanException.class.php";s:25:"sfgearmantimeoutexception";s:83:"/home/bicou/sfGearmanPlugin/trunk/lib/exception/sfGearmanTimeoutException.class.php";s:19:"sfgearmanconnection";s:67:"/home/bicou/sfGearmanPlugin/trunk/lib/sfGearmanConnection.class.php";s:23:"sfgearmanworkerdoctrine";s:71:"/home/bicou/sfGearmanPlugin/trunk/lib/sfGearmanWorkerDoctrine.class.php";}i:1;a:1:{i:2;s:37:"/home/bicou/sfGearmanPlugin/trunk/lib";}i:2;a:0:{}} \ No newline at end of file -- You received this message because you are subscribed to the Google Groups "symfony SVN" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/symfony-svn?hl=en.
