Problem/Motivation
Detect config file renames during the import process. According to comment #2, there are two use cases (in the fields and instances conversion) where the import is not going to detect a rename, of which one is quite problematic.
1. Rename bundle, e.g article to article2: in that case the instance is going to be renamed, e.g field.instance.node.article.field_name.yml to field.instance.node.article2.field_name. The import will detect a delete and a new import. It would be nicer in case this was detected as a rename, but isn't problematic as there's no data loss or so.
2. Deleting a field and recreating a field with the same name. The import will detect this as a change for both the field instance, while it actually should be a delete and a new import. Ironically, we /do/ want data loss here because deleting a field is going to rename the data en revision table so the data in it can be removed later during cron runs.
Proposed resolution
According to comment #3, we should re-write the config import process to use UUIDs instead of config object names. Or a combination of both (comment #5).
Remaining tasks
- Write unit test for addChangelistRename()
- Review #1609418: Configuration objects are not unique to determine if this is a duplicate
- Create patch
Original report by heyrocker
Right now, we have no way to handle renamed config. If you rename a config file, it will appear in the import process as the deletion of one file and the creation of another one. This will probably use UUIDs within the config file to determine that this same config just has a different name, rather than being an all new config.
Followups
#2240813: Move ImportableEntityStorageInterface check in ConfigImporter to a validation event
#2240819: Improve storage changelists to handle rename information better
| Comment | File | Size | Author |
|---|---|---|---|
| #78 | 1740378.78.patch | 38.8 KB | alexpott |
| #78 | 75-78-interdiff.txt | 2.12 KB | alexpott |
| #75 | 1740378.75.patch | 38.6 KB | alexpott |
| #75 | 72-75-interdiff.txt | 20.78 KB | alexpott |
| #72 | interdiff-68-72.txt | 18.69 KB | xjm |
Comments
Comment #1
sunThis issue sorta duplicates #1609418: Configuration objects are not unique
Comment #2
swentel commentedThis is important for the fields and instances conversion. There are two use cases where the import is not going to detect a rename, of which one is quite problematic.
Talked this through quickly with alexpott and he suggested to use validation API and error out and say people should go back to dev and re-rename (basically delete and create) the field to a different name. This buys us time, however, we lose a feature that was in Field API.
Comment #3
sunThat's why we implemented UUIDs — see #1. As explained over in that issue already:
The config import process has to be rewritten to be based on UUIDs instead of config object names, so we are able to detect renames as well as deleted-recreated-with-same-name objects.
That's supposed to happen in that issue.
Comment #4
yched commented[edit: crosspost]
Recognizing "different uuid on import" as "delete existing + create new" is definitely something that we need to support in the end. I kind of thought it was "fixed" alredy.
I've lost track where exactly this logic could/should reside - "somewhere" in the generic import process or within Field API's specific reaction to it. I'd tend to think the former of course, sounds like basic import logic rather than something specific to Field API. Basically, this is needed for any config entity with a CRUD cycle - and with entity API hooks possibly implemented in 3rd part code, that's *all* config entities, Field API is just the subsystem with the most critical use case in core right now.
Comment #5
gddUpgrading this status because this is obviously a critical missing feature.
I was thinking about this today and I think the best way to manage this is to add the config UUIDs to the manifest files. Using the combination of UUID and machine name to detect renames should be reasonably simple to implement.
Comment #6
thanhtek commentedIssue Summary Template has been added.
Comment #6.0
thanhtek commentedIssue Summary Template
Comment #6.1
mtiftUpdated issue summary.
Comment #7
mtiftTagging
Comment #8
ruloweb commentedJust tested case 1
And it breaks nodes of that bundle.
Usually when you change the bundle machine_name the node_type_update_nodes function change the node's bundle in the database:
https://api.drupal.org/api/drupal/core%21modules%21node%21lib%21Drupal%2...
so everything is ok, but when you import a config that change a bundle name, this function is not executed, then you breaks all the nodes of that bundle, so yes, there is data lost.
I suggest to change the issue summary.
Comment #9
Anonymous (not verified) commentedbump.
Comment #9.0
Anonymous (not verified) commentedUpdated issue summary.
Comment #10
alexpottWow how did we forget about this?
Comment #11
alexpottA starter for ten :)
The patch detects both renames (where an incoming create has the same UUID as a delete) and recreations (where an update has a UUID change and should be a delete and then a create). It adds a test based on these two scenarios for a content type - which works out well because creating a node type at the moment involves the creation of a field, field instance, entity form mode and an entity display mode! Additionally it is currently possible to change the machine name of a content type through the UI.
The recreation import works fine but the renames breaks due to secondary writes and deletes and needs #2080823: Create API to discover config entities' soft dependencies and use this to present a confirm form on module uninstall to land. Therefore I will postpone this issue on that once the testbot is back.
That said I think we should consider preventing renames and actually not support them - the complexities of keeping everything in sync and not breaking anything is boggling and this will only actually get worse after the dependencies patch lands since we we need to update the dependencies of other config entity when a rename occurs :(
Comment #13
berdirYeah, renames are hard. Not sure what and how to support them, but we should probably at least inform the user?
And recreations is something that we should be able to handle, and is IMHO at least as important.
Not sure I understand why we can't use the existing API to rename a config entity?
Comment #14
xjmPostponing per #11.
Comment #15
jessebeach commentedComment #16
jessebeach commentedComment #17
jessebeach commentedUnpostponed!
Comment #18
Anonymous (not verified) commentedjust had a discussion with alexpott at drupaldevdays - we'd like to split this out in to two issues.
first, when we do delete/create (a config entity with the same name but different UUID is imported), and second, when we do a rename (where a config entity changes name, but the UUID is the same).
leaving this issue as rename, and created #2224873: Handle a config entity with the same name but different UUID being imported for the first case.
Comment #19
Désiré commentedReroll
Comment #21
Désiré commentedWell... ConfigStorageController.php was just renamed in the last 15 minutes...
Comment #22
alexpottThis test is not needed. It already exists in #2224873: Handle a config entity with the same name but different UUID being imported
Comment #24
Désiré commentedComment #25
Désiré commentedComment #27
xjmNow posptoned on #2124535: Prevent secondary configuration creates and deletes from breaking the ConfigImporter.
Comment #28
Désiré commentedComment #29
Désiré commentedWhile we waiting for #2124535: Prevent secondary configuration creates and deletes from breaking the ConfigImporter here are some improvements for this.
Comment #30
Désiré commentedHere I'm unsure how can we do this better, since the new addChangelistRename() will call 'readMultiple' more times.
Also here, the debug will show that the body field base will not updated only the field instance. I don't know what is the expected here.
Comment #32
Désiré commentedThis new patch should fix the exception in ConfigSnapshotTest. But I think this solution will need some cleanup.
Comment #34
Désiré commentedSome minor refactor in addChangelistRename().
Comment #35
Désiré commentedComment #37
Désiré commentedComment #39
Désiré commentedHm... Something messed up when I created the #34 patch (and #37, just see the file sizes), so here I recreated the right one.
Comment #41
Désiré commentedI see, that syntax error doesn't happens with PHP 5.5... (So repeat #37)
Comment #43
xjmThanks for the work on this. At this point, per discussion with @alexpott, posptoning until #2124535: Prevent secondary configuration creates and deletes from breaking the ConfigImporter is in since there is overlap.
Comment #44
xjmUnpostponed now.
Comment #45
xjmComment #46
alexpottWorking patch - no interdiff due to considerable changes. Needs tidy up.
Comment #47
xjmComment #48
alexpottComment #49
xjmWe're removing this @todo, but I don't see anything in the interdiff that addresses it?
Comment #50
alexpottThe @todo is being removed from the patch because it was addressed by #2030073: Config cannot be imported in order for dependencies
Comment #51
gábor hojtsyReviewed this patch.
Docs need to be filled in still.
I'm not very well versed in the import code, but this :: concatenated approach seems to be very strange to me...
I see the changelists are designed to take strings only but this looks like an ugly workaround so we don't need to change any of that...
So this is not really a name, it may be a name pair concatenated too for the rename op. This may need to be updated elsewhere too.
Comment #52
xjmComment #53
alexpott1. Fixed
2 & 3. Yeah @beejeebus is not that keen either but change this from a string will mean that all the changelist code will need to change - can we explore doing this is a beta target - as this works and is not really an important developer facing API.
Comment #54
Anonymous (not verified) commentedi'm ok with fixing the awkward in a follow up. RTBC if this is green.
Comment #55
Anonymous (not verified) commenteddoh, i forgot the other concern i had.
this looks out of place. if we can work this out during validate, we should, else we're allowing a known-bad import to proceed past the point of no return.
Comment #56
xjmLet's also get any needed followups filed before we RTBC, and maybe we should get a review from yched if possible?
Comment #57
alexpott#55 should be a followup too since this is a copy and paste from
ConfigImporter::importInvokeOwner()All necessary followups created. I think this is good to go.
Comment #58
xjm@alexpott, can you link said followups?
Comment #59
alexpottI did add them to the issue summary but let's make them related too...
Comment #60
alexpottRerolled now that #2236553: Config import validation needs to be able to list multiple issues and the messages should be translatable has landed
Comment #61
Anonymous (not verified) commentedok, RTBC with follow-ups in.
Comment #62
xjmThe test makes sense up to this bit?
Comment #63
xjmAlso, the comment says 5 config entities, but the assertion says 4. More specific assertions might be better. Looks like the test could use some work, so setting NW per discussion with @alexpott. It might also be good to document what happens for the user with screenshots or suchlike.
Comment #64
alexpottImproved the test and added a new test to cover the validation code.
So @xjm's question about the UI prompted quite a bit of thinking. Renames were not even being listed by the patch in #64. Now they are and the user can diff them. Screenshot of how the list of renames looks attached.
Comment #65
xjmHow would this case even happen? Aren't the renames detected based on UUIDs?
There's also a few minor coding standards issues in the patch. Rather than enumerating, I'll clean them up. :)
Comment #66
xjmThis is where the magical double colon concatenation happens; I spent awhile trying to find it. We should document that in this docblock and probably @see from the methods that take the colon-concatenated-name-thing.
Also, colons are a legitimate filename character on my filesystem, weird as that seems. Are we already validating config prefixes and IDs to ensure this character is not used otherwise, so that there is no risk of false positives?
Comment #68
alexpott@xjm: so colons are not legal config characters see ConfigBase::validateName()
Added methods to StorageComparer to encapsulate the rename name oddness which might make #2240819: Improve storage changelists to handle rename information better obsolete. And fixed the test.
Comment #69
xjmThanks, that's much better.
Comment #70
xjmMore correct status since I'm still fixing things here albeit distractedly.
Comment #71
tim.plunkettThis should use
explode('::', $name, 2)to be safeComment #72
xjmHere we go. Sorry for the delay; the patch went and got twice as big following my question about the UI. :)
I still have several outstanding questions, which I'll post in a moment when I can use dreditor on my own patch. ;)
Comment #73
xjmI'm still not sure how we would ever get to this case, since the rename is detected based on UUID; we check elsewhere that the importer is an instance of WhatsitEntityImporterInterface (whatever). It's certainly robust to have this here, but does this mean we have an untested code path?
This has me going "hmm" in light of #2240709: ConfigImportUITest::testImport fails when the module list changes. It's unclear why the array_reverse() means that content types are handled before fields, and it's weird that we're mentioning content types and fields specifically in this generic class. At least, we need more detail in this comment.
Maybe these methods should be static, since they don't require the instantiated object in any way?
I did as @tim.plunkett suggested here as a best practice for explode(). We shouldn't ever run into a case where we have more than one set of :: because of the config name validation described above, but this is fine.
This still needs to be replaced with our new method. I guess that means we'd need to add the config importer to the 39,000 injected dependencies? Edit: Maybe unless we made it static as I suggest above.
Where are they coming from, why are there these 5? Does node really create all of this for you automagically, or is this coming from the config test module? More detail or an @see to the config we're testing on would not go amiss.
Comment #74
xjmAlso,
ConfigImportRenameTestisn't generic; rather, it's a test of renames' impact on content types and fields. Maybe it would be good to make that the explicit purpose for the test (including renaming it, moving it to node.module, whatever) so that it doesn't break unexpectedly when we change what node does with the body field and whatnot.A part of me also wants to ask for a decoupled test that just uses the test implementation, but OTOH we already have other coverage using the test config entity and this specific case is already more rich than a test scenario we'd derive, so I think the coverage is good enough.
Comment #75
alexpottMove and renamed the test to node & NodeTypeRenameConfigImportTest
Comment #76
xjmLooking good. Two things.
I have no idea how the comment at the beginning of this bears any relation to the code following it. To the reader, the code inside the foreach() has nothing to do with ordering. Either the comment is in totally the wrong place, or it's missing some explanation, or both. @alexpott spammed a lot of "look at docblocks for fooMethod()" at me and mentioned StorageComparer::getAndSortConfigData(), so if no one else improves this in the meanwhile, I'll look at that when I have the chance to try to figure out what needs to be clarified here. ;)
Copypasta fail.
Comment #77
xjmOne more question, sorry:
If the array_reverse() was completely wrong, why did no tests fail? :)
Comment #78
alexpott#76:
#77 - Because the order checked for in the test was wrong - because the test is renamed this is hidden in the interdiff
Comment #79
xjmThat all makes sense. Looks good to me.
Since I mostly only cleaned up nitpicky stuff, I'm comfortable RTBCing this pending tests passing. :)
Comment #81
xjm