I've tried to enable Diff module and the code went to loop. This also happening when I'm doing feature-update from drush.
It's just rebuilding caches over and over again and the code goes over 5000 levels deep.

Here is the main issue:

   17.7711  297204736  14. entity_flush_caches() includes/module.inc:934
   17.7830  281430864  15. entity_defaults_rebuild($entity_types = ???) entity/entity.module:1085
   57.1516  335722104  16. _entity_defaults_rebuild($entity_type = 'rules_config') entity/entity.module:857
   58.2322  401386312  17. entity_delete($entity_type = 'rules_config', $id = 'rules_foo') entity/entity.module:898
   58.2322  401386680  18. entity_delete_multiple($entity_type = 'rules_config', $ids = array (0 => 'rules_applicant_rule_somebody_else')) entity/entity.module:316
   58.2323  401387000  19. RulesEntityController->delete($ids = array (0 => 'rules_foo'), $transaction = ???) entity/entity.module:338
   58.2359  401387744  20. EntityAPIControllerExportable->delete($ids = array (0 => 'rules_applicant_rule_somebody_else'), $transaction = class DatabaseTransaction { protected $connection = class DatabaseConnection_mysql { ... includes/rules.core.inc:276
   58.2551  401399872  21. entity_defaults_rebuild($entity_types = array (0 => 'rules_config')) entity/includes/entity.controller.inc:890

So basically entity_defaults_rebuild() is called first from entity_flush_caches(), then while rebuilding, it's calling again entity_defaults_rebuild() on Entity delete.

Affected code:

entity.module

function entity_flush_caches() {
  entity_property_info_cache_clear();
  // Re-build defaults in code, however skip it on the admin modules page. In
  // case of enabling or disabling modules we already rebuild defaults in
  // entity_modules_enabled() and entity_modules_disabled(), so we do not need
  // to do it again.
  // Also check if rebuilding on cache flush is explicitly disabled.
  if (current_path() != 'admin/modules/list/confirm' && variable_get('entity_rebuild_on_flush', TRUE)) {         
    entity_defaults_rebuild();                                                                                   
  }
  ...

which triggers code in includes/entity.controller.inc on RulesEntityController->delete():

 public function delete($ids, DatabaseTransaction $transaction = NULL) {
    $entities = $ids ? $this->load($ids) : FALSE;
    if ($entities) {
      parent::delete($ids, $transaction);
  
      foreach ($entities as $id => $entity) {
        if (entity_has_status($this->entityType, $entity, ENTITY_IN_CODE)) {
          entity_defaults_rebuild(array($this->entityType)); // HERE!!!                                                  
          break;
        }
      }
    }
  } 

And this goes endlessly on each entity_flush_caches().

Issue fork entity-2698553

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

kenorb created an issue. See original summary.

kenorb’s picture

Version: 7.x-1.6 » 7.x-1.7
kenorb’s picture

kenorb’s picture

kenorb’s picture

kenorb’s picture

Status: Closed (duplicate) » Active
FileSize
162.38 KB

I'm having 750 rules to feature-update. Increasing `xdebug.max_nesting_level` to 512, 1024, 2048 or 4096 didn't help. See the backtrace dump in the attachment.

kenorb’s picture

It sounds that I needed ~6 levels for each rule, so over 5000 levels deep potentially should be enough. Increased max_nesting_level to 6000, however some other modules (commerce_ss) got involved:

2015. entity_delete($entity_type = 'rules_config', $id = 'rules_council_tax_dis
2016. entity_delete_multiple($entity_type = 'rules_config', $ids = array (0 => 
2017. RulesEntityController->delete($ids = array (0 => 'rules_council_tax_disco
2018. EntityAPIControllerExportable->delete($ids = array (0 => 'rules_council_t
2019. entity_defaults_rebuild($entity_types = array (0 => 'rules_config')) site
2020. _entity_defaults_rebuild($entity_type = 'rules_config') sites/all/modules
2021. module_invoke($module = 'commerce_ss', $hook = 'default_rules_configurati
2022. call_user_func_array:{includes/module.inc:905}('commerce_ss_default_rules
2023. commerce_ss_default_rules_configuration() includes/module.inc:905
2024. rules_import($export = '{ "rules_increment_stock_canceling_order" : {\n  
2025. RulesEntityController->import($export = '{ "rules_increment_stock_canceli
2026. Rule->import($export = array ('LABEL' => 'Stock: Increase when canceling 
2027. RulesActionContainer->import($export = array ('LABEL' => 'Stock: Increase
2028. RulesContainerPlugin->import($export = array ('LABEL' => 'Stock: Increase
2029. RulesReactionRule->importChildren($export = array ('LABEL' => 'Stock: Inc
2030. Rule->importChildren($export = array ('LABEL' => 'Stock: Increase when ca
2031. RulesConditionContainer->importChildren($export = array ('LABEL' => 'Stoc
2032. RulesContainerPlugin->importChildren($export = array ('LABEL' => 'Stock: 
2033. RulesCondition->import($export = array ('data_is' => array ('data' => arr
2034. RulesAbstractPlugin->setUp() sites/all/modules/contrib/rules/includes/rul
2035. RulesExtendable->__call($name = 'info_alter', $arguments = array (0 => ar
2036. FacesExtendable->__call($name = 'info_alter', $arguments = array (0 => ar
2037. call_user_func_array:{sites/all/modules/contrib/rules/includes/faces.inc:
2038. rules_condition_data_is_info_alter($element_info = array ('label' => 'Dat
2039. RulesPlugin->applyDataSelector($selector = NULL) sites/all/modules/contri
2040. RulesPlugin->availableVariables() sites/all/modules/contrib/rules/include
2041. RulesConditionContainer->stateVariables($element = class RulesCondition {
2042. RulesAbstractPlugin->variableInfoAssertions() sites/all/modules/contrib/r
2043. RulesExtendable->__call($name = 'assertions', $arguments = *uninitialized
2044. FacesExtendable->__call($name = 'assertions', $arguments = array ()) site
2045. call_user_func_array:{sites/all/modules/contrib/rules/includes/faces.inc:
2046. rules_condition_data_is_assertions($element = class RulesCondition { prot
2047. RulesPlugin->applyDataSelector($selector = 'commerce-order:status') sites
2048. RulesPlugin->availableVariables() sites/all/modules/contrib/rules/include

and I think at some point went further (without stopping with the error), but started heavily processing feature-update for the few hours or so (I had to stop it).

kenorb’s picture

Title: entity_delete triggers another entity_delete causing a loop » entity_defaults_rebuild triggers another entity_defaults_rebuild causing an infinite loop
kenorb’s picture

Issue summary: View changes

Workaround is to set variable entity_rebuild_on_flush to FALSE (e.g. drush vset entity_rebuild_on_flush FALSE), to explicitly disable rebuilding defaults on cache flush.

kenorb’s picture

BR0kEN’s picture

This error also valid for another scenario:

  1. entity_flush_caches()
  2. entity_defaults_rebuild()
  3. ...
  4. features_flush_caches()
  5. features_rebuild()
  6. entity_features_post_restore()
  7. entity_defaults_rebuild()
james.williams’s picture

I ran into this kind of issue because I removed a commerce discount offer type, so suddenly lots of discounts needed removing. Discounts are implemented by commerce_discount as rules (which are entities) in its default hook, so they all have the 'in code' status. During the first rebuild, on removing the first discount, entity_defaults_rebuild() is called, despite being in the middle of a rebuild. I can see that a rebuild would be necessary if truly reverting an entity that was in the database, to its definition in code, but if it is being deleted during a rebuild, there's really no need to rebuild again at that point.

This attached patch avoids rebuilding in the middle of a rebuild. I can't guarantee this is the best approach, or that it will actually fix anyone else's problem, but someone that knows the entity module better than me might be able to confirm this makes sense to do in general. It may well help lots of people, who knows!

Steven Jones’s picture

Status: Active » Reviewed & tested by the community

@james.williams thanks so much for your patch. I was trying to change something about commerce shipping services and that was causing and infinite loop that led me to this issue. I've applied the patch it solves the issue perfectly. Given that _entity_defaults_rebuild goes to the trouble of setting ->is_rebuild on the entity it makes sense to me to check that flag before performing another rebuild.
It uses it for the \EntityAPIControllerExportable::save method already so why not use it in the delete method too.

VladimirAus made their first commit to this issue’s fork.

VladimirAus’s picture

Version: 7.x-1.7 » 7.x-1.x-dev

Created MR.