Problem/Motivation
This is a follow-up for #1888424: Make Drupal's URL generation logic available to HttpKernel, and minimize code repetition/divergence
Part of this is postponed on #2351015: URL generation does not bubble cache contexts
Now that we have all link generation centralized into one of two methods on the generator, we need to make it faster. The obvious way to do that is to cache requests to generateFromRoute() and generateFromPath(), so that we catch all of the work done by the outbound path processors.
Proposed resolution
For path alias lookups, we did that with a wrapping object. I think it makes sense to do the same here, with an object that has the same interfaces as the generator but simply forwards requests to it and then caches them. The pattern established by the path alias cache should work fine here. (I think it's now using the Destructable interface? If not, we should do that.)
We may also want to consider removing the cache wrapper from path alias lookups once we're done. We probably shouldn't do that in this patch, but a follow up would be to benchmark and see if it's still useful, and if not, get rid of it to avoid the complexity and extra cache storage.
Remaining tasks
Task | Novice task? | Contributor instructions | Complete? |
---|---|---|---|
Update the issue summary | Instructions | ||
Add automated tests | Instructions | ||
performance profiling |
User interface changes
API changes
Comment | File | Size | Author |
---|---|---|---|
#256 | interdiff.txt | 11.16 KB | rteijeiro |
#256 | 1965074-256.patch | 34.67 KB | rteijeiro |
#242 | interdiff.txt | 486 bytes | dawehner |
#242 | 1965074-242.patch | 34.72 KB | dawehner |
#240 | interdiff.txt | 3.75 KB | dawehner |
Comments
Comment #1
katbailey CreditAttribution: katbailey commentedI've made a bit of a start on this, will try to get a patch up at the weekend.
Comment #2
BerdirCan we also add a Drupal::generator() or something like that method here while we're at it.
And, let's not do something and something.cached as service names, because people end up using the wrong service. Maybe something and something.uncached , maybe the uncached one can even be a private service?
Comment #3
Crell CreditAttribution: Crell commentedDrupal::urlGenerator() was already added in the previous issue.
Comment #4
katbailey CreditAttribution: katbailey commentedUgh, sorry - I have been very distracted lately. Here are the beginnings of a patch - it totally breaks the installer and I haven't yet figured out what exactly is going wrong. Hardly seems worth setting to needs review, though I wouldn't mind getting some eyes on it to see if I'm at least going in the right direction with this as I'm not totally sure I've understood what's required.
Comment #5
Crell CreditAttribution: Crell commentedI don't know why that would be breaking the installer, other than "it's the installer *cries*". It does look like what I expected it to look, though, so it's on the right track if nothing else. (Or I'm completely wrong, which is always a possibility.)
In my experience, this sometimes works out well and sometimes causes all sorts of insanity. What methods is this useful for, or is it "just in case"?
Comment #6
dawehnerLet's remove that and implement the single method which is needed (getPathFromRoute).
Found one bug while writing some tests: You also have to add the parameters to the key.
Comment #8
dawehnerThis fixes a couple of the failures.
Comment #10
dawehnerLet's at least remove the old test code from urlgeneratorTest.
I have a feeling that at least some of the remaining test failures are caused by to strict caching. For example we don't take the language into account, but just putting it into the cacheKey did not helped.
Comment #12
dawehnerLet's move it out #2057215: PathBasedGeneratorInterface should extend the base ones, so the proper generate method can also be used
Comment #13
katbailey CreditAttribution: katbailey commentedUnassigning myself from some issues as I have very little time for core these days as I prepare to move 5000kms across the country...
Comment #14
dawehner#10: drupal-1965074-10.patch queued for re-testing.
Comment #16
pwolanin CreditAttribution: pwolanin commentedAlso needs work since PathBasedGeneratorInterface has been replaced.
Comment #17
dawehnerRerolled and added tests for generateFromRoute.
Comment #19
dawehnerI can't fix the language based failures now.
Comment #21
pwolanin CreditAttribution: pwolanin commentedSo, I see a couple bugs in there ('system_path' -> '_system_path', and the wrong prefix for some entries) . Added a pass at language handling and simplified the code a bit.
Also, why don't we consider moving the in-memory caching and the clear() method to the Drupal UrlGenerator and interface? Though at that point inheritance might make more sense than decorating.
Comment #23
pwolanin CreditAttribution: pwolanin commented#21: cache-url_generator-1965074-21.patch queued for re-testing.
Comment #25
dawehnerWe are testing the generate method, so we should call the generate method.
Comment #26
pwolanin CreditAttribution: pwolanin commenteddawehner - in our implementation that basically delegates to generateFromRoute, and I inlined that delegation in this class now. That mean you could cache once calls to generate() or generateFromRoute(). Actually to do that right, we should actually not set $options unless $absolute is TRUE.
Comment #27
pwolanin CreditAttribution: pwolanin commentedputs the test mostly back, and add comments and a small change to generate(), fixes use of Languge
Comment #29
pwolanin CreditAttribution: pwolanin commentedfix language stuff mostly
Comment #30
pwolanin CreditAttribution: pwolanin commentedfix doxygen.
Comment #31
pwolanin CreditAttribution: pwolanin commentedshould fix the last test
Comment #32
dawehnerLet's try to bring down the patch size and introduce a CachedUrlGeneratorInterface similar to CachedModuleHandler and CachedPathAliasManager etc.
Comment #33
pwolanin CreditAttribution: pwolanin commentedMissing file for CachedUrlGeneratorInterface ?
Comment #35
pwolanin CreditAttribution: pwolanin commentedcan't find dawehner, so adding the missing interface (not hard to guess).
Comment #36
mradcliffeI just reviewed the interdiff. The interface contains understandable basic documentation.
additioanl should be additional
Comment #37
pwolanin CreditAttribution: pwolanin commentedthanks.
Comment #38
pwolanin CreditAttribution: pwolanin commentedupdating title
Comment #39
pwolanin CreditAttribution: pwolanin commented#37: url_generator-1965074-37.patch queued for re-testing.
Comment #40
dawehner#37: url_generator-1965074-37.patch queued for re-testing.
Comment #42
dawehnerreroll.
Comment #43
pwolanin CreditAttribution: pwolanin commentedI think this needs work in 1 of 2 directions.
A quick fix would be to cache just in memory, so we avoid repeating the work if the same link is generated more than once per page (not sure this is even worth doing).
Otherwise, we need to maybe put the cache set into a destructor so we can accumulate all the generated routes for a page?
Comment #44
pwolanin CreditAttribution: pwolanin commentedFrom Berdir here, we can implement Drupal\Core\DestructableInterface - core will cal the destruct() method on these when shutting down the container so that's the opportunity to persist the data.
Comment #45
dawehnerWe could potentially move somehow the storage logic of AliasManagerCacheDecorator into here.
Comment #46
damiankloip CreditAttribution: damiankloip commentedWhat happens here when we have dynamic query string parameters etc... like tokens?
Comment #46.0
damiankloip CreditAttribution: damiankloip commentedUpdated issue summary.
Comment #47
martin107 CreditAttribution: martin107 commentedThis issue is no longer blocked! This is just a reroll.
Last patch is from September 20, 2013 which in HEAD years this makes it an aged pensioner...
So no promises except it installs, UrlGenerator test pass locally .. lets see what testbot has to say
Comment #48
martin107 CreditAttribution: martin107 commentedComment #50
martin107 CreditAttribution: martin107 commented1) My reroll in #47 mangled the service definition of url_generator in core.services.yml.
2) Relaxed __construct of MenuLinkFormController to permit any object conforming to UrlGeneratorInterface
so we can feed it a caching version.
3) minor tidy now using the appropriate getter in CachedUrlGenerator.php
Comment #51
martin107 CreditAttribution: martin107 commentedbutter fingers
Comment #53
martin107 CreditAttribution: martin107 commentedjust looking at the commit log 7 commits between submission of patch and testing of patch
good o'le sprint time at NYC camp... will try again shortly
Comment #54
martin107 CreditAttribution: martin107 commentedBrowser install worked locally for me at #51, works for me now after commit sprint .. retesting
Comment #55
martin107 CreditAttribution: martin107 commented51: url_generator-1965074-50.patch queued for re-testing.
Comment #57
martin107 CreditAttribution: martin107 commentedMore careful inspection of logs shows I need to backout "minor tidy" #50.3.
Comment #58
martin107 CreditAttribution: martin107 commentedComment #60
martin107 CreditAttribution: martin107 commentedTrivial reroll + fixed a single issue.
Constructor of CachedUrlGenerator now takes LanguageManagerInterface
Comment #62
martin107 CreditAttribution: martin107 commentedRecent patches reintroduced assertions that HEAD removed ...
LanguageListTest should pass now this has been resolved.
I am still trying to track down those pesky exceptions ... they all seem to have a common form.
Comment #64
tstoecklerI think it would make sense to call this "url_generator.cached" and then have "url_generator" simply be an alias for the former. That makes it easier to swap from cached to uncached.
Comment #65
martin107 CreditAttribution: martin107 commented#64 .. This is how it looks
looking at the other uses of alias in HEAD, and looking for conformity. The obvious set of names would be
url_generator
url_generator.cached,
url_generator.active
but I am happy to go with tstoeckler's suggestion as that way we hide a implementation details away from other
devs.
Comment #66
martin107 CreditAttribution: martin107 commentedStill hunting those 36 exceptions ... but in the mean time I have found 2 nitpicks.
Comment #68
martin107 CreditAttribution: martin107 commentedThat blew up because I did not subject #66 to the testbot..... and there was a problem with my aliasing.
I retried to get the alias to work but failed
using http://symfony.com/doc/current/components/dependency_injection/advanced.... as a reference I tried the alternate form of
but they introduced a different class of problem..
Unnervingly I tried swapping the order of url_generator and url_generator.uncached service definition in core.services.yml which if I understand the multiple pass dependency resolution nature of setting up services should make no difference.. and yet the error messages changed. [ and yes I was clearing the cache :-) ]
I tried for extra protection making the url_generator.uncached. private, so no application code can make use of it but no success..
So in the end while muttering under my breath about alpha level software I concluded that all this was out of the scope of this issues and backout the alias changes in
#65
PS making 'url_generator.uncached' private seems sensible.. so that remained.
Comment #70
martin107 CreditAttribution: martin107 commentedRemoved some exceptions.
Comment #72
martin107 CreditAttribution: martin107 commentedPatch no longer applied. Reroll.
Comment #74
martin107 CreditAttribution: martin107 commentedPatch no longer applies - reroll.
Nothing but merging and auto-merging.
Comment #76
martin107 CreditAttribution: martin107 commentedReroll.
Comment #78
martin107 CreditAttribution: martin107 commentedLooking at git history ... there was a revert between submission and testing .. which causes a conflict ....
so the reroll lasted about 30mins
This is the second revert today .. will move this forward tomorrow when there is more stability
Comment #79
martin107 CreditAttribution: martin107 commentedReroll of #74 just to be sure.
Just keeping notes for myself, so I don't forget, things to correct when reroll is better ...
trival fix for CachedUrlGenerator.php
implementation and likelihood need a spelling correction
( convert to psr-4 has just landed. )
Comment #80
martin107 CreditAttribution: martin107 commentedComment #83
martin107 CreditAttribution: martin107 commentedHmm traded the 3 identical exceptions exceptions in #74
For 96 exceptions today in #79.. So still the one issue to understand. :-)
Comment #84
martin107 CreditAttribution: martin107 commentedReroll.
Comment #86
martin107 CreditAttribution: martin107 commentedStraight reroll.
Comment #88
BerdirThis fixes those errors.
But the fact that we get that far is scary, because it means $options is getting passed entities which have plugins bags, so even with this, we are serializating a lot of stuff, which makes building the cache key slow.
Also pretty sure this isn't working at all right now, DestructableInterface needs the corresponding needs_destruction service tag to do anything which means we never write the cache from what I can see. Unit tests are nice but this needs an integration test to make sure that we actually write something into the cache after a request. Also, cache stampedes could be an issue..
Comment #89
pwolanin CreditAttribution: pwolanin commentedSo, I'm wondering how much of this patch is overkill?
It seems a lot of the slowness is in basically generating the internal (system) path from the route name and route parameters.
Can we just cache that piece of it and let the rest of the generator do its work?
Basically in generateFromRoute(), caching the result of these 3 method calls:
Comment #90
effulgentsia CreditAttribution: effulgentsia commentedCorrect. Even if you add the tag, it still doesn't work, because writeCache() only writes if there's a $this->cacheKey, which only gets set in setRequest(). If you try to make setRequest() called by adding a
to the 'url_generator' service definition, it still doesn't work, because that gets called prior to _system_path getting populated.
Comment #91
effulgentsia CreditAttribution: effulgentsia commentedAlso, related to #89, not caching this on its own (whether in addition to or instead of the full URLs) is unfortunate, because LinkGenerator calls $url->getInternalPath() which calls this, so this isn't speeding up link generation as much as it could be. Although LinkGenerator does have a @todo to remove that, that hasn't been done yet, so pending that being done, I think we should include getPathFromRoute() in what gets cached by this issue.
Comment #92
pwolanin CreditAttribution: pwolanin commentedSo, I'm a little confused about why we are generating the cache key off the system path.
Looking at core.services.yaml we currently have that:
Among other things, we shouldn't be using _system_path here (or anyplace) if we can avoid it. The problems with it or the route and parameters not being populated could instead be solved by loading the cache entry when the first generate call is made?
As above I think we might be better off just caching the part of the work that doesn't depend on the current request.
That call to the UrlGenerator should be removed ASAP, so let's not expand the scope of this issue for something that's deprecated.
Comment #93
Wim LeersComing from #2335661: Outbound path & route processors must specify cacheability metadata, which led me to the issue that introduced route processors (#1798296: Integrate CSRF link token directly into routing system). Which led me to this issue and to #1798296-80: Integrate CSRF link token directly into routing system, which said:
This patch does not yet take into account the cacheability implications of route processors and path processors. Therefore it would currently happily cache links with CSRF tokens.
Comment #94
Fabianx CreditAttribution: Fabianx commentedOptimizing this was a _requirement_ to get the link generator in as per:
- https://www.drupal.org/node/2047619#comment-7801557
AND
- https://www.drupal.org/node/2047619#comment-7813067
There was a 50% performance regression (!) at time this got in for route generation.
I can understand why chx' asks for a roll back of the router with the way this was added.
This is very poor process just to get an agenda done.
Comment #95
Fabianx CreditAttribution: Fabianx commentedComment #96
dawehnerWe could something similar to path aliases and preload all routes per route, this would already improve a bit here. Something similar is though already done for menu links, so we don't
load most of the routes in non-parallel code.
Is there anything we can do to improve the actual performance of generating the URL (and not having to go back to URLs again, as the amount of work here would be notestimatable).
Comment #97
catch@dawehner didn't you implement that already in #2058845: Pre-load non-admin routes? Have we regressed again since?
Comment #98
Fabianx CreditAttribution: Fabianx commentedAll I am saying is: This needs to go in before core is released as it was stated this would be ready soon to justify the LinkGenerator going in immediately.
It seems berdir is close though, so this should be a non-issue.
We originally introduced a 50% performance regression against l(), so with caching we should be seeing only 5-20% performance regression or such.
Comment #101
catchI agree this is independently critical of #1744302: [meta] Resolve known performance regressions in Drupal 8.
There is 'performance is critically bad overall', and then there is 'critical performance issue', and this is the latter.
Also for the record I said exactly the same thing before the original routing patch was even committed #1793520-7: Add access control mechanism for new router system but actually enforcing that this got dealt with has slipped through the cracks until now. Thanks Fabianx for re-reviewing some of these issues and re-identifying the blockers.
Comment #102
dawehner,
Comment #103
kim.pepperThis is a fairly naive re-roll of #88 to keep the issue moving.
Comment #105
cilefen CreditAttribution: cilefen commentedComment #106
cilefen CreditAttribution: cilefen commentedComment #108
cilefen CreditAttribution: cilefen commentedSomehow
use DependencySerializationTrait;
got in there twice.Comment #110
dawehnerRelated issue
Comment #111
dawehnerI think we have to ensure that some generated URLs aren't cacheable. You can also potentially cleanup a bunch of stuff.
Comment #113
undertext CreditAttribution: undertext commentedComment #114
undertext CreditAttribution: undertext commentedHere is a patch with interdiff.
- fixing PHP error in CachedURLGenerator class (conflict in namespaces)
- fixing unit test for that class
Comment #116
undertext CreditAttribution: undertext commentedOh. Sorry. This one. The interdiff is the same.
Comment #118
undertext CreditAttribution: undertext commentedComment #120
undertext CreditAttribution: undertext commentedUhh. 9 fails, will try to identify the problem and fix them...
Comment #121
martin107 CreditAttribution: martin107 commentedThis patch contains a new class CachedUrlGeneratorTest
Comment #122
martin107 CreditAttribution: martin107 commentedA couple of observations .... tomorrow night I may fix..
CachedUrlGenerator::generateFromRoute() - first line
This maybe OK, but it is out of step with the annotations in UrlGeneratorInterface
inherited via
Which require $name to be a string. Now I want to highlight the mechanism of how this interface change breaks the code....
I am still mulling over the fix.
$names which are not guaranteed to be strings enter RouteProvider::getRoutesByNames($names) as an array
and fall over at the the first function which requires them to be strings ( highlighted with an *)
Comment #123
dawehnerI'll have a look at it forawhile, if you are okay with it
Comment #124
dawehnerThis could fix it.
Comment #126
undertext CreditAttribution: undertext commentedNice.
DownloadTest fails because it checks generated url of private file both when clean urls are on and off, and it always returns cached url for version with clean urls enabled.
(line 122 in the file)
Comment #127
undertext CreditAttribution: undertext commentedOne more thing.
As Berdir mentioned in #88 - "DestructableInterface needs the corresponding needs_destruction service tag to do anything which means we never write the cache from what I can see."
So for now we do not write anything into cache, and this is not handled by added tests.
Also "setRequest" method never called for CachedUrlGenerator service because setter injection is not defined for it in core.services.yml.
Comment #128
undertext CreditAttribution: undertext commentedComment #129
undertext CreditAttribution: undertext commentedComment #133
martin107 CreditAttribution: martin107 commentedA little fix-up.
Comment #135
martin107 CreditAttribution: martin107 commentedFirst off Drupal\block\Tests\BlockTest takes a long time to rerun. So here is a short cut
At full stack trace can easily be obtained by visiting, these 2 URLs in sequence.
/user ( correct response a 302 to redirect )
/USER ( correct response a 404 to indicate not found )
With the patch applied the visit to /USER will trigger and error.
What this highlights is that cache keys are case-sensitive.
Currently CachedUrlGenerator::fromRequestStack in response to each URL generate cache keys of "user" and "USER"
The problem is they are being fed into a CASE-INSENSTIVE SQL query in DatabaseBackend::getMultiple(0
So that is where I am tonight ... more maybe tomorrow:)
Comment #136
martin107 CreditAttribution: martin107 commentedThis is a one line fix to resolve the case sensitivity of cache keys
It is not without performance implications though
Also the issue is picked up ONLY in a unrelated test, which is subject to change in the future without thought for caching
.. so next I want to add more tests in CachedUrlGeneratorTest.
Comment #138
martin107 CreditAttribution: martin107 commentedupdates involving Symfony 2.6 went in today
changes affecting UrlGeneration - retesting out of a sense of paranoia.
Comment #141
martin107 CreditAttribution: martin107 commentedA) CachedUrlGenerator::fromRequestStack() is not a descriptive function name. I see it follows the naming convention established in router.request_context, but I don't think that is a good enough reason to keep it.
The function selects/loads the appropriate cache of URLs required to service the current Request. I have changed to name to prepareCache() - naming things is hard, so I am open to suggestions... just something more descriptive please.
I see this as one of the key functions to focus on when analysing the design decisions good or bad ... so I have added some comments.
B) There are comments above about the costliness of generating the cacheKeys. While this an infrequently run task can someone justify the use of a sha256 hash? Given the use is for fast lookup rather than a cryptographic function. Additionally do we need a 128 bit digest? How many cache elements are expected?
From cuting and pasting from http://php.net/manual/en/function.hash.php
Milage may vary, but as a guide only .... here is the time in microseconds to calculate the various hashes ( see web page for full details )
1. md4 5307.912
2. md5 6890.058
3. crc32b 7298.946
4. crc32 7561.922
5. sha1 8886.098
...
29. sha256 19020.08
So in a non-rigorous handwavey way can I suggest we would get a speedup by a factor of 3.6 in one of the more expensive functions associated with the task
by dropping down to a md4 - a 32 bit digest.
C) In a separate issue CachedUrlGenerator::fromRequestStack/prepareCache - in #36 I adopted a sha1 as a work arround to case insensitive cache-keys lookups - md4 is a better choice there.
Comment #143
martin107 CreditAttribution: martin107 commented#135-#136 fixed the problem that the accumulated generated URLs for the following requests should be cached separately.
/user ( a 302 to redirect )
/USER ( a 404 )
Well here is a unit test that covers the issue.
So now we are not dependant on unrelated test accidently identifying the problems for us.
Note that this works by attaching a monitor that logs the number of distinct caches accessed. So indirectly CachedUrlGenerator::writeCache() is given some coverage.
Also in a minor note I have added the appropriate @coversDefaultClass to CachedGeneratorUnitTest.php
Comment #145
BerdirI noticed the cache problem somewhere else too (block cache when doing lower and upercase URL's), I think this should be fixed in a separate issue, in the cache API. The easiest fix might be to set the 'binary' => TRUE flag on the cid column in the cache table schema. Can you open an issue for that?
Comment #146
martin107 CreditAttribution: martin107 commentedI agree, I would love to keep a relatively expensive md5 hash out of the critical path ( a hash that will be calculated for every page request! )
Somebody already has :)
#2352207: Database cache backend does not treat cid as case sensitive
Comment #147
martin107 CreditAttribution: martin107 commentedOk So I think I have the kernel of an idea which will explain the failing tests...
There is a common link between ConfigImportAllTest and Download test
ConfigImportAllTest touches the URL generation and caching in a very limited way... it simulates a visit to /admin/config/development/configuration
I am including the blob that is pushed into the database when I visit the page manually.
I have pretty printed it so you can make out a serialized array of 30 key value pairs stored in a array ( 60 strings )
the last string of the pair contains the URL - This gnarly looking URL is stored.
/admin/config/user-interface/shortcut/manage/default/add-link-inline?token=nX__dWCXnUdBccb-P1bC3wjtkMejv-vjDI-kKJkFauQ&link=admin/config/development/configuration&name=Synchronize&destination=admin/config/development/configuration
the failing "download test" also includes a troublesome URL
How to store the URL in a database? google helps and suggests a few solutions
http://stackoverflow.com/questions/4923011/how-to-serialize-url-and-make...
I am busy and may not get to this this week... the weekend is looking better..
The unit test I have in my head is to demonstrate the storage and retrieval of some evil looking URLs
Comment #148
catch@martino107 that looks like it might be fixed by #2351015: URL generation does not bubble cache contexts - assuming the caching is breaking the CSRF token.
Comment #149
martin107 CreditAttribution: martin107 commentedYay #2352207: Database cache backend does not treat cid as case sensitive is in. @Berdir thanks for broadening my perspective. I have removed the md5 hash
#2351015: URL generation does not bubble cache contexts looks like a good solution ... Caching URLs with perishable tokens is a thorny issue. I am playing Catch's hunch and for now I am removing all shortcut.link_add_inline routes from the cache.
As a separate thorny issue. We should cache all possible URLs. So here is a reference http://www.w3schools.com/tags/ref_urlencode.asp
I don't think our current solution will work in the general case. but hey that is for the weekend.
Comment #151
catchAnother thing that could break this, see the last few comments on #2382503: Not possible to render self-contained render array while a render stack is active.
Comment #152
xjmComment #153
martin107 CreditAttribution: martin107 commentedNow that #2377281: Upgrade to Symfony 2.6 stable is in...
Since the last testbot pass, we take a new path through the code, so I am retesting.
The changes, I see are in the area of Crell's Symfony\Cmf\Component\Routing\ProviderBaseGenerator::generate()
PS
I have removed the now redundant tests.
Comment #155
martin107 CreditAttribution: martin107 commentedA) Fixed DownloadTest::testFileCreateUrl()
As it simulates toggling between clean and unclean URLs, the contents of the URL cache will become invalid.
B) Trivial nit-pick
Drupal\Core\Routing\UrlGenerator::generate($name, $parameters = array(), $absolute = FALSE) which implements the following interface Symfony\Component\Routing\Generator\UrlGeneratorInterface
Where the third parameter $absolute/$referenceType is a bool|string ... one on the following constants
const ABSOLUTE_URL = true;
const ABSOLUTE_PATH = false;
const RELATIVE_PATH = 'relative';
const NETWORK_PATH = 'network';
As it still stands, this could propagate a string into the $options array when a bool was expected..
C) The retest was necessary - there is now a "Recursive router rebuild detected" exception hmm
Comment #157
martin107 CreditAttribution: martin107 commentedI was reading #2359369: Render cache is not cleared when module is uninstalled and saw this :-
https://www.drupal.org/node/2359369#comment-9388575
I guess we need to do that here also
Comment #158
BerdirWhy would you want to do that and when?
Comment #159
martin107 CreditAttribution: martin107 commentedThere are no important performance or database size implications worth talking about...
I just want to keep things tidy.
The scenario I have in mind is a site with a long uptime and where modules come and go.
Each module would be free to populate the cache_url table with entries set with no expiry date...
The accumulation of junk would be small, but I favour a design with a comprehensive cache invalidation strategy.
In the back of my mind I also have this security scenario
Evil module takes advantage of a good uninstalled module and finds a way to make use of a stale but still cached external link to
and good site gone bad ...
Far fetched I know but something that makes me pause.
Comment #160
catchFor this I think we should move away from a permanent cache entry and instead use a long TTL - could default to one month and make it configurable (either by service override or in settings). Just means that backends like the database that never evict can eventually clear out old items.
Not sure if that should be done here, or added to the database cache backend itself though.
Comment #161
martin107 CreditAttribution: martin107 commentedCache garbage collection/invalidation is a topic that needs widening to a larger audience.
From a quick review of open cache issues, there are a couple of things that touch on this point... tangentially
I am have opened a new issue to try and draw all the loose threads together...
Comment #162
martin107 CreditAttribution: martin107 commentedReroll
This was triggered by #2342593: Remove mixed SSL support from core
which removed the following parameter from the constructor in UrlGenerator
Comment #163
mikeytown2 CreditAttribution: mikeytown2 commentedIn D7 I stopped using cache permanent due to the database not evicting like catch said in #160. Just be sure to make it so the chance of a cache stampede is rare; just in case the cached entries got set in bulk via drush or something like that.
Comment #165
martin107 CreditAttribution: martin107 commentedRestoring the _cachable status of shortcut.link_add_inline before I forget ( see #149 )
@mikeytown2 and interesting refinement, something I would like to see added to all caches via #2384957: Garbage collection when module is uninstalled.
Comment #167
YesCT CreditAttribution: YesCT commentedadded the issue summary template to the summary, but it still needs someone familiar with the issue to update the remaining tasks.. and other parts of the summary.
Comment #168
martin107 CreditAttribution: martin107 commentedI have unpicked LanguageUILanguageNegotiationTest:testLanguageDomain() and have a questions to ask...
looking at the HTTP option from UrlGenerator->generateFromPath
https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Routing!UrlGenera...
HTTP has three states :-
unset - use current scheme
TRUE - must point to a secure location ( HTTPS )
FALSE - enforce http
So with regard to testLangaugeDomain() the three calls to _url ( aka generateFromPath ) :-
1) create URL with 'HTTP option unset - pick up default scheme ( HTTP )
2) create URL with 'HTTP' option set to TRUE enforce HTTPS
3) create URL with 'HTTP" options unset, BUT pickup that the default schema changed to HTTPS
in (3) the default schema is simulated by pushing a request onto the stack with 'HTTPS' set to on, and the test failure is the cached http version, from 2 is returned.
So the questions relates to mix-mode support for HTTP and HTTPS
I can't find any policy docs relating to drupal 8 and mixed mode support. Can someone help me out? At the moment the best I can find is https://www.drupal.org/https-information. Which I read as contrib modules need this so YOU can ignore the massive security problems is opens up.
If we support mixed mode, which I fear we must then :-
CachedUrlGenerator::prepareCache() needs to inspect the current request and generate the cackey key using a call to Request::isSecure()
Comment #169
znerol CreditAttribution: znerol commented#2342593: Remove mixed SSL support from core
Comment #170
znerol CreditAttribution: znerol commentedAlso #92 still not addressed:
See #2124749: [meta] Stop using $request->attributes->get(MAGIC_KEY) as a public API, #2362227: Replace all instances of current_path(), #2372507: Remove _system_path from $request->attributes
Comment #171
martin107 CreditAttribution: martin107 commentedThanks for the comment @znerol, that was very helpful.
LanguageUILanguageNegotiationTest now passes.
Yep, but I was prioritizing bug fixing over refinement. as a way to encourage more activity on the issue. :)
Unfortunately I can only spend a little time each day on open source stuff.
For me, the next priority is what is missing from our unit tests, that let those ConfigImportAll test failures slip through.
Comment #173
martin107 CreditAttribution: martin107 commentedSo I hope to ask some difficult questions about ConfigImportAllTest::testInstallUninstall()
and hope it sparks ideas in other....
testInstallUnistall :-
1) Install every module.
2) Uninstall every module that can be uninstalled.
3) Import the configuration thereby re-installing all the modules.
4) rebuild the container.
5) Check that all modules that were uninstalled are now reinstalled.
So far so good - I want to focus on step 4.
WebTestBase::rebuildContainer()
Ok so the "maintain the global request object" is ahem non-functional-- the local variable is not used!!!
that is ok as long as an identical request object is created and pushed onto the stack.
But as far as I can see at no point is this new request stack [ created in prepareRequestForGenerator() ] passed into the CachedUrlGenerator::prepareCache()
it is too late, the sequence is wrong, the kernel is rebuilt without it.
I have a second, difficult question ... but tomorrow is another day.
Comment #174
znerol CreditAttribution: znerol commentedThis might have been introduced by #2016629: Refactor bootstrap to better utilize the kernel.
Comment #175
martin107 CreditAttribution: martin107 commentedWe have a cyclic path while rebuilding the container... see the only exception
As after reading the issues surrounding #2016629: Refactor bootstrap to better utilize the kernel. Well that suggest we haven't taken enough care with the order of things while simulating the rebuilding of the container.
Also it make me reprioritize the _system_path refactoring task. It is better to try and open the loop with the code in as final a state as possible.
#167 asked for a summary update... FWIW here is my todo list.
A) Bug FIx - work out if the following observations can help identify the rebuild loop.
1) The UrlGenerator service HAD a soft-dependance of the @?request_stack and now has a hard dependence ( see 'calls:' prepareCache )
2) The UrlGenerator is now a service that need explicit destruction. ( CachedUrlGenerator implements DestructibleInterface ) is that affecting the way services are being restored.
B) Add simple integration test (see #88 )
I agree My take on this is that is doesn't need to be anything fancy, apart from the stampede test. The unit tests are comprehensive.
C) Profiling - as well as the standard task. For me there is an additional question First off it may not matter, but I think the response to a unprimed cache
may be unnecessarily slow... but again that may not matter. ( see #141 )
D) Garbage Collection #160 outlines a perfectly reasonable solution - a separate issue was created ... but strictly we should either acknowledge that this
issue is dependant on that one .. and bump that one to critical ... or discuss it further here.
Comment #176
martin107 CreditAttribution: martin107 commentedSo I plumbed in @current_route_match service into the UrlGenerator as $this->routeMatch
so I could swap out
for
Unfortunately, as UrlGenerator is invoked prior to routing a Null RouteMatch is injected for $this->routeMatch and the whole thing becomes a bust..
Do anyone else have a different way forward.. otherwise this needs to be declared a 'special case'.
Looking back to June 2013, the very first patch. .. We started using setter injection to mimic the way the uncached url Generator was constructed.
Well things have changed .. the only reason to use setter injection is for optional parameters. The uncached UrlGenerator's style of construction has changed and this patch did not keep up.
[ In short I have moved the prepareCache() functionality into the constructor where it should be ]
Comment #178
znerol CreditAttribution: znerol commentedCurrent route match only returns meaningful results after routing. Why not use
$request->getUri();
as cache key instead? The page cache relies on that too.Comment #179
martin107 CreditAttribution: martin107 commentedWorks for me. This also simplifies the cache selection algorithm.
It may look like I have taken an axe to the code. but just to be specific the old functionality is preserved.
1) URI's distinguishable by script name still utilise separate bins.
2) URI's with language dependant elements still utilise separate bins.
Comment #181
martin107 CreditAttribution: martin107 commentedFirst cut version of integration test -
Just the simple version first - it just a "Is the cache is being populated test?"
As it stands I have looked for integration tests in some other caches.
cache.bootstrap, cache.menu and they don't bother ... I make no comment - as I am not sure if this is a good or bad thing.
Despite what the CacheFactory::get() documentation says if the cache not has been initialized the return type in not a
\Drupal\Core\Cache\CacheBackendInterface - If not initialized a call to get() returns bool FALSE.
I am about to file a documentation issue for that.
If this is acceptable, then we could just test at the appropriate moment that the cache has been initialize and signal everything is OK
Until that is definite... I have taken the paranoid approach reaching into the cache and confirming its contents.
Full disclosure .. For anyone thinking this is overkill -- this extra tests consumes an extra 1min17sec on my local test environment.
Comment #182
martin107 CreditAttribution: martin107 commentedComment #184
martin107 CreditAttribution: martin107 commentedReroll. as might be expected after the Ghent DA sprint.
no conflicts just merging
Comment #186
martin107 CreditAttribution: martin107 commentedThis issue has a concurrency problem, that is, it works fine when pages are requested one after the other but falls over when multiple pages are requested simultaneous.... Ugh
To expose it open up access of the admin pages to the anonymous user and run this load test
ab -n 100 -c 10 http://HOSTNAME/admin/structure/views
As always it is difficult to get a handle on these things, but as best I can guess, the longer it takes to write the cache to the persistent store so the greater the chance of the next request loading a bad partially copy.
Let me try and justify that with a story I was trying to do some initial profiling.
I want to capture a sample of the saving a typical user might see. I did typical admin things, added users, created view, was generally nosey...
Here is a summary of the smallest and the greatest potential savings out of the 25 pages I visited. It is based on a subsequent inspection the cache_url_generator table.
smallest admin/modules/uninstall/confirm - 1 cached url (140B )
next smallest admin/structure/views/view/who_s_online/preview/who_s_online_block (16 elements 2.5KB of cache )
largest /admin/structure/views 117 elements ( 15.5 KB of cache data )
So that gave 3 profiles for testing :-
Hampster-16 [ admin/structure/views/view/who_s_online/preview/who_s_online_block ]
Hippo-117 [ /admin/structure/views ]
Headless Hipster-1
The headless hipster would be the "Are we doing more harm than good?" test - You are trying to be a speedy as possible and you are serving a simple JSON object which contains a single URL. Does the overhead of the cache wrapper impact on the single minded speed objective?
Anyway back to load testing
Hampster-16 works fine without error and gives about a 19% speedup ( from 378ms to 305ms )
Hiipo-117 when run without concurrency gives about a 16% speedup ( 617 to 515ms ) but like I say gives errors when run concurrently.
We seem to get away with writing 2.5KB to cache safely, BUT the writing 15.5KB to cache is too much before the next request comes in.
Anyway I would welcome comments on how to proceed.
Comment #187
dawehner@martin107
Do you understand already why the
ConfigImportAll
test fails exactly?We certainly should profile, whether the effort done here is worth it, no question!
The reason here is that we write using the
::destruct()
method. This one is executed AFTER the response is send out, so the cache entry is not nec. available for the next request. For real life I think this is fine, we should more concentrate us on the case of warm caching here.Comment #190
martin107 CreditAttribution: martin107 commented@dawehner, sorry getting ready for Christmas got in the way ...
I thought I did, but no...the more I look the less I like my explanation :(
Comment #191
martin107 CreditAttribution: martin107 commentedThe newly failing tests SaveUploadTest and RemoteFileSaveUploadTest .. both pass locally for me
Comment #192
dawehnerFor now this is just a reroll.
Comment #194
dawehnerAlright, let's fix it.
Comment #195
dawehnerRerolled.
Comment #197
dawehnerFixed it.
Comment #198
kim.pepperAwesome! Do we need to see some performance metrics?
Comment #199
martin107 CreditAttribution: martin107 commentedYes please, I tried outlining 3 test profiles in #186
They are only suggestions ... if want to follow a different path, please go ahead
Comment #200
kim.pepperDid a comparison of the views admin page (/admin/structure/views) with and without this patch.
https://blackfire.io/profiles/compare/c64355c1-14e7-4fbf-9bf1-feb79c9fd0...
To me, this looks like performace is worse, but I can't see a reference to CachedUrlGenerator in the graph. :-(
I'm testing on a vagrant image (see https://github.com/nickschuch/vd8) which has xdebug enabled so the results are possibly inaccurate.
Comment #201
dawehnerYeah, don't do that.
One thing you can see is that there are some ms saved in the url generator, see https://blackfire.io/profiles/compare/c64355c1-14e7-4fbf-9bf1-feb79c9fd0...()&selected=Drupal%5CCore%5CRouting%5CUrlGenerator%3A%3AgetInternalPathFromRoute&settings%5Bdimension%5D=wt&settings%5BcallerDepth%5D=2&settings%5BcalleeDepth%5D=&settings%5BminNodeCost%5D=5&settings%5BminCallCost%5D=0.01
If you search for url generator on the left side, it seems not worth to do.
Maybe we have to figure out a caching strategy per page, much like path aliases.
Comment #202
dawehnerFirst, let's post a reroll, got some conflicts.
A really simple frontpage with in total 6 links renderer in total 4% faster. Not sure how good these numbers are. I would like to see someone setting up a menu with more items.
PS: It turned out to be a bad idea to profile admin/index, given that it parses YML files on every request.
Comment #203
larowlanHoping someone might be interested in an issue summary update
Comment #204
jibranNothing code related just minor comments improvements. BTW nice work on the patch @dawehner.
Correct me if I am wrong this means that we are storing all the urls of the page against its uri. If this is correct can we add this to class description?
WoW
Nice test. Can we add comment before 2nd assert that it is fetched from cache?
Comment #205
kattekrab CreditAttribution: kattekrab commentedI'll have a crack at updating the issue summary on this. But with 200+ comments, it might take a while :)
Comment #206
larowlanaddresses #204 and fixes some minor nits - only documentation changes I found during review - so rtbcing
Comment #207
larowlanProfiling:
Created 5 nodes with links to terms etc, promoted them
Loaded home page to prime caches
Loaded home page again - this time profiling
Truncated cache_url_generator table (but no other caches)
Loaded home page again (cold url generator cache)
Comment #208
webchickSince this is performance-related, tossing catch's way. Great work, folks!
Comment #209
hussainwebI am just fixing nitpicks and documentation mostly. I changed a couple of
self::
tostatic::
in case of constants as constants can be overridden when the class is extended. I am also tempted to change allarray()
to[]
in the new files but didn't so as to avoid confusion in this critical.I just don't know how to rephrase this.
Comment #211
hussainwebThat was quick. :)
The conflict was in this part:
The $generator was removed in #2364157: Replace most existing _url calls with Url objects, which just got in. Without the generator, it seemed to me that there was nothing to do here and hence this file does not appear in the reroll at all. This is why the status is back to Needs review instead of RTBC.
Comment #213
hussainwebThe failure occurs because we are now caching the URL and now the context is different. Earlier, it worked because we had:
But I am not sure if this is a proper test. Here, we are forcing the cache to be cleared before URL is regenerated so that HTTPS is picked up from the request stack. However, this may not happen in real life scenarios. If the user suddenly switches to HTTPS, won't the URL's continue to be served from the cache? To me, it seems that we have to account for specific request attributes when generating the cache key.
Comment #214
larowlanRight, so we need to react to $this->container->get('request_stack')->push($request) and update our cache key.
Comment #215
znerol CreditAttribution: znerol commentedThe cache key depends on the current request and hence should not be computed upon construction.
Comment #216
kattekrab CreditAttribution: kattekrab commentedSorry. I've read the whole issue, and can't update the issue summary. This needs someone with a deeper understanding of what's actually going on here. :/
Comment #217
larowlanComment #218
larowlana start
needs work still to make sure all the cache keys are stored in this->cacheKeys so delete/clear work correctly - or that we just use the keys of the ->cachedUrls
Comment #219
larowlanshould be finished now
Comment #221
pwolanin CreditAttribution: pwolanin commentedGiven that we need to load the route for access checks, usually, I still think we should look at #2074297: Optimize the code in doGenerate() in the UrlGenerator to take advantage of Drupal path restrictions. as an alternative to *another* attempt to cache things.
Comment #222
larowlan@pwolanin ok, but this is a critical, that isn't
can't we do both?
Comment #223
pwolanin CreditAttribution: pwolanin commentedYes, we can do this then maybe remove the caching of path alias as suggested in the summary and also optimize the code. Not sure what will really give us the best outcome, but having the code optimized is a win no matter what.
Comment #224
dawehnerWe need some ways to mark CSRF protected URLs as not cacheable, at least for now, until #2351015: URL generation does not bubble cache contexts is in.
Comment #225
kim.pepperRe-roll of #219
Comment #226
dawehnerOne thing we could do is to ask route processors whether they want to alter the route objects on built time. Once we do that,
the individual routes could be marked as non-cacheable.
Comment #227
pwolanin CreditAttribution: pwolanin commentedSo, I think the static cache that larolan moved out should really be in this patch - we don't want to hit the cache backend at all if we keep it in memory.
Comment #228
effulgentsia CreditAttribution: effulgentsia commentedJust a reroll.
Comment #229
effulgentsia CreditAttribution: effulgentsia commentedSorry, #228 was a bad reroll. This is a correct reroll of #225.
Comment #230
effulgentsia CreditAttribution: effulgentsia commented@pwolanin: what do you mean by this? Looks to me like #219 and subsequent patches still retain the static
$cachedUrls
. The only thing that #219's interdiff removed was the static$cacheKeys
, because the only thing that was in there was$request_stack->getCurrentRequest()->getUri()
, which can be quickly fetched when needed.Comment #231
pwolanin CreditAttribution: pwolanin commented@effulgentsia - nevermind, I was confused. They were trying to put it at a lower level in the generator there, but this looks like it does cache all generate URLs in memory during the request.
It seems like it caches and then loads every one that's ever been generated though?
Comment #232
effulgentsia CreditAttribution: effulgentsia commentedYes, each one that's been generated for the current request's URI. I wonder how efficient that is, given:
The first one can be fixed by adding role to the cache key. Not sure what we want to do about the second.
Comment #233
dawehnerWe potentially can get rid of the cached path aliases when we can the URLs.
Well, IMHO there are many more things which render URLs beside menus.
Comment #234
effulgentsia CreditAttribution: effulgentsia commentedReally? Like what? Nodes and other content entities are already render cached. So are Views.
Well, we need to at least be able to quickly do a getPathByAlias() if the current request's URI is an alias. However, this comment in AliasManager::writeCache():
I think is suspect, because it was based on D7 experience where we were not caching rendered nodes or menus.
Comment #235
dawehnerRandom links in local tasks, breadcrumbs, quick edit links, or simple parts of the admin area.
Well fair, but an index query should be fast? Alternative implementations could also use other storage engines for path alias, as always.
Note: For the inbound processing we don't rely currently on the alias manager but rather on the path alias storage itself:
The aliasManager cache key will be set afterwards, because before you don't have the system path available.
Comment #236
dawehner@effulgentsia
If you think this is not a critical issue, please discuss it with @fabianx and @catch
I still think making the url generator fast is important, given how complex it is in Drupal.
Comment #237
effulgentsia CreditAttribution: effulgentsia commentedYep, I'll discuss with @catch. My concern is not only with it maybe not being critical, but possibly that the solution here is worse than the problem. Because we're doing a cache get in the constructor, and the data in that single cache record can be large, and then not used in the very common case of 90+% of those URLs already being inside of a larger cached structure (like a rendered node, menu, or View). I think the approach here made a lot of sense when this issue started 1-2 years ago, but D8's improved approach of caching larger rendered things makes the approach here either moot or counterproductive.
Comment #238
dawehnerI totally agree with your point that the constructor should not be slow ... but rather just be retrieved, when needed.
Comment #239
dawehnerYou could argue that you want to also care about the cases, in which something is not rendercacheable.
Comment #240
dawehnerJust want to be sure, we can already mark specific routes as not-cacheable. IMHO we don't need a separate mechanism from the existing event subscribers.
This interdiff marks all routes with _csrf_token requirements as not cacheable.
Comment #242
dawehnerUps ...
Comment #243
Wim LeersQuick edit links are cached on the client side.
Local tasks and breadcrumbs will become cacheable once we have fixed menu tree render caching.
Absolutely!
I think to make a decision here, we need to know where the links live that aren't being cached as part of a bigger structure (like menus, nodes, etc.). Do we have an idea about that? Could we collect that data automatically by using
debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 20)
and thus collect the callers of "uncached links"?Comment #244
dawehnerI'm so tired of optimizing for the easy cases only.
Comment #246
catchIf we're able to replace path alias caching with this then it definitely makes sense regardless of caching elsewhere - since it'll be higher-level than the path alias caching by itself.
Not sure how many links we'll end up that aren't cached within menus or entities and yes that'd be good to know about.
Comment #247
xjmMarking for reassessment based on the feedback from @efffulgentsia.
Comment #248
Fabianx CreditAttribution: Fabianx commentedI will give this a review as its green now.
I still think a semi-persistent router cache is good, because the larger structures:
a) have usually more granularity by design
b) this cache would not be used at all, when the things are already render cached, so this cache existing is still helpful as a backing of the uncached case.
The router and link generators have been one of the slowest components introduced so I think its important to not make them as fast as possible, where caching is one reason.
That said I don't know how much of my reasoning applies to the particular patch here in the issue.
Comment #249
Fabianx CreditAttribution: Fabianx commentedQuick feedback:
- The idea to cache generated URLs per request is good, but the implementation needs work as this re-implements a cache collector, which we already have in core, which has the advantage of lazy loading the cache only when its needed. @see Drupal/Core/Cache/CacheCollectorInterface.php (lets hold off on porting that though, the implementation here also has a keys which can always just be one.)
- The good thing is that this has a global granularity. (I can't see any cache contexts it depends on except uncacheable for CSRF, which is fine) - however this is set back that it is unlikely to have a cache hit for anything not yet render cached.
On the render caching:
- Based on the granularity it can be that we need to re-generate some links again for different roles, but not sure if this would be on the same page already visited.
Question:
- Is it possible to cache one entry of a route per link OR is the route generation always dependent on the current request?
Rephrased:
Can we have (regardless that this cache might grew large for links never used for now) one cache item per generated route?
[ It might be also possible to e.g. use some CacheCollectorLRU or something like that, possibly only in APCu for most frequently generated links. ]
Thanks,
Fabian
Comment #250
Fabianx CreditAttribution: Fabianx commentedComment #251
BerdirI lost a bit track of what which of the various router/url/menu performance issues are doing, what problem they're solving and how they are related to each other.
Having a summary of those issues would be awesome, maybe in a new meta issue or the existing performance critical meta issue.
Comment #252
dawehner.
Comment #254
catchI think everywhere that we'd render a link, we're doing render caching already.
On a render cache miss, between route pre-loading which we already do, and path alias caching which we already do, this won't provide enough of an additional optimization to be worth it. And it'll only kick in when the render cache is a miss but the url generation cache is a hit.
However, I do think it's worth doing if we can successfully replace the path alias cache with it - since that'd be caching at a higher level, and feels like the more appropriate place generally.
Comment #255
dawehner+1
Agreed that this isn't critical.
Comment #256
rteijeiro CreditAttribution: rteijeiro commentedRe-rolled and fixed a few nitpicks.
Comment #257
Fabianx CreditAttribution: Fabianx commentedCould someone please answer these questions:
Question:
- Is it possible to cache one entry of a route per link OR is the route generation always dependent on the current request?
Rephrased:
Can we have (regardless that this cache might grew large for links never used for now) one cache item per generated route?
[ It might be also possible to e.g. use some CacheCollectorLRU or something like that, possibly only in APCu for most frequently generated links. ]
Comment #258
effulgentsia CreditAttribution: effulgentsia commentedSince this issue is no longer critical, untagging for critical office hours. Don't know if we have or should start a "Major Office Hours" or similar tag.
Comment #259
Fabianx CreditAttribution: Fabianx commentedJust a short ping to answer my own questions:
I think we can cache url generation with that at a much more granular level now that all Path processors specify cacheable metadata. (yeah!)
So the cache collector per page is no longer needed, but something that generates a link based on current url we could just choose to not cache.
That together with the alias routing could indeed speed up things for large lists of links.
Comment #260
Fabianx CreditAttribution: Fabianx commentedSetting back to active as the approach needs to be changed from collecting all links per request url, to just cache links in general and depend on their cacheable metadata.
Comment #262
pwolanin CreditAttribution: pwolanin at Acquia commentedGiven the other page and block caching, and route pre-loading is the extra complexity of more caching worth it?
Comment #263
Fabianx CreditAttribution: Fabianx commentedWe should check it out for some list of things that is itself uncacheable (e.g. form with lots of links?) and see what it gives us.
Comment #265
dawehnerSo it seems to be that this won't happen ...
Comment #267
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThe current status makes it look like this was committed unless you read the comments all the way to the bottom. Setting a more accurate status.