Permalink
Browse files

Added Gearman Lock Test and Worker, verified lockings indeed works on…

… MySQL, PostgreSQL and Oracle
  • Loading branch information...
1 parent f65a555 commit b8402c9563682b1e547e501378ff8b50292cf3eb @beberlei beberlei committed May 2, 2010
@@ -0,0 +1,177 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\Locking;
+
+use Doctrine\Tests\Models\CMS\CmsArticle,
+ Doctrine\Tests\Models\CMS\CmsUser,
+ Doctrine\ORM\LockMode,
+ Doctrine\ORM\EntityManager;
+
+require_once __DIR__ . '/../../../TestInit.php';
+
+class GearmanLockTest extends \Doctrine\Tests\OrmFunctionalTestCase
+{
+ private $gearman = null;
+ private $maxRunTime = 0;
+ private $articleId;
+
+ protected function setUp()
+ {
+ if (!class_exists('GearmanClient', false)) {
+ $this->markTestSkipped('pecl/gearman is required for this test to run.');
+ }
+
+ $this->useModelSet('cms');
+ parent::setUp();
+ $this->tasks = array();
+
+ $this->gearman = new \GearmanClient();
+ $this->gearman->addServer();
+ $this->gearman->setCompleteCallback(array($this, "gearmanTaskCompleted"));
+
+ $article = new CmsArticle();
+ $article->text = "my article";
+ $article->topic = "Hello";
+
+ $this->_em->persist($article);
+ $this->_em->flush();
+
+ $this->articleId = $article->id;
+ }
+
+ public function gearmanTaskCompleted($task)
+ {
+ $this->maxRunTime = max($this->maxRunTime, $task->data());
+ }
+
+ public function testFindWithLock()
+ {
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_WRITE);
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_WRITE);
+
+ $this->assertLockWorked();
+ }
+
+ public function testFindWithWriteThenReadLock()
+ {
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_WRITE);
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_READ);
+
+ $this->assertLockWorked();
+ }
+
+ public function testFindWithReadThenWriteLock()
+ {
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_READ);
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_WRITE);
+
+ $this->assertLockWorked();
+ }
+
+ public function testFindWithOneLock()
+ {
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_WRITE);
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::NONE);
+
+ $this->assertLockDoesNotBlock();
+ }
+
+ public function testDqlWithLock()
+ {
+ $this->asyncDqlWithLock('SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a', array(), LockMode::PESSIMISTIC_WRITE);
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_WRITE);
+
+ $this->assertLockWorked();
+ }
+
+ public function testLock()
+ {
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_WRITE);
+ $this->asyncLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_WRITE);
+
+ $this->assertLockWorked();
+ }
+
+ public function testLock2()
+ {
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_WRITE);
+ $this->asyncLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_READ);
+
+ $this->assertLockWorked();
+ }
+
+ public function testLock3()
+ {
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_READ);
+ $this->asyncLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_WRITE);
+
+ $this->assertLockWorked();
+ }
+
+ public function testLock4()
+ {
+ $this->asyncFindWithLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::NONE);
+ $this->asyncLock('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId, LockMode::PESSIMISTIC_WRITE);
+
+ $this->assertLockDoesNotBlock();
+ }
+
+ protected function assertLockDoesNotBlock()
+ {
+ $this->assertLockWorked($onlyForSeconds = 1);
+ }
+
+ protected function assertLockWorked($forTime = 2, $notLongerThan = null)
+ {
+ if ($notLongerThan === null) {
+ $notLongerThan = $forTime + 1;
+ }
+
+ $this->gearman->runTasks();
+
+ $this->assertTrue($this->maxRunTime > $forTime,
+ "Because of locking this tests should have run at least " . $forTime . " seconds, ".
+ "but only did for " . $this->maxRunTime . " seconds.");
+ $this->assertTrue($this->maxRunTime < $notLongerThan,
+ "The longest task should not run longer than " . $notLongerThan . " seconds, ".
+ "but did for " . $this->maxRunTime . " seconds."
+ );
+ }
+
+ protected function asyncFindWithLock($entityName, $entityId, $lockMode)
+ {
+ $this->startGearmanJob('findWithLock', array(
+ 'entityName' => $entityName,
+ 'entityId' => $entityId,
+ 'lockMode' => $lockMode,
+ ));
+ }
+
+ protected function asyncDqlWithLock($dql, $params, $lockMode)
+ {
+ $this->startGearmanJob('dqlWithLock', array(
+ 'dql' => $dql,
+ 'dqlParams' => $params,
+ 'lockMode' => $lockMode,
+ ));
+ }
+
+ protected function asyncLock($entityName, $entityId, $lockMode)
+ {
+ $this->startGearmanJob('lock', array(
+ 'entityName' => $entityName,
+ 'entityId' => $entityId,
+ 'lockMode' => $lockMode,
+ ));
+ }
+
+ protected function startGearmanJob($fn, $fixture)
+ {
+ $this->gearman->addTask($fn, serialize(array(
+ 'conn' => $this->_em->getConnection()->getParams(),
+ 'fixture' => $fixture
+ )));
+
+ $this->assertEquals(GEARMAN_SUCCESS, $this->gearman->returnCode());
+ }
+}
@@ -0,0 +1,111 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\Locking;
+
+require_once __DIR__ . "/../../../TestInit.php";
+
+class LockAgentWorker
+{
+ private $em;
+
+ static public function run()
+ {
+ $lockAgent = new LockAgentWorker();
+
+ $worker = new \GearmanWorker();
+ $worker->addServer();
+ $worker->addFunction("findWithLock", array($lockAgent, "findWithLock"));
+ $worker->addFunction("dqlWithLock", array($lockAgent, "dqlWithLock"));
+ $worker->addFunction('lock', array($lockAgent, 'lock'));
+
+ while($worker->work()) {
+ if ($worker->returnCode() != GEARMAN_SUCCESS) {
+ echo "return_code: " . $worker->returnCode() . "\n";
+ break;
+ }
+ }
+ }
+
+ protected function process($job, \Closure $do)
+ {
+ $fixture = $this->processWorkload($job);
+
+ $s = microtime(true);
+ $this->em->beginTransaction();
+ $do($fixture, $this->em);
+
+ sleep(1);
+ $this->em->rollback();
+ $this->em->clear();
+ $this->em->close();
+ $this->em->getConnection()->close();
+
+ return (microtime(true) - $s);
+ }
+
+ public function findWithLock($job)
+ {
+ return $this->process($job, function($fixture, $em) {
+ $entity = $em->find($fixture['entityName'], $fixture['entityId'], $fixture['lockMode']);
+ });
+ }
+
+ public function dqlWithLock($job)
+ {
+ return $this->process($job, function($fixture, $em) {
+ /* @var $query Doctrine\ORM\Query */
+ $query = $em->createQuery($fixture['dql']);
+ $query->setLockMode($fixture['lockMode']);
+ $query->setParameters($fixture['dqlParams']);
+ $result = $query->getResult();
+ });
+ }
+
+ public function lock($job)
+ {
+ return $this->process($job, function($fixture, $em) {
+ $entity = $em->find($fixture['entityName'], $fixture['entityId']);
+ $em->lock($entity, $fixture['lockMode']);
+ });
+ }
+
+ protected function processWorkload($job)
+ {
+ echo "Received job: " . $job->handle() . " for function " . $job->functionName() . "\n";
+
+ $workload = $job->workload();
+ $workload = unserialize($workload);
+
+ if (!isset($workload['conn']) || !is_array($workload['conn'])) {
+ throw new \InvalidArgumentException("Missing Database parameters");
+ }
+
+ $this->em = $this->createEntityManager($workload['conn']);
+
+ if (!isset($workload['fixture'])) {
+ throw new \InvalidArgumentException("Missing Fixture parameters");
+ }
+ return $workload['fixture'];
+ }
+
+ protected function createEntityManager($conn)
+ {
+ $config = new \Doctrine\ORM\Configuration();
+ $config->setProxyDir(__DIR__ . '/../../../Proxies');
+ $config->setProxyNamespace('MyProject\Proxies');
+ $config->setAutoGenerateProxyClasses(true);
+
+ $annotDriver = $config->newDefaultAnnotationDriver(array(__DIR__ . '/../../../Models/'));
+ $config->setMetadataDriverImpl($annotDriver);
+
+ $cache = new \Doctrine\Common\Cache\ArrayCache();
+ $config->setMetadataCacheImpl($cache);
+ $config->setQueryCacheImpl($cache);
+
+ $em = \Doctrine\ORM\EntityManager::create($conn, $config);
+
+ return $em;
+ }
+}
+
+LockAgentWorker::run();
View
@@ -0,0 +1,25 @@
+# Running the Doctrine 2 Testsuite
+
+## Setting up a PHPUnit Configuration XML
+
+..
+
+## Testing Lock-Support
+
+The Lock support in Doctrine 2 is tested using Gearman, which allows to run concurrent tasks in parallel.
+Install Gearman with PHP as follows:
+
+1. Go to http://www.gearman.org and download the latest Gearman Server
+2. Compile it and then call ldconfig
+3. Start it up "gearmand -vvvv"
+4. Install pecl/gearman by calling "gearman-beta"
+
+You can then go into tests/ and start up two workers:
+
+ php Doctrine/Tests/ORM/Functional/Locking/LockAgentWorker.php
+
+Then run the locking test-suite:
+
+ phpunit --configuration <myconfig.xml> Doctrine/Tests/ORM/Functional/Locking/GearmanLockTest.php
+
+This can run considerable time, because it is using sleep() to test for the timing ranges of locks.

0 comments on commit b8402c9

Please sign in to comment.