-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add cache for the discovery loading #89
base: main
Are you sure you want to change the base?
Changes from all commits
c3109a7
d38279b
f85c412
1dca4a5
1a80352
6cec6ae
f8fa3c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<?php | ||
|
||
/* | ||
* Copyright the Collabora Online contributors. | ||
* | ||
* SPDX-License-Identifier: MPL-2.0 | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Drupal\collabora_online\Discovery; | ||
|
||
use Drupal\Component\Datetime\TimeInterface; | ||
use Drupal\Core\Cache\Cache; | ||
use Drupal\Core\Cache\CacheableMetadata; | ||
use Drupal\Core\Cache\CacheBackendInterface; | ||
use Drupal\Core\Cache\RefinableCacheableDependencyInterface; | ||
use Symfony\Component\DependencyInjection\Attribute\Autowire; | ||
use Symfony\Component\DependencyInjection\Attribute\AutowireDecorated; | ||
|
||
/** | ||
* Service to load the discovery.xml from the Collabora server. | ||
*/ | ||
class CachingDiscoveryFetcher implements CollaboraDiscoveryFetcherInterface { | ||
|
||
protected const DEFAULT_CID = 'collabora_online.discovery'; | ||
|
||
public function __construct( | ||
#[AutowireDecorated] | ||
protected readonly CollaboraDiscoveryFetcherInterface $decorated, | ||
#[Autowire(service: 'cache.default')] | ||
protected readonly CacheBackendInterface $cache, | ||
protected readonly TimeInterface $time, | ||
protected readonly string $cid = self::DEFAULT_CID, | ||
) {} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getDiscoveryXml(RefinableCacheableDependencyInterface $cacheability): string { | ||
$cached = $this->cache->get($this->cid); | ||
if ($cached) { | ||
$cacheability->addCacheTags($cached->tags); | ||
$expire = $cached->expire; | ||
$max_age = ($expire === Cache::PERMANENT) | ||
? Cache::PERMANENT | ||
: $expire - $this->time->getRequestTime(); | ||
$cacheability->mergeCacheMaxAge($max_age); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @brummbar While you are reviewing this: |
||
return $cached->data; | ||
} | ||
// In theory, the $cacheability could already contain unrelated cache | ||
// metadata when this method is called. We need to make sure that these do | ||
// not leak into the cache. | ||
$local_cacheability = new CacheableMetadata(); | ||
$xml = $this->decorated->getDiscoveryXml($local_cacheability); | ||
$max_age = $local_cacheability->getCacheMaxAge(); | ||
|
||
$cacheability->addCacheableDependency($local_cacheability); | ||
|
||
/* @see \Drupal\Core\Cache\VariationCache::maxAgeToExpire() */ | ||
$expire = ($max_age === Cache::PERMANENT) | ||
? Cache::PERMANENT | ||
: $max_age + $this->time->getRequestTime(); | ||
$this->cache->set( | ||
$this->cid, | ||
$xml, | ||
$expire, | ||
$local_cacheability->getCacheTags(), | ||
); | ||
return $xml; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,35 +12,38 @@ | |
|
||
declare(strict_types=1); | ||
|
||
namespace Drupal\collabora_online\Cool; | ||
namespace Drupal\collabora_online\Discovery; | ||
|
||
use Drupal\collabora_online\Exception\CollaboraNotAvailableException; | ||
use Drupal\Component\Datetime\TimeInterface; | ||
use Drupal\Core\Cache\Cache; | ||
use Drupal\Core\Cache\CacheableMetadata; | ||
use Drupal\Core\Cache\MemoryCache\MemoryCacheInterface; | ||
use Symfony\Component\ErrorHandler\ErrorHandler; | ||
|
||
/** | ||
* Service to get values from the discovery.xml. | ||
*/ | ||
class CollaboraDiscovery implements CollaboraDiscoveryInterface { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the end this service is a value object. Probably a factory would help in retrieving either a fresh or a cached instance of it, otherwise it gets cached in the service container and doesn't get really invalidated. |
||
|
||
/** | ||
* Constructor. | ||
* | ||
* @param \Drupal\collabora_online\Cool\CollaboraDiscoveryFetcherInterface $discoveryFetcher | ||
* Service to load the discovery.xml from the Collabora server. | ||
*/ | ||
protected const DEFAULT_CID = 'collabora_online.discovery.parsed'; | ||
|
||
public function __construct( | ||
protected readonly CollaboraDiscoveryFetcherInterface $discoveryFetcher, | ||
protected readonly MemoryCacheInterface $cache, | ||
protected readonly TimeInterface $time, | ||
protected readonly string $cid = self::DEFAULT_CID, | ||
) {} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getWopiClientURL(string $mimetype = 'text/plain'): string { | ||
public function getWopiClientURL(string $mimetype = 'text/plain'): ?string { | ||
$discovery_parsed = $this->getParsedXml(); | ||
|
||
$result = $discovery_parsed->xpath(sprintf('/wopi-discovery/net-zone/app[@name=\'%s\']/action', $mimetype)); | ||
if (empty($result[0]['urlsrc'][0])) { | ||
throw new CollaboraNotAvailableException('The requested mime type is not handled.'); | ||
return NULL; | ||
} | ||
|
||
return (string) $result[0]['urlsrc'][0]; | ||
|
@@ -74,8 +77,26 @@ public function getProofKeyOld(): ?string { | |
* Fetching the discovery.xml failed, or the result is not valid xml. | ||
*/ | ||
protected function getParsedXml(): \SimpleXMLElement { | ||
$xml = $this->discoveryFetcher->getDiscoveryXml(); | ||
return $this->parseXml($xml); | ||
$cached = $this->cache->get($this->cid); | ||
if ($cached) { | ||
return $cached->data; | ||
} | ||
$cacheability = new CacheableMetadata(); | ||
$xml = $this->discoveryFetcher->getDiscoveryXml($cacheability); | ||
$parsed_xml = $this->parseXml($xml); | ||
|
||
$max_age = $cacheability->getCacheMaxAge(); | ||
/* @see \Drupal\Core\Cache\VariationCache::maxAgeToExpire() */ | ||
$expire = ($max_age === Cache::PERMANENT) | ||
? Cache::PERMANENT | ||
: $max_age + $this->time->getRequestTime(); | ||
$this->cache->set( | ||
$this->cid, | ||
$parsed_xml, | ||
$expire, | ||
$cacheability->getCacheTags(), | ||
); | ||
return $parsed_xml; | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,9 @@ | |
|
||
declare(strict_types=1); | ||
|
||
namespace Drupal\collabora_online\Cool; | ||
namespace Drupal\collabora_online\Discovery; | ||
|
||
use Drupal\Core\Cache\RefinableCacheableDependencyInterface; | ||
|
||
/** | ||
* Service to load the discovery.xml from the Collabora server. | ||
|
@@ -22,12 +24,15 @@ interface CollaboraDiscoveryFetcherInterface { | |
/** | ||
* Gets the contents of discovery.xml from the Collabora server. | ||
* | ||
* @param \Drupal\Core\Cache\RefinableCacheableDependencyInterface $cacheability | ||
* Mutable object to collect cache metadata. | ||
* | ||
* @return string | ||
* The full contents of discovery.xml. | ||
* | ||
* @throws \Drupal\collabora_online\Exception\CollaboraNotAvailableException | ||
* The client url cannot be retrieved. | ||
*/ | ||
public function getDiscoveryXml(): string; | ||
public function getDiscoveryXml(RefinableCacheableDependencyInterface $cacheability): string; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are forced to add this because we are separating the cache into a different class. But it feels unneeded. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Btw I don't see ResourceFetcher adding any cache tags to the cache entry. |
||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add a configuration value that determines for how long the xml is cached. We should put a standard value of 5 minutes, which sounds quite sensible.
See https://git.drupalcode.org/project/media_avportal/-/blob/8.x-1.x/src/AvPortalClient.php?ref_type=heads#L109 for a similar approach .