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.
Blocks are currently generated directly in block_page_build()
(via block_get_blocks_by_region() / block_list() / _block_render_blocks()), which prevents the implementation any kind of caching strategy via the #cache
property.
We need to delay the heavy work of block generation until render time if we want to be able to leverage the full power of the render cache on blocks.
Comment | File | Size | Author |
---|---|---|---|
#71 | block.diff | 1.16 KB | moshe weitzman |
#59 | blockcache.diff | 13.38 KB | moshe weitzman |
#57 | blockcache.diff | 13.39 KB | moshe weitzman |
#55 | blockcache.diff | 12.23 KB | moshe weitzman |
#53 | blockcache.diff | 12.23 KB | moshe weitzman |
Comments
Comment #1
Damien Tournoud CreditAttribution: Damien Tournoud commentedIt's actually pretty easy to do so.
There are some whitespace changes required here, that makes the patch harder to read then necessary. Attaching the full version and a review version with ignored whitespace changes.
Comment #3
manarth CreditAttribution: manarth commentedsub
Comment #4
Damien Tournoud CreditAttribution: Damien Tournoud commentedThis accidentally broke the rendering of titles. This version should pass all tests.
Comment #6
carlos8f CreditAttribution: carlos8f commentedsubscribing
Comment #7
Damien Tournoud CreditAttribution: Damien Tournoud commented#4: 918808-block-delay-render.patch queued for re-testing.
Comment #8
Damien Tournoud CreditAttribution: Damien Tournoud commentedOk, the last failure is actually legitimate: because we are rendering the blocks *after* template_preprocess_page(), the messages generated in the blocks will only display on the next page load.
I need opinions on the way forward here, given that I see two options:
$variables['messages'] = theme('status_messages')
by a$variables['messages'] = array('#theme' => 'status_messages')
and callrender($messages)
in the page.tpl.php templates). The only real drawback of this approach is that messages generated *after* the main content region is generated will still be displayed on the next page loadComment #9
EvanDonovan CreditAttribution: EvanDonovan commentedWould option #1 mean that themers would have to remember to add render($messages) to their page.tpl.php or status messages would not get displayed? If so, that might be a bit of a burdensome change.
With option #2, would this mean that I couldn't use drupal_set_messages() or dsm() to debug a block? Could I still use kprint_r()? I think that as long as kprint_r() or the like was available that wouldn't be too burdensome.
Comment #10
Damien Tournoud CreditAttribution: Damien Tournoud commentedYes, option #1 means changing all the
echo $messages
withecho render($messages)
or the messages will not be displayed at all.Yes, kprint_r() should still work anywhere on the page.
Comment #11
EvanDonovan CreditAttribution: EvanDonovan commented@DamZ:
For #1:
Actually now that I looked at http://api.drupal.org/api/drupal/themes--bartik--templates--page.tpl.php..., I realized there's already something like
if($messages) { print $messages; }
in page.tpl.php. So this wouldn't be too big of a change. It's just the new render() syntax, but people moving up from D6->D7 will have to learn that anyway.As someone who missed that whole party, it seems kind of random when it's render() and when it's print/echo, so it might even be good to have one more use of render() for consistency's sake.
For #2:
I agree that blocks shouldn't print messages. I don't see a good reason why they would, anyway. If kprint_r() and the like work, then debugging is still possible.
So I think either alternative would be OK, but probably feedback from some others would be needed, since I'm certainly not an expert.
Comment #12
dwwNote: the block causing these test failures is actually update status. It uses hook_help() to conditionally generate status messages. That was long before someone got the bright idea to turn hook_help() output into a block. So, it's not intentional that a block from update status is generating messages, that's a side effect of other changes to D7 core. ;)
So, I'd be happy to generate those messages in update status via some other means, if that's what it takes to make this sane. I agree that caching blocks makes generating side effects via blocks pretty dangerous. That was never the intention in update status. Kudos to DamZ for fleshing out this problem...
Comment #13
Damien Tournoud CreditAttribution: Damien Tournoud commentedThis is basically option #2: we move the
drupal_set_message()
fromupdate_help()
toupdate_init()
.Comment #14
dwwDamZ asked me to review the update.module part of this patch. Mostly looks fine. I agree hook_init() is safer than hook_help() given that hook_help() is now a cachable block. I only saw this:
typo. It's 'appearance'...
I'll look more closely, but so far, so good.
Comment #15
dwwWe also need an } else { to avoid hitting this case:
// Otherwise, if we're on *any* admin page and there's a security
// update missing, print an error message about it.
if we're on admin/modules or admin/appearance...
Comment #16
Damien Tournoud CreditAttribution: Damien Tournoud commentedErm. The logic in update_help() was really convoluted. I think this is the correct implementation.
Comment #17
dwwYup, the update.module hunk of this patch is now RTBC from my eyes. I haven't thought about the rest of this issue to comment meaningfully on the rest of the patch, but the update.module hunk should go in regardless. So, if this patch gets derailed for D7, we should split that hunk out into a separate issue, since those update.module changes are irrefutable.
Comment #18
dwwWell, actually, except there's a minor typo in one of the code comments. ;) Try this.
Comment #19
moshe weitzman CreditAttribution: moshe weitzman commentedEr, forum_block_view() already uses render cache successfully. So it is possible for blocks to use render cache.
I think you want to be able to use #cache on blocks that were not designed for it. I don't think that’s needed now and perhaps not desireable either. When all blocks use #pre_render, you make it hard to alter blocks using hook_page_alter(). Further, you can always make a new block which uses this technique and calls the existing hook_block_view of from some other module.
IMO this is won't fix. Willing to discuss that some more. Please elaborate on when you want this.
Comment #20
Damien Tournoud CreditAttribution: Damien Tournoud commentedWe definitely need that if we want to be able to implement external cache strategies via Edge Side Includes / Server Side Includes, as I presented during my talk at Drupalcon CPH.
Comment #21
Damien Tournoud CreditAttribution: Damien Tournoud commentedThat's the only valid point in #19. True. But it's the same for all the other elements we render via #pre_render, including the toolbar and shortcut bar. It's easy to use hook_page_alter() to add an additional #pre_render to the target block and modify it from there.
Comment #22
Damien Tournoud CreditAttribution: Damien Tournoud commentedTo elaborate on why this is needed, here is how the ESI module could very cleanly implement ESI / SSI for blocks in Drupal 7 (after this patch):
hook_page_alter()
to add a#cache
to each block that you want to render via ESI (not the block content, as in the forum module, the block element itself)<!--# include file="esi/[cachekey]" -->
). This is possible because#markup
is passed tocache_set()
by reference indrupal_render_cache_set()
(this was done specifically for this use case by #651902: Allow ESI tie in).esi/[cachekey]
directly to Memcache, or use a simple wrapper for VarnishThe only flaw in that plan is that blocks are currently rendered very early, even before the page structure is generated. This issue is meant specifically to fix that.
Comment #23
moshe weitzman CreditAttribution: moshe weitzman commentedThanks for the detail.
Could you not do that ESI plan today with the forum blocks? If so, then you can do it for your custom blocks. For *expensive* blocks provided by contrib modules, you should submit patches asking them to do the #pre_render thing. I really think thats preferable to obscuring all blocks, even simple ones, during hook_page_alter(). This patch neuters hook_page_alter(). I'm all about deferring work, but this patch makes goes too far IMO.
Comment #24
dwwSplit off the fixes to update_help() into #931284: Update status admin UI shouldn't rely on hook_help() in case this gets derailed/won't fixed, since that really is a bug fix.
Comment #25
dwwSweet, #931284: Update status admin UI shouldn't rely on hook_help() already landed, so #18 needs a re-roll if the rest of this change is still going to happen...
Comment #26
dwwNot my code, and I don't have a strong opinion on if this should happen, but here's the re-roll to keep things moving.
Comment #27
sunI think that both Damien and moshe are right on their own. It's a conflict of interests and highly depends on whether site builders are willing to do performance optimizations that basically eliminate the usefulness of hook_page_alter(), or not.
So we need a compromise. I think that a module like http://drupal.org/project/esi would just need a way to swap out _block_get_renderable_array() with a custom callback, so core uses that by default (and builds blocks early), but highly optimized sites are free to swap that callback out in order to perform delayed rendering of blocks.
One possible option to achieve that would be to set a #block_render_callback on the region, which defaults to _block_get_renderable_array(), but prior to invoking the callback, a drupal_alter() allows modules to swap that out (and eventually do other neat things).
Basically, this proposal would bring us back to #778476: It's impossible to alter theme regions, resp. #908116: hook_block_region_alter() is missing in block_get_blocks_by_region(), and some other related issues in the queue.
Powered by Dreditor.
Comment #28
catchSubscribing.
Comment #29
moshe weitzman CreditAttribution: moshe weitzman commentedOne more thing - block.module isn't really designed for high performance sites. I would recommend a fresh start in Contrib land. Lets leave block.module as is (easy to alter) in core for D7.
Comment #30
EclipseGc CreditAttribution: EclipseGc commentedSo, working through the ctools/panels upgrades, it's become obvious we need something along these lines, as we have had, and would like to continue to have, the ability to kill a region before it renders its content. Currently we have to rely on hook_page_alter() in which case we've already spent all the processing time rendering blocks we had no intention of displaying. Where we go from there in terms of what core is actually going to do, I don't know, but I know the idea of disabling that particular feature in panels has been discussed.
Eclipse
Comment #31
moshe weitzman CreditAttribution: moshe weitzman commented@EclipseGC - my conclusion is in #29. Performance sensitive sites should disable block module. Its nearly deprecated IMO. Panels subsumes it for some sites, context for others, and there will be more.
Comment #32
merlinofchaos CreditAttribution: merlinofchaos commented#29: One of the features of Panels -- and I'm not talking about Panels Everywhere -- is that right now you can for just one page, use a panel and not use blocks on that page. This is very common for a front page, or just one or two landing pages for example, where it has a different look than all other pages.
Right now, it's possible for people to do hybrid blocks/panels solutions. Saying that you can no longer accomplish that (or rather, that doing so is now a giant performance screw-you) is a major regression, and I don't think it is the right call.
Comment #33
moshe weitzman CreditAttribution: moshe weitzman commentedI see your point. I'm really just opposed to the delay solution proposed by Damien. If you want to fiddle with the block list retrieval (block_list() or thereabouts) it looks like http://api.drupal.org/api/function/hook_block_list_alter/7 is your friend. Its docs are a bit misleading it says "Act on blocks prior to rendering.". It would be even better to day "Act on blocks prior to *building*".
If you don't want to user that hook, then the query above it has a tag and could be query altered.
Comment #34
moshe weitzman CreditAttribution: moshe weitzman commentedOK, here is a healthy compromise, plus some icing. I've taken the last patch here and enhanced it slightly so that blocks that do no caching get rendered immediately, while cached blocks go through #pre_render. I think that's sane, especially given that ...
This patch preserves the block cache API but changes its implementation from custom to a standard drupal_render #cache. This should help the Butler project, as it has to deal with one less special case.
In D8, we hope to immensely increase our cache lifetime and hit rates if we can get cache tags in as articulated recently at #636454-30: Cache tag support. With that, we can have #cache items like these blocks declare their #attached['cache tags'] and have these cache tags aggregated up the render tree like css/js/library items are now (was added at #859602: #cache does not record the #attached declared in child elements). Thus, we can have a block like 'recent content in term x' that can survive a cache_clear_all(). We can also enable block cache for sites that use node access modules.
This patch also removes the admin checkbox 'Enable block caching' since thats really a devel level pref and has no business being in our standard UX IMO. Core assumes blocks behave as they are declared. Folks wishing to disable block cache universally can use hook_block_info_alter or assign the DrupalFakeCache backend for the cache_block bin.
Comment #36
moshe weitzman CreditAttribution: moshe weitzman commentedFixed one test failure. 2 others are eluding me. simpletest debugging is a special section of hell.
Comment #38
moshe weitzman CreditAttribution: moshe weitzman commentedUgh. Uploaded wrong file.
Comment #40
chx CreditAttribution: chx commentedMoshe, congratulations on finding a core bug! If in pristine HEAD you force $conf['block_cache'] to be TRUE at least the node recent test fails. Working on it.
Comment #41
chx CreditAttribution: chx commentedThere is no bug. This is a feature. A feature Moshe asked for http://drupal.org/node/115319#comment-1031039 here :D see what happens is that the block is rendered on the block admin screen and when
$node4 = $this->drupalCreateNode($default_settings);
happens then the caches are not cleared! Solutions are obviously many: add a cache_clear_all() to dCN or just document you need one and add a cache_clear_all to testRecentNodeBlockComment #42
chx CreditAttribution: chx commentedAfter a brief discussion with webchick, cache clears in dCN would make test performance even worse. So let's add a cache_clear_all to testRecentNodeBlock and the other one.
Comment #43
moshe weitzman CreditAttribution: moshe weitzman commentedAdded a cache_clear_all() call in each test.
Comment #44
catchPatch looks like great cleanup. Have discussed with Moshe the possibility of removing the block cache altogether in favour of the render cache, but that's no reason to not do this patch which is just consolidating code.
Even though this is a large code block, it should use // comments. Also two comment lines exceed 80 chars.
Should be "without content".
We should add an update here to remove the block_cache variable, didn't see one in the patch.
Also the cache_clear_all() in the test points to a d.o issue, I'd rather a comment that just explains that drupalCreateNode() does not automatically flush content caches unlike manually creating a node from a node form.
Otherwise looks great.
Comment #45
moshe weitzman CreditAttribution: moshe weitzman commentedAddressed all of catch's items in #44.
Comment #46
moshe weitzman CreditAttribution: moshe weitzman commented#45: 0001-Block-cache-is-now-implemented-as-a-standard-drupal_.patch queued for re-testing.
Comment #47
catchIssue summary:
Drupal 7 introduced the render cache, which amongst other things correctly handles stuff like #attached from cached items.
This is used in contrib/custom code, but not so much in core.
The patch converts the block cache, which is currently entirely custom to use the render cache - this allows it to piggy back off helpers like drupal_render_cache_key() etc.
Additionally, it removes the UI for block caching settings - module authors already determine whether their block should be cached or not in hook_block_info(), and there is a contrib module to override this, having a setting for it doesn't really make sense.
6 files changed, 99 insertions(+), 113 deletions(-)
Has an update in it, would be nice to have the boilerplate upgrade tests issue fixed before we start committing those, but a variable_del() does not need tests.
However I am wondering a bit if we need an _update_8000_variable_del() function - variable_del() doesn't fire hooks, but that function may not exist by the time this update runs, however as long as the database table does a straight delete would still work.
Comment #48
catchMoving to RTBC. If we wanted to be strict, we'd wait for upgrade path testing, but it's also just variable_del(), and the patch doesn't need anything extra IMO.
Comment #49
catchSpoke to webchick in irc, to make sure there's no confusion, I'm moving all D8 patches with updates to 'postponed' on #1182290: Add boilerplate upgrade path tests, while this one has pretty much zero chance of breaking anything to do with the upgrade path, we should be consistent on requiring code coverage for updates at all, so doing it to this issue too.
Comment #50
catchThat issue is now RTBC, unpostponing this.
Comment #51
catch#45: 0001-Block-cache-is-now-implemented-as-a-standard-drupal_.patch queued for re-testing.
Comment #53
moshe weitzman CreditAttribution: moshe weitzman commentedThis one should apply.
Comment #55
moshe weitzman CreditAttribution: moshe weitzman commentedAnother try.
Comment #57
moshe weitzman CreditAttribution: moshe weitzman commentedUploading right diff ... and removed --no-prefix.
Comment #58
catchLooks good. The only external change here is the removal of the ui for block caching, everything else is just updating block module internally to the new pattern.
Comment #59
moshe weitzman CreditAttribution: moshe weitzman commentedModernized the bin name from cache_block => block in #cache array. catch approves for leaving RTBC.
Comment #60
webchickCan someone test this with some Google AdSense code or similar thing that *has* to show uniquely on every page? Just want to make sure we don't have any behaviour regressions, and I'm not sure how to write unit tests for that.
Comment #61
webchickComment #62
moshe weitzman CreditAttribution: moshe weitzman commentedWe test for that and more in BlockCacheTestCase (see block.test). Back to RTBC?
Comment #63
webchickOops. Carry on. :D
Comment #64
catchSince I RTBCed this I'm not going to also commit it. Putting in Dries' queue for that reason.
However if you can find someone to do their own review and confirm the RTBC before Dries gets to it, unassign him and I'll be happy to commit after that.
Comment #65
chx CreditAttribution: chx commentedMy concern was variable_del('block_cache'); but that can be tended from contrib. Another concern is the uid 1 check but that can't be fixed without turnng this into a huge meow, by changing significantly how our access controls work.So this is good to go for now.
Comment #66
catch#59: blockcache.diff queued for re-testing.
Comment #67
catchThanks! Committed and pushed to 8.x
This needs a change notification.
- the block cache setting has been removed, if you want to disable block caching use a Null cache inc for cache_block - this is more an administrator notification than a contrib developer one but I think we can do that now.
- I don't think we need a note about the private function that dashboard module is calling, naughty dashboard module, but that was the only thing that remotely looked like an API change to me so just flagging up for the record.
Comment #68
webchickComment #69
webchickAnd since adding that summary is relatively easy, tagging as Novice.
Comment #70
webchickThanks to the magic of git bisect, I discovered that this patch caused #1297370: Default installation of D8 shows help region, which is a pretty nasty user-facing bug.
Comment #71
moshe weitzman CreditAttribution: moshe weitzman commentedFixed help region and a book block that was showing itself despite having no content.
Comment #72
chx CreditAttribution: chx commentedmoshe, that's the wrong issue. Edit: i copied the patch in the other one and credited the comment to you :)
Comment #73
David_Rothstein CreditAttribution: David_Rothstein commentedI think this caused #1304470: Add tests for module-provided contextual links on blocks also.
Comment #74
aspilicious CreditAttribution: aspilicious commentedDon't know anything about caching but made a notification anyway. I know it's not that good but at least others just have to edit now.
http://drupal.org/node/1339672
Comment #75
xjmAdded detail to the change notice (with chx's help).
Comment #76
dwwThanks, xjm! Setting these back to normal for posterity...