(dev/core#174) CRM_Utils_Cache - Always use a prefix. Standardize delimiter.
authorTim Otten <totten@civicrm.org>
Fri, 30 Mar 2018 00:36:56 +0000 (17:36 -0700)
committerTim Otten <totten@civicrm.org>
Tue, 19 Jun 2018 21:40:57 +0000 (14:40 -0700)
commit118eb8309d743007397bc3169e5170812eb8dea9
treee8bebebbbd9dc3b7032b4033d4ee85df526eb15e
parent9f06cdc9850fcdec5aec40fb5c48a3e769bb0008
(dev/core#174) CRM_Utils_Cache - Always use a prefix. Standardize delimiter.

"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 *`.
CRM/Utils/Cache.php