Problem/Motivation
Nodes - as the canonical example of revisionable entities - store the revision author, the revision timestamp and a log message for each revision. The Node
entity declares these fields in its baseFieldDefinitions()
.
Custom blocks only provide a log, but no revision author or timestamp.
We should make this consistent and make it easy for contrib entity types to provide the same behavior.
#2666808: Add a revisionable entity base class and log interface introduced three revision metadata fields, which are named "revision_created, revision_user, revision_log_message". However the core still assumes at some places that they are called "revision_timestamp, revision_uid, revision_log". That is the case where the node entity and the block entity use the previous nomenclature. But unfortunately this is not the only place.. There is one another really important place and it is in SqlContentEntityStorage::getTableMapping where only the old nomenclature is used to put these revision metadata fields in the "entity_type_id_revision" table. #2666808: Add a revisionable entity base class and log interface did not extend the list with the new revision metadata fields and what now happens is that if you use the new RevisionLogEntityTrait your revision metadata fields will not be put - as they should - in the "entity_type_id_revision" table, but they will be put in the field table.
Proposed resolution
Provide a revision_metadata_keys
key in the entity annotation which provides a mapping of common identifiers like "log", "uid", "created" to the actual field names. Because there are places where we need the list of revision metadata fields explicitly, for example in SqlContentEntityStorage::getTableMapping()
or in ContentEntityBase::hasTranslationChanges()
with #2615016: ContentEntityBase::hasTranslationChanges should exclude the "changed" fields from the comparison, this is not part of the entity_keys
key, even though it is similar in nature. This also allows us to ignore (at least for the scope of this issue) the fact that entity key fields get a NOT NULL
constraint in the SQL schema. Note that calling EntityType::getKeys()
will also not return the revision metadata keys.
In order to provide backwards-compatibility, in the case that revision_metadata_keys
is not set, we will check the field definitions if fields with the appropriate names (revision_log
, revision_log_message
) exist and use those as as values. This behavior can be disabled by a boolean parameter which is utilized in RevisionLogEntityTrait
because due to the above checking of the field definitions, checking the revision metadata keys while building the field definitions would lead to infinite recursion.
Because the hardcoding of incorrect revision metadata field names in SqlContentEntityStorage::getTableMapping()
entity types using RevisionLogTrait
have the database columns for the revision metadata fields in the wrong table. (They are in the revision data table, but they should be in the revision table.) To correct this, an update function is provided that moves the field values to the correct table and fixed any views that reference the old table names in their views field configuration.
Remaining tasks
Follow-up issue for D9 to remove the hard coded revision metadata field list from \Drupal\Core\Entity\RevisionLogEntityTrait::getRevisionMetadataKey and RevisionLogEntityTrait should create the fields only if they are mentioned in the entity annotation.
Follow-up issue for D9 to remove the backwards compatibility parameter from \Drupal\Core\Entity\ContentEntityTypeInterface::getRevisionMetadataKeys and make the annotation for revision metadata keys obligatory for revisionable entities.
User interface changes
API changes
New functions :
-EntityTypeInterface::getRevisionMetadataKeys
-EntityTypeInterface::getRevisionMetadataKey
-EntityTypeInterface::hasRevisionMetadataKey
Change record
Comment | File | Size | Author |
---|---|---|---|
#267 | 2248983-267-followup.patch | 738 bytes | hchonov |
#260 | 2248983-followup.patch | 856 bytes | amateescu |
#257 | interdiff.txt | 1.21 KB | amateescu |
#257 | 2248983-257.patch | 216.71 KB | amateescu |
#255 | interdiff.txt | 1.14 KB | amateescu |
Comments
Comment #1
larowlanMakes sense
Comment #2
andypostSuppose this could be closed now.
@plach please confirm
Comment #3
andypostJust need to remove @todo
ContentEntityDatabaseStorage.php:273
Comment #4
tstoeckler@andypost: Can you elaborate why this can be closed? I think the points in the issue summary are still valid?
Comment #5
plachYep, I am not sure why this should be closed. What changed since we opened it?
Comment #6
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThis was done in #2666808: Add a revisionable entity base class and log interface.
Comment #7
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedActually, in that issue we just added the generic RevisionLogInterface. Maybe here we could change Node and Block Content to implement it?
Comment #8
Wim LeersComment #9
dawehner+1 for it, but NOT change the actual used base fields.
Comment #10
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI'm not sure what that means...
Also, I assume we should only do this in 8.2.x.
Comment #11
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedFor starters, we should get rid of that @todo.
And now I think I understand what @dawehner wanted to say: we shouldn't change the name of the revision metadata fields for nodes and custom blocks?
Comment #13
BerdirYes. Implement the interface but not the trait. Which is exactly why your code can't work (the trait is just a default implementation, we shouldn't really on that as an API).
To make that code more generic, we'd need entity keys or a similar approach (which have a problem that they then enforce all "keyed" fields to be required.
Comment #14
timmillwoodIn #2716081: BlockContent should have revision_user and revision_created fields and implement RevisionLogInterface I added
revision_created
andrevision_user
toBlockContent
, this does not useRevisionLogEntityTrait
, it's a simple copy & paste of the two missing fields, and implementingRevisionLogInterface
.In #2705389: Selected content entities should extend EditorialContentEntityBase or RevisionableContentEntityBase I am working to make (possibly) all content entities (apart from Node and BlockContent) extend
RevisionableContentEntityBase
this will force them to useRevisionLogEntityTrait
and get the three new fields.The concept of the patch in #11 looks good to me, although it needs an upgrade path and tests.
Comment #15
hchonovI wasn't aware of this issue and created #2732699: The SqlContentEntityStorage should be aware of the new revision metadata fields from RevisionLogEntityTrait, which however describes a problem for which we now need an update path:
Comment #16
hchonovComment #17
paranojik CreditAttribution: paranojik as a volunteer commentedI tried to implement the approach mentioned in #13. Not sure if correctly, but the idea is to allow for a smooth transition when implementing
RevisionableContentEntityBase
.Comment #18
paranojik CreditAttribution: paranojik as a volunteer commentedMoved revision fields to entity_keys as discussed on IRC with @hchonov...
Comment #21
hchonovIf we are not going to create the fields if the entity keys are not set then we do not need any fallback here or?
Comment #22
paranojik CreditAttribution: paranojik as a volunteer commentedTrying to address failing tests.
@hchonov: could be. Will try that...
Comment #24
hchonovComment #25
timmillwoodWe could do with this for #2725463: WI: Add StatusItem field type to store archived state, the current patch there just adds the new metadata base field to the list in SqlContentEntityStorage::getTableMapping.
Comment #26
paranojik CreditAttribution: paranojik as a volunteer commentedIMHO the approach from #17 would be a much cleaner solution and would also satisfy the case from #2725463: WI: Add StatusItem field type to store archived state.
Comment #27
paranojik CreditAttribution: paranojik as a volunteer commentedWent ahead and cleaned up #17 a little bit. Still need to write the update path...
Comment #28
timmillwoodWhat's the difference between
revision_fields
andentity_keys
?Comment #29
paranojik CreditAttribution: paranojik as a volunteer commentedrevision_fields
are revision metadata fields that are stored in the "revisions" table and are not required (which is a restriction on entity_keys).Comment #30
timmillwoodok, sounds fair!
Comment #32
paranojik CreditAttribution: paranojik as a volunteer commentedAddressing failing tests.
Comment #34
paranojik CreditAttribution: paranojik as a volunteer commented...my bad..
Comment #36
timmillwoodMany of the test failures are
UpdatePathTestBase
tests, and failing due to:I'm struggling to track down why this is failing because after a site install block_content_revision.revision_log exists before and after this patch.
Comment #38
hchonovThat is because the update path is needed. Now revision metadata fields will not be in the data and field_revision tables, but in the revision table for the revision metadata. So we need an update function, which is taking care of updating the field storage definitions, removing the revision metadata fields from the data and field_revision tables and putting them into the revision metadata table.
However there is one problem - we should still assume default revision metadata field names, as when the update function runs, the developer might still haven't updated the entity annotation with the revision metadata fields.
P.S. we have to this only for revision metadata fields using the new name nomenclature in RevisionLogEntityTrait::revisionLogBaseFieldDefinitions, because those fields are now in wrong tables.
Comment #39
hchonovUnfortunately the entity definition update manager will not be able to update the definition itself, as the old code says the field is in table A and the new code says the field is in table B... So a manual update is needed.
Comment #40
hchonovHere is the update function, which is moving the new revision metadata fields from the data tables to the revision table for revisionable and translatable entities.
Comment #42
hchonovI've added backward comparability in case the revision fields are not contained in the entity annotation. I am afraid, but we are going to need this hard coded until D9, where we could remove it and throw an exception if a revisionable entity type does not define the revision fields, but this will be a follow-up issue.
Comment #43
hchonovClearing some nit picks to match the interface definition.
Comment #46
hchonovI've fixed the failures in SqlContentEntityStorageTest.
Comment #47
timmillwoodStoring these in an array seems a bit odd, can't we fetch them from somewhere?
Comment #48
hchonovUnfortunately not, they are hard coded over the entities. The list has been hard coded previously and it should remain so until D9 where we can introduce an exception if the entity is revisionable, but the revision fields are not defined in the entity annotation.
Comment #49
hchonovShould this issue remain a task? It is also needed for fixing an existing bug.
Comment #50
hchonovComment #51
dawehnerI'm kinda confused why we not provide setters as every other field.
Comment #52
hchonov@dawehner I am not really sure what you mean exactly? This here are getters for properties from the entity annotation. A setter is not needed here. They are exactly like the EntityTypeInterface::getKey function to retrieve the field name of an entity key.
Comment #53
tstoecklerI had a larger review written, but somehow lost that...
I see that this was tried above, but I actually think the revision fields should move back to
entity_keys
. That is exactly what that is being used for. I realize there might be some fallout with the schema generation or other places but I think we should rather fix or workaround that instead of introducing a whole new key for something very similar.Comment #54
hchonov@tstoeckler this is fine for me, however I am not sure about something - do we want the revision metadata fields on the first level in the entity keys or on a second level wrapped behind the revision key? I mean
entity_keys[revision_log] = rev_log_message
versus
entity_keys[revision_metadata] = [revision_log => rev_log_message]
The second way gives us the possibility to retrieve all the revision metadata fields at once without having the storage knowing that there is revision log, revision user, revision timestamp ...
Comment #55
paranojik CreditAttribution: paranojik as a volunteer commented@tstoeckler I've already tried that, but as @berdir said in #13 all keyed fields are automatically required which does not really work for revision metadata fields. Working around that will be another hack... why would you want to do that?
Comment #56
hchonovHm, I've just added the following to the node entity keys
"revision_log" = "revision_log"
cleared the cache and saved a node entity without any revision log message and it landed in the database as NULL and not an empty string, so it means it was not flagged as required.
So I am not aware of any enforcement that all the entity keys have to be required.
Comment #57
hchonovI am sorry, my fault, after I've updated the field definitions the field is really required...
Comment #58
tstoecklerWe discussed this in person with @Berdir, @alexpott, @hchonov and I. Will update the issue summary with the details. #53 is moot, but "needs work" still applies.
Comment #59
tstoecklerOK, updated the issue summary.
Comment #60
tstoecklerComment #61
hchonovComment #62
hchonovComment #64
hchonovOups.. I've forgot to pass the entity type to the call of Entity::baseFieldDefinitions.
Comment #66
tstoecklerEdit: x-post with #61
I really like the approach, now that we've talked about it. I think it makes a lot of sense. Here's a review, most of it is nitpicks though. The upgrade path will be problematic, however, see below.
1. entity metadata fields -> entity metadata keys (I know the terminology is very unintuitive and confusing, but I think at the very least it's good to be consistent)
2. I realize that
EntityType::$entity_keys
is not being a good example here, but I think we could make the documentation a bit more helpful. Maybe add a second sentence like "The keys of the array are the 'revision_log', 'revision_uid', and 'revision_created' and the values are the field names of the respective fields for this entity type." It would just be nice to have docs on what's actually in this.Edit: I know see that you added some great docs on
EntityType::getRevisionMetadataKeys()
, so that's fine I guess.I woud say "Add a backwards compatibility" -> "Provide backwards compatibility"
Minor, but the
empty()
is not neededThis should be
\Drupal::service('entity_field.manager')->getBaseFieldDefinitions($this->id())
. (I don't think we can inject anything properly here.)I think we should be consistent with using
empty()
andisset()
(I would personally preferisset()
).property -> field (also elsewhere)
Minor, but it seems strange to use NULL for an array structure, what about an empty array?
I'm not sure about this myself, but I think the
revision_
prefix inside of the keys is a bit repetitive, what do you think about removing this? (I.e. just have the inner keys beuser
,created
, ...?)In theory this should be using the EntityFieldManager. I'm not sure whether that's a good idea in an update hook, but it also seems wrong to ignore fields declared in a hook here. Hmm...
Awesome that you provided an upgrade path. Unfortunately:
- We need to run the actual upgrade in a batch.
- This will need upgrade path tests.
Comment #67
hchonov@tstoeckler I've addressed everything beside the names of the revision metadata keys. Somehow I prefer having the revision_ prefix explicitly there.
Comment #68
hchonovForgot to finish the batch properly...
Comment #69
hchonovI wasn't initializing properly the revision metadata keys.
Comment #72
BerdirI think all of this needs to be in ContentEntityType, we only have fields there, so at leat the BC logic woudln't work for other entity types.
I think the property and method belongs in ContentEntityType. only those have revisions and those fields.
false or null?
Comment #73
hchonov@berdir, yes of course you are right the functions and the property have to be in the ContentEntityType.
About the return value of getRevisionMetadataKey, we made it consistent with getKey, which also returns FALSE.
Comment #74
hchonovSo we've just talked with tstoeckler and we need an update path test and additionally he mentioned that we have to update views as well, which are already configured for the database scheme we are changing, which dawehner confirmed.
Comment #75
hchonovSo here we go, I've added an update for views using revision metadata fields and an update path test.
Additionally we've decided that we have to extend the ContentEntityTypeInterface, but this is an API break, so now I am introducing a new interface ContentEntityTypeRevisionMetadataInterface, which ContentEntityType is implementing.
In D9 we could merge ContentEntityTypeRevisionMetadataInterface into ContentEntityTypeInterface.
As we require a database dump since the new revision metadata fields have been introduced, I've created a bare dump for Drupal 8.2.1 with the standard profile and additionally the entity_test module enabled.
Comment #77
hchonovIt should be better now.
Comment #80
hchonovChanges since #77 :
1. The new interface ContentEntityTypeRevisionMetadataInterface now extends the ContentEntityTypeInterface and the class ContentEntityType implements the ContentEntityTypeRevisionMetadataInterface instead of the ContentEntityTypeInterface.
2. SqlContentEntityStorage::getTableMapping checks if the entity type implements the ContentEntityTypeRevisionMetadataInterface and if so retrieves the revision metadata fields through the method ContentEntityTypeRevisionMetadataInterface::getRevisionMetadataKeys, otherwise if the entity type does not implement the ContentEntityTypeRevisionMetadataInterface we still have to leave the hard coded list of revision metadata fields.
Comment #81
hchonovI've added backward compatibility also to the update hook if the entity type does not implement the new ContentEntityTypeRevisionMetadataInterface, which I've forgot to do in the previous patch.
Comment #82
timmillwoodwhy does this issue not update RevisionLogEntityTrait with the revision_metadata_keys?
I want to try and get this issue pushed over the line so we can switch Node and BlockContent to extend RevisionableContentEntityBase.
Comment #83
hchonov@timmillwood: because of the current implementation of ContentEntityType::getRevisionMetadataKeys, which as a backward compatibility will retrieve the base field definitions and so the function could not be used in RevisionLogEntityTrait::revisionLogBaseFieldDefinitions until D9 where we could make the revision metadata fields in the annotation obligatory.
Comment #84
timmillwood@hchonov - Could we not get a revision_metadata_key, and if it's not set use a hardcoded value? This would at least allow us to use the trait in node and block_content which we currently can't do.
Comment #85
hchonov@timmillwood - no, because the function calls the RevisionLogEntityTrait::revisionLogBaseFieldDefinitions, it is just not possible from within RevisionLogEntityTrait::revisionLogBaseFieldDefinitions to retrieve the revision metadata keys.
Comment #86
amateescu CreditAttribution: amateescu for Pfizer, Inc. commented@hchonov, it's definitely possible,
RevisionLogEntityTrait::revisionLogBaseFieldDefinitions()
receives the calling$entity_type
definition as an argument ;)Comment #87
hchonovNo, it is not possible to call ContentEntityType::getRevisionMetadataKyes from within RevisionLogEntityTrait::revisionLogBaseFieldDefinitions, because currently ContentEntityType::getRevisionMetadataKyes will call itself RevisionLogEntityTrait::revisionLogBaseFieldDefinitions because of backward compatibility.
Comment #88
timmillwoodThen I think the code needs refactoring.
Comment #89
timmillwoodI don't think we should provide this backwards compatibility magic. For example this causes
hasRevisionMetadataKey($key)
to return true, when there isn't actually a key set.Comment #90
hchonovWe sure need the bc otherwise the people update and they get exceptions, which we could not afford.
And I do not get it why would hasRevisionMetadataKey return true when the key does not exist? You are welcome to provide a test as a prove.
This BC has been discussed and approved between me, tstoeckler and berdir at the DrupalCon Dublin 2016.
Comment #91
timmillwood@hchonov - Well the key kinda does exist, because getRevisionMetadataKeys() dynamically generates it, but it doesn't exist in the annotation. I think it would be better in the trait to do
$revision_created_key = $this->getRevisionMetadataKey('revision_created') ?: 'revision_created';
and similar for each of the base fields.This would work for BC if there are any entity types in contrib already using the trait, but not setting revision_metadata_keys. It will also work for node and block_content which have different keys than in the trait, as we can specifically define the keys in the entity type then use the trait.
I'll try to roll a patch today.
Comment #92
timmillwoodThe attached patch now makes use of the revision_metadata_keys in RevisionLogEntityTrait.
Comment #93
hchonovYour change works for the storage only because of the BC added there in case the entity type does not implement the new ContentEntityTypeRevisionMedataInterface:
However now you have achieved that hasRevisionMetadataKey will return FALSE if the revision metadata key is not provided through the entity annotation but is coming from the trait, because now we do not check the base fields by removing the BC:
Comment #94
timmillwoodI would hope
hasRevisionMetadataKey()
returns FALSE if there is not revision_metadata_key set.We have updated Node and BlockContent, so the only BC we need to cover is contrib entity types who are defining a 'revision_timestamp', 'revision_uid', 'revision_log', 'revision_created', 'revision_user', or 'revision_log_message' field. As you say, we are doing this in SqlContentEntityStorage.
The biggest benefit of the patch in #92 is that we can now use RevisionLogEntityTrait for Node and BlockContent, which also means they can extend RevisionableContentEntityBase.
Also, when we introduce EntityPublishedInterface there is talk about creating a new base class which uses EntityPublishedTrait and extends RevisionableContentEntityBase. Again we wouldn't be able to use this for Node or BlockContent.
Ultimately I find it crazy that we have a revisionable base class which neither of the core revisionable entity types can use. This is the issue to unblock that!
Comment #95
hchonovBut the revision metadata key is there, it is not just set by the developers in the annotation and if we do not cover this case until the annotation for the revision metadata keys is made obligatory it makes the whole idea of revision metadata keys currently unnecessary, as would not be able to do stuff with these keys if we cannot retrieve them.
Comment #96
timmillwoodThe ultimate issue is, we can't use it with Node and BlockContent.
Comment #97
hchonovThe problem this issue is solving is that the revision metadata fields are not in the correct tables. For Node and BlockContent we have to find another way.
Comment #98
timmillwoodbut if this issue gets in there will never be a good way to fix Node and BlockContent. So it makes sense just to fix this.
Comment #99
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThe original scope of this issue also included updating Node and BlockContent to use the trait (see #7 and #9), so I really think that we should find a good way for them to be able to use
RevisionLogTrait
in this issue.If that's the only problem with the patch in #92, then I propose to make the revision metadata keys required in 8.3.x if your entity type implements the newly added
\Drupal\Core\Entity\ContentEntityTypeRevisionMetadataInterface
.This way, when people upgrade to 8.3.0 they will get an update error message with a clear explanation of what they need to do in order to complete the update successfully.
Another good thing about this approach is that we would no longer need the BC code from
\Drupal\Core\Entity\Sql\SqlContentEntityStorage::getTableMapping()
as we will be able to rely on the existence of the revision metadata keys.What do you think, does this solve your concerns?
Comment #101
BerdirAs mentioned in IRC, I think having a BC for existing entity types that use the trait would be good to have.
That said, we IMHO do not need BC for a method that didn't exist before. We just need a BC for existing code that used to hardcode this information continuous to work.
Here we actually have a BC already. I'm not sure if need the new optional interface here, Our current rules afaik allow adding new methods if we can provide a default implementation in a base class that we can expect everyone to use (the chance of someone providing a custom entity type class is pretty much 0.. not sure if it is even possible), which is the case here.
So this would already be enough BC.
But I do agree that the if we switch the trait to the new method, then we should have a BC there.
And there are multiple variants for that:
a) Inline it: $entity_type->getRevisionMetadataKey('revision_user') ? 'revision_user'
b) Add a separate method to get it without BC, deprecate it, clarifying that in 9.x, the other one will return it.
c) Add a separate method, only do the BC there, deprecate as well.
d) Add an argument to skip BC.
IMHO, a) would be enough, unless we expect that this will be a common case that developers will need an API for.. which I doubt. If you have an entity, you can and should use the methods provided by \Drupal\Core\Entity\RevisionLogInterface, you only need the metadata for an entity type when you have no entity object or when writing/changing those values.. which is not something that a lot of contrib/custom code will need to do in a generic way.
Comment #102
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedHere's a patch that adds a "bc" parameter to
getRevisionMetadataKeys()
and removes the new interface. This is option d) from #101.The interdiff is to #81.
Comment #103
Berdirnot sure if it should be TRUE or FALSE by default.
Since we have it, it probably should be TRUE by default. It's actually not accessible for (get|has)RevisionMetadataKey(), so if you want BC, you'd have to use that always.
We should probably also explicit document that the argument will be removed in 9.x and it will then behave as if FALSE was passed in?
Comment #104
hchonovI agree with berdir and I also think the parameter should be TRUE by default and its removal in 9.x has to be documented as well.
Comment #105
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedMy initial thoughts when writing the patch in #102 were that the new API should be like we want it to behave in 9.0.x, but after sleeping on it I realized that the default value of the bc parameter doesn't matter since in 9.0.x the method will only look at what it's in the annotation.
Changed it to TRUE if that's what needed to get this RTBC :)
Comment #106
BerdirNitpick: You could simply add defaults to the $revision_metadata_keys with += ['revision_created' => 'revision_created', ... then you can just use the key and don't need the conditions.
Comment #107
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThat's true, and we also need to generalize this for the rest of the methods in the trait.
Comment #108
timmillwoodWe've come to a good conclusion here.
As we start to make more things revisionable, and potentially get more revisionable metadata fields this patch makes perfect sense.
@Berdir, @tstoeckler, etc any final thoughts? We don't want to rush this in, but after 2.5 years and over 100 patches it looks good.
Comment #109
BerdirWorks for me.
Comment #110
catchThe views should be done in a post update hook separately, and in views.install?
Also doing the config changes here won't catch default views - shouldn't this be done in preSave() or a config listener so it covers both? Then the post update just has to load and save with no manipulation of the view.
Didn't review the rest of the patch, just this has come up with nearly every config update so far so figured check it early.
Comment #111
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI started to write a config listener for this but I found out that
ConfigEvents::SAVE
is fired after the configuration object is saved, so we can not change anything from the config object unless we save it again.I think we need a pre-save config event in order to do this properly..
Comment #112
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedFixed #110 by moving the table fix code to
\Drupal\views\Entity\View::preSave()
.Comment #114
hchonovIf we do it like this then I would check that the post update function is not yet executed and then have the logic in View::preSave otherwise just skip it.
Comment #116
hchonovAnd also in which Drupal version should we mark this code for removal? If this gets into Drupal 8.3.x could we expect that everybody updates from 8.2.x to 8.3.x or we have to support the case that people might update from 8.2.x to 8.3+.x? This would mean we have to carry the code in View::preSave until Drupal 9?
Comment #119
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI don't think we ever check in runtime code if an update function was executed or not, we always assume they are executed as soon as the code base is updated.
We have to support upgrading to any future 8.x.y version until 9.x, so yes, we have to keep this code until then.
Comment #121
hchonovSo after the update is executed this additional und unnecessary code will always run each time a view entity is saved?
Comment #122
timmillwoodI can't seem to replicate any of the test fails in #112.
Comment #124
timmillwoodSo it passes with php7.
Comment #125
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedYes. Consider this scenario:
1. you have a custom module written that provides a default view config entity, that's currently uninstalled so the view is not in "active" configuration
2. you update core and the post update function runs, all your active views are changed accordingly
3. ... time passes ...
4. you enable your custom module, and the default view is installed. however, the view will be broken because it will look for the metadata fields in the wrong table, since it wasn't updated in step 2.
So the only way for us to be sure that you're not dealing with a broken view is if we execute that BC code on every view presave :)
Comment #127
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedAll the random test fails are because of #2828559: UpdatePathTestBase tests randomly failing, but I'm going to keep on re-testing until we get a green patch for PHP 5.5 as well :/
Comment #130
catch#125 is exactly why the logic needs to be in preSave() and not the update, and why it has to exist until 9.x
Comment #131
hchonovCould we get someone to review the updating of views as I am not an expert there and I've wrote the update code just by looking at couple of views configs but probably I've overlooked something?
Comment #136
timmillwoodRe-roll of #112 due to system.install not applying.
Comment #140
timmillwoodIn #1812202: Add UUID support for entity revisions we are looking to add a new revision metadata field. Therefore it'd be great to get this issue in first. It's still weirdly failing.
Comment #141
tstoecklerWe usually do fully-qualified class references in inline variable typehints (i.e. with the full namespace). That way they will also be marked as unused in PhpStorm, which is good, as they are actually unused and phpcs will complain with the current patch.
Minor, but I guess we could do the assertion on the base table always, even for translatable entity types. I think that would make the code just a tad clearer.
I think it would be awesome to actually execute the view and check that we have some results. The current assertions are good, though, let's leave those in place as well.
First of all: Thank you for adding such detailed documentation. Especially in these kind of tricky issues, that is so, so helpful and not many people take the time to write this up so nicely. Yay!
However: This comment is actually parsed for displaying a description when running update.php and such multi-line comments look pretty strange in that case. Therefore, it's better to move the second part to an inline comment at the top of the function.
I think it would be better to avoid a static here. Can we not just fetch the revisions outside of the foreach?
Let's add a use statement at the top and then just use the classname here.
As this table fallback is always cause of much fun and/or headache, can we at least add a
@see
to\Drupal\Core\Entity\Sql\SqlContentEntityStorage::initTableLayout()
?Again, thank you for this comment block, that is incredibly helpful.
But you know me, I also need to complain ;-): Missing comma after "table". I'm usually not that picky ;-), but here it's actually a bit hard to read currently.
Can we have a period (.) and a new sentence after "table" here? My brain currently does not have enough RAM to parse the entire sentence.
I don't see the case where non-translatable entity types are being handled. What am I missing?
Edit: Now I understand that in the non-translatable case actually no data needs to be migrated. That's awesome, but this could actually use a comment, because it's not clear in my opinion when just looking at this function.
So we're actually performing a whole batch per entity-type and then resetting the batch after each entity type. I've never seen that before and it does make me slightly uneasy. Won't the progress bar in the UI go back to being empty after having been complete when switching from one entity type to another?
I think it would be better to instead have the following structure in
$sandbox
:Then we could avoid the unsetting the sandbox information.
So above we actually checked whether the fields somehow ended up the data table but it seems this is not handled here. So I guess we can drop that check from above, but not sure...
Also (minor): I think
$fetch_from_table
is a somewhat misleading variable name, it sounds like a boolean. Why not just$table
?Ahh, so I guess my earlier comment about the
$fetch_from_comment
line was invalid. I guess there is data in both the data table and the revision data table, but it is sufficient to copy the data from the revision data table as that is a superset of the data in the data table? If so, would be nice to add a comment to the$fetch_from_table
line above.Great update code for the views. Very defensive and safe which is exactly how it should be.
One question: This does not actually affect views on non-translatable (but revisionable) entity types, correct? Because in that case it seems the views field already (correctly) specifies the revision table.
You don't necessarily need to update this, I'm just asking to make sure I understand this correctly. (If you do add a check for translatability, please add a comment to this effect.)
Comment #143
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedGreat review! :)
1: Fixed.
2: Personally, I think it's correct to test both cases because it properly tests the logic in
system_update_8300()
which drops the fields from different tables if the entity types is translatable or not.3: Not done yet, coming in a folloup patch.
4: 5: 6: 7: 8: 9: 10: Fixed.
11: I'm not sure that nesting stuff under
$sandox['progress']
will work, wouldn't that disable the progress bar completely?12: 13: Yeah, we don't need to check which table to get the data from. We already check above that we're dealing with a translatable entity type, so the data is always in the revision data table. Fixed by removing the
$fetch_from_table
assignment.14: That's right, we don't need to do anything for non-translatable entity types. Added a check and a comment :)
Comment #144
tstoecklerThanks for the update, looks great!
Re 11: Yes, messing with
$sandbox['progress']
is not good, but AFAIKis not part of the API and we can totally nest that. Also, if other people are fine with the current batch implementation, we can leave it, it just makes me personally a bit uneasy, but Batch API is weird and confusing either way, so.... :-)
There should be a newline after the namespace. Also the use statements should now be unused.
I guess can be fixed on commit.
I want to read through the issue again, though, before marking RTBC, so not doing that just yet. Of course feel free to beat me to it.
Comment #146
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI have a feeling that all those fails are not really random, but they are caused by the patch somehow. So let's try to see how a patch without any test fares. This is simply #143 without the added test coverage.
Comment #148
tstoecklerI think this deserves a comment:
This is quite an intricate detail so marking needs work for that. Also updating the issue summary to make note of the boolean flag.
Also just a reminder that this still needs a change record.
Comment #149
hchonovI have the feeling that probably the problem is that we update the views in View::preSave.. I know it is not correct doing this in hook_update_n, but I just wanna see what happens. So please ignore this patch.
Comment #150
hchonovoups..
Comment #151
hchonovHmm this is strange, somehow the testbot does not like the name of the patch or why is it skipping it? Last try with a simpler name.
Comment #153
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI also tried out some things in #2126447-92: Patch testing (and #91) and View::preSave() is the problem indeed. However, I am away until Tuesday and I won't be able to dig deeper until then.
Comment #154
hchonovHmm would it change anything if we trust the data when saving the views? Probably we have to do this no matter what, right?
Making only this change against #143.
Comment #156
hchonovOups got the patch wrong..
Comment #158
hchonovSmall changes against #143 and also found a bug in View::preSave where we haven't checked correctly if the field name is part of the revision metadata fields, which means that the testing for the correctly updated views wasn't working as the test passed with PHP 7. I've changed that and now instead of loading the config I am directly loading the view entity and through it its displays for checking.
I've addressed #144 about the new line after the namespace and #148 about the comment.
Let's see what happens now.
Comment #159
tstoecklerThis seems strange. Can you explain why this is necessary?
Comment #161
hchonovI am not sure that we really need this, I was just thinking that because we are re-saving views and change them with a trusted data we probably have to do this.
Comment #162
Berdir->trustData() shouldn't be required in post update functions but it also shouldn't hurt if you know that your data structure has the correct types (e.g, integers are actually integers and not strings and so on)
It is important in update functions because your change there might not actually match the schema of the current schema definition, so you explicitly want to avoid that it is being checked. but since a post update should only make content and not structural changes, it *should* be optional ;)
Comment #163
tstoecklerOK, then let's remove it, especially because you added it to a pre-existing post update function.
Comment #164
timmillwoodWhat's the difference between #143 and #151? Can we get an interdiff?
Comment #165
hchonov@tstoeckler: Oh yes.. I wanted to add it to the new post update function also to test if the update failure here has something to do with the trusted data or not...
@timmillwood: I was testing with updating view configs directly in hook_update_n instead of triggering resave in hook_post_update and having the update logic in View::preSave. As @amateescu has found independently this is where the patch fails.
Comment #166
hchonovSo I just wanna make sure the problem is not caused by not trusting the data.
Comment #167
hchonovAnd a patch without trusting the data on saving the view entities.
Comment #170
hchonovI have the feeling it fails because of some timeouts with PHP 5.5. I've added a batch to the post update function.
Comment #171
hchonovOups I've forgot to remove a line of the copy paste for the sandbox :).
Comment #173
hchonovSo the problem are not timeouts and the batch is not needed but I think I've found out the problem with PHP 5.5...
Comment #176
hchonovFixed the failing test where views of revisionable non-translatable entities weren't updated.
P.S. and also instead of using foreach by reference in View::preSave I try now directly modifying the values.
Comment #177
hchonovSo the problem with PHP 5.5 was the foreach by reference and altering the values ... Oh..
So after we have a passing patch now I've decided to move the logic from View::preSave into a private function which I've marked as deprecated, this should make it easy to remove and not forget in Drupal 9.
Comment #178
tstoecklerThe patch looks good to me. The only thing that sticks out to me is unsetting of the batch progress stuff, I would like someone else to confirm that that is OK before marking RTBC. Also this needs a change record anyway before RTBC and it's tagged "Needs documentation", not exactly sure why.
Comment #179
hchonovI've reimplemented the batch which should now be progressing pro entity type instead pro revision of an entity type. I hope this looks better now?
Comment #181
hchonovRe-roll only as hook_update_n has been added to system.install.
Comment #182
hchonovCreated a change record - https://www.drupal.org/node/2831499.
Feel free to publish it as soon as this gets committed.
Comment #183
hchonovComment #184
hchonovComment #186
hchonovI had couple of errors in the batch processing.. I hope that now it is better.
Comment #189
tstoecklerMaybe we could pre-filter the definitions so we don't "process" a bunch of unaffected entity types.
As this is now inside the ->isTranslatable() branch the check is no longer necessary here.
Also $sandbox['#finished'] has special meaning in Batch API, while $sandbox[$entity_type_id]['#finished'] does not - so we could just as well use $sandbox[$entity_type_id]['finished']. I can see how we want to make it consistent, though, so I'm with the current way, just wanted to point it out.
Can we change this to "This mist be the first run for this entity type."?
Hmm... I guess since this is really just the integer this may be fine, but if you have a huge number of revisions (think Drupal.org) this will still eat up a lot of memory. What is sometimes done to prevent this is to add a deterministic sort to the query (i.e. revision ID) and then just count the number of processed revisions and then use that as an offset in the query.
Maybe others have more experience with this on big sites.
Since you're already recording the number of revisions processed (vs. the actual revision ID(s)) this may actually be easy to change?
This should be "updates_*per*_iteration" in English ;-)
Also should have noticed this earlier, but won't this mean that the field storage definition will be installed multiple times? Won't that be problematic?
Can't this be moved in front of the ->isTranslatable() check - as it's also in the else branch?
Comment #190
hchonovI've addressed #189.
Comment #192
hchonovRe-roll only.
Comment #193
tstoecklerYay, this looks great! Only some minor nitpicks left.
This was marked "Needs documentation" in #30 not sure why. This now has a change record, thank you, that looks good as well! Maybe @timmillwood can clarify what documentation specifically needs to be updated?
Double assignment of $base_fields
"mist" -> "must" (Sorry, I realize you copied this from my comment)
The else should be on a newline.
Comment #194
timmillwoodNot sure why I tagged it as needs documentation. I think the change record is sufficient.
Comment #195
hchonovAddressing #193.
Comment #196
tstoecklerAwesome, thank you so much for your persistence!
Comment #197
timmillwoodTagging this for tracking.
(+1 to RTBC)
Comment #198
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedRerolled after #2789315: Create EntityPublishedInterface and use for Node and Comment.
Comment #199
catchIn each of these three branches should we do a @trigger_error('...', E_USER_DEPRECATED)?
git blame is plenty no? Or should we put the issue nid in the test class name - have done that elsewhere?
This update makes this a major bugfix rather than a task. I'm assuming there's absolutely no way to separate the new feature out from the bug fix?
This is missing an accessCheck(FALSE) which means it would skip inaccessible revisions not available to the anon user with an access module enabled. If this affected nodes, that'd be a really nasty upgrade path bug, but it's still a theoretical issue with other entity types.
Same with accessCheck(FALSE) here.
Comment #200
timmillwoodUpdating all items from #199.
#199.2 I think git blame should be good enough.
#199.3 I don't think it's possible to split.
Comment #201
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThe interdiff from #200 looks ok to me :)
Comment #204
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThis is the fix for
Drupal\Tests\views\Unit\EntityViewsDataTest
.Drupal\system\Tests\Entity\Update\MoveRevisionMetadataFieldsUpdateTest
was broken by #2669802-119: Add a content entity form which allows to set revisions and I posted a small followup patch for that here: #2835588: Restore EntityTestRev's behavior to not implement RevisionLogInterface. This patch should be green again when the followup is committed.Comment #206
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedOk, I took @Berdir's suggestion from #2835588-8: Restore EntityTestRev's behavior to not implement RevisionLogInterface and moved
EntityTestWithRevisionLog
andEntityTestMulWithRevisionLog
to a separate module and recreated the test db dump using that module.Regarding #2835588-9: Restore EntityTestRev's behavior to not implement RevisionLogInterface, there's just one test that uses the pre-existing
EntityTestWithRevisionLog
class, so I think it's ok to not duplicate those two entity types.Comment #207
hchonovI am not really sure that it is ok to do this as probably contrib and custom is using it for tests as well..
How can we ensure that those entities even in a different module would not be modified as if they get modified the dump would not be up to date anymore?
Comment #208
BerdirWe don't provide BC for test entities, and those are pretty special and not used as often as entity_test, entity_test_rev and so on I think. So moving them is probably OK.
To avoid changes, we could maybe clearly name the module as entity_test_upgrade_test or something. But if we do make changes, also have the option to add more update functions to actually make it pass again.
Comment #209
hchonov#199.2
Actually it is possible to separate the bug fix from the new feature and for this to happen the bug fix should first introduce the new known revision metadata fields to the hard coded lists and have the update function a little bit adjusted and then let the new feature come in. However I am against separating it as the new feature is part of the solution of the bug and a beautiful one plus it adds the possibility with the change record for the developers to add to the entity annotation the revision metadata keys before the update is run in order to cover field names used for the revision metadata fields which are not the default ones used in the core and by doing this the update function will place them in the correct tables as well.
#206
Ok the moving of the entity definition to a separate module is fine, but why do we have to remove parts of the entity annotation? I think that they are removed because they are not really needed but this is kinda out of scope of this issue, right?
Beside that it look good now :)
Comment #210
tstoecklerYeah, I agree there is no way to fix this without the feature. The problem is that different entity types use different field names while the storage hardcodes them. The solution is to make them properly configurable and have the storage utilize that configuration (not in the CMI way, in the general way). That's precisely what the patch does.
Regarding the tests, on the one hand I can see that being considered out of scope, on the other hand we do treat dead code as a bug, so not removing that part of the annotation would increase the code debt for the future, so I think the removal is fine for now. Of course, the committers may disagree, but it would be really nice to get this is in soon, as this is a blocker for so many things, so setting back to RTBC.
Comment #211
alexpottIs the BC layer tested? I couldn't spot an obvious test.
We so need to add the php unit symfony bridge thing to test the errors are being triggered.
I think we should add an @deprecated tag here so we know that we need to clean up the $includes_backwards_compatility_field_names before 9.0.0. I'm not sure we've worked out the best way of deprecating params
Good to see this here - so we know to remove it.
Comment #212
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedRe #211:
1. It is tested in
\Drupal\Tests\Core\Entity\Sql\SqlContentEntityStorageTest::testGetTableMappingRevisionableWithFields()
2. Yup :)
3. The parameter is marked as deprecated in the method's doc block:
I didn't add the '@' symbol before deprecated because that makes PHPStorm mark the entire method as deprecated..
4. +1 :)
Comment #213
catchI think it's fine to just have the word 'deprecated', while it won't warn in IDEs, the @trigger_error() will eventually warn developers, and it's greppable for 9.x removal.
Comment #215
tstoecklerComment #217
tstoecklerComment #219
tstoecklerTest failure seemed legit unfortunately (https://www.drupal.org/pift-ci-job/551861), but nevertheless sending for a retest just to make sure.
Comment #220
amateescu CreditAttribution: amateescu for Pfizer, Inc. commented#204 does have a legit failure indeed, but the good/RTBC one is #206 :)
Comment #221
tstoecklerOh, you're right. Sorry, the testbot confused me...
Comment #222
michaellenahan CreditAttribution: michaellenahan at erdfisch commentedSmall typo fix
Comment #223
catchShould this be settable in $settings - some sites might want to do a bigger size.
Shouldn't this have an explicit order by?
Comment #224
timmillwood#223.1 - Not sure how much a $setting will get used? but wondering if the default value should be higher than 50.
#223.2 - I guess an order by makes sense, just so each iteration is processed the same.
Comment #225
hchonovA bigger step size might be useful, but even a small one will do the job. We could increase it, but I don't think we should offer a setting for the step size. I couldn't imagine that someone is going to use it and if we introduce it it should be only for this update.
Why would an "order by" be needed? I don't think that it matters in which order the revisions are processed.
Comment #226
alexpottWhy not just checking if $sandbox['current'] is set? progress is never used apart from here.
This looks brittle. If something unexpected created a revision or maybe more likely deleted a revision whilst the update was happening this potentially could break. I think all we need to ensure is that we finish also if $revisions is empty.
Comment #227
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedHere's an update for #223.2 and #226.
I quite liked the idea to have a generic setting for how many entities to update in a batch run and we could've used it in #2721313: Upgrade path between revisionable / non-revisionable entities as well, but it seems I'm the only one who liked it so I didn't include it in the patch.
Comment #228
timmillwoodThe interdiff in #227 looks good so moving back to RTBC.
Comment #229
catch@amateescu, I liked it ;) see #118 on that issue.
Comment #230
hchonovSuch a feature would be out of scope, right? I think that it might be useful, but it then deserves its own issue.
Comment #231
catch@hchonov I don't really think it's out of scope, we're adding updates that could run on hundreds of thousands/millions of records, which will be non-optional once sites need to update to 8.3.0+. Until there's an option of 8-8 migration paths for large sites, we need to make sure updates are going to actually run successfully for them.
Having said that @alexpott's comment on batch running multiple times if a second hasn't elapsed yet is valid - main issue is whether the initial query itself is a bottleneck since increasing the batch size is an easy way to run less queries.
Comment #232
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedHere's the interdiff for a variable step size that I had laying around.. maybe it helps us come to a conclusion either way :)
Comment #233
timmillwoodI can't see any harm in adding #232. I know in #224 I expressed hesitation because it might not be used, but for such a simple change it does make me think "why not?".
Comment #234
hchonovOk then, we could add it, but then I think we need at least a separate Change Record for this :).
Comment #235
hchonovI would prefer not using the word "entity". I think "row" would be better. What do you think?
Comment #236
alexpottAre we sure that the size thing is really going to matter... batch set up is not that expense and no one is addressing #226.1
Imo if we want longer running batches we should just make
if ($batch['progressive'] && Timer::read('batch_processing') > 1000) {
configurable.Comment #237
amateescu CreditAttribution: amateescu for Pfizer, Inc. commented@hchonov, I think it's important to use the word 'entity' because we want people to be aware of the heavy-duty processing that will take place (i.e. all kinds of entity hooks firing). 'Rows' makes me think of database-only operations, which are usually much faster than what Entity API is doing.
@alexpott, to be honest, I don't really understand #226.1 and #236, but it seems that @catch does, so I'll let you both decide that. That's why I posted only the interdiff in the first place :)
Comment #238
catchEven if a single batch request processes multiple iterations, even if we only did a single query per batch, that's 1m / 50 = 20,000.
Some queries are always going to be per each individual item, but assuming most systems will be able to process 100 or 500 items, that's a big difference in total queries required to finish the job. Equally if not more relevant when you have updates loading multiple entities etc.
Comment #240
timmillwoodJust a reroll of #227 due to system.install conflict.
Comment #241
tstoecklerStill looking good.
Comment #243
xjmThere are several comments since #227 that need to be addressed as well, so setting NR. Thanks everyone!
Comment #244
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedSo the remaining discussion is only about whether we should apply the interdiff from #232 or not? FWIW, I can happily live without it :)
Comment #245
tstoecklerTo be perfectly honest I think a setting for that is out of scope for this issue. If we want something like that we should have a general setting for how many entities an update should save/process etc. We have more of those in core already and for custom sites this would be even a greater win. Would also be super useful for Drush, etc. So I'm 100% percent for such a setting, but 100% against doing it here simply because it will unnecessarily increase scope/contention/etc.
Hope that's alright ;-)
Comment #246
xjmIf I'm reading this correctly, @catch, who is a performance topic coordinator and release manager, has said he thinks it is in scope. See #231 and #238. Hopefully I'm not mistaken about that.
Comment #247
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThen let's apply the interdiff from #232 and be done with it :)
Edit:
That's exactly what the interdiff provides.
Comment #249
timmillwoodI was just uploading the same patch, so lets RTBC it!
Comment #250
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedMinor conflict in system.install.
Comment #252
catchOK I'm happy with this now, however given the upgrade path I think it's better to get it into 8.4.x very early than 8.3.x relatively later. Going to try to have one more look over before commit.
Comment #253
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI gave it another look as well and found that the update function was relying on latest entity type definitions from code, which is a big no-no :)
Comment #255
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedRight, we need to exclude entity types for which we're not storing the last installed definition, like config entities.
Comment #256
hchonovNow that #2808335: Changed time not updated when only non-translatable fields are changed on a translated entity got in we should use the new method of retrieving the revision metadata fields in ContentEntityBase:: getFieldsToSkipFromTranslationChangesCheck() as well.
Comment #257
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedYup, let's to do that.
Comment #259
catchOpened #2855315: Remove hard-coding of revision metadata fields in ContentEntityBase::getFieldsToSkipFromTranslationChangesCheck() for that change.
Thanks for all the work here, afaik this is the first patch to change entity storage anywhere on this scale, we've got 7 months to find bugs in the upgrade path so hopefully more people will test 8.4.x with real databases before then too.
Committed/pushed to 8.4.x, thanks!
edit: cross-posted - I think it's completely fine to do that in a follow-up, the original issue didn't have the API available, this issue adds it.
Comment #260
amateescu CreditAttribution: amateescu for Pfizer, Inc. commented@catch also realized in IRC that we should change the update function number if this patch won't end up in 8.3.x, so here's a small followup patch for that.
Comment #262
catchCommitted/pushed the update rename to 8.4.x, thanks!
Comment #264
xjmComment #265
jibranShouldn't this be 8400?
Comment #266
jibranx-post sorry.
Comment #267
hchonov#265 that is correct.
I hope that we do not need a separate issue for this patch?
Comment #268
hchonovIf it couldn't be committed here feel free to close it again and I am sorry for re-opening it.
Comment #269
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedActually, this should be
testSystemUpdate8400()
, but since we already have a followup issue (#2855315: Remove hard-coding of revision metadata fields in ContentEntityBase::getFieldsToSkipFromTranslationChangesCheck()), I propose to fix it there.Comment #271
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedWe forgot to publish the change record for this issue. Just did that now :)
Comment #272
tstoecklerNote to archeologists: This caused an interesting problem at #2976040: default_revision can be left around in data table due to broken entity type CRUD.