Follow-up to #2225775: Migrate Drupal 6 core node translation to Drupal 8
Problem/Motivation
In D8, a node revision has data for all languages. In D6 and D7, each node revision has data for just one language. This means currently D6 -> D8 and D7 -> D8 migrations yield a revision history where each node revision has only one language present, even if previous revisions had translations. This is strange and likely to confuse users.
Priority is given to the D7 migration because the D6 one might be effected by the term node migrations.
This is for migrating core translation revisions, migrating revisions for entity translation is now being done in #3076447: Migrate D7 entity translation revision translations. It is intended that this issue is completed first and then the approach used here is also used for entity translations.
The following notes use the term 'master' which was the original term what is now the 'complete' node migration.
Meeting notes
Things to consider, taken from notes of meeting with alexpott, Gábor Hojtsy, heddn, mikelutz and quiteone
- Migration dependencies
- Possible: easy to do
- BC concerns: none
- BC what about contrib? Commerce / metatag
- Possible incompatible
- How do we figure out you need the new complex master migration?
- Possible: moderate to do
- BC concerns: none
- Don’t effect expose new d*_node_master unless explicitly enabled to existing sites
- This handles incremental, no UI option available to enable
- For new installs, give an option to upgraders to enable disable the new d*_node_master migration and use the old method.
- What about migration lookups on dn_node etc.
- Possible: easy to do
- BC concerns: none
- What about incremental migrations that have already begun
- Not allowed. You can only use the new approach on a site that has not run migrations. So we need some of flag where existing installs use the old migrations.
- Contrib/Custom
- Does it make sense to use d*_node_master instead of d*_node migrations?
Proposed resolution
The current focus is to get the D7 version working and then move those changes to D6.
Using the existing method where the final revision is migrated first doesn't work instead the revisions need to be migrated in order. The first proof of concept patch to show this is in #121, thanks to mikelutz. This new approach is called the Complete node migration and the migrations and files use that name.
The migrate_drupal_ui functional tests use the complete node migration, as this is now the default. The exception is d6/NodeClassicTest which as the name suggests uses the classic node migration.
Do not deprecate the classic node migrations
"Removing it [the classic node migrations] will break the whole ecosystem. Deprecating it means that it’s not used anymore which in and of itself will break the eco system, and for what benefit? To remove it in Drupal 10, a year after D7 is EOL? At that point we are looking at deprecating the entire D7 upgrade path anyway (Probably for removal in 11, not 10) We really don’t want to break the migration ecosystem right now at this critical time when we are trying to get everyone off of D7 in the next 18-20 months". by mikelutz in #migration on Drupal Slack). See #3109819: [PP-1] Deprecate classic node migration plugin in Drupal 9 for removal in Drupal 10 for discussion of when deprecation of classic node migrations would happen.
Run Complete or classic node migrations
However, the Complete approach doesn't integrate with the current migrations, now being called Classic, that is the one either runs the existing node migrations or the new complete one, not both. Choosing between the two methods is done at run time by checking the node migration map tables in MigrationConfigurationTrait() and migrate_drupal_migration_plugins_alter(). In the trait the classic method is selected if any of the classic node migrate_map tables has data (using processedCount) and in the plugins_alter it is selected if any classic node migrate_map exists and there are no complete node migrate_maps.
The selection of node migration method also allows tests to decide which method to use. The test does this by adding either 'node_migrate_classic' or 'node_migrate_complete' to $modules.
Destination IDs
The entity complete destination plugin creates up to three IDs. First is the entity ID key. Then the revision key and the langcode, if they exist.
Altered migrations
There are several migrations that use migration_lookup on the node migration. The processes and dependencies for these are altered when the complete node migration is being run. The changes include new processes to convert the 3 destination IDs returned by complete node to the correct destination ID expected when using the classic migration.
The migrations altered are: d6_term_node, d6_term_node_revision, d6_term_node_translation, d6_comment, d6_url_alias,d7_comment, d7_url_alias, statistics_node_counter, node_translation_menu_links.
Incremental migrations
For the core UI, if map tables for the classic mode migrations exist and at least one has a row then the classic migrations will run and not the complete node migrations. This is determined at run time. Therefor, existing incremental migration should continue to function.
From the original IS
D6 and D7 node revisions do not directly indicate which other translated node revisions they are associated with. But we can attempt to guess this, based on the order of revisions.
Remaining tasks
As of 2020-05-11, the only remaining task is to update the patch for 8.8.x so that classic migrations are the default (set in settings.php).
Fix tests, add comments
Review
For Reviewers
- All level of code reviews welcome.
- There are BC issues raised in meeting notes, above
For Testers
- Use the lastest patch patch in this issue.
- There are instructions in the Issue summary of the META issue #2208401: [META] Remaining multilingual migration paths
From #55
For developers
- Make this work with a multilingual source and a non multilingual source
- Discuss and document implications for drush, especially
drush migrate-upgrade --all - Make sure the d6 and d7 tests includes testing a fields on each revision
- Handle the case where the vid of the first revision is higher the vid of a later revision. This is true for both d6 and d7.
- Make sure that the use of migration_tag 'translation' is documented. It is used in
- d6_node_translation.yml
- d7_node_entity_translation.yml
- d7_node_translation.yml
- d7_node_revision_translation.yml
- d7_user_entity_translation.yml
- d7_taxonomy_term_entity_translation.yml/li>
- d7_comment_entity_translation.yml
- d6_node_revision_translation.yml
- Make sure that the source plugin 'translation' property is documented
- Make sure that the destination plugin 'translations' property is documented

Release notes snippet
A new node migration, d*_node_complete.yml, referred to as the 'complete node migration', now exists that will migrate all nodes, the node revisions, including translated nodes and the translated node revisions. The complete node migration will eventually replace the existing trio of node migrations, d*_node, d*_node_revision and d*_node_translation, now collectively referred to as the classic node migrations. See https://www.drupal.org/node/3105503 for more information.
| Comment | File | Size | Author |
|---|---|---|---|
| #465 | interdiff-2746541-451-463.txt | 1.73 KB | benjifisher |
| #463 | 2746541-463.patch | 188.59 KB | quietone |
| #463 | interdiff-461-463.txt | 8.83 KB | quietone |
| #461 | 2746541-461.patch | 192.06 KB | quietone |
| #461 | interdiff-457-461.txt | 7.98 KB | quietone |
Comments
Comment #2
catchReverting a revision in Drupal 6/7 creates a new revision based on the older one, so the only case where the current revision is not the highest one would be in the case of a not-yet-published forward revision.
Comment #3
vasi commented@catch: Can you unpublish a single revision in D6/7? I think 'status' lives in the node table...
Comment #4
mikeryanPostponed on #2225775: Migrate Drupal 6 core node translation to Drupal 8.
Comment #6
gábor hojtsy@vasi: some contrib modules implement forward revisions with some core hacks, allowing for the "latest revision" to not necessarily be the published revision. See https://www.drupal.org/project/revisioning, Workbench moderation, etc, eg. https://www.drupal.org/files/images/workbench-moderation-screenshot.png (note difference between current vs. published revision -- the published revision state is orthogonal to the published node status). While these are not core features, entities encountered in the migration may have gone through these modules earlier.
Comment #9
maxocub commentedHere's a failing test to start with.
Comment #15
heddnPostponing on #2921661: Add support to migrate multilingual revisions.
Comment #17
maxocub commentedI know this is postponed, but I wanted to make sure that what was being done in #2921661: Add support to migrate multilingual revisions was going to help here, and it does.
So here's what I've got so far.
Comment #18
maxocub commentedFixing the tests
Comment #19
maxocub commented#2921661: Add support to migrate multilingual revisions is in, unpostponing. First, it needs a reroll.
Comment #21
jofitzCorrect 1 of the test failures (the rest confuse me!)
Comment #22
maxocub commentedLet's try this to fix the remaining tests.
Comment #24
masipila commentedI did quite a lot of manual testing with D6-D8 and ran into two different issues.
This fist one is easily reproducible:
1. Have 2 languages installed
2. Have a content type (e.g. Story) that has multilingual support enabled
3. Create a Story node in English
4. Translate the node to Finnish
5. Create a Rev 2 revision in Finnish
6. Revert to the original Finnish revision
7. Run the upgrade to D8
8. Notice that the original Finnish translation is not shown in the revision list. One picture tells more than a thousand words so have a look at the screenshot below.
The second issue that I found is more trickier to reproduce but it leads to fatal error. I have a hunch on the root cause but I don't want to speculate before I have exact steps to reproduce. Once I have, I'll post the findings as a separate comment for the sake of clarity.
Cheers,
Markus
Edit: typo
Comment #25
masipila commentedOk, here we go with further test results. For the sake of clarity, I post each different test to a separate comment.
1. Have 2 languages installed
2. Have a content type (e.g. Story) that has multilingual support enabled
3. Add taxonomy to Story so that the taxonomy does **not** have multilingual support.
3. Create a Story node in English and make sure that you have a taxonomy term associated with the node.
4. Translate the node to Finnish and ensure that the term is associated.
5. Create a Rev 2 revision in Finnish
6. Revert to the original Finnish revision
7. Create a Rev 2 revision in English
8. Run the upgrade to D8
9. Notice that the current Finnish revision is shown on the English revision list. One picture tells more than a thousand words so have a look at the screenshot below.
Note: this is not yet the fatal error issue I reported in #24. I'll post that as the next comment.
Edit: typo
Comment #26
masipila commentedAnd here's the third issue I found in my manual tests. This leads to a fatal error, details below.
1. Have 2 languages installed
2. Have a content type (e.g. Story) that has multilingual support enabled
3. Add taxonomy to Story so that the taxonomy has 'Localized' multilingual setting enabled.
3. Create a Story node in English and make sure that you have a taxonomy term associated with the node.
4. Translate the node to Finnish and ensure that the term is associated.
5. Create a Rev 2 revision in Finnish
6. Revert to the original Finnish revision
7. Create a Rev 2 revision in English
8. Run the upgrade to D8
After the upgrade, go to admin/content on your D8 site to see the migrated nodes.
A. Try to access the Finnish version of the node. It will be otherwise OK except the 'localized' term is not associated with the node. This is because of #2886609: Migrate translations for D6 i18n taxonomy 'localized' terms and more specifically because of #2859297: Migrate taxonomy term references for D6 Node translations. The latter issue (2859297) is about making the association between the translated node and localized term.
B. Try to access the English version of the node. You'll get HTTP 500.
EDIT / Additional information
The fatal error occurs also when there are no revisions i.e. when there is only the English D6 node which is translated to Finnish and they both have the 'localized' term. Let's move this part to be handled as part of #2859297: Migrate taxonomy term references for D6 Node translations instead of this issue as this seems to have nothing to with revisions migration.
Edit 2
The fatal error is caused by the fact that the term record in taxonomy_term_field_data does not have a langcode as it should. So this is definitely out of scope of this node revision migrate issue.
Comment #27
masipila commentedI was testing this more. This is highly dependent on the multilingual taxonomy term migrations. I tested so that I had the following patches applied:
- 2746541#22 (this issue)
- 2975509-50
- 2886609-86
The results were better but still not complete.
When using 'localized' terms, the results seemed to be OK
When using 'Per language' terms, the EN revision list was showing the latest FI revision as 'current' and the current EN revision was not shown as current.
When using 'Fixed language' terms, the revision lists were OK but the Finnish term was not associated with the Finnish version of the node.
This is way too complex to test without the term migrations being done first, let's postpone this on #2975509: Migrate D6 vocabulary language settings and #2886609: Migrate translations for D6 i18n taxonomy 'localized' terms.
Markus
Comment #28
quietone commentedPut the issues this is postponed on at the top of the IS for convenience.
Comment #30
masipila commentedRe-parenting so that all multilingual migrations can be found from the same meta. Moved original parent to related issues.
Comment #31
masipila commentedUnpostponed as this landed: #2886609: Migrate translations for D6 i18n taxonomy 'localized' terms. D7 part may need a separate follow-up.
Comment #33
quietone commentedLet's reroll this.
Aside from the reroll, the changes include moving the migrations to content_translation, creating a property for a variable in both source plugins. The node revision translation tests fail, but let's see what else may fail.
Comment #35
quietone commentedTesting for the error reported in #24. I was able to reproduce the problem and it is not related to translations. The error occurs when there has been a 'revert' action on a content type.
Tested by making a new content type, with out translations and with revisions. Added a node of the content type, then made a revision, then reverted that revision. Then I ran 'd6_node' and 'd6_node_revision'. When looking at the revision history on Drupal 8, the original revision is not listed, just like what masipila reported.
Comment #36
quietone commentedTracked down the problem with the display to the field revision_translation_affected field. It is NULL for the 1st revision migrated and if I change it to 1, the display shows all the revisions. And thus solves the problem reported in #24. The question remains, how to migrate that value?
This field is a bit mysterious. Finding documentation on it isn't easy. Eventually found #2933518: The semantics of the "revision_translation_affected" field are unclear to Decoupled Drupal developers (REST/JSON API/GraphQL) users, improve this, and in there was a link to Entity.api.php Lin 102 which is the documentation. In that it states
So with that one would assume, at least I do, that revision_translation_affected is set only when a translation has changes in a revision. That is not true.
I created a D8 site with the standard profile, did not install any translation module or language, created a new content type that is revision-able and added a few revisions. Then, looked at the db tables node_field_data/node_field_revision, and revision_translation_affected is set to 1. There are no translations yet the field is set.
Related to this is the revision_default flag for each revision. That same documentation states
Every revision for the test content type has revision_default set to 1.
So, I made another content type and that is not revision-able and made some content. Just like before revision_translation_affected is set to 1 even though this is a content type that is not revision-able and on a single language site with no translation modules enabled.
It is a really hot day here any maybe I'm not thinking clearly or done something wrong in my tests but if I haven't this is really whacky.
From a migration perspective, the solution is to set revision_translation_affected to TRUE and have a followup to determine what to do about that field value.
Comment #37
masipila commentedWhoa...
@quietone, can you summarize what would be the result if we would do this as you suggested?
Markus
Comment #38
quietone commented@masipila, Yea, seems radical. The result is that migrate is duplicating what I see D8 doing in my testing. If I can make a non revision-able, non translatable content type that has revision_translation_affected set to 1 and that content type behaves normally, then it shouldn't break a migration for a non revision-able and non translatable content type. And since the field is supposed to be set for revision-able content type and revision-able and translatable content types it should not break those either.
Comment #39
masipila commentedOk, sounds logical to try that approach!
Comment #40
quietone commentedA patch implementing the idea in #36. Haven't worked on the failing MigrateUploadTest
Comment #43
quietone commentedReroll
Comment #45
quietone commentedChange the order of migrations in MigrateContent so that d6_node_revision_translation only runs after d6_node_translation and d6_node_revision.
Also, removed the 'vid: vid', from MigrateUploadTest which is an indication of a larger issue with revision numbers. I'm thinking that the migration of vid needs to be removed from all migrations except perhaps d6_node.yml. More on that later, it is too late now.
Comment #46
mikelutzAssigning to myself to review this week. I want to take a look at the revision_translation_affected stuff and the revision ids.
Comment #47
quietone commentedWoke up thinking that it might be worthwhile to move the vid issue to it's own issue and focus on the revision_translation_affected here. What do you think?
Comment #48
masipila commentedI was testing the patch #45 manually on a break between the games. The patch breaks something quite badly, see the attached screenshot from D8 /admin/content with the patch applied:
I gotta go to play another game now so I can't troubleshoot this more right now but I did test the migration with and without patch #45. Clean 8.7.x -- the /admin/content looks normal
With patch ¤45 -- the /admin/content shows corrupted data as illustrated in the screenshot above.
Cheers,
Markus
p.s. I unassigned @mikelutz who had assigned self for review. Now that I kicked this back to NW, the assignment would indicate that @mikeluz would work on the actual patch.
Edit:
I'm not able to easily reproduce this issue with the /admin/content view.
I orignally had an English story node in D6 without any revisions.
I also had a Finnish story node which was linked as a translation to the English node
The Finnish node had a revision and I had then reverted the original as current.
When I migrated to D8, this was the result on the /admin/content
However, I then later added a revision also to the English node and reverted the original as the current. When I did this, the /admin/content view looked normal.
I tried to repro the issue shown in the screenshot above by deleting the English revisions on my D6 site but at least in this setup the /admin/content looked normal.
This might have something to do with the fact that originally I had the English node which did not have any revisions + a linked Finnish node which did have revisions. I need to go to bed now so I'm not able to try to repro this from the scratch.
Comment #49
masipila commentedD8 standard behavior
I chose to go back to basics and double check how D8 shows the revisions of multilingual content types when the data is created in D8. This is what we should achieve with migrations.
D6 data
I have similar data setup in D6, look at the screenshot below.
Migration results with patch #45 applied.
English revisions after the migration

