Updated: Comment #0
Problem/Motivation
Cache handling is split between the AliasManager and AliasManagerCacheDecorator and the AliasManagerInterface and CacheDecoratorInterface.
The current caching has various bugs and as a result, does not work at all. Multiple languages are not properly supported, paths without aliases aren't cached. With the current API's, it is not possible to fix that.
Proposed resolution
Merge the two classes together, handle caching internally.
Remaining tasks
Needs reviews.
User interface changes
API changes
- AliasManagerCacheDecorator and the corresponding service is removed.
Comments
Comment #1
visabhishek commentedComment #2
visabhishek commentedComment #3
slashrsm commentedIt turns out that AliasManager becomes just a simple wrapper around AliasStorage if we move all cache-related code out of it. It makes more sense to merge AliasManager and AliasManagerCacheDecorator.
Comment #4
dawehner+1 as you can easily forget to use the cached version.
It is a bit odd that writeCache is part of the public API but yeah this is not the fault of the patch itself.
If we change this line we can use @param string directly. In general this could be seen as somehow out of scope.
Comment #5
slashrsm commentedwriteCache() is called from outside in this case (from \Drupal\Core\EventSubscriber\PathSubscriber).
It could, but as part of the parent issue we also try to standardize terminology. There was no specific ticket to do that, it was do as part of other tickets. I am more than happy to move this to a separate ticket if desired so.
Comment #6
dawehnerMy comment sounded maybe a bit pessimistic, but I really like this refactoring.
Comment #7
berdirNoticed during profiling that something is going wrong right now with alias caching and languages.
Looks like the AliasManager right now can only write back caches of a single language, the first in the lookup map?
In my case, I was importing nodes with langcode not specificed (und), they now explicitly pass along the language context. The result is that they remain uncached. I was displayed 100 of them in a view and I have 100 queries due to that right now.
And even after changing them to en, caching still doesn't seem to be working, because it never ends up in the cache... this seems to be because there are no aliases, so they're added to noAlias, which isn't added to the cache.
This might not be the correct issue, but with all the refactoring, we could possibly fix at least some of the issues here? What about caching the alias hits and misses separately in the cache, so that we don't have to attempt to load the alias for them? Why are we not caching the aliases btw, should be possible to do that by language?
That said, merging this seems to make a lot of sense and it can go even further, the cache write method still uses getPathLookups(), which is a public method on the interface, the only reason for that seems to be that the cache wrapper can do the caching? The method is very limited, because it is not possible to solve the problems above while still using it as it has no understanding of language and if an alias existed. So we can probably just remove that and use $this->lookupMap and $this->noAlias directly in writeCache() ?
Comment #8
slashrsm commentedYah... one thing that is definitely broken ATM is this (wrong CID being used to load from cache - $path instead of $this->cacheKey). This should be fixed with this patch.
I was asking myself two cache-related questions when working on this:
1. Why we cache only paths (as also mentioned by @berdir). It seems that we could cache entire path-alias mapping instead of preloading it fro DB.
2. Why are we preloading only in getAliasByPath()? Wouldn't also getPathByAlias() benefit from preloaded mappings? - I suspect that we want to prevent preloading SQL queries in case only one "path from alias" is needed (for current url/page).
Comment #9
catchCaching only the paths means that it's not necessary to invalidate the cache when path aliases are added/updated. That decision pre-dates having cache tags available so we could maybe look at that again.
getAliasByPath() is needed for URL generation, getPathByAlias() is needed only for the incoming request - so there's no real saving on that side.
Haven't reviewed the latest patch yet.
Comment #10
catchAlso looks like this needs more discussion, or a follow-up.
Comment #11
catchDiscussed with berdir in irc, let's fix the caching here. This is completely broken atm so bumping to critical.
Comment #12
Anonymous (not verified) commentedyes please to fixing caching.
over at #2231595: Add a cache backend that checks an inconsistent cache, then falls back to a consistent cache backend, any needless cache writes show up pretty much straight away, and the path system sure does love it some silly writes.
Comment #13
berdirGoing to try and work a bit on this.
Comment #14
xjm(Title needs a correction too)
Comment #15
xjmComment #16
berdirOk, this should be a much better and has a ton of unit test coverage :)
Relevant changes:
- cacheNeedsWriting defaults to FALSE, no need to set it to FALSE then when loading from cache.
- Killed getPathLookups(), it is useless now (it was only necessary when the caching was in the decorator) and it was way too limit for the other improvements
- path lookup cache is now keyed by language and also includes noAlias misses.. the logic was there in preload but they were not cached :(
Unit tests can possibly be refactored a bit, I focussed on covering all scenarios that I could think of and not to optimize lines of code ;)
Did also manual testing of a page of nodes in 3 different languages, everything went through preload, with and without aliases.
I think we can look into caching actual aliases in a follow-up because that will then be an internal implementation detail.
What I still don't like is that whole CacheDecoratorInterface business: It's a weird split of methods, writeCache() is on decorator, clearCache() on aliasManager and has very generic/misleading method names but I can possibly live with it. Don't really see the point in that, but so be it.
Generic method names: setCacheKey() is actually the path, and this should be more like a cache key suffix, because there's no longer a cache_path bin that is only used for this, but the generic cache_data bin. This means that you could access site/some-existing-other-cid and that would then seriously mess up things :) Another bug that needs to be fixed.
What I didn't change is that preloading happens in getPathFromAlias(), the reason is that otherwise we can't expect that the cache key is set as that happens at the same time (Which is *not* checked and another example while the generic naming is problematic).
Comment #17
Anonymous (not verified) commentedplayed with this patch a bit, one thing i noticed:
so the call to pathProcessor->processInbound() ends up in the AliasManager code, and runs a cache get before the cache key is set. that seems bad because of this:
the cache get will fail (because no cacheKey), and we'll always end up with an empty preload list?
Comment #18
berdirYeah, that needs to change, not sure why it worked for me. I'm actually wondering now if I tested it before I moved it back into getPathFromAlias() ;)
Not sure how exactly we want to do that? Move it back and make it conditional on having a cache Key? That would also avoid loading the cache in case no aliases URL's are displayed?
Comment #19
berdirCan you check this? also fixed the cache key prefix problem.
Comment #21
Anonymous (not verified) commentedyep, #19 fixes the issue i mentioned in #17, nice work :-)
Comment #22
berdirThis should fix the test fails.
Comment #23
berdirComment #24
Anonymous (not verified) commentedyay, this is looking good. a couple of small nits, otherwise i think this is RTBC:
s/laias/alias/
maybe just use a descriptive variable name instead:
Comment #25
slashrsm commentedGreat job! Thank you!
This last two nitbits fixed.
Comment #26
berdirIf you want to be nitpicky, then you could just put that inline now, a separate variable for that seems unnecessary and not what we usually do?
Comment #27
slashrsm commentedHonestly, I don't care how we do it. Until it is clear what those numbers mean I'm fine with it. Another possibility would be to create a constant on class.
Comment #28
Anonymous (not verified) commentedyep, me either. berdir - please reroll with whatever you want for that block.
Comment #29
berdirWhat makes you think I do? (Just care about this getting in) :)
Here's a patch with my suggestion, feel free to RTBC whatever you prefer ;)
Comment #30
berdirActually, you have to RTBC #29 or re-roll the previous patch, because it didn't apply anymore :p
Comment #31
Anonymous (not verified) commentedyay.
Comment #32
catchCouldn't se do the $this->cacheNeedsWritingCheck at the start of the method? No point building the array otherwise.
Looks good apart from this.
Comment #33
slashrsm commentedFixed.
Comment #34
catchComment #35
xjmReroll for #2247991: [May 27] Move all module code from …/lib/Drupal/… to …/src/… for PSR-4.
Comment #36
catchCommitted/pushed to 8.x, thanks!
Comment #38
wim leersYay, thanks for this!
Comment #39
berdirUrks, what's up with those variable names...
Wow, why did my unit tests not catch this?
Double-Urks.
:)
Comment #40
alexpottConfirmed the tests catch the bug
Comment #41
catchExtra tests look great, not sure how we wall missed $twenty_four_hours_four_hours.
Committed/pushed to 8.x, thanks!
Comment #43
effulgentsia commentedFrom the #35 patch committed in #36:
This does not look like a no-op. Looks to me like this meant that prior to this patch, the decorator's getAliasByPath() didn't trigger a new cache write, but after this patch, we started triggering that. Patch in comment #3 also has this hunk.
#2582219: Preload Cache in AliasManager can get huge is now trying to revert that, so if anyone here has feedback about that, please comment on that issue.