Added Gearman Lock Test and Worker, verified lockings indeed works on MySQL, PostgreSQL and Oracle
This commit is contained in:
parent
f65a555d04
commit
b8402c9563
177
tests/Doctrine/Tests/ORM/Functional/Locking/GearmanLockTest.php
Normal file
177
tests/Doctrine/Tests/ORM/Functional/Locking/GearmanLockTest.php
Normal file
@ -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());
|
||||||
|
}
|
||||||
|
}
|
111
tests/Doctrine/Tests/ORM/Functional/Locking/LockAgentWorker.php
Normal file
111
tests/Doctrine/Tests/ORM/Functional/Locking/LockAgentWorker.php
Normal file
@ -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();
|
25
tests/README.markdown
Normal file
25
tests/README.markdown
Normal file
@ -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.
|
Loading…
Reference in New Issue
Block a user