The screenshot below shows the English revisions after the migration.
There are two problems:
1. The Finnish revisions are displayed on the English revisions list.
2. The current English revision is not displayed as the 'Current' revision because the current Finnish revision is displayed as current.
Finnish revisions after the migration

The screenshot below shows the Finnish revisions after the migration.
There is one problem:
3. The English revisions are displayed on the Finnish revision list.
Comment #50
quietone commented@masipila, I followed your instructions on how to create the nodes in D6, ran the migration via the UI and got different results. The English revisions were OK but there was one revision missing in the Italian revisions list. I'll look into that but I'd really like to reproduce your results. Can you post the Drupal 6 node and node_revisions tables you used? Hopefully, you still have it around.
Comment #51
quietone commentedAh, found that vid:vid was still being used. I have removed them it the migrations are working for me, at least for my test cases.
Comment #53
masipila commentedManual Drupal 6 tests
Re #48: I'm not able to reproduce this issue anymore. I'm suspecting that I managed to screw up my D6 test data somehow in D6 which triggered this weirdness.
Re #49: I re-tested patch #51. My migration results with the nodes shown in #49 are quite weird but having a closer look in D6, it's even more weird there i.e. I somehow managed to trigger some D6 bug.
So I created completely new set of test data in D6 as follows:
English
Finnish
The D6-D8 migration results were all OK.
Drupal 7 / node translation tests
Results for D7 node translation tests were all OK.
Drupal 7 / entity translation tests
Drupal 7 also supports entity translation concept so I tested what happens with that.
In Drupal 7, the 'Revisions' tab shows a list of all revisions of the node
Entity translation results in D8:
I'm close to have a brain meltdown trying to think what does the node revision conceptually mean in the context of entity translation and languages, because the different fields may be translatable or then not. I would be tempted to say that this is the best that we can achieve but others may present better ideas if you're able to explain a concrete example.
Conclusion:
Cheers,
Markus
Comment #54
masipila commentedI queueud this to PostgreSQL again to see if we are now down to 4 errors like on MySQL and SQLite. I assume that the difference in the error count was due to the fact that the whole 8.7.x was not passing with PostgreSQL but that seems to be fixed now. Let's see.
Comment #55
masipila commentedI read the patch #51 on my way home today.
1. Question: Could you please describe the background why we needed to remove the vid:vid mapping? I can see that it was removed also from d6_upload.yml which wasn't obvious to me. Do we have something similar for D7 that should be taken into account?
2. I checked the test coverage. Could we add a test to D7 fixture for a content type that uses entity translation? Point being that this is complex and if the migration result ever changes because of some other issue, we would notice it?
Cheers,
Markus
Comment #56
masipila commentedRe: #54. Just as I assumed and hoped, the PostgreSQL has now the same amount of fails than other database backends.
Comment #58
quietone commentedFixing the failed tests requires changing the revision ids.
Comment #59
quietone commentedHmm, looked at the patch in #58 and it is not right, ignore it. This starts again from #51
Comment #60
quietone commentedStill to do is to address the question in #55
Comment #62
catchComment #64
quietone commentedRerolling the patch in #59 but excluding the D7 work because there are changes to the source fixture that do not apply and that is too much to deal with just now.
Comment #65
quietone commentedNow add the d7 stuff. Rerolling the d7 test fixture was a disaster so I remade it and removed many, but not all, of the unnecessary changes. I haven't run all the tests, so lets see what may fail.
Oh and no interdiff since this is just adding the D7 code.
Comment #67
quietone commentedMight have a working d7 fixture now. There is no interdiff since this contains an untrimmed version of the fixture. Reducing the size will come later once this passes tests.
Comment #68
quietone commentedTry again.
Comment #70
quietone commentedLet's try this fixture, it has been trimmed a bit as well.
Comment #72
quietone commentedTry again, only changes are to the fixture.
Comment #74
quietone commentedAnd update the new d7 MigrateNodeRevisionTranslationTest to use the correct text for the title fields.
Comment #75
quietone commentedUpdate IS
NW for the remaining tasks
Comment #77
quietone commentedThis is an attempt to add revisions to the test content type which uses entity translation. I expect lots of test failures.
Comment #79
quietone commentedRemove more changes from the fixture. Plus modify a test since the field field_text is now translatable.
Comment #81
quietone commentedThis trims the d7 fixture some more and add tests. Specifically adding to d6/MigrateNodeRevisionTest, d6/MigrateNodeRevisionTranslationTest, d7/MigrateNodeRevisionTranslationTest and creating a d7/MigrateNodeRevisionTest. No interdiff because of all the changes to the fixture make it full of noise.
Comment #83
quietone commentedDid some manual testing for d6 and d7. The d7 one created extra nodes and crashed when trying to view a node, so pretty bad there. The d6 one looks much better. I noticed the following problems.
1) The page content type is not translatable and it should be, it has translated nodes in d6.

2) The tag field in the page content type is a required field but the drop down has no selections so you can't enter data. I deleted the field to keep testing.
3) The revisions listed for the french translation of the page are in vid order which is not in the date entered order.
Comment #84
quietone commentedManual testing of d6 I discovered that the node revisions for node 1, which does not have translations, are NOT listed in the revisions tab. So, I deleted and started over this time without the translation modules enabled and ran the migration from the UI. Same results. What would prevent revisions from being listed in the revisions tab?
Comment #85
amateescu commented@quietone, looking at
\Drupal\node\Controller\NodeController::revisionOverview(), it seems that the revisions tab only displays the revisions which have therevision_translation_affectedfield set to1in the database. Maybe the migration process doesn't populate that field?If that's not the problem, can you give me some pointers (or a link to a docs page) on how to replicate your manual testing process? :)
Comment #86
quietone commented@amateescu, revision_translation_affected is set to true in the d6 and d7 node_revision_translation.yml. But there is a problem with the revisions without translation enabled. The d6 fixture has node 1 with 3 revisions and post migration only the latest one shows up on the revisions tab, node/1/revisions. That needs to be fixed first before adding the translations on top.
Right, so for testing. First, I just checked and didn't see a docs page for how to test. So, here goes.
php core/scripts/db-tools.php import --database=d6_dump core/modules/migrate_drupal/tests/fixtures/drupal6.phpScreenshots:

Drupal 6, node/1/revisions
Drupal8, node/1/revisions

