Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
Discovered in #2380389: Use a single vendor directory in the root.
The current implementation in DrupalKernel::initializeSettings does not invalidate the APCu Cache even if the autoload was regenerated by composer or moved to a different directory.
Also this behavior is needed in rebuild.php
, so that the APC cache can be invalidated by the user.
Proposed resolution
Use a unique deployment identifier based on composers lock file as APCu cache prefix.
Remaining tasks
User interface changes
API changes
Data model changes
Comment | File | Size | Author |
---|---|---|---|
#51 | 2575495-51.patch | 1.01 KB | webflo |
#39 | 2575495_39.patch | 2.18 KB | Mile23 |
#37 | 2575495_37.patch | 1.41 KB | Mile23 |
Comments
Comment #2
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedComment #4
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedComment #5
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedForgot to add a few files.
Comment #7
daffie CreditAttribution: daffie commentedQuick look:
This looks wrong
Comment #8
webflo CreditAttribution: webflo at UEBERBIT GmbH commented@daffie: Can you explain what you mean? Thats the inline php template.
Comment #9
daffie CreditAttribution: daffie commented@webflo: Then it is my mistake. It looks strange to me, thats why I mentioned it.
Comment #12
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedComment #13
pfrenssenReviewing.
Comment #14
pfrenssenIf these two switch positions they are alphabetically ordered which is more acceptable for the control freak in me.
It wouldn't hurt to explain why we are generating this class. Perhaps add a line like:
"This class has a method that will return an ID that matches a particular Composer installation. It is used to ensure that PHP opcode caches will be invalidated whenever the Composer dependencies change."
Maybe put a /** @file */ docblock here so that the generated file is compliant with coding standards.
The @see is incomplete, link it to the generator static method.
Use {@inheritdoc} instead.
This also needs a test. We cannot change the composer dependencies in a running test so the only thing we can do is to check that the identifier that is returned by the generated class matches the composer installation.
Comment #15
hussainwebAddressing points in #14.
Comment #16
Mile23OK, so hang on... :-)
One of the problems being solved here is that someone just moves the vendor directory to another location, updates the top-level autoload.php, and calls it a day, which breaks APC.
This means that the strategy here fails in two ways:
1) If they never run
composer dumpautoload
then the script here will never run, and APC will barf.2) Even if they do that, the hash in
composer.lock
will be the same, because it's just a hash of the contents ofcomposer.json
, so subsequent changes will not be reflected.So if it's the kernel's job to catch this (which I'm not sure it is), then the Composer script has to use the non-APC autoloader to resolve a class that's in
vendor
(like, for instance,Symfony\Component\ClassLoader\ApcClassLoader)
for it's file path. Then hash that file path along with thecomposer.lock
hash for the unique ID. This way the moved file will have a different hash, and will also reflect the status ofcomposer.json
.Also, I'd suggest that we write out the hash as JSON instead of a PHP file, and put it in
vendor/drupal/deployment.json
. This way it has a higher probability of being removed for reasons that would change its validity anyway.So that solves the second problem, but now we have to solve the first. Which we can't. People have to run
composer dumpautoload
. We should also haveupdate.php
andrebuild.php
call the same script code to generate thedeployment.json
.One downside to this is that the user now has to merge their git pull, because we store vendor in the repo.
But since the goal is to ultimately remove vendor from the repo, then +1.
Comment #17
pfrenssenComment #18
Jaesin CreditAttribution: Jaesin at Chapter Three commentedBut isn't the goal to be able to remove vendor from the repo, not require it? Or to remove vendor from the do drupal repo?
Comment #19
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedI try to repsond to the concerns from @Mile23 in #16
1) The dump-autoload event is trigger via
composer install
,composer update
and<code>composer dump-autoload
. The class will be automatically by one of these commands. The class will be always available, because we will run composer install during Drupal.org Packaging and Drupal CI.2) The hash from
composer.json
orcomposer.lock
might not change butSettings::getApcuPrefix
uses the site root as additional criteria to create unique prefixes for APCu.We generate this class on the fly because its faster than decoding the JSON file. Also the class is auto-discoverable through composers class loader. The JSON file would hard-code the vendor directory to a specific location. Drupal should not know about the location of the vendor directory, because we inject the autoloader in our application.
Comment #20
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedComment #21
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedSorry messed up the interdiff in #20.
Comment #24
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedComment #25
Mile23Right, but the point of this issue is that anyone could move vendor at any time anywhere, and that will break APC, so we have to invalidate APC so it can start caching again.
Yes, true, but again: We don't expect the site root to change, we expect vendor/ to change.
Sorry to sound like a broken record (or, uh, skipping CD? or poorly ripped MP3?) but a changed vendor directory is exactly the problem being solved for here, unless I've completely missed the point of https://www.drupal.org/node/2380389#comment-10375583
If the JSON (or PHP or whatever) file isn't present, then we can manage APC and generate a new one.
Comment #28
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedComment #29
Mile23Again, when the vendor directory gets moved, this will result in a fatal error because APC can't load the class. That's why we want JSON, and then to check if it's there, and then load it and compare hashes.
Comment #30
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedI expect that the composer hash will change because we rebuild the autoloader in #2380389: Use a single vendor directory in the root. It is not supported to just move the autoloader to the root. I tried this in my local installation and i get the following error. The error is in composers autoload before we even try to bootstrap Drupal.
Comment #31
Mile23That's the special case of
\Drupal
which is just file-included by the autoloader.I have an intuition that some of this is compounded by
config.autoload-suffix
in bothcomposer.json
files. If we remove that, then the autoloader class changes names with a hash when the autoloader is rebuilt. I have yet to figure out if that's enough to invalidate APC's cache though. It might be a suitable hash for dynamically adding as the prefix in the kernel, though.Also, I've discovered that if you don't manually delete
vendor/autoload.php
it never gets rebuilt, even without the autoload-suffix. I think Composer just gives up too soon in that case.Comment #32
Mile23The
composer.lock
hash will only change when thecomposer.json
file changes *and* you rebuild the lock file:composer update --lock
Comment #33
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedI tried this already. Composer autoloader prefix is not related to the APCu key which we use to get the autoload from APCu. Thats why we use a different key after we generated a new autoloader. There are problems with APC afaik, because APC will use the file modification date to invalide the cache. Besides that, APC is not a thing in PHP 5.5.
We could use the content hash as additional information.
Comment #34
Mile23Same deal. The content hash just pulls in the JSON, sorts the array, writes it as a file, and then hashes. (So the hash doesn't change when you just re-arrange lines.)
Comment #35
webflo CreditAttribution: webflo at UEBERBIT GmbH commented@Mile23: I would love to see an alternative patch. I don´t how you want to work around the fatal error from #30.
Comment #36
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedJust another idea. We could generate the following code.
Comment #37
Mile23Somewhat more simple... Not sure how to test it.
Though I did apply #166 from #2380389: Use a single vendor directory in the root, apply this patch, and then reload, and it works. :-)
Comment #38
Mile23Might refactor to put this functionality in
Settings::getApcuPrefix()
.Comment #39
Mile23Even more simple. More documentation than code changes. :-)
Still works such that you can apply #166 from #2380389: Use a single vendor directory in the root, apply this patch, and your APC-enabled Drupal installation will simply reload.
Ideas on how to test this are welcome.
Comment #40
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedI like that Patch in #39 but it does not invalidate the class loader if the vendor stays in the same directory.
Comment #41
alexpottI don't think the recent approach in #39 works for a few reasons:
I do think we need an issue to flush APCu in rebuild.php
I think #36 might offer some protection against moving vendor but it'll need testing.
Comment #42
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedIntegrated the __DIR__ constant from #36.
Comment #43
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedChanged to hash algorithm to plain sha256 because its makes no sense to access the class loader at this stage.
Comment #44
Mile23@alexpott:
#2380389: Use a single vendor directory in the root Moves the vendor directory and updates the autoloading and so forth. It works fine as long as you don't have APC on.
Agreed.
Let's rescope and add that here, since that's what's happening.
Comment #46
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedrebuild.php calls 'apc_clear_cache' and 'apcu_clear_cache' already.
Comment #47
Mile23Moved the patch in #39 to #2380389: Use a single vendor directory in the root since it solves the immediate problem there.
Comment #48
neclimdulWhere this might help is with symlink deployment.
I don't have a better suggestion but changing this every time we modify composer.json seems like an annoying maintenance hurdle.
Comment #49
Mile23The idea is that DeploymentIdentifier.php is generated when you (or a script) type
composer dumpautoload
(orinstall
orupdate
).Comment #50
catchI think in this issue to unblock #2380389: Use a single vendor directory in the root we should use more or less the same logic as https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21DrupalKer...
All of those things are or can be available before the APCu class loader is initialized and don't rely on anything in the vendor directory which is what prompted this issue in the first place.
This will work great if you update between tagged releases.
If you are running the 8.0.x branch and git pull within the timeframe of a tagged release, you might have to update $deployment_identifier in settings.php (or restart apache, call rebuild.php etc.). If you're running dev then that's fine. Same for dev tarballs.
All we need to do here is not break sites between betas or RCs/patch releases.
Also the deployment identifier here has exactly the same name as the one in settings.php already give or take snake casing, that's not great for documentation purposes especially since they're dealing with similar goals albeit the classloader vs. the container.
Also raising priority since the vendor dir change feels like it's RC deadline.
Comment #51
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedAlright same logic as in DrupalKernel::getContainerCacheKey.
Comment #53
webflo CreditAttribution: webflo at UEBERBIT GmbH commentedComment #54
timmillwood#51 looks to address everything in #50.
Comment #55
Mile23Er, never mind. I guess I just don't get the scope of this issue, especially before coffee.
Comment #56
Mile23Comment #57
catchCommitted/pushed to 8.0.x, thanks!
Comment #62
webchick