Problem/Motivation
Modules sometimes get installed without any entry in system.schema, because this is used to discover updates, this results in their updates never getting called either.
Proposed resolution
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Original report.
Debugging is using 8.1.1 while the patch is for 8.2.x. It would be nice to show how to reproduce this bug in 8.2.x but since it does not always occur I want to get it documented first. The PHP version is 5.6.20.
Some hook_update_N functions are getting called and some are not. For the "my_master" module the update hooks are getting called while for the "my_newsletter" the update hooks are not found. I do not know why some are found and others are not but we have had this occur twice for different people implementing update hooks for different modules.
In schema.inc the PHP function get_defined_functions() is called to get all the functions:
$functions = get_defined_functions();
Sorting and dumping the result for the my_newsletter module shows:
[571] => my_master_form_alter
[1086] => my_master_update_8001
[1087] => my_master_update_8105
[1088] => my_master_update_8111
[572] => my_newsletter_help
[573] => my_newsletter_mail
The update hook my_newsletter_update_8001 does not show up but the hooks for the my_master module are there. It appears that the my_newsletter.install file is not getting included. If you want to find the update hooks you had better include the .install file for my_newsletter! So add the line
module_load_include('install', $module);
Now the functions found are
[571] => my_master_form_alter
[1086] => my_master_update_8001
[1087] => my_master_update_8105
[1088] => my_master_update_8111
[572] => my_newsletter_help
[573] => my_newsletter_mail
[1142] => my_newsletter_schema
[1143] => my_newsletter_update_8101
Now I am getting prompted for database updates. There is no harm (except performance) in including this line to make sure the .install file is included.
Comment | File | Size | Author |
---|---|---|---|
#74 | 2738879-74.hotfix.patch | 589 bytes | dww |
#68 | 2738879-2-68.patch | 4.67 KB | alexpott |
#68 | 66-68-interdiff.txt | 762 bytes | alexpott |
#66 | 2738879-66.patch | 4.7 KB | mr.baileys |
#65 | 2738879-65.patch | 4.69 KB | mr.baileys |
Comments
Comment #2
trobey CreditAttribution: trobey as a volunteer commentedI have verified this with 8.2.0-dev. Attached are the two sample modules. I first enabled my_master and then uncommented the first update hook. Then I enabled my_newsletter with another update hook. Only the update hook for my_master shows up. I am using PHP 7.0.4 for this test.
Comment #3
cilefen CreditAttribution: cilefen commentedPossibly related: #2679008: Module weight is not taken into account after module installation
Comment #6
Jerenus CreditAttribution: Jerenus as a volunteer commented@trobey
I think this might not be a bug, it looks like works as design.
I tried to reproduce the problem:
1. Put my_master and my_newsletter module to my module directory.
2. Install my_master without changes.
3. Install my_newsletter module and uncommented the my_master_update_8105() and my_master_update_8111() function.
4. Run update.php: 8105 and 8111 are showing up.
5. Add a my_newsletter_update_8113() function to my_newsletter.install file and run update.php.
It seems works as design. The my_newsletter_update_8101() would only be script when the instance installed the old version of my_newsletter which without my_newsletter_subscribers table installed. It that right? If it is, should this be closed with works as designed?
Versions:
PHP 7.1.3
Drupal 8.4.x-dev
Comment #7
cilefen CreditAttribution: cilefen commentedI removed the "Triaged D8 major" tag because that is not the correct tag following a triage session.
Comment #8
cilefen CreditAttribution: cilefen commented@Jerenus That's good work so far on triage. I could give you credit if you document the triage steps (even if brief). It is the only way to know if the triage has actually been completed. Here are some made-up examples of documented triage steps:
Note that there are tags expected if it "Needs steps to reproduce".
Thank you!
Comment #9
cilefen CreditAttribution: cilefen commentedComment #13
BoobaaWas fiddling with a similar problem: added a new
mymodule_update_8004()
which wasn't recognized bydrush updb
. Having a breakpoint in\Drush\Commands\core\UpdateDBCommands::getUpdatedbStatus()
revealed this message:Removing this offending
mymodule_update_8000()
resolved the problem for me.Comment #15
Fernly CreditAttribution: Fernly at Dropsolid for District09 commentedThis issue still exists.
When a module gets installed without install file, there is no system.schema record in the key_value table for this module.
If later on an install file is added containing an update hook, it's never triggered until a system.schema record in the key_value table is created for this module.
Reproduce:
You'll see that no update hooks get picked up.
Comment #16
robin.ingelbrecht CreditAttribution: robin.ingelbrecht at EntityOne commentedI can confirm #15. Same problem.
Any solutions?
Comment #17
catchBumping to critical.
Comment #18
trobey CreditAttribution: trobey as a volunteer commentedI spent some time digging into the code and have some thoughts.
There is a entry in the key value table and the update is still not found. It could still be an issue of what is in the key value table (not just schema of 8000). But with the items above this should no longer be an issue.
I can write a patch with the above items if there is no issues with the approach.
Comment #19
trobey CreditAttribution: trobey as a volunteer commentedThis is a first attempt at a patch. This loads the install files when running the database updates. It also provides the same functionality for any other places that need to load install files. I did not see where install files are getting loaded elsewhere in core so I did not address that part.
This can use testing to make sure it fixes the problem where update hooks are not found.
Comment #20
trobey CreditAttribution: trobey as a volunteer commentedUpdate to fix tests.
Comment #21
trobey CreditAttribution: trobey as a volunteer commentedAnother try to get tests to run.
Comment #22
trobey CreditAttribution: trobey as a volunteer commentedDigging into the code further, the function drupal_load_updates() is used to load the install files. This calls drupal_get_installed_schema_version(). Usually this is the same as traversing all installed modules but there are some edge cases where the schema is missing. This seems to be causing the problem. It is safer to take the approach in the patch to check for install files for all modules. Once the code for the patch is added then drupal_load_updates() no longer needs to be called. I will try to update the patch when I have time. This should cover all the items in comment #18.
Comment #23
catchIdeally we should be finding out why that happens, as well as adding an update to correct the data for installed modules.
Cross-linking #2010380: Deprecate module_load_install() and replace it with ModuleHandler::loadInstall.
Comment #24
trobey CreditAttribution: trobey as a volunteer commentedIdeally, yes. But reality is that the reason may be multiple and unforeseen. I have encountered a problem and been able to reproduce it and others have had the same experience. But I cannot take someone else's case and reliably reproduce it. So the function call to schema versions is unreliable and there is no way to ensure that it always is reliable. I will try to implement a more reliable method that updates the schema versions when it discovers a problem. But relying on the schema versions is just asking for more of these strange problems popping up.
Comment #25
catchdrupal_load_updates() hasn't been updated since Drupal 7 or 6.
I think the answer here might be to stop using the schema versions altogether there and use the module list instead.
Comment #26
trobey CreditAttribution: trobey as a volunteer commentedThat information is helpful and the link to the related issue helps. I think we are in agreement about how to proceed.
Comment #27
trobey CreditAttribution: trobey as a volunteer commentedI updated the patch to include some of the suggestions.
Comment #28
pandaski CreditAttribution: pandaski at govCMS (Australian Government Department of Finance) commentedThanks for the steps #15
Comment #30
trobey CreditAttribution: trobey as a volunteer commentedComment #32
trobey CreditAttribution: trobey as a volunteer commentedComment #33
trobey CreditAttribution: trobey as a volunteer commentedComment #34
dawehnerI'm wondering whether we could provide a way smaller bugfix:
Fix this function, given in how many places it exists. My idea was to use
loadAllIncludes('install')
which has the right behaviour already.Personally I don't think we need a separate
.install
loader.Comment #35
alexpott+1 to a fix with a smaller surface area - especially less interface changes.
This looks testable.
Comment #37
dawehnerI tried writing a test, to then realize that the current bugfix doesn't fix the problem. The patch attached solves the additional problem (assuming the schema version isn't set on install).
I've been thinking about this issue a bit more. I tried to actually reproduce this, but I'm confused.
I totally see the schema version being set. Looking at the code in
/Users/dawehner/www/d8/core/lib/Drupal/Core/Extension/ModuleInstaller.php::install
it really looks like 8000 should always be set.This is certainly what happened locally when I renamed the install file.
One could argue that on some level having some more resilient code is fine.
Comment #38
alexpottComment #41
catchboth of the patches in #37 are the test patches.
Comment #42
mr.baileysI tried reproducing this with the steps outlined in #15, but if I enable a module without an install file, the correct
\Drupal::CORE_MINIMUM_SCHEMA_VERSION
is stored in the KeyValue store as expected. If I subsequently add an install file and update hook, the update hook is correctly discovered and executed on the next updatedb run. As @dawehner points out in #34, it should not be possible to end up with a module that is enabled while the schema version is missing. Maybe this could happen if the module install fails after it is enabled, before the schema version is stored?That said, I did encounter a live site where the schema version for a module without an update hook was missing, and newly created update hooks for that module were not discovered/executed.
Attached is a simple patch that ensures that for any enabled module,
\Drupal::CORE_MINIMUM_SCHEMA_VERSION
is used as installed schema version if version information is missing in system.schema. It includes the test from #37.Comment #44
mr.baileysFixed a bug, test now passes locally
Comment #45
mr.baileysComment #46
catchComment #47
catchComment #49
junaidpv#45 works for me.
Comment #50
george.karaivanov CreditAttribution: george.karaivanov at Peytz & Co commented#45 works for me as well.
Comment #51
alexpottThis is installing the update_test_no_preexisting module
Comment #52
mr.baileysComment #53
alexpottTest modules don't need the core key
There are now 8.8.x dumps we should use. See #3089900: Drupal 8.8/8.9/9.0 update test coverage
Comment #54
ravi.shankar CreditAttribution: ravi.shankar at OpenSense Labs commentedI have re-rolled the patch #45 and made changes as suggested in #53.
Comment #56
alexpottFixing the tests, ignoring coding standards where appropriate and making the test more robust.
Comment #57
catchThis should probably be in UpdateSystem now.
Should probably get the same treatment as in #3102059: Make tests of the update system use UpdatePathTestTrait instead of UpdatePathTestBase.
Comment #58
mr.baileysMoved the
NoPreExistingSchemaUpdateTest
to theDrupal\Tests\system\Functional\Update\UpdateSystem
namespace, switched to extendingBrowserTestBase
with theUpdatePathTestTrait
instead of subclassingUpdatePathTestBase
.Comment #61
mr.baileysMessed up the namespace for the test in previous patch.
Comment #63
miro_dietikerComment #64
longwaveI think a comment explaining why we need to do this config change would be helpful.
Comment #65
mr.baileysMade the comment more commenty.
Comment #66
mr.baileysSame as above, but with the comment staying under 80 chars.
Comment #67
longwaveThanks, this looks good now.
Comment #68
alexpottSome v small tidy-ups. We shouldn't call
$this->ensureUpdatesToRun();
as that ensures we have at least one update to run but what we really want to do here is to ensure that update_test_no_preexisting_update_8001() is run.Comment #69
alexpottCrediting @catch for issue review, @Fernly for providing clear steps to reproduce the issue.
Committed and pushed 8faf10c9b1 to 9.0.x and b57a81c762 to 8.9.x. Thanks!
Committed 26189f0 and pushed to 8.8.x. Thanks!
My patches has been minor tidy-ups.
Comment #73
dwwThis seems to have broken tests on the 8.8.x branch:
Not sure if we should hot-fix 8.8.x or revert this.
Thoughts?
Thanks!
-Derek
Comment #74
dwwA bit of local debugging... this test was added at
/core/modules/system/tests/src/Functional/Update/NoPreExistingSchemaUpdateTest.php
But the namespace says:
namespace Drupal\Tests\system\Functional\UpdateSystem;
phpunit is happy either way, but run-tests.sh is terribly confused.
This patch gets run-tests.sh passing locally again.
Not sure if it's better to move the file or change the namespace, but for now, the namespace was the smaller diff. ;)
Thoughts?
Thanks!
-Derek
Comment #75
longwaveThe patch in #74 has the correct namespace, the UpdateSystem namespace was introduced in #3106395: Move tests that test the update system to UpdateSystem namespace and in 8.9.x and 9.0.x only, it wasn't backported.
Comment #76
dwwOh yeah, I see what happened. There is no
core/modules/system/tests/src/Functional/UpdateSystem
in 8.8.x branch, so cherry-picking et al got confused. Test was moved tocore/modules/system/tests/src/Functional/Update
but the namespace wasn't changed to match. So I believe #74 is the right solution for 8.8.x to work properly again.Reviews, corrections, improvements most welcome. ;)
Thanks,
-Derek
p.s. x-post. Cool, glad @longwave agrees!
Comment #78
larowlanCommitted 38f68cd and pushed to 8.8.x. Thanks!