This problem with the node revisions, without translation, should be moved to it's own issue. Hopefully, I will get to that before the end of the day.
Comment #87
quietone commentedCreated a new issue for the problem with the node revision tab without translations. #3073088: d6 term node migration changes revision log on wrong revision.
Comment #88
quietone commentedRemove changes for node revisions tests which are now in #3073088: d6 term node migration changes revision log on wrong revision
Comment #90
gábor hojtsyThis is a bit over my head to review. Can you update the issue summary in terms of what is the before/after migration scenario? There are a lot of data updates in the dumps to make this clear on review to me sorry.
What does "translation revision" mean here?
Comment #91
mikelutzI'm working on reviewing this, but It's going to take me a couple days. I have a pretty good understanding of what is going on, and my initial test show some things in the migrated database that don't seem exactly right to me. I'm working on seeing what we need to adjust to make it work.
Comment #93
quietone commentedItem 2 of remaining task: Could you please describe the background why we needed to remove the vid:vid mapping?
I removed that change because honestly I don't remember all the details. And it does not seem to have caused any problems. So removing from the remaining task list.
Comment #95
quietone commentedI am really tempted to split this into a d6 an d7 version because the d6 has the added complication of the effect of the term node migrations and then d7 has the entity translation which d6 doesn't.
Any objections?
Comment #97
mikelutzI think splitting them off is a good idea. I suspect removing vid:vid was necessary in d7 entity translation migrations because each vid has multiple translations, but we are trying to save them one at a time, which the entity API doesn't like. Problem with that is that we are then saving past revisions with higher revision IDs than default revisions, and that can cause problems in the system. We may need to go back to scratch and rework the source and destination
such that we can save a whole revision at once somehow.
Comment #99
quietone commentedTalked to mikelutz and heddn about this today and we decided to make a new issue for working solely on the d7 entity translation revisions. And that here, the priority is the D7 migration. It is much that same as D6 and since with D6 there is a possible complication with the term node migration it is best for right now to just get the D7 working correctly.
Comment #100
quietone commentedI've been staring and the revisions before and after a D7 migration and found there are 'extra' revisions made. Then I remembered the entity translation. Yes, d7_entity_reference_translation is migrating data for the article content type, which should not be using entity translation. Is there yet another problem with the fixture?
edit: Hmm, that is a follow up migration and is adjusting entity references. that is not causing the extra revisions
Comment #101
quietone commentedThis patch only changes the d7 migration.
Spent quite a bit of time running the d7 migrations in different order and looking at the resulting tables. For now, going with d7_node, d7_node_revision, d7_node_translation then d7_node_revision_translation. And I added vid:vid back to the node translation migrations and that seems to work for d7 where the current revision is the highest revision id.
One problem is the revision tab post migration were incorrect, the revision tab for the english node was showing an icelandic revision. Tracked that to the d7_node_translation migration which will creates two revisions for the one icelandic row and both have the revision_translation_affected flag set TRUE. The only place I could see to set that to FALSE for the english revision is in onPostRowSave in NodeTranslatoinMigrateSubscriber, which has to load that revision, set the value FALSE, and then save again. It is unfortunate to do that extra work.
The migration dependencies haven't been changed yet and the migration of a field that changes on the revision (field_text_plain) needs to be checked.
Comment #103
quietone commentedThe errors in the patch are all d6 related so that is fine. Right now just focusing on D7. Anyone reviewing, the next step is to check the results that are explained here.
Now to explain the results for d7.
The d7 source has 4 revisions, 2 'en' and 2 'is', with the following nid/vid.
d7 source
2/2 en first rev
3/3 is first rev
2/11 en 2nd rev
3/12 is 2nd rev
The d7 results has 6 revisions, 4 'en' and 2 'is'. They are shown below along with the migration that creates them. The translation migrations do not specifically migrate the 'en' versions, exactly where that is happening I don't know. There is only 1 row in the map table but two revisions are created. The 'is' translations and there fields are correct. The "extra" 'en' revisions are always the latest revision and have revision_translation_affected set to 0 or NULL so they don't display in the revisions tab.
d8 results
The values are nid/vid/langcode, revision, revision_translation_affected
Is this an acceptable solution? What problems might it cause?
edit: formatting
Comment #104
mikelutzThis is close, but not quite right.
There should be 4 en and 3 is when is all said and done. It should have
2/2/en/the 1st revision 1
2/3/en/the 1st revision NULL
2/3/is/the 1st revision 1
2/11/en/the 2nd revision 1
2/11/is/the1st revision NULL
2/13/en/the 2nd revision NULL
2/13/is/the 2nd revision 1
That being said, the entity api is really going to fight us trying to get that structure saved with the final default revision being saved first.
Comment #105
quietone commentedRight, that is what D8 does, which isn't possible to produce using the current way migrate works. Is it necessary to match the revisioning that D8 would do or is it sufficient to keep all the data and display it in the correct order?
Highlighting differences between what D8 does (as shown in above comment) and current migrated results
2/2/en, the 1st revision, 1
2/3/en, the 2nd revision, NULL ---> this is the the first revision in non migrated D8
2/3/is, the 1st is revision, 1
2/11/en, the 2nd revision, 1
2/11/is/the 1st revision NULL ----> missing from migrated version
2/12/en, the 2nd revision, 1 ---> revision translated affected NULL in non migrated D8
2/12/is, the 2nd revision, 0 ---> revision translated affected 1 in non migrated D8
edit: add comparison of results
Comment #106
quietone commentedSo, is the solution in #103 sufficient. mikelutz disagrees but local testing has convinced me it isn't going to get any better. Anyone is welcome to prove me wrong.
Getting all the revisions to match what D8 would do out of the box doesn't seem possible in the current environment. The dN_node_revision migration will migrate ONLY the default language revision, there will NOT be a translated revision with that vid. Whereas D8 doesn't do that. When I make a new revision of a node in the default language (en) and save, the result is that both a default language (en) and a translated (it) version are saved. That is why this translation "2/11/is/the 1st revision NULL ----> missing from migrated version" is missing in the example above. Now, functionally I think that will work, at least in my local testing.
I had an idea of another approach and discussed with mikelutz and heddn on slack. The idea was to not use the existing node, node_revision and node_translation migrations. Instead, we need to ask the user if they want to run multilingual with or without revisions and then when they want revision translation we use a new migration (or migrations) with a new fancy source plugin that does what is needed (details TBD) and unset the dN_node* migrations that aren't needed. That will hopefully give us the ability to save the revision translations correctly.
mikelutz thought this "would work just fine for the core one button upgrade" but that it wouldn't for custom incremental migrations.
But as heddn pointed out unsetting the dN_node family of migration is a problem. They are used in migration_lookups in core and in migration_dependencies. Most of those are dN_node so that problem could be alleviated if dN_node was still run.
Comment #107
quietone commentedHere is a version of the patch with just D7 changes. Let's get this one sorted then we can go back and update the D6 version.
This the the node_field_revision table post migration in the test node/tests/src/Kernel/Migrate/d7/MigrateNodeRevisionTranslationTest.php. If the same database is used as the source for running the migration from the UI, the results are likely to be different since the migration may run in a different order.
Comment #108
quietone commentedThe IS proposed that "for each D6 and D7 revision, the migration source should yield every translation that we think goes along with it. This means multiple rows per revision, which is slightly confusing. But it will provide a mostly-sane revision history.". I don't see how to make that work. First there needs to be some logic to determine which nid/vids goes along with a translation. But then you have to manage multiple node translations in a single Row and the mapping? Seems custom destination and idmap would be needed?
Comment #110
gábor hojtsy@quietone: so my mental model is languages are columns on a table where rows are revisions. When a new revision is saved, Drupal saves translations as well, so when you load the latest revision you get all translations in their most up to date form in that revision. If migrations don't save with this mental model into the revisions database tables then how does loading translations work?
I don't personally think we necessarily need to merge the revision rows creatively. The vids are globally consecutive (across entities) so all the migrations would need to do is to save revisions in vid order and rely on the entity API saving the other translations with the right value that was already available. That way merging various entities into one should result in the right order of revisions and all revisions should contain the most up to date translations at the time, no? Something like:
Node 1 French translation: revisions 4, 7, 12
Node 2 English translation: revisions 3, 8, 11
Node 3 Spanish translation: revisions 5, 6
(Node 1, 2 and 3 should all have unique vid values since it was not possible in D6/D7 to save a vid across entities).
So then we can order by vid and merge accordingly. Resulting merged node should contain revisions:
If we cannot/don't want the vid to stay the same, that does not matter in terms of the local history of the entity, we can renumber them. As long as we saved with the original vid order, we should merge them perfectly on top of each other, no?
Comment #111
mikelutzThis is what I mean when I say that the migrations should be combined into one, and we need to use the entity api to 'replay' the data in the node revision table in the same order it was created to avoid fighting the entity API. Let the entity API do the merging for us.
Comment #112
quietone commentedI have managed to run a migration by doing the revisions in order, just for the article content type in the test fixture. The results of that are the same as the results in #103 but not taking into account the revision_translated_affected field which I am not worrying about right now. The key thing is that when migrating a revision that is not a translation the result is different than when adding such a revision through the UI in D8. When adding a English revision in D8 the revision table will have a new English and Italian revision with the same vid. When migrating an English revision the revision table will have only a new English revision. As such, I don't see how it is possible to match the D8 structure exactly. I don't see it is possible to achieve the results in #104.
I have not tested with a source like the example in #110.
Now, I do agree that it is better to run this in revision order and would like to pursue that and see what problems we run into but first, do we agree with the results I am getting.
edit: Forgot to add that using the UI post migration the revision tabs are wrong.
Comment #113
masipila commentedI believe I followed the logic of #112 but would it be possible to have screenshots or diagrams that highlight the conceptual differences between D8 and D6/D7? For example similar diagrams that we have been using here:
https://www.drupal.org/docs/8/upgrade/upgrading-multilingual-drupal-6-to...
I mean, the amount of energy that has gone into digging into this is so exessive that it is worth summarizing it so that we can minimize the risk of needing get back to this monster and tear up the whole thing...
Cheers,
Markus
Comment #114
quietone commentedI'm not good at making diagrams and screen shots are not sufficient to show the differences. I have settled on showing the database tables for the source D7, D8 post migration and D8 made via the UI. What I made via the UI should reflect the Drupal 7 source. All this is in the attached text file.
Are we making the assumption that since the post migration database tables does not match what is done via that UI that it is broken? What if the differences are OK and the site works just fine? I admit I have done limited testing but it did seem to be working with the patch in #88. Has anyone else tried?
Comment #115
gábor hojtsyThanks for the details. I think this is a problem:
A. Your latest revision does not have an Icelandic translation. When you edit it does it even appear? How would Drupal know it was not intentionally removed? (There can be any number of independent changes in a revision).
B. Also, what happens when you revert back to revision 11, that also does not have the Icelandic translation. Ok it does not have French either but that was not yet supposed to have it. This B case may be solved with the revision translation affected logic.
I'll ask Francesco to chime in if he can as well, he knows how A and B expect data and where are the bounds of acceptable data.
Comment #116
gábor hojtsyRevert does not seem to be a problem, because revert only reverts the state of the translation being acted on, not the whole entity, as per @hchonov in #2766957-2: Forward revisions + translation UI can result in forked draft revisions https://api.drupal.org/api/drupal/core%21modules%21node%21src%21Form%21N... well, at least a node revert. How does loading/editing code with translations not being present in the latest revision?
Comment #117
quietone commentedand finally what I have been testing with. No point running tests but you are welcome to have a go migrating with drush.
There are two migrations to run to migrate the revision translations. d7_node_first_revision.yml followed by d7_node_revision_translation.yml. Both use the Node.php source plugin, which is modified to use some added configuration keys to make the desired query. It was the quickest way I could think of to test getting the revisions in the correct order.
Comment #118
hchonovIt is definitely a serious problem when a new revision is created, which does not contain all the translations. As seen in #114 the last revision contains only 2 instead of 3 revisions, which might considered data loss. Even if the data is in the database it is not present on all revisions.
There might be 2 reasons for that:
I've seen something strange that I don't understand in
\Drupal\migrate\Plugin\migrate\destination\EntityRevision::getEntity()where the new entity revision is flagged as a non-default revision. When an entity revision is saved in a non-default revision, then this revision will not be returned by$storage::load(), but only by$storage::loadRevision()with the rightrevision ID.In order to understand better what exactly is happening it would be better if every revision has an unique change on a specific translatable field - for example a counter. We need the source data and the migrated data with this counter. This should confirm whether we are retrieving the correct revision and using it for creating a new one.
Also could you please test by removing the following line from
\Drupal\migrate\Plugin\migrate\destination\EntityRevision::getEntity():$entity->isDefaultRevision(FALSE);---
I also don't see a place where we're actively removing translations, therefore I would say that for some reason the wrong revisions are being used as a base.
Comment #119
gábor hojtsyHow does migrations ensure that the default revisions are accurate and have all the translations and why can that not be used to do the same for revisions? They would need to save over each other for the various languages. In more discussion with @hchonov, he said one of the possible problems is "you cannot pick a revision and simply change the content language and see how this revision looks in a different language". Also while reverting a revision with the UI would work, using the Entity API could still lead to data loss.
Comment #121
mikelutzHere is the relevant proof of concept master migrations for D6 and D7. There is a lot of work left to do to actually integrate these into the migrations, and make sure we are handling non-translatable and non-revisionable nodes correctly. Additionally, this works with core translations, and would probably fail with entity translations. Those will be trickier, but I think should also be done in one migration in a similar fashion.
I had to adjust the D7 fixture as it's currently invalid, using node 1 vid 1 as the current default instead of vid 2001 like it should.
I dropped the test suite down to just the two migration tests, and put an export of the node_field_data and node_field_revision tables in the test files for both so you can see what the migration should end up looking like.
There may be some additional work around making sure the fields and everything are correct across revisions too, I was just worried about the main node tables being correct.
I think we can also fix content translation source to the default translation, we don't really have the concept of a translation source in d6 or d7.
Comment #124
quietone commented@mikelutz, thanks. That is much nicer, clearing I was on the wrong path. The removes any remaining files for that wrong patch and adds the assertRevision method to the d7 MigrateNodeMasterTest. That adds two things to the test that mike wrote, a) it load the revision to test the fields and b) it actually has a plain text field that has data that states the revision it is associated with. And that is encouraging.
Testing this with drush and it works very well. Although I do seem to be able to break it in some way that the is translations wind up on the revision tab of the english version. But I was also doing a fair bit of rolling back and remigrating so that may not mean a whole lot.
What definitely fails is running this from the UI. It is failing somewhere in the auditing done for the IdConflictForm'.
Comment #125
quietone commentedThis version has changes so that this would run from the UI. There are two changes.
One is cobbled together code in migrate_drupal_migrations_plugin_alter to not run d7_node, d7_node_translation and d7_revision. I'm still leaning to having a question in the UI that asks if they want to run multilingual migration with node revisions. Not sure about drush though.
The other change is to Sql::getHighestId() to prevent the exception shown in the previous comment. Original the getHighest method checked that every id had a type on integer. Obviously that doesn't work with a langcode id. (Which, btw has a type of 'language' - where is that defined?) Now, getHighest method should throw the exception only if an id with an integer type is not found.
Fortunately, the results are the as with drush.
The revision tabs post migration are not yet correct.
English revision tab
Icelandic revision tab
Comment #126
mikelutzThe reason that all revisions show up on both trans for node 2 in d7 is due to the untranslatable taxonomy fixed field. It's set in revision 2, removed in revision 3, and restored in revision 11 and removed again in revision 12. I can compensate depending on what that should is supposed to be doing, but based on that each revision affects each language, which is why they all show up.
Comment #127
quietone commentedAdd a few things to the migration. Add the Multilingual tag, a migration for content_translation_source, declare the destination module, add a dependency on language migration. MigrateNodeMasterTest is updated with the content_translation_source values and what I think are the correct values for revision_translation_affected. The latter causes the test to fail.
Edit: only changed d7, not d6
Comment #129
mikelutzThe discrepancies in revision_translation_affected for D7 node 2 is the same issue as the revision tabs described in #126. (And in fact are simply two descriptions of the same thing, as the revision tab display IS the revision_translation_affected)
The untranslatable field is only stored on the original node, and isn't pulled for the iceandic copy
The Icelandic translation added in revision 3 removes field_vocab_fixed, which is an untranslatable field. This mean that when we are add revision 3 in icelanding we are affecting the english translation, as we cannot modify field_vocab_fixed in one translation and not the other. Revision 11 has a value in the D7 fixture for field_vocab_fixed, so the field is reset in revision 11 (again affecting both translations) Finally it is removed again in revision 12 (still affecting both translations) .
I can't quite seem to figure out What is supposed to be happening here. field_vocab_localize and field_tags both also come across as untranslatable fields, but they have entries in the d7 fixture for both languages that happen to match, so they are fine.
Is there something in the configuration of field_vocab_fixed such that is only has a record in the default language and for the translations we should go get the value from the original node? If we can find the condition that declares it in the D7 database we can either adjust the source to include the field data from the original node (which the entity api will correctly detect as no change for English, and thus set the rta flags correctly), or we can set the untranslated field to match the original language in the destination, again causing the entity api to manage the rta flag correctly. I just need to understand how D7 is interpreting the field, and why it's different in the fixture than the other untranslated fields.
Comment #130
quietone commentedYes, the vocabulary fixed is problematic and behaves differently for me in D7 than in D8. I found that out when working on #3035392: Migrate vocabulary translations and taxonomy term references for Drupal 7 node translations. See Comment #18.
Comment #131
mikelutzInteresting. I'll fire up a d7 instance with the fixture and play with it and pick Gabor's brain on exactly what data state we want on that type of field. I'm certain I can adjust the master migration to handle it once I know what we want the d8 version of that type of field to look like. Otherwise, I'm really happy with how the one shot approach is working though. I definitely think it's the right solution to pursue.
Comment #132
quietone commentedAs per discussion with mikelutz on slack I'm going to look into fixing tests. This patch restores drupalci.yml and changes the @group on the two new MigrateNodeMaster tests. Now, lets see what fails besides the functional test.
Oh, and I did an IS update so removing that tag. Hope it is sufficient for now.
Comment #134
quietone commentedMove the check for a multilingual source database to MigrationConfigurationTrait.
Comment #136
catch@quietone
So for Drupal 7 field translation this seems like the 'simplest' option since the Drupal 8 entity structure is similar.
But... is it possible for that approach to co-exist with a Drupal 6/7 tnid-style source?
Comment #137
quietone commentedNow update the failing D6 Node tests to use the values in the current fixture. Move the node master migrations to content_translation. And coding standard fixes. That should fix a few more tests.
Comment #139
quietone commentedIn EntityContentBase instead of setting new revision true if the entity is revisionable set it if it revisionable and IsNewRevision. Found this while looking into the MigrateUploadTest was failing, where on import it was making a new revision when the content type has new_revision as false.
@catch, not really, and instead of that method we are trying what mikelutz proposes, in #121.
Comment #141
plach@Gábor Hojtsy asked my feedback on #114.
I just went through the whole thread (I didn’t read any code though) and I agree with the issues @Gábor Hojtsy and @hchonov brought up. In general I don’t think we can avoid matching the D8 data model strictly: a missing record would mean that the related translation does not exist in that revision. Since we don’t track parents, that may either mean that the translation was removed or that the “parent” revision had no such translation in the first place. In any case that outcome would be wrong, since the translation should always be there, as we wouldn’t be migrating it otherwise (the entire d6/d7 node would have been deleted in that case).
The
revision_translation_affectedflag acts as a poorman’s “parent” field, as it allows to reconstruct the translation history by connecting the revisions affecting it.Comment #142
quietone commented@plach, thanks for the input.
This patch has more test fixes and add brings the d6 migration and d6 tests up to date. And hanged the MigrateNodeMasterTests to be easier to read, at least that is the intention.
Comment #144
quietone commentedI have found a problem with this approach and the idAuditor. The auditor will search for the highestId() on the destination and compare that to the highestId in the relevant migrate_map table or tables. Because the destination of dN_node_master is node, we are not checking the node revisions and we can't inform the user if they existing revision ids that will conflict with the ones to be migrated.
Comment #145
quietone commentedHere is a hack that adds auditing code to IdConflictForm so that it will audit node revisions.
Comment #147
quietone commentedOK, I was wrong earlier about the tests being easy to fix. This is a bit of a rabbit hole. Looks like we will need to test fixtures per Drupal version, one to be used for i18n and one not. And when it is i18n use the node_master migration and not run the others in the dN_node family, and alter all the processes and dependencies to use node_master, and then add a new process to account for the fact that node_master returns 3 ids not 1 like the dN_node family. Fun eh but possible.
All that does nothing to get the revision_translation_affected field to be correct.
Comment #148
quietone commentedThis needed a reroll because of changes to the fixture in #3031727: Migrate D7 synchronized fields. That change uses the field_text_plain field which is also used here. To avoid the conflict I changed this patch to use the field, field_text_long_plain. And then, of course, the MigrateNodeMasterTest needed to be changed.
Comment #150
quietone commentedFor some reason this reroll is giving me a headache.
Missed some changes to the test fixture.
Comment #153
alexpottHere's a fix for the failing kernel test. Note I have only fixed the test - I've not checked that the test assertions are actually correct.
The migrate UI upgrade fails are interesting - the test could be more helpful and tell us what migrations are failing. Here are screenshots of what is going on the D6 migrate upgrade UI test:
Comment #154
alexpottHere's my patch :)
Comment #156
quietone commentedThis just adds some comments.
Yes, that is a pain and should be a new issue.
For d6 I'm showing failures in these migrations, Books, Comments, File uploads, Term/node relationships, Term/node relationship revisions.
Edit; new issue made #3082211: Migrate UI upgrade tests should provide the complete log
Comment #157
quietone commentedTo make sure that dN_node_master was being used everywhere for multilingual I tried altering all the migrations. Seems to work locally.
Now alter any migration_lookup in core that has a source migration of dN_node* to use dN_node_master and add a plugin to that pipeline that will retrieve the desired IDs. That is needed because dN_node_master has 3 source IDs but dN_node has 1 source ID and dN_node_translation has two. For example, a lookup on d7_node will return the nid but a lookup on d7_node_translation expects two ids to be return, the nid and the language. The new process plugins handle that.
Comment #160
quietone commentedSome fixes and code cleanup. Only alter migrations that are not dN_node family.
The changes so for for migration_lookups won't work. More on that later - I have to run to catch a bus.
Comment #162
heddnI'm concerned that we might run into BC issues. If we didn't have to concern ourselves w/ existing migrations, this would be a lot easier.
That said, the proposed solution is probably the *only* path forward. And it might be a good reason to keep the
migrate_drupal_multilingualmodule though as the way to tell core and migrate_upgrade to use the master migration in place of the existing "normal" migrations.TLDR; +1 on architecture. Need more discussions on actual implementation.
Comment #163
quietone commented@heddn, thanks good to have confirmation of mikelutz's node_master work.
Using migrate_drupal_multilingual to tell core and migrate_upgrade to use the master migration in place of the existing "normal" migrations is perhaps changing the meaning of that module. Right now that module only says 'this is multilingual' whereas for node_master we need something that says 'this is multilingual with revision translations'. Now for core UI that may be fine, after all the idea is to migrate a copy of the original site which should include revisions. That's OK, just want to be clear on the point.
But the real challenge is the existing migrations. Hopefully it is limited to the subset where migration_lookup on dn_node/node_revision/node_translation migrations. I think we need to alter those migrations to use a new smart wrapper around migration lookup that will check both the dn_node/node_revision/node_translation table or dn_node_master to find the destination ids. We could avoid altering those migrations by creating 'copies' of the affected migrations and change migration_lookups to use the new smart wrapper around migration lookup and do some unsetting of migrations to remove the ones we don't want to run. Make sense?
Are there other cases where the existing migrations will fail?
Comment #164
mikelutzYou know, random thought. I didn't actually get round to building anything out for #3061676-2: Create an audit plugin class/manager for migrations, but theoretically, that might be an excellent place to detect the existence of revisions+translations in d(6|7), ask the user if they even want to migrate revisions, and if so, disable the standard node migrations, enable the master one, and even tweak any other dependant migrations that might need a process adjustment.
I think migration_lookup issues will be helped by #3004927: Create Migration Lookup and Stub services and possibly a more advanced lookup plugin based on that service. The service has better handling of lookups with a subset of the source keys, and would be useful if we are running a master migration and need to lookup based only on nid for example.
Comment #165
quietone commentedYes, I am looking forward to the migration lookup service.
This is a better way to do the migration lookup, a process plugin that will check both the original dn_node migration and if not found check the dn_node_master. It should be smarter and check the migration property so that if it is dn_node_master it checks that first.
Comment #167
quietone commentedAnd this is an even better way to change the migration_lookups. The Upgrade7Test now has two failures, d7_book and d7_comment. I haven't run Upgrade6Test locally.
Comment #169
quietone commentedI accidentally removed all the code in the migrations_plugin_alter that was altering the destination and the dependencies. This restores that code. And a change to the _isMultilinugalSource function in used in the alter so that it checks that the definition of the migration 'system_site' exists before using it.
Comment #171
quietone commentedAdd the patch from #3082719: migrate_drupal_migration_plugins_alter() should only alter definitions that exist for a problem I discovered while working on this. And fix coding standard errors.
There will still be failures.
Comment #172
quietone commentedI made a small mistake in that patch.
Comment #174
quietone commentedOne way to fix the Kernel tests is to create a copy of the drupal6 and drupal7 test fixtures and change the status of the 'i18n' module to 0/uninstalled. That way the master_node migrations are not used and the test can proceed as it did before. It would be nice to use migrateDumpAlter() to do this but that happens after the migration_plugin_alter. I've only changed the source fixture on the failing tests. This may not be the best solution but it is a step forward.
The interdiff excludes changes to the fixtures.
Comment #176
quietone commentedWell, the Kernel tests are passing now, that just leaves the Functional tests.
Comment #177
quietone commentedNeeds a reroll.
Comment #179
quietone commentedFix d7/MigrateNodeRevisionTest.php and remove unused use statement.
Comment #181
quietone commentedNow, back to fixing the functional tests. The Upgrade7Test looks straightforward but I need to bring up a D7 site and run the full migration to compare results before making a new patch. Unfortunately, Upgrade6Test is failing on an error I don't recall seeing before so will take some time.
Comment #182
quietone commentedThe d6 error is happening here
and is related to comments which is a nuisance as I haven't worked with the forum or the comment migrations before. Anyone have some pointers?
Comment #183
quietone commentedOK, D7 failures are from followupMigrations. So make sure the followup migrations run when the node_master migration is being run.
Comment #185
quietone commentedThat fixed Upgrade7Test leaving one failing test, Upgrade6Test.
Anyone keen to test this with Drupal 7, go right ahead. You will have to use the UI for testing.
Comment #188
quietone commentedI have run the failing test, Upgrade6, 3 times over the past few times and it always passes. I do not know why it is failing with the test bot.
I have also run the upgrade manually, setting up the D8 site the same as in the test. Again, the migration complete successfully and the Forum node display correctly with all the comments.
I am going to start a retest, not that I expect anything different. Ideas welcome.
Comment #189
quietone commentedYicks new disk and rebuild most of dev environment and Upgrade6Test takes way too long, "The test run finished in 1 hour 21 min.".
It does fail locally. Now to run it again an preserve the tables.
Comment #190
quietone commentedRan the test again and saved the tables. No migrate messages in forum or comment migrations and nothing in watchdog either. Anyone have a suggestion?
Comment #192
quietone commentedAh, now have a failing migration test for the problem. No solution, just the test.
Comment #194
quietone commentedTrying to fix formatting of list in IS with help from tbradbury
Comment #195
quietone commentedYes, it was d6_term_node causing the test to fail. This patch should fix it by going back to the way the migration lookups were done in #160. If you read the code the comments are not all updated - I've had a tough day with my local test servers losing their ip address in the middle of running tests.
Comment #197
quietone commentedRight, of course, can't change all the processes to use node_master because the tests run with a multilingual source so the pipeline needs to change so the migration tests and the Upgrade tests work. So, let's try this.
Comment #199
quietone commentedFix two typos.
Comment #200
quietone commentedIt is a day for typos. Found another one.
Comment #203
quietone commentedFix entity count on Upgrade 7 test.
Comment #205
quietone commentedMore that disappointing that Upgrade6 is still failing. Any help appreciated.
Comment #206
quietone commentedSince node master will be the default we need to check if a migration has already been run on this site using the dN_node. Just checking if one has run isn't sufficient because it would prevent the running of future incrementals using node master. Instead check the counts in the migrate_map tables for node and node_migration.
I'm looking at the comment tests for the failure in Upgrade6.
Comment #207
quietone commentedLots of failing tests so I aborted the test run. Just add a check that the table exists before accessing it.
Comment #209
quietone commentedI learned about schema()->findTables so that is now used in migrate_drupal_migrations_plugin_alter to help determine if the master migrations are to be used. But then there was still no way to know if the master migration was to be used in a kernel test so nothing better came to mind other than making a module (sort of like migate_drupal_multilingual). I've been having a bit of a bother running functional tests so will have to use the test bot.
Comment #211
quietone commentedA few small adjustments to correctly use the master migration in the functional test.
Comment #213
quietone commentedThe message table, d6_node_master__story, has "Invalid translation language (und) specified. (/opt/sites/d8/core/lib/Drupal/Core/Entity
Seems that 'und' is not valid for story content type. Don't know why.
Comment #214
quietone commentedStepped through Upgrad6Test on HEAD and found that the language for node/1 is 'und' in the destination but somewhere in the import process it gets changed to 'en'. With this patch that does not happen and the language remains 'und' and fails later when forum.module tries to access node/1, which failed to migrate.
Comment #215
quietone commentedNow with the failing test:
So in EntityContentBase it checks if this is a translation destination, which it is. And when
$entity->addTranslation($language)is executed an InvalidArgumentException("Invalid translation language 'und' specified.") is thrown.The difference is that without this patch when node/1 is imported the destination is from d6_node which is not configured as a translation destination.
This is happening for the story content type, not for the other types. Why? Questions questions...
Comment #216
quietone commentedIn SqlContentEntityStorage::mapFromStorageRecords the bundle for node/1 vid/1 is set to page when it should be story. No idea why yet.
Comment #217
quietone commentedThere is an existing entry in the node table before any migrations start. I didn't expect this but finally the light bulb went off. Before the test starts content is created \Drupal\Tests\migrate_drupal\Traits\CreateTestContentEntitiesTrait::createContent(), for both Upgrade6 and Upgrade7, to allow checking for existing conflicts.
I don't have the whole picture yet but for Update6, it is something like this. On import of vid/1, type 'story', it is discovered that there is an existing vid/1, type 'page'. The existing vid/1 is loaded and the to be imported row is set to type 'page' and then it is treated as a translation. The saving of the translation fails because 'und' is not a valid translation language.
What makes Upgrade7 different? One, there are no nodes in the fixture of type 'page' and two, nid/1, vid/1 is of type 'test_content_type'. After that I am not sure why it doesn't fail as well.
Comment #218
quietone commentedFirst, a reroll
Comment #219
quietone commentedNow, lets try making each revision a new revision. Seems worth a try and I am about to leave for night class.
Comment #222
quietone commentedIgnore #219.
This starts from #218 and changes d6 and d7 MigrateNodeMasterTest to create content and thus will show the fail. Finally.
Comment #224
quietone commentedSo, existing content on the site is causing the failure. When there is existing content on the site and a vid of the existing data is the same as a vid of an imported row, that row will fail. As will all rows that have that node id.
One thing that happens is that the content type is being changed, the same is happening in D6 but it tries to add the row as a translation so there is a different error message than the one show here for d7.
Right now I don't have a fix in mind but it is late. If anyone has ideas, please speak up.
Comment #225
quietone commentedThinking about this I thought the best way forward was treat the upgrade as we always said, that it was to be to an empty D8 site, minimal install. Since then I've chatted with mikelutz in Slack who come to the same conclusion.
This patch begins to do that, to delete the added nodes. It is done in the MigrateNodeMaster Kernel tests and started in the functional tests. The Upgrade6 and Upgrad7 tests will have failures because of the id conflict. Those functional tests are not easy to change and, well, cleaning them up needs its own issue.
edit: tried to improve the formatting in the Slack discussion
Comment #227
quietone commentedCleaning up the failing tests.
Comment #229
quietone commentedBother, the namespace for the tests got mixed up. Also removed some testing that does not need to be done here.
But did you notice that Upgrade6Test passed. Yea!
Comment #230
sinasalek commentedI check this issue on daily basis. Great job @quietone can't wait to see this land
Comment #231
quietone commented@sinasalek, thanks. Some testing of this would be most welcome. Are you able to do some and report back?
Just removing a file that I thought would be needed but isn't and I forgot to delete it and adding a review only patch.
Now that we have passing tests, I want to update the IS to explain the changes. I think the patch here should be merged into the entity translation revision issue as well, to make sure all is well there.
Comment #233
quietone commentedNow, remove test files. There were made to help chase down problems. Often while hunting for the cause of failures in Upgrade6Test and while using the state of the i18n module in the source to determine the migration is of type master or not. No longer strictly needed. Of course, we can always add them back.
Although maybe we need a test with node master.
The interdiff is > 3MB but here it is anyway.
Comment #235
quietone commentedWhile reviewing the files in the patch to find test files no longer needed I made a list (I think in lists). Here it is for your reading pleasure.
/migrate/src/Plugin/migrate/destination/EntityContentMaster.php and
/migrate/src/Plugin/Derivative/MigrateEntityMaster.php
master
migration and b) change the processes that do a migration_lookup on d*_node to also use d*node_master.
migration
dependences and b) change the processes that do a migration_lookup on d*_node to also use d*node_master.
d*node_translation use nid and d*_node_revision uses vid.
and
NodeMasterNodeTranslationLookup.php
d7/MigrateNodeRevisionTest.php
d7/IdConflictTest.php
Edit; trying to fix formatting
Comment #239
quietone commentedMissed removing some changes to a test file
Comment #240
alexpottRerolled and removed an unneeded file.
Comment #241
alexpottPartial review and patch addressing some of it.
This needs a comment. It looks tautological ie. if new revision make it a new revision.
ID. Fixed in patch.
Let's mark this @internal and use Drupal naming standards. Fixed.
Let's mark @internal. Fixed.
FIXED: These should be constants.
As far as I can see this state value is never used. Do we need it?
Comment #242
quietone commented@alexpott, thanks for the review and fixes.
#1. Ah, that one. I forgot to come back to it. That was meant to be a temporary for what I found in #139. For now, I have added a comment and todo.
#6. I think you are right. It was an earlier idea I played with. It is removed now.
So, let's confirm there are no errors with the removed code (#6). In the meantime I will look at the failing test related to #1.
Comment #243
quietone commentedNow, remove the new code mentioned in #241-1 and let's see what errors we get.
Comment #244
quietone commentedTests are passing, a good sign. This removes all changes to EntityContentBase.php that were introduced in this patch, which was simply to make a new revision whenever the entity is revisionable in updateEntity.
This was causing failures in d6/MigrateUploadTest because it was making a new revision for each migrated node. When it saved the nodes of type page the version id was changed and then when the upload migration ran, the vid no longer matched the source and we get the error "Update existing 'node' entity revision while changing the revision ID is not supported."
Comment #245
alexpottFIXED: Looking at the code I don't think this can ever return false.
FIXED: We can simplify this a bit
FIXED: This can be made a little bit simpler in terms of number of loops and conditions.
These alters concern me. Doesn't this mean that contrib and custom are going to have to do a lot of work to support this? And what happens if _use_master_node_migration() detects that they should be using MASTER migrations but they got contrib / custom migrations that depend on the old ones?
Clever and nice to see. One question is shouldn't we be able to test for a better migration when using the d*_node_master migrations. Or are we proving here that the migrations are equivalent in the simple case?
It is really interesting that the current d*_node migrations work fine with existing content but this new one doesn't. What's happening on HEAD atm? At the very least I think this needs a comment.
Comment #246
quietone commented@alexpott, again, thanks for the review and cleanup.
245.4. Agree, this needs explanation. One thing I will point out now is that the altering is done to add node_master to the migration lookups. Migration lookup will search the map tables for all listed migrations until it finds a match and return that value. Core already uses that feature in some migrations.
245.5 This test is just proving that the FollowUpMigrations work with node master, nothing more. I am not sure what you mean by a 'better' migration but there is a question to answer about what to test both ways. Do we change all the migration Kernel tests to add a test with using node_master? That will be a big patch itself. Better, what subset, if any, do we want to see here? The Functional test of the full migration should be using node master.
245.6 This is because the full upgrade test has had everything thrown at it and it is doing to much. In this specific case, it is adding content to test the ID conflict form. That should be a separate test and is being done in #2974445: [Meta] Refactor Migrate Drupal UI Functional tests, which would be better to get committed before this issue. There is also an issue to refactor all the functional tests in migrate_drupal_ui, which I've finally started poking at. Comment added.
TODO:
254.4. More explanation needed
254.7 Add CR
edit: change formatting of 'add' to emphasis
Comment #247
quietone commented254.4. 245.4. I think there are 4 cases to consider, migrations as configuration, running migrations with migrate_run or migrate_manifest, a one off migration via the UI, and incrementals via the UI. Did I miss one?
This key here is the function _use_master_node_migration and method MigrationConfigurationTrait:useMasterNodeMigration which determines if this is a master or not master migration. They search for tables named migrate_map_d*_node* but not migrate_map_d*_node_master. If any are found then, and only then, will the migrations be altered.
Just as finished writing that I realize there should be a test of the not node master incremental, another version of Upgrade6Test and Upgrade7Test.php.
Oh and how about instead of NODE_MIGRATE_TYPE_NOT_MASTER we could use NODE_MIGRATE_TYPE_CLASSIC?
Comment #248
heddnI like
NODE_MIGRATE_TYPE_CLASSIC.In regards to 245.4, this shouldn't effect
migrate_upgrade(and migrate_tools/migrate_plus config entities) because the alters to the migrations would happen prior to the config entities being generated. So we should be good to go there. Some feedback below:I don't really like these as .module file constants. But I can't come up with a better location to store them. Maybe new interface in node module?
Could we encapsulate all this into a new utility class? Then we could add the constants to it from above and have something self contained that could be utilized by others (contrib/custom), if it is needed.
It would also make things more testable.
The wording here seems complicated. Not sure I follow it. Might need some extra commas.
being used, get the audit
Instead of inserting all of this directly into the form, can we create a new class, say NodeMasterIdAuditor to encapsulate all this logic? We should probably add an auditor manager at some point, but that's a bigger change.
an entity_revision...
Could we add some more comments (with an example) of what should and should not pass this check. I'm not sure what is the expected pass/fail of these conditions.
This fancy variable logic could use a comment.
Comment #249
quietone commentedCool, another review! thanks.
Comment fixes only. #248: 3, 4, 6, 7 and 8.
Comment #250
quietone commentedNow changes for #248: 1 and 2. Not sure about these changes nor the names I've chosen.
Still to do 248.5.
Comment #251
heddnNW for 248.5. And a little feedback.
I'm hesitant to put this in the general namespace. Probably in Migrate Drupal or Node is fine. It would be interesting though if Node module isn't enabled, so I'm not sure how that would all shake out. And we probably don't need an interface as I can't think of any scenario where we'd want someone else to swap it out. In fact, the whole class can probably be Final since we don't want folks changing the logic.
This is all about node migrations, so why not call the class NodeMigrationType.
Here and elsewhere, inside this class, the constants can use
static::I believe.Comment #252
quietone commentedYes, that is a better idea. This patch moves that work, now called NodeMigrationType, to migrate drupal. I originally moved it to Node but then Kernel tests that were not in the node path were failing. For example. core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemConfigurationTest.php, failed with
I'd appreciate an explanation of why the class is not found when it is in the node module. And, if for some reason we really wanted it to be in node, what would be the way to make it work?
Comment #254
quietone commentedI'd say this would fix the tests but I found a problem in the plugin alter in that the destination was not being changed from 'entity:node' to 'entity:node_master'. This patch fixes that. I also added more tests to MigrationPluginAlterTest, including testing the destination and the migration_dependencies. Also, some comment updates.
Comment #256
quietone commentedSo, now we have migrate_drupal_migrations_plugin_alter doing what it should be doing and some better testing on it.
The failing tests are coming from the followup migrations, dn_entity_reference_translation.yml. That migration uses the d8 content_entity source plugin which returns one nid with an array of vids and passes those values straight through the pipeline. Not surprisingly, the destination plugin, EntityContentMaster, can't handle an array of vids. My first thought is to change the source plugin to return a unique row for each nid/vid/langcode. Is there a better approach?
Comment #257
quietone commentedTaking a close look at the d8 ContentEntity source plugin it was easy to add the revision id to the existing ids, and to do so only for revisionable entities. ContentEntityTest.php is changed to check for the revision id. So that piece is done. That can move to it's own issue.
But first, lets see what this does for the failing tests.
Comment #259
quietone commentedLooking further into the followup migration and I learned that because they are created after all the migration run, they do not get passed to migrate_drupal_migrations_plugin_alter(). There is the choice of adding the altering code to the EntityReferenceTranslationDeriver or having the driver call migrate_drupal_migrations_plugin_alter. I chose the latter so they use the same code.
Fixed a bug in the regex for replacing the classic node migrations with master node migration such that the d6_node_type destination was being changed. That is in the migrations_plugin_alter. And NodeMigrateType::useMasterNodeMigration() now correctly identifies the followup migrations.
Added two new test for testing the FollowupMigrations. New tests are needed so that the test module, 'node_migrate_master', is installed before any migrations run. Otherwise the migrations in the parent::setup() method run and there are failures.
Note: NodeMigrateType::useMasterNodeMigration() needs a name change since it returns a string not a boolean.
Comment #261
quietone commentedOne coding standard fix and fix for the failing test d7\FollowUpMigrationsTest. The other two failures will have to wait.
If you are coming here to test, the migration test for Drupal 7 sources are passing, so you are welcome to test that. The failing test d7\FollowUpMigrationsTest does not use the node migrations that the node revision translation does, so it is unrelated.
Comment #263
alexpottOne test fix.
Comment #265
quietone commented@alexpott, thanks. Unfortunately, while that test passes when node_migrate_master is installed, it needs to be run both with and without it. I'm hope to redesign that test today.
Comment #266
quietone commentedJust confirming that the remaining failure only affects Drupal6 sources. They are about the term node migrations, no surprise there.
Testing with Drupal 7 source is worthwhile.
Comment #267
quietone commentedAdding a test to show an error in d6_term_node.
Comment #269
quietone commentedLet's not alter the destination plugin.
Comment #271
quietone commentedYep, no need to change the destination plugin, that was a mistake.
This is a rewrite of MifrationPluginAlterTest that is hopefully a bit easier to understand. I think there are sufficient test cases.
And I did a bit of renaming and updated doc blocks.
Comment #272
quietone commentedTesters - use this patch. Yes, despite the failure. There is a quick fix in here and the failures are expected.
Sorry for any confusion. The problem is in the logic of deciding if the migration should be of type master (the new one for node revision translations) or classic. And I have been testing so much with kernel tests that I didn't catch this. A proper fix is needed. This patch forces the migration to be the master type.
Comment #273
quietone commentedClarify testing instructions
Comment #275
quietone commentedLooking over the issue I found an item not addressed.
1) This makes a new class as per #248.5
Comment #276
quietone commentedDeciding if the node migration type is class or master is complicated by the fact that on a fresh install and in a test there will be no migration tables to aid the decision. When that happens via the UI we want to use the master migrations. But, when in a test, the test has to be able to decided which to use. I have finally gone for a not elegant solution where the test has declare the type of node migration. It does this by enabling one of two testing modules, node_migrate_classic or node_migrate_master, that are hidden and should not interfere with real migrations. Let's see what breaks.
Comment #278
quietone commentedHere are results of a simple test with a D7 source. One node, 3 languages, a few revisions, including a revert in each language. I didn't notice any problems with the node migration.
There was one error with the migration, 'The "block_content" entity type does not exist.', something I have not investigated.
Comment #279
gábor hojtsy@queitone: sounds like the block_content problem should not be related unless this is applied to blocks?
Comment #280
quietone commentedGábor Hojtsy, I agree it is not related. I want to do an incremental migration next.
Comment #281
quietone commentedThis test includes an incremental and a revert. Again, just one node. Migrated the content, then added a revision to each language, en, it an pl, and a reverted pl to the first revision. The revision tabs for each match the d7 source.
Comment #283
quietone commentedThis needs a reroll since #3086238: getHighestId() should not fail when there is a destination id with type string was committed.
Comment #284
quietone commentedDid manual testing with a multilingual database (en and zh-hans) that does NOT have revisions. The D8 site was a minimal install.
The errors were
This was from the IdConflict form login. I commented out that getHighestId() method in order for the migration to run.
After that the migration ran with no problems. The errors logged were about missing files (which I don't have), filter_null, ' Attempt to create a field without a field_name' (looks like one per content type).
Of those, getHighestId() needs to be fixed and I want to check which field did not have a field name.
The good news
The migration created tables only for d7_node_master and not d7_node, d7_node_translation and d7_node_revision.
Visuals checks of the source and destination node and node revision tables are OK.
Able to edit and save translated nodes.
Comment #285
quietone commentedAnother reroll since #3073050: Migrate D7 i18n taxonomy term field translations was committed.
Comment #286
Madhura BK commentedHi,
I wanted to test this issue. So I started with the steps mentioned in "For Testers".
I have 8.9.x-dev version with minimal installation profile and I tried applying patch in #276.
But it does not apply.
Comment #287
alexpott@Madhura BK here's a new patch - the very last commit on 8.9.x conflicts with this one as it is a small commit to make getting this one in a bit simpler. Also there is no need to upload an image of patch not applying - press the retest button the patch in #285 and that would tell everyone too...
Fixed some coding standards on the way too.
Comment #288
Madhura BK commented@alexpott,
Can you please let me know on which minor version of drupal 8 I need to apply this patch.
I have tried applying patch #287 on Drupal 8.8.0.I get the following error:
error: patch failed: core/modules/migrate_drupal/tests/fixtures/drupal7.php:9037
error: core/modules/migrate_drupal/tests/fixtures/drupal7.php: patch does not apply
error: core/modules/migrate_drupal_ui/tests/src/Functional/d6/IdConflictTest.php: No such file or directory
error: core/modules/migrate_drupal_ui/tests/src/Functional/d7/IdConflictTest.php: No such file or directory
On Drupal 8.9.0 I get these errors:
error: core/modules/migrate_drupal_ui/tests/src/Functional/d6/IdConflictTest.php: No such file or directory
error: core/modules/migrate_drupal_ui/tests/src/Functional/d7/IdConflictTest.php: No such file or directory
Comment #289
alexpott@Madhura BK you can see from the test run that this can be applied against the latest 8.9.x branch. There is no 8.9.0 as that has not been released yet.
Comment #290
quietone commented@Madhura BK, thanks for starting to test this patch. I have correct the IS here so that it states to use the latest patch. You can also find support for testing this on Slack, #migrate. Just ping me.
Comment #291
quietone commentedThe errors encountered in #284 are not related to this patch. They occur when testing without this patch and with the test fixture. See #3101438: getHIghestId queries tables that do not exist.
Comment #292
Madhura BK commentedHi,
I have set up and run the migrations following the steps in "For Testers".
Comment #293
wim leersThis would really benefit from a change record.
(I discovered this earlier today, #2746517-15: Automatically derive auxiliary migrations pointed here as a reason to not do that issue anymore. Reading a >100 KB patch and a very long issue summary where it's not entirely clear what the BC implications are makes it hard to jump in and participate. A change record showing the current state of what changes would be communicated to the general public would help in gaining an understanding and participating here 🙏
Comment #294
quietone commentedYes, this is complicated.
I have just started a CR but I have a bit of a headache so not making much progress.
Comment #295
firewaller commented+1
Comment #296
wim leersPartial first review. Will continue tomorrow. Have also started actually testing this, but no conclusions/remarks for that yet. Below are mostly nits, but there's unfortunately a lot of them, that's why I'm already posting this partial review.
🔎
altered..→altered.🤔😅 I don't know what this means. Could we rephrase this slightly so that people who aren't migration system maintainers also understand it?
🤓 Nit: this can/should be
elseif, or perhaps better still: aswitchstatement?🤓 Nit: why not
MigrationInterface[]?👎 Let's refer to this consistently — not "legacy Drupal version" in one place and "version of the source database" in another.
🤓 Nit: Shouldn't this say
One of NodeMigrateType::…, NodeMigrateType::… or NodeMigrateType::….?🗣 Nit: "set it the default value" → something wrong here.
(🗣 = English writing problem.)
🗣
🤔 This can be simplified.
🤔 Huh?
🗣 "Indicates of" sounds pretty weird to my non-native speaker ears.
🗣"… array of migration."
🐛 This should be
static, since it doesn't use$this, nor can it, because it's a trait.🐛 This TODO needs to be resolved still.
🤯 This definitely needs a comment. Dark magic! 🧙♀️
🤯 Wait, so first we do check if
system_siteis in the list of migration definitions (which is pretty complicated), and if it isn't we check the follow-up migrations? Why is that okay? Why only these specific two?🤔 The second bulk of the logic in this method simply may or may not run? This method is really begging to be split up into multiple methods to make it easier to understand. 🙏
🤔 How can it be both? Aren't we very much trying to avoid it being possible for both migrations to run?
(Also: missing trailing period.)
🙈 This is some really weird code formatting.
🤔 Why is it okay to special case one particular deriver and then hardcode the invocation a specific alter hook implementation?
🗣
calssic→classic🤓
..→.🗣"without an extras modules installed"
Comment #297
quietone commentedThis patch has fixes for #296 1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 15, 19, 21, 22, 23. There are mostly the comment improvements.
todo: 9, 10, 13, 14, 16, 17, 18, 20
For #9. I happen to like the explicit nature of those statements. What would you prefer?
Comment #298
wim leersThanks for the #297 interdiff, a lot of really solid comment improvements that I hope you agree will help future maintenance :)
Also, I've changed strategies: rather than pointing out nits, I'm raising them but fixing them myself, so that you can focus on the actual interesting questions (well, hopefully they're good questions 🤞!).
Interdiff review
🤓 s/mode/node/✅
🤓 s/ids/IDs/ (It's written this way everywhere else!)✅
🙏 NOW I ACTUALLY UNDERSTAND THIS! Thank you, great comment!
🤓 Indentation nit.✅
🤔 Why are we copy/pasting some code instead of calling that existing code?
🤓 s/This/this/✅
🤓 s/follow up/follow-up/✅
Continued patch review (again partial, still going!)
🤔 This comment doesn't match the one in
d6_node, whereas the equivalent comments ind7_nodeandd7_node_masterare identical.Is there a reason for this deviation?
🤔 In
d*_nodethis isWhy is it different here?
🤔 This is the only additional field that is mapped compared to
d6_node.But
d6_nodehas the following comment:Why are we not retaining this comment?
🤓 Comment is wrong.✅
🤓 s/ids/IDs/ and 80 cols.✅
🤔✅ IMHO this code would be easier to understand and maintain if this would be moved into a helper method, because there are two different kinds of "highest ID" being computed here.Implemented that, because I don't think this is going to be controversial.
🤔 Aha, so the goal here is to prevent node revision ID conflicts (which could happen if after the migration, more revisions were created on the destination).
Should we clarify this in the class doc? And presumably add a similar comment to
IdAuditor's class doc?Because is super abstract. With only a single auditor in core, there was less of a need to explain things clearly, but with multiple, the distinction needs to be crystal clear.
🤓 Let's use FQCN.✅
🤔 "Master destination". I don't know how to interpret this. "Master" as opposed to what?
Left a comment at #2937782-26: Create trait for getDefinitionFromEntity and raised attention with a core committer.
EDIT: this got committer attention, addressed their concerns in #2937782-28: Create trait for getDefinitionFromEntity and #2937782-29: Create trait for getDefinitionFromEntity 😊
Comment #299
wim leersSorry, didn't get much further due to other obligations. Posting what I have for today.
🤓We usually format this differently.✅
🤔 The overriding of the new revision flag would be a good thing to document I think. I haven't been able to figure out the "why" behind this yet.
🤔 Why do we sometimes enforce it as a new thing?
🤔 Is it attempt or ensure?
🤔 I think this should document why this uses
\Drupal\Core\Entity\EntityChangedInterface::getChangedTime()and not\Drupal\Core\Entity\EntityChangedInterface::getChangedTimeAcrossTranslations().Comment #300
quietone commentedWasn't able to spend as much time on this as I hoped the good thing though is I found a bug. I migrated the d6 test site, without this patch applied, then added a node to the source, then applied the patch and ran an incremental migration. That results in an error in a follow up migration.
And here are a few responses.
#296. 16. The followup migrations are created after all other migrations are run. We can alter them before hand because they will not exist. So, we force a check of them when they are derived. I've improved the search for them by searching for the migration tag 'Follow up migration' to find them.
#298. Continued patch review (again partial, still going!)
1. Comments in d6 and d7 node_master now have the same text concerning the 'tnid'.
2. The 'timestamp' value is used because it is the timestamp of the revision whereas 'changed' is the timestamp for the latest revision.
See .https://git.drupalcode.org/project/drupal/blob/8.9.x/core/modules/node/s...
3. Copied comment from d6_node.yml to d6_node_master.yml and removed 'tnid' since it is actually mapped. For completeness, the comment is added to d7_node_master.yml.
6. Yes, good idea it is much more readable now.
Still to answer:
296: 10, 13, 14, 17, 18, 20
298. 5. And from the second half 7, 9
299 3, 4, 5, 6
Comment #302
quietone commentedTry that again.
Comment #305
wim leersAh, that makes this generic instead of hardcoding those two. Excellent! That means contrib/custom migrations that add additional follow-up migrations will also be picked up 👍 That addresses the gravest concern I had here. But the overall if/else still is very tricky IMHO: why the
system_sitecheck exists isn't clear to me.Interesting. So how will this still result in a correct
changedtimestamp in thenodetable?I'm concerned about the complexity this introduces forever: this means that the
d7_node_mastermigration (whose name suggests it's of an all-encompassing nature) will not be used on sites withoutcontent_translationturned on.On sites with
content_translation, you get:d7_node_masterOn sites without
content_translation, you get:d7_noded7_node_revisionAt least, that's …
… according to this.
But
"both" implies both "master" + "classic" will run, but all comments so far indicate the opposite? How can that occur?
Also, "classic" implies "this is old and will be removed at some point", but that does not seem to be the case? Based on this, I think "monolingual" would be a better name?
(I suspect when you started a migration prior to "master" existing, and then end up with both? But this is a complex edge case worth documenting I think. Related: why do we absolutely need to support this edge case?)
🤔🤔 Now it's 100% certain that the interface/API that #2876085: Before upgrading, audit for potential ID conflicts introduced is inadequate. Or rather, it was too optimistic at the time to add an interface, because we only knew of a single use case.
We can see that this actually does not implement the interface … it even violates it! The interface dictates:
and here we do:
I'm not saying we need to fix this here. I'm just making the observation. I do think we should at least have a class-level comment on
NodeMasterIdAuditor, to explain why it doesn't implement the interface.🤔 Why change the timestamps?
🤔 These changes seem out of scope?
🤓 Nit.
If it's an array of migrations, it should be
\Drupal\migrate\Plugin\MigrationInterface[] . $migrations.If it's an array of migration definitions, it should be
array $migration_definitions.It's the latter. Fixed. ✅
🐛✅ This is exactly the same as the parent implementation, so can be removed. Done.🐛🐛✅🤔🤔🤔 BUT! Why do we have two different
node_migrate_classicmodules?! One atcore/modules/node/tests/modules/node_migrate_classic, the other atcore/modules/migrate_drupal/tests/modules/node_migrate_classic. This is super duper confusing!? 🤯🤔 We don't anything about
$result. That seems wrong?🤔 Why are we changing the node created time here? Shouldn't we only be seeing revision- and translation-related changes in the patch?
🗣 "Source node 1."?
🗣 "[…] does not have a migrate."?
I'm not sure what this is saying.
🤔 Why are we testing this only for Drupal 6, and not also for Drupal 7?
Comment #306
quietone commented@Wim Leers, again, thanks for the reviews and patches.
Been thinking about the followup migrations and this error, from #300
Message SQLSTATE[42S22]: Column not found: 1054 Unknown column 'sourceid3' in 'field list': INSERT INTO @migrate_map_d6_entity_reference_translation__node__page (source_ids_hash,The error message can be fixed by modifying the migrate_map table for the d6_entity_reference_translations for nodes. This adds an update hook that should do that.
Also I think that altering them can be avoided by adding the master_node migration to the migration_lookups in the process pipeline when the migrations are created in the entityreferencetranslation deriver. If the master node migration does not exist the migration_lookup will throw a Plugindoesnotexist but continue on to the other migrations to find the lookup. Testing that manually is working, so let's find out what the test bot thinks of it.
Comment #307
quietone commentedAborted the test since it looks like very migrate kernel test is failing.
Some answers for #305
#3. Presumably the entity save is doing this for us?
#4. Ah, right. Originally this was for multilingual migrations but somewhere along the way it became the default migration for new migrations. If the latter, then the migration needs to move to node module. However, this points needs to be double checked with, at least, the migrate maintainers.
#6. mikelutz found problems with the fixtures when he created the master node migration in #121. There are some other questions about the fixtures and will do those all together.
#10. Yes, I forgot to remove the one from migrate_drupal. Fixed in the patch in #306.
#14. There are no term node migrations for Drupal 7. I don't know the history of term node and didn't work on those migrations.
Still to answer:
296: 10, 13, 14, 17, 18, 20
298. 5. And from the second half 7, 9
299. 3, 4, 5, 6
305. 2, 5, 7, 11, 12, 13
Comment #308
quietone commentedAnd fix the bad patch in #306
Comment #309
wim leersSo, comments #296 until #305 were me trying to make sense of things, and verbalizing all the things that stood out to me as hard to understand. Hopefully that helps this patch get to a point where it's committable.
Deeper concerns that I needed to double-check by running code locally and stepping through it with a debugger
d7_node_masterandd7_nodemigrations getting picked up under the hood (only under the hood, because this UI does not show any of that, it just runs all migration plugin instances in order in a single long-running batch). This worked fine, thanks to\Drupal\migrate_drupal_ui\Form\CredentialFormusing\Drupal\migrate_drupal\MigrationConfigurationTrait::getMigrations().\Drupal\migrate_upgrade\MigrateUpgradeDrushRunnerusing\Drupal\migrate_drupal\MigrationConfigurationTrait::getMigrations()also.See how both the "classic" and "master" migrations are listed! I'm not sure what the answer is here. Just documentation? Detect if the "master" migration ran and then refuse to run the "classic" migration, and vice versa? (This relates to the question I asked in #305.4 about the "both" case — this is probably the biggest architectural concern.)
Because the row counts do line up nicely:
versus
241 for the "master" migration matches the 25 + 216 for the "classic" migration's cumulative entity + entity revision table migration plugin instance row counts. (This is for
wimleers.com, which is monolingual.)content_translationmodule is not installed, no big deal, then "master" simply would not handle translations.That's conceptually much simpler.
I think this is the most important question by far, because it seems there is a huge potential for simplification. If that's not true, then we need to thoroughly document why this simplification is not possible or not desirable.
Until the questions in this comment are answered, I don't think it makes sense for me to continue reviewing this.
Comment #310
quietone commentedThis patch changes the way the migrate type is determined. Instead of checking for the existence of the classic and master migrate_map tables it now checks if the map table has rows. The map tables get created before the migration is run and an empty table just tells us that the migration exists when what we want to know is if the classic or master node migrations have run.
Comment #311
quietone commentedJust answering some of the questions from Wim Leers.
#296:10 - When a test is run NodeMigrateType executes before the migrate_map tables are created (in Sql::ensureTables()) adding a module
was the quickest and easiest way to indicate the type of migration. I suppose the proper way is to add a migrate_map table
to the d8 database and add a row as part of the setup. It was just a bridge too far at the time.
#296:13 - The change to static was made but now I see that the comment is for the method that is not in the trait. That will have to be fixed.
#296:14 Perhaps, a message the same as the one in CredentialForm could be used here?
#296:18 - It is possible to have classic and master migrate_map tables with data if one is using drush.
#296:20 - Answered in #300.
#305:3 Presumably the entity save handles keeping the latest revision correct as each revision is saved during the migration. That seems to be that case in practice.
TODO:
296: 17
298. 5. And from the second half 7, 9
299. 3, 4, 5, 6
305. 2, 5, 7, 11, 12, 13
309. 3, 4
Comment #312
quietone commented@Wim Leers, your review and the break I took from working on this has helped greatly to put this all together and find my errors. Thank you.
309:3. The drush ms command lists all the available migrations so in that sense the display is correct. The developer can then choose which they want to run. Should core be deciding for the developer which node migrations are available? I am not a user of migrate_tools so input from others on what they expect would be helpful.
And, as I write this I recall that there is an option that will run all the dependencies for a given migration. That dependencies on the node migrations should be checked. I doubt they are correct. But, again, need to know what is expected.
309.4 Excellent question. I now think that the BOTH case doesn't matter. All that matters is knowing if there is data in the classic node tables and if there are then continue to use the classic migrations. That is just for the UI, not drush.
And we need to keep in mind that we can not deprecate a migration. It is a serious blocker. :-( See #3039240: Create a way to declare a plugin as deprecated.
It may help to show the cases we are dealing with.
Before
Only classic node migrations exist
After
Case A
Core UI /upgrade
CASE B
Core UI migration first run before this patch and then after with this patch.
CASE C
drush
TODO:
296: 17
298. 5. And from the second half 7, 9
299. 3, 4, 5, 6
305. 2, 5, 7, 11, 12, 13
Comment #313
quietone commentedUpdate IS with comment by mikelutz about deprecating the classic node migrations.
Comment #314
quietone commented296: 17 - helper method created.
298: 5. - now uses getLegacyDrupalVersion from MigrationConfigurationTrait
298: from the second half, 9 - updated entitycontentmaste summary line.
299: 5 - Changed comment.
305: 11, 13 - Fixed.
TODO:
298. And from the second half, 7
299. 3, 4, 6
305. 2, 5, 7, 12
Comment #315
quietone commented@mikelutz can you respond to #299. 3, 4 and 6? These are all about adding comments to the EntityContentMaster destination plugin.
This patch has fixes for the following, about the IdAuditor.
298. And from the second half, 7
305. 5. This one suggests adding comment to IdAuditor.php. Since that file is not changed in this patch and this patch is already large I refrained from making any changes. There are other issues about the auditor and perhaps improving the comments could be done in one of them.
TODO:
299. 3, 4, 6
305. 2, 7, 12
Comment #316
gábor hojtsyThe more I am reading comments on the issue, the more I feel I should raise the terminology question :) we moved off of "master" as a terminology in our database drivers (where it was used alongside "slave") due to our better awareness of how it relates to the colonial past. (We are cited in the Wikipedia article for it even: https://en.wikipedia.org/wiki/Master/slave_(technology)#Terminology_concerns). I am wondering if we can use a better term here as well before we land it.
In other news I opened #3109819: [PP-1] Deprecate classic node migration plugin in Drupal 9 for removal in Drupal 10 which we maybe can do in Drupal 9 later on. Let's discuss there. If we postpone that for deprecating for Drupal 11 like @mikelutz said that is also an option, but that is a different discussion from solving this missing migration coverage.
Comment #317
wim leers#310:
🗣 "then the any migration use a node migration"
👍 Much clearer :)
#311
#312:
You're most welcome :) This is super complicated, and super important. Thank you for having pushed it so far!!! 🙏🥳
RE: #309.3: I defer to others on what is expected in this scenario. All I know is that as a new user of
migrate_tools, I find this extremely confusing.RE: #309.4: heh, thanks :) And … good point about #3039240! What you're saying is that this is really blocked on #3039240. Since this is critical, I've just bumped that issue to critical too: #3039240-29: Create a way to declare a plugin as deprecated.
#316++
Comment #318
gábor hojtsyRe blockers in terms of deprecation infrastructure, @mikelutz said this on slack an is copied on the issue summary:
We also cannot deprecate anything anymore for removal in Drupal 9 unless super-critical, we said we are done with the release of Drupal 8.8. So we can only deprecate in Drupal 9 for removal in Drupal 10 soonest. I opened #3109819: [PP-1] Deprecate classic node migration plugin in Drupal 9 for removal in Drupal 10 for that as per above (probably cross-post :). And deprecating something for removal in Drupal 10 or later cannot be done now as per current policy it needs to be filed against 9.1 and will only land on that branch, see https://groups.drupal.org/node/535670 so if that would block this issue then this would not get done before Drupal 9.1, which is a paradox :)
TL;DR: not blocked on deprecating anything.
Comment #319
quietone commentedTotally agree with Gábor Hojtsy in #136 about not using the term 'master'. And I can hear heddn saying 'naming is hard'!
Just has a look at some on-line thesauri and the best there was 'complete', which is accurate. A shorter version would be 'all'. Any suggestions?
And some more feedback on questions.
#296:10 Further to this, which is about the test modules for testing. The proper thing to do is to improve the tests so they are not needed. So, that is a todo.
#296:18 - For the UI, if we make the decision that if the classic tables exist then only the classic migrations run, there will never be both classic and non classic migrate map tables. That is easily done.
#309.3 Last time I used migration_tools `drush ms` will list all migrations discovered which will include Drupal 6 and Drupal 7 ones. That is, unless migrate_tools does something to filter the migrations. The `drush ms` command in migrate_run does list all migrations.
#309.4: This issue doesn't need to deprecate any migration yml so it is isn't blocked on that. I will defer to mikelutz and others on the timing of deprecating the classic node migrations.
Comment #320
gábor hojtsy"Complete" sounds great :)
Comment #321
quietone commentedFixing some comments and removing the BOTH migrate type.
Comment #322
wim leersAgreed!
Comment #323
quietone commentedDidn't get as much time to work on this during the weekend as I had hoped.
This focuses on doing the testing properly. Instead of using modules to indicate the type of migration, tests can add a row in the migrate_map_d6_node__page table to indicate that a classic migration should be run. A new test trait is added with the helper functions for creating, removing and getting the number of the node migrate map tables. I hope it is clearer now what is happening.
So, far there is support for changing 'master' to 'complete'. If you have any other ideas for that add it here as I was planning on making that the next change.
Comment #324
quietone commentedMissed a use statement causing most of the d7 Kernel tests to fail.
Comment #326
quietone commentedI think some of the errors has to do with the fact that the new test trait, NodeMigrateTestTrait, creates a migrate_map_d*_node__page table (used to determine if this is classic migration) and sometimes that table is used in the test. So, lets use a random bundle name for the migrate_map table created in NodeMigrateTestTrait. This worked locally for MigrateDrupal7AuditIdsTest so should fix all the audit tests.
Comment #328
quietone commentedThe name of the migrate_map created in the new trait needs to be preserved, otherwise the table is not removed. This also has code standard fixes.
Comment #330
quietone commentedThis should fix two tests and hopefully leave 1 more to fix, a funcational test.
Comment #332
quietone commentedThis should fix the failing test.
Comment #333
quietone commentedAnd now rename from master to complete.
Comment #335
quietone commentedMissed renaming in a file and somehow lost a file.
Comment #337
quietone commentedAnd missed a few more places to rename master to complete.
Comment #338
quietone commentedIf the complete node migration is to be the default then it should be in node. Let's see what fails.
Comment #340
quietone commentedI expected the Validate tests to fail, not sure about the Functional test though. Here are fixes for the Validation tests.
I've also updated the CR,
Comment #342
quietone commentedSince the complete node migration is not just for Multilingual, the tag needs to be removed and some tests adjusted.
Comment #343
wim leers@quietone You keep making excellent and very difficult contributions here — thank you so much for all this! 🙏😊🥳
#323 through #332 and then #333 through #337: that is indeed far less confusing, and more realistic 🙂 👍🙏 (Thanks for splitting up the
master→completerename into a separate patch sequence, that made it much easier to review!)#338 + #342: YES! This is what I was worried about after seeing #323–#337: that we wouldn't make "complete" the new default, and would only use it in case of
content_translationbeing installed. That's what I raised in #305.4 and #309.4.#340: That must have felt sooooo satisfying 😄 So much work, just to be allowed to change those few highly impactful and meaningful lines of metadata! 👏
Remaining concerns:
What about all the existingd7_nodemigration dependencies? In case of an optional dependency ond7_node(example:d7_menu_links), we can add an optional dependency ond7_node_complete. In case of a required dependency (examples:d7_node_revision,d7_book,d7_node_entity_translation) … well … I'm not sure.migrate_drupal_migration_plugins_alter()handles this dynamically (look at$replace_with_complete_migration). It only does this whenmigrate_drupalis enabled, but that's probably okay.Probably to preserve backwards compatibility? Let's document this magic explicitly in the change record.
We definitely will need this magic for contrib/custom migrations, but … I'm not sure we need this in Drupal core? In Drupal core, I think we can update the migration definition YAML files instead?
migrate_lookupalters inmigrate_drupal_migration_plugins_alter()update core migrations. But what about contrib migrations? I think the answer "oh but we don't removed7_nodeso BC is not a concern" is only true for in-progress migrations. What about new migrations? All contrib modules that usemigrate_lookupond7_nodewould need to be auto-derived tod7_node_complete.migrate_drupalmodule perform this alteration, instead of thenodemodule? (As of #338 that would become possible.)Comment #344
quietone commented@Wim Leers, thank you. Yes, that last round of changes was very satisfying!
I don't have time now to answer the concerns right now but I am pleased we are done to 5.
ps. I'm in the middle of rerolling #3076447: Migrate D7 entity translation revision translations.
Comment #345
sinasalek commented@wim-leers Yes we are and closely following this issue :))
I know how completed and huge this one is, thank you guys for the hard works you've put into this.
My website is a complicated case, so you can expect my test and feedback once this is finalized
Comment #346
gábor hojtsyTagging.
Comment #347
quietone commentedOnly have time to make this patch
343.3 Moving the plugins alter from migrate_drupal to node.
Comment #348
wim leers#347++ 🥳 Sounds like that was just an oversight and not intentional then! Great :)
Comment #350
quietone commented343.1 For core only I think we can modify the YAML. And if we do, how will contrib modify the migrations? I would like mikelutz and heddn to comment on this point. And they should have a look again at this due to the recent changes.
This patch fixes the forgotten test when moving the changes in migrate_drupal migration_plugins_alter to the node module. And I had a go at making a patch for 8.8.x too.
Comment #351
quietone commentedChanged terminology in the IS from master to complete.
There are still a few points that have not been answered. And I hope @mikelutz can respond to them because they are about changes he made when introducing what was then called the master node migration. There are #299. 3, 4, 6 and #305 7, 12
305.2 - When using the core UI the source database connection is know but what about contrib? In Commerce Migrate system_site is used because the system module will be enabled and that migration found and then from its source plugin we can get the connection. I recall that heddn suggested this method. @heddn, can you comment please.
Comment #352
wim leersWe'd need to keep the at-run-time altering. It just would only affect contrib migrations. I should've explicitly said that, sorry! Does my suggestion make more sense then? :)
Comment #353
quietone commentedFound a bug, the list of migration that are unset when classic is being run includes the regex pattern 'node_entity_translation' and it should be 'd7_node_entity_translation:'. And updating the comment as well. Having run any tests though.
Comment #355
quietone commentedOnly have time to change the entity count for 8.9.x
Comment #357
quietone commentedAnd now patches for both versions with the entity count changes.
Comment #358
quietone commentedNeed to add node_entity_translation to the list of classic migration where the dependency is not altered.
Comment #359
quietone commentedIn an earlier comment wim leers suggested changing the migration ymls and not using migration_plugin_alter. This is a test patch of that idea, there will be test failures. I don't think there are BC issues (correct me if I am wrong) but there is a problem with dependencies. For now, the dependencies on classic node migrations are moved from required to optional and added the complete migration as well. I don't really like that option but what else is there?
Comment #361
quietone commentedFix some typos.
Comment #363
mikelutz299.3
The call to setNewRevision(FALSE) is necessary if we are re-running a migration with set revision ids, and the destination revision id already exists. In this case we want to update the existing revision, not create a new one. I believe the call to setNewRevision(TRUE) my be unnecessary, I believe I just added it in to be explicit there and to remind myself in which of those blocks we are intentionally creating a new revision.
299.4
Similar to above, the enforceIsNew() call is necessary to properly save a new entity while setting the id. Without it, the system would see that the id is already set and assume it is an update. The call to enforceIsNew(FALSE) I think is unnecessary, again, just a reminder to myself explicitly when we were creating a new entity versus an update.
getChangedTimeAcrossTranslations wouldn't make sense here. We are trying to build the revision table, and all we are doing is saying that if we updated an untranslated field, then set the changed time for all translations to match the current row that we are saving. in this context, getChangedTime() should return the value we just set in the updateEntity() call above.
Comment #364
quietone commentedFixing the errors in the most recent patch. It was all a mistake on my part in that I shouldn't have mucked with the dependencies n the yml files. They are now modified in a migrations_pugin_alter in node.module and the test simplified.
@mikelutz, thanks for the explanations, which I haven't read thoroughly yet as I am on a short holiday. But I did see that you think that the call to setNewRevision(TRUE) my be unnecessary, I intend to run the tests without that and see what happens.
Comment #365
mikelutz305.7 Those changes do seem slightly off. We added revisions for nodes 2 and 3, hence the extra field link entries, but the field data table doesn't match the field revision table, so those should match.
305.12 This changed because the original d6 fixture was weird.
Originally, in the node table you had node 1, Revision id 1, created time 1388271197. changed time 1420861423
in the node revision table you had node 1 revision 1 timestamp 1420861423 and node 1, revision 2001, timestamp 1390095702. It made no sense, the latest revision timestamp was earlier than the first revision timestamp, and the node and revision tables don't match. I swapped the first and last timestamps, and set the node table to match, though I see now the node table still lists the revision for node 1 as revision 1 when it should be revision 2001, and I don't know how changing that will affect other tests.
I think the approach here is correct, and I fully sign off on the basics of the patch here as a maintainer, though I would like to clean up these details in the fixture. I think we should get this in and then focus on iterations on it, as this method has significant improvements and advantages over what exists currently.
Comment #366
quietone commentedThis makes the correction to the drupal6 test fixture stated in the second paragraph above.
Comment #368
quietone commentedAs we see making that change in the drupal6 fixture causes test failures and in test unrelated to the changes here. I'd prefer to have that fixed on HEAD and not here.
This patch starts again from #364 but this time remove the changes to field_link in the drupal7 fixture. Lets see what changes now.
Comment #370
quietone commentedSilly me, I must have gotten too much sun today.
Start again from #364 and correct the entries in the drupal7 fixture (instead of removing them )for field_link and fix a coding standard error.
Comment #371
mikelutzI am fine with additional iterations on the fixture being moved to follow-ups.
Comment #372
wim leers#363: those all sound solid, but can we please add those as code comments instead of issue comments? That's all I asked for, really :) Not going to un-RTBC for that though; not important enough.
#359 + #364: 👍💯
Comment #373
quietone commentedAdding comments as per #372. And in doing that I changed the logic of an if statement in node.module.
Then I discovered I found that 'statistics_node_counter.yml' still had dependencies on the complete node migrations. That should have been removed in #364.
So, there are two code changes here plus the comments.
Comment #374
quietone commentedAnd I've updated the change record.
Comment #375
wim leers#373++
#374++
Thanks, @quietone! :) <3
Comment #376
quietone commentedNow try to make a patch for 8.8.x
edit: s/path/patch
Comment #377
quietone commentedThere was a test of the plugin alter that got removed when most of those changes were made directly to the migration ymls. Since node.module still uses hook migration_plugins_alter, the test should be restored. So, here it is for both 8.8.x and 8.9.x.
Comment #380
quietone commentedOops, namespace error. And no need to install taxonomy in the test.
Comment #381
mikelutzComment #382
alexpottWe need a D9 patch too. There's a conflict in
core/modules/migrate_drupal/migrate_drupal.installbecause of the removal of the update hooks and addition ofmigrate_drupal_update_last_removed().Comment #383
alexpottGiven this class has dependencies it seems funny it is not a service.
This is not a node destination. It can be used for other entity types, right?
This looks really odd. $definitions are only required if $version is NULL which is not required. I think caller should work out $version.
This seems a very fragile way to determine whether a migration is using the node complete migration. Is there are better way to determine this? In other places we seem to have a more specific regex being used.
new NodeMigrateType() - but also this shows the awkwardness of this method passing in an empty array and the version.
Patch attached addresses these concerns by allowing destinations to say they can audit themselves, removing NodeCompleteIdAuditor.php, and refactoring \Drupal\migrate_drupal\NodeMigrateType::getNodeMigrateType() to be a static helper. The patch also fixes duplicate types on the ID conflict form (and tests the fix) - see screenshot of how this looks with #382 - https://www.drupal.org/files/issues/2020-03-02/Screenshot%202020-03-02%2...
I'll apply to the D8.9 and D8.8 patches once it is green on D9.
Comment #385
alexpottWhoops - too many $connection vars.
Comment #387
alexpottComment #388
quietone commented#383.1. While working on this I found limitations with the auditor and getHighest() because they assume that all IDs are integers and that the first one is the one to use for auditing. I choose to make the minimal changes to the audit functions necessary for this patch as this is already complicated enough. I'd rather have any more work on the auditor done in separate issues. There is already, #3061676: Create an audit plugin class/manager for migrations and #3091004: getHighestId() should be able to use any integer destination id.
#383.4. Sure, it could be, preg_match('/d([67])_node_complete($|:.*)/', $migration_id).
Comment #389
alexpott@quietone so yep this patch does expose the limitations of the audit system but the implementation in #382 and earlier have several drawbacks. There are:
I think if we really want to not pre-empt those over improvements and maintain the current API then we should do the special casing inside \Drupal\migrate\Audit\IdAuditor::audit() to limit API creep.
Comment #390
alexpottHere's #389 implemented. So no extension to the audit system - leaving that for follow-ups and a private method introduced so we can clean this up in those issues without worrying about BC and API implications while still getting this done without have to fix it.
Comment #391
alexpottMissed out an important
!Comment #393
quietone commented@alexpott, nice changes. I guess these should be applied to the 8.8.x and 8.9.x versions?
Comment #394
alexpott@quietone yep I was waiting till you +1'd the changes but #393 is even better. I consider all the points of my review in #383 resolved now and this could be back to rtbc.
Comment #395
masipila commentedQueued for sqlite and PostgreSQL just to be sure.
Comment #396
catchsqlite fails are real:
https://www.drupal.org/pift-ci-job/1599790
Comment #397
alexpottWell that was fun. The node complete migration lookups without all the source keys are causing a new postgres fail :) It's explained in the code comment in the patch - see interdiff.
The interdiff is the same for all three branches.
Comment #398
quietone commentedNice solution alexpott.
The test failures appear to be unrelated to this patch.
Comment #399
alexpottI've opened #3117629: Postgres converts all tablenames to lowercase causing problems when dropping them to address the Postgres upper case table name fun.
Comment #400
firewaller commented#397 works well for me on 8.8.x except the revision_translation_affected value is not always set for a revision an it gets hidden in the node revisions form (last discussed in #141).
Comment #401
masipila commentedThanks for testing this @firewaller and taking the time to read the previous comments of this 400 comment long mega monster!
The revision_translation_affected thingy is quite creepy stuff. Are you able to point a specific scenario where you expected a different outcome than what was migrated?
Cheers,
Markus
Comment #402
quietone commentedI asked mikelutz about the new entity_complete destination plugin which creates from 1 to 3 destination IDs in light of the fact there is another issue to remove conditionals from all destination plugins #3004574: Remove conditional logic from all destination getIds(). Here is his response:
I agree with mikelutz's explanation. And any further discussion can happen in #3004574: Remove conditional logic from all destination getIds().
Comment #403
firewaller commented@masipila I've been following the progress of this for a while, great work to everybody so far!
Sorry for the redactions, but here's an example D7 vs D8 node revisions comparison:
Drupal 7 English Node Revisions

