Problem/Motivation
Talks about #2628144: Ensure that ConfigImport is taking place against the same code base making config import more reliable. There is some contention because it reduces some of the abilities of developers to do what they please. This issue is going to do something simpler. Ensure that when running configuration import there are no outstanding database updates to run.
Proposed resolution
Add validator to ensure that no outstanding updates need running.
Remaining tasks
User interface changes
Additional configuration import validation messages
API changes
None
For kernel tests we have added \Drupal\KernelTests\KernelTestBase::installUpdateRegistryState()
to set the state for modules so that config imports are easy to test.
Data model changes
None
Release notes snippet
Config imports are prevented if there are outstanding database updates. In order to import Configuration, all available updates must have been run. This change helps users follow best practices around configuration management, drupal updates and deployments.
Comment | File | Size | Author |
---|---|---|---|
#74 | interdiff_72-74.txt | 8.15 KB | pooja saraah |
#74 | 2762235-74.patch | 24 KB | pooja saraah |
#72 | 2762235-2-72.patch | 23.41 KB | alexpott |
#72 | 68-72-interdiff.txt | 2.54 KB | alexpott |
Comments
Comment #2
dawehner+1 for the general approach. Updates are designed to be executed before anything else. On the other hand we should maybe consider to allow config imports as part of post update, otherwise post update config changes would be overridden by deployment scripts.
Comment #3
alexpott@dawehner this is why #2628144: Ensure that ConfigImport is taking place against the same code base - I think that that is the only real way to deal with this - but this is a first and less controversial step.
Comment #4
alexpottComment #5
moshe weitzman CreditAttribution: moshe weitzman at Acquia commentedI was never totally clear on the right order between these two steps so IMO this explicit ordering is helpful.
Comment #7
vijaycs85Initial patch...
Comment #8
alexpottThere are also post updates to check for. This is the code from system_requirements()...
Comment #9
vijaycs85Thanks @alexpott. I have added tests #1970534-108: Patch testing issue and working on test failures (ref: https://www.drupal.org/pift-ci-job/568413)
Comment #11
swentel CreditAttribution: swentel as a volunteer commentedRerolled the patch from the test patch issue.
Made the specific added test work - except for the post update check, but I think there's some static caching going on there.
Note also the irony of running the import before 'profile' is set, throws notices in the test, bitter irony :)
Comment #13
swentel CreditAttribution: swentel as a volunteer commentedSo the remaining failures are due to pending post updates. Checked whether I could replace the post update registry in the kerneltest, but seem to fail so far .. will look further this evening.
Comment #14
swentel CreditAttribution: swentel as a volunteer commentedOk, I was focusing on the wrong thing, thanks for berdir for pointing me to drupal_set_installed_schema_version() ...
Comment #15
swentel CreditAttribution: swentel as a volunteer commentedBring back the post updates, will trigger fails,
- either because of static values in update path tests (at least, that's my suspicion)
- in kerneltestbase because the registry finds the post update functions from system (so either we swap that service or we run them in kerneltestbase - first one seems more appropriate)
Comment #17
swentel CreditAttribution: swentel as a volunteer commentedThis should fix the fails for classes using kernelTestBase.
Comment #18
swentel CreditAttribution: swentel as a volunteer commentedstupid file_put_contents left over
Comment #21
swentel CreditAttribution: swentel as a volunteer commentedThis should be green
Comment #22
alexpottGiven that most of the code in this method is a copy of system_requirements() we might want to open a follow up to create an update list service or something similar.
let's inject these things
There's nothing else to do other than call these procedural methods :(
Lots of space :)
Static hmmm.... services are static. But after running updates we could rebuild the container and the config importer as before. I think to keep us sane do everything via the UI and assert on the error messages that would appear to a user. The test should have a comment that nothing should be done of the parent site and everything should be done on the system-under-test to test what a user would see.
Comment #23
swentel CreditAttribution: swentel as a volunteer commented1. good idea
2. done : UpdateRegistry doesn't have an interface though, introduce one as UpdateRegistryTest is a bit lying of course in it's comments :) Also, is that the right place for that class to be ? Maybe rename to UpdateRegistryNull ?
3. sad schema ..
4. fixed
5. ugh, so I can't login first to try and run an initial sync because system_update_8400() hasn't run yet .. the irony ... I've been thinking whether we should not rely on UpdatePathTestBase(), but being able to run updates is nice .. let me think about it some more .. suggestions welcome of course.
Comment #25
swentel CreditAttribution: swentel as a volunteer commentedAnd hence all the fails when injecting, the fun!
Comment #26
alexpottSo the question is do we want people to override the UpdateRegistry - I'm not sure about that. The simple thing is to make the test class extend the real class.
Comment #28
alexpottShouldn't post a patch without running a couple of tests first...
Comment #29
zaporylie+1 to general concept because developers are still confused what should goes first - updb or cim. Adding such a check will keep developers happy.
docblock and class name doesn't match
Missing full stop.
Can we change the order? First check for missing post update functions and then, if needed, iterate over module list? That will, potentially, make it faster(?).
Instead of setting value for $has_pending_updates we could just
return TRUE
. There's no need to continue.Comment #30
alexpottThanks for the review @zaporylie.
Comment #31
zaporylieI think we can remove $has_pending_updates variable and condition wrapping $missing_post_update_functions and return FALSE in last line of hasModuleUpdates()
Comment #33
fagoI don't think that the patch follow the right approach regarding post updates.
Post update hooks are for applying content changes. However, content changes might require config changes to work, e.g. having a new field added. Thus, imo the right order of things would be
1. updates
2. config import
3. post updates
There is also a PR to drush what the above that approach possible using drush commands: https://github.com/drush-ops/drush/pull/2740
That said, I think the patch should check for all updates being applied, but not for all post-updates being applied.
Comment #34
bircher#31: I agree, attached the patch. Also I tested the failing test on 8.4.x and it worked there, so lets see if the CI likes it better now.
#33: Well, this is a very common problem when building sites indeed.
But I think the workflow you propose is flawed because hook_post_update_NAME is for fixing content you broke in a hook_update_N function since in those functions you can not assume to have the API available. So post_update hooks should always follow the update hooks immediately.
On the other hand what you describe is a hook that should be run after configuration deployment to fix content that had been broken due to configuration changes. It is natural to turn to the hook that is already there, and we had a couple of projects that manually adjusted the configuration in the hook so that the content could be fixed in that hook. But then I discovered that such a post_config_import hook is not difficult to do. Here is the code we use to this effect: https://github.com/bircher/drupal-config_import_n I would have to add some more automated tests and create a project on d.o, but maybe instead we would want to have that as part of drupal core directly.
Comment #35
fagoI can't see why post-update should be only valid for this case. The documentation says:
This is exactly what I'm trying to do in #33 - so the hook seems to be fine?
Adding a separate post-config-import update would be an option, but I'm not convinced we need pre + post config import updates. After running updates, the system should be in a working state and it should be able to process config imports, no? I don't see data migrations would have to be run before config import when we have it afterwards?
Comment #36
bircherYou are right, there is a perfectly valid use case for updating entities in a post_update hook and the documentation is not wrong.
But I think it makes sense to distinguish between an update to an entity because of configuration changes due to schema changes in a (contrib) module, and due to configuration change as part of the site building process. Because in the first one the configuration changes in the update hook and hence the post_update hook is the perfect place to change the content entity. But the second changes the configuration as part of the configuration import and in my opinion the content should change in the post_config_import hook.
Of course since we currently only have the post_update hook it seems natural to use it for both cases and not differentiate between the two. But that leads to the problem that sometimes you want to import the configuration before the update. And that is because (as of now) both drush and the drupal UI do hook updates and post_updates at the same time (one after the other). And in my opinion that is a good thing.
"Sometimes" is very bad for repeatable scripts, and ergo I am suggesting to use a different hook in those times.
You are right that we don't necessarily need a pre and post config import hook since the post_update hook *is* also the pre config import hook. But I added both to my proof of concept to differentiate what kind of changes we are talking about. And as such a pre import hook would save the data of a field to be removed and the post import hook would add it to the new field. This is for the use case of site building where in the site development process you remove a field and add a new one (of say a new type with the same name).
So the argument is more for DX that you can say these two pre and post import hooks go together and this post_update goes with that update hook.
Right now without this patch there is:
(bad) and (good)
With the patch there is only:
You would like:
And I propose as a follow up:
or possibly:
But I think that should be a follow-up and not block the issue at hand.
Comment #37
moshe weitzman CreditAttribution: moshe weitzman at Acquia commentedThat gets my vote. The pre_config_import hook is a great place to pull out config before it gets changed. It would naturally get used by the post_config_import hook.
Comment #38
fagook, I can follow the reasoning outlined by bircher - I agree it makes to have a post update hook + a post config import hook.
I cannot follow that use case though, can you elaborate on this? What do you mean with pulling out config and how does that relate to post-config?
This makes total sense to me, but I'm wondering whether we need the pre-config hook:
Still, of course this would need a documentation update to clarify when to use what.
Comment #39
moshe weitzman CreditAttribution: moshe weitzman at Acquia commentedSorry, "pull out config" is a vague term. I think whats desired is a way to store in memory relevant config values in a pre_config_import hook and then use those values in a post_config_import hook
Comment #40
moshe weitzman CreditAttribution: moshe weitzman at Acquia commentedI guess we should get this in, and work on pre/post_config_import hooks in a new issue. Maybe @bircher is up for creating it?
I did a code review of this patch and it LGTM. Further, I retested and bot is happy.
Comment #41
catchI have at least one concern with this issue in its current state.
Especially on custom projects, let's say you have a configuration change that adds a field, then a post update hook that populates the field for some entities. This patch would require you to write a hook_update_N() for the configuration change instead of being able to do a drush cim -y && drush updb. Sometimes field content is set dynamically, say if data is being denormalized, so the hook_update_N() exists purely to load and save affected entities with no other logic.
In this case you may very well have zero hook_update_N() to run at all - you're deploying one changeset which you control entirely. You might not even be deploying the change to production but only to dev/staging during medium stages of a project.
If we shut that workflow off (which I've used personally, why write a hook_update_N() unless you have to), it undoes some of the convenience of configuration management.
Also missing a change record and the issue summary isn't clear, but I think we probably need to either add the new hooks here rather than in a follow-up.
Comment #43
bircherHmm
I see your concern. However, I do not really understand the difference between writing a `hook_update_N` and a `hook_post_config_import_NAME` for a one-off hook that doesn't do anything dangerous and is meant to run after configuration imports.
The above mentioned hook exists in https://github.com/bircher/drupal-config_import_n which I can make into either a contrib project or a core patch #2901418: Add hook_post_config_import_NAME after config import if we decide to block this issue on having the hooks in core.
Comment #44
fagoWe had a production discussion on this topic during Drupalcon Vienna sprints. We being: alex pott, bircher, jibran, mbm80 and me.
Here is a short summary of our conclusions:
Comment #45
jibranCreated #2914213: Don't create content in install hooks if module is installed during config import for this.
Comment #48
mpp CreditAttribution: mpp at AmeXio for District09 commentedComment #49
claudiu.cristeaI think we should only check for
hook_update_N()
. Those updates are supposed to repair the DB. If such updates are successful the config import should be safe.As an alternative to #2901418: Add hook_post_config_import_NAME after config import, we're using this sequence, in order to run content manipulations with the new installed modules and updated configs:
In this way we are able to run content manipulations, in
hook_post_update_NAME()
, that are relying on the updated configs and new modules installed byconfig:import
.EDIT: And this was supposed to be the initial scope of
hook_post_update_NAME()
, to run updates on top of the healed API. With config import, we're installing new modules & updating config, and that is part of “fixing the API”, in the same way ashook_update_N()
is.EDIT2: I've missed the @fago's comment from #33. It's exactly the same solution.
Comment #52
bircherSince I closed #2901418: Add hook_post_config_import_NAME after config import which was listed here as a solution for why some people were against this change I will also link the better way to handle this situation here: Drush deploy documentation
So there is nothing stopping us from doing this.
Comment #53
KapilV CreditAttribution: KapilV as a volunteer and at Innoraft for Drupal Care, Drupal Association commentedComment #54
kishor_kolekar CreditAttribution: kishor_kolekar as a volunteer and at QED42 for Drupal India Association commentedRe-roll the patch for 8.9.x-dev
Comment #55
kishor_kolekar CreditAttribution: kishor_kolekar as a volunteer and at QED42 for Drupal India Association commentedopss sorry @kapilkumar0324, I haven't seen you assign this issue yourself
Comment #56
KapilV CreditAttribution: KapilV as a volunteer and at Innoraft for Drupal Care, Drupal Association commentedhear a reroll patch.
Comment #57
KapilV CreditAttribution: KapilV as a volunteer and at Innoraft for Drupal Care, Drupal Association commentedComment #58
KapilV CreditAttribution: KapilV as a volunteer and at Innoraft for Drupal Care, Drupal Association commentedComment #63
alexpottNow that we have drush deploy and there's way more consensus about how updates and config imports are supposed to work together I think we can return to this. Especially as people experience things like #3241246: Missing schema: views.settings:ui.show.master_display
Here's a patch for D10 - I've used some PHP 8 features so we'll need a different patch for 9.4.x.
Comment #64
alexpottFixing the tests and some comments.
Comment #65
alexpottAdding release note to the issue summary - we also need a change record. I've load to point to something like https://drupalize.me/topic/deployment-workflows which explains when to import and when to export.
Comment #67
alexpottAfter discussing this with @catch decided to add \Drupal\KernelTests\KernelTestBase::installUpdateRegistryState() to set the update registry state in kernel tests when necessary. I think this is the best way to support adding the validation. Module install in kernel tests is already very very odd and there can not be much in contrib or custom that is using the ConfigImporter during kernel tests so maybe doing this is okay.
Here are the options I considered:
$this->installUpdateRegistryState(static::$modules);
in set up and things will work.Comment #68
alexpottFixing some more kernel tests...
Makes me less sure about adding the new method.
Comment #70
alexpottAdded CR and fixed up the release note a bit.
Comment #71
alexpottComment #72
alexpottSmall improvements to the code.
Comment #73
daffie CreditAttribution: daffie commentedWe are not using the $filename in the loop, than we therefor change it to:
foreach (array_keys($this->moduleHandler->getModuleList()) as $module) {
.I think that this method should be added to the method
doTest()
.Nitpick: can we add "as fully updated"?
The testbot failing for PHP 7.4. Probably for
string|array
.Comment #74
pooja saraah CreditAttribution: pooja saraah at Srijan | A Material+ Company for Drupal India Association commentedCan't apply the Patch on #72 against Drupal 9.4.x
Thanks for your suggestions @daffie
I have addressed the point 1. and 3. on comment #73
Here, I have Attached patch against Drupal 9.4.x
Comment #76
maskedjellybeanThere's a lot of references to
hook_post_config_import_NAME
in these comments and in related issues, but according to api.drupal.org, no such hook exists. I'd like to know what the solution to the problem posed in #41 will be. I've always run config import before database updates. That's because the primary purpose of database updates (in my opinion) is to modify content and the purpose of config is to modify structure. We need structure in place before we can create content that relies on that structure. Doing the opposite feels very counterintuitive to me but I can see that almost everyone disagrees with me. It's certainly got me scratching my head.I'm not here to tell you not to add this requirement. I think the fact that there's confusion about which to run first is a problem. But I'd really like to know how you all would solve the issue of the hook_update creating content for a field that does not exist yet because config has not been imported. EDIT: Without deploying twice (one deploy with the config and the next with the hook_update).
Comment #77
bircherThe
hook_post_config_import_NAME
idea has been replaced withhook_deploy_NAME
in drush.https://www.drush.org/latest/deploycommand/
The deploy hooks are not supported by drupal (yet) but that is a different issue. Deploy hooks are not meant for contrib modules or core, since they are meant to be "owned" by the same git repository as the exported config. But if this is a need, then the UI for deploy hooks would be relatively easy to implement as a contrib module.
From my understanding update hooks are for fixing the database schema to work with the latest codebase, post update hooks are for fixing the data that the update hook broke. Config import changes the structure and deploy hooks can update content knowing the config has been imported.
Comment #78
maskedjellybeanAh thank you for the info! So there is no working solution for my question yet, but eventually it will be to use
hook_deploy_NAME
and then modify my deploy script like this:drush updb -y; drush cim -y; drush deploy:hook -y
? Does the "NAME" part ofhook_deploy_NAME
matter or does it just need to be unique?Comment #79
bircherAh no! you misunderstood!
Drupal does not have a UI to run deploy hooks (yet). But deploy hooks are a thing already for a while and work in production with the stable drush version since two years.
So since you are using drush this is already available to you.
Deploy hooks work the same way as post update hooks.