Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
I've seen this once before and thought it was just a bad HEAD install, but it just came up again on #597236: Add entity caching to core.
This shows up on every request when loading a node with 50 comments: SELECT table_name FROM information_schema.tables WHERE (table_schema = :db_condition_placeholder_0) AND (table_name = :db_condition_plceholder_1)
.
Comment | File | Size | Author |
---|---|---|---|
#26 | 667098_26.patch | 1.9 KB | chx |
#24 | drupal-get_bootstrap_phase_broken-667098-24.patch | 369 bytes | plach |
#17 | bootstrap_phase_breakage.patch | 4.18 KB | catch |
#17 | bootstrap_phase_breakage_no_test.patch | 1.87 KB | catch |
#13 | bootstrap_phase_breakage.patch | 3.63 KB | catch |
Comments
Comment #1
catchOK to reproduce this, truncate {cache} then visit any number of node/n pages but not the front page. This will also be the same for any 'entity' page like user/n, taxonomy/term/n etc.
Here's what happens:
menu_custom_theme() calls menu_get_item(), menu_get_item() loads the objects for that page, this calls node_load(), which calls drupal_schema_fields_sql()
This eventually gets down to drupal_get_schema(). On a cache miss, drupal_get_schema() caches on these conditions:
menu_custom_theme() is called while we're still officially at DRUPAL_BOOTSTRAP_LANGUAGE, because it actually happens inside _drupal_bootstrap_full(), and the phase is set after that's completed.
This means that the schema will never be cached until you hit a page which both requires a schema hit, and calls it after _drupal_bootstrap_full() has completed, which includes the default front page in HEAD, but not many others I can think of.
We probably wouldn't have noticed this unless field_sql_storage_schema() was doing a bad SQL query, but it also means we load all .install files which is a big memory hog.
This patch is a bit ugly but fixes it, along with a bad drupal_function_exists() revert found at the same time - we just set $completed_phase immediately before running the bootstrap phase, instead of afterwards, so that operations which occur during the full bootstrap, know that they're in that rather than the previous one. This is how things happened in D6.
Comment #2
catchNote this also means that any function called during hook_init() currently gets DRUPAL_BOOTSTRAP_LANGUAGE if they consult drupal_get_bootstrap_phase(). That's probably a good way to test this thinking about it.
Comment #3
catchTracked down the change to #370454: Simplify page caching - up until then, we set the phase prior to executing it, not afterwards - that looks to be a side-effect of the code there, not its intention.
So while we probably want to do it differently to what's in the current patch, that's the main bug here.
Comment #4
catchRe-titling, uploading proper patch with a test, and also the test separately - should have one failure.
Comment #5
catchchx reviewed in irc, changing the ternary back to an if.
Comment #6
chx CreditAttribution: chx commentedHm, good catch.
Comment #7
int CreditAttribution: int commentedAll patch's fail the tests
Comment #8
chx CreditAttribution: chx commentedYes, yes but that does not mean it's wrong.
Comment #9
yched CreditAttribution: yched commentedlooks like the bot should be back now ?
Comment #12
catchIt shouldn't be wrong, but I can reproduce the test failures locally with the patch, and not without it.
Comment #13
catchdrupal_set_message() suppresses page caching, the page cache tests depend on system_test.module.
Comment #15
Dries CreditAttribution: Dries commentedThis seems like the proper fix to me. Although, the test is a bit odd/weird -- it is a bit of a hack really. Do we need a test for this?
Comment #16
catchSince it was broken for a good 2-3 months, and caused a serious regression, without anyone noticing apart from via repeated and diverse benchmarking on a completely unrelated patch by more than 2-3 people, I thought it was worth having a test. However I agree the test is a bit weird / hacky and wouldn't be sad if it's removed - the proper test to catch things like this is #638078: Automated performance testing for core.
Comment #17
catchchx asked for a change from $completed_phase to $stored_phase - since setting that before we actually execute the phase is a bit strange. internal variable name only. I've also attached a version with and without the test.
Comment #18
chx CreditAttribution: chx commentedAbsolutely yes to the test! It helps those who mess with drupal_bootstrap... like Moshe and me :)
Comment #19
Dries CreditAttribution: Dries commentedDon't get me wrong; I'm all for testing. It just that this test is really ugly. Can't we write a proper unit test for this?
Comment #20
chx CreditAttribution: chx commentednot without mocking the actual bootstrap functions. my mock function based unit testing idea was voted down for Drupal 7 early. See you in Drupal 8 :)
Comment #21
Dries CreditAttribution: Dries commentedI committed the patch without the test to move things forward. However, I'll look into the test within 48 hours (tests can go in after code freeze). I want to ponder about the tests a bit more. I'm setting this back to 'needs review' for the time being.
Comment #22
David_Rothstein CreditAttribution: David_Rothstein commentedHm...
The API docs for drupal_bootstrap() say this:
And for drupal_get_bootstrap_phase() say this:
Which means the API docs were correct before this patch, but now are completely wrong, right?
Also, even if the API docs are changed... what I don't understand is this seems to mean there is now no way to use this function to actually detect when the bootstrap is fully complete??? Since it will return DRUPAL_BOOTSTRAP_FULL when in the middle of running that phase, but also will return it at the end. It almost seems like we need a second function here, i.e. drupal_get_last_completed_bootstrap_phase() or something like that.
Comment #23
Fabianx CreditAttribution: Fabianx commentedIndeed it is still totally broken, calling drupal_get_bootstrap_phase from any phase below FULL will cause the bootstrap to _abort_ leading to a WSOD without any information at all.
The patch is very simple: Don't set new phase when retrieving the state.
Before what was happening was:
drupal_bootstrap($phase = NULL, $new_phase = TRUE) // called with default values.
if ($new_phase) {
$final_phase = $phase;
}
$final_phase gets set to NULL.
and then obviously $final_phase > $stored_phase is no longer TRUE, so the loop gets aborted ...
Comment #24
plachHere's a patch implementing #23.
Comment #25
chx CreditAttribution: chx commentedComment #26
chx CreditAttribution: chx commentedNote this is identical (aside from a comment) to the patch in #1806992: drupal_get_bootstrap_phase / drupal_bootstrap is broken and honestly this issue should be closed in favor of that but w/e.
Comment #27
Fabianx CreditAttribution: Fabianx commentedThis looks great to me!
**RTBC**
Comment #30
plachBack to RTBC
Comment #31
plachComment #34
plachComment #37
chx CreditAttribution: chx commentedComment #40
chx CreditAttribution: chx commentedComment #43
dcam CreditAttribution: dcam commentedComment #46
chx CreditAttribution: chx commentedComment #48
David_Rothstein CreditAttribution: David_Rothstein commentedCommitted to 7.x - thanks!
But I would say this is still a bit broken even afterwards (or at least incorrectly documented) - see #22. And the history of this issue plus the fact that there are some uncommitted tests in the patches above suggest that it would be great to have some kind of test for this function...
Maybe those can be split off to new issues, but leaving this one open for now.