Drupal 8 English Node Revisions

*Note: I had to remove the pager limit and add the vid column manually in \Drupal\diff\Form\RevisionOverviewForm to display this appropriately.
As you can see, both revision IDs ****681 and ****671 are missing from the D8 node revisions page, but I have confirmed they have been migrated and have a NULL value in revision_translation_affected. From what I can tell, in D7 there is no significant difference between them and the revisions that have revision_translation_affected = 1 aside from the fact that ****681 and ****671 end up in draft in D7.
Comment #404
mikelutzLooks like draft revisions are perhaps not showing correctly, but I'd be curious as to what the node_revisions table looks like for those rows in your database.
Comment #405
firewaller commentedHere is a query result from the node_revision in both D7 and D8 (again sorry for redactions):
D7:

D8

Let me know if I can provide any further information!
Comment #406
firewaller commented(Updating image above)
Comment #407
firewaller commentedAdditionally here is a query result from the node_field_revision in D8:

Comment #408
xjmComment #409
quietone commented@firewaller, thanks for the testing.
Unfortunately, the testing was done with moderation (which I gather is workbench_moderation in Drupal7) which I have no experience with and it is not used in the Drupal7 test fixture. So, I gather the next step it to add workbench moderation to the fixture and use it in some content types (entity translation and node translation). But will adding that break any existing test? I don't know, but I hope not.
For me, I would prefer to have confirmation from testing that this patch works without moderation first.
Comment #410
masipila commented@firewaller, if the patch works without the D7 moderation module, it would be extremely beneficial if you could help narrow this down by trying to find full steps to reproduce the moderation steps that trigger the unexpected behavior.
Cheers,
Markus
Comment #411
catchI've opened a follow-up for firewaller's issue at #3118625: Migrate sets revision_translation_affected incorrectly when different languages co-exist for 8.x un-translatable fields. This issue is quite unweildy at 400+ comments, and if it is due to workbench_moderation or a similar module, then while I agree we should try to account for that in the core migration assuming we can, it also shouldn't prevent this being committed and tested more widely.
Tentatively moving back to RTBC - I haven't reviewed the whole patch recently, but did review @alexpott's interdiff in #397 which fixed sqlite/postgres failures from the previously RTBC patch.
Comment #412
firewaller commentedThanks everybody! It seems appropriate to split this out, I will follow up in the new thread above with any findings related to workbench.
Comment #413
alexpottI'm doing final reviews looking to commit this. I discussed this issue yesterday with @catch and, whilst I have contributed fixes to the patch I'm not the patch author (that's @quietone) or the originator of the idea (I think that's @mikelutz), the size and complexity of the patch mean that the time I've spent working on this probably puts me in the best place to do the final review and commit.
Comment #414
quietone commentedYes, the idea was mikelutz's.
Comment #415
alexpottI think the change record might need expanding to cover the change from the perspective of different use-cases. For example:
This update function looks like it is untested. I'm a bit confused as to why it's needed. In #306 it says it's to fix an error from incremental migrations. But I thought if you have migrations in progress already we'd be using CLASSIC migrations because the map tables exist already - so why do we need an update?
Or is this what the FollowUpMigrations is testing - I don't think it is because I can't see how it is.
Comment #416
heddnGive a 188k patch to a new reviewer and you are bound to have something surface. So sorry for the late nits.
There seems some oddness here. This says d6, but then lists version 7. And dreditor says this is for a d7 test. I'm confused.
Either put an issue here or remove this todo, no? Maybe just logging is enough.
Nit: this seems like the coherency of the message could be more clear.
Comment #417
quietone commented#415.1 - Yes, I see what you mean. There are 15 migrations that will have dependencies altered to use the complete node migrations and those will then fail to run in the custom environment envisaged because requirements will fail. Not sure how to address that yet.
#415.2 - Good spotting. The upgrade hook will be needed when moving from using classic node migrations to complete node migrations, if that is to happen. The hook was created in response to an error from a specific case. The steps to reproduce that from #300 are: "I migrated the d6 test site, without this patch applied, then added a node to the source, then applied the patch and ran an incremental migration. That results in an error in a follow up migration." But that scenario is not possible anymore with the UI. If one has classic node migrate maps with data then it will always use classic mode.
Discussion of moving from classic to complete is for a separate issue, in my opinion.
As for the FollowupMigrationsCompleteTests, they can be removed. It was mostly my unfamiliarity with the Followup migrations that I wrote those tests to be sure they worked. Since are not creating new kernel migration tests for all the complete node migrations there is no need to single these one out.
#416.3 - Fixed
#416.4 - Added the same error message used on the CredentialForm when the database is not found. Is there a better thing to do here?
#416.5 - I saw that same thing as well and have improved the comment.
Comment #418
catchCould we do something like add a $settings check to the alter to make it a no-op if it's set?
Custom migrations would then hit the problem described, but they'd have a choice of flipping the setting (a one line change), or converting to use the new migrations.
It is a bit of disruption, but don't think this is a detectable situation.
Comment #419
quietone commentedI was thinking along the same lines. How is this?
Comment #421
quietone commentedFixing the test.
Comment #422
alexpottI agree that adding a setting is probably the best way around this. I've updated the change record with this information and I've added a release note snippet. Now I think all we need are patches for 9.0.x and 8.8.x too.
Comment #423
quietone commentedThanks alexpoptt.
And here are the patches. I made one for each version and made an interdiff against the last time patches were made for all three versions, #397, thinking it would be easier to review.
Comment #424
wim leersThis sounds odd to me? Shouldn't it be ?
Not enough reason to hold this up though :) Also, this definitely has a change record already, so untagging.
Comment #425
alexpott@Wim Leers good point. Fixed on commit.
@quietone yay you did it!
Committed dcd666c and pushed to 9.0.x. Thanks!
Committed 8002785 and pushed to 8.9.x. Thanks!
Leaving open against 8.8.x for discussion around the backport. I think it needs to go into 8.8.x as a migrate critical that allow people to update to Drupal 8 that were not able to before.
I've additionally credited @catch, @plach and @hchonov for issue review.
Comment #428
alexpottHere's a patch with the settings update.
Comment #429
daffie commentedCreate an followup for the removal the lowercase table naming in NodeMigrateTypeTestTrait::getTableName() for PostgreSQL, because #2986452: Database reserved keywords need to be quoted as per the ANSI standard has landed for Drupal 9.0. See: #3119710: Remove lowercase table naming in NodeMigrateTypeTestTrait::getTableName().
Comment #430
masipila commented@quietone (and all other contributors to this patch), woop, woop! \o/
This must be the longest and most complex migrate issue I've seen...
Cheers,
Markus
Comment #431
darren ohI have a Drupal 7 site that uses Organic Groups access control to allow multiple groups to each create their own private translation of a node. I have not been able to think of any way to migrate this to Drupal 8 translations.
Comment #432
xjmI think this should actually be NR for #428 now.
Comment #433
quietone commentedI agree with the change in #428, it corrects an error I made in the comment. But I can't RTBC this one.
Comment #434
masipila commented+ PostgreSQL & SQLite test executions
Comment #435
wim leersIn testing today, it seems that rolling back the
d7_node_completemigration fails. Can anybody else reproduce this?Comment #436
quietone commentedI've made an issue to fix the the rollback problem reported in #435 , #3123095: Rollback of complete node migration fails.
Plus retesting PostgreSQL
Comment #437
quietone commentedReran the PostgreSQL test and it is passing now.
Comment #438
masipila commented#423 was committed to 8.9.x and 9.0.x
@alexpott's change in #428 is only in docblock and the update makes sense.
Issues reported after #428 have their own follow-ups.
All tests are green. RTBC.
Happy Easter to all who are celebrating it!
Cheers,
Markus
Comment #439
heddnThis needs work to incorporate #3125763: Hidden dependency on migrate_drupal from node module when only migrate.module is enabled. Otherwise we'd have a 8.8 release that is broken.
Comment #440
heddnHere's that re-roll that incorporates #3125763: Hidden dependency on migrate_drupal from node module when only migrate.module is enabled into this patch. It wasn't RTBC (yet), so back to NR for a little while. Ideally, we'd have any feedback on _it_ over in _its_ issue, then re-rolls to incorporate _it_ back here.
Comment #441
heddnOops, did that wrong. If this goes to RTBC, the testbot will try to test the interdiff. Let's head that off.
Comment #442
heddn:blush:
Comment #443
masipila commentedRe: #439 I'm confused to the max. :)
According to the interdiff 423-428 the only change is the docblock change. I must be missing something?
Markus
Comment #444
heddnre #443: I hadn't realized that this backport hadn't landed yet in 8.8. But currently 8.9 and 9.x are both broken rather badly. So we need to incorporate the bug fixes from that other issue into this one. Otherwise, 8.8 will also be badly broken. The fix is a one-line module_exists check on
migrate_drupalin node.module.I apologize for all the noise in uploading the merged patch.
Comment #445
quietone commentedNow the patch for 8.8.x.
This does not have the test for the hidden dependency from #3125763: Hidden dependency on migrate_drupal from node module when only migrate.module is enabled because that is intended to be committed first. As expected the interdiff with the previous patch here for 8.8.x shows that the migration_plugin_alter hook and the associated kernel test are moved from the node module to migrate_drupal.
Comment #446
heddnThis should be postponed on #3125763: Hidden dependency on migrate_drupal from node module when only migrate.module is enabled being backported first.
Comment #447
catchJust committed the patch there. This will need the bug fix from that issue incorporated into the patch here now.
Comment #448
catchComment #449
heddnRTBC, because the only things here added since it was last RTBC and landed in 8.9 and 9.x are the fixes from #3125763: Hidden dependency on migrate_drupal from node module when only migrate.module is enabled.
Comment #450
benjifisherI compared the patches for 8.8.x and 8.9.x in #423. Can someone comment on the differences? There are several differences in the fixtures. Maybe I should not worry, but I prefer to start with the patch that was committed (to the 8.9.x branch). I guess both patches were RTBC in #424.
I compared the patches for 8.8.x in #423 and #428. As expected, the only differences were the ones suggested in #424.
I compared the patches for 8.8.x in #428 and #445. The differences are as expected: the new patch makes more changes to
migrate_drupal.module, it adds the kernel test to themigrate_drupalmodule instead of thenodemodule, and it makes no changes tonode.module.I applied the patch to 8.8.x and compared the
.modulefile and the kernel test in themigrate_drupalmodule to the versions in 8.9.x, where #3125763: Hidden dependency on migrate_drupal from node module when only migrate.module is enabled has been fixed. The files are identical.I am really close to saying +1 for RTBC, but I would appreciate an answer to my question in (1).
Comment #451
quietone commented@benjifisher, your thoroughness never ceases to amaze me.
#450-1. Yes, I think you are worrying to much. However, I have made another 8.8.x patch, this one has the same fixture as 8.9.x except, of course, for changes to the fixture made in commits only made on 8.9.x.
I started by diffing 2746541-8.8.x-423.patch 2746541-8.9.x-423.patch which showed differences in the drupal7 fixture. I checked where the differences may have come from with git blame and that didn't turn up anything useful. I gave up. I used Meld to work with the 8.8.x and 8.9.x versions of drupal7.php and to create an 8.8.x version that is the same as the 8.9.x version (expect for patches only applied to 8.9.x).
Then from 8.8.x I applied 2746541-8.8.x-445.patch and overwrote drupal7.php with the version created using Meld. At this point I edited drupal7.php to fix some existing spacing errors.
So, lets see if this breaks any tests.
Comment #452
catchComment #453
benjifisher@quietone, funny, I am likewise impressed by your energy.
I confirmed that the only changes from #445 to #451 are in the fixture.
I tried comparing the patches directly. That did not go well. So I applied the patch from #451 locally and compared the fixture to the version in 8.9.x. There are several differences, and I did not check them all, but the ones I did examine seem to come from patches that, as you say, were applied to 8.9.x but not 8.8.x. Especially
Extracting the parts of these patches that affect the fixture, the first does not apply to 8.8.x, but the second does. If the committers want a more thorough comparison, then I will start by applying that in order to make it easier.
At this point, I leave it up to the committers. You can accept the patch in #445 based on the the reviews in #449 and #450, or accept the patch in #451 based on this review, or request additional review or changes. I do hope that this issue gets into 8.8.x so that site owners have the option of using this improvement to migrate their older sites into 8.8.
Comment #454
benjifisherWe discussed this issue on Slack.
@catch suggested that we change the default for
$settings['migrate_node_migrate_type_classic']in 8.8. @heddn approves of this idea. Let's make it happen!I am setting the status to NW. If someone else can supply a patch, then I will review it promptly.
I think we can leave the release-notes snippet as is, but we should update the CR when we make this change.
Comment #455
quietone commentedOK, set the flag to FALSE.
Comment #457
quietone commentedSigh, I forgot to change /core/assets/scaffold/files/default.settings.php. And we are setting 'migrate_node_migrate_type_classic' to TRUE not FALSE like I typed above.
Comment #459
benjifisher@quietone: no worries. That is why we have automated tests.
It seems that several tests are failing. Do these tests assume that we are using the complete node migrations?
I do not know why NoMultilingualReviewPageTest fails: that tests the review form before any migrations have run, right?
Do we need additional test coverage (in the 8.9.x, 9.0.x, 9.1.x branches) for the case
$settings['migrate_node_migrate_type_classic'] = TRUE;?Comment #460
quietone commentedAll the functional tests in migrate_drupal_ui, except d6/NodeClassicTest.php, assume that the complete node migration is enabled. I would like to keep these test like that.
The NoMultilingualReviewPageTest is about IdConflicts. The auditor is deciding that there is a IdConflict when the complete node migration is enabled whereas it doesn't with the classic node migration. That is pretty basic answer and I'd have to look at the auditing code to provide a better answer.
I really hope not because these tests are awful to work with and take a terribly long time to run locally. And yes, I wrote these tests. If I knew what I know now when I was creating them I would have done it different. And, there is an issue to refactor these tests, which I would like to finish.
Now, back to your question. There is a d6/NodeClassicTest which is the same as Upgrade6Test. They both run a full migration (one classic node and the other complete node) and they also test the IdConflict page, the ReviewPage and the final entity counts.
If it is deemed necessary to add more tests I will, of course, do so but I should not be the one to decide if they are needed.
Comment #461
quietone commentedThis patch restores all the functional tests, except d6/NodeClassicTest, to using the complete node migration. This is done byt changing the $settings value in setUp(), though it took me a while to learn how to alter $setting.
Comment #462
benjifisher@quietone, thanks so much for your continued work on this issue! I think we all assumed this would be a really easy update. We can take comfort knowing that it was a shared mistake.
We do not need these
usestatements. Sorry, but I have to set the status back to NW for this.migrate_node_migrate_type_classicin MigrateUpgradeTestBase, then I think the only other place we have to change it (in the other direction) is NodeClassicTest. This would have the advantage that all of these tests would be decoupled from the actual setting insettings.php.Maybe we can just update the base class and leave NodeClassicTest alone, since it overrides the setting by creating a migrate map table.
I am not sure, but I think we should have test coverage for the classic migrations. It seems to me that we did have such coverage before this issue, and we removed it. Since this issue has already been committed to the other branches, I think that test coverage should be added in a separate issue. I hope the core committers will disagree, but I do not think we can commit this patch to 8.8.x until we have restored that test coverage.
Comment #463
quietone commentedThere is nothing easy about the migrate_drupal_ui functional test. ;-)
I made the change in all the test file because I have been bitten before in migration tests where the base class does too much. However, point taken and the change is made.
While it is painful to read that I know we should too. I just have to get over my snit about it and then write the tests. There is a walk in the bush in my future!
Comment #464
quietone commentedSo, now the migrate_drupal_ui functional tests in the 8.8. backport use the complete node migration, the same as the is done in other branches. The exception being d6/NodeClassicTest.
Meant to add that I agree with benjifisher that restoring test coverage for the classic node migrations in functional tests should be done in a separate issue.
Comment #465
benjifisherThanks for the updated patch. I compared it to the one in #451, and it makes the required changes. I am attaching an interdiff, since this comparison is easier than comparing either of these to the patch in #461.
I am afraid that it is too late to get the patch into 8.8, but I hope there is still time.
As I said in #462, I am afraid that this patch effectively removes existing test coverage for the classic node migrations. I hope that the added coverage in NodeClassicTest is enough to make up for that.
I am setting the status to RTBC. It is up to the release managers to decide these questions.
Comment #467
benjifisherSince this issue has already been fixed for 8.9.x and it is now too late to back-port it to 8.8.x, I am marking it Fixed.
Comment #468
chriscalip commentedHello, I am trying to figure out how to proceed.
This patch introduced an edge case BC from drupal 8.8.x to 8.9.x.
My existing migrations was doing a drupal 8 source content_entity:node to a custom analytics database.
Initially these migrations only had 2 keys: nid & langcode. Drupal 8.9 migrate_drupal now adds another key in the mix, vid.
so all migrate_map_[xxx] stop working because it only has 2 sourceid1 and sourceid2.. but
Drupal\migrate_drupal\Plugin\migrate\source\ContentEntityinsist there should be a sourceid3; thus breaking existing migrations.Example
Looking at the problem briefly, I believe adding specific configuration keys should ameliorate this probem.
The configuration keys would be: do we use langcode as a sourceid key, and do we use revision id as a sourceid key.
Comment #469
chriscalip commentedI believe adding the source-key, revision-id, is in error.
Situation a
content_entity:nodeto a destination sql table.We want nid-8 to go to corresponding target nid-1008.
We want nid-8-lang-en to go to corresponding target nid-1008.
We want nid-8-lang-fr to go to corresponding target nid-1009.
We want nid-8-lang-en-vid-8 to go to corresponding target nid-1008.
We want nid-8-lang-en-vid-9 to ALSO go to corresponding target nid-1008.
We want nid-8-lang-fr-vid-9 to go to corresponding target nid-1009.
Having configuration keys: langcode as a sourceid key, revision id as a sourceid key; would give migrate-api users the flexibility needed.
Comment #471
geek-merlinImpressing work! We got a regression though.
Comment #472
drummThe large number of files attached to this issue, and comments on it, have been causing Drupal.org to have out-of-memory issues. I am closing comments so we do not have to spend more time looking for memory-saving hacks.