Proposed commit message (D7)
Issue #1081266 by stefan.r, jeroen.b, mikeytown2, tsphethean, mfb, joseph.olstad, marcelovani, Fabianx: Avoid re-scanning module directory when a filename or a module is missing
Problem/Motivation
Drupal occurs a performance deficit when multiple filenames (ie. modules, themes) are missing or moved. The deficit occurs because we do a file scan perpetually to look for missing modules on certain pages, such as admin/config.
Proposed resolution
For D8: We remove the "missing file scan" that happens in drupal_get_filename()
altogether and do various workarounds to deal with this scan in places where we haven't cached file locations yet, such as in the installer and in some tests (see #351). Instead of a file scan, we now do a trigger_error()
, informing the developer/administrator about the missing file.
For D7: We cache any missing files in a static variable as well as in cache_bootstrap (for 24 hours, or until we visit the themes/modules overview page). We also do a trigger_error() as soon as we find a missing file in the drupal_get_filename() call.
In a followup issue we can check for this in hook_requirements() as well.
Remaining tasks
None.
User interface changes
None.
API changes
In D8, we remove the file scan from drupal_get_filename(), which also affects drupal_get_path(). The lack of this is only a "problem" in the installer, in tests and during module rebuild, which are not part of the API.
Original report by mfb
This patch provides a performance boost for sites that are missing modules (common on old sites which have been maintained for many years). In this case, drupal_system_listing() and file_scan_directory() will be called over and over looking for missing modules on certain pages, such as admin/config.
This patch adds an additional static variable to drupal_get_filename() to store an array of mask-directory combinations that have been scanned. Once one scan has been performed for a particular extension in a particular directory, there is no need to run the same scan later in the same request.
This information will be cached in 'drupal_get_filename:missing' in cache_bootstrap as well. Invalidation of this cache happens upon running cron, and upon visiting the themes/modules overview pages.
Beta phase evaluation
Issue category | Bug because significant performance slowdown | |
---|---|---|
Issue priority | Major because significant performance slowdown. | |
Prioritized changes | The main goal of this issue is performance | |
Disruption | N/A |
Comment | File | Size | Author |
---|---|---|---|
#611 | interdiff.txt | 12.78 KB | David_Rothstein |
#611 | 1081266-611.patch | 39.79 KB | David_Rothstein |
#611 | 1081266-546-plus-new-tests-should-fail.patch | 36.87 KB | David_Rothstein |
#606 | interdiff-546-606.txt | 3.24 KB | David_Rothstein |
#606 | 1081266-606.patch | 29.72 KB | David_Rothstein |
Comments
Comment #1
catchI can't remember which issue this came up in, (edit: now I can, it was #752730: Remove file_exists() during bootstrap) but
someonemoshe mentioned we should probably disable modules that are missing if we find they don't exist. Old core modules like ping that were removed, contrib modules that are deleted rather than uninstalled, there are lots of ways this happens.If we did that, are there still circumstances this could run more than once? Also even if we do go for disabling those, I'm split on whether it should be a one-off or built in somewhere.
Comment #2
mfbDisabling modules would not address this issue. system_status() calls drupal_load_updates() which calls drupal_get_installed_schema_version() which loads everything of type module from the system table, regardless of status. module_load_install() is called as long as the schema version is 0 or higher, even if the module is disabled.
Comment #3
sun1) Let's move the variable assignment right below the conditional check, so the purpose of the conditional as well as the assignment is clear.
2) Wouldn't it make more sense to flip $dir and $extension in the array? And when considering that, wouldn't $dirs be a better name than $scanned? Perhaps not, just an idea.
Powered by Dreditor.
Comment #4
mfbOK changed to $dirs and moved that line (hope $dirs variable is ok coding standards wise).
Comment #5
sunMy brain is filled up with other stuff right now, so just throwing the idea out there: Any remotely potential issues if $dir == $extension (say, 'inc') ?
Also, we should open a separate issue to fix the root cause; namely, fixing {system}.status columns on a certain page/callback.
Comment #6
sunSee #1096742: Disable enabled but missing modules
Comment #7
mfb$dir should be one of themes, themes/engines, modules, profiles and $extension should be one of info, engine, module, profile. But I don't think there'd be any problem if $dir == $extension.
As mentioned in #2, this code is running for all modules, disabled and enabled, so disabling missing modules wouldn't help here.
Comment #8
Damien Tournoud CreditAttribution: Damien Tournoud commentedComment #9
mfbHere's the D8 version.
Comment #10
Peter Bex CreditAttribution: Peter Bex commented+1 on this patch, this can really slow down some sites!
Comment #11
mfbhopefully "+1" means you reviewed the patch? it's not totally clear :)
Comment #12
Peter Bex CreditAttribution: Peter Bex commentedNo, I just wanted this bug to get fixed. Didn't get around to testing it. Sorry :)
Comment #13
das-peter CreditAttribution: das-peter commentedCould this issue be related to this one #733308: Add static caching for drupal_system_listing()?
Comment #14
mfbRelated, but this is a higher-level fix to add (more) static caching to drupal_get_filename() so as to avoid unnecessary calls to drupal_system_listing() altogether.
Comment #15
Mark Theunissen CreditAttribution: Mark Theunissen commentedI've tested the patch and it works - the speed increase is major for my upgraded site that has 17 orphaned module entries in system table (I will be removing those).
The patch provides a working solution, but I wonder if it would be better to implement #733308: Add static caching for drupal_system_listing() as that adds a static cache to drupal_system_listing(), which will catch other places this may crop up. Once that is in place, I think the performance increase by doing this high level fix will be quite small.
Comment #16
mfbHmm #733308: Add static caching for drupal_system_listing() seems like redundant static caching of a large array, when drupal_get_filename() already has a static cache of $files found via drupal_system_listing()?
Comment #17
corbacho CreditAttribution: corbacho commentedI think this bug is more important than it seems.
I had only 1 orphaned module in a brand new Drupal 7 site we are building. (It was a feature-module that was removed from our git repository without proper uninstall)
I discover this bug only because I was profiling it, so I can give you some real data:
BEFORE:
The frontpage load taking 20 secs.
"file_scan_directory" was executed 1162 times. Taking 9 seconds in total
*Every single* page load.
AFTER:
Frontpage load taking 7 secs.
'file_scan_directory' wasn't executed at all.
(The load times are quite increased because of the profiler tool. Without profiling, it takes about half of the time in both cases)
So, especially if you have a lot of folders it's really expensive. In my case was even worse because I placed CKEditor source code folder inside the module folder. It has 238 folders. I have put it now in /libraries/ instead. (This reduced file_scan_directory to 868 calls, 7 secs in total)
But I think it's quite easy to end up in this situation and very difficult to realize for normal users, unless you have PHP debugging tools.
Comment #18
corbacho CreditAttribution: corbacho commentedThat dramatic change of speed was due of my own debug patch:
If ($name == orphaned module) return;
But with the patch of #4 still file_scan_directory is called 434 times taking 4 secs.
The problem is that those 4 secs are in *every* single page load.
Comment #19
mfbYou might want to figure out why the scan is happening on every page load. Theoretically it should only happen on certain paths, such as admin/config which calls system_status().
Comment #22
MaxWesten CreditAttribution: MaxWesten commented#4 is a drastic speed improvement.
Comment #23
Kars-T CreditAttribution: Kars-T commentedSubscribe. This is really needed. The Problem killed 80% performance of 2 our D7 sites.
Comment #25
Kars-T CreditAttribution: Kars-T commentedI did test and profile this on a major site of ours. The bug killed the performance and every page call did use 100% CPU power and took around 30 seconds. With suns query fix from #1096742: Disable enabled but missing modules everything runs fine again CPU usage is very low again. But still file_scan_directory eats up 16% of a page-call. With this patch it goes down to 9%. Still a lot but much nicer and there shouldn't be any endless recursion because the directories are scanned over and over again .
The patch itself is very simple. Just one more static variable. And than a check if the directory was already parsed so it is not checked again for this page-call. I say RTBC.
Comment #26
das-peter CreditAttribution: das-peter commented+1 for getting this in asap. We use this patches (this one too: #733308: Add static caching for drupal_system_listing()) in production, too. And everything works like a charm.
Comment #27
webchickSince this is a performance patch, and since catch already chimed in above, moving to his queue.
Comment #28
alanburke CreditAttribution: alanburke commentedTested patch at #4
Worked and made a dramatic difference on a site.
Comment #29
catchI'd like to see the following before committing this:
Profile a site where this bug is in effect, then compare:
No patch.
This patch.
This patch and #733308: Add static caching for drupal_system_listing().
#733308: Add static caching for drupal_system_listing() without this patch.
Screenshots from either xdebug or xhprof will do fine - can show detail from drupal_system_listing() or file_scan_directory() whichever is most obvious.
Comment #30
Kars-T CreditAttribution: Kars-T commentedSure I do this :)
Okay I did
drush dl devel feeds job_scheduler
drush en devel feeds feeds_ui job_scheduler -y
and than deleted those modules.
With latest 7.8 bootstrap the load of "/" takes around 20 seconds.
With patch from 1081266#9 the load of "/" takes around 3 seconds.
With patch from 1081266#9 and 733308#8 the load of "/" takes around 3 seconds.
With patch from 733308#8 the load of "/" takes around 3 seconds.
Performance seems to be equal with any patch. But lowest usage of file_scan_directory() is with both. For D8 I'd say test peters a bit more and add it. BUt for D7 I would really love to see the patch from this issue in the next release. If you reorganise features your site will be dead in the current state.
Comment #31
catchThanks for the profiling, that means there's no reason to have both patches then.
#733308: Add static caching for drupal_system_listing() is the older issue but I've marked that duplicate.
However to me it makes more sense to add the static caching to either drupal_system_listing() or file_scan_directory() - since that's called by more places (such as the installer which was the original motivation for opening that issue).
Comment #32
mfb@catch: since drupal_get_filename() already has a static cache of $files found via drupal_system_listing(), are you suggesting moving the static cache from drupal_get_filename() to drupal_system_listing()?
Comment #33
catch@mfb, the drupal_get_filename() cache is primed by http://api.drupal.org/api/drupal/includes--module.inc/function/module_li... on every request, so removing the cache from there isn't an option. The way that cache is primed is a bit odd so we could consider trying to clean that up elsewhere.
Since drupal_system_listing() is only really called via this error condition or otherwise very rarely it seems fine to have the static cache in two places to me - in the usual case they won't both be used at the same time.
Comment #34
Kars-T CreditAttribution: Kars-T commented@catch So is this issue again RTBC or what should be done?
Comment #35
klausiJust ran into this myself, this a huge performance show stopper. Escalating to critical. Patch from #9 still applies. Marking for review.
Comment #36
Kars-T CreditAttribution: Kars-T commentedThan again I set this to RTBC. But for D8 I would say it should follow the more complex path that das-peter showed in #733308: Add static caching for drupal_system_listing(). For D7 I say this is essential or any bigger system that uses features will die eventually. D7 really needs some kind of hot fix.
Comment #37
Dries CreditAttribution: Dries commentedCommitted to 7.x and 8.x. Thanks!
I'd still like to figure out why we keep looking for these files on every request. It feels like we patched the problem vs eliminated the root cause. I've updated the priority and status accordingly. Hopefully we can continue to investigate this.
Comment #38
das-peter CreditAttribution: das-peter commentedThanks & I fully agree, working on this felt always like patching a symptom and not the cause.
While the static cache is a good thing anyway, I've been asking myself if it would be a good idea to disable missing modules and flag them as broken. Idea behind this is that I would like to know if there's anything wrong with my setup.
Next question would be what's considered as broken?
Only if the full module is missing or even if registered files of a module are missing?
What about a consistency check on the module page or an extra report page?
Comment #39
Kars-T CreditAttribution: Kars-T commentedsystem_list() is called on every bootstrap. But there is only a cache for the database table. The directories will be scanned on every call.
So is there any reason why we couldn't put the result from the loop in line 185 into the cache?
Only thing I currently can think of we will have a problem if a module is gone missing.
Comment #40
flock CreditAttribution: flock commentedDruapl 7.9
Hi, i'm new to drupal and I believe I am suffering from poor performance because of this same reason.
on my site I keep getting Internal Server errors, not enough memory errors and My webhosting company tells me I have to upgrade to the $19.99 per month plan in stead of shared hosting [thanks greengeeks the super useful drupal hosts]
Also, images seem to drop off
Site Logo
Captcha Image
Views gallery - of small rotating logos
-this curiously only on Mac Safari and Mac Firefox
Can any one please instruct me or direct me as to how I can apply or where exactly do I execute that snippet of code so I can find out what modules are causing the super slow performance?.
This is the only post that I found that had a seeming relationship to my problem, my apologies if this is not the right place to post it.
Many Thanks
Comment #41
oriol_e9gFirst we have to solve this in D8. The most important problem here was solved in D7 and commited in October.
I think that your problem is a suport request, open a new issue.
Comment #43
bmateus CreditAttribution: bmateus commentedI've patched my bootstrap with patch #4 and the performance increase is notorious.
Any idea when this might be updated on a D7 official version?
I think it is very important, as most D7 sites I've built all suffer from the same lack of performance.
Comment #44
klausihttp://groups.drupal.org/node/203508
Comment #46
Tor Arne Thune CreditAttribution: Tor Arne Thune commentedComment #47
bensnyder CreditAttribution: bensnyder commentedStop subscribing, start following
Comment #49
onelittleant CreditAttribution: onelittleant commentedWhat would the negative implications be of just eradicating invalid system table entries? Perhaps provide an indication on the module / theme / status report admin screens indicating references to modules or themes that don't exist and providing a button to simply delete the references.
http://drupal.org/node/1052250#comment-5113888
Comment #50
onelittleant CreditAttribution: onelittleant commentedYou can try this if the problem is related to system table references to modules that don't exist:
http://drupal.org/node/1052250#comment-5113888
Comment #51
emcniece CreditAttribution: emcniece commentedPretty worried about this. I've followed all the links here, done all the patches to 10 different installations of D7 in different combinations, and the only conclusion I can draw is that I simply have too many modules installed.
Initially, page load times were upwards of 70 seconds and file_scan_directory() was being called over 10,800 times each page view. Since updating to 7.x-dev and monkeying around with reducing the number of available modules, I've got this down to ~1500 calls but the page load is still around 16 seconds.
If it helps anyone, I initialized a variable at the top of /includes/file.inc (outside any function, as a global):
$ctest = 0;
and then threw at the bottom of file_scan_directory():global $ctest; echo $ctest++. " $depth: $dir<br />\n";
My site has 77 modules installed on top of core, with 37 available but not installed. Is this simply too complex for Drupal?
Comment #52
catchYou should do a debug_backtrace() from file_scan_directory() to see what's calling it. It shouldn't be called during normal operation at all, but it can be if you have a badly behaving contrib module installed.
I've seen sites with upwards of 300 modules installed, 100 is unfortunately not unusual at all.
Comment #53
emcniece CreditAttribution: emcniece commentedDUDE, catch... THANK YOU for the nudge!! I rolled down that path earlier but abandoned when I couldn't get any meaningful data out of debug_backtrace(). Well this time I found the right output - and here's the results:
Crazy. What does that even mean, rogue menus? I'm looking at menu_execute_active_handler right now.
For reference, here's what the last few lines of my file_scan_directory() now looks like:
Any ideas by chance?
Comment #54
catchYou're looking at the first function in the backtrace, instead find file_scan_directory() in the full backtrace then work up from there - the first couple of functions you find outside file.inc are a good place to concentrate on.
Comment #55
emcniece CreditAttribution: emcniece commentedDear catch: today I learned things.
debug_backtrace()
is super handydrupal_flush_all_caches()
callsFor anyone else:
drupal_flush_all_caches()
slows sites down BIGTIME and seems to initiate a good number of file_scan_directory() calls through whatever cache-clearing things it does. This didn't really have much to do with the actual thread topic, but the directory scans instead turned up as a symptom. Thanks again for your help.Comment #56
sertaconay CreditAttribution: sertaconay commenteddrupal_get_filename.patch queued for re-testing.
Comment #57
ExTexan CreditAttribution: ExTexan commentedWhen I went to apply this #9 patch (manually) in D7, I saw that the *new* code is already in the version of D7 I'm running (7.14), but I'm still experiencing very slow initial loading of my front page. This is running locally on a virtual host in Win7 with WAMP. After the initial load time of 15-20 seconds, my pages load within a second or two thereafter.
Anyone have any suggestions of what else to try / look for?
Comment #58
mikeytown2 CreditAttribution: mikeytown2 commented@sertaconay
There is no active patch above to review, Dries in #37 committed patches to D7 (#4) & D8 (#9).
I've attached a patch for 7.x that fixes this issue for us. Once testbot gives the A-OK, go ahead and move the issue back to 8.x.
Comment #59
DerekL CreditAttribution: DerekL commentedWow.
The above patch just turned my glacial D7.14 ZWAMP dev env into a rocketship.
You all deserve beers, thanks.
Comment #60
mikeytown2 CreditAttribution: mikeytown2 commentedPatch for D8 that should fix this issue as well.
Comment #62
mikeytown2 CreditAttribution: mikeytown2 commentedUsing the D8 caching interface now :)
Comment #64
mikeytown2 CreditAttribution: mikeytown2 commentedWhat happened to CACHE_TEMPORARY?
http://drupalcode.org/project/drupal.git/blob/58964deb124e55d61f51a167d5...
API says it's still in there: http://api.drupal.org/api/drupal/core!includes!bootstrap.inc/constant/CA... and is referenced here http://api.drupal.org/api/drupal/core!lib!Drupal!Core!Cache!CacheBackend...
Going to re-roll patch with -1 instead of CACHE_TEMPORARY as a temp solution to this problem.
Comment #66
aspilicious CreditAttribution: aspilicious commented#730060: Replace CACHE_TEMPORARY, cache_clear_all() and minimum cache lifetime with cache tags support
The api docs have troubles with new commits. It can take weeks before it gets refreshed.
Comment #67
mikeytown2 CreditAttribution: mikeytown2 commentedSo I need to add in 'drupal_get_filename' to the list of caches getting reset in system_list_reset(), or is there a better way to do this?
Comment #68
tsphethean CreditAttribution: tsphethean commentedI came across this issue when looking at why D6 was querying the DB on every module_load_include call and thinking of how to mitigate.
My understanding of the issue is that it fixes the edge case where the DB isn't available to lookup the module path (or its not specified in system table), and the module directory no longer exists
Is there a reason why even with the improved D7/8 drupal_get_filename doing file scans and then caching the list of bad files, why we wouldn't add the list of found files to a cache table for retrieval by future calls? I guess it would need profiling, and maybe be a configurable option depending on what caching mechanism was being used, but would looking up a value from a cache not be more efficient across requests? My thought is that on a production site the files aren't likely to change much in between requests, so the efficiency gains could be quite sizeable?
If this feels like getting off topic (or has been raised as another defect elsewhere) I'm happy to move it out of this issue and create a new one, and equally happy to attempt some work on a patch if it feels like a valid way of thinking.
Comment #69
tsphethean CreditAttribution: tsphethean commentedOk, so I've had a go based on my comment in #68 and @mikeytown2's good work in #64 at creating a cache which stores the "good" cache entries, and an additional cache for the "bad" or missing files being included.
I need to do more profiling to determine if there is any performance benefit of this cache on a vanilla installation, and also with alternative cache backends, but thought it best to see what testbot and others think.
My working assumption is that retrieving from the cache should always be more optimal than a db query even when the cache is in the db, (flawed assumption?) but that the benefits of this would be felt in complex sites with 100+ modules, particularly custom modules, with dedicated caching solutions.
Comment #71
tsphethean CreditAttribution: tsphethean commentedHm. Not sure I quite understand this. Output from the test failure is:
but that doesn't appear (from what I can figure out) to be anything that calls or is related to the drupal_get_filename function which is being modified.
Any suggestions welcome (not done much of this so still getting to grips with testbot)
Comment #72
tsphethean CreditAttribution: tsphethean commented#69: drupal_get_filename_cache-1081266-69.patch queued for re-testing.
Comment #74
tsphethean CreditAttribution: tsphethean commentedAh - error in the variable name I was saving in the cache set following the file scan.
This now passes the failing test in my local environment now so resubmitting.
Comment #77
tsphethean CreditAttribution: tsphethean commented#74: drupal_get_filename_cache-1081266-74.patch queued for re-testing.
Comment #79
tsphethean CreditAttribution: tsphethean commentedDefinitely confused by these test failures. All upgrade path tests pass on my local site. Does anyone else get any failures from this patch?
Comment #80
tsphethean CreditAttribution: tsphethean commentedUpdated patch - passes tests locally.
Comment #83
disasm CreditAttribution: disasm commentedComment #84
corbacho CreditAttribution: corbacho commentedRelated ?
http://drupal.org/project/bootstrap_optimizer
Comment #85
andypostAnother solution http://drupal.org/project/clean_missing_modules
Comment #86
SpleshkaI think that every time when admin submits module list, Drupal should check all enabled modules and clean up orphaned file names from {system} table.
Comment #87
mikeytown2 CreditAttribution: mikeytown2 commentedThe patch for D7 works great, the D8 patch has issues. Thus I'm changing the tags to show this.
Comment #88
SpleshkaD7 patch looks good to me too.
Comment #89
mgifford#80: drupal_get_filename_cache-1081266-80.patch queued for re-testing.
Comment #90
mgiffordIf you use http://drupal.org/project/clean_missing_modules then this shouldn't be a problem right?
The patch in #80 is for D7 or D8? Just the Version is for D8, but people are talking about applying this to D7.
@tsphethean any direction on where this should be applied? It can't be for both D7 & D8.
Comment #92
tsphethean CreditAttribution: tsphethean commentedSorry, yes - #80 is a D8 patch but is pretty old so might need a re-roll.
Comment #93
tsphethean CreditAttribution: tsphethean commentedTrying a re-roll against latest D8 build.
Comment #95
tsphethean CreditAttribution: tsphethean commentedFixing whitespace issue which prevented patch applying.
Comment #98
tsphethean CreditAttribution: tsphethean commentedOk think I've made a mess of my local site - rebuilding and will re-roll the patch by hand tomorrow.
Comment #99
tsphethean CreditAttribution: tsphethean commentedHaving a bit of a look at the revised drupal_get_filename in D8 I'm actually having a few second thoughts about my original approach. We're now doing:
$file_list = state()->get('system.' . $type . '.files');
To get a list of all the files in the system, and then building up the array. Not knowing too much about the new state system, is this going to be DB level state in the first instance, so would benefit from the eventual $files array being cached, or is state going to be cached anyway by the state interfaces?
Grateful for pointers... happy to proceed if it seems worthwhile. Still getting to grips with state, config and things :-)
Comment #100
Anonymous (not verified) CreditAttribution: Anonymous commentedre caching in #99 - #1786490: Add caching to the state system.
Comment #101
Anonymous (not verified) CreditAttribution: Anonymous commentedOutput of dry-run 1081266-58-drupal_get_filename-D7.patch on Drupal 7.18
patch --dry-run < bootstrap.patch
patching file bootstrap.inc
Hunk #1 succeeded at 816 (offset -27 lines).
Hunk #2 succeeded at 867 (offset -27 lines).
patch unexpectedly ends in middle of line
Hunk #3 succeeded at 895 with fuzz 1 (offset -27 lines).
Comment #102
mikeytown2 CreditAttribution: mikeytown2 commentedRe-roll of #58 against the latest D7 dev
Comment #103
mikeytown2 CreditAttribution: mikeytown2 commentedGoing to test #102 on D7 via testbot. Will move issue back to D8 once patch goes green.
Comment #104
mikeytown2 CreditAttribution: mikeytown2 commented#102 Passed all tests.
Moving back to 8.x and needs work.
Comment #105
Anonymous (not verified) CreditAttribution: Anonymous commentedpatch --dry-run < bootstrap.patch
patching file bootstrap.inc
Hunk #1 succeeded at 816 (offset 1 line).
Hunk #2 succeeded at 867 (offset 1 line).
patch unexpectedly ends in middle of line
Hunk #3 succeeded at 895 with fuzz 1 (offset 1 line).
After that, used TextWrangler to ´normalize´ line endings, same result.
Comment #106
mikeytown2 CreditAttribution: mikeytown2 commented@StinkFoot
I think you need to use
-p1
with your patch commands.Dry run:
Apply:
Comment #107
Anonymous (not verified) CreditAttribution: Anonymous commented@Mikeytown2
Thanks for your support!
After reading what the option does, I´ve looked inside the file and - please correct me if I´m getting this wrong - I see that patch will be looking at
a/includes/bootstrap.inc
b/includes/bootstrap.inc
Patch documentation reads:
So, NUM would be 3.
Omitting
-p
should result in patch using the final component and since I runpatch
in the same directory, that should work.I tried
p0
up top4
and get the same result every time, so... I am not to sure about your suggestion.But, always eager to learn, so please enlighten me if I don´t know what I am doing:-)
Merry Xmas!
Update:
After reading my own comment, decided to test the procedure by creating the directories and try to apply the patch from their mutual parent.
Failed.
Comment #108
Lukas von BlarerI am running the patch in #102 in production since today and I am experiencing an insane improvement. My page load times went from 2200ms down to 500ms! So far I am not facing any problems. Thank you!
Comment #109
PlayfulWolf CreditAttribution: PlayfulWolf commentedIs this patch already applied on last D7?
Comment #110
mikeytown2 CreditAttribution: mikeytown2 commented@PlayfulWolf
Don't think so. https://api.drupal.org/api/drupal/includes!bootstrap.inc/function/drupal...
D7 patch #102 is RTBC, D8 is still needs work.
Comment #110.0
mikeytown2 CreditAttribution: mikeytown2 commentedCreated an issue summary.
Comment #111
nbourdial CreditAttribution: nbourdial commentedHi,
any idea when the #102 patch will be commited and released to the next D7 release ?
For info, the patch saved 3.5sec for each page load (7sec -> 3.5sec), for a Drupal instance with about 250 modules enabled !
Thanks for the patch anyway !
Comment #112
oriol_e9gOnly for information, I'm using this code since D6 to remove all missing files from system table.
Why we can't add a similar cleanup process every time we launch update.php?
Comment #113
nicholas.alipaz CreditAttribution: nicholas.alipaz commentedI couldn't read through all of the over 100 posts here, but I thought that I would share how I was able to determin which missing modules were causing this issue on our server.
Where PLATFORMDIRECTORY = '/var/www/drupal' and SITEDIRECTORY = 'example.com' or possibly 'all' depending on your setup.
Running that from cli will give you the name of any missing files and the status of the module that is missing. Example output:
As you can see I found my module causing the issue.
Comment #114
mikeytown2 CreditAttribution: mikeytown2 commentedAdding tag to show that we have a RTBC 7.x patch in #102 but not a 8.x patch.
Comment #115
ChristophWeber CreditAttribution: ChristophWeber commentedApplied #102 to 7.19 core, which has 148 enabled modules per drush pm-list, and runs on a well tuned cluster. We do not have any missing modules or files AFAIK.
Patch works well, no issues, but also no real speed improvement.
Comment #116
mikeytown2 CreditAttribution: mikeytown2 commented@ChristophWeber
The speed improvement only happens if you have a missing module. Thanks for the info on the 7.x patch though.
Comment #117
marcelovaniI have a situation where an empty string is passed as parameter $name, which causes some extra loops for no reason.
This is also causing this error message:
See a proposed fix here https://www.drupal.org/node/2383823#comment-9385897
I applied both patches (issue 9385897 and issue 9386005) and I can see they both work well together.
ps: I am using Drupal 7.34
Comment #118
jeroen.b CreditAttribution: jeroen.b commentedThis issue is more relevant than ever, see the related issue.
I'll re-roll and test this patch soon.
Comment #119
mikeytown2 CreditAttribution: mikeytown2 commented#102 still applies with an offset of 21 lines and is RTBC from my understanding. In order for this to get into D7 a patch for D8 needs to be created OR it needs to be proven that this is fixed or is no longer relevant in D8.
Comment #120
jeroen.b CreditAttribution: jeroen.b commentedAh okay, thats good.
Checked in D8 beta 3, still seems relevant. Code got a rewrite but I think it still has the same issue.
New patch attached, created from D8 git head.
Comment #123
jeroen.b CreditAttribution: jeroen.b commentedHmm, guess watchdog isn't always available at that point. Any suggestions?
Comment #124
mikeytown2 CreditAttribution: mikeytown2 commentedhttp://www.appneta.com/blog/drupal-8-watchdog/
Drupal::logger() is the way of the future; but your patch already uses that.
Drupal::logger() -> LoggerTrait::error() -> DbLog::log() which writes to the watchdog table. Having an error here seems like a separate issue.
The profile being missing has to do with the code in ExtensionInstallStorage::getAllFolders(); it calls drupal_get_profile() which returns NULL and then InstallStorage::getComponentNames
('profile', array(NULL)) is executed resulting in the error.
I've modified your patch in #120 so it won't call getComponentNames on a null array and I've also incorporated the fix for this from #2383823: drupal_get_filename() should check if $name is empty
Comment #126
mikeytown2 CreditAttribution: mikeytown2 commentedLine 28 is where it went fatal and it's the function definition.
https://api.drupal.org/api/drupal/core!modules!system!src!Tests!Bootstra...
Not sure where to go from here
EDIT:
Running the test locally I get
ResponseText:
( ! ) Fatal error: Call to a member function get() on a non-object in /core/lib/Drupal.php on line 262
Call Stack
#TimeMemoryFunctionLocation
10.0010150168{main}( )..\index.php:0
20.16301262768Drupal\Core\DrupalKernel->handle( )..\index.php:24
30.33404883904Stack\StackedHttpKernel->handle( )..\DrupalKernel.php:579
40.33404884008Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle( )..\StackedHttpKernel.php:23
50.33404884048Drupal\Core\StackMiddleware\PageCache->handle( )..\ReverseProxyMiddleware.php:58
60.37005466664Drupal\Core\StackMiddleware\KernelPreHandle->handle( )..\PageCache.php:52
70.42908183680Symfony\Component\HttpKernel\HttpKernel->handle( )..\KernelPreHandle.php:53
80.42908183928Symfony\Component\HttpKernel\HttpKernel->handleRaw( )..\HttpKernel.php:66
90.636011414640call_user_func_array
( )..\HttpKernel.php:145
100.636011414864Drupal\system\Controller\BatchController->batchPage( )..\HttpKernel.php:145
110.637011472440_batch_page( )..\BatchController.php:60
120.649012191896_batch_do( )..\batch.inc:83
130.649012192056_batch_process( )..\batch.inc:101
140.653012232128call_user_func_array
( )..\batch.inc:256
150.653012232264_simpletest_batch_operation( )..\batch.inc:256
161.880113557656Drupal\simpletest\TestBase->run( )..\simpletest.module:320
173.962217933560Drupal\system\Tests\Bootstrap\GetFilenameUnitTest->testDrupalGetFilename( )..\TestBase.php:869
183.972217980328drupal_get_filename( )..\GetFilenameUnitTest.php:39
193.972217980616Drupal::cache( )..\bootstrap.inc:270
Will work on this some more.
Comment #127
mikeytown2 CreditAttribution: mikeytown2 commentedOK story is the testing framework sets https://api.drupal.org/api/drupal/core!lib!Drupal.php/function/Drupal%3A... to NULL. This causes all calls to the Drupal class to fail it seems; thus I feel like I'm missing something. I've edited the code to work around this so the tests pass.
https://api.drupal.org/api/drupal/core!tests!Drupal!Tests!UnitTestCase.p...
Comment #129
jeroen.b CreditAttribution: jeroen.b commentedLooks like a weird workaround. Are you sure it's the testing framework that sets it to NULL?
Maybe it's being called at a point where the Drupal class isn't initialized yet. I did not look that deep into D8 yet though so would have to do some more searching.
Comment #130
mikeytown2 CreditAttribution: mikeytown2 commentedNot 100% sure but according to the docs that is what it does
Comment #131
mikeytown2 CreditAttribution: mikeytown2 commentedBrought this up in IRC and the workaround I created for this seems to be ok.
"The idea with that test is to ensure that drupal_get_filename() works in the bootstrap process in which the container is not there for a purpose." - dawehner
I'll document this so people know why we're checking types.
Comment #132
mikeytown2 CreditAttribution: mikeytown2 commentedAdded this comment to the code
In early stages of the bootstrap process the container may not be set inside of the Drupal class.
Comment #135
Anonymous (not verified) CreditAttribution: Anonymous commentedpassing NULL/FALSE/'' to method_exists is safe, so this:
can be shortened to:
this check:
will be true over and over again there's no data in
$cache_object->get('drupal_get_filename:bad', TRUE);
, which doesn't seem to fit the intention of the static. better to initialize the static as $bad as NULL, and check for is_null.Comment #136
marcelovaniI can't see any problem in doing extra checks. Is that about having extra bytes in the code? making the execution slower?
We should seriously focus on getting this fix applied rather than debating what is better.
BTW, nothing can be worse than the current implementation.
Comment #137
jeroen.b CreditAttribution: jeroen.b commentedI think @beejeebus is right though. Better fix it properly now.
You don't want to execute an SQL query (for a cache lookup) every time you hit that piece of code.
And extra checks can really make your code more unreadable, so why add extra checks when you don't need them?
Comment #138
jeroen.b CreditAttribution: jeroen.b commentedAnd let's fix that mistake in the 7.x patch too while we're at it.
Comment #140
jeroen.b CreditAttribution: jeroen.b commentedComment #142
jeroen.b CreditAttribution: jeroen.b commentedD7 patch passed, so back to D8
Comment #143
jeroen.b CreditAttribution: jeroen.b commentedChanged to major as this issue keeps popping up for me, for example when you delete a module without uninstalling it (which could happen quite often when you use makefiles/profiles).
I also think more people are having this issue without them knowing it.
Comment #144
mikeytown2 CreditAttribution: mikeytown2 commentedBoth patches (#137, #138) look good. +1 from me. Also agree that this is an under reported issue.
Comment #145
askibinski CreditAttribution: askibinski commentedYes, this can really cripple your servers with high CPU loads.
Not only with deleted modules , but also if you have an unpatched version of imagecache_actions which contains a typo which triggers this bug.
#2166715: Typo causes extra query every page load
Comment #146
marcelovaniLets get this committed, shall we?
Comment #148
jeroen.b CreditAttribution: jeroen.b commentedHmm, how we get this working for D7 and D8 at the same time. Seems impossible...
Comment #149
marcelovaniOnce they apply the patch on D8 they will change the issue to be backported to D7
Comment #150
marcelovaniRe-submitting the patch
Comment #151
swentel CreditAttribution: swentel commentedNot a fan of the extra call of cache_get so early. The changes in Drupal.php to return NULL funky and shouldn't really happen there.
I'm also leaning more to the idea of cleaning up when installing/uninstalling modules, see comment #86.
Also, needs tests.
Comment #152
BerdirWhat about adding a hook_requirements() check that looks for missing modules and gives you a link that you can click that will drop that module from the system?
That is also a good place to place a bold red warning that you should never ever just remove a module without uninstalling it first. We're putting a huge amount of efforts into D8 to make uninstallation save and not leaving behind configuration and data and not being able to uninstall when a module is still used, just removing a module breaks all that. What if that module for example provides a bundle or a field type that is still used?
Comment #153
oriol_e9g+1000
See #112
I also think that it's better remove invalid entries than workaround.
Comment #154
joseph.olstadjeroen.b's patch from comment # 137 and #138 makes sense to me
The following comments between #151, 152, 153 talk about Removing module schema information without having run a proper uninstall.
Comment #155
mikeytown2 CreditAttribution: mikeytown2 commented@swentel
I Didn't see Drupal::hasService(); will use that instead of the funky NULL as Drupal::hasService() is used elsewhere in drupal_get_filename(). Also moved the cache to right before we do a filesystem scan; prevents a cache get for the vast majority of calls. Thanks for the critique!
joseph.olstad is correct; removing module schema information without having run a proper uninstall is why this logs the missing module; so an admin can properly deal with it. Since the code is missing there is nothing drupal
canshould auto do.Agree with Berdir; adding an additional check in hook_requirements for missing modules sounds like a good idea. I do think we need both solutions; hook_requirements & avoid re-scanning module directory. If we implement an additional check using hook_requirements, we could probably get rid of the logging inside of drupal_get_filename().
Comment #157
mikeytown2 CreditAttribution: mikeytown2 commentednew patch with less php syntax errors™
Comment #159
mikeytown2 CreditAttribution: mikeytown2 commentedThinking I'll have to install D8 if this fails again...
Comment #160
mikeytown2 CreditAttribution: mikeytown2 commentedComment #161
jeroen.b CreditAttribution: jeroen.b commentedI just tested this against HEAD, \Drupal::hasService('cache') and \Drupal::hasService('logger') don't return TRUE, so the cache never gets created.
How would we create tests for this?
AFAIK
\Drupal::hasService('cache')
returns FALSE in the testsuite so we have to find some other way to test this.Regarding #152, I think we should do that in a separate issue.
Comment #162
jeroen.b CreditAttribution: jeroen.b commentedAh, it had to be
\Drupal::hasService('cache.bootstrap')
and\Drupal::hasService('logger.factory')
New patch attached which fixes that. Just tested locally and now I get cache and a watchdog message.
Comment #163
joelpittetRegarding #137 D7 version @jeroen.b
After applying that patch I got this error:
And on
drush rr
following that message:Second
drush rr
went through ok, but didn't resolve the first on page exception.Omega 4 is the theme being used.
Comment #164
jeroen.b CreditAttribution: jeroen.b commented@joelpittet
And you are sure those messages are related to this patch?
Comment #165
das-peter CreditAttribution: das-peter commented@joelpittet Any chance you updated rules recently? Because: #2324587: Rules might be triggered too early in the bootstrap This can trigger the theme() exception as far as I remember.
Comment #166
joelpittetYes it was related to this patch it was the only one applied at the time, accept it was d7 patch in #138
@das-peter I just woke up but totally, I do plan on applying that when I get in. Thanks:)
Comment #167
joelpittetDidn't have my coffee, re-read #165, I'll try them together and report back:)
Comment #168
joelpittet@das-peter That rules patch in #165 didn't resolve the issues mentioned in #163 but that rules patch didn't cause any new problems, so far.
@jeroen.b #138 issues I mentioned in #163 are quite repeatable.
Directly after applying the patch, run registry rebuild.
Comment #169
jeroen.b CreditAttribution: jeroen.b commented@joelpittet
Can you pastebin the output of
drush pm-list
?I can't reproduce, even when I use Omega.
I do think I have seen this behaviour before. I think it was when I did a drupal_get_path on a module that wasn't enabled/installed and also wasn't found on the filesystem.Nervermind that, that makes no sense.Comment #170
joelpittetTotally here, sorry it's long: https://gist.github.com/d7e1da8b7113e0f313cb
Comment #171
jeroen.b CreditAttribution: jeroen.b commentedWoah, that's too much to debug for me. Can you try the attached patch?
It adds an backtrace to the watchdog message because I think this is in some contrib module.
Comment #173
joelpittet@jeroen.b thanks for that it gave me an idea. The backtrace is super huge too, so weeding through that wouldn't be fun, I can still post it if you prefer, but I put a breakpoint there to see the context.
The $name is empty, but the $matched_name was
addressfield
.Would that have an effect on this patch?
Comment #174
joelpittetDigging through the callstack and two other modules were mentioned workbench_moderation and linked_field, though I have my money on workbench_moderation being the trouble maker... it's an invaluable module but has to fight harder than most to do what it does.
Comment #175
jeroen.b CreditAttribution: jeroen.b commentedNah,
addressfield
just was the last value in the array$matches
, so the last module it was able to find.And seeing the menu_rebuild, now I remember where I have seen it before.
It's when you have a menu_item with a wrong "file path" value. Can you gist the backtrace?
Comment #176
joelpittetHere's the backtrace: (formatted with pre in the patch)
https://gist.github.com/bf0ee1fe8feb398647ad
Comment #177
joelpittetOh elysia cron comes into the call stack too as the $parent in _menu_router_build()
$item['file path'] = drupal_get_path('module', $parent['module']);'
admin/config/system/cron
Comment #178
joelpittet@jeroen.b Aha you were right! #2407129: Missing module<empty> on menu rebuild because of bad admin/config/system/cron override
Comment #179
joelpittetThe module name is sometimes empty, I patched the elysia_cron one but the next one is with commerce commerce_tax_ui_tax_rate_title.
I'm trying to decide if this is something we can/should fix in this patch, or should I just use this and file patches on all those modules (daunting argh)
Comment #180
mikeytown2 CreditAttribution: mikeytown2 commentedJust a heads up #1158322-199: Add backtrace to all errors is handy for error backtracing. In this case, the linked issue wouldn't have helped; but I do find the linked issue to be super helpful.
Comment #181
joelpittetThe tax one doesn't have an issue opened up for it and it's a bit different but very similar.
The 'commerce_tax_ui' builds the hook_menu with
'file' => 'includes/commerce_tax_ui.admin.inc',
but without a module key again. But in the screenshot the path has already been filled out... :SComment #182
jeroen.b CreditAttribution: jeroen.b commentedI can't reproduce that with the commerce_tax_ui.
Maybe there is some weird hook_menu_alter somewhere?
Anyway, I don't think we should fix these things in the patch. The stuff we find with this patch is already broken, only difference is that we can see what's broken now.
It's up to the maintainers now to fix their modules.
Comment #183
joelpittet@jeroen.b very well could be, I'm a bit worried about the error produced in #137. Though I can't figure out why this patch would cause that at all... sorry for holding this up.
Grateful though that this indirectly solved elysia_cron issue.
Comment #184
jeroen.b CreditAttribution: jeroen.b commented@joelpittet
Don't worry about holding this up, this issue has been here for a few years ;)
About the error in #137. Are you talking about the error you reported in #163?
This one?
I could not reproduce this. Not with a clean Drupal 7 HEAD, Drupal 7.34, Drupal 7.34 with Omega and Commerce.
Can you figure out what module (indirectly) calls theme()?
Comment #185
joelpittetAfter repairing elysia cron I've not seen this error since but I'm keeping my eye on it, the patch is on my staging environment till I deploy the next round of changes.
Comment #186
jeroen.b CreditAttribution: jeroen.b commentedI heard you guys want tests.
Here's a D8 patch with a testDrupalGetFilename() assert. I had to change the $bad records to a drupal_static to reach it from the test.
We can't use the bootstrap drupal_get_filename:bad cache for tests as the cache isn't available in the tests.
I'll look at tests for the D7 version later.
Comment #188
jeroen.b CreditAttribution: jeroen.b commentedI think these tags aren't relevant anymore
Comment #189
jeroen.b CreditAttribution: jeroen.b commentedHere is a D7 patch with tests.
Comment #192
mikeytown2 CreditAttribution: mikeytown2 commentedchanging to 7.x for testing
Comment #194
jeroen.b CreditAttribution: jeroen.b commentedAll green. Let's get this committed?
Comment #195
swentel CreditAttribution: swentel commentedWell, first of all, this should go in D8 first. Secondly, see comment #150 and #151. Doing this kind of optimization at run-time really isn't the proper way to fix this bug. To be clear: it is a bug (sometimes extremely annoying), I've actually hit it a couple of times as well, but I disagree that we should fix this at run time. There are better, cleaner ways (order is my preference)
I'm in favor of number 1 because we then also have a UI that informs the user which module(s) are actually missing. All the other variants, which may trigger watchog, are less obvious and are easily missed.
I'm in London next week for drupalcamp london, I'll try to write up a patch during those days.
Comment #196
jeroen.b CreditAttribution: jeroen.b commentedI'm all for really fixing this, but sometimes that just not possible by just cleaning the registry.
Sometimes other modules call drupal_get_path manually with wrong arguments, or with disabled modules, I have seen a couple of cases.
Sometimes you can't properly uninstall a module, we deploy all sites with drush make, if we remove the module from the makefile, the record is still in the registry.
My plan was to get this patch committed and then create a new issue where we add the hook_requirements functionality. If you want to include that in this patch that's fine with me, but please also use the existing patches.
Currently if something goes wrong in drupal_get_filename, you have absolutely no idea.
Comment #197
mikeytown2 CreditAttribution: mikeytown2 commentedComment #198
joseph.olstadI agree with jeroen.b , there's going to be situations where admins of large scale sites managed by committee that can't just decide by themselves if or when to cleanup something and determine if it was something left by a drush make or if it was a module that was removed from the filesystem by mistake. The best approach is his solution because it fixes the performance problem and also is noisy enough so that admins will be able to see what is going on and let them take a reasonable course of action without a huge performance penalty.
Prior to this patch not only are admins left in the dark but there's also a huge performance penalty. Lets get this committed. We've been running patch #102 in production for a while and I just reviewed patch 189 and it looks good. So for those working on D8, please follow the advice of jeroen.b because not only do we want to see D8 improve but we also are running production D7 sites that pay for our work on D8.
Comment #199
heyyo CreditAttribution: heyyo commentedI'm using the patch from #189, I'm not sure what's wrong but it reports me this error:
"The following module is missing from the file system: mothership"
Mothership is not a module, but a theme, and this theme is present in sites/all/themes.
Mothership is not enabled(but was enabled before), I had the version 2.x before, but now I'm using 3.x.
Not sure how to fix that ?
Comment #200
SocialNicheGuru CreditAttribution: SocialNicheGuru commented@heyyo, do a "grep -in mothership" in your module and themes directory.
There is probably a call to a file in that module somewhere or a misspelling.
an issue where something like this occurred to me:
https://www.drupal.org/node/2428105
here is another:
https://www.drupal.org/node/2375741
Comment #201
heyyo CreditAttribution: heyyo commentedThanks a lot @SocialNicheGuru it was indeed the problem with this file mothership/mothership/panelsstyles/motherstyle/motherstyle.inc
Including 3 times :
Comment #202
joseph.olstadThe nice patch from by jeroen.b from comment #186 needed to be refactored after D8 core commit #40cc6312 for issue #2090115 by alexpott, xjm: Don't install a module when its default configuration has unmet dependencies.
Attached is a refactored patch for D8 accordingly.
Comment #203
joseph.olstadD8 patch #202 passed all tests march 26, 2015 22:00 0400+gmt
D7 patch #189 passed all tests in February 2015
D6 not yet backported, lets commit the D8 and D7 patch first.
Comment #204
David_Rothstein CreditAttribution: David_Rothstein commentedAdding a cache to this kind of low-level function looks odd to me (others above have said the same thing)... Also there is no code that ever clears the cache (except on a sitewide cache clear)? Can't it get stale?
Maybe a simple trigger_error() is better here? That should get it into the logs anyway, plus onto the screen on dev sites (a key part of this, so developers can find mistakes in their code that lead to this). This is sort of getting into the territory of #2380361: Latest tweaks to bootstrap.inc can make Drupal sluggish when there is code that tries to load a file that doesn't exist but the two issues are closely related. I tend to think warning about this problem is realtime is better than trying to fix it with a cache in realtime.
Comment #205
joseph.olstadWhen developers are removing things from the filesystem they're usually:
A) running a drush make script
B) performing filesystem operations perhaps from a script
C) changing in or out modules, in which case a full cache clear is run.
D) upgrading a Drupal site from one distribution to another
E) upgrading a Drupal distribution from one version to another (that may be missing files seen in previous versions and lacks appropriate hook_updates)
F) upgrading Drupal core and forgot to restore a folder such as sites/all/modules/contrib
G) any combination of the above combined with updatedb
operations C, D, E, F, G will trigger a site-wide cache clear, so the cache is not stale.
and so does re-scanning module directory when a module is missing
sure it would be nice what about the performance issue? Example: webform_conditionals from webforms 3.x was swallowed up by webforms 4.x , but if you run a pm_uninstall on webform_conditionals 3.x are you sure that you're not going to remove tables that are now used by webforms 4.x? So now you have a performance issue while your production site which needs the new features from this new module that swallowed up another module but the schema table says it's still installed but you've removed it from the filesystem because the webforms readme.txt told you to do so. No one paid attention to this before until they realised their site was slower than it used to be so they went on to find some performance fixes and applied this patch. Now they're aware of multiple other problems that they were not aware of, but no problem, this patch fixes the performance issue without having to immediately deal with the mess undernieth that you will need a committee to sit together and point the finger and look at test results and build behat tests for and wait for approval to make the necessary changes, meanwhile the clients are frustrated that your site is slow except with this patch thankfully we get both INFORMED and RESTORED PERFORMANCE.
Meanwhile, much of the drupal community sits in ignorance wondering how entropy is creeping in and slowing down their sites, not knowing how to resolve it, they start thinking that the best solution is to re-install and start from scratch and hope that the performance problem doesn't come back. Or worse yet, they say, hey, lets install Joomla instead.
Why don't we just commit the patches, illuminate the drupal community and put the smiles back on the developers faces and the confidence back in their ability to administer sites, we get performance back and we also get informed of what is going on! Otherwise we're completely oblivious as to what is happening and why our sites become so damn slow over time. Finding this patch was a stroke of good luck. Thanks to all those that created it and pushed it forward.
RTBC + please
Comment #206
mikeytown2 CreditAttribution: mikeytown2 commented100% agree with joseph.olstad #205.
Will also say caching a bad list makes sense due to typo's when calling drupal_get_path(), see #199. Also see this issue where the bad module name was ''. Having your site be slow while you have no idea where to look sucks #2407129: Missing module<empty> on menu rebuild because of bad admin/config/system/cron override. These 2 issues point out that with the mothership theme or the Elysia Cron module installed and this patch not enabled results in a much slower Drupal install for those using the code; which is around 20k sites (counting the d7 installs).
I agree that in an ideal world we wouldn't need a cache here; but we're on the web where bad things happen all the time (typos) and bureaucracy can tie your hands.
Comment #207
joseph.olstadA while ago I created a drush tool for detection and auto cleanup of phantom modules based on comment #112 however even with that, my tool only cleans out phantom modules and does not yet clean out phantom themes. With that said, I have run it on one production site but not on another. The patch that everyone here has worked hard on over the past 4 years correctly informs us and gives us the luxury of delaying such a cleanup without a performance penalty. There may be some scenarios where a cleanup has to be delayed for long periods of time. I think we've already outlined the importance of this but just to show you how much this issue is important it's why I am sharing this.
Comment #208
joseph.olstadA small drip for Dries and D8, a nice warm douche for all of Drupal kind!
Comment #209
David_Rothstein CreditAttribution: David_Rothstein commentedTrue, I guess the cache only goes stale if a missing module in the filesystem is added back, and most people probably do expect to have to clear the site's caches when doing something as big as adding a new module (although it's not always technically required now).
Keep in mind that for the trigger_error() solution, it would not just be that - the idea would be to combine it with something like #195 to fix the cases that actually can be fixed. (I'd add "run it on cron" as a possible option to the list of places in that comment where the fix could be done, by the way.)
Comment #210
jeroen.b CreditAttribution: jeroen.b commentedReplacing the watchdog with trigger_error() is fine with me. I'll see if I can update the patch soon.
I also agree with joseph.olstad here. Why don't we RTBC this and figure out #195 in another issue?
As in my opinion idea 1 (which I think is the way to go) from #195 requires a new discussion and it only addresses a small part (dirty system table) of what this patch addresses.
Comment #211
Fabianx CreditAttribution: Fabianx commentedI am moving this back to RTBC, I think the cache is okay, because it can only get stale if you add a new module or move a module and then the module_implements cache is as much a problem as this cache.
Comment #212
stefan.r CreditAttribution: stefan.r commentedIs the D7 patch in #189 in sync with the D8 patch, and equally RTBC, so both can be committed at the same time? Or does the D7 patch need work still?
Comment #213
jeroen.b CreditAttribution: jeroen.b commentedD7 and D8 patches are in sync, they contain the same fixes and tests.
Comment #214
Fabianx CreditAttribution: Fabianx commented#212 However 8.0.x needs to be committed first and then the D7 patch is discussed.
Comment #215
alexpottbad is a really odd name. How about missing or something more descriptive.
Also the issue summary needs a update since it says that we're only going to statically cache this but no we're putting it in cache_bootstrap too. Which like others before me I'm incredibly dubious of. If you move a module by mistake and break your site, it ends up in the cache, you quickly fix your mistake and then it is still not fixed until you obliterate all the caches. I'd prefer to be more conservative here and just implement the static cache.
Comment #216
jeroen.b CreditAttribution: jeroen.b commentedYes, in that one example, the cache might be a bit of a struggle. However, in most other examples the cache will come in very handy as this function might be very heavy on every page load.
When I would do something like moving a module by mistake and then place it back, my first idea would be to drush rr and drush cc all...
Comment #217
joseph.olstadJeroen.b makes another valid point in #216 that re-iterates what I already described in #205. We have a high performance solution here that is very practical in realworld use, many have been using it in production (at least the D7 version) and is urgently needed for those that are unaware that they need it (because everyone running Drupal should have this fix). I'm not sure why we keep going back to needs work. 4 years of work, it's more than enough time. IMHO, there aren't enough valid reasons to redesign at this point something that is practical in the realworld as this is and guarantees that performance will not be affected by bone-head operations. Meanwhile admins will be aware of what is going on. All of this has been stated many times.
*everybody* using Drupal needed this 4 years ago when this issue was openned (or even 8 years ago when people didn't know the problem existed but they should have had this fix) and people still need this today and they'll need it tomorrow, those that don't have it should have it.
Comment #218
alexpottCaching a cache miss - which is what we are doing is generally a really bad idea. That said perhaps it is an acceptable trade-off if we ignore or clear this cache when we list modules or themes. Or rebuild it on cron as suggested by @David_Rothstein in #209
And
bad
is still a bad name :)Comment #219
Fabianx CreditAttribution: Fabianx commentedAt least in Drupal 7 we can use CACHE_TEMPORARY for clearing on cron and a low ttl (24 hours?) for Drupal 8 instead of CACHE_PERMANENT.
In addition to that clearing this when visiting the admin/modules page sounds good to me.
We used to rebuild block caches since ages when visiting the blocks page, so that makes sense.
#218
Caching cache-misses is strange, right, but not caching cache misses is kind of an anti pattern too.
Usually:
So every cache_get() should usually be followed by a cache_set, which includes setting empty values or FALSE.
That said, we can't store the information that the module is not there in the original modules files cache?
#217: Keep pushing with good focus (and address feedback) and it will get in - one day :). Most issues that have someone dedicated to them found their way into core eventually.
Issues like this stall only for years, because they are sitting waiting for review or feedback or ... And usually that is lack of "resources" rather than lack of interest.
Comment #220
joseph.olstadAlexpott, you proposed two options: "ignore or clear this cache when we list modules or themes"
this seems like a reasonable option (to ignore the cache for that case, clearing it is really not necessary as Jereon.b and I have previously explained)
However : "Or rebuild it on cron as suggested by @David_Rothstein in #209"
I am less favourable of this option because cron is already very resource intensive and doesn't need more load.
with that said, I like even better the "acceptable tradeoff" as you stated that the current patch gives. Slam dunk this.
Comment #221
stefan.r CreditAttribution: stefan.r commentedI concur with joseph.olstad about the importance of fixing this ASAP, and while I agree this is not an ideal implementation I quite like both suggestions in #219 as acceptable workarounds.
This path renames
bad
tomissing
and invalidates the missing file cache on cron runs as well as when visiting module/theme list pages.Comment #222
stefan.r CreditAttribution: stefan.r commentedActually this patch doesn't clear the static cache so whoever visits the site right after a cron run may still be hit with a slowdown, correct?
I assume that if we have missing files, clearing up the static cache as well as the Drupal cache in the middle of a cron run will mean that we rebuild it further down that same cron run as there's likely new calls to
drupal_get_filename()
? Or should we clear the caches of the module/theme lists for that as well?).I realize that load going up during cron runs is a concern, but that's what we can do a hook_requirements() thing for in a follow-up issue, and besides, I think it still beats giving significant load slowdowns to unlucky users visiting right after a cache clear :)
Comment #223
stefan.r CreditAttribution: stefan.r commentedComment #225
stefan.r CreditAttribution: stefan.r commentedSo I assume this patch would also fix this performance regression listed in the 7.33 release notes?
Comment #226
stefan.r CreditAttribution: stefan.r commentedCreated followup issue over at #2464601: Check for missing modules in hook_requirements so we can do both the caching fix as well as a hook_requirements fix as suggested by many in this issue.
Just wondering if checking the system table in D7 and checking system.module.files / system.theme.data in D8 is enough, or if these missing files can come from elsewhere as well? (in which case we may want to get a list of these files by checking the missing files cache)
Comment #227
joseph.olstadok, so Stefan.r played it safe and went with both recommendations by Alexpott. Good job.
Alexpott, please review this and commit it to D8 so we can agree on a backport to D7 and commit that too!
Comment #228
stefan.r CreditAttribution: stefan.r commented@joseph.olstad I had missed your comment over in #220.
I am fine with updating the patch so it merely ignores the cache on admin theme/module pages instead of invalidating it, if we can get a committer to sign off on that :)
Comment #229
Fabianx CreditAttribution: Fabianx commentedAs {system} is more a registry than a cache I am okay with the patch to catch misses, lets see what Alex says.
Invalidation is better than ignoring, else you wonder why the module page is correct, but your system still does not see the new module, which is worse DX. Invalidation on cron is also not costly.
#227: Please do not tell core committers what to do or not do. There is a lot of voluntary work in there as well and it borderline almost violates our code-of-conduct (not really, but please treat core committers as other fellow co-workers).
I know that the core process can be slow at times, but all recommendations by core committers are done for the whole good of the whole Drupal ecosystem!
I have seen both in Drupal as well as in client work, lines of code that have been 'very simple change', it is so obvious, etc.
And some of them have made quite big problems later. Trust me in the end following the process and getting through good peer review will make Drupal for all of us better in the end.
Comment #230
joseph.olstad@Fabianx , invalidation has a performance cost, so it's not necessarily better than ignoring. Stefan.r sees the point in #227. See reasons outlined by Jereon.b in #216 and myself in #205.
I'm fine either way, prefer ignoring the cache to invalidation as I stated previously (because invalidation defeats the purpose of a cache and again for the same reasons already explained). But close enough.
Comment #231
jeroen.b CreditAttribution: jeroen.b commented#225, yes, I think it's the same issue, maybe it should be marked as duplicate?
Also, what about the trigger_error() idea from #204? trigger_error() will make the error be catched by Drupal's error handler which is more advanced than just a watchdog call.
Rest of the patch in #223 looks fine to me.
Comment #232
stefan.r CreditAttribution: stefan.r commentedYes @David_Rothstein's suggestion makes sense, let me do a patch.
Comment #233
stefan.r CreditAttribution: stefan.r commentedThis adds the trigger_error().
Comment #235
stefan.r CreditAttribution: stefan.r commentedOK so this test fails because we do things like this in the tests:
GetFilenameUnitTest.php:
AttachedAssetsTest.php
Comment #236
Fabianx CreditAttribution: Fabianx commentedWe use !isset() instead of is_null() in core.
See http://3v4l.org/11S72 - it works.
We don't want to use , TRUE here as we don't deal with invalid data.
Third parameter should be omitted.
Currently we cache this for < 1s, hence the need to use TRUE above ...
Edit:
Use REQUEST_TIME + 24 * 60 * 60
to store this for 24 hours.
It will then automatically be cleaned up by the next cron run.
Should use ->delete() and no need to check for the service.
I think we want to inject the cache.bootstrap service instead here, but that we can do once the rest of the patch is sound.
Should use ->delete(), and this can rely on the cache.bootstrap being available already.
Should use ->delete() instead of ->invalidate().
And yes, re-calculating system files once after each cron run is acceptable.
That said I think we should let garbage collection do the job for us.
Here is a more extensive review.
Comment #237
Fabianx CreditAttribution: Fabianx commented#235: That is a unit test, which should probably be moved to its own method with @expectedException annotation present:
https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writ...
Comment #238
stefan.r CreditAttribution: stefan.r commentedProbably expectedExceptionMessage would be better as if I give the exception type to expectedException it will work like the "@" operator and hide all errors which would hurt the test. There's also 6 test fails and not all of those are PHPUnit tests...
For the other ones maybe we could temporarily set a different error handler which ignores "file not found" warnings?
E_USER_NOTICE still triggers exceptions on the testbot right?
Comment #239
BerdirThose are kernel tests. phpunit tests could not be calling functions.
Comment #240
stefan.r CreditAttribution: stefan.r commentedSo currently I'm leaning toward setting set_error_handler() / restore_error_handler() before and after the problematic statements and filtering out file not found warnings...
Comment #241
stefan.r CreditAttribution: stefan.r commentedComment #243
stefan.r CreditAttribution: stefan.r commentedComment #245
stefan.r CreditAttribution: stefan.r commentedMissed a comma.
Comment #246
stefan.r CreditAttribution: stefan.r commentedComment #248
stefan.r CreditAttribution: stefan.r commentedComment #250
stefan.r CreditAttribution: stefan.r commentedQueueing this for re-testing this to make sure this wasn't a fluke as I haven't seen the current test fail in MigrateFileTest with this patch before.
Also just to summarize for reviewers, in terms of changes to the last reviewed patch, this patch:
@Fabianx just to double check that this invalidation won't break anything, why did you ask to delete instead of invalidate the cache on the admin/theme pages? Won't an invalid cache mean just that when we try to do a get() the cache won't return anything (as $allow_invalid is now set to FALSE), and the cache will just be reset next time we try to do a cache_set(), or during garbage collection in cron?
Lastly, I may be wrong but I think ceasing to explicitly clean up the cache on every cron run also means that the cache will only be cleared once it's been fully built and all missing files have been found. This as any time a new missing file is found the expiration date is set further into the future. In the edge case of any code referring to a nonexisting file with a random file name, that could mean the cache never gets cleaned up. Is that something we can live with? If not, maybe we could set a fixed expiration date of 24 hours that doesn't get bumped further into the future every time the cache is updated?
Comment #252
stefan.r CreditAttribution: stefan.r commentedOK this is green again.
Comment #253
joseph.olstadHere's my test plan for D8:
1) install fresh with the patch
2) create a dummy module, enable it
3) remove the dummy module from the filesystem
4) check for expected results (error message)
5) if possible, performance profile
repeat same tests as above without patch
Comment #254
stefan.r CreditAttribution: stefan.r commentedSo I discussed this with @Fabianx on IRC and apparently the concerns in #250 won't pose a problem. The last point would just need feedback from a core committer.
Just a tiny tweak to the last patch in the attachment:
I'll just delete that altogether, looks like no extra modules are needed in that test anyway.
Comment #255
arosboro CreditAttribution: arosboro commentedPatch #189 helped me identify a missing module. Profiling reports a significant decrease in function calls and a savings of 2,000,000 microseconds. (Yes I'm testing a very slow site, but this patch made an improvement none the less)
Comment #256
joseph.olstadMy dev box is running off of a failover drive (like a human on one kidney) so I'll delay my D8 tests until I get the replacement drive up. I did some preliminary D8 tests but was right in the middle of this when I discovered a hardware problem. Keep up the good work Stefan.r .
Comment #257
jeroen.b CreditAttribution: jeroen.b commentedJust tested this on a fresh install from 8.0.x-dev with the patch from #254 and steps from #253, worked great.
Got the following message in WD:
<em class="placeholder">User warning</em>: The following module is missing from the file system: dummy in <em class="placeholder">drupal_get_filename()</em> (line <em class="placeholder">300</em> of <em class="placeholder">/home/vhosts/jeroen/drupal/d8-patch/core/includes/bootstrap.inc</em>).
Comment #258
stefan.r CreditAttribution: stefan.r commentedInterdiff with last RTBC'ed patch for reviewers
Comment #259
Wim LeersOnly nitpicks. I think it'd be better if e.g. Fabianx reviews this. Assigning.
s/drupal static/drupal_static()/
s/Drupal static fast/The drupal_static_fast()/
Creative :D
s/drupal_get_filename/drupal_get_filename()/
80 cols, and same remark as above.
Comment #260
Wim LeersComment #261
stefan.r CreditAttribution: stefan.r commentedComment #264
Fabianx CreditAttribution: Fabianx commentedThis should be using an existing module, but a non-existing library.
That would be a better test anyway and the error_handler handling should be removed here.
It has been a while, so pardon if I re-ask this question:
Why do we need to remove that line again?
Why do we remove that?
Does that module not exist?
Why do we remove that?
Does that module not exist?
CNW for #1, the rest looks good. I assume core used some non-existing modules in tests. Is that right?
Comment #265
stefan.r CreditAttribution: stefan.r commentedre #2, #3, #4: those modules don't exist, so they trigger errors.
Comment #266
stefan.r CreditAttribution: stefan.r commentedComment #267
stefan.r CreditAttribution: stefan.r commentedUpdated issue summary and added beta evaluation
Comment #268
stefan.r CreditAttribution: stefan.r commentedComment #271
Fabianx CreditAttribution: Fabianx commentedSetting to RTBC, this looks great to me now and really needs more feedback by a core committer now.
Comment #272
alexpottShould we be triggering an error here if this occurs. It strikes me that this is a bug too and that we'd want to report it.
Comment #273
stefan.r CreditAttribution: stefan.r commentedI actually don't know that we need to be checking for that there, in any case it seems outside of the scope of this patch.
@joseph.olstad added it in #202 citing #2090115: Don't install a module when its default configuration has unmet dependencies as the reason but I don't really see how that would be passing empty values for those. Maybe he can clarify?
Comment #274
stefan.r CreditAttribution: stefan.r commentedRemoving this for now, unless anyone can give any examples where $type/$name would actually be empty. Will ping @joseph.olstad for feedback.
The check would probably be better dealt with in a follow-up issue, along with fixing this at the source instead if there's actually any cases of core sending odd values for those.
Comment #276
stefan.r CreditAttribution: stefan.r commentedAh perfect, seems it already triggers an error anyway, as we have a bug in ExtensionInstallStorage::getAllFolders(), where we look for a file even if drupal_get_profile() == NULL:
Comment #277
stefan.r CreditAttribution: stefan.r commentedComment #279
stefan.r CreditAttribution: stefan.r commentedComment #281
stefan.r CreditAttribution: stefan.r commentedThe piece of code in the interdiff doesn't really makes sense to me, why do we pass the type "module" if we're looking for a profile?
Comment #282
stefan.r CreditAttribution: stefan.r commentedAdding comment.
Comment #284
stefan.r CreditAttribution: stefan.r commentedIn any case I don't think #281 is pertinent to this issue.
Opening #2476055: ConfigInstaller searches for profile overrides in module paths for that, let's stick to test failures here :)
Comment #287
stefan.r CreditAttribution: stefan.r commentedComment #289
stefan.r CreditAttribution: stefan.r commentedThat took a few tries, let's see if this is green now :)
Interdiff is with the patch that was RTBC'ed in #266
Comment #290
joseph.olstad@stefan.r
regarding comments #274, #273 and #272 and #202
I saw you made the spoken changes in #274, looks good to me. My refactoring in #202 kept a regression from #186 because of the flux upstream it was difficult to figure out what part of 186 should remain and what part should be refactored from core commit #40cc6312 for issue #2090115. I imagine that there's been other changes to the d8 dev branch since. Looks like that code snippet spoken of in comment #272 and #273 came in during patch
#186 by jeroen.b#124 (see next comment)which may have actually come from another older core commit.I see #289 passed testing, great work Stefan.r! Stefan.r I know you've done already a lot of work on this but can you please summarize the current status of #289 so everyone else can focus on what is the current status for the D8 patch? I've got a few ideas for test plans but just want to double confirm so that we're all on the same page.
Comment #291
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedIndeed great work @stefan.r!
I think the code snippet from #272 was added in #124, not in #186.
Comment #292
stefan.r CreditAttribution: stefan.r commentedThe patch in 266 was RTBC and the concern was this NULL check, which I am fine with removing. It exposed a few test fails, which have been fixed in the latest patch.
As far as I'm concerned this is ready. Someone just needs to review the interdiff in #289 to make sure the test fail fixes are correct.
Comment #293
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedI think it's good that the empty check was removed.
Returning null on an empty $name or $type is rather a workaround than a bugfix and such errors should be fixed at the source. (like you did)
Can't really tell if your fixes from the interdiff are correct so someone else should look at that.
Comment #294
stefan.r CreditAttribution: stefan.r commentedThanks for pointing out that the NULL check came from #124. That confirms it was just about a couple of test fails and should be safe to remove at this point.
@joseph.olstad I wouldn't worry too much about profiling this, the performance penalty is obvious at this point, the current patch doesn't differ functionality-wise from the previous RTBC patch (which had been profiled already).
Comment #295
alexpottThese changes surprise me - which tests fail for this?
Comment #296
stefan.r CreditAttribution: stefan.r commented@alexpott: removing the NULL check causes 799 exceptions altogether now that we have the trigger_error warning: https://qa.drupal.org/pifr/test/1031513
The piece of code you isolated (
ConfigInstaller::installOptionalConfig()
), makes about all of the tests listed in this other test run fail (other than the QuickEdit and CKEditor tests, which have separate fixes):https://qa.drupal.org/pifr/test/1031653
Comment #297
stefan.r CreditAttribution: stefan.r commentedJust to reiterate, we just need someone to review the interdiff in #289 in order to move this issue forward now :)
It's just a few test fixes to make sure we don't do unnecessary calls to
drupal_get_filename()
if we haven't set a theme/profile yet.Comment #298
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedReviewed the fixes in the interdiff, looks good to me.
As the rest of the patch was RTBC before... RTBC for me.
Comment #299
catchHmm like David in #204 I'd also have just gone for the trigger_error() here. This is adding complexity for what is a very rare case, and the trigger_error() should mean the problem is found and fixed quickly.
Comment #300
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedBut we did go for the trigger_error():
trigger_error(SafeMarkup::format('The following @type is missing from the file system: @name', array('@type' => $type, '@name' => $name)), E_USER_WARNING);
Comment #301
catchYes, I'd have only done that.
Comment #302
stefan.r CreditAttribution: stefan.r commented@catch in #204 David Rothstein was suggesting to do the trigger_error on top of the caching, not just to "replace" it.(seems we may have misread, see @alexpott's comment in #218)In D7 a missing/moved module is not such a rare case at all (hence the 92 followers on this issue). A trigger_error() by itself will mean the problem is found/fixed quickly on dev environments, but not so much on all the production environments out there, hence why we need the caching.
Comment #303
stefan.r CreditAttribution: stefan.r commentedJust to add to this, I've actually run into this issue on at least 4 different Drupal installs. And version 7.34 may have introduced this issue in further installs per #2380361: Latest tweaks to bootstrap.inc can make Drupal sluggish when there is code that tries to load a file that doesn't exist. I'd be fine with a simple trigger_error() if we didn't do a filesystem scan when a module is missing, but this is causing a serious performance hit where I strongly doubt a simple trigger_error will make even half of sites with missing files clean up their stuff.
I think around #206 we had a similar argument about how in the real world typo's happen, bureaucracy exists, not everyone watches their logs, and not all sites are being actively developed anymore?
Comment #304
joseph.olstad@stefan.r ya, my organisation has invested several million dollars into Drupal (three years straight of well paid Drupal consultants, consulting firms, several well paid full time staff dedicated to Drupal, infrastructure, management, support) and every single one of our sites was afflicted by "Drupal sluggish when there is code that tries to load a file that doesn't exist. ". We have sites that have relatively high loads and we need to squeeze every bit of performance we can out of Drupal. Had this performance fix already been in core we might have been able to get many millions more of business for Drupal. Thanks to Mikeytown2s' work we were able to bring in a lot of performance enhancements in the form of drupal core patches , it'd be nice to see this stuff already in core because performance becomes very important once your site scales up and you have higher loads and more and more complexity. Complexity is something we cannot avoid in this case. Either way it's going to be complicated, either we maintain core patches or maintain core it'self. We greatly appreciate your hard work stefan.r (and jereon.b and others here as well as mikeytown2 ), you've helped us with contrib modules, and now the great work here, keep up the great work. It is appreciated.
Comment #305
catchThe fact there are existing production sites running 7.x where this is a problem and it silently does the scanning is a decent reason to do the caching in 7.x - on the assumption that some sites get a core update but no other monitoring.
However there are no such sites for 8.x, and I can't see a situation where you'd go from a working site to this issue, and the trigger_error() wouldn't be sufficient warning (unless you're randomly deleting folders on prod). So please upload an 8.x patch with just the trigger_error(), the backport can have the additional safeguards (assuming David's fine with that).
Comment #306
Fabianx CreditAttribution: Fabianx commentedI talked with catch about this.
The outcome was:
For D7 especially the caching makes sense, it does make less sense for D8 as there are no production sites out there, yet.
If you delete a module from production that is still used you usually have other problems workflow, etc. wise than that the path is no longer found. Also there is no longer the option to disable a module in Drupal 8, but only to uninstall it, which you should definitely do, which also removes the entry.
And hopefully adding new modules either goes via a dev cycle so you see the errors and also in Drupal 8 that function should be way less used as it is all moving to classes.
It is great that the patch has found so many bugs in core though, which shows the usefulness of the trigger_error.
Therefore compromise:
- trigger_error for D8
- Caching + trigger_error for D7 (as we never warned before so its good to handle gracefully)
#304: In our profiling wetkit and the used stack (bean) etc. also had been performance bottle necks in itself and many issues. So the reason why you need many core performance patches is partially also because you use a rather 'inefficient' (or partially overly complex) setup to start. Also while mikeytown2's work is very very great, it is also important to note that some of it is still a little risky and e.g. for drupal.org the opposite approach (cache synchronization) instead of async-ness has been used.
Please also remember that we are a community, it is free software and we work on consensus approach. The best way to ensure getting an issue into core quickly is to work _with_ the core committers, not against them.
They usually have good reasons for their decisions and the best of the project in mind.
Comment #307
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedI don't think anyone is working against the core committers here. We just didn't understand why we would leave caching out.
I see catch's point now and leaving the caching out only in D8 seems like a very good consensus to me.
Comment #308
Fabianx CreditAttribution: Fabianx commentedThought some more and I think instead of caching misses we should start for Drupal 8 to avoid the auto-healing in the first place. That also solves the performance issue as it would no longer scan the system as fallback, but just fail.
Already before we had the consensus in here once that if system misbehaves you just clear caches.
The auto-healing was meant to find moved modules, etc. without having to clear caches.
However as we have seen that approach is flawed and with class loader in Drupal 8 and caches on that front it is needed to clear caches afterwards or call /core/rebuild.php.
Note also:
The auto-healing never updates the actual cache, so in the case of a fallback system scan we do this fallback system scan again and again (without a trigger_error) until the cache is cleared finally.
So as we now finally found out this whole approach is flawed my new take (for Drupal 8) is:
- Remove the automatic system scan
- Use trigger_error instead always.
Be done with all cases.
Comment #309
Fabianx CreditAttribution: Fabianx commentedClarified title, this is not just about missing or moved modules.
Comment #310
catchYes +1 on removing the automatic scan altogether for 8.x, that would only be useful in the case that discovery hasn't run at all yet - and those cases we could fix individually rather than relying on multiple layers of fallback.
Comment #311
stefan.r CreditAttribution: stefan.r commentedYes this is a great solution, thanks so much Fabianx for staying on top of this.
The whole reason we need the caching magic is we have the file scan magic, happy to get rid of that for D8 :)
Comment #312
stefan.r CreditAttribution: stefan.r commentedThis patch removes the filesystem scan and the caching for D8.
Comment #313
stefan.r CreditAttribution: stefan.r commentedNote the information returned by the module handler / saved in state may be outdated and the file might have disappeared/moved in the mean time. Are we fine with that or should we check whether the file exists in drupal_get_filename()?
Comment #314
catchWe use include()/include_once() in most places in 8.x now after #752730: Remove file_exists() during bootstrap so no need for file_exists() - it'll error but shouldn't be fatal.
Comment #315
stefan.r CreditAttribution: stefan.r commentedComment #319
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedComment #320
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedWhoops, I noticed https://www.drupal.org/node/2480027 a little too late...
Comment #321
stefan.r CreditAttribution: stefan.r commentedThis required some refactoring and hackery, patch is not final but some feedback would still be good. Just to summarize the changes:
1. We do separate
ExtensionDiscovery
scans inExtensionInstallStorage::getAllFolders()
,InstallStorage::getAllFolders() and <code>drupal_check_profile()
.2. As we can't know what the location of the system module is before having installed the system module, we hardcode it in
install_drupal()
. I realize it's ugly but this ought to be a stop-gap solution before we have a proper extension listing service.3. We do a
system_rebuild_module_data
before installing the user module in the installer so we know where it's located. This is expensive so maybe we need to use solution (1) or (2) instead here.Comment #323
stefan.r CreditAttribution: stefan.r commentedComment #325
stefan.r CreditAttribution: stefan.r commentedKernelTestBase::enableModules()
has a similar problem.As a workaround, just to see if I can get the patch green I'll do another ExtensionDiscovery scan there :/
Comment #327
stefan.r CreditAttribution: stefan.r commentedStill a few more failures to fix here:
Comment #329
stefan.r CreditAttribution: stefan.r commentedComment #331
stefan.r CreditAttribution: stefan.r commentedThis should be green now. Would be good to have some feedback on the attached interdiff with #312, and ideas on what to do with the GetFilenameUnitTest test now that we don't have the filescan anymore.
Comment #332
stefan.r CreditAttribution: stefan.r commentedComment #333
stefan.r CreditAttribution: stefan.r commentedNow that
drupal_get_filename
doesn't have the file scan anymore we'll need to change the testing strategy in the unit test for that function a bit (GetFileNameUnitTest
). Just to describe the current logic:system_list()
)ModuleHandler
system_rebuild_module_data
andThemeHandler::rebuildThemeData()
.Do a file scan(which this patch removes).As to where values are saved:
system_rebuild_module_data()
ThemHandler::rebuildThemeData()
ModuleHandler
list is set up byDrupalKernel
from the thecontainer.modules
parameter.In the current implementation, we can't scan for theme engines anymore as there's no listing available for them, so we can either build a way to retrieve a cached list for them, we do a file scan for or we drop support for them in
drupal_get_filename()
(which would be an API break).The ugliness of this patch in a way exposes the need for an extension listing service, there was talk about this with some core committers on IRC but "this is such a tangled mess that it will confuse developers and may introduce bugs outside of core" and "this might possibly improve performance as we do less unnecessary rebuilds" are not really strong enough arguments here, so it's currently looking like a bit of a long shot to get the extension listing as envisioned by @sun in #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList into the beta.
Comment #334
stefan.r CreditAttribution: stefan.r commentedComment #335
stefan.r CreditAttribution: stefan.r commentedThis addresses the comments in #333 by saving theme engines into state in the theme handler and scanning profiles before modules in _system_module_rebuild_info(), as well as various changes to
GetFileNameUnitTest
so it can deal with the lack of a file scan.If this is green this just needs some feedback now from someone familiar with the extension system.
Comment #337
joseph.olstadHead of D8 is broken, I couldn't run my test plan even when I did a hard reset to HEAD with no patches.Modules aren't installing and cannot run a cache-rebuild without an error:drush cache-rebuild
This isn't a problem with your patch, but a problem with HEAD of D8 dev.I also tried Beta 10 and it also had similar problems.*EDIT*I reinstalled again from D8 head, without patching , but was still unable to install a module according to my test plan. @stefan.r do you have a test module you can share that works with recent commits of D8 ?
I'm testing using the migrate_plus module that was recently updated in April to work with D8 but it's not installing.
drush en migrate_plus
PHP Fatal error: Class 'Drupal\taxonomy\Plugin\Field\FieldType\TaxonomyTermReferenceItem' not found in drupal/core/modules/field/src/FieldStorageConfigStorage.php on line 160
also, I see that the test failed for the patch, what commit were you developing from? it seems the rapid flux of D8 dev is putting a curv ball on development of this patch.*END EDIT*
**DOUBLE EDIT**
retesting, double checking config
**END DOUBLE EDIT
** triple EDIT **
ok, configuratoin issue resolved, retesting
** end triple EDIT **
Comment #339
stefan.r CreditAttribution: stefan.r commentedHEAD is fine according to https://qa.drupal.org/8.0.x-status, I think this is a testbot issue so I've queued the patch for re-testing.
Comment #340
joseph.olstadok, I applied your patch after installing HEAD
, should also test installing HEAD with patch (haven't yet)
here's what I noticed:
installed migrate_plus (some module from github)
removed it from the filesystem
ran a cache-rebuild
didn't notice anything
then ran drush updatedb
I expected the message "The following module is missing from the file system: migrate_plus"
This is good
as for the getPath error:
related to these lines changed in InstallStorage.php
Here`s where the problem is, however I'll let @stefan.r weigh in on this.
Comment #341
stefan.r CreditAttribution: stefan.r commentedYes, that's to be expected if the file is missing. It's an ugly errror that we'll need to fix :)
But the patch is green now, so before digging into details too much ideally I would first like some high level feedback from someone familiar with the extension system as to whether this is the right approach at all.
Comment #342
joseph.olstadok so that is expected behaviour?
what also happens now is the site no longer is functional. Trying to use the UI interface gives this error:
LogicException: A stray drupal_render() invocation with $is_root_call = TRUE is causing bubbling of attached assets to break. in Drupal\Core\Render\Renderer->doRender() (line 393 of core/lib/Drupal/Core/Render/Renderer.php).
*edit*
I guess paralysing the site is one way of dealing with missing files. Definately a change from D7.*edit*Comment #343
joseph.olstadIf we could show an error to the interface (web site error message/warning) saying the same message that comes up in drush then that'd be more helpful.
So example: go to the site and you'd see something like: "error: The following module is missing from the file system: migrate_plus bootstrap.inc:254" then maybe the end user site admin might know what caused it.
I was sort of expecting what D7 would behave but yes things change, D7 was pretty forgiving when it came to moving modules around and having things appear and disappear. I understand however that there may be really good reasons for making changes in D8 and that the D8 core maintainers are busy with that.
Comment #344
stefan.r CreditAttribution: stefan.r commented@joseph.olstad this should fix your issue.
Not saying it will fix your broken site (which may need a cache rebuild or similar), but it should prevent the getPath() error from appearing and breaking your site in the first place :)
Comment #345
joseph.olstadok, patch 344 fixed the broken site problem. I'll do a few more tests maybe in a few days. Got some young sick kids to take care of, signing out.
Comment #346
joseph.olstadlets trigger the test bot
Comment #347
stefan.r CreditAttribution: stefan.r commentednitpicks
Comment #348
dawehnerI think we should document why we can't use the extension discovery
Can't we at least use \Drupal::root() at some point here?
This is kinda odd ... why can't we rely on drupal_get_path() here? Every new introduction of ExtensionDiscovery feels kinda wrong in this patch.
Mh, so we couple those two methods together? Not sure whether this is really the right thing to do regarding stability.
Comment #349
stefan.r CreditAttribution: stefan.r commented@dawehner thanks, as to 3 and 4: I agree they are ugly as mentioned in #321/ #325, but we can't rely on
drupal_get_path()
there. #333 explains why; basically because we don't do a file scan anymore.So we need to get their path some other way. Do you have any idea what to do there instead? I asked @catch for advice and he suggested priming the
drupal_get_filename()
cache, which we can only do if we actually know about the location, so for now I only saw a couple ofExtensionDiscoverys
as a realistic way out without making too many changes elsewhere. Longer term we'd probably want to get rid of those though...For instance in 3, as
drupal_get_path()
only works for the current profile, and inModuleHandlerTest
we pass on another profile to that function, we don't have any way to allow for that without making changes elsewhere.As to 4, do you have any suggestion as to what to do there instead? The problem is, when we do a module scan we have to know about the profile location as it calls
drupal_get_path({profile})
inExtensionDiscovery::scan()/setProfileDirectoriesFromSettings()
.Also keep in mind, everywhere we do workarounds like these now, we used to do a file scan anyway :)
Comment #350
stefan.r CreditAttribution: stefan.r commentedThis replaces the
DRUPAL_ROOT
usage in this patch by\Drupal::root()
and documents the concern in point number 1 from @dawehner's comment.As to the different workarounds used, I don't really see any way around those without significant re-work elsewhere -- which I would be all for, but for now we may have to live with this, unless core committers change their minds when they see to what extent we need workarounds when we get rid of the file scan.
I'll try to document the individual instances for every single workaround, but just to summarize for now, the reasoning used was:
a. If we know the physical location of the file, if possible skip the
drupal_get_path()
call altogether and just hardcode the location. Otherwise, if we can't get around thedrupal_get_path()
call, just prime thedrupal_get_filename()
static cache.b. If the physical location of the file is unknown and system module is available, do a module rebuild.
c. If the physical location of the file is unknown and the system module is not available or we are in the middle of a rebuild, perform an
ExtensionDiscovery
scan.Comment #351
stefan.r CreditAttribution: stefan.r commentedJust to document all the individual changes that the removal of a file scan and the inability to get an extension listing required:
1. drupal_check_profile() @ core/includes/install.inc:
Performs an ExtensionDiscovery as this function is only called in the installer, when the system module is still unavailable and we don't yet know where all the modules are located. This way we prevent a call to drupal_get_filename() in module_load_install which would have needed a file scan.
2. install_begin_request() @ core/includes/install.core.inc:
Before installing the system module, primes the drupal_get_filename() static cache with the location of the system module as we know its exact location.
3. install_begin_request() @ core/includes/install.core.inc:
Before installing the user module, performs a module rebuild as the system module is now available.Before installing the user module, primes the drupal_get_filename() static cache with the location of the user module as we know its exact location.4. InstallStorage/ExtensionInstallStorage::getAllFolders():
Performs an ExtensionDiscovery scan on themes/modules/profiles instead of relying on a drupal_get_filename() file scan in ExtensionDiscovery::getComponentFolder(), as this method is called during install where the system module is not yet available. This had been marked as a @todo anyway: "// @todo Refactor getComponentNames() to use the extension list directly." Note this also required a small change in TestInstallStorage.
5. system_get_info() @ system.module:
The order of the ExtensionDiscovery scans changes: we have to scan for profiles before being able to scan for modules, as a module scan requires knowing where the active profile is located (which we don't always do here). We save the result of this scan into the drupal_get_filename() static cache so we can use drupal_get_path() in ExtensionDiscovery::setProfileDirectoriesFromSettings()
As to the changes in tests:
- ConfigDependencyTest::testConfigEntityUninstall():
Performs a module rebuild as the system module is now available.
- KernelTestBase::enableModules():
Performs an ExtensionDiscovery scan as this function receives a profile that is not the current profile. We don't yet have a way to receive inactive profile information in an uncached way.
- GetFilenameUnitTest::testDrupalGetFilename():
Performs a module rebuild as the system module is available.
- ModuleHandlerTest::setUp():
Performs a module rebuild as the system module is available. Hardcodes the location of the testing and minimal profiles as these are known and shouldn't change.
- ThemeHandlerTest::testThemeInfoAlter():
Performs a module rebuild as we are looking to install a new module while the drupal_get_filename state data is still unavailable.
- ScanDirectoryTest::setUp()/testOptionKey() and InstallerLanguageTest::testInstallerTranslationFiles():
Hardcodes the location of the simpletest files directory as it is already known and shouldn't change.
- UrlAliasFixtures::sampleUrlAliases():
Primes the drupal_get_filename static cache as we are looking to install the system module here and its location is known and shouldn't change.
Tangentially related:
- ThemeHandler::rebuildThemeData():
Now caches theme engine data as well, as otherwise drupal_get_filename has no way to retrieve this information without a file scan.
Comment #352
stefan.r CreditAttribution: stefan.r commentedThis documents the individual workarounds in code as well.
Comment #353
stefan.r CreditAttribution: stefan.r commentedComment #354
catchThis could hard-code the user module location too - would save the full rebuild.
Comment #355
stefan.r CreditAttribution: stefan.r commentedI pinged @catch for feedback and in terms of the overall approach this should be OK as all the workarounds are far from the critical path and there's only 5 of them in the actual code. Most are in tests.
A lot of this will need to be removed as part of the refactoring suggested in #2186491: [meta] D8 Extension System: Discovery/Listing/Info, so just to keep track of this we've settled on adding a @todo in all the affected spots, as opposed to adding a separate method.
I'm including an interdiff with the previously RTBC'ed patch in #289 for easier reviewing.
Comment #357
stefan.r CreditAttribution: stefan.r commentedUpdating issue summary and removing a typo that I accidentally introduced in the previous patch.
Comment #358
stefan.r CreditAttribution: stefan.r commentedAs to installing the user module in the installer, let's hardcode its location as well as @catch had suggested in #354.
Previously I had thought we could do the module rebuild there and could skip the other rebuild in the installer that was happening anyway, but this is less of a change in the logic and consistent with what we do with the system module.
Comment #359
stefan.r CreditAttribution: stefan.r commentedHmm, this test failure exposes the fact that any ExtensionDiscovery::scan() for a list of modules needs to be able to get the profile path from drupal_get_filename(), which is a problem if we haven't ever run system_module_rebuild().
We've already implemented a workaround for this in this patch in
_system_rebuild_module_data(),
and @dawehner indicated he didn't much like the coupling withdrupal_get_path()
that this introduces.I see three ways out here:
a) Do the same workaround as in _system_rebuild_module_data() in ExtensionInstallStorage::getAllFolders() -- ie. do a profile scan before a module scan and save the location of the profile into the drupal_get_filename() static cache. Again, this introduces the same coupling @dawehner was worried about, but we're hopefully removing all this for #2186491: [meta] D8 Extension System: Discovery/Listing/Info anyway.
b) Keep the module rebuild right after installing the system module. The next module rebuild is in install_profile_modules() -- this data is only statically cached and we are looking for dependencies (which are not cached anywhere), so as this happens in another request we make the installer some 200 milliseconds slower :/
c) Refactor ExtensionDiscovery::scan() to lose the drupal_get_filename dependency -- maybe doing a recursive call to itself to figure out the location of the profile directory. This is a speed decrease on every module scan though, so probably the worst option out of the three. However it would remove the need for the workaround in _system_rebuild_module_data(), which could just remain as is.
As a) introduces the smallest performance hit, likely that's the "least worst" option?
Comment #360
stefan.r CreditAttribution: stefan.r commentedThis is what #359.a) would look like...
Comment #361
stefan.r CreditAttribution: stefan.r commentedComment #362
stefan.r CreditAttribution: stefan.r commentedJust FYI, I am finished working on this and this patch just needs further review now on the newly introduced changes since #289.
I am including with this comment an interdiff with the latest RTBC patch in #289.
Comment #363
Fabianx CreditAttribution: Fabianx commentedAssigning to catch for review
Comment #364
stefan.r CreditAttribution: stefan.r commentedThanks, just sharing our little IRC conversation from 10 minutes ago here for everyone's information:
Also tiny nits I found on final review, for future reference after @catch reviews:
needs reflowing
80 cols
stray dot
s/'minimal'/$profile/
Comment #365
catchDidn't do a thorough review but I think the changes here are OK as well. It's verbose but the trade-off vs supporting this behaviour on all requests is worth it - and the @todos mean we have a decent chance to clean things up when the listing/info issue goes in.
Comment #366
Fabianx CreditAttribution: Fabianx commentedThen lets RTBC this.
Comment #367
stefan.r CreditAttribution: stefan.r commentedThis adds the spelling corrections I had mentioned earlier
Comment #368
Fabianx CreditAttribution: Fabianx commentedThis change was what you wanted?
And the comment above should still speak of minimal?
Comment #369
stefan.r CreditAttribution: stefan.r commentedYes, that was merely for code readability. The profile is hardcoded to minimal a line above this all, if it gets changed, people can update the comment.
Tests will be red if this is wrong :)
Comment #370
stefan.r CreditAttribution: stefan.r commentedComment #371
catchCommitted/pushed to 8.0.x, thanks!
Comment #373
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedPretty sure there was an intention to backport at least some part of this (whether the trigger_error or the trigger_error + cache or something else).
Comment #374
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedDrupal 7 patch was already finished (without the trigger_error).
If we are not going to do such an extensive rewrite of drupal_get_filename in D7 we can use that as a start.
Comment #375
stefan.r CreditAttribution: stefan.r commentedThanks @jeroen.b for the D7 patch and thanks again @Fabianx and @catch for the helpful reviews.
The trigger_error in the D8 patch made a whole bunch of tests fail, let's see how this one does.
Comment #376
stefan.r CreditAttribution: stefan.r commentedComment #378
marcelovaniNice work with the D8 patch.
Now, back to D7, when I go to the modules page, I get this error:
PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE (cid = 'drupal_get_filename:missing')' at line 1: DELETE FROM {} WHERE (cid = :db_condition_placeholder_0) ; Array ( [:db_condition_placeholder_0] => drupal_get_filename:missing ) in cache_clear_all() (line 167 of /Users/marcelo/vm/vagrant/repos/drupal/includes/cache.inc).
ps: I am testing with DB cache.
It looks like, when clearing the cache, providing a cid, the parameter bin is required. See http://cgit.drupalcode.org/drupal/tree/includes/cache.inc?h=7.x#n150
in this case, you would have to change it to
cache_clear_all('drupal_get_filename:missing', 'cache');
in the two functions.Comment #379
stefan.r CreditAttribution: stefan.r commentedComment #381
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedFixed the cache_clear_all
Comment #383
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedInterdiff in a txt now to prevent testing.
Comment #385
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedTrying to fix the fails.
Comment #387
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedWhoops, that was stupid.
Comment #389
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedI think this is a little cleaner approach without cache or statics in the error handler.
Should have no fails. I wonder where all the "The following module is missing from the file system: default." are coming from though.
Also included an interdiff with #379.
Comment #391
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedRewrote one comment.
Fixed one of our own tests.
The "default" module updates are all in upgrade tests. For some reason there is a "default" record in the system table for those upgrade tests.
The "simpletest_missing_module" is from a test from simpletest to actually test for a missing module.
Any suggestions to solve that?
Comment #393
marcelovaniI don't see the "default" but I do see this
"The following module is missing from the file system: ."
Comment #394
marcelovaniThat is related to $name being passed as empty by other contrib/core modules
We already spoke about this here https://www.drupal.org/node/2383823
Comment #395
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedCan you try to figure out what module is doing that and open an issue for that project?
Current status of the patch is that it only fails in tests because:
Point 1 I'm not really sure how to fix that without removing the test.
Point 2 I'm not really confident that I know why the db seed contains a "default" module.
Also attached a patch in which it's easier to find the causing module for the trigger_error because it adds an debug_backtrace to the trigger_error.
Comment #396
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedDicovered something, this seems really wrong, this is from the system table in the upgrade DB seed:
Comment #397
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedI think I have a fix for the missing "default" module. In Drupal 7 it has been renamed to "standard".
This gets updated in a hook_update_n() of the system module but obviously this code runs before running the updates.
Comment #399
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedThis should fix all the upgrade path tests. Only one thats left now are the lookups for simpletest_missing_module.
Comment #401
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedIndeed, only the simpletest_missing_module left now. It's caused by modules/simpletest/simpletest.test:
Comment #402
joshtaylor CreditAttribution: joshtaylor commentedRunning just this, and no contrib modules, trying to do a test run on any core module gives me the following:
I'm on Ubuntu, I also get this on Travis with PHP 5.4/5.5/5.6 - https://travis-ci.org/joshuataylor/commerce/builds/61874609
Comment #403
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commented@joshtaylor D8 has already been committed, maybe it's better to open a new issue for that.
I went for the same method as the bootstrap tests. All tests should be green now. Not sure if it's the way to go though.
Comment #404
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedComment #405
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedFor reference, interdiff with #373.
Comment #406
joshtaylor CreditAttribution: joshtaylor as a volunteer commentedCreated a followup issue https://www.drupal.org/node/2486083
Comment #407
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedSome minor tweaks and found a mistake with restoring the error handler.
Comment #408
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedUpdated interdiff with #373.
Comment #409
fgmNew side-effect of this patch: run-scripts is broken for some configurations #2486083: The following module is missing from the file system: standard
Comment #410
fgmComment #411
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedWhy did you put it back to 8.0.x-dev?
Can you please keep patches/discussions about the new errors in the new issue?
Comment #412
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedOther issue has a patch and is RTBC, so let's keep this at 7.x-dev.
Comment #413
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedComment #414
stefan.r CreditAttribution: stefan.r commentedWell @catch and @Fabianx had already mentioned the caching+trigger_error could work for D7, and #407 is essentially equal to the previously RTBC'ed D8 patch in #289. It's green too, so I guess we're close here?
This could use better documentation -- why are we changing the profile from default to standard. Are there any possible secondary effects here?
80 cols
Maybe this should merely wrap the drupal_get_filename call, I don't see that we need to put it around the other statements and having restore_error_handler there twice looks confusing.
Comment #415
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedAddresses your comments.
I think the context of update_fix_d7_install_profile() and the "@see system_update_7049()" comment are enough now, what do you think?
Comment #416
mikeytown2 CreditAttribution: mikeytown2 commented#415 looks good! Hopefully not to many people have the error_level set to 1 or 2 on a production site with a missing module.
Comment #417
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedYeah this should definitely be mentioned in the release notes. Possibly with a link to a documentation page with the following structure:
Anyone feels like writing it up? If not I'll see when I have some spare time.
Comment #418
stefan.r CreditAttribution: stefan.r commentedJeroen.b yes those changes in the latest patch look good. Can we link to this documentation page in the error message as well?
Comment #419
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedCreated a documentation page on https://www.drupal.org/node/2487215.
Here's a new patch with a mention of the documentation page in the warning.
Comment #420
stefan.r CreditAttribution: stefan.r commented'Default' profile has been renamed to 'Standard' in Drupal 7.
80 cols
s/bad/missing/
s/drupal_get_filename/drupal_get_filename()/
80 cols
s/drupal_get_filename/drupal_get_filename()/
Comment #421
stefan.r CreditAttribution: stefan.r commentedComment #424
stefan.r CreditAttribution: stefan.r commentedComment #425
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedUpdated the comments.
Comment #426
stefan.r CreditAttribution: stefan.r commentedManually tested this.
1. The warning now says:
"User warning: The following module is missing from the file system: crumbs. For more information, see the documentation page. in drupal_get_filename() (line 934 of /Applications/MAMP/htdocs/drupal7/includes/bootstrap.inc)."
That last part isn't very informative (..in drupal_get_filename()...), and the transition from "page" to "in" seems odd, ideally we'd be pointing to wherever drupal_get_path()/drupal_get_filename() was called from, though not sure we can without debug_backtrace() magic. It will also show once for every drupal_get_filename() call, maybe we should only show each type/file combination once per request?
2. We may want to remove a file from the "missing" cache if it actually reappears. Ie. we could do the cache_get if the static is not set, unset the item in the static if it reappears and do a cache_set if ($missing !== $cache->data)
3. I'm a bit worried about the 1 day cache lifetime. I think it should be unlimited as otherwise things may be broken for a day and then suddenly work again, which could lead to a lot of head-scratching :)
Maybe the worry was that if we don't expire the cache, we mark the wrong items as missing, but that is not actually a problem, we still check in the old location with a file_exists() call before we check the missing file cache. So temporarily removing a file and then putting it back would actually be fine. It's just he moving that's problematic. That would only work after 1 day if we keep the expiry. Only fixing this upon visiting the modules/themes overview pages or upon a full cache clear (as we do now) might be the better way to go?
Comment #427
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commented1. Yeah, but even with some backtrace magic you won't be able to pull it off. Let's say that module_load_include called it, then the message still won't help you any further because it's just another function that's called a million times. If people want to find the calling function, I think it's best if they add the debug_backtrace manually. I can't really justify any line of debug_backtrace in code.
I think it's better to always show the PHP warning. I think developers will ignore it when we don't ("it's just one time after the cache clear... whatever"), especially if we change the cache time to unlimited.
Any idea how we can make the PHP warning better?
2. Agreed. But that might be unwanted because we have to do an unnecessary cache_get.
3. Agreed.
Comment #428
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedAny reason not to move this code down to where it's actually going to be used (i.e., the filesystem-scanning part of the function)? Then we don't call it at all in most cases, and a regular drupal_static rather than drupal_static_fast would be fine.
I suspect we need some protections around this, since this could run during the early bootstrap (and looking at how other calls from inside this function are handled, they have special protections). For example, consider what happens if cache_get() is undefined, or if it throws an exception because the database isn't available yet.
This probably shouldn't be translated here, because we want the admin to see it in their language (not necessarily the visitor's language) when viewing the watchdog logs - i.e. just like how the watchdog() function works.
I guess we have no way to do that with trigger_error(), so maybe we could just not translate it at all for now and leave that for a followup issue.
These won't work in PHP 5.2.
Is that even guaranteed to return a legal module name? Maybe it doesn't matter for this test, but I think $this->randomName() is a more standard way to do that.
Is there a reason not to do this inside system_rebuild_theme_data() and system_rebuild_module_data() instead?
drush pm-update
, or other Drush commands that change things in the file system while interacting with Drupal)?Comment #429
stefan.r CreditAttribution: stefan.r commented1. Moving both the static and the database cache initialization into the if block that has the file scan in it (and making it a regular static) makes sense.
2. @jeroen.b see the protections around db_query() for an example :)
3. may have to remain untranslated
As to 7, the worry there is Drupal runs system_rebuild_theme_data and system_rebuild_module_data more often than necessary, so we wouldn't have as much of a performance improvement. On the other hand, it does seem safer...
Comment #430
stefan.r CreditAttribution: stefan.r commented#427:
1. Yes, I guess we may have to do without that then. As to when to show the warning, I meant once per module per request, not once after a cache clear. So it would still appear on every page, except we wouldn't see 10 warnings for the same module on top of every page.
As to the warning, I guess it's not a problem in the message itself. More so in the error handler, which shouldn't show the "in bootstrap.inc line xyz" part, and should translate the message. Maybe we can make a
missing_file_error_handler()
?2. We can live with the
cache_get()
on every request if it only occurs if we have modules that would require a file scan as mentioned by @David_RothsteinComment #431
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedI don't really have any time left this week to work on this, so if anyone feels like picking it up :)
@David_Rothstein, feel free to make some changes to the documentation.
Comment #432
stefan.r CreditAttribution: stefan.r commentedMaybe in drush this should be merely a status message? Would it affect execution of a command if it's a user warning? (as it is right now)
Maybe we can have @moshe take a look as to whether this would cause unnecessary warnings?
Comment #433
stefan.r CreditAttribution: stefan.r commentedShould this patch also be updated to account for when we're moving modules around (ie. from
sites/all
tosites/all/contrib
? I think this is automatically fixed upon module rebuild anyway, but previously I think we were allowed to move modules to a different location than what was listed in the system table, at which point the file scan would find the new location. In such a case it's arguable whether the module is actually "missing"Comment #434
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedYes, moving modules is a problem, because sometimes you cannot even reliably clear caches (however that problem is pre-existing for other things). Can we add a test for that?
Comment #435
stefan.r CreditAttribution: stefan.r commented@Fabianx sounds good, however not sure how to deal with moved modules. If we cache the new locations of any moved files that's adding an extra layer of complexity.
Maybe we should just disallow that, and make people do a cache clear after moving a file instead?
Comment #436
stefan.r CreditAttribution: stefan.r commentedCurrent patch is untested and will still break with moved files, but should at least be a step in the right direction as it addresses some of the concerns above.
Comment #437
stefan.r CreditAttribution: stefan.r commentedComment #438
EvanSchisler CreditAttribution: EvanSchisler at Acro Commerce commentedWhen I tried to use this patch(from comment #437), it patched with no issues. However, after clearing my caches I received these notices and warnings(none of these were raised prior to the patch):
User warning: The following module is missing from the file system: imagcache_actions. For more information, see the documentation page. in drupal_get_filename() (line 942 of /home/acro/accounts/frame2/frame2/wwwroot/includes/bootstrap.inc).
User warning: The following module is missing from the file system: imagcache_actions. For more information, see the documentation page. in drupal_get_filename() (line 942 of /home/acro/accounts/frame2/frame2/wwwroot/includes/bootstrap.inc).
Notice: Undefined index: commerce_discount_product_category_has_specified_terms in commerce_discount_product_category_rules_condition_info() (line 28 of /home/acro/accounts/frame2/frame2/wwwroot/sites/all/modules/commerce_discount_product_category/commerce_discount_product_category.rules.inc).
Notice: Undefined index: commerce_order_compare_order_amount in inline_conditions_rules_condition_info() (line 37 of /home/acro/accounts/frame2/frame2/wwwroot/sites/all/modules/inline_conditions/inline_conditions.rules.inc).
Notice: Undefined index: commerce_order_has_owner in inline_conditions_rules_condition_info() (line 59 of /home/acro/accounts/frame2/frame2/wwwroot/sites/all/modules/inline_conditions/inline_conditions.rules.inc).
Notice: Undefined index: commerce_order_contains_products in inline_conditions_rules_condition_info() (line 81 of /home/acro/accounts/frame2/frame2/wwwroot/sites/all/modules/inline_conditions/inline_conditions.rules.inc).
Notice: Undefined index: commerce_order_has_specific_quantity_products in inline_conditions_rules_condition_info() (line 115 of /home/acro/accounts/frame2/frame2/wwwroot/sites/all/modules/inline_conditions/inline_conditions.rules.inc).
Notice: Undefined index: commerce_product_contains_products in inline_conditions_rules_condition_info() (line 139 of /home/acro/accounts/frame2/frame2/wwwroot/sites/all/modules/inline_conditions/inline_conditions.rules.inc).
Notice: Undefined index: commerce_product_has_specified_terms in inline_conditions_rules_condition_info() (line 163 of /home/acro/accounts/frame2/frame2/wwwroot/sites/all/modules/inline_conditions/inline_conditions.rules.inc).
Comment #439
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commented@EvanSchisler
That is a bug in the Imagecache Actions module, they already fixed it.
Please update the Imagecache Actions module.
See: #2166715: Typo causes extra query every page load
This is actually a good sign, because this patch finds such bugs.
Comment #440
stefan.r CreditAttribution: stefan.r commentedAny further ideas about what to do when a module is moved?
I'm thinking if the data diverges from whatever we had in cache, we should just mark the module as missing and refuse to load it until caches are cleared. It's a bit of a BC break but not a very severe one, I guess the file scan was intended mostly just in case we didn't have any cached data yet -- but if we do have (faulty) data, we should just wait for that to be rebuilt instead.
Comment #441
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedYeah, agreed. We can also extend the warning message if the record was in the cache.
Maybe something like: "If you just moved this module, try to clear the cache"?
Comment #442
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedThere is three cases of which we cover two now:
1) Something refers to a file that can never be found (typo, etc.). => trigger_error and cache in missing to avoid further file system scans.
2) Something refers to a module that was removed from the file system since => trigger_error and cache in missing to avoid further file system scans.
3) Something refers to a module that was moved => We do a file system scan every time.
Now case 3) is interesting as while it seems to "self-heal" it does so at the cost of run-time performance.
E.g. if you move a module, it will self-heal for those cases of rename using drupal_get_filename() (but e.g. registry files won't be found), but it won't be fully healed until the module system table is rebuild.
There is 2 possibilities for us here:
a) Ignore and hope caches are cleared once in a while fully => what we have now
b) Use missing not only for negative entries, but also as a 2nd level cache to cache what we found as missing
In that case $missing[$name] => FALSE is "not found" and $missing[$name] => $filename would be "found elsewhere".
In that case a file system scan would only be done when even our missing cache would be outdated.
That likely would be the perfect solution, but not sure if its in scope or not.
( There is a reason however why we don't just write-through the database in such cases as the cache is possibly database agnostic and e.g. in a memcache or redis server. )
Comment #443
stefan.r CreditAttribution: stefan.r commentedI think something like b) would be the better way to go here, I'm afraid there would be even more edge cases with odd behavior with what we have now.
Comment #444
stefan.r CreditAttribution: stefan.r commentedMaybe something like this could work? (please do not use in production yet as I haven't tested this yet, patch is just to illustrate)
Comment #445
stefan.r CreditAttribution: stefan.r commentedI had forgotten to implement reading of the cached file scan result in that previous patch. This patch still should not be used in production until this is further tested/reviewed...
Comment #447
stefan.r CreditAttribution: stefan.r commentedLooking at the whole patch, it needs a bit of further work to deal with the case where the cache_get('drupal_get_filename:missing') fails/is unavailable in early bootstrap. Just resetting the missing static during the database bootstrap doesn't feel like the right solution.
There's also a couple of trailing spaces.
Comment #449
stefan.r CreditAttribution: stefan.r commentedComment #450
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedLooks really great!
Comment #451
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedAdded myself to proposed commit message
Comment #452
stefan.r CreditAttribution: stefan.r commentedWell-deserved, thanks again for the helpful reviews :)
Adding some documentation
Comment #453
stefan.r CreditAttribution: stefan.r commentedAnd this removes the cached "missing" record if a missing file reappeaars.
Comment #454
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedArguably its an edge case: If the module gets moved back, we don't remove it from missing cache?
Comment #455
stefan.r CreditAttribution: stefan.r commentedComment #456
stefan.r CreditAttribution: stefan.r commentedMaybe this would work?
Comment #457
stefan.r CreditAttribution: stefan.r commentedCan we think of better verbiage here? It should probably explain the data in the system table is outdated and that that is what a cache clear might fix.
Comment #458
stefan.r CreditAttribution: stefan.r commentedJust wondering about #428.8, ie. the behavior changes posed by the current patch...
a) If we remove a file altogether, we will cache that it's missing and cease to do any further file scans until a module/theme rebuild happens (such as during cache clears and when visiting admin pages). This means that once a file has been marked as missing, we can't put it back in another location anymore but we can put it back in the location logged in the system table, at which point everything will be OK again and the file will be removed from the missing cache.
b) If we move a file from wherever it was supposed to be, we will cache the result of the file scan and use that instead of a file scan from then on. If the file then moves again from its new location, we will do a new file scan, and cache the result of that. However, if we move a file back to where it was supposed to be, everything is OK again and we remove the record from the missing cache.
In the case of moving a file, the "missing" caching is only relevant as long as we haven't done a rebuild. The rebuild should log the new location into the system table.
So outside of whatever is happening in
drupal_get_filename()
, the only limitation/behavior change that this patch introduces is that now we can't first delete a file, and then put it back in another location (as it has been marked as missing already, so we don't do a file scan). Reactivating the file would require either putting it back in its original location first, or performing a cache clear.Anything I'm missing here?
Comment #459
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedThis is getting complicated. If a file moves, and we know what the old location and the new location are, why do we need to put that in a cache? Shouldn't we really just do something like this:
Not sure if we'd want to do that inside drupal_get_filename(), but even if not we could do it at the end of the page request (like the patch is currently doing for the cache).
Interesting question. That plus some other reasons makes me wonder if we should go back to using watchdog() after all (rather than trigger_error()). When I originally suggested trigger_error() it was based on a couple things:
OK, I did https://www.drupal.org/node/2487215/revisions/view/8451805/8483001 to address the disabled/uninstalled issue.
Comment #460
stefan.r CreditAttribution: stefan.r commentedI agree we want to simplify the current patch as much as possible, so updating the system table at the end of the request with the moved modules that had been statically cached could potentially work, although it could cause a bit of a slowdown on subsequent requests and be more of a BC break than just doing the caching: the data in the system can now change on any regular request as opposed to just after a rebuild. The other worry is we may not catch all the moved files so this may turn out to be spread out over several requests. @Fabianx what do you think?
As to just doing a watchdog() call, the problem is it's not annoying enough for people to clean up their stuff and people can easily miss/ignore it. A moved module may not be a big deal, but a missing one is. And even with the cache, the first few requests after a missing module cache clear are still going to be very slow as we re-do the file scans.
Ideally we want to nag administrators and developers as much as possible. Maybe instead of a trigger_error() we should do a drupal_set_message() (in addition to a watchdog() call)?
Comment #461
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedI think the cache is actually pretty nice for such reasons.
Yes we might want to change the code to be simpler, but writing the system table with partially new information is likely not the right choice. And doing dynamic rebuilds - like menu_get_item() vs. menu_rebuild() vs. menu_rebuild_needed() has a ton of own problems ...
I still think a cache layer is a good thing to take care of what we would have done anyway (falling back).
It just means we cache the result of the fallback file system scan - which should be fine.
Maybe moving the file_system_scan to its own function would make this all clearer, and allow clearer wrapping of it?
--
drupal_set_message() for something like this is a no-go -- end users see it and that is problematic ...
Comment #462
stefan.r CreditAttribution: stefan.r commentedThinking this over a bit further I think you may be right here as far as the moved file caching is concerned.
I agree the drupal_get_filename() function is getting way to long, taking out the file system scan part would be a good start at making it clearer.
As to the status message, I was thinking we wrap the drupal_set_message() in an if statement so only admins and developers see it, but we don't really have a way of determining that do we? So it'd really have to be trigger_errror() if we want to annoy a bit more than just a watchdog message?
Comment #463
stefan.r CreditAttribution: stefan.r commentedJust to clarify, other than potentially disrupting Drush, are there any further concerns with a trigger_error()? Maybe we could live with a Drupal message to users with permission 'administer site configuration' only?
Comment #464
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedI don't fully understand what's wrong with updating the system table when we know it's out of date. Essentially, that already is our cache of where known files are in the filesystem (and it already has other caches on top of it), right? Which is what makes me a little uneasy about adding a whole separate cache for the case of a module/theme that has moved somewhere else.
But perhaps moving this to a separate function as suggested above would make it cleaner and less complex.
Could probably do something to check the 'error_level' variable, like error_displayable() I suppose - but yeah, it's not ideal.
Not really - a minor concern about the lack of translatability (mentioned earlier) but it's not too bad.
And a general concern that we're going to see lots of support requests from people who suddenly see this message displaying on the screen on their production sites. They shouldn't have set their production site up to display errors to the screen in the first place, but the reality is, I bet there's a lot of overlap between sites that don't direct error messages to the logs on production and sites that delete modules from the filesystem without disabling them :)
Comment #465
stefan.r CreditAttribution: stefan.r commentedHmm the last concern seems like the more serious one. A substantial amount of installs probably doesn't have that configured right. And I guess the same concern would still apply if we say that error_level must be
ERROR_REPORTING_DISPLAY_ALL
(even if we recommend people not to):On the other hand, the error message is a disturbance but a minor one; as soon as the missing module is dealt with, the error disappears. And in the link in the error message we can also have a "Why do end users see this error message?" heading where we point people to wherever they can set the error_level to "Errors and warnings" or "None".
I'll let others have some input on this as I can see the arguments both for only displaying this to logged in administrators (to prevent breaking a number of misconfigured sites), but also for making it a user level notice/warning and potentially breaking a few sites, but pushing a much larger number of sites to fix their stuff
Comment #466
stefan.r CreditAttribution: stefan.r commentedAs to fixing the system table in real time, I'll let Fabianx chime in but my main concern was that it felt like a substantially more invasive change than just caching the file scan result.
And yes, in a way it's a cache, but it's a pretty important one compared to other caches, and updating it in real time feels like making it easier than it should be to change whatever is in there (which should be leading, and not what may be -temporarily- happening on the file system), or to only do a partial update when several files have moved. Having to wait for a rebuild means less things can go wrong in the mean time, ie. when one person passes a database dump to someone else with almost the same files installed, except this one module is duplicated in two locations on one system but only in one location on the other.
Comment #467
stefan.r CreditAttribution: stefan.r commentedWhich makes me think of another edge case that would be a change from current behavior, both in the case of caching the result of the file scan and in the case of changing the system record directly: a module being installed in more than one place, ie. in
sites/all
as well as in the profile folder.For instance:
sites/all/modules
toprofiles/some_distribution/contrib/modules
profiles/some_distribution/modules
?), and now have two versions of this module on our file system....and I could see something like this happening when people symlink a distribution for different sites but want to change a module for one site only.
Result without the patch: Drupal would have used the module in the third location as it would do a file scan every time.
Result with the patch: Drupal will use the module in the second location (as it caches the result of the file scan), and only after a module rebulid, would it go back to using the file in the third location.
Comment #468
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedMy main concern is that the system_rebuild_module_data() would rebuild the data for the full module at once (and also take care of registry, etc. potentially if done via drush cc all), while this partial update of system table would only do for the accessed files. The system table is then out of sync.
It would be a different story if we rebuild the system table for this whole module / directory. (if that is possible)
==
Error messages:
Yes, trigger_error is potentially problematic, but given a good message and a warning in the release notes, that should be okay.
==
One module at two locations at once:
- The behavior has always been undefined, therefore we can change the behavior without problems.
Comment #469
stefan.r CreditAttribution: stefan.r commentedAnd scheduling a complete rebuild for the next request, such as
variable_set('system_rebuild_needed', TRUE)
(as opposed to justsystem_list_reset()
) is also going to be problematic I guess, the rebuild can take quite a while...Comment #470
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedThere is only one row per module in the system table, so I still don't fully understand most of that concern. For each module, either we know the correct location or we don't. (And if the concern is about related modules, e.g. multiple submodules in the same package, note that in practice all enabled modules will be touched in the same page request anyway, at least for uncached page requests.)
OK, yeah, I can buy that we'd like to avoid any problems with that.
Comment #471
stefan.r CreditAttribution: stefan.r commented...which reminds me, we should be checking if a module/theme has moved in
system_list()
(where thedrupal_get_filename()
cache gets primed after a module_load_all() under normal circumstances).We may also need to look what to do for theme_engine files, I don't think those locations are stored in the system table?
I'm willing to investigate if updating the system table directly is feasible if @Fabianx thinks it's fine after @David's reminder that all enabled modules are loaded on every request. But there's not a single edge case where (moved) disabled modules/profiles could be a problem? (I think we already loads disabled themes on every request?) And as far as core is concerned I believe the system table should only hold module and theme files, can it hold anything else?
Comment #472
stefan.r CreditAttribution: stefan.r commentedAlso, moving a file may still leave the menu_router include_file data and the registry data inconsistent, but that would have happened before the patch as well, right?
Comment #473
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedYes, if there was a chance to fix that, that would be great though :-D, but the problem is that then we need a lot of locking to ensure we don't get new races, while with the current cache layer the worst that can happen is that we do another file scan ...
Comment #474
stefan.r CreditAttribution: stefan.r commentedDiscussed the system table updating at the end of a request on IRC with @catch and @Fabianx and I think I'm going to explore taking out the file scan and caching its result a bit further first, maybe we can make that simpler after all...
Comment #475
stefan.r CreditAttribution: stefan.r commentedComment #476
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedOverall looks much better now, will have a closer look again tomorrow - hopefully.
Comment #477
stefan.r CreditAttribution: stefan.r commentedThanks for having a look. Just a few questions to look into when you do:
Maybe we can we think of a better name for this.
It's not too confusing that this is called the same as the leading static in drupal_get_filename()?
Are we fine with the way this is named?
Comment #478
stefan.r CreditAttribution: stefan.r commentedSome nitpicks, and moved the cache clearing to system_list_reset()
Comment #479
stefan.r CreditAttribution: stefan.r commentedUpdated some comments
Comment #480
stefan.r CreditAttribution: stefan.r commentedComment #481
joseph.olstadTested patch
480481.Applied patch.
installed test contrib module
removed test contrib module from the filesystem
clear cache, no notices
drush updatedb
WD system: The following module is missing from the file system: test. [error]
No database updates required [success]
then did some basic performance profile of a cache clear.
Missing module from filesystem test took 43 seconds to perform the cache clear.
Restored test module to the filesystem.
Performed another cache clear.
Test configuration took exactly 43 seconds to perform the cache clear, same as when files were missing.
From these rudimentary tests I can confirm that patch
480481 is working as expected.Thanks
RTBC+
Comment #482
stefan.r CreditAttribution: stefan.r commentedThanks for the manual testing. So Drush ought to be fine then, I guess it's only during a "drush make" where an error may be problematic? (and a user-level PHP warning may still be fine)
I also noticed this triggers a missing file warning on every request, but only triggers a moved file error once, so we should probably update the patch to warn on every request with moved files as well.
I think the ability to move modules may actually be an unintended consequence of the file scan, because even if the drupal_get_filename()/drupal_get_path() call works correctly for a moved module, the registry / menu router records still remain broken.
Comment #483
stefan.r CreditAttribution: stefan.r commentedThis should show the "moved file" warning every request.
Comment #484
joseph.olstadHi stefan.r 481 was sufficient for my tastebuds (warning when running updatedb, no problem with drush make, I believe that drush make confusion may have been refering to some earlier discussion about how some distributions of drupal might forget to pm-uninstall between versions while removing a feature or custom or contrib module), hence the need for this issue to get a final patch to commit and into core.
Comment #485
joseph.olstad(same message as above)
Comment #486
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedOnly skimmed it, but the new approach looks cleaner so far!
We probably need to test this with the Update Manager also. In Drupal 8, this is causing an issue there because the Update Manager code was relying on drupal_get_path() to check whether a module/theme exists in the filesystem or not (see #2042447-133: Install a module user interface does not install modules (or themes)). I think Drupal 7 might have a similar issue...
Comment #487
joseph.olstadOk, I re-ran a similar testplan outlined in comment #481 however this time instead of removing only one test module I removed 4 features/modules from the filesystem thinking that I might notice bigger performance differences (however I only sampled a half dozen times or so.
Results: between 38 and 47 seconds for a cache clear. The higher number came when the patch was rolled back(reversed). I ran more cache-clears afterwards and could not get the higher number again.
Visual tests were done (no perceptible performance differences between tests with and without patch) - xhprof would come in handy about now.
The patch otherwise working as expected, the only thing I could say is that the drush message includes HTML markup that isn't pretty in the drush command line mode.
Here's what the output looks like:
Also, for some reason it looks like the same/similar notice shows up twice.
It'd be nice to pull out xhprof and run benchmarks with and without the patch 483, and compare that to patch 102 (which we're currently using in production thanks). This would give us a better idea of where we're at in terms of performance and have some sort of benchmark set up.
My personal server is one of the better places to test this because it's load doesn't fluctuate very much and it's on a physical box with predictable performance/availability.
For now, aside from the markup on the text, I don't have any concerns other than wanting a full xhprof performance diagnosis.
The text output in 483 differs from 481 in the html tagging of the drush text output added to 483.
We've come so far, almost to the finish-line, please help!
Comment #488
stefan.r CreditAttribution: stefan.r commented@David_Rothstein regarding the updater, D7 does also have a ModuleUpdater::getInstallDirectory() and ModuleUpdater::isInstalled() that do a drupal_get_path(), but I'm not sure those should be triggering a (partial) cache clear. As opposed to D8 (where we dispose of the file scan), in the current D7 patch we merely cache the result of the file scan, which as far as the update manager is concerned we should probably stick with, for consistency. So I gues the only thing we want to test is that it doesn't trigger any "missing file" errors where it shouldn't?
@joseph.olstad I am not sure that comparing with #102 will help us further along here -- both patches skip the file scan so the performance should be the same.
The user warning will show up once for every call to drupal_get_filename(). Indeed as mentioned earlier we should probably make that be once every request instead.
As to the drush message, what do you suggest we do about it? Maybe a separate issue (in Drush or in Drupal core) where we make the error handler show cleaner messages on CLI?
Comment #489
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commented#488: The HTML message thingy is out-of-scope here, but just doing it once per request should be fine.
Comment #490
stefan.r CreditAttribution: stefan.r commentedYes, that was what I was suggesting as well, i.e. keep the HTML in the message, and solve the CLI HTML filtering in another issue. It could even be fixed in Drush itself.
Comment #491
stefan.r CreditAttribution: stefan.r commentedUpdate manager seems fine.I don't think there's actually a case where it would throw an unnecessary error as it doesn't allow for installing previously non-existing modules anyway. I don't think it needs to be (partially) clearing any caches either -- if the rest of drupal considers the module to be in a certain place, the update manager can assume that too. And if it's missing, it won't be trying to update it anyway.wrong, I forgot about the installerAdded a "one error per request" function.
Comment #492
stefan.r CreditAttribution: stefan.r commentedRemoving a trailing space
Comment #493
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commented$errors_shown vs. $errors_created won't fly ...
Comment #494
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedRight, that's what I meant. Thanks for checking. I'm surprised it doesn't trigger an error actually - the code in the Update Manager that does this looks very very similar in Drupal 7 and Drupal 8 so I would expect that in both cases it calls drupal_get_path() on a nonexistent module and therefore triggers the error message.
Comment #495
stefan.r CreditAttribution: stefan.r commented@David_Rothstein well maybe I was doing this wrong then, perhaps a silly question but at what point would that be calling a drupal_get_path() on a nonexistent module?
The thing I tested was updating an existing module - should that call a drupal_get_path() on a nonexistent module at any point?
Comment #496
stefan.r CreditAttribution: stefan.r commentedComment #497
stefan.r CreditAttribution: stefan.r commentedAh, I had overlooked admin/modules/install -- let me test that.
Comment #498
stefan.r CreditAttribution: stefan.r commentedIndeed that was triggering an unnecessary error, here's a try at fixing that.
Comment #499
stefan.r CreditAttribution: stefan.r commentedAnd we can actually keep casting to boolean as
(bool) NULL === FALSE
Comment #500
stefan.r CreditAttribution: stefan.r commentedAnyone willing to review/test this? :)
Comment #501
dagmarI ran a quick search and it seems the pattern cache_get('_cache_identifier') is not used anywhere in core.
Maybe you could use a more descriptive value for that cache ID. For reference here is the list of the current values for the cache_bootstrap table:
Comment #502
stefan.r CreditAttribution: stefan.r commentedA lot of those use the function name though. This one just happens to begin with an underscore.
How about we rename it to
file_scans
?Comment #503
mikeytown2 CreditAttribution: mikeytown2 commentedJust gave #499 a spin and it seems the cache it not written under all circumstances; even if drupal was fully bootstraped.
Put this in a php file like test.php at the root level; note that REQUEST_TIME_FLOAT requires php 5.4
Run it a couple of times to get baseline on how long drupal takes. Remove a module and run test.php again a couple of times. Visit a normal drupal page like /admin, then hit test.php and should get faster.
Why doesn't test.php get faster after the 2nd run, why do I need to run menu_execute_active_handler() for this to work; drupal_deliver_html_page()? What about Drupal acting as a backend for something else?
Making sure we're OK with this or if we want to look over what we have currently; maybe use a shutdown function? Then again looking at module_implements_write_cache() makes me think this is in the correct place...
Comment #504
stefan.r CreditAttribution: stefan.r commentedbecause the cache is written to in drupal_page_footer()
Comment #505
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commented#503: Yes, the benchmark needs to include a call to drupal_page_footer(), that is normal and needs to be used for bootstrap testing, also for module_implements testing ...
Comment #506
mikeytown2 CreditAttribution: mikeytown2 commentedRTBC from me
Comment #507
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedLets do it!
Comment #508
pwaterz CreditAttribution: pwaterz commentedFirst off, thanks so much for all the hard work on this! I have a question and I can't quite make out based on the book of comments here. Will this patch prevent modules from being disabled if the module goes missing? Like if I put the module back in place, will it re enable it self?
Comment #509
marcelovaniNothing to be worried about pwaterz, it will check again anytime the cache is cleared
Comment #510
fgmFunny, I just happened to notice this issue yesterday diagnosing a slow D7 customer site : I had not seen a site with removed/moved modules for years.
I think the patch may be ignoring one case: it assumes the DB can only be unavailable during an install of it the DB can not be reached. However, I suspect that with
$conf['page_cache_without_database'] = TRUE
, combined with$conf['page_cache_invoke_hooks'] = TRUE
(it is usually set toFALSE
in that case), this might be triggered to look for hook implementations after a non-DB cache flush. This probably needs to be tested to be sure the case is covered, but even if it works, the comment leading to the exception would then need to be completed by this case.Since this patch adds an exception, the phpdoc should be updated with a @throws.
Comment #511
stefan.r CreditAttribution: stefan.r commented+++ b/includes/bootstrap.inc
@@ -847,68 +854,269 @@ function drupal_get_filename($type, $name, $filename = NULL) {
+ catch (Exception $e) {
I don't think #2 is needed? drupal_get_filename doesn't throw an exception does it?
Comment #512
stefan.r CreditAttribution: stefan.r commentedComment #513
fgm@stefan.r : my bad, you're right. If the patch keeps this pokemon catch, any exception will not surface, so there is nothing to add.
I cannot but wonder if it is really sane to catch all the exceptions which can be raised by
db_query
, though. Because, although this is not documented, db_query can throw exceptions, at least\DatabaseConnectionNotDefinedException
andDatabaseDriverNotSpecifiedException
from\Database::openConnection
and\PDOException
from the\DatabaseConnection::query()
method, and catching\Exception
is normally seen as bad practice.Comment #514
stefan.r CreditAttribution: stefan.r commentedWell yes... it's not pretty but in practice I can't see this doing any damage. Considering the same catch is in the current function as well and we merely leave it unchanged, getting that exactly right would be out of scope for this patch.
Let's re-RTBC if you agree with the updated comment in #512?
Comment #515
fgmIndeed.
Comment #516
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedThe patch is complicated but well-written and well-documented. I'd like to get it in, but I tested it and it didn't seem to fully work:
Undefined index: module in _drupal_get_filename_fallback() (line 985
.I think some of this might be due to the 'system_filepaths' static variable, the logic of which I found hard to follow and I'm not sure it's working correctly.
Do we even need that variable and the logic surrounding it? I know we've been through a few iterations here and there are a lot of edge cases we're trying to handle, but I'm trying to remember if there's a reason we couldn't use simpler logic. For example, leave the main part of drupal_get_filename() alone, but at the end, before performing a filesystem scan:
Would anything be missing if we simplified it like the above?
Some other things:
and:
I'm still a little wary of those changes, but the patch seems to catch every case in core that needs it, and I couldn't find any in contrib after grepping through a few popular modules. Plus the worst that happens is a trigger_error() anyway, so it should be OK.
We probably also need a section on "the module never existed in the first place" (e.g. where this error appears due to a bug in a contrib module). Most of the first paragraph could move to this section; also, the reference to module_exists() in the first paragraph doesn't look right - I don't think this issue is necessarily limited to enabled modules.
I think this would be better named $trigger_error (and documented as "whether to trigger an error"). Also I think it should be
@param bool
rather than@param boolean
?"moved within the file system" might be better wording here. Although based on the above discussion, it could be better to say "has moved or has recently appeared in the file system"?
Should be a complete sentence since this is an inline code comment (not PHPDoc), e.g. "See system_update_7049()."
Comment #517
stefan.r CreditAttribution: stefan.r commentedAren't 1 and 2 the intended behavior (other than the notice)? Let me have a look.
Comment #518
stefan.r CreditAttribution: stefan.r commentedI don't know that we need to be catering to use case #2 (drupal_get_filename on a module not yet in the system table) - we hadn't thought of it earlier but it sounds like enough of a edge case for it to be out of scope here... we can keep the file scan there I think.
As to the notice, that seems very odd. How can I reproduce this? Merely installing a module and moving it doesn't work. Are you calling drupal_get_filename() before system_list() had been called? (in module_load_all()). That's where the static is being built...
As to the proposal of getting rid of the system static entirely, I'm not too sure. I remember discussing this with Fabianx/catch(?) and I think it's better to keep it. The problem is we want to know if something has moved relative to the path in the system table which we consider "leading" (see earlier comments). Removing that feels like too drastic of a change.
Uploading a new patch that incorporates the feedback. It seems the file scan for moved modules wasn't being cached properly indeed :)
Comment #519
stefan.r CreditAttribution: stefan.r commentedUpdated the docs at https://www.drupal.org/node/2487215 and added a CR draft at https://www.drupal.org/node/2581445
Comment #520
stefan.r CreditAttribution: stefan.r commentedActually I misread the line number... 985 was not about the system static but about the $file_scans static. That should now be fixed with the latest change:
This tells us to write to persistent cache if it's a new "moved" record as well (i.e. when $file_scans[$type][$name] is not yet set)
I did manage to reproduce the notice with the old patch (on a bootstrap module - not sure if that mattered), but with the new patch this seems to work.
Comment #521
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedMy testing was with a non-bootstrap module (I used the Diff module). Here are the steps I took:
The issue was reproducible with #512 but I can confirm that #518 fixes it.
I saw the comments, but I guess it's just not clear to me how the system static relates to them. With the code already in drupal_get_filename() before this patch, we know that the only time we run a file scan is when the info from the {system} table is unavailable, missing, or out-of-date. Given that we already do that, I'm not sure what else we get from the static variable. Can anyone think of a (manual) test scenario where the static is expected to be necessary?
Comment #522
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedThe change record and documentation page are looking good. I made a few more updates to them now, and I think they are probably set.
Comment #523
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedBtw. another module leading to a file scan on each drupal_load(): flickrapi (fixed in their -dev versions already), but the module_load_include with a typo really puzzled us at first ...
So good to see this moving forward fine.
Comment #524
stefan.r CreditAttribution: stefan.r commentedImplementing the suggestion in #521 to see what it looks like.
Not sure it's a simplification but I don't see any big problem with it either.
Comment #526
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedHm, I thought it might be simpler than that actually. I will try to take a look at this later and see.
I guess another part that isn't clear to me is why all the code at the bottom of drupal_get_filename() that modifies &drupal_static('_drupal_get_filename_fallback') is necessary? It seemed to me like that logic is (or at least could be) handled by _drupal_get_filename_fallback() itself.
Comment #527
stefan.r CreditAttribution: stefan.r commentedLet's see if this fixes the test fail, the trigger_error() message was being suppressed because the system table didn't exist.
As to the code at the bottom of drupal_get_filename(), it's necessary just to keep the cached list up-to-date and remove entries that used to be marked as moved or missing which have reappeared at their original locations.
If the modules reappear, a call to drupal_get_filename('reappeared_module') won't go through _drupal_get_filename_fallback(). This was the case in the previous version of the patch as well FWIW.
@David_Rothstein expressed a concern about the logic in the current code being too different to the previously RTBC'ed code... could anyone give the changes a close review and see if this should actually be a worry?
Comment #529
stefan.r CreditAttribution: stefan.r commentedHmm looking at these failures I wonder if we should go back to the system table check solution that had been RTBCed 4 months ago. It's not that complicated really - it just caches the result of the 2 system table checks we do in core.
Not sure catering to the "newly introduced file" use case is worth the effort and additional risk here... as that would be fixed by a cache clear anyway and is not as common.
Comment #530
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedSorry for the delay. I'm going to take another look at this tomorrow.
Comment #531
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedOK, I tried reworking the patch and think I may have succeeded in simplifying it. All the logic is now self-contained underneath _drupal_get_filename_fallback() and reasonably straightforward, and all the various cases should be handled.
The patch is bigger than the last one, but only because I expanded the tests also. (I'm posting the interdiff too, but the patch itself is probably easier to review.)
But if _drupal_get_filename_fallback() doesn't get called, why do we care about its cache being up to date? It doesn't have to be (and can't be) an accurate record of exactly what's in the filesystem - it's just a cache of what we found the last time we scanned the filesystem.
The only case I can think of where this would make a difference would be if you deleted a module, then restored it to its original location, then moved it to a new location (all without a system_list_reset() anywhere in between). But the earlier patches don't actually deal with that either (I tested this with #527) since they don't do a cache_get() at the bottom of drupal_get_filename() (and presumably shouldn't, for performance reasons). The cache doesn't actually get updated in those cases either, right?
It also seems fine not to deal with that. I think you all came to a similar conclusion earlier - we need to support putting a module back in its original location (so the site can recover after something goes wrong) and we need to support moving a module too, but we shouldn't have to support any more complicated scenarios because it's reasonable to assume you need to visit admin/modules or clear caches after making big changes to the filesystem.
Comment #532
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedRemoving a line of code that was probably a bad idea on my part...
Comment #533
stefan.r CreditAttribution: stefan.r commentedThis seems well thought out, it's quite a bit simpler and has extra test coverage, so +1.
I agree we can live with the cached file scans not being completely up to date - it changes behavior a bit in some edge cases, but those would be solved through a system_rebuild_module_data.
Is the & needed here?
Comment #534
stefan.r CreditAttribution: stefan.r commentedShould we also have a prominent reminder in the release notes about turning off error messages on production systems and about this patch potentially causing thousands of entries in the error log?
Comment #535
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedThanks for the review! Anyone able to do some more review or testing of this?
I believe so since it's what allows functions that call this to modify the cache. (Same reason that the definitions of drupal_static() and batch_get() have it.)
Yeah, good idea. Regarding the thousands of entries in the error log, I wonder if there's a way to make it fewer? But the only obvious way I can think of would be to show the message once after the cache is cleared (i.e. only when the actual file scan is performed) which would perhaps be too infrequent.
Comment #536
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commented+1 looking good, also great work on the test coverage.
I believe that's what we had before. Only adding a log entry when the actual scan is performed.
It makes more sense to do that because mostly that's a point a developer/site builder will see it (on update.php or drush cc all), and afaik, that's the only one that is able to fix such issues.
Comment #537
stefan.r CreditAttribution: stefan.r commentedNot a fan of only doing it on file scan, we want to incentivize people to fix their stuff, just a single log entry would be easily ignored... and missed on actual page loads on dev environments (which display trigger_error messages in a drupal_set_message)
Comment #538
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedOne question:
Should we use the "all_modules_loaded" condition here as that is what we really care about and not the bootstrap phase to be consistent?
Overall:
+2 to the new patch, RTBC
Comment #539
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedI get that, but I wonder if showing it every time is going to help to do that. I'd rather go with a hook_requirements approach if we want to motivate people to fix it.
The current approach works fine for me (show on actual file scan), but maybe that's because I'm a CLI guy and I see the message clearly when I run drush updb.
Comment #540
stefan.r CreditAttribution: stefan.r commented@jeroen.b maybe we're actually on the same page -- I agree the current patch is fine. I think it does a trigger_error() on every failed drupal_get_filename call, rather than on every uncached file scan.
I remember we went through this earlier in the thread, the only real disadvantage is that we may be a bit too annoying in people's logs, but that's solved as soon as they fix the problem, and other PHP errors trigger on every request as well... So building some kind of limit in the error handler feels like unnecessary optimization and doing it only on uncached file scans feels like not doing it often enough. A hook_requirements() would be nice, but that can be looked at in #2464601: Check for missing modules in hook_requirements
Checked with @Fabianx as well and looks like we're good with the current patch:
Comment #541
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedActually, do we need to do either? What we're writing to the cache here is just the results of a filesystem scan, so why would it matter?
Instead of that, we could probably just check the #cache_merge_done key and make sure it's set, since that indicates that whatever came from the cache will get written back to the cache, and also ensures that the cache_set() call will succeed (in case this is done extremely early in the bootstrap) since it means cache_get() ran successfully earlier in the same request.
Comment #542
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commented@stefan.r, yeah, you're right. On every page request is fine.
Once every real filescan is way too easy to ignore/miss.
Comment #543
stefan.r CreditAttribution: stefan.r commented@David_Rothstein I guess now that we don't care about whether modules that are marked as missing/moved actually still are it may be fine to write to it always.
The only problem (which we already have anyway) is that it can be a bit confusing when debugging, and the "unnecessary" caching could lead to us cache a file scan before we want it to be cached in some edge cases (i.e. not finding the new location when a module is in both the profile and in sites/all), but that would be solved through a cache clear anyway.
Wouldn't this possibly be an extra unnecessary cache_set() call though - or the #cache_merge_done check would fix that?
Comment #544
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedSorry, just to clarify, I didn't mean #cache_merge_done would be the only check (the #write_cache would still be checked too) so something like this:
Comment #545
stefan.r CreditAttribution: stefan.r commentedYes I understood that much, just haven't given it any though yet - for now I don't see any big problem, so if you think this optimization is an improvement I have no objection.
OTOH if we leave the patch as-is, the only drawback is we don't write to persistent cache in the edge case of finishing the request before full bootstrap, doing a file scan that we currently do anyway, which I could live with as well, we already do a trigger_error() anyway.
Comment #546
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedI could go either way also and don't have a huge preference. I think checking #cache_merge_done might be slightly nicer since it's more self-contained, so here's what the patch looks like if we change it to that.
Either way, we won't be able to get this patch in for this week's release unfortunately. Too many changes too recently, and I think we'll benefit from getting a bit more review here (and hopefully real world testing). I think we can definitely aim to get it in the next release though, which probably won't be too many months down the road either. Sorry for pushing back a lot on this issue, but since it's a big change that affects a very low-level part of Drupal, I think it needs extra review to make sure we get it right.
Comment #547
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedI agree with #546 that it is better to skip this release rather than rush it in. +1 to that.
The interdiff looks fine to me.
Comment #548
joseph.olstadThe new patch looks good, seems to be quick.
I have only a minor quirp, I'm a CLI guy and like using drush.
Patch #102 makes a nice pretty CLI output as follows:
WD system: The following module is missing from the file system: linkit. [error]
however Patch #546 outputs HTML onto the CLI in drush as follows:
PHP Warning: The following module is missing from the file system: <em class="placeholder">linkit</em>. In order to fix this, put the module back in its original location. For more information, see <a href="https://www.drupal.org/node/2487215">the documentation page</a>. in includes/bootstrap.inc on line 1079
However I'm fine with that as I'd rather have the PHP warning than no warning at all (but prefer the WD system message as in patch 102).
Either way it looks great! Thanks.
Comment #549
stefan.r CreditAttribution: stefan.r commentedThe CLI problem should rather be solved in drush IMO... this is not the only commands that outputs HTML
Comment #550
stefan.r CreditAttribution: stefan.r commentedAnyone have #546 running on production or care to give this another review?
Comment #551
skaduI'm not running #546 on production but just patched a development environment. All seems to be working fine for now. I will keep an eye on it over the next couple of weeks.
Only thing to mention was the patch output:
Not sure if there is the offset because of something on my end or the patch, just wanted to share my findings.
Comment #553
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedI have been using #546 on a few sites for over a month now. No problems occurred.
Comment #554
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedActually, this generates an error when you want to install a profile.
Line 1183 of includes/install.inc adds the profile name to the list of dependencies.
Line 1188 tries to load it as a module, which results in a error by _drupal_get_filename_fallback_trigger_error, as there is no module by the name of the profile.
Comment #555
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedComment #556
stefan.r CreditAttribution: stefan.r commented@jeroen.b is loading a profile as if it were a module something we should support? I.e. should drupal_get_filename('module', 'profile_name') return the location of the profile?
Comment #557
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedI'm not sure.
AFAIK drupal_get_filename has basic support for profiles by telling it you want a profile:
However, the profile is not a type "profile" in the system table, it's a "module" (which is kinda weird). So we should probably prime the cache somewhere as we do in D8.
drupal_get_filename('module', 'profile_name', 'profiles/profile_name/profile_name.profile');
Comment #558
stefan.r CreditAttribution: stefan.r commentedI'm OK with that solution - it'd need a file_exists as well
Comment #559
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedHere's a new patch. The file_exists is already in the code (4 lines above my addition). If it can't find the profile, the installation doesn't even start.
Just tested it, no error in install.
Comment #560
sylus CreditAttribution: sylus as a volunteer commentedHmm with the latest patch testing in Travis CI I am getting the following with Drush SI:
Comment #561
stefan.r CreditAttribution: stefan.r commented@sylus could you may want to backtrace that - seems it's doing a drupal_get_filename('', 'module')?
@jeroen.b that fix seems fine...
Comment #562
sylus CreditAttribution: sylus as a volunteer commentedAh your right issue is related to #2383823: drupal_get_filename() should check if $name is empty I removed that patch as thought this patch alone would be enough, sorry for the noise :)
Comment #563
pwaterz CreditAttribution: pwaterz commentedI'd like to make note of some what related issue that we were facing. At my current job we ran into an issue where we moved our site from dev/stage to production. The production environment sometimes has hicups and also sometimes can place modules in different directories that dev or stage. During some of the build hicups modules were not placed in the proper directories. We had issues where entire sets of modules would be auto disabled by drupal core. After a few hours of digging around I found that is was caused by update.php/drush updb. The update process calls https://api.drupal.org/api/drupal/includes%21update.inc/function/update_... to check 'compatibility' of modules and disables them if they aren't. One issue is that if a module is missing for some reason, like a broken symlink and you run drush updb, the system table gets updated and these modules get disabled. This is really scary for our organization because we run hundreds of drupal sites, and if this was to happen on massive scale, yikes! I think there should be a way to disable this feature in drupal core. Does this go along with this ticket or a new ticket?
Comment #564
stefan.r CreditAttribution: stefan.r commented@pwaterz new issue - it's unrelated to this one
Comment #565
stefan.r CreditAttribution: stefan.r commentedHas anyone tested #546 or #559 yet?
Comment #566
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedI have been using #559 since I posted the patch.
But it's my own patch so would be weird to put it to RTBC.
Comment #567
stefan.r CreditAttribution: stefan.r commentedI gave this another look and I concur with Fabianx in #547... David_Rothstein's changes still look great to me. joseph.olstad, sylus, skadu and jeroen.b have tested this in production, and #559 makes sense, so back to RTBC.
Comment #568
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedAgreed this look ready. Given the somewhat disruptive nature of this (and the late date compared to tomorrow's release window) I'm going to hold it for one more release; especially if we do #2598382: [policy] Adopt a 6-month feature release schedule for Drupal 7 (similar to Drupal 8) and use pseudo-semantic versioning this would be a great fit for an April 20 release of Drupal 7.50 which would have some other bigger changes also. But either way, rest assured it should be in the next non-security release after this one. Thanks everyone for sticking through a 550+ comment issue :)
I could not quite figure out #559 though. The change looks harmless but I can't figure out how to generate the problem (at least #546 seemed to work fine with the core install profiles).
@jeroen.b, what install profile were you using when you experienced that?
In particular:
I think for line 1183 you're referring to https://api.drupal.org/api/drupal/includes!install.inc/function/install_... but the code there is very explicit about making sure that the profile does not get listed as a dependency of itself. So it seems like it shouldn't ever try to load the .install file via the code you mentioned. That's why I'm not sure how to trigger this.
Comment #569
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commented@David_Rothstein
It's a custom profile. I just tested it but couldn't reproduce it with Drupal 7.41.
Then I remembered that specific profile used the Omega8 version, I was able to reproduce it with that.
I'll take it up with Omega8 when this patch lands. Patch #559 can be ignored.
Comment #570
kenorb CreditAttribution: kenorb commentedShell command to test for missing modules:
while read -r file; do [ -f "$file" ] || echo "$file is missing."; done < <(drush sqlq "SELECT filename FROM system WHERE status = 1")
Comment #571
Fabianx CreditAttribution: Fabianx at Tag1 Consulting commentedThere is a problem with this patch (or rather sites that update to it):
It can under bad circumstances together with #2450533: SchemaCache is prone to persistent cache corruption / drupal_get_complete_schema() is not robust against recursion break a site:
From cold caches:
- hook_init() =>
- entity_get_info is called (by e.g. menu_set_custom_theme => get_admin_paths() => entity_translation_admin_paths() => bean => ... )
- That triggers drupal_get_schema()
- That triggers drupal_get_complete_schema()
- That calls field_sql_storage_schema, which checks all fields for data
- This calls drupal_get_filename() to load the .install files
- This leads to watchdog being triggered. (Note: The module is just referenced somewhere, but not even in the system table anymore).
- rules_watchdog triggers a loading of event rules
- That triggers ctools_load_plugins => ... => entity_get_info()
- That triggers drupal_get_complete_schema() again, which returns the now incompletely build schema()
- That leads to errors and entity_get_info() being incompletely populated.
While not the fault of this patch, this condition is only resolvable by putting the original module back (or an empty .install file) as it is very hard to find and prune all data referencing somewhere to a module file.
On the other hand, without this patch a system scan would always be done, which would lead to strange and unexplainable slow down and the warning message gives a dev at least a chance to restore the modules.
However a watchdog call can have side effects during early bootstrap or during cache rebuilding.
So my proposal:
'Cache' the watchdog call in a static variable and execute it during shutdown (drupal_exit()), where we also create the caches for e.g. modules. That reduces / eliminates the side effects of this. There is a slight chance that a site crashes and drupal_exit() is not called. However I think we need to live with that.
The 'bad' cache is still good and solves the performance problems to avoid re-scanning the filesystem.
@David: What do you think?
Comment #572
stefan.r CreditAttribution: stefan.r commentedSo, postpone execution of any and all watchdog calls that happen during early bootstrap to the end of the request?
Should we discuss in a separate issue?
Comment #573
Fabianx CreditAttribution: Fabianx at Tag1 Consulting commented#572: No, not all watchdog calls, that would indeed be a follow-up. Just this newly introduced one for now.
e.g.
Comment #574
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedHm, I think that makes sense, but the patch is currently using trigger_error (rather than a direct watchdog call) in order to get the message to appear on the screen on development sites... I'm not sure offhand what happens if you try to do a trigger_error that late (after sessions have already been committed).
We could also look at trying to get #2450533: SchemaCache is prone to persistent cache corruption / drupal_get_complete_schema() is not robust against recursion fixed in the same release. (At this point I think we're aiming for May 4 rather than April 20.) But I think you're saying that you see other potential risks with this watchdog call being directly triggered by a low-level function?
It looks like rules_watchdog() is doing some pretty heavy stuff for a low-level hook like hook_watchdog(); ideally it would not do that, but it is what it is :)
Comment #575
Fabianx CreditAttribution: Fabianx at Tag1 Consulting commented#574: Indeed, my distribution site used an older version of this patch, which is why I was confused.
If we only trigger_error(), we should be okay. I will confirm on my site, but this means the patch can go ahead as planned.
And I agree that rules_watchdog() should at least also check for BOOTSTRAP_FULL or at least their (rules) hook_init() should be moved as last item in the hook chain. I will open an issue.
Comment #576
marcelovaniI think this is related to https://www.drupal.org/node/2324587#comment-9397273
Can you check if you are using the version of Rules that contains the fix of patch #37?
Comment #577
stefan.r CreditAttribution: stefan.r commentedSo I guess this will be for a May 4 release?
Just a note that we'll need to update the docs at https://www.drupal.org/node/2487215
Comment #578
SocialNicheGuru CreditAttribution: SocialNicheGuru commentedI applied the patch (awesome patch by the way).
I deleted a directory by accident
I got this on my screen
Notice: Undefined variable: trigger_error in drupal_get_filename() (line 888 of /includes/bootstrap.inc).
the line is:
should we pass in 'TRUE' for $trigger_error since the variable is not defined and as no default value?
Comment #579
stefan.r CreditAttribution: stefan.r commented#578: I think we already do? You'll want to use the patch in #559 and ensure it is applied correctly, I don't see how you would get an "undefined variable" error like that when the function signature says:
Comment #580
alexmoreno CreditAttribution: alexmoreno commented#559 tested (with an Omega subtheme, although don't think it's relevant) and works more than fine.
I was doing some profiling and found an alarming file_scan_directory function being called more than 1.000 times per call. Applying the patch makes this to dissapear, although I'd recommend anyone to dig further and remove any entries or modules disabled but not uninstalled and similar.
IMO this should be merged ASAP, as it is relatively easy to reproduce in a big/medium site with a big team working for some time (you know, one disables a module, someone else enables, ... you get the idea).
Comment #581
stefan.r CreditAttribution: stefan.r commented@Fabianx re #575, doesn't trigger_error() cause a watchdog call?
Comment #582
stefan.r CreditAttribution: stefan.r commentedLooking at this with jeroen.b, back to NW for #571
Comment #583
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedAfter discussion with @stefan.r we decided to only delay the trigger_errors for the missing/moved errors so we don't have to touch other code base. We are storing the errors in a drupal_static and then doing a trigger_error in _drupal_bootstrap_full. We are not doing it in system_exit because it's too late to do an drupal_set_message so then the developer wouldn't see the error message.
Comment #584
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedWhoops, missed the interdiff.
Comment #586
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedAh, yeah, that makes sense.
Because we do the invoke in _drupal_bootstrap_full(), all the missing/moved errors after _drupal_bootstrap_full won't do a trigger_error.
My solution would be to set another drupal_static in _drupal_get_filename_fallback_trigger_error_invoke whether the invocation has already happened.
In _drupal_get_filename_fallback_trigger_error we would check for that static. The logic to do the actual trigger error would be separated in a new function. When the static is TRUE, the trigger_error happens directly, if it is FALSE the trigger_error is delayed until bootstrap_full.
Comment #587
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedHere's a patch that does what I described.
Comment #589
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedWe might need to update the bootstrap test a bit. It looks directly for a triggered error in the custom error handler, but since we delay the messages now it will fail.
Comment #590
stefan.r CreditAttribution: stefan.r commentedComment #591
Fabianx CreditAttribution: Fabianx at Tag1 Consulting commentedI like the idea, I am a little worried about the implementation.
For me this should just be:
Possibly can add something to the message as done before, but the implementation should really be a drop-in replacement for trigger_error(), which we call after we have completed the bootstrap_phase().
Comment #592
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedThanks Fabian. I get your feedback.
However, after some thinking, shouldn't we just make trigger_error "phase safe"?
Comment #593
Fabianx CreditAttribution: Fabianx at Tag1 Consulting commented#592: trigger_error() is an internal function, so we can't override it.
On the other hand I would not like to make our error handler depend on bootstrap phase.
So lets please do it the way I suggest in #591.
Comment #594
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedThat's not what I meant. I think we should fix it inside our own error handler instead of creating a trigger_error_safe() because we might not be the only one possibly calling trigger_error too early.
Comment #595
Fabianx CreditAttribution: Fabianx at Tag1 Consulting commented#594: And that is an excellent idea, but not feasible to put into this is already large patch.
Lets use _drupal_trigger_error_safe and _drupal_trigger_all_queued_errors as names then we can technically still remove the functionality again or re-use it from our error handler.
Reasoning:
- We add a new trigger_error() call, where there was none before
- Therefore we get new side effects from this trigger_error() call - e.g. my updb dieing
- If we remove the side effects for _just_ this call, we are good to go for the run-time
- This only means trigger_error() is called shortly bootstrap full phase is reached, which is an acceptable stage to trigger an error
- If we introduce queueing of errors in the error handler now, we get new side effects related to that
- This might lead to unexpected behavior on other sites, etc.
Therefore - even though the idea is great - and even though it would be better to fix this at the "core" in the error handler, this is not helping this patch to get into 7.50 any time soon.
Isolation of side effects needs to be our goal to get this patch in - if we want it now.
Comment #596
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedI get your reasoning. I would be fine with _drupal_trigger_error_safe just for the sake of getting it in.
Maybe we should create a follow-up issue for fixing the watchdog call inside trigger_error?
Comment #597
stefan.r CreditAttribution: stefan.r commentedYes, sounds like follow-up material :)
Comment #598
Fabianx CreditAttribution: Fabianx at Tag1 Consulting commented#596: Yes, a follow-up would be great!
Comment #599
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commented@Fabianx will you create the new patch or should I?
Comment #600
Fabianx CreditAttribution: Fabianx at Tag1 Consulting commented#559: Can you do that please? I am pretty busy atm.
Comment #601
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedThe above plan sounds good to me, but I don't understand this part from #591:
The main scenario we're trying to protect against here (from #571) is triggered in hook_init() (during full bootstrap) so won't the above code fail to protect against it? (As far as I know, drupal_get_bootstrap_phase() returns DRUPAL_BOOTSTRAP_FULL anytime after the full bootstrap phase has begun.)
But if we leave that code out (which would make the function's behavior more consistent/predictable anyway) then it makes sense to me.
I'd also consider calling the new functions _drupal_trigger_error_delayed() and _drupal_trigger_all_delayed_errors() (or similar - the key point being to use the same terminology for each).
Comment #602
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commented@David_Rothstein You are right about the naming of the functions, we should change it.
You are also right about drupal_get_bootstrap_phase() returning DRUPAL_BOOTSTRAP_FULL when it started bootstrapping, not when it finished bootstrapping.
That's actually inconsistent with documentation, as it says "The current phase is the one most recently completed by drupal_bootstrap()".
Thing is, we can't just remove these lines:
Because then we will have no trigger_error() when Drupal has been fully bootstrapped as _drupal_trigger_all_queued_errors() will never be executed.
Comment #603
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedHere is the new patch for completeness. Like @David_Rothstein said it will probably not work.
I could also not think of a proper way to test this edge case.
Comment #604
stefan.r CreditAttribution: stefan.r commentedComment #606
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedAh, I see.
Well it's kind of ugly but it could be possible to do something like the attached (rough code, untested).
It does seem to me like the key point here is not what stage of the bootstrap you're in, but whether or not you're potentially in the middle of an operation that can't be "interrupted". Because calling trigger_error() => watchdog() at any point in time will invoke a hook which runs arbitrary code which could involve something that breaks the operation you're in the middle of. I think that's effectively what we're trying to say here.
Note this patch is built on top of #546 (not #559) since I think we agreed in #568/#569 that the change in #559 wasn't necessary.
Comment #607
jeroen.b CreditAttribution: jeroen.b at .VDMi/ commentedThat would probably work yeah. Any idea how we can create a test for this?
Comment #608
stefan.r CreditAttribution: stefan.r commentedI think that may be fine, for the test we can check the watchdog table after a call to _drupal_trigger_error_with_delayed_logging() (if needed, from a test module, for example with a drupalGet() to a page that triggers an error)
Comment #609
stefan.r CreditAttribution: stefan.r commentedThis needs the @todo documented and ideally a test
Comment #610
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedI should be able to look into that a little later tonight.
Comment #611
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedHere is a new patch with the documentation plus additional tests.
In addition to testing the watchdog() call itself, I think I was able to test the actual scenario with drupal_get_schema() failing. It's a bit convoluted, but still may have been easier than setting up the Rules module in such a way as to trigger this as per #571 and then testing that manually :)
I'm also posting a patch with the new tests combined with the earlier patch from this issue. If this is working correctly we should see failures for some of the new tests in that case.
Comment #612
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedThe interdiff is compared to #606, by the way.
Comment #615
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedRetesting - that looks like a random testbot fail on the real patch.
The first patch failed with the expected failure.
Comment #616
stefan.r CreditAttribution: stefan.r commentedInterdiff looks great, with some very nice tests!
Comment #617
Fabianx CreditAttribution: Fabianx at Tag1 Consulting commentedThe new solution to just trigger the watchdog call delayed is great!
Giving credit to a lot of people on this issue (soooo many have tested the patch or given otherwise useful feedback).
Marking for commit.
Comment #619
Fabianx CreditAttribution: Fabianx at Tag1 Consulting commentedCommitted and pushed to 7.x! Thanks all!
Fixed on commit (as git had a warning related to whitespace problems being added):
Comment #620
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedThanks for going through and figuring out all those issue credits - that must have been a lot of work :)
I edited the change notice to make it a bit more relevant for site builders (who might see this PHP warning too), and published it:
https://www.drupal.org/node/2581445/revisions/view/8963663/9848351
I also edited the documentation page to indicate this is now in Drupal 7.50:
https://www.drupal.org/node/2487215/revisions/view/9220114/9848355
We are hopefully finally done with this enormously long issue. Thanks everyone!
Comment #621
DamienMcKennaFYI I'm seeing a weird regression in Metatag's tests from this change: #2761829: Getting drupal_trigger_error_with_delayed_logging errors on Drupal 7.x dev (7.50)
Comment #622
MustangGB CreditAttribution: MustangGB commented43 "missing" modules :'(
Comment #623
MustangGB CreditAttribution: MustangGB commentedSerious question now though, I have a "profiles/default/default.profile" entry in my system table, was there ever a hook_update_N() that should have removed this?
Comment #624
joseph.olstad@MustangGB , for those missing modules, if you can, put them back into the filesystem temporarily, then run a pm_uninstall either
drush pmu module_was_awol module_was_awol2 module_was_awol3 module_was_awol4 module_was_awol5 module_was_awol6 module_was_awol7 module_was_awolXX module_was_awol40 -y
or, (if all else fails) in the case that you cannot find the module(s) install this cleanup utility (for use with drush) or for some reason the uninstall fails:
download this ZIP of cleanup_tool
extract it to your sites/all/modules folder
USAGE:
For more details, please see project page
Comment #625
MustangGB CreditAttribution: MustangGB commented@joseph.olstad:
Yea don't worry I've already sorted them all out, except for "default".
My question was really regarding people that have upgraded from D5/D6 and whether "default" should have already been removed during the upgrade/migration process, and are there any consequences of it not having been dealt with, or should we just remove it now with a new hook_update_N().
(Nice module though)
Comment #626
stefan.r CreditAttribution: stefan.r commented@MustangGB can you open a new issue for the default.profile problem, in case we want to fix this in an update hook?
Comment #627
MustangGB CreditAttribution: MustangGB commentedSure: #2762241: Missing default.profile from the file system
Comment #628
charginghawk CreditAttribution: charginghawk at Genuine commentedFYI, the D8 fix spawned #2486083: The following module is missing from the file system: standard, an issue which seems to be appearing in 7.50. We may need to port the fix there to D7.
Comment #629
geek-merlinThanks everyone. A 40k patch, finally awesone!