A typical workflow is that developers, on a local version of a site, will update the code, then run:

...
drush updb
drush cim
...

The important thing here is that drush updb is often run before drush cim, and this is the suggested course of action proposed, for example, in:

Where this will fail however when drush updb updates configuration, as happens, for example, when:

We realize, thus, that whatever order we run drush cim, drush updb, and drush cex in, none is foolproof:

This will fail, for example, in the scenario described here:

drush cim
drush updb

This will fail, for example, in the scenario described here

drush updb
drush cim

Possible resolutions

  • Better documentation?
  • A combined updb/cim command?
  • Warning that drush cex should be run in case config is deemed out-of-date in code?

Comments

alberto56 created an issue. See original summary.

alberto56’s picture

Title: Update hooks which modify configuration can lead to unstable sites; figure out how to deal with this » If an update hook modifies configuration, then old configuration is imported, the changes made by the update hook are forever lost.

This title better reflects what I am trying to convey with this issue.

xjm’s picture

Version: 9.0.x-dev » 8.8.x-dev

Since we would presumably fix this in D8 too, I'm filing it against 8.8.x (which is the current bugfix support branch). Patches can be tested against other branches as needed when they're uploaded without changing the issue's version selector.

The issue will be automatically updated to 8.9.x after the last 8.8.x bugfix release. Thanks!

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Dakwamine’s picture

As a developer, it would be interesting that updb do the following:

  1. check the current sync state of the config against the yaml config files: I can evaluate with a diff list if the updb may be run safely (aka I did not forget to import important configs before applying the updb)
  2. prompt if ok to start the update process
  3. do the updb as usual: show the updates that will be run + prompt
  4. warn me the current sync state of the config again: if configs are changed, it tells me that configs need to be exported to finish the update process properly (additionnally, ask me to export rightaway).

On top of this, a --deployment or --no-config-check flag could be added for scenarios when checking the configs are unnecessary (or maybe reuse the -y flag?).

The only unsupported scenario is when the update is triggered in the UI (update.php). A similar functionality has to be implemented in the update.php: UI to show the config diff, ask to run the update, show the diff again to warn the config changes.

Could this be enough to help with avoiding CMI mistakes? This has the advantage that the update functionality itself is not modified, it only asks for confirmation and warns.

bircher’s picture

I am adding two related issues:
#2762235: Ensure that ConfigImport is taking place without outstanding updates This will effectively enforce the order of operations (ie you can not cim before updb)
#3046903: [discussion] Environment specific modules with updates. This seems unrelated at first but will maybe be needing a similar approach than what #5 is suggesting.

Worth noting is also the drush deploy command.

moshe weitzman’s picture

IMO this is not a supported workflow. You *must* get your configuration right before you run a config import. Specifically, the config in the import must represent the values AFTER hook_updated_n runs. The developer workflow I advocate is to run the hook_update_n() locally, run drush config:export, and and then commit the results. Only then is the saved config ready to be imported.

I'm sure docs could be improved in this area.

a.dmitriiev’s picture

@moshe weitzman, what about the fields? If the fields storage and field instance was created in update/post_update hook and then exported locally to config/sync with some uuid, then on remote server the uuid might be different and then the field will be re-created on configuration import. If there is any post_update that migrates some data to those fields, that data will be lost, because config import will delete the field and create it again.

moshe weitzman’s picture

If there is any post_update that migrates some data to those fields, that data will be lost, because config import will delete the field and create it again.

Content should be entered via a HOOK_deploy_NAME(). Thats a new hook introduced by the drush deploy command. See https://www.drush.org/deploycommand/

Also, you don't need hook_update_n() to propagate a field creation. Do it locally in the GUI, and then config:export and the commit the result. No more is needed. Or just use base fields which are all in code.

a.dmitriiev’s picture

Thanks, but that deploy hook is only available with Drush 10, or? and the update hook I use to create commerce_subscription field, there is no UI for that.

moshe weitzman’s picture

Correct on Drush 10. Ideally core would have that hook but it doesnt yet acknowledge that drupal deployment is a thing.

alberto56’s picture

You *must* get your configuration right before you run a config import.

This is reasonable, but there is still another case where configuration changes made by hook_update_N() are not reflected. Consider the following scenario:

* a site development team overrides configuration in the settings.php file, but only on production. Concretely, maybe they have webform 5.x and override the webform.settings.default_page_base_path (which is "form" by default) and change it to "forms", but in settings.php, not in the database or in the site's config files.
* webform 6.x comes out, and in an update hook, updates "webform.settings.default_page_base_path" so that it is prepended with a slash, "/forms". In fact all forms completely break if this update is not performed.
* the developers run drush updb, make sure to commit the resulting changes to the config ("get your configuration right"), and push to production, and run drush updb && drush cim there.

At this point all forms stop working on production because config is overridden in settings.php using an "old" way of doing things, and the reason is not immediately clear.

A solution might be strongly advise module developers to avoid modifying configuration in update hooks, and, if they do, to implement hook_requirements() to make sure the changes are, in fact, reflected.

I wrote more about this in a blog post at https://blog.dcycle.com/blog/2021-01-29/hook_update_n/

Version: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

markdorison’s picture

Version: 9.5.x-dev » 10.1.x-dev
Related issues: +#1845004: Replace custom password hashing library with PHP password_hash()

This issue recently affected us with the release of Drupal 10.1. #1845004: Replace custom password hashing library with PHP password_hash() included an update hook to enable the new phpass core module. Once that update hook ran, the module was enabled only to be disabled when config:import was subsequently run.

We have automated tests that check the status of Drupal config state, but these did not fail because once config:import had run (via drush deploy), the configuration on disk matched the active configuration as far as Drupal was concerned.

If we are allowing folks to believe that configuration on disk is their source of truth and then modifying active configuration with an update hook, this seems like it will inevitably cause issues like this.

gcb’s picture

It seems to me like there's a fundamental contradiction to running database updates and configuration updates all in a single action.

It's already very complicated to wrestle with the concept of "correct configuration" when you have site administrators making configuration updates and developers making configuration updates in parallel.

What if configuration updates in code were given special treatment? We could have a separate one-time operation from updb [hook_install_configuration_N(): returns an array of configuration files to be re-imported from a module's /config/*/ directories; is triggered by 'drush update-configuration' or visiting /update-config.php]

Then, the "best practice" could be:
1. updb
2. cim
3. upcfg

(and then: export that config into code!)

If we tracked the executed upcfg functions in their own table with a date or "invocation" column, we could provide a way to "re-run" an invocation if a site accidentally ran CIM after upcfg.

YesCT’s picture

At Lullabot, we wrote up an Architecture Decision Record (ADR) to deal with a similar situation.

https://architecture.lullabot.com/adr/20210924-drupal-build-steps/

Depends on developers using hook_post_update_NAME() . [ 1845004 did use a hook_post_update_NAME: system_post_update_enable_password_compatibility ]

Oh, and that ADR works in conjunction with this one: https://architecture.lullabot.com/adr/20211212-config-status-check/

Does that help?

moshe weitzman’s picture

A PR is in progress for drush deploy to catch this problem and stop the deployment - https://github.com/drush-ops/drush/pull/5713

Anybody’s picture

Version: 10.1.x-dev » 11.x-dev