As far as i can track, in early stages of bootstrap "drupal_get_path" function is not defined, unless we explicitly load "common.inc":

// module.inc file
function module_load_include($type, $module, $name = NULL) {
  if (!isset($name)) {
    $name = $module;
  }
<i>  if (!function_exists('drupal_get_path')) require_once DRUPAL_ROOT . '/includes/common.inc'; //added by me </i>
<b>  if (function_exists('drupal_get_path')) { </b>
    $file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$name.$type";
    if (is_file($file)) {
      require_once $file;
      return $file;
    }
  }
  return FALSE;
}

This affects domain_alias_domain_load and my custom hook_domain_load. domain_alias_domain_load is lazy loading, but because of the above situation "domain_alias.domain.inc" never gets included by module_implements:

.
.
      if ($group) {
        module_load_include('inc', $module, "$module.$group");
      }
.
.

I don't know if this is a core issue or something specific to me. What do you suggest ?

Comments

agentrickard’s picture

Status: Active » Postponed (maintainer needs more info)

What symptoms are you seeing of this bug? I don't understand. Domain Alias works fine.

Have you cleared the registry cache?

If you want to run something during domain's bootstrap phase, it needs to be in the main module file, because drupal_load() is available at this point.

Any other domain_load() calls are typically delayed to hook_init().

This doesn't appear to be documented in the API. Perhaps that's the problem.

omercioglu’s picture

Still investigating, may be it's because i use eAccelerator. It's like "module_implements" cache gets corrupted somewhere.

function domain_api($domain, $reset = FALSE) {
  static $_modules;
  if (!isset($_modules) || $reset) {
    $_modules = module_implements('domain_load'); 
    // sometimes it sets $_modules = array('domain) here, may be cache corruption
    // and sometimes "drupal_get_path" function is not reachable in module_implements function (see post #0)
  }
  if (!empty($_modules)) {
    foreach ($_modules as $module) {
      // Cannot use module_invoke_all() since these are passed by reference.
      $function = $module . '_domain_load';
      $function($domain);
    }
  }
  return $domain;
}

The behaviour is erratic.

agentrickard’s picture

Status: Postponed (maintainer needs more info) » Active

Interesting. This is the second eAccelerator issue I've seen this week.

omercioglu’s picture

I've switched to APC and getting this error "Call to undefined function module_implements() in ...sites/all/modules/domain/domain.module on line 1293".
I had to add require_once DRUPAL_ROOT . '/includes/module.inc'; in domain_api() to fix this.
Simetimes it works without this fix (cached somewhere?), when i clear caches or somewhat it comes back.

agentrickard’s picture

Ugly. Not sure what to tell you except that you must rebuild the registry before enabling the opcode cache.

AFAIK, this would be a core issue with APC / eAccelerator, since it works fine without those.

agentrickard’s picture

Status: Active » Postponed (maintainer needs more info)
agentrickard’s picture

I'm getting bitten by this right now, testing Domain 7.x-2.x-dev and Drupal 7.2.

It appears to be a registry cache issue. Very frustrating.

omercioglu’s picture

I'm on drupal 7.2, with Domain 7.x.-3.x-dev. This issue is very critical for us, since most of our customizations depends on hook_domain_load. Right now, we are using a very dirty hack to make it work properly:

function domain_api($domain, $reset = FALSE) {
     $_modules = &drupal_static(__FUNCTION__);
     if (!isset($_modules) || $reset) {
         require_once DRUPAL_ROOT . '/includes/module.inc';      
         $_modules = module_implements('domain_load');
     }
     if (count($_modules) <= 1) {
         module_load_include('inc', 'domain_alias', "domain_alias.domain");
         $_modules = array('domain', 'domain_alias', '3rd_party_module');
     }
     if (!empty($_modules)) {
         foreach ($_modules as $module) {
             // Cannot use module_invoke_all() since these are passed by reference.
             $function = $module . '_domain_load';
             $function($domain);
         }
     }
     return $domain;
}

"domain_alias.domain.inc" does not get included and "hook_domain_load" does not register or get called other than "domain_domain_load".
I don't see this behaviour for other hooks (or perhaps i don't notice?).
So my solution proposals:
1- Bypass cache, find which modules implement hook_domain_load and save related hook info into our own variable
2- Create a new fake hook that works and assume that any module implementing that hook also implements hook_domain_load

agentrickard’s picture

Priority: Normal » Critical
Status: Postponed (maintainer needs more info) » Needs work

Thanks.

agentrickard’s picture

Oh, the problem may be that we are running this function _before_ module.inc is loaded.

agentrickard’s picture

The registry that stores hook implementations is running before all modules are loaded, which means that only modules implementing hook_boot() -- it seems -- are registered. Here's the list I get:

dblog domain_load
domain domain_load
overlay domain_load
system domain_load
devel domain_load

So we have to stop that.

agentrickard’s picture

Status: Needs work » Needs review
StatusFileSize
new538 bytes

Patch for 7.x.3.

This has to do with default domains lookups that get run prior to module loads. So we stop that.

omercioglu’s picture

agentrickard’s picture

Possibly, though class-loading is not an issue here. In the case I was testing, module_implements was caching results pre hook_boot and causing issues.

omercioglu’s picture

After a few tests this seems to solve the problem, thank you :-)

agentrickard’s picture

Status: Needs review » Fixed

Sweet! Committed!

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

omercioglu’s picture

Status: Closed (fixed) » Active

Sorry for reopening this case but same thing happing here again :(. I can make it work with a cache clear but eventually my domain_load hook unregisters/disappears at some random time. Most probably a registry cache issue as you mention. So if someone really must use this hook, he needs to manually add his hook to the module_implements list by modifiying domain_api():

  $_modules = &drupal_static(__FUNCTION__);
  if (!isset($_modules) || $reset) {
      $_modules = module_implements('domain_load');
  }
  
  if (!in_array('etu_utility', $_modules)) $_modules[] = 'etu_utility';

  if (count($_modules) <= 1) {
      $_modules = array('domain', 'domain_alias', 'etu_utility');
      module_load_include('inc', 'domain_alias', "domain_alias.domain");
  }

  if (!empty($_modules)) {
      foreach ($_modules as $module) {
          // Cannot use module_invoke_all() since these are passed by reference.
          $function = $module . '_domain_load';
          $function($domain);
      }
  }
  return $domain;

I'm giving up on this and moving my code in hook_domain_load() to hook_domain_bootstrap_full().

agentrickard’s picture

Priority: Critical » Normal
Status: Active » Postponed (maintainer needs more info)

I have never had this problem with adding data via hook_domain_load().

This may be a question of when you are loading the data and if/how you are caching it.

What does your hook implementation look like?

omercioglu’s picture

It's very simple. Here it is.

function hook_domain_bootstrap_full($domain) {
    global $conf, $_domain;

    if (isset($_domain['subdomain']) && !isset($_domain['base_subdomain'])) {

        $subdomain_elements = explode('.', $_domain['subdomain']);
        if ($subdomain_elements[0] == 'www') array_shift($subdomain_elements);
        $subdomain = $subdomain_elements[0];
        $_domain['base_subdomain'] = $subdomain;
    }
    
}

agentrickard’s picture

What did it look like as a hook_domain_load() implementation?

omercioglu’s picture

Actually it's almost the same:)

function hook_domain_load(&$domain) {
    if (isset($domain['subdomain']) && !isset($domain['base_subdomain'])) {

        $subdomain_elements = explode('.', $domain['subdomain']);
        if ($subdomain_elements[0] == 'www') array_shift($subdomain_elements);
        $subdomain = $subdomain_elements[0];
        $domain['base_subdomain'] = $subdomain;
    }
}
agentrickard’s picture

Status: Postponed (maintainer needs more info) » Active

I would remove the second condition from the IF and see if that helps. My guess is that the hook is getting called but not executed.

I have seen this before with custom hook_domain_load() and it is related to caching of the lookups.

dmitrit’s picture

I had a similar problem when calling domain_lookup() function from hook_domain_bootstrap_lookup() hooks directly: and domain_api() call was a problem.

omercioglu’s picture

Caching of the lookups is the cause? Good to know this! This bug was really annoying me!

agentrickard’s picture

Yes, there is an oddity in the static cache of domain_lookup, it gets called once very early, then called again, and I haven't been able to pinpoint where the issue is. The developer WTF is, sadly, better than the performance hit.

b-prod’s picture

Status: Active » Needs review
StatusFileSize
new1.01 KB

First point: commenting the line 56 of the domain.module file ($user->domain_user = domain_get_user_domains($user);) solves this issue.

Tracing the functions that are involved in domain_get_user_domains() give the following route:
domain_boot() -> domain_get_user_domains() -> domain_domains() -> domain_lookup() -> domain_api().

The issue seems to reduce to the following points:

  • the domain_api() function is called to early (actually during boot phase)
  • the static variables 'domain_domains' and 'domain_lookup_domains' are stored with partial values

The patch below fixes those bugs.

agentrickard’s picture

Yes, please review #1403270: DA collides with pathauto, which I believe solves this issue in a different way.

b-prod’s picture

Actually I can't figure how the patch #19 in #1403270: DA collides with pathauto solves current issue, since the domain_get_user_domains() function is still called before bootstrap phases end.

So the Drupal static variable $domain_api is still populated during boot phase, calling only the modules that implements hook_boot().

But I may miss something.

agentrickard’s picture

Nope. I was hoping that the two were related. If that patch doesn't solve this issue, then we have to test both of them.

agentrickard’s picture

Status: Needs review » Postponed (maintainer needs more info)

I just committed #1403270: DA collides with pathauto and can no longer replicate this issue.

b-prod’s picture

I will try with the next release of Domains when available, without applying my patch, then update this issue.

agentrickard’s picture

That patch hasn't made it to a release yet. It is very hard to roll new releases if no one tests patches.

agentrickard’s picture

To be clear: this issue is postponed because I can no longer replicate it in 7.x-3.x-dev.

If you can provide steps to replicate, then please post them and set to active.

b-prod’s picture

No worry, I will wait for the next release with the committed patch and try to replicate this problem. Whatever the results, I will update this issue. Do not hesitate to send me an email when the release is published.

Note: I cannot easily try the DEV version because of the development server configuration, that would make things quite difficult to roll back and above all it would take me time I do not have for a delivered project... That's the reason why I wait for the next release.

agentrickard’s picture

You need a proper dev build. Your process is broken.

agentrickard’s picture

Even a Drupal instance on your PC is fine for testing.

ejustice’s picture

I think I've been able to duplicate this issue from another angle, and have a repeatable way of testing it. I'm working with domain_directory (sandbox) and when clearing the cache from a domain that is using a subdirectory (listed in this issue). After the cache is cleared module_implements('domain_load'); in domain_api no longer knows about any other domain modules (such as domain_alias and/or domain_dir) for at least domain_load.

agentrickard’s picture

Status: Postponed (maintainer needs more info) » Active
StatusFileSize
new2.04 KB

I suspect the problem is that the domain_resolve_host() process (in the bootstrap) kicks off a process by which the module_implements() gets cached.

This patch bypasses domain_api() during bootstrap, which might break expected functionality. The other option, I suspect, is to change how _domain_bootstrap_modules_load() works, by including modules that implement hook_domain_load() in that list.

agentrickard’s picture

Status: Active » Needs review
StatusFileSize
new509 bytes

Here's the alternate approach, which loads the modules using hook_domain_load(). It might be a safer solution.

agentrickard’s picture

The alternate patch in #40 is probably the safest approach. It also needs an update hook and a module_disabled() change.

To test it, disable and then enable the module you are testing.

agentrickard’s picture

I am trying to write a failing test to prove this bug exists, and I cannot.

agentrickard’s picture

And the above is due to the fact that domain_test.module includes bootstrap hooks.

agentrickard’s picture

I still can't write a failing test, even trying to break the functions.

ejustice’s picture

Sadly patches in #39 and #40 aren't solving my issue (cache clearing using domain_directory) aren't working for me I'm still investigating from my side of things, but I cannot speak for the original author's problem so I'm going to leave it at needs review.

UPDATE: I don't know if this will help with building a test or not, but it appears that calling domain_load from hook_domain_bootstrap_lookup (with a hook_domain_load function) after clearing the cache (specifically cache_bootstrap > cid = module_implements) can cause the problem. I've been able to reproduce it by deleting just the module_implements row from the cache bootstrap table (manually or by calling drupal_flush_all_caches via the interface at admin/config/development/performance) and then reloading a page for a domain directory site (again manually or by calling header() in the form processing for the previously stated page).

agentrickard’s picture

That makes sense. I think you have to call domain_lookup_simple() from within domain_bootstrap hooks.

agentrickard’s picture

Status: Needs review » Needs work
b-prod’s picture

Steps to reproduce such bug:

  1. Create a dummy module "domain_boot" that implements either hook_boot, hook_domain_bootstrap_lookup and hook_domain_load. In its hook_boot function, the module calls _domain_bootstrap_modules() and domain_api() (see sample above).
  2. Create another dummy module "domain_not_boot" that implements only hook_domain_load and hook_domain_bootstrap_lookup.
  3. Install both modules and check the logs.

Sample of the hook_boot function:

domain_api(NULL, TRUE);
$modules = array(
  '_domain_bootstrap_modules' => array_filter(_domain_bootstrap_modules(), create_function('$module', 'return function_exists($module . "_domain_bootstrap_lookup");')),
);
$modules['domain_api'] = &drupal_static('domain_api');

foreach ($modules as $function => $list) {
  watchdog('domain', 'Available modules on boot phase through %function function: !modules.', array('!modules' => highlight_string('<?php ' . var_export($list, TRUE), TRUE), '%function' => $function), WATCHDOG_DEBUG);
}//end foreach

Conclusion:

  • modules that implement with hook_domain_load and hook_domain_bootstrap_lookup need to be aware of what they do if they deal with hook_boot. That's in the hand of developers and there is no bug here in Domain module code.
  • There is a possible hole if some module implements hook_init before Domain module and calls domain_api or another function that calls it without resetting the static variable $modules, because in such case only the modules that implements hook_boot will be called. The patch in #27 solves such issue, but not #45.
  • For #45, it could be safe to add the following check in domain_api before calling module_implements:
    $implementations = drupal_static('module_implements');
    if (isset($implementations[$hook])) {
      $_modules = module_implements('domain_load'); // Current existing code
    }
    

    @ejustice: could you tell us if this last point solves your issue?

agentrickard’s picture

Status: Needs work » Needs review
StatusFileSize
new828 bytes

New patch. Fixed the problem I was testing.

agentrickard’s picture

Moving domain_test hooks into a domain_test.domain.inc file for proper test coverage.

agentrickard’s picture

StatusFileSize
new7.98 KB

And a patch with tests.

agentrickard’s picture

Status: Needs review » Fixed

Committed.

   ce60bca..fb44df7  7.x-3.x -> 7.x-3.x

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

richsky’s picture

Status: Closed (fixed) » Needs review
Issue tags: +Performance

A big side effect with this patch, at least for me, is that module_implements is always reset.

I ended with more than 1600 module_load_include on my home page. With a slower file system, while being logged on, it is a huge performance penalty.

We had to revert back to module_implements('domain_load', TRUE, FALSE);

agentrickard’s picture

Status: Needs review » Postponed (maintainer needs more info)

In future, please open a new issue instead of re-opening closed ones. And please only mark "Needs review" if there is an active patch to review.

Are there steps to reproduce this behavior? No one else has noticed it, and it doesn't happen in the tests.

richsky’s picture

I will. No there is no step to reproduce this, it seems the static variable $_modules is reset every time domain_api is called, reset is always TRUE. Domain_api get called twice on node load, at bootstrap and at node_load. I have no exeperience with tests, but xdebug showed us the abnormal number of queries on a page resquest due to module_implements reset too many times.

agentrickard’s picture

This is my point. The code in domain_node_load() does not directly call domain_api() -- and it certainly shouldn't be called twice. It calls domain_lookup() with $reset set to FALSE.

So why would your site be doing something else?

richsky’s picture

But domain_init does, this is mine.

drupal_bootstrap > _drupal_bootstrap_full > module_invoke_all > domain_init
> domain_lookup
ln 125 args] => Array
(
[0] => 1
[1] =>
[2] => 1
)
> domain_api
ln 1094 args] => Array
(
[0] => Array
(
[domain_id] => 1
[subdomain] => mydomain.com
[sitename] => Fake name
[scheme] => http
[valid] => 1
[weight] => -1
[is_default] => 1
[machine_name] => mydomain_com
)

[1] => 1
)
ln 1453 $reset == 1
Do you have something else?

