Closed (fixed)
Project:
Drupal core
Version:
8.0.x-dev
Component:
entity system
Priority:
Critical
Category:
Bug report
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
22 Sep 2014 at 09:03 UTC
Updated:
26 Dec 2014 at 16:24 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #1
dixon_The first idea that came to my mind was to simply handle the exception gracefully when creating the field fails because it already exists (e.g.
SchemaObjectExistsException). The exception is still logged as a warning just to make it more transparent what's going on. But this is obviously not a very pretty solution, so we probably should find a nicer way.Comment #3
catchComment #4
dixon_Couldn't figure out the circular container references when passing the logger. So I restored to using
\Drupal::logger()instead which generally seems accepted throughout core anyhow.Comment #5
dixon_Just talked to @effulgentsia and we discussed a more proper way to solve this. We would essentially let
SqlContentEntityStorageSchema::onEntityTypeCreate()be a wrapper around a new protected method, something likecreateEntityTypeSchema(), that's able to install the definition you give it.That way while creating the schema for the new storage handler, in
SqlContentEntityStorageSchema::onEntityTypeUpdate(), we can pass it the last installed definition, so that the fields that were added in the same process can be applied without problem.Comment #6
jeqq commentedPlain re-roll for #4.
Comment #7
xjmComment #8
dixon_I've been playing around with this implementation, and the problem here is that since the updates are being applied in two steps...
...there's no way for step 1 to know if any new fields are going to be added since that information isn't passed along through the event. So we simply don't have enough information to know if we need to base the updates on the last definition or not (in order for subsequent field updates to not collide).
Further, this approach fail in cases where there are other things like index updates that actually do need to be created as part of step 1.
So rather than passing the necessary information down through the event (which would mean additions and changes to the API) we can instead simply bubble up information needed to take the right decisions. This change requires a lot smaller/simpler changes to the API. Basically all we need to know is if the schema handler recreated the entity schema as part of the step 1 update. Because if the schema was recreated it means that any new field storage definitions already got created as part of this process. In this case we should not carry out the updates as part of
EntityDefinitionUpdateManager::applyUpdates(), because that would result in conflicts.Attached is a patch that demonstrates this approach. This patch implements only the minimum of what's required to solve the problem. For consistency we could perhaps make
onEntityTypeCreateandonEntityTypeDeletebubble up (e.g. return) it's results as well. But since those cases are a lot more straight forward I'm not sure that's really needed...Comment #9
dixon_This bug is blocking a stable release of the Multiversion contrib module.
Comment #10
dixon_Updated the patch with some docs, comment fixes and made
::onEntityTypeUpdate()a bit more consistent by always returning a status.Comment #11
fagotwo any
While the general architecture of the solution would be fine to me, the implementation is a bit problematic as the schema handler currently implements a listener concept. It's weird for a listener to provide additional information via return values. In particular if the return values stem from an "unrelated" different interface :)
Also, we've the current problem/glitch that we do not invoke onFieldStorageDefinitionCreate() when creating the entity type either - so I'm wondering whether shouldn't just always compile a complete $change_list and pass it own to all listeners. So listeners can decide whether to act based upon the complete change_list? That would allow us to account for any other change combination issues we might run into in future as well.
Another option would be to just override the onEntityUpdate() method in the EntityStorageSchemaInterface ?
Comment #12
fagoDiscussed with plach, yched and berdir on how we could solve that.
Came up with a possible way with plach: It's the same class that consumes both events, so we could statically keep the events processed before and then skip the field event once the entity event was there before. I could see this a bit troublesome only if on a single page load multiple changelists are compiled instead of a single one - what probably mostly happens in tests, but might happen due to custom update scripts also. To mitigate we'd have to provide changelist ids, set them on event objects and make sure listeners receive the events.
But then, plach came up with another way simpler solution: Just move around the around ordering and apply field updates first - so let's try that.
Comment #13
fagoComment #14
fagoTried re-ordering things and took over the test case - looks like it doesn't solve the problem, not sure why yet.
Comment #15
fagosilly me. this should work.
Comment #17
plachThis fix was originally introduced in #1916790: Convert translation metadata into regular entity fields. There I added also a comment:
Comment #18
fagoThanks. Refactored the comment a bit to take account of this issue and updated patch.
Comment #19
plachTypo, should be "create". Aside from that this looks good to me.
Comment #20
fagoThanks, fixed.
Comment #21
plachLet's get this in.
Comment #22
alexpottThis issue addresses a critical bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 4738109 and pushed to 8.0.x. Thanks!