Set config synching true before configs imported

Created on 27 May 2025, 3 days ago

Problem/Motivation

I have a recipe that bundles a Search API index as configuration. Recipe Installer installs the dependencies of the config first and one of the installed module dependencies introduces a content type with id "foo". The Recipe Installer also installs another module dependency which provides SAPI data source plugins via plugin derivative, where the plugin derivative generates data source plugin instances for every content type. When the the Recipe Installer would get to the point to import the Search API index config after dependency install it fails because the data source plugin id derived from the installed "Foo" content entity type and present inside the Search API config still considered to be missing.

The full stack trace:

In DiscoveryTrait.php line 53:
                                                                                                                                                                                                
  [Drupal\Component\Plugin\Exception\PluginNotFoundException]                                                                                                                                   
  The "datasource_foo" plugin does not exist. Valid plugin IDs for Drupal\search_api\Datasource\DatasourcePluginManager are: entity:block_content, entity:  
  comment, entity:contact_message, entity:file, entity:menu_link_content, entity:node, entity:path_alias, entity:search_api_task, entity:shortcut, entity:taxonomy_term, entity:user            
                                                                                                                                                                                                

Exception trace:
  at /var/www/html/build/web/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryTrait.php:53
 Drupal\Core\Plugin\DefaultPluginManager->doGetDefinition() at /var/www/html/build/web/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryCachedTrait.php:25
 Drupal\Core\Plugin\DefaultPluginManager->getDefinition() at /var/www/html/build/web/core/lib/Drupal/Core/Plugin/Factory/ContainerFactory.php:16
 Drupal\Core\Plugin\Factory\ContainerFactory->createInstance() at /var/www/html/build/web/core/lib/Drupal/Component/Plugin/PluginManagerBase.php:83
 Drupal\Component\Plugin\PluginManagerBase->createInstance() at /var/www/html/build/web/modules/contrib/search_api/src/Utility/PluginHelper.php:78
 Drupal\search_api\Utility\PluginHelper->createIndexPlugin() at /var/www/html/build/web/modules/contrib/search_api/src/Utility/PluginHelper.php:120
 Drupal\search_api\Utility\PluginHelper->createIndexPlugins() at /var/www/html/build/web/modules/contrib/search_api/src/Utility/PluginHelper.php:151
 Drupal\search_api\Utility\PluginHelper->createDatasourcePlugins() at /var/www/html/build/web/modules/contrib/search_api/src/Entity/Index.php:358
 Drupal\search_api\Entity\Index->getDatasources() at /var/www/html/build/web/modules/contrib/search_api/src/Entity/Index.php:2073
 Drupal\search_api\Entity\Index->getAllPlugins() at /var/www/html/build/web/modules/contrib/search_api/src/Entity/Index.php:1846
 Drupal\search_api\Entity\Index->getDependencyData() at /var/www/html/build/web/modules/contrib/search_api/src/Entity/Index.php:1727
 Drupal\search_api\Entity\Index->calculateDependencies() at /var/www/html/build/web/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php:328
 Drupal\Core\Config\Entity\ConfigEntityBase->preSave() at /var/www/html/build/web/modules/contrib/search_api/src/Entity/Index.php:1326
 Drupal\search_api\Entity\Index->preSave() at /var/www/html/build/web/core/lib/Drupal/Core/Entity/EntityStorageBase.php:528
 Drupal\Core\Entity\EntityStorageBase->doPreSave() at /var/www/html/build/web/core/lib/Drupal/Core/Entity/EntityStorageBase.php:483
 Drupal\Core\Entity\EntityStorageBase->save() at /var/www/html/build/web/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php:257
 Drupal\Core\Config\Entity\ConfigEntityStorage->save() at /var/www/html/build/web/core/lib/Drupal/Core/Entity/EntityBase.php:354
 Drupal\Core\Entity\EntityBase->save() at /var/www/html/build/web/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php:617
 Drupal\Core\Config\Entity\ConfigEntityBase->save() at /var/www/html/build/web/core/lib/Drupal/Core/Config/ConfigInstaller.php:390
 Drupal\Core\Config\ConfigInstaller->createConfiguration() at /var/www/html/build/web/core/lib/Drupal/Core/Recipe/RecipeConfigInstaller.php:44
 Drupal\Core\Recipe\RecipeConfigInstaller->installRecipeConfig() at /var/www/html/build/web/core/lib/Drupal/Core/Recipe/RecipeRunner.php:109
 Drupal\Core\Recipe\RecipeRunner::processConfiguration() at /var/www/html/build/web/core/lib/Drupal/Core/Recipe/RecipeRunner.php:309
 Drupal\Core\Recipe\RecipeRunner::installConfig() at n/a:n/a

For long time, I thought there is a stale cache somewhere along these places:

  \Drupal::service('plugin.manager.search_api.datasource')->clearCachedDefinitions();
  \Drupal::getContainer()->get('entity_type.manager')->clearCachedDefinitions();
  \Drupal::getContainer()->get(\Drupal\Core\Entity\EntityTypeBundleInfoInterface::class)->clearCachedBundles();
  \Drupal::entityTypeManager()->getStorage('node_type')->resetCache();