agentrickard’s picture

Right, that's by design to ensure things are loaded properly.

What you are not explaining is how this gets called 1600 times. This code is executed _once_. Is your homepage invoking domain_init() multiple times? If so, how and why?

agentrickard’s picture

And if $_modules is being reset that suggests some code is issuing a drupal_static_reset() or similar.

agentrickard’s picture

StatusFileSize
new73.96 KB

Here's a test for you: add this line

 function domain_api($domain, $reset = FALSE) {
  $_modules = &drupal_static(__FUNCTION__);
  // NEW LINE
  dsm('domain_api() reset: ' . (bool) $reset);

  if (!isset($_modules) || $reset) {

And look at the output. If reset == 1, stick a debug_backtrace() in to see where it is being called.

Attached is a pic of what should be happening, once per page request.

agentrickard’s picture

Please see if #1983860: domain_load called multiple times fixes this issue for you.

agentrickard’s picture

Status: Postponed (maintainer needs more info) » Closed (fixed)

I'm moving this issue over there. #1983860: domain_load called multiple times.

s_leu’s picture

Status: Closed (fixed) » Needs work

I had the same problem as it is described in in #1396442: Form to manage alias not show existing alias after updating from 7.x-2.x to 7.x-3.x on the alias form. I am using the latest stable release 7.x-3.10, so i don't think the problem is solved yet.

The problem seems to be that if you have your current role checked for the domain at admin/structure/domain/roles, the function domain_get_user_domains() will return something already in the booting process of drupal which will then write your domains in the static cache.
Unfortunately the module_invoke calls won't be executed at that time and so other modules like in my case domain_alias can't alter/add something to the domain informations.

If someone can fix that it should also be covered by some test, which shouldn't be too hard to implement.

agentrickard’s picture

Status: Needs work » Closed (fixed)

Please open a new issue with a proper description of the problem.

Referencing two old issues is not helpful.