-
-
Notifications
You must be signed in to change notification settings - Fork 825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(dev/core#174) CRM_Utils_Cache - Always use a prefix. Standardize delimiter #12331
(dev/core#174) CRM_Utils_Cache - Always use a prefix. Standardize delimiter #12331
Conversation
(Standard links)
|
Trying to think about how one would review this... and I think I may have found a problem. (Though it took a bit to spot.) Generally, the question is whether there's any case in which you flush the https://lab.civicrm.org/dev/core/issues/174#note_5812 and, analytically, we can say Except for one -- the general use-case of a system-flush (aka Perhaps a good way to visualize is this:
Anyway, I'm gonna think a bit more today about this... |
So I think there are a couple ways to get the desired impact
|
Pushed up a surgical fix. Note: if testing on Redis, there's a concurrent fix in #12330 which is also needed. |
2e7fcca
to
24ed487
Compare
fail likely related "/home/jenkins/buildkit/app/config/drupal-clean/install.sh: line 40: 22106 Segmentation fault drush -y en civicrm toolbar locale garland" |
test this please |
Yup, that's why we have CI. :) In my Redis-based env, it worked -- but in the default SQL-backed config, it gets stuck in infinite recursion of the form
Investigating... |
OK, I think that is fixed by another patch that was in my queue. Was going to file a separate PR, but guess it's easier to tack on here. |
…imiter. "Prefixes" are a way to have one cache-server (e.g. one instance of redis or memcached) which stores several different data-sets. `CRM_Utils_Cache` uses prefixes in a couple ways: * (1) General site prefix (controlled via `civicrm.settings.php`) * (1a) If you have a single-site deployment, then the general prefix is blank. * (1b) If you have a multi-site deployment, then each site should use a different prefix (`mysite_1`, `mysite_2`, etc). * (2) Within a given deployment, prefixes may indicate different logical data-sets. * (2a) `Civi::cache()` or `Civi::cache('default')` or `CRM_Utils_Cache::singleton()` are the `default` data-set. * (2b) `CRM_Utils_Cache::create()` can instantiate new, special-purpose data-sets. For example, this is used for `Civi::cache('js_strings')`. This patch addresses two issues: * (Functional) Flushing the 'default' cache would likely flush all other caches because the 'default' cache didn't have a distinctive prefix. (This was observed Redis. Theoretically, the bug would apply to some-but-not-all cache backends.) * (Aesthetic) The full cache paths don't look consistent because they don't have a standard dlimiter. To fully understand, it helps to see example cache keys produced in a few configurations before and after the patch. See also: https://lab.civicrm.org/dev/core/issues/174 Before ----------------------------- | |Deployment Type|Logical Cache |Combined Cache Prefix |Example Cache Item (`foobar`)| |-|-|---------------|-------|-----------------| |1a,2a|Single-site|`default` |(empty-string)|`foobar`| |1a,2b|Single-site| `js_strings` |`_js_strings`|`_js_stringsfoobar`| |1b,2a|Multi-site |`default` |`mysite_1_`|`mysite_1_foobar`| |1b,2b|Multi-site |`js_strings` |`mysite_1_js_strings`|`mysite_1_js_stringsfoobar`| * If you have a single-site deployment and try to flush `default`, you inadvertently flush `js_strings` because everything matches the empty-string prefix. * If you have a multi-site deployment and try to flush `default`, you inadvertently flush `js_strings` because the prefix overlaps. * The three parts of the key (deployment ID, logical cache, and cache item) are not necessarily separated. After ----------------------------- | |Deployment Type|Logical Cache |Combined Cache Prefix |Example Cache Item (`foobar`)| |-|-|---------------|-------|-----------------| |1a,2a|Single-site|`default` |`/default/`|`/default/foobar`| |1a,2b|Single-site|`js_strings` |`/js_strings/`|`/js_strings/foobar`| |1b,2a|Multi-site |`default` |`mysite_1/default/`|`mysite_1/default/foobar`| |1b,2b|Multi-site |`js_strings` |`mysite_1/js_strings/`|`mysite_1/js_strings/foobar`| * If you have a single-site deployment and try to flush `default`, you only flush `default` because the prefixes are distinct. * If you have a multi-site deployment and try to flush `default`, you only flush `default` because the prefixes are distinct. * The three parts of the key (deployment ID, logical cache, and cache item) are always separated by `/`. Comments -------- When developing this patch, I found it helpful to: * Enable Redis driver * Open `redis-cli` and view the list of cache items with `keys *`.
…ring fixing prefixes The preceding update to `CRM_Utils_Cache` meant that `CRM_Utils_Cache::singleton()->flush()` (aka `Civi::cache()->flush()`) would flush only the *default* cache. This revision ensures that a general system-flush still hits the same caches. However, we can now define *other* caches which *won't* be hit by system-flush.
The `deleteGroup(...$clearAll...)` option is heavy-handed and leads to call-paths that hard to grok. In the current design of CRM_Core_BAO_Cache (with multi-tier caching), one does need to clear these things. But let's not clear everything under the sun...
aa3023f
to
1d521e7
Compare
There were some reported test failures. I patched/rebased to address them. |
$cache->flush(); | ||
CRM_Utils_Cache::singleton()->flush(); | ||
if (Civi\Core\Container::isContainerBooted()) { | ||
Civi::cache('settings')->flush(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So @totten what strikes me here is what if my extension uses a different key - it will not now be flushed & I have 'no say' in that...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(1) In a hypothetical sense, I can see how that concern comes up. CRM_Utils_Cache::singleton()->flush();
previously flushed the big yellow circle, and now it just flushes the smaller default
gold circle. But in a practical sense, I don't think the change has the effect you're concerned about.
Why not?
- In
universe
, I could only find two extensions (be.chiro.civi.idcache
anduk.co.compucorp.civicrm.pivotreport
) that touch thedefault
instance (Civi::cache()
orCRM_Utils_Cache::singleton()
). Neither of them does any flushing. They just read/write a couple keys. - Most developers and admins work with higher-level flush operations -- like
CRM_Core_Invoke::rebuildMenuAndCaches()
. At least, this is what shows up most in grepping, and it matches anecdotal experience. - Consider https://gist.github.com/totten/0f0a0d5ca03e8e4ff5b7b6260e3ff36c -- if your feeling is "I want to flush things", then most folks go for one of the big LISTs (like
rebuildMenuAndCaches()
) because they're too nervous about digging into the specific lifecycle of each cache. In the big picture,CRM_Utils_Cache::singleton()->flush()
is just a BASIC. (This PR/issue is saying that the BASIC flush was slightly broader than one would expect -- but we're preserving the functionality of the bigger LISTs.) - There isn't much selective pressure that would force people to be aware of
CRM_Utils_Cache::singleton()->flush()
. On a vanilla/default deployment, you don't have memcache/redis/whatever. Instead,default
is implemented byCRM_Utils_Cache_ArrayCache
-- which means the cache flushes as soon as the page-request ends. You'd rarely think to flush a cache that automatically flushes for you in a typical page-request.
(2) The use of the word "key" suggests some confusion. Let's consider two ways in which two hypothetical extensions might be working with cache services:
/* 1 -- Using the default cache, and setting some keys inside there */
/* 1a */ Civi::cache()->set('multisite_mydata', 123);
/* 1b */ Civi::cache()->set('extended_report_otherdata', 456);
/* 1c */ Civi::cache()->flush();
/* 2 -- Using a custom cache object, and each is a separate namespace for keys */
/* 2a */ Civi::cache('default')->set('multisite_mydata', 123);
/* 2b */ Civi::cache('extended_report')->set('otherdata', 456);
/* 2c */ Civi::cache('default')->flush();
/* 2d */ Civi::cache('extended_report')->flush();
There are two cases in universe
which looks like example 1. That example works the same as before.
The PR would hypothetically affect something like example 2 (i.e. because 2c would flush data from 2b) on systems using memory-backed cache, but I don't think anyone does example 2. It's just not well known; there are no tutorials or documentation explaining it, and I can't find any evidence in universe
of code even vaguely like it. The issue really should be limited to 3-ish cache-services in civicrm-core
.
Civi::cache('settings')->flush(); | ||
Civi::cache('js_strings')->flush(); | ||
Civi::cache('community_messages')->flush(); | ||
CRM_Extension_System::singleton()->getCache()->flush(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or is that totally covered here ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the extension concern discussed above is addressed more by saying, "Extensions don't use this thing".
The code there does mention extensions (CRM_Extension_System::singleton()->getCache()->flush();
), but this is metadata used by the extension-system tracking info about extensions (specifically the data structure which says "the source code for foo
is located in sites/default/files/civicrm/ext/foo
). The line is probably redundant or irrelevant in most usage, but there's arguably an edge-case where it's needed to maintain the behavior of "flush the big yellow circle".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@totten I was more thinking about whether I might want to use cache prefixes in an extension - although I guess I can agree that is not something in use now so it could be punted
I feel like this has been investigated pretty thoroughly & am merging. I think the test suite will be the most rigourous tester of all this! |
"Prefixes" are a way to have one cache-server (e.g. one instance of redis or memcached)
which stores several different data-sets.
CRM_Utils_Cache
uses prefixes in a couple ways:civicrm.settings.php
)mysite_1
,mysite_2
, etc).Civi::cache()
orCivi::cache('default')
orCRM_Utils_Cache::singleton()
are thedefault
data-set.CRM_Utils_Cache::create()
can instantiate new, special-purposedata-sets. For example, this is used for
Civi::cache('js_strings')
.This patch addresses two issues:
To fully understand, it helps to see example cache keys produced in a few
configurations before and after the patch.
See also: https://lab.civicrm.org/dev/core/issues/174
Before
foobar
)default
foobar
js_strings
_js_strings
_js_stringsfoobar
default
mysite_1_
mysite_1_foobar
js_strings
mysite_1_js_strings
mysite_1_js_stringsfoobar
default
, you inadvertently flushjs_strings
because everything matches the empty-string prefix.default
, you inadvertently flushjs_strings
because the prefix overlaps.After
foobar
)default
/default/
/default/foobar
js_strings
/js_strings/
/js_strings/foobar
default
mysite_1/default/
mysite_1/default/foobar
js_strings
mysite_1/js_strings/
mysite_1/js_strings/foobar
default
, you only flushdefault
because the prefixes are distinct.default
, you only flushdefault
because the prefixes are distinct./
.Comments
When developing this patch, I found it helpful to:
redis-cli
and view the list of cache items withkeys *
.