However, after analyzing the stack trace closely I came to a different conclusion: I think the actual problem that Drupal\search_api\Entity\Index->preSave() should trigger the Drupal\search_api\Entity\Index->calculateDependencies() which is the triggering point of this failure. Why? Because when a config from YAML is imported, dependencies are already set, therefore there is no need to calculate them. (This is how config installer works when site is installed from config.)
And Drupal\search_api\Entity\Index->preSave() would not trigger dependency calculation if it would be aware that config synchronization is happening at this moment.

Steps to reproduce

Proposed resolution

My suggesting solution is settingisSycnhing() to TRUE in \Drupal\Core\Recipe\RecipeRunner::processConfiguration() before bundled configurations from a recipe are imported and unsettling when it is done. (\Drupal\Core\Recipe\RecipeRunner::installModule() also already implements a similar pattern.)

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

πŸ› Bug report
Status

Active

Version

11.0 πŸ”₯

Component

recipe system

Created by

πŸ‡­πŸ‡ΊHungary mxr576 Hungary

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @mxr576
  • Pipeline finished with Failed
    3 days ago
    Total: 967s
    #507516
  • πŸ‡­πŸ‡ΊHungary mxr576 Hungary

    Okay, so it seems the proposed change breaks the RecipeQuickStartTest with a similar error that I have reported, although the proposed fix fixes the problem that I reported.

    In DiscoveryTrait.php line 53:\n
                                                                                                                                                                                                    \n
      The "entity:shortcut:default" plugin does not exist. Valid plugin IDs for Drupal\Core\TypedData\TypedDataManager are: filter_format, entity_reference, entity, entity:block, entity:block_co  \n
      ntent_type, entity:block_content, entity:field_config, entity:field_storage_config, entity:filter_format, entity:path_alias, entity:shortcut_set, entity:shortcut, entity:menu, entity:actio  \n
      n, entity:user_role, entity:user, entity:view, entity:date_format, entity:entity_view_mode, entity:entity_view_display, entity:entity_form_mode, entity:entity_form_display, entity:base_fie  \n
      ld_override, field_item:link, field_item:text, field_item:text_with_summary, field_item:text_long, field_item:float, field_item:integer, field_item:decimal, field_item:string_long, field_i  \n
      tem:created, field_item:entity_reference, field_item:string, field_item:changed, field_item:map, field_item:password, field_item:timestamp, field_item:uuid, field_item:language, field_item  \n
      :uri, field_item:email, field_item:boolean, float, decimal, boolean, email, uri, list, language, map, string, duration_iso8601, timespan, timestamp, any, binary, datetime_iso8601, integer,  \n
       language_reference          
    
  • πŸ‡­πŸ‡ΊHungary mxr576 Hungary

    and the exception originates from \Drupal\Core\Recipe\RecipeRunner::installContent()... so maybe I bumped into another issue and the proposed fix is not completely a bad idea (open for comments, please!)

  • Pipeline finished with Failed
    2 days ago
    Total: 415s
    #508125
  • πŸ‡­πŸ‡ΊHungary mxr576 Hungary

    Okay, at this point, I am unsure where to catch this bug and which layers are impacted... of course setting is synching on the config installer is not a god idea since it triggers #3 by design (from \Drupal\Core\Config\ConfigInstaller::createConfiguration():

            // If we are syncing do not create configuration entities. Pluggable
            // configuration entities can have dependencies on modules that are
            // not yet enabled. This approach means that any code that expects
            // default configuration entities to exist will be unstable after the
            // module has been enabled and before the config entity has been
            // imported.
            if ($this->isSyncing()) {
              continue;
            }
    

    setting the isSynching() on the config entity was an idea to bypass the dependency calculation in \Drupal\Core\Config\Entity\ConfigEntityBase::preSave():

        if (!$this->isSyncing()) {
          // Ensure the correct dependencies are present. If the configuration is
          // being written during a configuration synchronization then there is no
          // need to recalculate the dependencies.
          $this->calculateDependencies();
          // If the data is trusted we need to ensure that the dependencies are
          // sorted as per their schema. If the save is not trusted then the
          // configuration will be sorted by StorableConfigBase.
          if ($this->trustedData) {}
    

    ...but this changes Drupal core behavior globally, there is a way to scope it to recipes only, but still, would that be the approach? Unsure, especially even with this change other parts of the Recipe install fails when it tries to fetch the "datasource_foo" entity for different reasons (like because entity insert hooks are triggered).

  • πŸ‡­πŸ‡ΊHungary mxr576 Hungary
  • πŸ‡­πŸ‡ΊHungary mxr576 Hungary

    mxr576 β†’ changed the visibility of the branch 3526781-set-config-synching to hidden.

  • πŸ‡ͺπŸ‡ΈSpain penyaskito Seville πŸ’ƒ, Spain πŸ‡ͺπŸ‡Έ, UTC+2 πŸ‡ͺπŸ‡Ί

    You don't post your recipe, so just guessing, but FYI recipes don't install dependent config _entities_ unless explicitly said.

    You need

    config:
      import:
        your_module:
          - node.type.foo
    
  • πŸ‡­πŸ‡ΊHungary mxr576 Hungary

    And the answer was in front of my eyes after my new RCA... Gotta go to the corner to cry.

    Thanks for saving me from another hours of debugging and sorry for the noise.

  • πŸ‡ΊπŸ‡ΈUnited States thejimbirch Cape Cod, Massachusetts
Production build 0.71.5 2024