Problem/Motivation
The link manager service allows to set a specific link domain by calling the method 'ConfigurableLinkManagerInterface::setLinkDomain($domain)'.
On the other hand, the type link manager manages a cache of type links present in the system. Each type link is identified by a URI that is based on the link domain set at the moment that the cache is written.
If the link domain name changes by calling setLinkDomain, the link type cache remains as it was.
Steps to reproduce
- fresh install of 8.5-dev Drupal with standard profile
- enable the HAL module. Will ask to enable serialization as well by dependency. Also enable the 'rest' module needed as a new dependency, see [#1155816]
- download and enable the devel module
- try at /devel/php this working-well PHP snippet, that serializes and un-serializes a node:
$node = \Drupal\node\Entity\Node::create([ 'type' => 'page', 'title' => 'Test node', ]); $serialized_node = \Drupal::service('serializer')->serialize($node, 'hal_json'); $new_node = \Drupal::service('serializer')->deserialize($serialized_node, 'Drupal\node\Entity\Node', 'hal_json'); echo $new_node->get('title')->getString();
- at the same place, replace previous test code by this one that does the same but changing the link domain, it causes a "Type http://example.com/rest/type/node/page does not correspond to an entity on this site." exception:
\Drupal::service('hal.link_manager')->setLinkDomain('http://example.com'); $node = \Drupal\node\Entity\Node::create([ 'type' => 'page', 'title' => 'Test node', ]); $serialized_node = \Drupal::service('serializer')->serialize($node, 'hal_json'); $new_node = \Drupal::service('serializer')->deserialize($serialized_node, 'Drupal\node\Entity\Node', 'hal_json'); echo $new_node->get('title')->getString(); \Drupal::service('hal.link_manager')->setLinkDomain('');
Rebuilding the cache and retrying the last step will work, because the type links cache is write again with the set link domain.
Note that it only affects the de-serialization process, in serialization the last set link domain is always used.
Proposed resolution
Clear the type links cache when a new link domain is set.Write the type link by absolute URI (with no domain) and add it in getters.- Cache types per link domain
Remaining tasks
Notify about it in docs (until solved) to module developers that makes usage of serialization. Currently, any module that changes the link domain should clear the "hal:links:types" cache to avoid this issue and also prevent malfunctioning of any other module that works with HAL, since type links cache remains with unexpected URI.
API changes
Depending on how it is solved, the API could be affected.
Comment | File | Size | Author |
---|---|---|---|
#33 | 2928882_cache_multiple_link_domains_2.patch | 4.9 KB | mkalkbrenner |
Comments
Comment #2
manuel.adanComment #3
borisson_Adding related tags. It would be great if we add test for this as well.
Comment #6
neclimdulBreaks default_content #2928952: Invalidate the type links cache after altering the link domain
Test
Comment #7
neclimdulforgot to remove test tag.
Comment #8
Wim LeersComment #9
alexpottThis looks super vulnerable to cache thrashing ie. multiple clears of the cache on the same request. Should the cid have the link domain as part of it instead?
Comment #10
neclimdulI don't know about "super" because I think it generally just get called settings up services but I guess is could basically nullify the cache making it more or less just overhead if it gets called on every run. So much for the trivial solution I guess...
Comment #12
manuel.adanIt now invalidates the "hal:links:types" cache only if not empty and it was built with a different link domain. Patch also updated to match with the 8.8 branch.
Comment #13
neclimdulCouldn't apply the patch but the idea is interesting. Changed it up a bit and fixed the tests. Seems to mostly address Alex's concerns with out a seismic shift in the way we cache hal information.
To add a little extra thrash protection I compare the domain to the previous one as well. So if some code is just continuously setting the same domain we're protected.
Comment #14
yogeshmpawarComment #15
yogeshmpawarResolved one coding standard issue with an interdiff.
Comment #16
mikeryanI don't have a good enough understanding of HAL type links and caching to intelligently review the code - but, it fixed a problem for me where after a
drush site-install
with our custom entity and *slightly* customized REST endpoints, a PUT of our entity would fail with "Type https://example.ddev.site/rest/type/our_entity/our_entity does not correspond to an entity on this site" unless we did an extradrush cr
first. One thumb up.Comment #18
Berdirwouldn't it be easier to just make the link domain part of the cache key, aka making it a poormans-cache context? would just work then.
Also, The cacheability of the resulting page also doesn't correctly include the cache tag yet if the setting is currently empty.
Comment #19
ericdsd CreditAttribution: ericdsd commented#15 fixed my issue of import with default_content_deploy but but only using latest release (1.0-beta2 from april 15th) default_content_deploy 8.x-1.0-beta2
and including --uri parameter to my drush command to avoid this
using
instead works.
Tested on core 8.8.5
Comment #23
mkalkbrennerOne Drupal installation can serve multiple domains, protocols or ports. The current implementation in core regarding the cache is simply wrong. Also the approach in the proposed patch is wrong and is not robust against race conditions.
Here's a failing patch first.
Comment #24
mkalkbrennerI think berdir's proposal from #18 is the correct way to fix the issue without any race conditions or performance loss.
But as mentioned before, the current implementation must be considered to be broken. Multisite or multi-domain setups will fail.
Contrib modules like Default Content, Default Content Deploy, Domain Access and other are currently affected.
In core language negotiation by domain is incompatible for example.
Comment #25
mkalkbrennerRelations are also affected, not just types.
Comment #26
mkalkbrennerComment #27
mkalkbrennerAnother try of a failing test.
Comment #28
mkalkbrennerI missed to fetch the cached values in the previous tries of a failing patch.
Comment #30
mkalkbrennerNow the patch to solve the issue.
Comment #31
Berdirchanging the method just so we don't have to call getLinkDomain() a second time doesn't really seem worth it?
Comment #32
mkalkbrennerI checked the sources and didn't find any other calls of this protected method. But I don't care. If you'll set RTBC I'll change it to be be 100% backwards compatible.
BTW I run into this issue because of default_content_deploy.
Comment #33
mkalkbrennerComment #34
BerdirPretty sure it's much more likely to be committed without that change. We try to avoid changing protected methods, keep deprecated protected methods around and so on. There are known subclasses of this, it's theoretically possible that a custom project overrides or calls it in a subclass?
Patch looks good to me, we will wait for testbot to confirm.
Comment #35
mkalkbrenner@berdir RTBC?
Comment #36
BerdirYeah, I think this is RTBC. fixes the problem with minimal changes and has test coverage.
Comment #37
catchShould this get a cache tag for hal.settings?
https://api.drupal.org/api/drupal/core%21modules%21hal%21src%21LinkManag...
Also doesn't look like there's test coverage for the link domain being changed in config.
Comment #38
mkalkbrennerI don't think so. The cache must not be invalidated when
link_domain
in hal.settings changes. This was an issue before this patch!Now the cache entry can remain regardless that the link domain changed and regardless how it changed, via settings or via API calls at runtime.
Comment #39
mkalkbrennerIf you're argument is to save cache space when the setting changes, then we can add the tag.
Comment #40
BerdirCache tags don't clear space. And yes, the configured url is basically a cache context now with this patch.
Comment #42
catchOh good point didn't think it all the way through, so yeah when the settings are changed the cache key changes and everything will work fine.
Committed 7dabb04 and pushed to 9.3.x. Also cherry-picked to 9.2.x. Thanks!