Skip to content
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

Rewrite subTypeInfo to use caching mechanism #14715

Merged
merged 1 commit into from Aug 9, 2019

Conversation

eileenmcnaughton
Copy link
Contributor

Overview

Fix unnecessary DB lookups on sites with no sub types due to cache miss

Before

Cache being missed

After

Cache not being missed

Technical Details

So @totten @seamuslee001 I spotted this one getting a lot of setex in our redis logs & it also seems a good one for me to clarify the 'right' methodology. The issue here is that the change in this PR seems to work & fixes it but I'm no longer sure the best way to clear the cache - currently if you add a type it calls

     // reset the cache after adding
    self::subTypeInfo(NULL, FALSE, FALSE, TRUE);

This PR flattens the key so we would need to do multiple cache deletes to achieve the same - what is the right approach?

Comments

@civibot civibot bot added the master label Jul 3, 2019
@civibot
Copy link

civibot bot commented Jul 3, 2019

(Standard links)

@seamuslee001
Copy link
Contributor

Test fails look related @eileenmcnaughton

@eileenmcnaughton
Copy link
Contributor Author

@seamuslee001 yeah this is more a discussion piece at the moment - I had some questions on it ^^

@seamuslee001
Copy link
Contributor

@eileenmcnaughton when you say it flatterns the key what do you mean?

@eileenmcnaughton
Copy link
Contributor Author

So the current function stores a static array

$cache = ['permutation1' => x, 'permutation2' => y];

If we look at things like OptionGroup::values it saves a value for each option group which is like

Cache->set('permutation1', x)
Cache->set('permutation1', y)

This later feels better to me - except when you want to clear them all - which we would do here if editing contact subtypes

@totten
Copy link
Member

totten commented Jul 10, 2019

This PR flattens the key so we would need to do multiple cache deletes to achieve the same - what is the right approach?

IMHO, adding a new cache-group is probably the simplest way to get the desired effect. You'd probably want to:

  • Declare the cache in Container.php.
    • Bonus: The default cache doesn't specify withArray=>fast, but you can specify it on a new group. If I'm reading correctly, that's closer to the previous policy (ie using the composition of a php-local cache plus a redis cache).
  • Update CRM_Utils_System::flushCache() to also flush this cache.

There are alternatives, of course, e.g.

  • If you can easily list all the values of permutation1, permutation2, etc -- then just delete all of them. ($cache->deleteMultiple(['permutation1', 'permutation2', ...])).
  • Some cache backends support tagging and/or hierarchies. The PSR-16 interface doesn't expose this functionality. It might be possible to write or find an extension to PSR-16 that makes it possible, but I'd regard that as a distinct project. (Probably 1-3 days' work.)

@eileenmcnaughton
Copy link
Contributor Author

@totten so I feel like we'll quickly get a lot of containers then? Perhaps that is not a bad thing. We can declare them in extensions too it just feels kinda like a heavy lift

@eileenmcnaughton
Copy link
Contributor Author

"Bonus: The default cache doesn't specify withArray=>fast"

@totten I thought we HAD made it so that we got fast cache mgemt by default?

@totten
Copy link
Member

totten commented Jul 10, 2019

@eileenmcnaughton Yeah, there is a bit of boilerplate in declaring a cache.

Something like this mitigates that. We can probably come up with a similar mitigation for flushCache() so that flushCache() basically clears all cache.* services.

The most aggressive response to the boilerplate issue would be to move the cache mgmt out of Civi\Core\Container and create a dedicated Civi\Core\CacheManager service which could be more Clever. Ex: If you try to access an unrecognized cache, then it creates it implicitly/on-demand per some general policy, but does it in a way were we can still discover/clear out any of those lazily-made caches. We'd lose the presumption that all caches are injectable services, but otherwise (with a day or so's work) I think it could be done as a drop-in update.

@totten
Copy link
Member

totten commented Jul 10, 2019

@totten I thought we HAD made it so that we got fast cache mgemt by default?

I think that's true for caches that go through the CRM_Core_BAO_Cache_Psr16 adapter. But to my reading of CRM_Utils_Cache and Civi\Core\Container, the system-wide short+long caches don't.

@eileenmcnaughton
Copy link
Contributor Author

hmm - I had thought we got to the point where Civi::cache()->get() WAS going through that adapter - we definitely don't want this (or the already merged groups one) to not be array backed.

@eileenmcnaughton
Copy link
Contributor Author

Discussion on chat

https://chat.civicrm.org/civicrm/pl/9n5gbbesf3bsjn8dzw5q6bcgyw

short version - this risks having a detrimental effect on performance for Redis/Memcache users without figuring out the fastArray stuff

@eileenmcnaughton
Copy link
Contributor Author

Added 'has-test' because after stepping through the tests that failed when I made a mistake I can see there is pretty solid cover here

@eileenmcnaughton
Copy link
Contributor Author

As an aside - I'm pretty sure that we can drop the $ignoreCache parameter too -will put up a PR to do so if I have a reviewer

Screen Shot 2019-08-02 at 4 17 00 PM

@eileenmcnaughton
Copy link
Contributor Author

After reviewing the test fails I decided to rename the cache contactTypes since it needs to be cleared whenever the contact type cache is cleared (outside tests that should be rare)

I also note that when adding or deleting a contactType we need to

  1. clear the cached data
  2. rebuild the menus

I kinda wonder if
a) we should have an on_clear callback that rebuilds the menus whenever the cache is cleared &
b) move both to some kinda listener.. - I think @colemanw sees listeners / some sort of post action as the way to go with creates & adds

Also I think we should add a pseudoconstant cache @seamuslee001 to move the cached vars in the Core_Pseudoconstant class into

@eileenmcnaughton
Copy link
Contributor Author

@seamuslee001 I think this is mergeable now

@@ -50,7 +50,7 @@ public static function registerProfileScripts() {
'phoneType' => CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id'),
],
'initialProfileList' => $ufGroups,
'contactSubTypes' => CRM_Contact_BAO_ContactType::subTypes(),
'contactTypes' => CRM_Contact_BAO_ContactType::subTypes(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eileenmcnaughton why the change of key here looks odd

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@seamuslee001 agree it looks wrong - changed it back

CRM_Core_BAO_Navigation::resetNavigation();
Civi::cache('contactTypes')->clear();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you using clear() here when we have consistently used flush() which is an alias of clear

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because flush is actually deprecated!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok do we want consistance or non deprecated code paths :-P

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we get consistency when the others are migrated over


// reset the cache after adding
self::subTypeInfo(NULL, FALSE, FALSE, TRUE);
Civi::cache('contactTypes')->clear();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as L604

@@ -1442,6 +1442,7 @@ public static function flushCache() {
Civi::cache('groups')->flush();
Civi::cache('navigation')->flush();
Civi::cache('customData')->flush();
Civi::cache('contactTypes')->clear();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as on ContactType BAO L604

This seems to be getting a lot of cache misses on prod - presumably due to a lack of sub types
so fixing to use 'modern caching';
@seamuslee001
Copy link
Contributor

ok i'm ok with this now and if Jenkins is then we can merge. I'm pretty confident we have good test coverage covering this

@eileenmcnaughton
Copy link
Contributor Author

@seamuslee001 thanks - working through this makes me feel like we probably also want a cache for 'pseudoconstants' - basically anything handled by CRM_Core_Pseudoconstant -

It was a good exercise to work through

@eileenmcnaughton eileenmcnaughton merged commit d92d559 into civicrm:master Aug 9, 2019
@eileenmcnaughton eileenmcnaughton deleted the cache_sub branch August 9, 2019 06:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants