Updated: Comment #N
Problem/Motivation
Currently RulesPlugin:save() clears the whole cache if the no original entity was found
or if the variables from the original entity don't match with the current ones.
Problem with this approach is that the property original never exists
because it's deleted by EntityAPIController::save().
In our environment with a looooot of rules (that are triggered in the frontend) this causes some serious trouble (stampeding) when rebuilding the whole cache.
Especially since the "cache" rules_event_whitelist is handled as variable: #2042851: PDOException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'rules_event_whitelist' for key 'PRIMARY
This would be less of a problem if it would be handled by cache_get() because we use redis and that wouldn't fail this way.
However, I guess the performance problem is already enough annoying to justify a fix ;)
Proposed resolution
Fetch the original entity on our own and make it reusable for the other entity controllers.
This pattern is already implemented in several other locations e.g. RulesEntityController::save().
Question:
Is there a way to make the cache flush even more atomic?
E.g. just removing the current component from the cache data array.
The patch rules-rules-plugin-cache-clearing-fix-flush-specific-component.patch contains a POC to test such an approach. The tests so far show a dramatic performance gain.
Remaining tasks
Reviews needed.
User interface changes
None
API changes
None
| Comment | File | Size | Author |
|---|---|---|---|
| #35 | 2189645-35.component-caches.patch | 10.7 KB | jonathan1055 |
Comments
Comment #1
das-peter commentedComment #2
das-peter commentedWorked a little bit on the POC (hey, it's green too *yay*).
There are still some cases I fear, like changing component variables will trigger again a whole cache clear. But thankfully this case should occur not that often.
Also the event caching / white-listing looks like it could be optimized in a similar way.
We've a pretty clean cache structure, that should allow us to implement more atomic operations in the cache clearing / building. And there's always the sledgehammer method if something goes west - full rebuild.
Comment #3
das-peter commentedAnother adjustment for the POC. With the new cache clearing for components it should be possible to keep the cache even if a new component is created. The cache of the new component will then be built on demand.
Comment #6
das-peter commentedI'm still working on this. I've to evaluate how I can adjust the cache to be more lazy.
Comment #7
das-peter commentedHere we go. No interdiff since it would be almost bigger than the patch.
I still don't like the fact that the cache has no "fallback": cache miss = fail instead cache miss = rebuild
But for now the adjustments should speed up all the stuff quite a bit.
Locally all tests pass.
Comment #9
das-peter commentedFixed issue. Seems to be php version dependent.
Comment #10
Paul B commentedWith the patch in #9, if I try to add a loop to an action set, I get a fatal error "Call to undefined function rules_rule_build_action_info()"
Comment #11
das-peter commentedOh, indeed. Unfortunately it's not granted that the newly used functions in
_rules_rebuild_component_cache()are available.Attached patch adds an include to the file that takes care of including all that stuff.
Comment #12
fagoHow can this happen / be necessary? The cache is just initialized above, does that mean the cache is built "broken"?
Good addition. Let's better name this getCacheId() though.
Why would we skip this if it is a rebuild? That does not say there are no changes.
When adding a new one, we need to rebuild caches as well or the new action won't be available.
Let's better aggregate event names first and then rebuild caches for all of them. This should make the code a bit simpler.
What's that about? Why would it be called more often?
Uhm, fuddling around with the built action/condition info cache seems to be dangerous, i.e. could quickly lead to obscure bugs.
Couldn't we just improve it to rebuild whole condition/action info caches when not more is necessary? Otherwise, if you need to save the cache rebuild I could see us adding an opt-out of cache-rebuilding when you add a new component?
Hm, that adds quite a few function call for *every* rules_invoke_event() what's bad. Also, I'm not sure about adding the separate cache vs the previous variable cache - why is that better?
Reading #2042851: PDOException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'rules_event_whitelist' for key 'PRIMARY - I see, but could we handle that over there? One issue by one? :-)
Comment #13
das-peter commentedComment #14
das-peter commentedDamn, I forgot to adjust an error I found yesterday.
Comment #15
joelpittetLooks like it needs a re-roll. I'll try and do so.
patching file includes/rules.core.inc
patching file includes/rules.plugins.inc
patching file modules/rules_core.rules.inc
patching file rules.module
Hunk #1 FAILED at 329.
Hunk #2 succeeded at 396 (offset 22 lines).
Hunk #3 succeeded at 426 (offset 22 lines).
Hunk #4 succeeded at 474 (offset 22 lines).
Hunk #5 FAILED at 1049.
Hunk #6 FAILED at 1082.
3 out of 6 hunks FAILED -- saving rejects to file rules.module.rej
Comment #16
joelpittetHmm. I gave it a try but it looks like a different approach was taken instead of static cache to use a $conf instead.
Comment #17
joelpittetFWIW, here's a the reroll, though I'm not totally sure I got it correct, @das-peter thoughts?
Comment #18
joelpittetLinking up a different approach to the same problem
Comment #19
joelpittet@das-peter This needed another re-roll. Again not sure if I got the conflicts merged correctly. Could you have a peek here? Or maybe this is not needed anymore?
Comment #20
fagoHaving a closer look at this one, here are my thoughts:
However, all of this improvements do not address this issue - still, a component change including variables, or an addition triggers clearing of the complete rules cache. We can easily improve this to only clear the rules 'data' cache + the cache of the component changed, once we have done the improvements above. Hopefully, that will be enough to solve the problems. If not, I think we should split up the rules 'data' cache monolith into separate chunks per (condition_info, action_info, ..) which can be built + cleared separately by converting it to a DrupalCacheArray (as it was done with the ThemeRegistry).
Comment #21
fagoCreated #2650886: Reduce the likeliness of cache rebuilding locks for the improvements not necessarily related to this and leaving this issue open for
>We can easily improve this to only clear the rules 'data' cache + the cache of the component changed, once we have done the improvements above.
Comment #22
fagoComment #23
fagook, I worked over the patch to make it work as described before (points 1+2) from #20 + #21.
>I'd not updated it immediately though, such that the update operation of the cache triggers the proper lock.
I figured it should be fine to update immediately and did so for events + components. As there should be old, active cache entries that are used meanwhile, there is no need for a lock while building the cache entry when updating per rule-save.
Patch attached. In my tests it worked fine.
Comment #24
joelpittetI've been using this patch for a while, so far so good. +1 but could use a last code-review before going in
Comment #26
tr commentedPatch just needed a little tweak/re-roll to apply against the current HEAD. I haven't reviewed this yet - this is just a re-roll to bring it up to date.
Comment #27
tr commentedThe testbot shows that the patch introduces two coding standards errors, so here is a re-roll with those two fixed:
Comment #28
joelpittetThanks for the re-roll @TR, I've reapplied it.
Comment #29
joelpittetSo far it's on two production sites and no issues to speak of
Comment #32
delacosta456 commentedhi
i try #17 and #19 with phpstorm and patch has some chunk issues
Comment #33
tr commented@delacosta45:
#17 is from the year 2014 and #19 is from 2015 - of course they don't apply anymore! I really don't understand the purpose of your post. #17 and #19 are obsolete patches, so why bother testing them?
Also, those are NOT the patches that we care about and those patches will NOT be committed. Fixes to these patches were made in #23, and that patch was re-rolled and improved in #26 and #27.
The patch under consideration is #27, which is more than a year old and needs a re-roll to apply against the current Rules HEAD.
It would be helpful if you could re-roll #27 and try it yourself, then post the new patch and your review here.
Comment #34
delacosta456 commented@TR
hi ok .. as those patch were not hidden and doesn't have the same naming i though i had to apply the one before the other.
Applying #27 directly applied successfully .
Comment #35
jonathan1055 commentedRe-roll of #27.
Other patches hidden to avoid the mistake like #32
Do we need to consider this for 8.x? I think the caching is done in a completely different way, and it is not a problem at 8.x but just wanted to ask the question.
Comment #36
tr commentedNo, none of this should be considered for D8. Caching in D8 is mostly a function of core. If someone provides specific tests/use-cases to demonstrate that caching in D8 can be improved, that will require a completely different solution and should be handled in a separate issue. At this time I have seen no indication that caching is currently a problem in D8.
The only thing bothering me about this patch is that it has the potential to be disruptive to the >200,000 users of D7 Rules. The D7 version of Rules is very stable, and we ought to take great pains to do nothing to break sites that have been using Rules for many years. While it's great we have a patch for people who DO need it, I question whether this should be pushed into the codebase to be used by people who DON'T need it. A set of test cases would go a long way to prove that this won't adversely affect existing users, especially since our D7 version doesn't have very good coverage to being with.