Problem/Motivation

Drupal crashes with a segfault when doing a file scan and the the source contains a npm/grunt/nodejs source folder (node_modules). This is probably due to the many folder nesting of npm.
https://drupal.stackexchange.com/questions/126880/how-do-i-prevent-drupa...

Proposed resolution

Ignore the node_modules folder in includes/file.inc, it has no use for Drupal.
Backport Ignore front end vendor folders to improve directory search performance

Remaining tasks

-

User interface changes

-

API changes

-

Original report by guusvandewal

I use grunt to development templates in plain HTML,
if I clone this HTML source for use in the Drupal templates folder( and run npm install afterwards) it's not possible to clear the cache (with or without drush) anymore.
I get an ' segmentation fault 11 ' error.

A fix would be to hide node_modules from Drupal core in includes/file.inc
I've included a patch.

It would be great if this could be implementated, thanks in advance.

Edit: Proposed change record: https://www.drupal.org/node/3024333

CommentFileSizeAuthor
#80 2482549-80-array-syntax.patch842 bytesTR
#66 interdiff.txt3.52 KBPol
#66 2482549.patch4.82 KBPol
#64 interdiff.txt4.35 KBPol
#64 2482549.patch6.17 KBPol
#61 interdiff.txt3.64 KBPol
#61 2482549.patch5.46 KBPol
#57 2482549.patch4.68 KBPol
#56 2482549.patch3.87 KBPol
#54 2482549.patch4.42 KBPol
#51 2482549.patch4.41 KBPol
#49 2482549.patch4.41 KBPol
#46 nomask-variable-2482549-46.patch498 bytesufku
#38 core-ignore_node_module-2482549-38.patch3.68 KBjenlampton
#31 ignore_node_module-2482549-27.patch5.16 KBmarcelovani
#27 ignore_node_module-2482549-27.patch5.16 KBmarcelovani
#20 ignore_front_end_vendor-2329453-111.patch3.57 KBndf
#20 ignore_front_end_vendor-2482549-19-7x.patch3.57 KBndf
#16 ignore_frontend_folders-2482549-16-7.x.patch3.42 KBkaidjohnson
#11 ignore-node_modules-2482549.patch463 bytesMiSc
core_removed-node_modules-from-directory-scan-D7.patch586 bytesdrupal@guusvandewal.nl
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

drupal@guusvandewal.nl’s picture

Issue summary: View changes
drupal@guusvandewal.nl’s picture

Issue summary: View changes
jeroen.b’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, core_removed-node_modules-from-directory-scan-D7.patch, failed testing.

jeroen.b’s picture

Issue summary: View changes
jeroen.b’s picture

Please provide a proper patch: https://www.drupal.org/node/707484

jeroen.b’s picture

Title: Patch core for use with npm/grunt/nodejs » Ignore node_module folder in core to use Drupal with npm/grunt/nodejs
jeroen.b’s picture

Status: Needs work » Closed (duplicate)
Related issues: +#2329453: Ignore front end vendor folders to improve directory search performance

Duplicate of #2329453

Fidelix’s picture

Status: Closed (duplicate) » Needs work

@jeroen.b, that is actually a D8 issue.

This is for D7.

David_Rothstein’s picture

Status: Needs work » Closed (duplicate)

It's marked for Drupal 7 backport, though.

MiSc’s picture

Adding an reroll here to not confuse the d8 issue.

rooby’s picture

Version: 7.36 » 7.x-dev
Status: Closed (duplicate) » Postponed

In light of https://www.drupal.org/node/2715157 I assume this should be reopened and marked as postponed until #2329453: Ignore front end vendor folders to improve directory search performance is fixed?

David_Rothstein’s picture

rootwork’s picture

hussainweb’s picture

Status: Needs work » Needs review

The patch still applies. Are there any other tasks here?

kaidjohnson’s picture

The patch in #11 is incomplete, imo. There are three places where this functionality happens in D7. Also, node_modules isn't enough. bower_components is just as likely to exist (maybe less so now than a year ago), but should be considered a standard front-end module folder.

