Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
#1779026-155: Convert Text Formats to Configuration System
The code is copied literally from DatabaseStorageController.
This issue will also need to prevent the config entities from being cached in the config factory.
Comment | File | Size | Author |
---|---|---|---|
#127 | 1885830-126.patch | 29.86 KB | Berdir |
#126 | 1885830-126-interdiff.txt | 2.14 KB | Berdir |
#123 | 1885830-123-interdiff.txt | 839 bytes | Berdir |
#123 | 1885830-123.patch | 28.52 KB | Berdir |
#118 | 1885830-118.patch | 27.7 KB | Berdir |
Comments
Comment #1
tim.plunkettTagging
Comment #2
sunerm, WTF is DatabaseStorageController doing for the insert operation there?
That's a no-op?
Comment #3
sunComment #5
sunFixed the variable name mismatch.
Comment #7
sundoh
Comment #9
sunAlright - the remaining failures are all caused by stale caches, it seems :)
For the existing config entities in core, the additional caching isn't actually that impactful. However, the more more frequently used things are converted - e.g., like menus - or filter formats, or user roles - or #111715: Convert node/content types into configuration, the additional cache layer will be more measurable.
That said, a persistent entity-level cache would actually have a much bigger impact than just the in-memory static caching. It would instantly allow us to get rid of a lot of custom caches like
filter_formats()
andnode_get_types()
anduser_roles()
, etc. which are all just simply wrappers aroundentity_load_multiple()
.Comment #10
catch#1596472: Replace hard coded static cache of entities with cache backends would make it easier to swap out different entity cache controllers.
Comment #11
plachHere and in #1919322: entity_load_unchanged() should be part of the storage controller we have repated code between database and config storage controllers. Would it make sense to have an abstract
BaseStorageController
as a common ancestry?Comment #12
tim.plunkettYes please. EntityStorageControllerBase would be a more correct name.
Comment #13
plach@sun suggested to address this in #1893772: Move entity-type specific storage logic into entity classes.
Comment #14
tim.plunkettReroll.
Comment #16
tim.plunkettFun fun fun. A lot of that is views, and I have some ideas as to why.
Comment #17
catchCross-linking #1596472: Replace hard coded static cache of entities with cache backends.
Comment #18
tim.plunkettI have some more Views fixes to post, but I found a big part of the problem with the tests.
During SimpleTest runs (but not page loads), EntityManager is recreated almost every time the plugin.manager.entity service is retrieved.
That means that the property containing all the controllers is reset, and each time a new storage controller will be used.
Which means that the $entityCache for each storage controller is different, and will lead to different results.
Who knows why retrieving a service gives you a new object only during tests?
Comment #19
effulgentsia CreditAttribution: effulgentsia commentedBecause modules are constantly being enabled/disabled, and the container rebuilt?
Comment #20
tim.plunkettThis is within a single test method.
Looking at:
http://api.drupal.org/api/drupal/core%21modules%21shortcut%21lib%21Drupa...
Between the $set = $this->set; and the #this->drupalPost(), EntityManager is recreated three times.
Unless I'm just losing it, which may be possible.
Comment #21
tim.plunkett#1978870: Add an EntityStorageControllerBase
Comment #22
tim.plunkettTagging.
Comment #23
damiankloip CreditAttribution: damiankloip commentedI think we should postpone this on #21 for now. As we can reuse cache related methods from there.
Comment #24
damiankloip CreditAttribution: damiankloip commentedComment #25
damiankloip CreditAttribution: damiankloip commentedHere's a reroll now we've got that base class. I will also write some tests for this.
Comment #26
BerdirWe discussed in Portland that static caching on this level is problematic because the config might return different information based on the config context, e.g. language.
So we either can't have a static cache or we need to care about the config context as part of the cache key. We need to check how big the overhead is of creating config entities.
Comment #27
damiankloip CreditAttribution: damiankloip commentedHmm, good point. Why aren't things ever easy.. :)
Comment #29
damiankloip CreditAttribution: damiankloip commentedSo maybe we would need to do something like this? Not sure if it will reap many benefits from a performance point of view tbh.
Comment #30
BerdirIn most scenarios does the UUID not change I guess (same language, domain, ...), so it should bring something.
Will do some profiling after #1971158: Follow-up: Add loadMultiple() and listAll() caching to (cached) config storage is in.
Comment #31
damiankloip CreditAttribution: damiankloip commentedYeah, I guess even it is is a bit better, it could be worth it, who knows. Let's wait for that issue. We don't want too many layers of static caching either I guess.
Comment #32
damiankloip CreditAttribution: damiankloip commentedComment #34
BerdirI guess most of those test fails now just need some static cache clears.
Comment #35
damiankloip CreditAttribution: damiankloip commentedReminder for the next patch: It would be good if getCurrentContextUuid() actually returned something.
Comment #36
yched CreditAttribution: yched commentedRelated: #2073297: Remove cache of Field / FieldInstance objects
Comment #36.0
yched CreditAttribution: yched commentedUpdated issue summary.
Comment #37
BerdirOk, so here's another reason why we need this: #2095283: Remove non-storage logic from the storage controllers. That's how often we load roles, up to 4k times in a single test.
Comment #38
BerdirRe-roll.
Comment #39
BerdirComment #41
damiankloip CreditAttribution: damiankloip commentedWhen I was working on this patch before there were issues with forms and clearing of the static cache. Hopefully this has been helped by the work to avoid serialising objects in the form cache etc... That was basically blocking it before iirc.
Comment #42
Berdir@damiankloip: There is no issue here :)
Those fails just prove that this works. That's what you get when you combine static caches and change the data in them through form submits. There is only one solution and that's manually clearing them (well, the other alternative would be to clear all entity caches on every drupalPost()). This patch starts doing that, still quite a lot to go, but this should be something that someone with less experience can do, I'll try to get @vladan.me on it, he started to work on a lot of entity issues last week.
Comment #44
vladan.me CreditAttribution: vladan.me commentedHopefully, this should fix all fails except ViewPageControllerTest, I am not quite sure right now what problem is, test is giving this as description "The test did not complete due to a fatal error."
Comment #46
vladan.me CreditAttribution: vladan.me commented44: 1885830-44.patch queued for re-testing.
Just to make sure that test results are correct.
Comment #48
BerdirFixing the path and views handler test.
No idea about the other one, that one is interesting...
Comment #49
vladan.me CreditAttribution: vladan.me commentedI couldn't find also cause of last one so I didn't upload those two fixes (had them though). What could cause such error, doesn't seem related at all...
Comment #51
BerdirHere's the fix that @dawehner found: http://paste.ubuntu.com/6418465/
Care to roll a patch with that included?
Comment #52
damiankloip CreditAttribution: damiankloip commentedI think we just want the second change and not the first.
Comment #53
vladan.me CreditAttribution: vladan.me commentedCan you be more specific damiankloip? Just to use this?
Comment #54
damiankloip CreditAttribution: damiankloip commentedYes, exactly that :)
I just tested this out locally, and the test doesn't actually pass with the first change added too. So I guess it was the right hunch.
Comment #55
vladan.me CreditAttribution: vladan.me commentedUploading new patch, patch is rerolled because of #2134951, also, made change noted in comment above, no interdiff included.
Comment #56
dawehnerIf we don't clone the object, we end up in manipulating the same view object somehow.
@berdir here comes a question. Let's assume you have the following code in two different blocks:
Both of these pieces of code ran, ... should we not try to clone the cached entity, so we don't have conflicting code at all?
Comment #57
Berdir55: 1885830-55.patch queued for re-testing.
Comment #58
BerdirI don't know, but that's how it's been for nodes/content entities since the static cache for nodes was added in D6...
Comment #60
BerdirRe-roll.
Comment #62
moshe weitzman CreditAttribution: moshe weitzman commentedComment #63
damiankloip CreditAttribution: damiankloip commentedJust another reroll.
Comment #66
Berdir63: 1885830-63.patch queued for re-testing.
Comment #68
damiankloip CreditAttribution: damiankloip commentedAnother reroll.
Let's look at what is still failing.
Comment #69
damiankloip CreditAttribution: damiankloip commentedComment #71
damiankloip CreditAttribution: damiankloip commentedAnd with the config context stuff now removed.
Comment #74
damiankloip CreditAttribution: damiankloip commentedComment #76
damiankloip CreditAttribution: damiankloip commentedLet's see how far this gets us.
Comment #78
damiankloip CreditAttribution: damiankloip commentedWe don't need to override those methods anymore at all, we can just use what's provided by EntityStorageControllerBase for that.
Comment #79
BerdirAnd what if code like user_mail() would load config entities and not raw config?
We will need a test for that somewhere...
I haven't looked at the changes at all yet, but according to @alexpott, there's a way to get the cache key from the config factory that could work as a cache separator.
Comment #81
BerdirThe method for that would be getCacheKey(), but that is actually flawed, as described in #2177739: Fix inefficient config factory caching, because it can not possibly consider module overrides.
And we do have an example for this in core, just not enough test coverage apparently, see Drupal\Core\Datetime\Date::formatDate()
Comment #82
jibran78: 1885830-78.patch queued for re-testing.
Comment #84
BerdirRe-roll, bunch of context conflicts.
Comment #86
damiankloip CreditAttribution: damiankloip commentedComment #88
damiankloip CreditAttribution: damiankloip commentedback to the same 5 failures then...
Comment #90
swentel CreditAttribution: swentel commentedFixes the textfieldtest
Comment #91
sunHm. The amount of manual calls to reset caches in web tests worries me a bit. Degrades the DX of writing web tests for everyone... :-/
Technically, we already have a solution for updating the process in the test runner with changes from the child site:
Every
WebTestBase
request calls intorefreshVariables()
, in order to reset static caches on e.g. config factory, etc.Until we have a more generic solution (→
StaticCacheInterface
+ tagged services), I wonder whether it wouldn't make more sense to replace all of these manual resetCache calls with a single call inrefreshVariables()
?I see that the storage controller is individually selected throughout this patch — is there a way to iterate over all available entity managers, get their storage controllers, and call
resetCache()
on them?These two (and because of these two most likely some more) cache resets do not appear to be needed, because the code runs in a single process.
Comment #92
Berdir1. Well, it just makes it as bad as it always was for content entities. Sure, we could consider to loop over all defined entity types and clear their static caches, but what we can not do is just do it for those that were actually used, so we'd have to initially a lot of storage controllers just to empty the obviously empty static cache :) Unless we introduce a new method on the entity manager...
We would also have to only do it for config entities IMHO, because it would be a bit unfortunate for content entities as soon as #597236: Add entity caching to core lands, as that would have to cache tag clears all the time.
See also my latest comment in that issue (https://drupal.org/comment/8541147#comment-85411479), clearing the caches all the time would hide that pretty serious bug, so I'd prefer to live with the worse DX. I think a lot of people that have written tests in 7.x are already pretty used to entity and drupal_static() caches interfering with tests...
2. Hm, that does look strange, but I think we just went through test fails and fixed them? Sounds like that can either be removed or would be a cache clear bug ?
Comment #93
BerdirRe-roll.
#91.2 was in fact an actual bug, we didn't clear the cache for renamed entities. This should address that.
Did some micro-benchmarks:
HEAD: ~650ms
Patch: ~180ms
=> ~3.5 times faster.
Question is, how much does it affect and actual page load, that's harder to answer. I did some basic tests, but on a frontpage with two nodes and a custom block, we apparently didn't have a single static cache hit. #2211723: FieldInstance::__construct() loads all field_config entities for example is a bigger problem right now. Not sure if that would change if we'd no longer kept cached entities in FieldInfo.
Based on that, this is probably not worth the effort at the moment, unless someone is able to provide different numbers?
Comment #94
BerdirForgot the interdiff.
Comment #96
moshe weitzman CreditAttribution: moshe weitzman commentedLatest profiling by @msonnabaum suggests that this would really help. He noticed us loading up the list of roles many many times for access checks. Anyone up for revisiting this?
Comment #97
BerdirInteresting, I did expect that to show up as well, but wasn't able to see that, care to share what/how you profiled?
For the specific case of permissions/roles, @alexpott also opened an issue to cache the list of permissions in User:.hasPermission(), we used to have built-in caches there which were all dropped.
Comment #98
msonnabaum CreditAttribution: msonnabaum commentedIt saw it when profiling a view that listed a large number of nodes/users. I forget where they were coming from, but the calls were to UserSession::hasPermission, which fetches roles each time.
Comment #99
BerdirAh that makes sense, access checks for operations mostly I guess...
Comment #100
BerdirOne thing that I discussed with @fago in Szeged was that was could provide the functionality but not enable it by default for config entities, we can do that very easily now with @ConfigEntityType.
Then config entity types that are loaded a lot can explicitly enable it and benefit, without having to do so for the dozen or more types that are not used that frequently.
Still for permission checks, even just looping over the roles and their permissions might be more overhead than we want, so we might want to do #2202185: Statically cache Role entities to speed up (User|UserSession)::hasPermission() anyway, and then I'm not sure if we want this to be enabled for roles?
Comment #101
msonnabaum CreditAttribution: msonnabaum commentedIt doesn't hurt for it to be enabled for roles, but I agree it's worth doing 2202185 if for no other reason than it's a code improvement.
I'd much prefer that static caching we're opt-out. Historically, opt-in results in people not knowing about it and then performance issues popping up that are only found when profiling (this happened with ctools plugins in contrib).
Comment #102
BerdirThe problem with opt-out can be seen in the current patch, if we enable it for all types on core, then we run into all those test issues that we need to fix, and manually disabling it for them will probably result in people copying that without thinking about it :)
Not enabling in this case also only means we just fall through to the next static cache, it's not like we need to execute database queries or something, so it's not *that* bad as long as we don't call it as often as roles can be.
Might also be useful to compare the memory usage when enabling it.
Note that I'm fine with moving forward with this, just want to make sure that we are sure that it's worth it and do it in a way that doesn't result in worse DX than it needs to be (static caches in web tests are the pain, it took us forever to get this green).
Comment #103
damiankloip CreditAttribution: damiankloip commentedNeeds a bit of a reroll with the getStorageController > getStorage change.
I think I would definitely like to see this opt-out too. I basically agree totally with #101.
I also agree that this current patch is pretty cumbersome. The amount of ad-hoc cache clears needed is obviously not ideal. If we were to use a real storage backend (memory backend), we could always just use a null backend in tests or something. This is more difficult all the time we have baked in logic like this. However, we discussed this a while ago (in Prague) and using a 'proper' backend/backend chain right would be very difficult to implement, and possibly controversial.
Comment #104
damiankloip CreditAttribution: damiankloip commentedwe could just try something like this?
reset the container in post/postForm. Otherwise, we could just reset controllers on the entity manager. That seems a little special snowflake like though.
Comment #106
BerdirNote that #2225955: Improve the DX of writing entity storage classes will do #100 because static cache will then be supported for all entity storage implementations that use the default methods.
Comment #107
jibran104: 1885830-104.patch queued for re-testing.
Comment #109
BerdirRe-roll.
- Almost no changes are now necessary because most of the static cache handling is now in the base class, also moved the one for deletion.
- Removed the by default disabled static cache flag for config entities but I still think this makes sense to do, just want to see what the testbot has to say. There might be a few that can use it, but most don't because we don't load them that much and then it's just unnecessary memory being used.
- I also still think that we need to correctly check the cache key for a given object or we will have problems with overriden config.
Comment #111
effulgentsia CreditAttribution: effulgentsia commentedLooks like that issue is now fixed, but from what I can tell, subsequent patches here haven't incorporated the call to getCacheKey(). So, I did so in #2303881: Config entity static cache doesn't get reset and isn't override aware.
Comment #112
effulgentsia CreditAttribution: effulgentsia commentedIn #2303881-6: Config entity static cache doesn't get reset and isn't override aware, Berdir asked me:
So here's my thoughts:
Comment #113
Crell CreditAttribution: Crell commentedeffulgentsia: The double-caching seems unnecessary to me. If we cache the object but NOT the array, wouldn't that get us the CPU savings we're talking about without a significant increase in memory usage? (I'm assuming that the array takes up the bulk of the memory of the object, which seems a safe assumption.)
Comment #114
effulgentsia CreditAttribution: effulgentsia commentedYes, I think for config entities, caching the entities and not the underlying config array makes the most sense. But ConfigFactory will still need to cache the non-entity config arrays, so this would add some complexity in terms of ConfigFactory knowing (or being told) which config arrays are non-entities and which are entities (or alternatively, which individual config IDs or ID patterns to cache/not-cache). I'm sure that's a solveable problem: just in general, while it's ok for the ConfigEntity system to depend on the Config system, we'd rather not have the Config system knowing too much about the ConfigEntity system.
Comment #115
Crell CreditAttribution: Crell commentedAgreed entirely on the last point. Is there some way for ConfigEntity to tell Config, when loading data, "don't cache this one, I got it"? (In some generic way, of course.)
Comment #116
BerdirNo, at the moment, there is not, and I also don't think that would be a good idea, config entity queries work on the raw data AFAIK, we want those to rely on the static cache too.
The actual bugs related to config entity caching have been fixed in the related issue, it is being enabled for roles in #2202185: Statically cache Role entities to speed up (User|UserSession)::hasPermission().
In the test fails that we had here, we AFAIK had no real bugs (other than those that were fixed now), only the usual test static cache that had to be cleared after web requests.
The question to ask is IMHO what config entities we expect to load multiple times. And enable static caching for those. Some are only expected to be loaded once or a few times on normal pages, so it does't matter too much if a hook_entity_load() is slow or not.
Comment #117
yched CreditAttribution: yched commentedJust a note that if we start statically caching EntityViewDisplays, then EntityViewBuilder::viewField() will need to be modified to clone the EVD it loads and modifies...
Comment #118
BerdirNot sure yet, but profiling showed some some config entity types being loaded a lot, although entity displays actually are the worst and based on what @yched said, we might need to clone them anyway. Or maybe we can improve how they're loaded, seeing a lot of calls originating in views in my case.
Comment #120
yched CreditAttribution: yched commentedLike, we load the same entity display over and over in the same request ? Weird, I wouldn't have expected that. I wrote #117 "just in case", but I didn't think those would be the primary config entity that needs optim.
Comment #121
BerdirYeah, I think it's if you have views that render fields, then it creates it for every row. Not sure, will need to check again.
Comment #122
yched CreditAttribution: yched commented@Berdir: oh right. Can't find it atm but we have an issue open with a plan to make "by field" views less horribly inefficient. Been opened since Portland I think, but we didn't get to it yet :-/
Comment #123
BerdirYeah, and that is actually called by EntityViewController, so we do the collecting twice per request for a normal entity view :( And I hope that was breaking most of those tests, was just testing one of them.
Anyway, cloned directly in collect, because we have that alter there, so we need to do it anyway I guess?
Comment #125
yched CreditAttribution: yched commentedYeah, that $entity->title->view('full') in EnityViewController is weird anyway, we should be passing explicit hardcoded formatter settings there, and not rely on the configuration of the 'full' Display. @jhodgdon opened an issue for that, was stuck on in-place-edit tests breaking.
Anyway, yeah, cloning there makes sense I guess.
Comment #126
BerdirEntityStorageBase::delete() assumes that entities are keyed by ID, but we never enforce it. Automatically deleted field storage configs are not, for example.
Comment #127
BerdirComment #129
alexpottAdding a note due to discussion on #2406543: Remove ConfigFactory::setOverrideState and ConfigFactory::getOverrideState(). This issue will also need to prevent the config entities from being cached in the config factory. I think the best implementation will be a parameter on the get() and loadMultiple() methods on the ConfigFactory. However this is not simple as @Berdir points out:
Comment #138
catchThis is no longer relevant. Config objects are statically cached.
Comment #139
BerdirNo, they still default to not caching the entity objects statically? It's supported but opt-in. This is about enabling it by default, which I'm not sure we can do, but we could decide if we can explicitly update at least some core entity types?
There's also a core issue to treat entity static cache in tests like other static caches and reset them on POST requests, that removes the need for 95% of the manual cache resets in tests.