Skip to content

Commit

Permalink
draft: Introduce conversation property version with lock on updates
Browse files Browse the repository at this point in the history
Signed-off-by: Joas Schilling <[email protected]>
  • Loading branch information
nickvergessen committed Aug 28, 2024
1 parent 5fbbbea commit ae6c74d
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 5 deletions.
13 changes: 13 additions & 0 deletions lib/Exceptions/RoomLockedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/


namespace OCA\Talk\Exceptions;

class RoomLockedException extends \RuntimeException {
}
56 changes: 51 additions & 5 deletions lib/Service/RoomService.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
namespace OCA\Talk\Service;

use InvalidArgumentException;
use OC\Memcache\NullCache;
use OCA\Talk\Config;
use OCA\Talk\Events\AParticipantModifiedEvent;
use OCA\Talk\Events\ARoomModifiedEvent;
Expand All @@ -26,6 +27,7 @@
use OCA\Talk\Events\RoomModifiedEvent;
use OCA\Talk\Events\RoomPasswordVerifyEvent;
use OCA\Talk\Events\RoomSyncedEvent;
use OCA\Talk\Exceptions\RoomLockedException;
use OCA\Talk\Exceptions\RoomNotFoundException;
use OCA\Talk\Manager;
use OCA\Talk\Model\Attendee;
Expand All @@ -37,10 +39,14 @@
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\Comments\IComment;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\HintException;
use OCP\ICacheFactory;
use OCP\IDBConnection;
use OCP\IMemcache;
use OCP\IRequestId;
use OCP\IUser;
use OCP\Log\Audit\CriticalActionPerformedEvent;
use OCP\Security\Events\ValidatePasswordPolicyEvent;
Expand All @@ -52,6 +58,7 @@
* @psalm-import-type TalkRoom from ResponseDefinitions
*/
class RoomService {
protected IMemcache $cache;

public function __construct(
protected Manager $manager,
Expand All @@ -64,7 +71,46 @@ public function __construct(
protected IEventDispatcher $dispatcher,
protected IJobList $jobList,
protected LoggerInterface $logger,
protected IRequestId $requestId,
ICacheFactory $cacheFactory,
) {
$this->cache = $cacheFactory->createLocking('talkroom_');
}

/**
* @throws RoomLockedException
*/
protected function updateRoomPropertyWithLock(Room $room, string $property, int $value): void {
$cacheKey = $this->cache instanceof NullCache ? null : (string)$room->getId();

Check failure on line 84 in lib/Service/RoomService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedClass

lib/Service/RoomService.php:84:39: UndefinedClass: Class, interface or enum named OC\Memcache\NullCache does not exist (see https://psalm.dev/019)
if ($cacheKey !== null) {
$this->cache->add($cacheKey, $this->requestId->getId(), 30);
$sleeps = 0;
while ($this->cache->get($cacheKey) !== $this->requestId->getId()) {
if ($sleeps > 30) {
throw new RoomLockedException();
}
usleep(10_000);
$sleeps++;
$this->cache->add($cacheKey, $this->requestId->getId(), 30);
}

if ($sleeps !== 0) {
$room = $this->manager->getRoomById($room->getId());
}
}

$update = $this->db->getQueryBuilder();
$update->update('talk_rooms')
->set($property, $update->createNamedParameter($value, IQueryBuilder::PARAM_INT))
->set('properties_version', $update->func()->add('properties_version', $update->expr()->literal(1, IQueryBuilder::PARAM_INT)))
->where($update->expr()->eq('id', $update->createNamedParameter($room->getId(), IQueryBuilder::PARAM_INT)))
->andWhere($update->expr()->eq('properties_version', $update->createNamedParameter($room->getPropertiesVersion(), IQueryBuilder::PARAM_INT)));
$update->executeStatement();

$room->setPropertiesVersion($room->getPropertiesVersion() + 1);
if ($cacheKey !== null) {
$this->cache->cad($cacheKey, $this->requestId->getId());
}
}

/**
Expand Down Expand Up @@ -213,11 +259,11 @@ public function setDefaultPermissions(Room $room, int $permissions): void {
// Reset custom user permissions to default
$this->participantService->updateAllPermissions($room, Attendee::PERMISSIONS_MODIFY_SET, Attendee::PERMISSIONS_DEFAULT);

$update = $this->db->getQueryBuilder();
$update->update('talk_rooms')
->set('default_permissions', $update->createNamedParameter($newPermissions, IQueryBuilder::PARAM_INT))
->where($update->expr()->eq('id', $update->createNamedParameter($room->getId(), IQueryBuilder::PARAM_INT)));
$update->executeStatement();
try {
$this->updateRoomPropertyWithLock($room, 'default_permissions', $newPermissions);
} catch (RoomLockedException $e) {
throw new InvalidArgumentException('locked');
}

$room->setDefaultPermissions($newPermissions);

Expand Down

0 comments on commit ae6c74d

Please sign in to comment.