Hi,
This is just a tiny patch, but it allows great things to do in contrib/. The idea is to make the variable_* system pluggable, which means that it can be overridden via:
$conf['variable_inc'] = 'sites/all/modules/faster_variables/fb_variable.inc';
and from variable_init() to shutdown will my own variable subsystem take over.
This patch is _not_ changing all bootstrap functions and adding another variable_bootstrap_get, which is used until the system is plugged, but rather keeping the default functions as fallback.
Because in PHP functions are not overwritable, I made it so that if there is a _variable_init / _variable_get / _variable_set / _variable_del defined, then it'll use those, else fallback to the current core implementation (though non-cached for simplicity).
This is again not used for replacing variables table alltogether, but rather give modules like faster_variables (my sandbox project), strongarm, memcache, etc. a chance to hook into the variable subsystem without having to use dirty hacks, which make big problems in strongarm for example. It'll also allow more granular caching and things like this to be implemented as contrib rather than core. While core could still have a great implementation it would be great to be able to start in contrib now.
Affected issues that could be solved with this are (not complete list):
#987768: [PP-1] Optimize variable caching
#79008: make variable prefetching optional
#1062452: strongarm_set_conf() needs to be called sooner
#661442: Document variables that are used before hook_init()
This will also make it possible to implement parts of the new CMI approaches in a backwards compatible way in D7, D6 as contrib modules, so that things can be tested long before D8 is out or stable.
This patch is rolled against 8.x, but is really meant for 7.x and 6.x as well. It is only valid for 8.x of course until the CMI takes over, but it can still be useful for testing CMI approaches also in 8.x until it is ready to be merged.
The patch includes a default include/variable.inc, which is just a copy of the core functions in 8.x. I changed the default variable_* function to be as minimal as possible while still providing a (non-cached) fallback if includes/variable.inc is not yet loaded.
Best Wishes,
Fabian
PS: Patches for 8.x,7.x,6.x follow as first comment when issue number is generated.
Comment | File | Size | Author |
---|---|---|---|
#56 | core-make_variable_subsystem-1193396-56.patch | 7.7 KB | amitgoyal |
#7 | 1193396-7-variable_inc.patch | 6.97 KB | Anonymous (not verified) |
#5 | 1193396-5-variable_inc.patch | 6.94 KB | Anonymous (not verified) |
#1 | 0001-Issue-1193396-Make-variable-subsystem-pluggable.patch | 6.57 KB | Fabianx |
#1 | 0001-Issue-1193396-Make-variable-subsystem-pluggable-d7.patch | 6.57 KB | Fabianx |
Comments
Comment #1
Fabianx CreditAttribution: Fabianx commentedAnd here are the patches.
Note that the D6 patch of includes/variable.inc is a direct port from D7, so does include the cache stampede protection.
This could be changed of course.
Best Wishes,
Fabian
PS:
EDIT: The D6 patch should not include the cache stampede protection as lock_wait is less than ideal in current D6. I'll change it for D6 as soon as I get feedback on the patch / D8 + D7 have been applied.
Comment #2
catchThere are a lot of issues around with the variables system indeed, and while I'm hoping we can get something good into core, this would allow improvements to happen in contrib projects - from working on some of the core patches it is very hard to get something clean that solves all the issues and passes tests, so allowing contrib to take over might be the better option.
Comment #3
Crell CreditAttribution: Crell commentedIf we're going to swap out the variable include file, just swap it out and include the right file early on as part of the base variable initialization. All of the wrapping _variable_set() and such only adds unnecessary complexity and CPU cycles. It is not possible for the variable implementation to change during a single request anyway.
Comment #4
catchIf we did that, would have to be in http://api.drupal.org/api/drupal/includes--bootstrap.inc/function/drupal..., the patch would be shorter if we did that, and no private functions, so it makes sense.
It would mean you couldn't use variable_get() for the file include itself, but checking $GLOBALS['conf'] at that low a level is probably worth it to make the change less intrusive.
While we're hoping to nuke variable_get() completely in D8, the current work on the configuration proposal with file based storage, covers only half of what variable_get() is used for - it's also used for tracking volatile state changes (and other stuff) between requests - for use cases that the cache system is not able to cope with - see #1175054: Add a storage (API) for persistent non-configuration state. The system that replaces that stuff is going to have to deal with some of the multiple performance issues that variable_set() currently has in D8 and below (hopefully not memory so much, but definitely balancing read vs. write performance), so making it easier to experiment with that stuff in contrib may assist with the overall effort of replacing it.
Comment #5
Anonymous (not verified) CreditAttribution: Anonymous commentedimplements #4.
Comment #7
Anonymous (not verified) CreditAttribution: Anonymous commentedwoops, this is a 7.x patch only.
Comment #8
RobLoachI'd love the variable system to be pluggable, but the method of swapping out PHP files should most likely be replaced with #1497366: Introduce Plugin System to Core and #1497230: Use Dependency Injection to handle object definitions .
Comment #9
Anonymous (not verified) CreditAttribution: Anonymous commentedyes, for D8. but this is aimed for D7.
variables in D8 will likely not look anything like the current setup because of CMI and key-value store.
Comment #10
RobLoachAh, didn't even see the 7.x-dev in there. Okay! Carry on! :-)
Comment #11
Anonymous (not verified) CreditAttribution: Anonymous commentedtalked to webchick about this, she's not keen on this getting in to D7, so marking as won't fix.
i've created a module that implements a less broken variable cache here: https://drupal.org/project/variables_that_suck_less
Comment #12
jonhattanIt doesn't hurt. It works. Tests passes. It allows to play with alternatives in contrib.
Comment #13
David Hernández CreditAttribution: David Hernández commented+1
The variables system it's outdated and it would be awesome to be able to write modules around it.
Comment #14
webchickOh, not necessarily not keen on it. Just wanted to thinker on it some more, and get some feedback from folks like catch, etc. I'll take a look at your project.
Comment #15
Anonymous (not verified) CreditAttribution: Anonymous commentedrerolled for HEAD.
what else needs to be done to get this in?
Comment #16
tstoecklerIs there a reason this is not going into D8? I am aware that variable_get() is going away eventually, but #1175054: Add a storage (API) for persistent non-configuration state is still far off, and last time I checked (literally) CMI isn't going to be able to nuke variable_get() on its own. So
($better && $has_patch > $perfect && !$has_patch)
?!Comment #17
Anonymous (not verified) CreditAttribution: Anonymous commentedI'm not opposed to putting this in D8, but I'd rather we wait until later in the cycle to decide that this is the best we'll get.
Comment #18
Anonymous (not verified) CreditAttribution: Anonymous commentedbump. wondering what David_Rothstein will think of this issue for D7.
Comment #19
Fabianx CreditAttribution: Fabianx commentedTested and reviewed. Works wonderful. Nice trick with checking $conf directly.
NOTE:
You don't need a core patch for your variables_suck_less by exploiting a hack in hook_boot:
https://drupal.org/sandbox/Fabianx/1172558
The trick is to replace $conf with an ArrayObject ...
The above sandbox approach is still a bad hack and #15 patch really does have _no_ impact at all besides making it possible to plugin another variable system.
REVIEWED and TESTED
Thanks beejebus!
Comment #20
sunThe additional filestat should be removed. We don't do that for any pluggable subsystem.
Overall, however, I'm not sure whether this makes sense. It has the potential of making the upgrade path to D8 more complex.
Comment #21
Fabianx CreditAttribution: Fabianx commented@sun: Why would this make the upgrade path potentially more complicated?
Wouldn't you remove all custom modules including things from settings.php before upgrading anyway?
Comment #22
Anonymous (not verified) CreditAttribution: Anonymous commented@21 - because presumably the upgrade path code will want to do something like:
SELECT * FROM {variable}
which won't work for sites that don't use {variable} to store their data.
my take on this is that sites that use alternative storage will have to migrate their variable data back in to {variable} before doing the upgrade. i don't think the core upgrade code should have to cater for contrib backends.
Comment #23
Anonymous (not verified) CreditAttribution: Anonymous commentedwhich i guess means i think this is still RTBC (with the stat taken out), as i don't think core's upgrade path should be effected.
Comment #24
Anonymous (not verified) CreditAttribution: Anonymous commentedComment #25
chx CreditAttribution: chx commentedI totally agree with
this.
Comment #26
catchA contrib backend can also just write variables back to the db too, then it'd be easier to switch back for whatever reason.
Haven't reviewed latest patch here. If this goes in, I will want to bump #1175054: Add a storage (API) for persistent non-configuration state to critical for 8.x, since we'll have a de-facto regression between releases until that's fixed.
Comment #27
David_Rothstein CreditAttribution: David_Rothstein commentedWhat are the chances that someone out there has a script like this:
Where some_low_level_drupal_function() happens to call variable_get().
That would work OK before this patch (variable_get would fall back on its defaults) but result in a fatal error after it.
Not sure how likely that is, but I thought it was worth bringing up. I've certainly seen (in general) scripts that load some Drupal code to access a convenient API function but don't actually bootstrap Drupal in any way.
Comment #28
Anonymous (not verified) CreditAttribution: Anonymous commented@David_Rothstein - that's possible. but do we really test all our D7 patches against that criteria (must be able to run before DRUPAL_BOOTSTRAP_CONFIGURATION)?
i think it's ok to require custom code like that to add a line to load variable.inc:
Comment #29
chx CreditAttribution: chx commented@David_Rothstein absolutely nil. If you do not bootstrap to at least DRUPAL_BOOTSTRAP_CONFIGURATION there is no Drupal function you can sensibly call. Aside from that, if you do not go up to DRUPAL_BOOTSTRAP_CONFIGURATION and yet call variable_get directly or indirectly you have bogus code because DRUPAL_BOOTSTRAP_CONFIGURATION includes settings.php which contains variable overrides.
Comment #30
David_Rothstein CreditAttribution: David_Rothstein commentedWell, this patch seems like a fairly unique case, since it's physically moving commonly-used functions out of bootstrap.inc. But I do see what you're saying.
I guess we can probably go ahead with it.
But shouldn't we get this committed to Drupal 8 first? I don't see any reason to let the codebases diverge, especially with so little of Drupal 8 using config() currently and the likelihood that something more variable-like will still need to exist (for #1175054: Add a storage (API) for persistent non-configuration state or otherwise).
Comment #31
David_Rothstein CreditAttribution: David_Rothstein commentedWell, I do know of one, because I've done it :)
user_password()
It doesn't happen to call variable_get(), but it easily could.
Comment #32
David_Rothstein CreditAttribution: David_Rothstein commentedAnother example came up in IRC: Turns out filter_xss() winds up calling variable_get() indirectly. And filter_xss(), like user_password(), is also the kind of function you might call, e.g., via a custom script that runs on a Drupal site that doesn't quite exist yet.
I don't think we have to worry too much about this; people doing this kind of thing are living on the edge anyway, and the fix (manually loading variable.inc) is pretty easy. I think it just means we have to announce this change pretty clearly in the release notes so nobody deploying the next release of Drupal 7 gets surprised by it.
Comment #33
tstoecklerAs I stated in #16 I agree with committing this to D8 first. If this only goes into D7 we have to open a critical task "Remove variable_get()", which we will probably achieve in D8 anyway, but is a pain because of the thresholds.
Comment #34
Anonymous (not verified) CreditAttribution: Anonymous commented@David_Rothstein and @catch - want me to reroll for D8?
Comment #35
sunDid anyone test whether Drush still works with this patch applied?
Comment #36
catchYeah I'd rather have this in D8 and rip it out later than have the code bases diverge, moving back.
Comment #37
chx CreditAttribution: chx commentedComment #39
catch@sun, I haven't tested drush with this patch, but I did test #973436: Overzealous locking in variable_initialize(). Any implementation that doesn't pre-load all variables into $conf would break drush vget, however I've posted a patch there at #1607072: Make drush vget compatible with alternative variable implementations which makes it work again.
There are some other parts of the drush variable commands (such as telling you whether a variable was originally set during drush vset) which would additionally break if you completely took things out of the variables table. However I think it's OK to require pluggable backends, at least during D7 to write to the variables table in variable_set() even if they don't use it for normal lookups.
Comment #40
Fabianx CreditAttribution: Fabianx commentedNow that CMI is in, we can safely go back to 7.x to get #23 in.
Comment #41
mgiffordThere's a whitespace error in #23, but it still applies.
Comment #42
mgifford23: 1193396-23-variable-inc.patch queued for re-testing.
Comment #44
mgiffordIt's pretty close, but needs a reroll.
Comment #45
Fabianx CreditAttribution: Fabianx commentedTook another look at #23, That actually has a check to work for all the crazy cases that contrib can come up with in not loading the variable subsystem, unless you:
a) use a custom subsystem for variables (rare, very rare) - though I do have a use case now
b) use one of the hacks to call user_password, filter_xss on the same site
a) OR b) work fine, just a) and b) can potentially break - IF and only if custom variable backends don't write through to the variables table on variable_set.
EDIT: oh, ic the function did exist just by including bootstrap.inc, got it ...
New use case:
- A read-only slave DB that is occasionally allowed to write variables to the master DB
attaching new patch in a moment.
Comment #46
Fabianx CreditAttribution: Fabianx commentedReworked the patch from current core.
Comment #47
Fabianx CreditAttribution: Fabianx commentedComment #50
Fabianx CreditAttribution: Fabianx commentedDrush indeed calls t() before bootstrap, such custom strings will be retrieved several times.
Just adding a variable_get() will only retrieve them once settings.php is loaded and such the variable subsystem.
Comment #55
mgiffordComment #56
amitgoyal CreditAttribution: amitgoyal commentedReroll of#50.
Comment #57
mgiffordPatch applies nicely in SimplyTest.me. What's the best way to test this to verify it won't interfere with existing sites?
Comment #58
Fabianx CreditAttribution: Fabianx commentedI think the simplest is to apply the patch to as many sites as possible and try if simple operations work especially when dealing with early bootstrap modules OR drush.
e.g. xmlsitemap was using variable_get in hook_drush_command() (which never worked anyway as it returned empty $conf), and adding drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION) made it work correctly. (I filed a bug against xmlsitemap.)
Such things ...
Comment #59
chx CreditAttribution: chx commentedThis should be good to go, the change in t() has absolutely negligible performance implications because the first t() call in a normal Drupal bootstrap will fill in that
$custom_strings[$options['langcode']]
so basically add 1 if isset($conf['variable_inc') and 1 function_exists call to the whole bootstrap. But drush calls t before settings are loaded so it's necessary.Comment #60
David_Rothstein CreditAttribution: David_Rothstein commentedSo we now have at least two real-life examples of the problem discussed above (the first fixed by a workaround in the patch, the second not fixed here). I feel like we really don't have any idea how many scripts there might be out there that use a low-level function like variable_get() (or something like filter_xss() that calls variable_get()) before formally bootstrapping Drupal...
Reading over this issue, is there any reason we can't do anything more like the original patch (which added private functions), only without the separate include file?
In other words, something like this:
(and similar for the other functions)
It would still only require one line of code in settings.php to load in an alternative implementation - it would just need to do the require_once directly rather than setting $conf['variable_inc'].
So basically, same functionality - but all the API functions remain in bootstrap.inc where they've always been and continue to be available regardless of when someone tries to call them.
Comment #61
Fabianx CreditAttribution: Fabianx commentedThanks David.
Yes, I agree the original with the fallback implementation is a better idea for BC compatibility.
Comment #62
chx CreditAttribution: chx commentedI would float the idea to make variable_initialize and variable_set overrideable but not variable_get for performance reasons. You must support GLOBALS['conf'] anyways because occassionally people will directly use it so there isnt really a good use case for overriding variable_get but if you really must you can make GLOBALS['conf'] something ArrayAccess and work from there.
Comment #63
Fabianx CreditAttribution: Fabianx commentedFor one use case variable_set / variable_del pluggable would work for me.
On the other hand, I think most sites that need to use pluggable variable systems might be fine with two function calls to variable_get() instead of one.
Could decide to not ship that function, and it would not be slow ...