mirror of
https://github.com/nextcloud/server.git
synced 2026-03-29 05:43:57 -04:00
Merge pull request #54435 from nextcloud/perf/caldav/preload-calendar-publish-status
perf(caldav): preload publish statuses for a whole calendar home at once
This commit is contained in:
commit
9001ae2a4e
7 changed files with 86 additions and 4 deletions
|
|
@ -78,6 +78,7 @@ $calDavBackend = new CalDavBackend(
|
|||
$config,
|
||||
Server::get(\OCA\DAV\CalDAV\Sharing\Backend::class),
|
||||
Server::get(FederatedCalendarMapper::class),
|
||||
Server::get(\OCP\ICacheFactory::class),
|
||||
true
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ use OCP\Calendar\Exceptions\CalendarException;
|
|||
use OCP\DB\Exception;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\ICache;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IUserManager;
|
||||
|
|
@ -202,6 +204,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
private string $dbObjectInvitationsTable = 'calendar_invitations';
|
||||
private array $cachedObjects = [];
|
||||
|
||||
private readonly ICache $publishStatusCache;
|
||||
|
||||
public function __construct(
|
||||
private IDBConnection $db,
|
||||
private Principal $principalBackend,
|
||||
|
|
@ -212,8 +216,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
private IConfig $config,
|
||||
private Sharing\Backend $calendarSharingBackend,
|
||||
private FederatedCalendarMapper $federatedCalendarMapper,
|
||||
ICacheFactory $cacheFactory,
|
||||
private bool $legacyEndpoint = false,
|
||||
) {
|
||||
$this->publishStatusCache = $cacheFactory->createInMemory();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -923,6 +929,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @return void
|
||||
*/
|
||||
public function deleteCalendar($calendarId, bool $forceDeletePermanently = false) {
|
||||
$this->publishStatusCache->remove((string)$calendarId);
|
||||
|
||||
$this->atomic(function () use ($calendarId, $forceDeletePermanently): void {
|
||||
// The calendar is deleted right away if this is either enforced by the caller
|
||||
// or the special contacts birthday calendar or when the preference of an empty
|
||||
|
|
@ -3221,7 +3229,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
* @return string|null
|
||||
*/
|
||||
public function setPublishStatus($value, $calendar) {
|
||||
return $this->atomic(function () use ($value, $calendar) {
|
||||
$publishStatus = $this->atomic(function () use ($value, $calendar) {
|
||||
$calendarId = $calendar->getResourceId();
|
||||
$calendarData = $this->getCalendarById($calendarId);
|
||||
|
||||
|
|
@ -3249,13 +3257,21 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
$this->dispatcher->dispatchTyped(new CalendarUnpublishedEvent($calendarId, $calendarData));
|
||||
return null;
|
||||
}, $this->db);
|
||||
|
||||
$this->publishStatusCache->set((string)$calendar->getResourceId(), $publishStatus ?? false);
|
||||
return $publishStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Calendar $calendar
|
||||
* @return mixed
|
||||
* @return string|false
|
||||
*/
|
||||
public function getPublishStatus($calendar) {
|
||||
$cached = $this->publishStatusCache->get((string)$calendar->getResourceId());
|
||||
if ($cached !== null) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$result = $query->select('publicuri')
|
||||
->from('dav_shares')
|
||||
|
|
@ -3263,9 +3279,46 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
->andWhere($query->expr()->eq('access', $query->createNamedParameter(self::ACCESS_PUBLIC)))
|
||||
->executeQuery();
|
||||
|
||||
$row = $result->fetch();
|
||||
$publishStatus = $result->fetchOne();
|
||||
$result->closeCursor();
|
||||
|
||||
$this->publishStatusCache->set((string)$calendar->getResourceId(), $publishStatus);
|
||||
return $publishStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $resourceIds
|
||||
*/
|
||||
public function preloadPublishStatuses(array $resourceIds): void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$result = $query->select('resourceid', 'publicuri')
|
||||
->from('dav_shares')
|
||||
->where($query->expr()->in(
|
||||
'resourceid',
|
||||
$query->createNamedParameter($resourceIds, IQueryBuilder::PARAM_INT_ARRAY),
|
||||
IQueryBuilder::PARAM_INT_ARRAY,
|
||||
))
|
||||
->andWhere($query->expr()->eq(
|
||||
'access',
|
||||
$query->createNamedParameter(self::ACCESS_PUBLIC, IQueryBuilder::PARAM_INT),
|
||||
IQueryBuilder::PARAM_INT,
|
||||
))
|
||||
->executeQuery();
|
||||
|
||||
$hasPublishStatuses = [];
|
||||
while ($row = $result->fetch()) {
|
||||
$this->publishStatusCache->set((string)$row['resourceid'], $row['publicuri']);
|
||||
$hasPublishStatuses[(int)$row['resourceid']] = true;
|
||||
}
|
||||
|
||||
// Also remember resources with no publish status
|
||||
foreach ($resourceIds as $resourceId) {
|
||||
if (!isset($hasPublishStatuses[$resourceId])) {
|
||||
$this->publishStatusCache->set((string)$resourceId, false);
|
||||
}
|
||||
}
|
||||
|
||||
$result->closeCursor();
|
||||
return $row ? reset($row) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@
|
|||
*/
|
||||
namespace OCA\DAV\CalDAV\Publishing;
|
||||
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\Calendar;
|
||||
use OCA\DAV\CalDAV\CalendarHome;
|
||||
use OCA\DAV\CalDAV\Publishing\Xml\Publisher;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\IConfig;
|
||||
|
|
@ -91,6 +93,20 @@ class PublishPlugin extends ServerPlugin {
|
|||
}
|
||||
|
||||
public function propFind(PropFind $propFind, INode $node) {
|
||||
if ($node instanceof CalendarHome && $propFind->getDepth() === 1) {
|
||||
$backend = $node->getCalDAVBackend();
|
||||
if ($backend instanceof CalDavBackend) {
|
||||
$calendars = array_filter(
|
||||
$node->getChildren(),
|
||||
static fn ($child) => $child instanceof Calendar,
|
||||
);
|
||||
$resourceIds = array_map(
|
||||
static fn (Calendar $calendar) => $calendar->getResourceId(),
|
||||
$calendars,
|
||||
);
|
||||
$backend->preloadPublishStatuses($resourceIds);
|
||||
}
|
||||
}
|
||||
if ($node instanceof Calendar) {
|
||||
$propFind->handle('{' . self::NS_CALENDARSERVER . '}publish-url', function () use ($node) {
|
||||
if ($node->getPublishStatus()) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use OCA\DAV\Connector\Sabre\Principal;
|
|||
use OCP\Accounts\IAccountManager;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IGroupManager;
|
||||
|
|
@ -82,6 +83,7 @@ class CreateCalendar extends Command {
|
|||
$config,
|
||||
Server::get(Backend::class),
|
||||
Server::get(FederatedCalendarMapper::class),
|
||||
Server::get(ICacheFactory::class),
|
||||
);
|
||||
$caldav->createCalendar("principals/users/$user", $name, []);
|
||||
return self::SUCCESS;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ use OCP\AppFramework\Utility\ITimeFactory;
|
|||
use OCP\Comments\ICommentsManager;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IGroupManager;
|
||||
|
|
@ -108,6 +109,7 @@ class RootCollection extends SimpleCollection {
|
|||
$config,
|
||||
$calendarSharingBackend,
|
||||
Server::get(FederatedCalendarMapper::class),
|
||||
Server::get(ICacheFactory::class),
|
||||
false,
|
||||
);
|
||||
$userCalendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users', $logger, $l10n, $config, $federatedCalendarFactory);
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ abstract class AbstractCalDavBackend extends TestCase {
|
|||
protected RemoteUserPrincipalBackend&MockObject $remoteUserPrincipalBackend;
|
||||
protected FederationSharingService&MockObject $federationSharingService;
|
||||
protected FederatedCalendarMapper&MockObject $federatedCalendarMapper;
|
||||
protected ICacheFactory $cacheFactory;
|
||||
|
||||
public const UNIT_TEST_USER = 'principals/users/caldav-unit-test';
|
||||
public const UNIT_TEST_USER1 = 'principals/users/caldav-unit-test1';
|
||||
public const UNIT_TEST_GROUP = 'principals/groups/caldav-unit-test-group';
|
||||
|
|
@ -110,6 +112,7 @@ abstract class AbstractCalDavBackend extends TestCase {
|
|||
new Service(new SharingMapper($this->db)),
|
||||
$this->federationSharingService,
|
||||
$this->logger);
|
||||
$this->cacheFactory = $this->createMock(ICacheFactory::class);
|
||||
$this->backend = new CalDavBackend(
|
||||
$this->db,
|
||||
$this->principal,
|
||||
|
|
@ -120,6 +123,7 @@ abstract class AbstractCalDavBackend extends TestCase {
|
|||
$this->config,
|
||||
$this->sharingBackend,
|
||||
$this->federatedCalendarMapper,
|
||||
$this->cacheFactory,
|
||||
false,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use OCA\DAV\CalDAV\PublicCalendar;
|
|||
use OCA\DAV\CalDAV\PublicCalendarRoot;
|
||||
use OCA\DAV\Connector\Sabre\Principal;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IGroupManager;
|
||||
|
|
@ -43,6 +44,7 @@ class PublicCalendarRootTest extends TestCase {
|
|||
protected IConfig&MockObject $config;
|
||||
private ISecureRandom $random;
|
||||
private LoggerInterface&MockObject $logger;
|
||||
protected ICacheFactory&MockObject $cacheFactory;
|
||||
|
||||
protected FederatedCalendarMapper&MockObject $federatedCalendarMapper;
|
||||
|
||||
|
|
@ -56,6 +58,7 @@ class PublicCalendarRootTest extends TestCase {
|
|||
$this->random = Server::get(ISecureRandom::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->federatedCalendarMapper = $this->createMock(FederatedCalendarMapper::class);
|
||||
$this->cacheFactory = $this->createMock(ICacheFactory::class);
|
||||
$dispatcher = $this->createMock(IEventDispatcher::class);
|
||||
$config = $this->createMock(IConfig::class);
|
||||
$sharingBackend = $this->createMock(\OCA\DAV\CalDAV\Sharing\Backend::class);
|
||||
|
|
@ -78,6 +81,7 @@ class PublicCalendarRootTest extends TestCase {
|
|||
$config,
|
||||
$sharingBackend,
|
||||
$this->federatedCalendarMapper,
|
||||
$this->cacheFactory,
|
||||
false,
|
||||
);
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
|
|
|
|||
Loading…
Reference in a new issue