Migrating final D7 patch (https://www.drupal.org/node/2329453#comment-10622208) from #2329453: Ignore front end vendor folders to improve directory search performance here for review.

RobLoach’s picture

Status: Needs review » Needs work

This should be hitting a variable_get('file_scan_ignore_directories'); to retrieve the defaults to keep parity with Drupal 8.

ndf’s picture

ndf’s picture

Status: Needs work » Needs review

The newest drupal 7 patch in #2329453 is https://www.drupal.org/node/2329453#comment-10637754 (#111 not #101).

Added that patch here (2 files are equal):
- ignore_front_end_vendor-2329453-111.patch <-- from #2329453
- ignore_front_end_vendor-2482549-19-7x.patch <-- renamed file
@#17 this patch has the variable_get

ndf’s picture

ndf’s picture

Issue summary: View changes

The last submitted patch, 20: ignore_front_end_vendor-2482549-19-7x.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 20: ignore_front_end_vendor-2329453-111.patch, failed testing.

brianV’s picture

Status: Needs work » Needs review

Both the patches in #20 have test runner passes as of Dec 7, 2016 (after they apparently failed). Moving back to CNR.

DamienMcKenna’s picture

The tests passed on June 7th, so given this is a backport of functionality already in D8 what's the recommended process to get this committed? Do we need a subsystem maintainer to review?

charginghawk’s picture

Can confirm that this patch works for me. node_modules is no longer a blight upon my loading times.

marcelovani’s picture

It is not necessary to repeat the following code 3 times in 3 different places

  // By default, do not check for files in common special-purpose directories.
  // The folders here are front-end related and they have been added to avoid
  // issues with Drupal recursive scanning. In this case, we added node_modules
  // and bower_components. This also improves performance on frontend builds.
  $ignore_directories = variable_get('drupal_file_scan_ignore_directories', array(
    'node_modules',
    'bower_components',
  ));
  $no_mask = '/^(\..*)|' . implode('|', $ignore_directories) . '$/';

The only place that needs the code above is file_scan_directory() in file.inc.

I will explain why:
If you call file_scan_directory() but you don’t provide the array of options containing ‘nonask’, file_scan_directory() will add it to the $options array, see

// Merge in defaults.
$options += array(
 'nomask' => $no_mask,
 'callback' => 0,
 'recurse' => TRUE,
 'key' => 'uri',
 'min_depth' => 0,
);
+++ b/includes/theme.inc
@@ -1325,8 +1325,19 @@ function drupal_find_theme_templates($cache, $extension, $path) {
+  $files = file_scan_directory($path, $regex, array('key' => 'name', 'nomask' => $no_mask));

We do not need to change the code above. We do not know what side effects we might have by changing the call from drupal_find_theme_templates() to file_scan_directory().
Besides, if you look into drupal_find_theme_templates(), it does a call to file_scan_directory() anyway.
For the same reasons as I explained on part I, if file_scan_directory() gets called without $options[‘nomask’], it will use the default options.

Lastly, for consistency, added the default values to default.settings.php

$conf['file_scan_ignore_directories'] = array(
  'node_modules',
  'bower_components',
);

So the actual patch can be really small.
I am adding a new patch with some tests for it.

The patch will look a lot more similar to what has been done in Drupal 8 on https://www.drupal.org/node/2329453

Status: Needs review » Needs work

The last submitted patch, 27: ignore_node_module-2482549-27.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

marcelovani’s picture

Status: Needs work » Needs review

Unrelated failure

marcelovani’s picture

Status: Needs review » Active
marcelovani’s picture

Status: Active » Needs review
FileSize
5.16 KB

Re-submitting the patch

tariqinam’s picture

confirming that #31 patch works as expected.

Leksat’s picture

Status: Needs review » Reviewed & tested by the community

The patch works as expected! Looks pretty similar to what we have in D8.

It makes life a lot easier in case if local development uses NFS file sharing and npm is used by some modules/themes.

Pol’s picture

Patch is working fine, thanks, we really need this!

joelpittet’s picture

Issue tags: +Performance
David_Rothstein’s picture

Status: Reviewed & tested by the community » Needs work
  1. +    $ignore_directories = variable_get('file_scan_ignore_directories', array(
    +      'node_modules',
    +      'bower_components',
    +    ));
    

    Unless I'm missing something, that's a big difference compared to the Drupal 8 code - Drupal 8 uses an empty array as the default value (and therefore ensures backwards compatibility by not forcing the behavior to change for existing sites; see #2329453-150: Ignore front end vendor folders to improve directory search performance). Why wasn't that done here?

  2. +    array_walk($ignore_directories, function (&$value) {
    +      $value = preg_quote($value, '/');
    +    });
    

    This code will break on PHP 5.2.

    I think a simple foreach ($ignore_directories as &$ignore_directory) would probably be easier to understand anyway.

  3. +    });
    +    $default_nomask = '/^(\.\.?)|CVS|' . implode('|', $ignore_directories) . '$/';
    +  }
    +
       // Merge in defaults.
    .....
    @@ -2143,7 +2157,10 @@ function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
       $files = array();
       if (is_dir($dir) && $handle = opendir($dir)) {
         while (FALSE !== ($filename = readdir($handle))) {
    -      if (!preg_match($options['nomask'], $filename) && $filename[0] != '.') {
    +      if ($filename[0] != '.'
    +          && !(isset($options['nomask']) && preg_match($options['nomask'], $filename))
    +          && !(!empty($default_nomask) && preg_match($default_nomask, $filename))
    +          ) {
    

    This looks more complicated than necessary. Is there any reason not to just set $options['nomask'] in the top code, then not change the bottom code at all? The whole $default_nomask variable really doesn't seem like it needs to exist.

    (This suggestion applies to Drupal 8 too, I suppose.)

marcelovani’s picture

Good points.
We are using this patch on production on around 16 sites running on PHP 5.6 and the patch works very well.

1. It was assumed that by shipping it with defaults would instantly give the performance benefits to everyone without doing anything. Not all the users follow CHANGELOG to see what is new and what manual steps they need to do. Talking about this, I think we should update the changelog too.

2. Definitely missed that

3. I tried to keep the patch as close as possible to the D8 version, we could change the logic if necessary, but that would require further testings.

jenlampton’s picture

1) Changed the default to an empty array.

2) I took a stab at this but I'm not sure where $value was coming from in the previous patch. +1 to making this bit of code easier to understand.

3) Included an attempt to rework this section without $default_nomask to make it simpler.

Status: Needs review » Needs work

The last submitted patch, 38: core-ignore_node_module-2482549-38.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jenlampton’s picture

Patch in #38 applies cleanly to 7.59, but I'd love some help with the failing tests.

hkirsman’s picture

I wasn't noticing anything odd for a year until I installed Lando which is a bit slower on Mac because of virtualisation. admin/config page was loading for about 20-30 seconds. Blamed Lando and Docker and almost gave out.
Luckily first I discovered that there was 3 modules that where not available in file system anymore and this is known to clog the system. For this I found this fix:


$files = db_select('system', 's')
  ->fields('s', array('filename', 'status'))
  ->execute()
  ->fetchAll();
foreach($files as $obj) {
  if(!file_exists($obj->filename)) {
    drupal_set_message(t('@fn is missing.  Status is @stat.', array('@fn' => $obj->filename, '@stat' => $obj->status)), 'error');
    // Uncomment the following 2 lines to delete missing files from the system table.  Backup your DB first!
    //db_delete('system')->condition('filename', $obj->filename, '=')->execute();
    //drupal_set_message(t('@fn deleted from system table', array('@fn' => $obj->filename)));
  }
}

I got the speed down to about 10 seconds which was still slow. Did some profiling and found the file_scan_directory() function. It was listing thousands of folders inside one of my custom module I built a year ago - it was the node_modules folder. When I removed that then site/admin folder ran about 2 seconds in Lando. Yay!

My 2 cents for this issue would be to treat node_modules (and bower_components) as importantly as . and CVS folders these days. CVS is probably not as popular even as node_modules is. So why not just hard-code it next to the other folders?

'nomask' => '/(\.\.?|CVS)$/',
vs
'nomask' => '/(\.\.?|CVS|node_modules|bower_components)$/',

I mean nobody would want to have possibility to remove these. 99% would miss the configuration option if it was optional.

drupal@guusvandewal.nl’s picture

3 years later ...

DamienMcKenna’s picture

drupal@guusvandewal.nl: Instead of passive-aggressively complaining that the problem hasn't been fixed yet, maybe see if you can help resolve the problem, or fund time for someone else to fix it?

drupal@guusvandewal.nl’s picture

I already gave a solution 3 years ago when I opened this issue, and other people have provided patches as well. I was just wondering why they haven't been implemented.

https://www.psychologytoday.com/us/blog/passive-aggressive-diaries/20170...

marcelovani’s picture

We have been using patch #31 for a year and it works fine, I know that there were some comments and new patches after it, but I did not have time to work on this issue.
All I know is that the patch #31 works for us.

ufku’s picture

Status: Needs work » Needs review
FileSize
498 bytes

Here is a patch that simply makes the nomask regexp a variable.
This doesn't change any functionality and can at least be overridden.

hkirsman’s picture

What about adding |node_modules|bower_components to the defaults?

Pol’s picture

At European Commission we are using patch #31 since a long time without any single issue.

I'd like to push it forward, is everyone ok with this ?

Pol’s picture

Refactoring the patch using.

Adressing remarks from @David Rothstein and tests should be green now.

Status: Needs review » Needs work

The last submitted patch, 49: 2482549.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 51: 2482549.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 54: 2482549.patch, failed testing. View results

Pol’s picture

Status: Needs work » Needs review
FileSize
3.87 KB

Fix patch and tests.

The last submitted patch, 56: 2482549.patch, failed testing. View results

marcelovani’s picture

Can you please upload the interdiff between your patch and #31

Fabianx’s picture

  1. +++ b/includes/file.inc
    @@ -2130,9 +2130,30 @@ function file_download_access($uri) {
    +    $ignore_directories = array_map(
    +      function ($value) {
    +        return preg_quote($value, '/');
    +      },
    +      variable_get('file_scan_ignore_directories', array())
    +    );
    

    I think a simple loop is easier to read than using array_map here.

  2. +++ b/includes/file.inc
    @@ -2130,9 +2130,30 @@ function file_download_access($uri) {
    +    if ($ignore_directories !== array()) {
    +      $nomask = '/^(\.\.?)|CVS|' . implode('|', $ignore_directories) . '$/';
    +    }
    

    if (!empty($ignore_directories)

    makes more sense here ...

  3. +++ b/sites/default/default.settings.php
    @@ -645,3 +645,17 @@ $conf['404_fast_html'] = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
    +$conf['file_scan_ignore_directories'] = array(
    +  'node_modules',
    +  'bower_components',
    +);
    

    This will need a change record.

    Also:

    How was this solved in D8? Is it configurable there?

Pol’s picture

FileSize
5.46 KB
3.64 KB

A bit of code tidying and the interdiff.

marcelovani’s picture

@Fabianx via settings.php https://www.drupal.org/node/2329453

Status: Needs review » Needs work

The last submitted patch, 61: 2482549.patch, failed testing. View results

Pol’s picture

Status: Needs work » Needs review
FileSize
6.17 KB
4.35 KB

Updated patch based on @fabianx's feedback.

Interdiff with #31 has been uploaded as well.

In Drupal 8, this has been fixed 2 years ago in the same way (see #2329453: Ignore front end vendor folders to improve directory search performance).

Fabianx’s picture

This will need a change record for the new variable in default.settings.php (the one from D8 can likely be copied).

It will also need special attention in release notes

Pol’s picture

Updated patch, remove "out-of-scope" improvements I introduced and leave only the strict necessary.

Interdiff with #31 included.

Working on the Change Record now.

Pol’s picture

Issue tags: -Needs change record

Change record: https://www.drupal.org/node/3024333

Please review it and provides some feedback.

Pol’s picture

Issue summary: View changes
Fabianx’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Pending Drupal 7 commit

Looks good now

Pol credited JohnAlbin.

Pol credited SebCorbin.

Pol credited geerlingguy.

Pol credited pablo.guerino.

Pol credited afoster.

  • Pol committed a098392 on 7.x
    Issue #2482549 by Pol, marcelovani, ndf, drupal@guusvandewal.nl,...
Pol’s picture

Fixed!!!

Thanks everybody !!!

DamienMcKenna’s picture

Status: Reviewed & tested by the community » Fixed
TR’s picture

Status: Fixed » Needs work
FileSize
842 bytes

C'mon guys, you can't use short array syntax in Drupal 7. Whatever happened to testing things before committing?

See https://www.drupal.org/pift-ci-job/1166553

This commit broke ALL PHP 5.3 tests for both core and contrib.

TR’s picture

Priority: Normal » Critical
Status: Needs work » Needs review

https://www.drupal.org/core/issue-priority

Critical bugs include those that:

  • Cause tests to fail in HEAD on the automated testing platform for any supported environment (including random failures), since this blocks all other work.
Liam Morland’s picture

PHP 5.3 isn't supported anymore.

Update: My comment is based on what Dries wrote about PHP 5. I note that the PHP requirements page doesn't match this.

In any case, it's not worth breaking compatibility over short array syntax.

TR’s picture

@Liam Morland: That's a ridiculous and inaccurate claim. Drupal 7 supports PHP >= 5.2.4 and PHP 5.3 tests are a standard part of core and contributed testing. If it weren't for this one little [] instead of array() introduced by the above patch, all these tests would still be running.

markhalliwell’s picture

Status: Needs review » Reviewed & tested by the community

Instead of adding fuel to the flames... how about an RTBC?

markhalliwell’s picture

FTR, 7.62 hasn't been tagged yet... so there's still time.

Great catch @TR.

andypost’s picture

+1 to commit follow-up fix

  • Pol committed a15eff0 on 7.x
    Issue #2482549 by Pol, marcelovani, ndf, drupal@guusvandewal.nl, TR,...
Pol’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: -Pending Drupal 7 commit

Fixed.

markhalliwell’s picture

Status: Fixed » Closed (fixed)

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

xlin’s picture

This commit seems change the default.settings.php permission from 644 to 755. Anyone know why that is the case?

Liam Morland’s picture

I don't see anything in this commit that would touch default.settings.php at all. It must be something else.

jacob.embree’s picture

jacob.embree’s picture