Follow-up of #2891215: Add a way to track whether a revision was default when originally created.

This is critical because it completely breaks entity types that still rely on the BC layer. Hopefully trivial to fix so we don't have to revert the other issue.

interdiff is big, easier to just read the new patch.

CommentFileSizeAuthor
#91 entity-rmk_update_fix-2936511-90.patch20.21 KBplach
#91 entity-rmk_update_fix-2936511-90.interdiff.txt956 bytesplach
#2 entity-revision-default-metadata-key-bc-2936511-2.patch2.22 KBBerdir
#4 entity-revision-default-metadata-key-bc-2936511-4.patch2.25 KBBerdir
#4 entity-revision-default-metadata-key-bc-2936511-4-interdiff.txt1.38 KBBerdir
#10 entity-revision-default-metadata-key-bc-2936511-10-test-only.patch1.08 KBBerdir
#10 entity-revision-default-metadata-key-bc-2936511-10.patch5.29 KBBerdir
#10 entity-revision-default-metadata-key-bc-2936511-10-interdiff.txt4.5 KBBerdir
#12 entity-revision-default-metadata-key-bc-2936511-12.patch3.31 KBBerdir
#12 entity-revision-default-metadata-key-bc-2936511-12-interdiff.txt3.4 KBBerdir
#20 entity-revision-default-metadata-key-bc-2936511-20.patch3.47 KBBerdir
#20 entity-revision-default-metadata-key-bc-2936511-20-interdiff.txt1.58 KBBerdir
#22 2936511-22.patch7.74 KBhchonov
#29 2936511-29.patch6.86 KBhchonov
#29 interdiff-22-29.txt1.84 KBhchonov
#29 content_entity_type_d9.txt2.41 KBhchonov
#32 2936511-32.patch4.75 KBhchonov
#36 2936511-35.patch3.79 KBBerdir
#41 2936511-41.patch3.67 KBamateescu
#45 2936511-45.patch16.09 KBhchonov
#45 interdiff-32-45.txt12.26 KBhchonov
#50 interdiff-47-50.txt13.1 KBWim Leers
#50 2936511-50.patch15.9 KBWim Leers
#51 interdiff-45-50.txt7.99 KBWim Leers
#55 2936511-55-test-only.patch16.53 KBmpdonadio
#55 interdiff-50-55.txt655 bytesmpdonadio
#62 2936511-62.patch11.69 KBhchonov
#62 interdiff-50-62.txt11.69 KBhchonov
#66 2936511-66.patch16.77 KBhchonov
#66 interdiff-50-66.txt11.98 KBhchonov
#66 2936511-66.patch16.77 KBhchonov
#66 interdiff-50-66.txt11.98 KBhchonov
#67 2936511-67.patch18.52 KBhchonov
#67 interdiff-66-67.txt1.75 KBhchonov
#83 2936511-82.patch18.54 KBcatch
#83 interdiff-2936511-67-83.txt1.59 KBcatch
#88 entity-rmk_update_fix-2936511-87.interdiff.txt4.02 KBplach
#88 entity-rmk_update_fix-2936511-87.test.patch17.65 KBplach
#88 entity-rmk_update_fix-2936511-87.patch19.47 KBplach
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Berdir created an issue. See original summary.

Berdir’s picture

Not quite as easy as I thought, tricky business with the BC layer and recursion, have to keep it in the constructor but change the BC logic to check for only one metadata key instead of none and also change EntityFieldManager to explicitly opt out of the BC layer.

Status: Needs review » Needs work
Berdir’s picture

More fun, apparently the key isn't set during the upgrade path and there the BC still worked. So the BC needs to work if we have 0 or 1 revision metadata key.

timmillwood’s picture

Issue tags: +Needs tests

Because #2891215: Add a way to track whether a revision was default when originally created passed all core tests it feels as though we don't have suitable test coverage for this issue. To prevent future regression of this issue I think we should add a new test, and a test-only patch in this issue.

Berdir’s picture

I thought about that, not sure. The easiest way to do that would be to have at least one (test or non-test) entity type that relies on the BC layer on purpose. Then that would break. But that will also trigger BC warnings.

plach’s picture

Before #2891215-57: Add a way to track whether a revision was default when originally created I was adopting a similar approach to what this patch is doing (counting keys), however @gabesullice (IMO) correctly pointed out that that's not a particularly solid approach. Can't we just explicitly check the keys?

    if (
      $include_backwards_compatibility_field_names &&
      !isset($this->revision_metadata_keys['revision_user']) &&
      !isset($this->revision_metadata_keys['revision_created']) &&
      !isset($this->revision_metadata_keys['revision_log_message'])  
    ) {
      // ...
    }
The easiest way to do that would be to have at least one (test or non-test) entity type that relies on the BC layer on purpose. Then that would break. But that will also trigger BC warnings.

I was about to suggest exactly that: why are BC warnings a problem? Are you concerned about keeping "deprecated" code around?

Berdir’s picture

I guess we could explicitly check the keys, yes. I kinda dislike long multi-line if statements though :)

Well, one thing with triggering BC messages is that it will actually make the tests fail unless we specifically put them in \Drupal\Tests\Listeners\DeprecationListenerTrait::getSkippedDeprecations().. but turns out that we already have them there. I guess that's because we're still getting into that case during the upgrade path, which is also why my first patch failed.

./core/modules/system/tests/modules/entity_test_revlog/src/Entity/EntityTestMulWithRevisionLog.php: *   revision_metadata_keys = {
./core/modules/system/tests/modules/entity_test_revlog/src/Entity/EntityTestWithRevisionLog.php: *   revision_metadata_keys = {
./core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChangedWithRevisionLog.php: *   revision_metadata_keys = {
./core/modules/block_content/src/Entity/BlockContent.php: *   revision_metadata_keys = {
./core/modules/media/src/Entity/Media.php: *   revision_metadata_keys = {
./core/modules/node/src/Entity/Node.php: *   revision_metadata_keys = {

Thoughts on where we want to remove them? a test or a non-test entity type? My vote would be for one in entity_test_revlog, simply becausae only a few tests would trigger it then, dozens of tests are enabled entity_test.

plach’s picture

I guess we could explicitly check the keys, yes. I kinda dislike long multi-line if statements though :)

Me too :)

A separate boolean variable to be checked in the if test would work for me.

[...] but turns out that we already have them there.

Yep, that's why I was confused by #6.

+1 on entity_test_revlog or a new dedicated entity test type.

Berdir’s picture

Ok, removed the keys from one entity type there, confirmed that MoveRevisionMetadataFieldsUpdateTest now fails. Interestingly. RevisionableContentEntityBaseTest does not fail, it just happily creates the fields in the wrong table then and is consistently inconsistent and nothing explicitly fails. We could also add explicit coverage for the keys there, but I think this is enough?

About the keys, what if we add an if () for each of the 3 keys? Means a bit more nesting, but but then there are no strange special cases anymore and if someone manages to define only one of those keys then BC would still work for them.

plach’s picture

RevisionableContentEntityBaseTest does not fail, it just happily creates the fields in the wrong table then and is consistently inconsistent and nothing explicitly fails.

Do you mean without the fix?

  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityType.php
    @@ -59,19 +59,25 @@ protected function checkStorageClass($class) {
    -    if (!$this->revision_metadata_keys && $include_backwards_compatibility_field_names) {
    +    if ($include_backwards_compatibility_field_names) {
           $base_fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($this->id());
    

    I think we were trying not to do this to avoid retrieving base fields. Not sure how much it buys, though.

  2. +++ b/core/lib/Drupal/Core/Entity/EntityFieldManager.php
    @@ -224,7 +224,9 @@ protected function buildBaseFieldDefinitions($entity_type_id) {
    -      $field_name = $entity_type->getRevisionMetadataKey('revision_default');
    +      // Disable the BC layer to prevent a recursion, this only needs the
    +      // revision_default key that is always set.
    +      $field_name = $entity_type->getRevisionMetadataKeys(FALSE)['revision_default'];
    

    Is this still needed?

Berdir’s picture

plach’s picture

Status: Needs review » Reviewed & tested by the community

Looks good, assuming testbots agree!

hchonov’s picture

+++ b/core/lib/Drupal/Core/Entity/ContentEntityType.php
@@ -59,7 +59,8 @@ protected function checkStorageClass($class) {
+    $needs_bc = !isset($this->revision_metadata_keys['revision_user']) && !isset($this->revision_metadata_keys['revision_timestamp']) && !isset($this->revision_metadata_keys['revision_log_message']);

With this condition we'll execute the BC logic on each method call for the case where an entity type doesn't have any of those fields. If we want to prevent this we would have to use another property e.g. $this->needsBC = TRUE; to store the information whether the BC logic has been already executed.

$needs_bc = $this->needsBC && !isset($this->revision_metadata_keys['revision_user']) && !isset($this->revision_metadata_keys['revision_timestamp']) && !isset($this->revision_metadata_keys['revision_log_message']);
and in the if statement we set that property to FALSE.

Berdir’s picture

Good point, but I think we already called it repeatedly if an entity type had none of those fields. With this change, the difference is that we'd also still call it if the entity type does not have all three but e.g. only the revision user and a log message but no timestamp.

At the same time, the previous code would never have called been called if only the revision_user key would have been defined? We can get the same behavior with isset() || isset() || isset(), further improvements could then be a separate issue and does not need to be in a critical regression fix? A bit worried about side effects of introducing more properties in those objects as they are persisted in entity definition manager and so on.

hchonov’s picture

Status: Reviewed & tested by the community » Needs work

At the same time, the previous code would never have called been called if only the revision_user key would have been defined?

Yes, but if a developer has defined one the keys, then it is her/his responsibility of providing all the keys. We've added the BC layer only because we cannot force the developers to set the revision metadata keys in the entity annotation until D9. Therefore it is fine to skip running the BC logic if one of the keys has been defined in the entity annotation, because this means the developer is aware of those keys.

Actually it is even wrong to run the BC logic if at least one the revision metadata keys has been defined, because the intention might have been to not consider the other fields as revision metadata keys and skip all the internal storage logic that we already have for them. Changing this might really break sites, as the storage will suddenly return completely different structure. I am sorry, but we have to restore the previous behavior - putting back the status to "needs work" for this.

A bit worried about side effects of introducing more properties in those objects as they are persisted in entity definition manager and so on.

We are constantly introducing new properties on classes, the objects of which are persisted. I don't see any problems with doing this. But beside that I think that we persist only the definition i.e. the annotation of an entity, not the entity type object itself.

hchonov’s picture

I think the easiest solution will be to not define the revision_default key in the constructor, but retrieve it through the BC layer like we are retrieving the other revision metadata keys.

Berdir’s picture

Yes, but if a developer has defined one the keys, then it is her/his responsibility of providing all the keys. We've added the BC layer only because we cannot force the developers to set the revision metadata keys in the entity annotation until D9. Therefore it is fine to skip running the BC logic if one of the keys has been defined in the entity annotation, because this means the developer is aware of those keys.

Actually it is even wrong to run the BC logic if at least one the revision metadata keys has been defined, because the intention might have been to not consider the other fields as revision metadata keys and skip all the internal storage logic that we already have for them. Changing this might really break sites, as the storage will suddenly return completely different structure. I am sorry, but we have to restore the previous behavior - putting back the status to "needs work" for this.

Yes, @plach and I basically agree with this, but we changed the logic to explicitly check those 3 keys and not any others. If we ever add more than that then we'll have to adjust this logic but I think it's highly unlikely that an entity type sets other revision metadata keys but not those three, which happen to be basically default fields now with the recommended base classes.

We are constantly introducing new properties on classes, the objects of which are persisted. I don't see any problems with doing this. But beside that I think that we persist only the definition i.e. the annotation of an entity, not the entity type object itself.

We are persisting the entity type definition objects, for example through \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface::setLastInstalledDefinition() and related methods and not just as a cache but pretty much forever. And it's important that those do not get updated/extend automatically. So we definitely need to be careful with what we put in there.

I think the easiest solution will be to not define the revision_default key in the constructor, but retrieve it through the BC layer like we are retrieving the other revision metadata keys.

This is not possible as far as I see for a combination of reasons, it has be be more or less exactly as it is now:

A) The revision_default key/field is added in system_update_8501(). It must not exist before that in the last installed definition. And having it in constructor enables that, because the constructor is not re-run on existing serialized objects.

B) The new key isn't a temporary BC fallback, it's a default, required key. Like default_langcode and revision_translation_affected, which are already defined in the parent in the same way. (not sure why those are there, though..) That means it would be outside of the BC logic but always be set. But the problem with that is that we would lock ourself out of the BC logic if we'd keep it. One of the first calls will be the one that calls it without BC in buildBaseFieldDefinitions(), then we would skip BC and initialize the key. But later on when we want BC, the array is no longer empty.

I actually tried to do what you suggested at first, but it doesn't work. IMHO, this is as close to the previous situation as we can get with the new default_revision flag. What I did is change the logic and variable name a bit to make it clearer when we are doing BC. Because I was actually confused about my own code before :)

plach’s picture

Title: ContentEntityType::getRevisionMetadataKeys() never considers the BC logic because of the new default_revision key » ContentEntityType::getRevisionMetadataKeys() never considers the BC logic because of the new "revision_default" key
hchonov’s picture

I think it's highly unlikely that an entity type sets other revision metadata keys but not those three

That it is "unlikely" from our POV doesn't mean that it is unlikely from the POV of others. I think that it is not good, that we make decisions, because according to our opinion something is unlikely or really a rare use case. If we could solve it without having to break the BC then we should solve it like this, which means that we should handle the "revision_default" revision metadata key just like the other ones. The places where it is required have to check if it exist and if not hard code its name - until the update has been performed - and in the BC layer we just check if the field is provided in the base field definitions with the default name we use.

This might not be a beautiful solution, but it is how we currently deal with the other revision metadata keys and I think this is the only option we have until D9, where we'll require that the revision metadata keys are defined in the entity type annotation.

This is not possible as far as I see for a combination of reasons, it has be be more or less exactly as it is now:

In the patch I am posting I've implemented it with the BC layer to show exactly what I mean and it is possible :).

If we don't want that the places needing the field name have to hard code it themselves then we could introduce a new method name ContentEntityTypeInterface::getRevisionMetadataDefaultKeyName(), which will simply return the default field name.

Berdir’s picture

But revision_default should not be BC, we don't want every entity type to explicitly specify it, it is a requirement and is defined by default, just like default_langcode, which is also set by default in the keys. @plach explicitly said yesterday that he doesn't want that every entity type needs to specify it and I agree.

hchonov’s picture

In the patch that I've provided it is not specified by any entity type, but added automatically.

Berdir’s picture

Yes but it is inside the BC logic that will be removed and you trigger a warning if it's not set.

hchonov’s picture

What is the problem with it being inside the BC logic? We can remove the error, it is actually not needed as it is us who add the metadata key.

amateescu’s picture

@hchonov, @Berdir explained why it shouldn't be inside the BC logic in #23 and I agree with that comment.

hchonov’s picture

It is not only in the BC logic but also in the constructor to cover the two cases where the revision metadata keys are provided / not provided in the entity type annotation. When the BC logic is removed then we'll just define it in the constructor without the condition that we currently have.

Sorry but I still don't understand what exactly is the problem with such an approach?

hchonov’s picture

Is the warning that is causing the confusion? I am sorry about this, my bad for adding it. I've removed the warning. This patch doesn't force any entity type to define the "revision_default" key and when we remove the BC layer the key will be just defined in the constructor like now, but we will simply remove the condition that we currently have in the constructor.

The file "content_entity_type_d9.txt" shows how the entity type will look like in D9.

amateescu’s picture

+++ b/core/modules/system/system.install
@@ -2078,7 +2078,7 @@ function system_update_8501() {
-    $field_name = $entity_type->getRevisionMetadataKey('revision_default');
+    $field_name = $entity_type->hasRevisionMetadataKey('revision_default') ? $entity_type->getRevisionMetadataKey('revision_default') : 'revision_default';

This is the problem with this approach. The system should *always* have a revision_default metadata key defined, either by default or specified in the entity annotation.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

hchonov’s picture

This is the problem with this approach. The system should *always* have a revision_default metadata key defined, either by default or specified in the entity annotation.

Like mentioned in Slack it is not possible because of the BC layer we've provided which relies on the revision_metadata_keys property being empty.


Here is the another approach where we need to make the BC layer a bit more complex but this way the "revision_default" key should always be returned by new instances of the class and never returned by unserialized objects prior to the update to cover the case where the entity type objects are cached in the EntityLastInstalledSchemaRepository.

@berdir will probably not be happy with this approach as he stated in #17:

A bit worried about side effects of introducing more properties in those objects as they are persisted in entity definition manager and so on.

Berdir’s picture

+++ b/core/lib/Drupal/Core/Entity/ContentEntityType.php
@@ -59,7 +80,7 @@ protected function checkStorageClass($class) {
     // Provide backwards compatibility in case the revision metadata keys are
     // not defined in the entity annotation.
-    if (!$this->revision_metadata_keys && $include_backwards_compatibility_field_names) {
+    if ((!$this->revision_metadata_keys || ($this->revision_metadata_keys === $this->requiredRevisionMetadataKeys)) && $include_backwards_compatibility_field_names) {
       $base_fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($this->id());
       if ((isset($base_fields['revision_uid']) && $revision_user = 'revision_uid') || (isset($base_fields['revision_user']) && $revision_user = 'revision_user')) {

Doesn't your own argument work against you here?

If I explicitly add revision_metadata_keys = ['revision_default' => 'revision_default'] to my entity type, then it will still call the BC logic.

I'm fine with that. But you argued that it shouldn't do that anymore as soon as any key is set explicitly by the user.

I'm OK with this patch, but you kind of aren't :)

To summarize, I'm fine with doing any of the following:
* this patch
* this idea with just hardcoding revision_default directly in the BC logic, I don't care that much about duplicating and hardcoding it, I don't think we'll add another 5 required keys and we can live with 1-2 additional hardcoded ones.
* my last patch
* your previous one

@plach also suggested adding a helper method that would prevent us from duplicating the revision_default fallback in EntityFieldManager. After thinking about it, I don't really like adding a public method to the class/interface because of a temporary BC layer that most likely nobody else will have to worry about.

plach’s picture

FWIW this is what @Berdir was referring to in the last paragraph of the previous comment:

https://gist.github.com/plach79/dda896dad256d507939ebd31e1558b0a

plach’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

Spent some more time discussing this with Berdir in Slack. I suggested an adjusted version of #33 (see https://gist.github.com/plach79/f22ceca27c8d071464f46e46d5769f24) while he suggested a simplified version that cannot be fixed the way I was suggesting (see https://gist.github.com/Berdir/410dc15cc4ff06aa2675503c7d500fa6).

I'm personally fine with both, but if we need to support these edge cases properly, we also need some test coverage, otherwise we will keep breaking stuff. I think that having a new test entity type defining only the revision_default key + some assertions around the generated schema should be enough.

Berdir’s picture

Status: Needs work » Needs review
FileSize
3.79 KB

Here is a patch for that gist to do what I described above as the second item. I'm OK with #33 too but I think the property for that is overkill for the single required property that we have no, we can always go back to that if we end up with different ones.

@plach will also post a gist or so for an approach that would fix the scenario that I described in #33 but I personally don't think it needs to be fixed. To clarify that, he only case that would be weird now is if that entity type would define only revision_default *and* would have a revision_user/.. field and would *not* want that field to be used as a revision metadata field. And the workaround for that would be to put a dummy key in the annotation so it's not empty/equals the required values. But such a workaround would already be required right now in 8.4.

Setting to needs review to have this tested too. To have a test for #35, we first need to agree on what should happen in that case, whether or not that should run BC. If running BC is fine, then we just need to change the keys accordingly on the second test entity type and our existing asserts in the update tests should be sufficient? If not then we'll need likely a new module with a new entity type.

hchonov’s picture

Doesn't your own argument work against you here?

If I explicitly add revision_metadata_keys = ['revision_default' => 'revision_default'] to my entity type, then it will still call the BC logic.

Good catch... I had another solution which I tried to simplify and didn't notice that I've introduced that behavior for this key. But the fix for this is pretty simple - we should not add the key to the new property if it is already provided in the annotation :). This way the condition ($this->revision_metadata_keys === $this->requiredRevisionMetadataKeys) will evaluate to FALSE because the property $this->revision_metadata_keys will not be empty and will not equal the empty property $this->requiredRevisionMetadataKeys.

I'm OK with #33 too but I think the property for that is overkill for the single required property that we have no, we can always go back to that if we end up with different ones.

The property has an idea and it is to always store the state of the last version which covers cached entity type objects. If we remove the property the next time we introduce a new key the condition in the getter method will have to be defined like this:

$previous_required_revision_metadata_keys = [
  'revision_default' => 'revision_default'
];
$new_required_revision_metadata_keys = [
  'revision_default' => 'revision_default',
  'new_metadata_key' => 'new_metadata_key'
];
if ((!$this->revision_metadata_keys || ($this->revision_metadata_keys === $previous_required_revision_metadata_keys) || ($this->revision_metadata_keys === $new_required_revision_metadata_keys)) && $include_backwards_compatibility_field_names) {

where ($this->revision_metadata_keys === $previous_required_revision_metadata_keys) will match entity type objects that have been cached and ($this->revision_metadata_keys === $new_required_revision_metadata_keys) will match new instances.

To prevent such complex updates and additions to the required revision metadata keys we have to keep the property.

hchonov’s picture

Should we merge the changes from this issue into the parent one, which has been reverted?

Berdir’s picture

It was only reverted from 8.5.x so that it's not in the alpha. AFAIK it will be committed again as-is once that is released. I'd agree but we would need to revert in both versions first.

plach’s picture

Let's keep things simple and keep working here, please :)

Did we reach consensus on how to move forward? It seems to me that #37 is an alternative to fix #32 that is similar in principle to what I was suggesting.

@hchonov:

Can we try that and add test coverage for the use cases it support?

amateescu’s picture

Isn't this enough to fix the BC layer?

hchonov’s picture

Assigned: Unassigned » hchonov

Can we try that and add test coverage for the use cases it support?

@plach, I'll write a test later today.

@amateescu, unfortunately this is not enough, because we don't want to execute the BC layer if the entity type definition ships with already set revision metadata keys.
@see #18:

Actually it is even wrong to run the BC logic if at least one the revision metadata keys has been defined, because the intention might have been to not consider the other fields as revision metadata keys and skip all the internal storage logic that we already have for them. Changing this might really break sites, as the storage will suddenly return completely different structure.

Status: Needs review » Needs work

The last submitted patch, 41: 2936511-41.patch, failed testing. View results

effulgentsia’s picture

It was only reverted from 8.5.x so that it's not in the alpha. AFAIK it will be committed again as-is once that is released.

Just confirming that this is correct. This issue's "version" field is set to 8.6.x and so can proceed, since #2891215: Add a way to track whether a revision was default when originally created is already in 8.6, and so the patch in this issue can be committed to 8.6 regardless of when 8.5 alpha is tagged. Once 8.5 alpha is tagged, I'll cherry pick that issue back into 8.5, as well as this one when it's ready.

hchonov’s picture

Status: Needs work » Needs review
FileSize
16.09 KB
12.26 KB

And here is the test and the fix so that we are able always to detect if the revision metadata fields have been defined in the annotation or not.

hchonov’s picture

Issue tags: -Needs tests

Status: Needs review » Needs work

The last submitted patch, 45: 2936511-45.patch, failed testing. View results

plach’s picture

Status: Needs work » Needs review

I'll have a look ASAP, thanks!

plach’s picture

This looks great to me, thanks a lot for the expanded test coverage!

I have only a few minor nits:

  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityType.php
    @@ -25,9 +36,23 @@ public function __construct($definition) {
    +    // have been provided by the entity type annotation therefore we add keys to
    
    +++ b/core/modules/system/tests/src/Functional/Entity/Update/MoveRevisionMetadataFieldsUpdateTest.php
    @@ -79,4 +80,128 @@ public function testSystemUpdate8400() {
    +    // have been provided by the entity type annotation therefore we add keys to
    

    Superminor: can we add a comma before "therefore" in both cases? :)

  2. +++ b/core/modules/system/tests/src/Functional/Entity/Update/MoveRevisionMetadataFieldsUpdateTest.php
    @@ -79,4 +80,128 @@ public function testSystemUpdate8400() {
    +    // Object Type: \Drupal\Tests\system\Functional\Entity\Update\TestRevisionMetadataBcLayerEntityType
    

    These are a bit confusing at first sight: do we need them given that the objects are type-hinted below?

  3. +++ b/core/modules/system/tests/src/Functional/Entity/Update/MoveRevisionMetadataFieldsUpdateTest.php
    @@ -79,4 +80,128 @@ public function testSystemUpdate8400() {
    +    $cached_with_no_metadata_keys = 'Tzo4MjoiRHJ1cGFsXFRlc3RzXHN5c3RlbVxGdW5jdGlvbmFsXEVudGl0eVxVcGRhdGVcVGVzdFJldmlzaW9uTWV0YWRhdGFCY0xheWVyRW50aXR5VHlwZSI6Mzk6e3M6MjU6IgAqAHJldmlzaW9uX21ldGFkYXRhX2tleXMiO2E6MDp7fXM6MzE6IgAqAHJlcXVpcmVkUmV2aXNpb25NZXRhZGF0YUtleXMiO2E6MDp7fXM6MTU6IgAqAHN0YXRpY19jYWNoZSI7YjoxO3M6MTU6IgAqAHJlbmRlcl9jYWNoZSI7YjoxO3M6MTk6IgAqAHBlcnNpc3RlbnRfY2FjaGUiO2I6MTtzOjE0OiIAKgBlbnRpdHlfa2V5cyI7YTo1OntzOjg6InJldmlzaW9uIjtzOjA6IiI7czo2OiJidW5kbGUiO3M6MDoiIjtzOjg6Imxhbmdjb2RlIjtzOjA6IiI7czoxNjoiZGVmYXVsdF9sYW5nY29kZSI7czoxNjoiZGVmYXVsdF9sYW5nY29kZSI7czoyOToicmV2aXNpb25fdHJhbnNsYXRpb25fYWZmZWN0ZWQiO3M6Mjk6InJldmlzaW9uX3RyYW5zbGF0aW9uX2FmZmVjdGVkIjt9czo1OiIAKgBpZCI7czo0OiJ0ZXN0IjtzOjE2OiIAKgBvcmlnaW5hbENsYXNzIjtOO3M6MTE6IgAqAGhhbmRsZXJzIjthOjM6e3M6NjoiYWNjZXNzIjtzOjQ1OiJEcnVwYWxcQ29yZVxFbnRpdHlcRW50aXR5QWNjZXNzQ29udHJvbEhhbmRsZXIiO3M6Nzoic3RvcmFnZSI7czo0NjoiRHJ1cGFsXENvcmVcRW50aXR5XFNxbFxTcWxDb250ZW50RW50aXR5U3RvcmFnZSI7czoxMjoidmlld19idWlsZGVyIjtzOjM2OiJEcnVwYWxcQ29yZVxFbnRpdHlcRW50aXR5Vmlld0J1aWxkZXIiO31zOjE5OiIAKgBhZG1pbl9wZXJtaXNzaW9uIjtOO3M6MjU6IgAqAHBlcm1pc3Npb25fZ3JhbnVsYXJpdHkiO3M6MTE6ImVudGl0eV90eXBlIjtzOjg6IgAqAGxpbmtzIjthOjA6e31zOjE3OiIAKgBsYWJlbF9jYWxsYmFjayI7TjtzOjIxOiIAKgBidW5kbGVfZW50aXR5X3R5cGUiO047czoxMjoiACoAYnVuZGxlX29mIjtOO3M6MTU6IgAqAGJ1bmRsZV9sYWJlbCI7TjtzOjEzOiIAKgBiYXNlX3RhYmxlIjtOO3M6MjI6IgAqAHJldmlzaW9uX2RhdGFfdGFibGUiO047czoxNzoiACoAcmV2aXNpb25fdGFibGUiO047czoxMzoiACoAZGF0YV90YWJsZSI7TjtzOjE1OiIAKgB0cmFuc2xhdGFibGUiO2I6MDtzOjE5OiIAKgBzaG93X3JldmlzaW9uX3VpIjtiOjA7czo4OiIAKgBsYWJlbCI7czowOiIiO3M6MTk6IgAqAGxhYmVsX2NvbGxlY3Rpb24iO3M6MDoiIjtzOjE3OiIAKgBsYWJlbF9zaW5ndWxhciI7czowOiIiO3M6MTU6IgAqAGxhYmVsX3BsdXJhbCI7czowOiIiO3M6MTQ6IgAqAGxhYmVsX2NvdW50IjthOjA6e31zOjE1OiIAKgB1cmlfY2FsbGJhY2siO047czo4OiIAKgBncm91cCI7TjtzOjE0OiIAKgBncm91cF9sYWJlbCI7TjtzOjIyOiIAKgBmaWVsZF91aV9iYXNlX3JvdXRlIjtOO3M6MjY6IgAqAGNvbW1vbl9yZWZlcmVuY2VfdGFyZ2V0IjtiOjA7czoyMjoiACoAbGlzdF9jYWNoZV9jb250ZXh0cyI7YTowOnt9czoxODoiACoAbGlzdF9jYWNoZV90YWdzIjthOjE6e2k6MDtzOjk6InRlc3RfbGlzdCI7fXM6MTQ6IgAqAGNvbnN0cmFpbnRzIjthOjA6e31zOjEzOiIAKgBhZGRpdGlvbmFsIjthOjA6e31zOjg6IgAqAGNsYXNzIjtOO3M6MTE6IgAqAHByb3ZpZGVyIjtOO3M6MjA6IgAqAHN0cmluZ1RyYW5zbGF0aW9uIjtOO30=';
    +    /** @var \Drupal\Tests\system\Functional\Entity\Update\TestRevisionMetadataBcLayerEntityType $entity_type */
    +    $entity_type = unserialize(base64_decode($cached_with_no_metadata_keys));
    +    $required_revision_metadata_keys = [];
    +    $this->assertEquals($required_revision_metadata_keys, $entity_type->getRevisionMetadataKeys(FALSE));
    ...
    +    $cached_with_metadata_key_revision_default = 'Tzo4MjoiRHJ1cGFsXFRlc3RzXHN5c3RlbVxGdW5jdGlvbmFsXEVudGl0eVxVcGRhdGVcVGVzdFJldmlzaW9uTWV0YWRhdGFCY0xheWVyRW50aXR5VHlwZSI6Mzk6e3M6MjU6IgAqAHJldmlzaW9uX21ldGFkYXRhX2tleXMiO2E6MTp7czoxNjoicmV2aXNpb25fZGVmYXVsdCI7czoxNjoicmV2aXNpb25fZGVmYXVsdCI7fXM6MzE6IgAqAHJlcXVpcmVkUmV2aXNpb25NZXRhZGF0YUtleXMiO2E6MTp7czoxNjoicmV2aXNpb25fZGVmYXVsdCI7czoxNjoicmV2aXNpb25fZGVmYXVsdCI7fXM6MTU6IgAqAHN0YXRpY19jYWNoZSI7YjoxO3M6MTU6IgAqAHJlbmRlcl9jYWNoZSI7YjoxO3M6MTk6IgAqAHBlcnNpc3RlbnRfY2FjaGUiO2I6MTtzOjE0OiIAKgBlbnRpdHlfa2V5cyI7YTo1OntzOjg6InJldmlzaW9uIjtzOjA6IiI7czo2OiJidW5kbGUiO3M6MDoiIjtzOjg6Imxhbmdjb2RlIjtzOjA6IiI7czoxNjoiZGVmYXVsdF9sYW5nY29kZSI7czoxNjoiZGVmYXVsdF9sYW5nY29kZSI7czoyOToicmV2aXNpb25fdHJhbnNsYXRpb25fYWZmZWN0ZWQiO3M6Mjk6InJldmlzaW9uX3RyYW5zbGF0aW9uX2FmZmVjdGVkIjt9czo1OiIAKgBpZCI7czo0OiJ0ZXN0IjtzOjE2OiIAKgBvcmlnaW5hbENsYXNzIjtOO3M6MTE6IgAqAGhhbmRsZXJzIjthOjM6e3M6NjoiYWNjZXNzIjtzOjQ1OiJEcnVwYWxcQ29yZVxFbnRpdHlcRW50aXR5QWNjZXNzQ29udHJvbEhhbmRsZXIiO3M6Nzoic3RvcmFnZSI7czo0NjoiRHJ1cGFsXENvcmVcRW50aXR5XFNxbFxTcWxDb250ZW50RW50aXR5U3RvcmFnZSI7czoxMjoidmlld19idWlsZGVyIjtzOjM2OiJEcnVwYWxcQ29yZVxFbnRpdHlcRW50aXR5Vmlld0J1aWxkZXIiO31zOjE5OiIAKgBhZG1pbl9wZXJtaXNzaW9uIjtOO3M6MjU6IgAqAHBlcm1pc3Npb25fZ3JhbnVsYXJpdHkiO3M6MTE6ImVudGl0eV90eXBlIjtzOjg6IgAqAGxpbmtzIjthOjA6e31zOjE3OiIAKgBsYWJlbF9jYWxsYmFjayI7TjtzOjIxOiIAKgBidW5kbGVfZW50aXR5X3R5cGUiO047czoxMjoiACoAYnVuZGxlX29mIjtOO3M6MTU6IgAqAGJ1bmRsZV9sYWJlbCI7TjtzOjEzOiIAKgBiYXNlX3RhYmxlIjtOO3M6MjI6IgAqAHJldmlzaW9uX2RhdGFfdGFibGUiO047czoxNzoiACoAcmV2aXNpb25fdGFibGUiO047czoxMzoiACoAZGF0YV90YWJsZSI7TjtzOjE1OiIAKgB0cmFuc2xhdGFibGUiO2I6MDtzOjE5OiIAKgBzaG93X3JldmlzaW9uX3VpIjtiOjA7czo4OiIAKgBsYWJlbCI7czowOiIiO3M6MTk6IgAqAGxhYmVsX2NvbGxlY3Rpb24iO3M6MDoiIjtzOjE3OiIAKgBsYWJlbF9zaW5ndWxhciI7czowOiIiO3M6MTU6IgAqAGxhYmVsX3BsdXJhbCI7czowOiIiO3M6MTQ6IgAqAGxhYmVsX2NvdW50IjthOjA6e31zOjE1OiIAKgB1cmlfY2FsbGJhY2siO047czo4OiIAKgBncm91cCI7TjtzOjE0OiIAKgBncm91cF9sYWJlbCI7TjtzOjIyOiIAKgBmaWVsZF91aV9iYXNlX3JvdXRlIjtOO3M6MjY6IgAqAGNvbW1vbl9yZWZlcmVuY2VfdGFyZ2V0IjtiOjA7czoyMjoiACoAbGlzdF9jYWNoZV9jb250ZXh0cyI7YTowOnt9czoxODoiACoAbGlzdF9jYWNoZV90YWdzIjthOjE6e2k6MDtzOjk6InRlc3RfbGlzdCI7fXM6MTQ6IgAqAGNvbnN0cmFpbnRzIjthOjA6e31zOjEzOiIAKgBhZGRpdGlvbmFsIjthOjA6e31zOjg6IgAqAGNsYXNzIjtOO3M6MTE6IgAqAHByb3ZpZGVyIjtOO3M6MjA6IgAqAHN0cmluZ1RyYW5zbGF0aW9uIjtOO30=';
    +    $entity_type = unserialize(base64_decode($cached_with_metadata_key_revision_default));
    +    $required_revision_metadata_keys = [
    +      'revision_default' => 'revision_default',
    +    ];
    +    $this->assertEquals($required_revision_metadata_keys, $entity_type->getRevisionMetadataKeys(FALSE));
    ...
    +    $entity_type = new TestRevisionMetadataBcLayerEntityType(['id' => 'test']);
    +    $required_revision_metadata_keys = [
    +      'revision_default' => 'revision_default',
    +      'second_required_key' => 'second_required_key',
    +    ];
    +    $this->assertEquals($required_revision_metadata_keys, $entity_type->getRevisionMetadataKeys(FALSE));
    

    Can we also add an assertion for the expected behavior with the BC-layer enabled?

    Btw, why are we base64-encoding the cached item? Wouldn't it be easier to troubleshoot this code in the future, if we just had the serialized string?

Wim Leers’s picture

#49:

  1. Done.
  2. Done.
  3. Can we also add an assertion for the expected behavior with the BC-layer enabled?

    I don't know this well enough to be sure what you're asking here. Can you clarify?

    Btw, why are we base64-encoding the cached item? Wouldn't it be easier to troubleshoot this code in the future, if we just had the serialized string?

    I tried, but we can't get rid of this AFAICT:

        $no_metadata_keys = new TestRevisionMetadataBcLayerEntityType(['id' => 'a', 'revision_metadata_keys' => []]);
        $this->assertEquals([], $no_metadata_keys->getRevisionMetadataKeys(FALSE));
    

    fails with:

    Failed asserting that two arrays are equal.
    --- Expected
    +++ Actual
    @@ @@
     Array (
    +    'second_required_key' => 'second_required_key'
    +    'revision_default' => 'revision_default'
     )
    

    and

        $with_metadata_key_revision_default = new TestRevisionMetadataBcLayerEntityType(['id' => 'a', 'revision_metadata_keys' => ['revision_default' => 'revision_default']]);
        $this->assertEquals(['revision_default' => 'revision_default'], $with_metadata_key_revision_default->getRevisionMetadataKeys(FALSE));
    

    fails with:

    Failed asserting that two arrays are equal.
    --- Expected
    +++ Actual
    @@ @@
     Array (
         'revision_default' => 'revision_default'
    +    'second_required_key' => 'second_required_key'
     )
Wim Leers’s picture

FileSize
7.99 KB

#50's interdiff was wrong, and the filename even indicated that.

effulgentsia’s picture

Thank you for everyone's diligent work on this. Just a reminder that 8.5 beta is next week, which will be the first tagged release since #2891215: Add a way to track whether a revision was default when originally created landed. Any help with final reviews and testing would be very much appreciated, so that ideally beta testers updating existing (alpha1 or earlier) sites can do so without getting those databases into a broken state. Thanks!

effulgentsia’s picture

Issue tags: +Upgrade path
effulgentsia’s picture

mpdonadio’s picture

Is this the same problem?

Status: Needs review » Needs work

The last submitted patch, 55: 2936511-55-test-only.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

plach’s picture

@Wim Leers, #50:

I don't know this well enough to be sure what you're asking here. Can you clarify?

I meant that we should add an invocation of ::getRevisionMetadataKeys(TRUE) and verify that the returned value is correct.


@mpdonadio, #56:

That test does not look right to me: it is not running updates. Loading an entity before updates have run may indeed lead to executing a broken query.

Wim Leers’s picture

#57: I see. But we can't do that for \Drupal\Tests\system\Functional\Entity\Update\TestRevisionMetadataBcLayerEntityType, since it's not an actual entity type and hence this:

$base_fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($this->id());

in \Drupal\Core\Entity\ContentEntityType::getRevisionMetadataKeys() will fail. How do you propose we do that then?

plach’s picture

Status: Needs work » Needs review

Ok, makes sense, let's ignore my suggestion. #50 seems ready to go to me, I will RTBC it tomorrow morning, unless @Berdir has any concern.

plach’s picture

Status: Needs review » Reviewed & tested by the community

RTBC patch is #50.

hchonov’s picture

Status: Reviewed & tested by the community » Needs work

Sorry, I am currently working on an updated patch :). Will post it in a minute.

hchonov’s picture

Assigned: hchonov » Unassigned
Status: Needs work » Needs review
FileSize
11.69 KB
11.69 KB

I am sorry for the delay.

Actually it is possible to add the test case where we check the BC layer of the serialized entity type. TestRevisionMetadataBcLayerEntityType is just the class, but the entity type it represents is defined through the "id" property. Therefore if the id represents a real entity type, then the entity field manager will be able to provide the base field definitions and so properly execute the BC layer.
When I've created that serialized entity type initially I used "test" for the id property, which doesn't represent any entity type. In the current patch I've exchanged it with the entity type "entity_test_mul_revlog", for which we've removed the revision metadata keys from the annotation so that we could test the BC layer.

@plach, now we have the additional test with the BC layer, that you wished :).

@plach, I had to use base64 encoding instead of simply providing the string output from serialize(), as I was unable of unserializing it. I've found the idea to use base64 encoding for such cases in https://stackoverflow.com/questions/10152904/unserialize-function-unseri... . Is there a better way of doing this?

@berdir pointed me to a new issue regarding this subject, but I was unable of reproducing it - #2941733: block_content_revision loses columns due to incorrect entity definition and I am not sure if it should prevent the current one from getting in?

Status: Needs review » Needs work

The last submitted patch, 62: 2936511-62.patch, failed testing. View results

hchonov’s picture

I've just noticed, that I've uploaded the interdiff as a patch ... I'll be back on my computer in 40 minutes and will upload the whole patch. I am sorry for this ...

plach’s picture

Thanks!

@berdir pointed me to a new issue regarding this subject, but I was unable of reproducing it - #2941733: block_content_revision loses columns due to incorrect entity definition with patch 2936511 and I am not sure if it should prevent the current one from getting in?

Well, unless it's directly causing that issue I think this shouldn't be blocked, I also tried to reproduce that issue without luck.

hchonov’s picture

Status: Needs work » Needs review
FileSize
16.77 KB
11.98 KB
16.77 KB
11.98 KB

Here is the patch my comment #62 was about.

P.S. It looks like I should not upload any patches anymore today :). I have no idea how I've managed it to upload the patch and the interdiff twice.

hchonov’s picture

After talking with @berdir about #2941733: block_content_revision loses columns due to incorrect entity definition, I've realized that with the new BC logic the update hook for adding the revision_default revision metadata key isn't complete. I've extended the patch and I am curious if somehow this is solving the referenced issue.

plach’s picture

Status: Needs review » Reviewed & tested by the community

Tests are green and I also manually tested the upgrade from 8.4 to 8.5 once again and it seems to be working fine.

I get a couple of "The entity type block_content does not have a "published" entity key." notices, but it's the same in HEAD, so I think this is ready to go now.

Berdir’s picture

> I get a couple of "The entity type block_content does not have a "published" entity key."

I only got those when updating directly from 8.3 to 8.5, not 8.4. And they're not notices but exceptions. And then it worked the second time I executed drush updb.

I think that's related to the order in which block_content_8400() and other update functions are executed. We might need some explicit dependencies there if we depend on a certain order.

plach’s picture

Berdir’s picture

To be clear, pretty sure that is *not* caused by that issue, I reproduced those errors without this patch while looking at #2941733: block_content_revision loses columns due to incorrect entity definition, it's just standard update function dependency fun (*hint* #2942096: [policy, no patch] Remove old update hooks prior to each major version (Drupal 10 and later) *hint*)

plach’s picture

Yep, I agree those seem to be unrelated.

sdewitt’s picture

sdewitt’s picture

@berdir @hchonov @plach #67 resolves the loss of the columns reported on #2941733: block_content_revision loses columns due to incorrect entity definition with patch 2936511. I apologize I did not realize that it was really the same issue and keep all the history here in this patch discussion. Thanks for all your hard work on this one. Please feel free to close out 2941733 as a dup.

plach’s picture

Thanks for reporting, glad we were able to sort this out. It would be bad if we discovered it post commit ;)

hchonov’s picture

Yay, I am happy that #67 is resolving your issue, @sdewitt.

We should add a test for the interdiff in #67, which is resolving the referenced issue. We'll certainly need the same test the next time we add a new required revision metadata key.

plach’s picture

Status: Reviewed & tested by the community » Needs work

Good point

hchonov’s picture

Any thoughts on how we could reproduce the problem in a test?

plach’s picture

No idea, honestly, I was never able to reproduce that problem. We could try to break the logic we had in place before the latest iteration and see whether that helps reproducing the issue. Worst case we would get implicit test coverage.

mpdonadio’s picture

Re #56 and #57, that wasn't a real test. It was just the minimum amount of code to show the problem (the query barfing). The test I was working on was an update test that

- use the filled fixture (drupal-8.filled.standard.php.gz)
- loaded one of the test nodes
- checked the field value
- ran updates (which does a field value change)
- reloaded the node
- checked the field value to see if the update worked.

The problem (for me at least) means that drupal-8.filled.standard.php.gz won't work (others may have this too, didn't check). I can make my own fixture; just wanted to use what was there. Rebuilding it may (or may not) be worthy of a followup.

Committers: please remove my credit on this; my file on this issue didn't add any value to the patch.

Berdir’s picture

- use the filled fixture (drupal-8.filled.standard.php.gz)
- loaded one of the test nodes
- checked the field value
- ran updates (which does a field value change)

You can't do this. Loading/using the entity API before/during updates is *not* supported. That's why we have post updates.

If you want to check the initial value, then do a raw DB query against the table.

effulgentsia’s picture

  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityType.php
    @@ -59,7 +84,7 @@ protected function checkStorageClass($class) {
    -    if (!$this->revision_metadata_keys && $include_backwards_compatibility_field_names) {
    +    if ((!$this->revision_metadata_keys || ($this->revision_metadata_keys === $this->requiredRevisionMetadataKeys)) && $include_backwards_compatibility_field_names) {
    

    Are we sure we want a strict (===) comparison here? Aren't there pathways by which they might not be in the same order? For example, if there ends up being a 2nd required one, added in a child class and therefore defined before 'revision_default', but an update function adds it to the last installed definition after 'revision_default' was already added by system_update_8501()?

  2. +++ b/core/modules/system/system.install
    @@ -2092,9 +2092,22 @@ function system_update_8501() {
    +        // Update the revision metadata keys to add the new required revision
    +        // metadata key "revision_default".
    +        $revision_metadata_keys += $required_revision_metadata_keys;
    

    This line of code is doing more than the comment says, depending on what else happens to be in $required_revision_metadata_keys besides 'revision_default'. I think it would be best to limit the scope of this update function to only 'revision_default', and leave future required keys to the update functions of the patches/modules that introduce them. Or is there a reason we need/want to merge in the entire required array here?

  3. +++ b/core/modules/system/system.install
    @@ -2092,9 +2092,22 @@ function system_update_8501() {
    +      if (!isset($revision_metadata_keys['revision_default'])) {
    +        // Update the property holding the required revision metadata keys,
    +        // which is used by the BC layer for retrieving the revision metadata
    +        // keys.
    +        // @see \Drupal\Core\Entity\ContentEntityType::getRevisionMetadataKeys().
    +        $required_revision_metadata_keys = $installed_entity_type->get('requiredRevisionMetadataKeys') + ['revision_default' => $field_name];
    +        $installed_entity_type->set('requiredRevisionMetadataKeys', $required_revision_metadata_keys);
    +
    +        // Update the revision metadata keys to add the new required revision
    +        // metadata key "revision_default".
    +        $revision_metadata_keys += $required_revision_metadata_keys;
    +        $installed_entity_type->set('revision_metadata_keys', $revision_metadata_keys);
    +
    +        $definition_update_manager->updateEntityType($installed_entity_type);
    +      }
    

    This entire block only runs when there isn't already an installed field that happens to be named 'revision_default'. But then we're in a situation where in such a case, the metadata keys end up being different between the last installed definition and the runtime code one. I think it would make more sense to run this block outside of the if (!$definition_update_manager->getFieldStorageDefinition($field_name, $entity_type_id)) { statement that's above it, since nothing in here needs to distinguish between whether that field already exists or not.

  4. We should add a test for the interdiff in #67...Any thoughts on how we could reproduce the problem in a test?

    If I understand correctly (please correct me if I don't), the problem prior to #67 was that system_update_8501() ran in such a way that following it, $installed_entity_type->getRevisionMetadataKeys() would not support the BC of 'revision_user' etc. And in #2941733: block_content_revision loses columns due to incorrect entity definition that manifested when block_content_update_8400() ran after system_update_8501(). In which case, can we expand EntityUpdateAddRevisionDefaultTest::testAddingTheRevisionDefaultField() to check that following the updates, that $installed_entity_type->get('requiredRevisionMetadataKeys') and $installed_entity_type->get('revision_metadata_keys') are what we expect and/or that $installed_entity_type->getRevisionMetadataKeys() returns what we expect? I think that would be sufficient test coverage, or am I missing something?

catch’s picture

I found several issues with the comments. This patch doesn't address #82, but it addresses my comment nits (seemed easier to directly make the changes than post a review).

While I wouldn't block this patch going in on it, because we already have the data loss in HEAD, I think we should have actual upgrade path tests that expose this issue (i.e. with an entity type that requires this bc layer, with some content in the db for it).

Berdir’s picture

+++ b/core/modules/system/tests/modules/entity_test_revlog/src/Entity/EntityTestMulWithRevisionLog.php
@@ -5,6 +5,9 @@
  *
+ * This entity type does not define revision_metadata_keys on purpose to test
+ * the BC layer.
+ *
  * @ContentEntityType(
  *   id = "entity_test_mul_revlog",
  *   label = @Translation("Test entity - data table, revisions log"),
@@ -21,11 +24,6 @@

@@ -21,11 +24,6 @@
  *     "label" = "name",
  *     "langcode" = "langcode",
  *   },
- *   revision_metadata_keys = {
- *     "revision_user" = "revision_user",
- *     "revision_created" = "revision_created",
- *     "revision_log_message" = "revision_log_message"
- *   },
  * )

We do, with this change, the existing test we have for the upgrade path of this are failing. See test-fail in #10.

plach’s picture

What about committing this as soon as #82 is addressed and trying to improve test coverage as a follow-up?

plach’s picture

Assigned: Unassigned » plach

Working on #82.

catch’s picture

#84/#10 is what I asked for indeed, I just didn't realise it was already covered, so fine with any additional coverage we can add being a follow-up.

plach’s picture

@effulgentsia, #82:

1: Seems a good fix to me. I manually checked the update once again and requiredRevisionMetadataKeys is initialized with an array even when the parent object is being unserialized, so I don't think we are at risk of matching a NULL with an empty array, which would be the only unintended scenario allowed by this change.
2: Fixed
3: I think the current logic is correct, imagine the following scenario:

  1. We are updating an entity type that does have an existing (unrelated) revision_default field;
  2. if system_update_8501() runs while the codebase has not been updated to deal with the new revision_default revision metadata key, the latter will get a default value of revision_default, thus matching the existing field;
  3. at this point the current logic will skip processing for this entity type id entirely;
  4. at the end of the process you get a warning in the status report telling you you need to update your code or things will not work correctly;
  5. you install an updated version of the provider module, which implements an update function as suggested at https://www.drupal.org/node/2936349 (just updated it), and everything works fine;
  6. if the updated module were already installed, system_update_8501() would do all the work and the provider module update would return early, but the end result would be correct also in this case.

If instead we go the way you suggest, we will be missing the warning in step 4, which I don't think would be correct.
4: Yep, that's what I meant in #79. Implemented that. Attaching also a failing patch.

hchonov’s picture

@effulgentsia, #82.3

This entire block only runs when there isn't already an installed field that happens to be named 'revision_default'.

If this happens then this is bad, really bad, as we rely on the field not being there. If an entity type already defines a field with such a name, we cannot simply reuse it and pretend it is the core field. How do we handle this? We cannot simply throw an exception, but probably log an error? I think we should start defining core reserved field names and not allow custom/contrib using them.

P.S. I've just seen the response of @plach about this in #88. Then I guess everything is correct.

P.S.2 @plach, but I think that it would be good to add an else branch and log an error pointing to the CR.

The last submitted patch, 88: entity-rmk_update_fix-2936511-87.test.patch, failed testing. View results

plach’s picture

Berdir’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me, I think all the reviews have been addressed.

effulgentsia’s picture

Looks good to me as well. Thanks!

Removing credit from @mpdonadio per request in #80. Adding credit to @sdewitt for the testing in #2941733: block_content_revision loses columns due to incorrect entity definition that led to this patch's improvement.

  • effulgentsia committed 3fcf079 on 8.6.x
    Issue #2936511 by hchonov, Berdir, plach, Wim Leers, catch, amateescu,...

  • effulgentsia committed cf1b8bf on 8.5.x
    Issue #2936511 by hchonov, Berdir, plach, Wim Leers, catch, amateescu,...
effulgentsia’s picture

Version: 8.6.x-dev » 8.5.x-dev
Status: Reviewed & tested by the community » Fixed

Thank you to everyone for helping resolve this critical regression prior to beta! Pushed to 8.6.x and 8.5.x!

KarimBou’s picture

Hi, I updated from 8.4.4 to 8.5.0beta and i'm getting this error, i checked the patch it is present, i've been struggling trying to find how to resolve this.

The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">Drupal\Core\Database\DatabaseExceptionWrapper</em>: SQLSTATE[42S22]: Column not found: 1054 Unknown column &#039;revision.revision_default&#039; in &#039;field list&#039;: SELECT revision.vid AS vid, revision.langcode AS langcode, revision.revision_user AS revision_user, revision.revision_created AS revision_created, revision.revision_log_message AS revision_log_message, revision.revision_default AS revision_default, base.mid AS mid, base.bundle AS bundle, base.uuid AS uuid, CASE base.vid WHEN revision.vid THEN 1 ELSE 0 END AS isDefaultRevision
FROM 
{media} base
INNER JOIN {media_revision} revision ON revision.vid = base.vid
WHERE base.mid IN (:db_condition_placeholder_0); Array
(
    [:db_condition_placeholder_0] =&gt; 1
)
 in <em class="placeholder">Drupal\Core\Entity\Sql\SqlContentEntityStorage-&gt;getFromStorage()</em> (line <em class="placeholder">455</em> of <em class="placeholder">core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php</em>).
Berdir’s picture

Are you sure that you did run all updates after doing the update?

That problem does not seem to be related to this issue.

hchonov’s picture

Hm even if the update hasn't been executed and the field isn't present then why would the storage assume it is there?

Berdir’s picture

Because we always look at the runtime definitions and *not* the last installed definitions for field definitions and entity types. There's an issue to change that, but not sure how easy that is.

KarimBou’s picture

@Berdir Yes I did check all the updates and even tried to restor databse to 8.4.4 and update again to 8.5.0beta.
I fixed this by adding a column "revision_default" in table 'media_revision'.

But now i'm getting this error after I followed upgrade path for Media which is now implemented in core. But I get this error when i try to add a Media. I tried recreating my own media type but i still get the same error:

<em class="placeholder">Drupal\Component\Plugin\Exception\PluginNotFoundException</em>: The &quot;media_bundle&quot; entity type does not exist. in <em class="placeholder">Drupal\Core\Entity\EntityTypeManager-&gt;getDefinition()</em> (line <em class="placeholder">133</em> of <em class="placeholder">core/lib/Drupal/Core/Entity/EntityTypeManager.php</em>

Any idea if this might come from the previous error i mentioned above ?

catch’s picture

@KarimBou could you open a new, critical, issue for this report?

Berdir’s picture

The second error sounds to me a bit like its related to media_entity/media and an unclean solution there. Are you sure that all the updates did run through and there was no error that is blocking the execution of other updates? drush updb should report no pending updates.

But yes, lets open a new issue and discuss there.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

BlinX’s picture

Referring to my problem as described in

https://www.drupal.org/project/drupal/issues/2956936

I am still trying to fix this.

At this moment I get these errors:

An existing "Default revision" field was found for the Aangepast blok[error]
entity type, but no "revision_default" revision metadata key was
found in its definition.
An existing "Default revision" field was found for the Inhoud entity [error]
type, but no "revision_default" revision metadata key was found in
its definition.

Since I see this is mentioned (solved!?) in this thread , can anyone tell me what to do (I am lost...) Thanks!

hchonov’s picture

@BlinX, these errors occurred during the update, right? Did you update your site through drush updb or through the Drupal UI?

BlinX’s picture

@hchonov, I first attempted a few times by Drupal UI, then after reading severall related posts I learned to install/use Drush and tryed drush updb and drush entup also few times.
Although this happened last time:
Site is in D8.4.8, drush updb to D8.5.3 did not work.
So I used Installatron to upgrade to D8.5.3 and after that did drush updb and drush entup.
I saw a lot (good things I believe) happening in Drush, but also the Errors I mentioned before...