I have written a new D6 module called "Dynamic Cache" which allows other modules to disable Drupal's standard caching from hook_boot(), for any arbitrary runtime conditions.

Due to the execution sequence of Drupal's bootstrap system, it is not normally possible to disable standard page caching from hook_boot(), because the cached page is already loaded before hook_boot() is invoked.

Page caching can be disabled on a per-page basis easily enough though programmatic methods, or the cacheexclude module. However there is no way of disabling the cache on a per-user basis (ie, disable cache for a given page, for some anonymous users but not others), except for manipulating global variables in settings.php.

You can see a discussion about this issue here: http://drupal.org/node/322104. It has not been resolved, even for D7, much less D6.

We have found a way to bypass this limitation, by essentially "hijacking" (ie, not returning to) Drupal's normal bootstrap process, and completing the rest of it ourselves. This is actually much simpler than it sounds, and works perfectly.

We have been using this technique without issue (in fact it immediately solved all of our caching issues) on a busy, live production site for some time now, and I would now like to contribute it to the community. I believe it to be a simple, original, and ingenious solution to a limitation of Drupal's standard caching mechanism, with extremely wide applicability. Hopefully this same method will also work for D7, however that is not something I have examined at this time.

I posted preliminary details on this technique in the forums (http://drupal.org/node/875152), but have now taken the time to turn it into a standalone module. I have done my best to follow Drupal's coding conventions, wrote a detailed README.txt, and implemented an install hook to ensure a very high module weight, since Dynamic Cache's hook_boot() must run after any other module's hook_boot().

I have been involved in the Drupal community in some form or another for over four years (often providing help on forums and issue queues) and have built dozens of sites from very small to very large. I recently attended DrupalCon Copenhagen where I had the great pleasure of meeting Dries, webchick, and many others, and found myself inspired and encouraged to contribute more actively back to the community. I was a presenter at DrupalCamp Alberta 2010 last weekend, and will be attending the Pacific Northwest Drupal Summit in a couple weeks.

CommentFileSizeAuthor
#1 dynamic_cache.zip3.01 KBbrian_c

Comments

brian_c’s picture

Status: Postponed (maintainer needs more info) » Needs review
StatusFileSize
new3.01 KB

Module attached.

avpaderno’s picture

Status: Needs review » Needs work
Issue tags: +Module review

Hello, and thank you for applying for a CVS account. I am adding the review tags, and some volunteers will review the code, pointing out what it needs to be changed.

What is used as code for the proposed module could be used to create a custom index.php that would replace (for sites that needs it) the default index.php file Drupal comes with.

brian_c’s picture

I don't believe this functionality can be achieved with a modified index.php, it would need to change bootstrap.inc. This module effectively bypasses the part of bootstrap.inc that would display a cached page, invokes the remaining bootstrap phases, then completes the remaining (unchanged) steps from index.php.

Modifying bootstrap.inc to accomplish this, is the exact subject of this thread: http://drupal.org/node/322104, but after two years it has still not been resolved, for D6 or for D7.

Given that, isn't it better to provide a module that can accomplish this immediately using the existing hook system (albeit in a novel way), instead of modifying core?

Thanks very much for your time.

avpaderno’s picture

Status: Needs work » Needs review
brian_c’s picture

Anything I can do to help move this along?

Have been using this code with zero issues on a large production site for 5+ months now.

I know at least one other person who is successfully using this module as well, they wrote about it here: http://gristmillmedia.wordpress.com/2011/01/14/drupal-conditional-page-c...

One thing I am not 100% sure about, is whether the module always assigns itself a high weight properly (to ensure its hook_boot() executes last). I copied what the Devel module does, just updating the system table manually via hook_install(), but am not sure it works all the time; I think I've seen the module weight remain unchanged (0) after install in some cases.

TimMachine’s picture

Yes, it's working well for us right now on two sites in which we need to be able to display pages that shouldn't be cached to specific anonymous users.

We implemented it last week and so far haven't seen any problems.

Thanks Brian!

avpaderno’s picture

Component: Miscellaneous » new project application

I will report here a little checklist:

  • The code needs to follow the coding standards; check in particular the format used for the control structures, and the name given to PHP variables, Drupal persistent variables, functions defined from the module.
  • Files available from third-party sites should not be included within the module/theme. This is particularly true for files that are not licensed under GPL License v2, but it is also true for files that are licensed under the same license used by Drupal.
  • The license file should not be included as well; the packaging script already include that file. In any cases, the code for modules/themes committed in drupal.org repository needs to be released under the same license used by Drupal; any compatible license is not allowed.
  • Check the code passes the Coder validation.
zzolo’s picture

Status: Needs review » Postponed

Hi. Please read all the following and the links provided as this is very important information about your CVS Application:

Drupal.org has moved from CVS to Git! This is a very significant change for the Drupal community and for your application. Please read the following documentation on how this affects and benefits you and the application process:
Migrating from CVS Applications to (Git) Full Project Applications

  • The status of this application will be put to "postponed" and by following the instructions in the above link, you will be able to reopen it.
  • Or if your application has been "needs work" for more than 5 weeks, your application will be marked as "closed (won't fix)". You can still reopen it, by reading the instructions above.
djdevin’s picture

Rebooting an old thread. This module saved us several times, for example when trying to detecting incoming SSO via cookies, logged out as an anonymous user. Any more work on this? Would love to see a project page.

brian_c’s picture

Hiya, glad you are finding the (proto) module to be useful!

No real updates... I kinda stopped pursuing this when the whole CVS -> Git switchover happened. Now that the dust has settled on that I should probably try again to make this into an actual project. Looks like I need to set it up as an "Experimental Project" with the new system. Will see if I can find some time in the next week or two.

djdevin’s picture

Would you mind if I create the project page? I have Git access with several other projects, and can clean up the module. Not sure how much future development it would need, but I could of course make you a co maintainer.

brian_c’s picture

Lemme give it a shot myself first, if you don't mind. I will try this week.

onelittleant’s picture

Bump.

This module does what it does very nicely. We use it on a site where depending on a particular session variable we want anonymous users to skip the cache. Prior to this module, we had to disable caching site-wide. Now our average response times are cut in half or less for anonymous users!

I'd like to see it available to the community. Let me know if you want assistance rolling this into a project.

greggles’s picture

Please see http://drupal.org/node/916552#comment-4147170 for how to move this forward.

doublejosh’s picture

brian_c do you just need to create a sandbox?

brian_c’s picture

I have (finally) created a sandbox for this project here: http://drupal.org/sandbox/brian_c/1413374

I am not sure whether I should be applying for Full Project status right away, or leave it as a sandbox for a while?

avpaderno’s picture

Project: Drupal.org CVS applications » Drupal.org security advisory coverage applications
Component: new project application » module
Status: Postponed » Needs review

I am moving the issue to the right queue.

misc’s picture

Hi,

The project is to small to get full project access:

  • The project has less than five function or class method definitions
  • The PHP/Javscript source code is less than 120 lines long (including comments and whitespace).

But you could apply just to get this project out as an full project. You could read more in this discussion: http://groups.drupal.org/node/195848

I did an automatic code review: http://ventral.org/pareview/httpgitdrupalorgsandboxbrianc1413374git - there some issues to take care about. Please feel free to ask if you have any questions.

doublejosh’s picture

Not sure what this could be combined into, but I use it on production to get around logging in SSO users.
My colleague borrowed this code to create a UserAgent cache bypass. Perhaps this module could be a generalized Cache ByPass module with a few components/uses.

patrickd’s picture

Title: brian_c [brianc] » Dynamic Cache
misc’s picture

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

You don't need to combine this module with existing ones, but you could not get full git access to create projects with a small module as an application, but you could try to have this promoted to a single project on a drupal.org.

brian_c’s picture

Yes I'd like to get this promoted as a single module then... don't really care if I have "promote at will" rights (didn't even know I was applying for that?), nor do I intend to bloat this to 120 lines / 5 functions when it's not needed.

I need to find time to go through the latest coding requirements I'm apparently no longer satisfying first.

nevosa’s picture

Hi Brian,
I'm using this code in a large production already, and it works very nicely.
I've also created a sandbox project called "anonymous theme" that uses this code and would be very happy to see it published!
see:
http://drupal.org/sandbox/nevos/1507962

doublejosh’s picture

BTW: Here is the output of the Coding Standards sniffer...

dynamic_cache.module:2:1: error - You must use "/**" style comments for a file comment
dynamic_cache.module:2:1: error - There must be no blank line following an inline comment
dynamic_cache.module:5:1: error - Inline comments must end in  full-stops, exclamation marks, or question marks
dynamic_cache.module:5:1: error - There must be no blank line following an inline comment
dynamic_cache.module:12:1: error - Whitespace found at end of line
dynamic_cache.module:13:5: error - There must be no blank line following an inline comment
dynamic_cache.module:14:1: error - Whitespace found at end of line
dynamic_cache.module:19:22: error - There should be no white space after an opening "("
dynamic_cache.module:19:44: error - There should be no white space before a closing ")"
dynamic_cache.module:20:1: error - Whitespace found at end of line
dynamic_cache.module:21:5: error - There must be no blank line following an inline comment
dynamic_cache.module:22:1: error - Whitespace found at end of line
dynamic_cache.module:24:45: error - Whitespace found at end of line
dynamic_cache.module:31:11: error - BREAK statements must be followed by a single blank line
dynamic_cache.module:34:11: error - BREAK statements must be followed by a single blank line
dynamic_cache.module:47:1: error - Whitespace found at end of line

dynamic_cache.install:2:1: error - You must use "/**" style comments for a file comment
dynamic_cache.install:2:1: error - There must be no blank line following an inline comment
rjacobs’s picture

Though the code is very short and simple, it seems that having this available as a proper module/project makes good sense. This would allow other module developers that depend on this functionality to simply define a dependency instead of incorporating their own (possibly inconsistent or hacky) methods of conditionally disabling the cache. Having this as a project would also provide a "go-to" place for exploring this concept in D7. Perhaps this could even grow into a module that manages a wide-range of simple, yet low-level, cache controls.

I suppose there are few issues in the sandbox queue that might warrant attention before moving too far forward with all this. At the moment there are 2 bug reports (I just added one, but I think it has a very easy solution):
http://drupal.org/project/issues/1413374

doublejosh’s picture

FYI: I'm involved in a D7 upgrade that will require this module. #1515474: D7 version

Likely I'll add a patch there.

rjacobs’s picture

It's looking like there is certainly some demand for getting this functionality out there as a full project, and as I noted above, this is the kind of thing that other modules could nicely leverage as a dependency.

Given that this has been an open case for quite some time, I'd like to offer whatever help I can to get this module to full project status while also staying in-line with brian_c's intentions. For example, I would be willing to:

  1. Setup a full project for this module via my own git access, and give initial commit credit to brian_c.
  2. Setup brian_c as a co-maintainer. I believe this access can now be granted by an existing maintainer without the candidate going through a formal co-maintainer application process? I see that brain_c is quite active in the Drupal community, and if this module cannot serve as a full project application (see comments #18-22) perhaps the co-maintainer approach would be the most efficient way to get this done.
  3. Do some cleanup (readme file, coding standards, doxygen, etc.).
  4. Address pending issues in the sandbox issue queues (we already have an RTBC patch for the only outstanding bug report).
  5. Port the module to D7 (we already have a leg-up on this based on the work in http://drupal.org/project/ua_cache_bypass).
  6. Explore ways to grow this concept. For example, they may be some room to discuss a dependency relationship with existing contrib modules like User-Agent Cache Bypass and/or the possibility to consolidate functionality from such modules. At the moment, the core "dynamic cache" concept seems like the lowest common denominator functionality, so I don't think it can be merged into any existing project the leverages cache control on hook_boot.

Of course if brian_c wants to keep moving this through as an app for individual project git access, or grow the module to the point where this can be an app for full git access, that's all good. I just wanted to suggest an alternative that might accomplish the same objectives a bit faster.

iamEAP’s picture

As a user of dynamic_cache and maintainer of UA Cache Bypass, I'm on board with rjacobs in #27. Perhaps someone could reach out to brian_c?

brian_c’s picture

Hi guys, I have been following this but have been too swamped at work with multiple converging site launches to put any time towards it recently. I am wrapping some things up this week, taking a much-needed vacation at the end of the week (weekend in Vegas baby), then should have time to dig back in when I get back. I want to correct the code sniffer stuff and re-submit.

I wanted to get this submitted as my own project, to better motivate myself to maintain it (and make myself more familiar with the Git workflow), as part of "getting my feet wet" at helping contribute more back to the community than just the odd forum/issue post.

rjacobs’s picture

Hi Brian,

The decision is totally yours! My guess is that if you are willing to run-through at least points 3 and 4 from comment in #27 within your sandbox, and if no other issues crop-up, this application can probably be marked as "reviewed". After that it would be a matter of seeing how long it would take for one of the folks with git admin access to see this, give it a final review, and then hopefully decide to grant project-specific access to you. My understanding is that the number of people with git admin access (the ability to setup git access that is not associated with an existing project) is very small, so I think getting their attention is the tricky part. Someone please correct me if I am wrong in this summary.

Having someone who already has git access start the project and give you co-maintainer access could essentially lead to the same result (you would be a project maintainer, with the same rights as any other maintainer). It just requires someone who's willing to personally help out and kick-start things for you, and you can see there are a couple such people out there.

My personal feeling is that if you are willing to see it through the whole way yourself you'll probably learn more about the process and community, and we'll all benefit from that. So I think that's a great approach. I just wanted to somewhat 'bump' the issue by offering my own help.

Looking forward to reviewing your sandbox updates!

brian_c’s picture

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

Alright, fresh bunch of commits, ready for re-review.

Notes:
- Created branch "6.x-1.0-rc1"
- Have updated code to comply with Coder & CodeSniffer
- Have included patch from the 1 bug on the issue queue: #1425156: Dynamic_cache should be bypassed in certain admin contexts (drush, cron, etc.).
- Have also written a testing module for Dynamic Cache, which provides a simple test page

The test module now puts the code at well over 120 lines and 5 functions, if that matters.

Here is the automatic code review: http://ventral.org/pareview/httpgitdrupalorgsandboxbrianc1413374git-6x-1...

rjacobs’s picture

Thanks Brian,

A few comments. These are mostly minor points, and I don't think they are deal-breakers to getting dynamic_cache set as an official project, but I think responding to some of these points might make it more likely that you could leverage this git application for proper git access, as opposed to just single-project git access:

  • The testing functionality in indeed a nice touch, and one that I think is a great asset. Some thoughts I had specific to this concept:
    • I'm wondering if the testing functionality needs to be it's own module? Perhaps this kind of thing could just be included within the main module, and if you want to be able to toggle it on-or-off you could expose a "debug" or "testing" configuration variable in the admin interface. I know that having another module allows you to implement a separate hook_boot (at a lower weight) where you can toggle the cache setting, but I think you could accomplish the same thing by checking for a test condition in the beginning of dynamic_cache_boot()
    • For the testing you are using a cookie to setup a condition where the cache is intentionally bypassed. Could this same thing be accomplished by just checking for a query string parameter? I just imagine it might be too easy to set the cookie for a test and then forget to unset it, meaning the cache is always bypassed for that user. A $_GET check could streamline the testing code a bit too I suppose. I could see some advantages to using a cookie, but I think it would depend on other ways the "testing" concept might be leveraged.
    • One thing that would really boost the utility of the test would be to make possible to see test results site-wide, and not just on a specific testing page. After all, the way developers use the dynamic_cache functionality can vary quite a bit, and they may want to test different dynamic contexts/conditions that should trigger a cache bypass across a site. Having the option to report the cache status (with something like drupal_set_message) on any page would be handy... subject of course to some kind of "debugging" option being enabled. Making a test status block available would also be an option, though a more involved one to implement I suppose.
    • At some point you might want to explore the creation of simpletest tests (http://drupal.org/project/simpletest) for this module. This can be a bit involved, but it something some people love to see.
  • You have a "package" defined for the module in the .info file (which is also the module name). I'm not personally too picky about these kinds of things, but I think you would need to have a larger project to justify this, see; http://drupal.org/node/231036.
  • You have a git branch of 6.x-1.0-rc1. However, I believe the convention is that this should be a tag within a 6.x-1.x branch (see: http://drupal.org/node/1015226). For a sandbox project I don't think this is an issue, but looking forward, if the project becomes official you'll want to adhere to the convention for identifying releases, etc.

Thanks for posting your changes.

brian_c’s picture

Thanks for the reply. On your points:

  • Testing module:
    • As boot-level code (ie, code executed on every single page request, even for cached pages) I would prefer to keep the actual module as lean as possible, and not burden every page request with unnecessary testing code (as lightweight as it may be, it's still unnecessary, and this is a performance-geared module intended for high volume sites where milliseconds may make a real difference to overall throughput). What would be the benefit of combining them into a single module? The only one that comes to mind is reducing clutter on the Modules page, but frankly that is a Module page UX issue. I tend to agree with Damien in #35 on this one: http://drupal.org/node/937814#comment-3557750. Additionally, Dynamic Cache is INTENDED to be used by a separate module, so Dynamic Cache Test serves as a working & independent "reference implementation" that others may copy and modify for their needs.
    • Using a query string parameter won't work; these will be treated as separate pages by Drupal's caching system which keys off the full URL. The whole point of Dynamic Cache is to switch between cached/non-cached for the *EXACT SAME URL*. If you wanted to disable caching for a page by adding ?foo=bar, you wouldn't need Dynamic Cache, you could disable page caching normally when the page is first generated. So really, we have two options here: $_SESSION or $_COOKIE. I chose to use cookies so I don't have to assume a session exists (although I realize the difference is largely academic, as Drupal should create sessions for every user including anonymous). As for cookies "hanging around", the expire time is set to 0, so the cookie should vanish when the user closes their browser.
    • Creating some sort of test block is a great idea, I'll have to give that some thought. Using drupal_set_message() won't work, by definition you're not going to see a cached page if you have messages waiting... page_get_cache() fails unless drupal_set_message() == 0 (http://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/page_...)
    • Would love to do testing via SimpleTest but know nothing about it. This also seems like it might be a tricky thing to test, how can an automated test tell whether the page it received is from Drupal's cache or not? But again, I am extremely ignorant of SimpleTest's capabilities.
  • Sounds good, sticking it in a package was pretty arbitrary. Should I leave it for "Other"? Or maybe "Util"?
  • Thanks, am still figuring out branching/tagging (and Git in general, I'm a recovering SVN-ite). So the branch should be "6.x-1.x", and then for an actual release I would tag it as "6.x-1.0-rc1" or whatever? How do dev branches/tags work? Would there be a dev branch? Or are dev releases just "nightly tags" from current normal version branches?

Appreciate the tips and suggestions, thanks.

brian_c’s picture

I chose to use cookies so I don't have to assume a session exists (although I realize the difference is largely academic, as Drupal should create sessions for every user including anonymous).

Actually, on further reflection, it occurs to me that high-performance sites may attempt to disable Drupal's automatic session creation for anonymous users (ie, as discussed here: http://drupal.org/node/183006), so I do think cookies are probably the most robust option.

brian_c’s picture

it might be too easy to set the cookie for a test and then forget to unset it, meaning the cache is always bypassed for that user

I see your point about how the cookie could disable the cache site-wide within the same browser session though, I've modified the check so that the the test cookie now only affects the test page.

I have also committed new "6.x-1.x" branch.

brian_c’s picture

rjacobs’s picture

Status: Needs review » Reviewed & tested by the community

Great, thanks for the replies,

I think the bottom line is that you have given great consideration to performance and scalability in this effort (certainly more so than I in a review capacity), plus you have a good sense of Drupal structures and APIs and appear to be leveraging the community in your efforts. In my option that should speak even louder than whether or not you meet some hard limit on the amount of code included with the application (you probably meet that limit now anyway if counting the test logic). I personally don't see any reason why this can't be a full git access application.

You have also addressed some of the "housekeeping" issues (coder, readme, etc). The only exception might be choosing which "package" to assign in the .info, but hey, I don't think that needs to stand in the way of promoting this effort.

So I'd like to just mark this as reviewed. We know several others have tested the core logic behind this module (and are using it in production), so review attention has been thorough.

Some more specific replies appear below:

------------

Regarding the extra module, this probably is a fine case where having tests in a separate contained module makes sense. I don't know the true relative cost of an extra "if" check in hook_boot, but your justification makes me agree that it should be avoided.

In terms of the cookie vs query, etc. A query parameter could toggle the logic of the test, but you are correct in that it could lead to different cache results for a given page request. Anyway, having the extra check to confirm you are on the test page while checking the cookie really resolves things (the issues was pretty nominal anyway).

Also in terms of tests, if you are willing to explore the usage of a block to report test results I think that would really be the best case (but my vote is to save that effort until this app process is complete so we can get this thing out there).

For the package assignment, how about "Performance and scalability" (I think this is one of the best practice suggested examples). Really, this module is all about performance, as it's a way for people to generally keep caching enabled while still having a way to conditionally bypass it.

Lastly, regarding the git branching and tagging, I don't think there are any "rules" about how you assign branch names for your own working purposes, but when it comes time to make public releases several Drupal systems depend on certain naming conventions (http://drupal.org/node/1015226 explains it all best). All this is surely something you would have sorted out on your own (when setting up a release after getting this promoted), but I figured it was worth pointing out now.

Thanks!

iamEAP’s picture

I'll echo rjacobs on the package assignment "Performance and scalability" is probably the way to go.

Otherwise, count this as a +1 to RTBC, particularly echoing rjacobs' sentiments on leveraging community, and the use of this module in production environments.

brian_c’s picture

Thanks guys.

  • You're right that the performance impact of an extra if() check is pretty much zero (especially if additional testing code is split into a separate file only loaded on-demand), but after giving this some more thought I still think this is a valid case for a separate module, because:
    1. as Earl Miles and others point out in that thread I linked to #937814: [meta] Smarter UI/API separation for modules above, a separate module can be omitted from a server environment entirely; so Dynamic Cache Test doesn't need to be on Production at all, I think this is a good thing.
    2. as I mentioned above, it provides a ready-made sample implementation that others can use, much like eg, Drupal Commerce comes with a sample Shipping module.
  • I will switch the package to "Performance and scalability", that definitely sounds like where it belongs.
  • Very much looking forward to new additions like a D7 branch (I think doublejosh may have already done some work towards this), and a Test Block feature, but agree that those should wait until we have a 1.0 out the door.
brian_c’s picture

Package has been switched to "Performance and scalability"

klausi’s picture

Status: Reviewed & tested by the community » Fixed

manual review:

  1. 6.x-1.0-rc1 is a git tag name and should not be used for branches to avoid confusion.
  2. dynamic_cache_test_page(): all user facing text should run through t() for translation.

But that are just minor issues, so ...

Thanks for your contribution, brian_c! Welcome to the community of project contributors on drupal.org.

I've granted you the git vetted user role which will let you promote this to a full project and also create new projects as either sandbox or "full" projects depending on which you feel is best.

Thanks, also, for your patience with the review process. Anyone is welcome to participate in the review process. Please consider reviewing other projects that are pending review. I encourage you to learn more about that process and join the group of reviewers.

As you continue to work on your module, keep in mind: Commit messages - providing history and credit and Release naming conventions.

Thanks to the dedicated reviewer(s) as well.

brian_c’s picture

Status: Fixed » Closed (fixed)

Thanks all.

Full project page is now up (with an RC1 release):

http://drupal.org/project/dynamic_cache

brian_c’s picture

Issue summary: View changes

Updated issue summary.

avpaderno’s picture

Issue tags: -Module review