Issue #1976172 by chx, andypost: Comment entity acquired and releases the different locks.

8.0.x
Nathaniel Catchpole 2013-12-19 17:54:14 +00:00
parent ba6591cdd2
commit 44ed771a1c
3 changed files with 105 additions and 9 deletions

View File

@ -19,11 +19,6 @@ use Drupal\Core\Entity\EntityChangedInterface;
*/ */
class CommentStorageController extends FieldableDatabaseStorageController implements CommentStorageControllerInterface { class CommentStorageController extends FieldableDatabaseStorageController implements CommentStorageControllerInterface {
/**
* The thread for which a lock was acquired.
*/
protected $threadLock = '';
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */

View File

@ -56,6 +56,11 @@ use Drupal\Core\TypedData\DataDefinition;
*/ */
class Comment extends ContentEntityBase implements CommentInterface { class Comment extends ContentEntityBase implements CommentInterface {
/**
* The thread for which a lock was acquired.
*/
protected $threadLock = '';
/** /**
* The comment ID. * The comment ID.
* *
@ -273,12 +278,13 @@ class Comment extends ContentEntityBase implements CommentInterface {
} }
} }
// Finally, build the thread field for this new comment. To avoid // Finally, build the thread field for this new comment. To avoid
// race conditions, get a lock on the thread. If aother process already // race conditions, get a lock on the thread. If another process already
// has the lock, just move to the next integer. // has the lock, just move to the next integer.
do { do {
$thread = $prefix . comment_int_to_alphadecimal(++$n) . '/'; $thread = $prefix . comment_int_to_alphadecimal(++$n) . '/';
} while (!lock()->acquire("comment:{$this->entity_id->value}:$thread")); $lock_name = "comment:{$this->entity_id->value}:$thread";
$this->threadLock = $thread; } while (!\Drupal::lock()->acquire($lock_name));
$this->threadLock = $lock_name;
} }
if (empty($this->created->value)) { if (empty($this->created->value)) {
$this->created->value = REQUEST_TIME; $this->created->value = REQUEST_TIME;
@ -316,7 +322,7 @@ class Comment extends ContentEntityBase implements CommentInterface {
*/ */
protected function releaseThreadLock() { protected function releaseThreadLock() {
if ($this->threadLock) { if ($this->threadLock) {
lock()->release($this->threadLock); \Drupal::lock()->release($this->threadLock);
$this->threadLock = ''; $this->threadLock = '';
} }
} }

View File

@ -0,0 +1,95 @@
<?php
/**
* @file
* Contains \Drupal\comment\Tests\Entity\CommentTest
*/
namespace Drupal\comment\Tests\Entity {
use Drupal\comment\Entity\Comment;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Tests\UnitTestCase;
/**
* Unit tests for the comment entity lock behavior.
*
* @group Drupal
*/
class CommentLockTest extends UnitTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Comment locks',
'description' => 'Test comment acquires and releases the right lock.',
'group' => 'Comment',
);
}
/**
* Test the lock behavior.
*/
public function testLocks() {
$container = new ContainerBuilder();
$container->set('current_user', $this->getMock('Drupal\Core\Session\AccountInterface'));
$container->register('request', 'Symfony\Component\HttpFoundation\Request');
$lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface');
$cid = 2;
$lock_name = "comment:$cid:./";
$lock->expects($this->at(0))
->method('acquire')
->with($lock_name, 30)
->will($this->returnValue(TRUE));
$lock->expects($this->at(1))
->method('release')
->with($lock_name);
$lock->expects($this->exactly(2))
->method($this->anything());
$container->set('lock', $lock);
\Drupal::setContainer($container);
$methods = get_class_methods('Drupal\comment\Entity\Comment');
unset($methods[array_search('preSave', $methods)]);
unset($methods[array_search('postSave', $methods)]);
$comment = $this->getMockBuilder('Drupal\comment\Entity\Comment')
->disableOriginalConstructor()
->setMethods($methods)
->getMock();
$comment->expects($this->once())
->method('isNew')
->will($this->returnValue(TRUE));
foreach (array('status', 'pid', 'created', 'changed', 'entity_id', 'uid', 'thread', 'hostname') as $property) {
$comment->$property = new \stdClass();
}
$comment->status->value = 1;
$comment->entity_id->value = $cid;
$comment->uid->target_id = 3;
$comment->pid->target_id = 42;
$comment->pid->entity = new \stdClass();
$comment->pid->entity->thread = (object) array('value' => '');
$storage_controller = $this->getMock('Drupal\comment\CommentStorageControllerInterface');
$comment->preSave($storage_controller);
$comment->postSave($storage_controller);
}
/**
* {@inheritdoc}
*/
protected function tearDown() {
parent::tearDown();
$container = new ContainerBuilder();
\Drupal::setContainer($container);
}
}
}
namespace {
if (!function_exists('comment_int_to_alphadecimal')) {
function comment_int_to_alphadecimal() {}
}
if (!function_exists('module_invoke_all')) {
function module_invoke_all() {}
}
}