Problem/Motivation
As @timmillwood and @alexpott have consistently warned, content moderation is experimental and has no upgrade path. Installing and using any experimental version of this module in production is discouraged and probably a really bad idea… however those of us who understood the risks and did it anyway will need an upgrade path.
Issues tagged content_moderation upgrade path
Proposed resolution
I don’t think an upgrade path should be added to the module. It would require fixing and updating every time a schema break was introduced, which would slow down the path to beta and probably create bad expectations that it would “just work".
Instead I propose using this issue to host possible upgrade path patches to allow those who need an upgrade path to collaborate. A bit like HEAD to HEAD for CM, use at your own risk etc.
Using this patch
- Upgrade core from 8.2.x to 8.3.x and apply the patch.
- Run drush updb.
- Export your sites configuration (if applicable).
- Remove the patch and dial back the content_moderation schema version using:
drush ev "drupal_set_installed_schema_version('content_moderation', 8000);"
Some of your sites code and configuration will need some updates to work with the changes made to content moderation:
- You'll need to reassign the content moderation permissions for the transitions each role is allowed to use.
- Custom code references to the moderation state in the form of
$entity->moderation_state->entity->id()
will need to be updated to be in the form of$entity->moderation_state->value
- Loading custom states and transitions like
ModerationState::loadMultiple()
will need to be updated to$workflow = Workflow::load('content_moderation_workflow'); $workflow->getStates();
Comment | File | Size | Author |
---|---|---|---|
#29 | unofficial-2846618-29.patch | 629.21 KB | nlisgo |
#22 | 2846618-82-83-upgrade-path-22.patch | 628.95 KB | Sam152 |
#16 | 2846618-82-83-upgrade-path-16.patch | 628.61 KB | bmcclure |
Comments
Comment #2
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedWorking:
Not working or @todo:
Comment #4
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedDid hit that error a few times locally, but not towards the end, will see if this fixes it. Given this isn't going to be committed, I don't mind putting hacks in there to make the upgrade possible. The patch will be reverted as soon as the updb runs.
Comment #6
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedRan this upgrade path on a project. Worked as expected but $entity_type_id here is actually the module providing the entity type, not the entity type ID itself. Works for for "node" obviously, but not for modules providing entity types where entity type ID !== module name.
Comment #7
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedFix attached.
Comment #8
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedComment #9
bmcclure CreditAttribution: bmcclure commentedJust wanted to say thanks for these patches, this is incredibly helpful for us! It worked for me so far, but I'll report back if I notice anything.
Comment #10
bmcclure CreditAttribution: bmcclure commentedOne issue I ran into:
In my site, I was unable to get to the point of being able to run database updates without first enabling the workflows module. I didn't realize it at the time, but the fact that workflows was already enabled completely prevented the update hook from doing anything.
I ended up commenting out the first few lines in the update hook because I needed it to run even though workflows was enabled.
Comment #11
bmcclure CreditAttribution: bmcclure commentedWhat I've been working on for a little while now is that, during this update hook, I'm receiving the error:
Unable to delete a field (moderation_state in content_moderation_state entity) with data that cannot be purged.
It's strange, since there's code in the update hook that removes those values before trying to delete the field, but somehow there must be left-over data.
I even tried deleting it from the tables directly right before deleting the field:
But I still run into the same issue. If I can figure out what's causing this for me, I'll post an updated patch.
Update: I realized what I'm doing wrong--my additional code was looking in the tables of the moderatable content types, rather than the moderation state entity's tables. I think I've got a working version, testing now then I'll post.
Comment #12
bmcclure CreditAttribution: bmcclure as a volunteer and at Top Floor commentedHere's an updated patch that works for me.
It first collects all of the required state data like before, but instead of clearing it out at the same time, it goes back afterward to update every row that has a value for that field in the tables to make sure there is nothing left before removing the field.
This allowed the update hook to complete successfully for me on a large site with a lot of existing revision data.
Comment #13
bmcclure CreditAttribution: bmcclure as a volunteer and at Top Floor commentedFound additional issue after the fact: Somehow the entity type for content moderation is being changed in the install hook from "node" to "block_content" and I'm this experiencing many issues after the upgrade.
Trying to investigate how this entity type could be wrong in the config. It seems like it must be related to the last fix in the patch version before I posted mine which changes how the entity type is determined, however I don't know why it's returning something wrong yet.
Comment #15
bmcclure CreditAttribution: bmcclure as a volunteer and at Top Floor commentedIt's because the patch is comparing based on $bundle_config_prefix, which for both node and block_content is "type". Trying to figure out a way around that now.
Are you sure that the first value in the config name isn't the entity type? If not, I don't think the entity type is listed anywhere in the bundle config which would be strange IMO. I'm going to try and verify what the best way to figure out the entity type from a bundle config entity is.
Comment #16
bmcclure CreditAttribution: bmcclure as a volunteer and at Top Floor commentedI ended up reverting to using the first part of the config name as the entity type as it was more reliable than using the bundle config prefix. However there may be a better way altogether.
The tests may fail, I haven't touched them.
Comment #17
rakesh.gectcrComment #19
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedHi bmcclure, thanks for working on this, glad you were able to use the patch. It would be great if you could provide an interdiff with your patches, so everyone can see what changed between them.
It would also be great to extend the test coverage with whatever bugs you encounter.
Comment #20
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedMakes sense, so we need a better way to figure this out.
This didn't work in my case, although it did work for node and block_content as you suggest.
Comment #21
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedThinking about this, I think the correct fix would be to get list($provider_of_the_entity_type, $config_prefix,) = explode..., then check the entity type ID against both the config prefix and the provider.
Comment #22
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedLets see if this goes green and fixes bmcclure's issue at the same time.
Comment #23
bmcclure CreditAttribution: bmcclure as a volunteer and at Top Floor commentedI think that solves that last issue I had. However one thing that still occurs with my dataset using this latest patch is it doesn't actually clear out every value of the workflow_state field, only most of them. In particular there are some left-over values in the main field data (not revision data) table.
In my last patch, the way I solved this was to split up the storing of needed values for later, and clearing of existing values:
By actually just querying the table to clear those values, it ensured I wouldn't get an error about there being non-purgable data in the field. I've attached an interdiff between my last patch and your previous patch before that to show the differences there more clearly.
Comment #24
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedThanks for the interdiff! Much easier to review this way.
I wonder what about this causes some of the field values to be left over? Maybe the entity query needs accessCheck(false).
This makes a lot of sense, I like this.
Sounds like we've got a solution for this that works across all entity types now.
I wonder if this works for non-translatable entity types. I'd be more interested in figuring out why the entity query can't empty everything.
Comment #25
bmcclure CreditAttribution: bmcclure as a volunteer and at Top Floor commentedThanks for reviewing!
I couldn't figure out why that initial query didn't catch all of the values either. I don't think it would be related to an access check as I'm running the updates via drush, however it might be worth a shot. I can set up a test environment with my old data to verify whether it works or not as well.
I noticed that values were only left over in the base data table and not in the revision data table for moderation states. I don't know if or why that's significant, but just wanted to note it.
I did originally install content_moderation on an existing database, so none of the content had a moderation state until it was saved again after that, and some of the content might never have been saved. But I don't think that would affect this, since if there is no moderation state then it wouldn't contain non-purgable data.
Is it possible there could be data in the base table which is never returned in an entity query because there is revision data overriding it? I'm not entirely clear on how data is stored in the base table vs the revision table when using revisions, TBH.
Comment #26
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedThe data table should have the default revision in it, which is a revision none the less, so it should have been updated. I wonder if we can safely empty the table, but I don't think I've seen a precent for this anywhere else.
Comment #27
nlisgo CreditAttribution: nlisgo commentedI will try this patch out and provide feedback on my experience. I'm not using content_moderation heavily (just for one node bundle with a draft and published state).
One concern about using the patch is that if there is no hope of it being adopted into core then I will most certainly have to remove the patch after it does it's work so I don't disrupt the upgrade path for core in future. But, the 8001 update hook will already have been fired in my system so when I remove the patch I will also have to trigger a reset on the system table for the content_moderation module.
Comment #28
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commented@nlisgo, that's a really good consideration to make. I'm totally in support of removing the patch as soon as the update hook has run, it was designed for that, but as far as the schema version goes, that'll need rolling back.
Maybe core make an allowance for this issue and start at 800X for wherever this gets up to, otherwise we'll need some instructions for dialing it back, but it's indeed another thing to consider/think about.
Comment #29
nlisgo CreditAttribution: nlisgo commentedI ran into an issue when the update hooks are fired. Sorry, I don't have time to improve the test to catch this bug but may contribute that later. Obviously, I have some modules installed that you may not have.
I tried simply rearranging the order in which the hooks were fired to ensure that content_moderation_update_8001 was run before block_content_update_8300 but it still gave me an error:
So, I added in the following:
And it worked!
Comment #30
bmcclure CreditAttribution: bmcclure as a volunteer and at Top Floor commentedI agree about removing the patch and rolling back after a successful upgrade.
Once you remove the patch, I think you could use the following drush command:
drush ev "drupal_set_installed_schema_version('content_moderation', 8000)
This way if any update hooks are ever added to content_moderation, they are sure to run still.
Comment #31
nlisgo CreditAttribution: nlisgo commented#30 worked for me to undo the patch and restore the schema version: https://github.com/elifesciences/journal-cms/pull/165/files#diff-346cbc6...
Comment #32
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedRolling back the schema version seems like a great idea. Perhaps worth adding some instructions in the issue summary?
Comment #33
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedComment #34
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedComment #35
jhedstromGlad I found this issue, it's serving as a nice guideline for upgrade paths. While I haven't tested the update hook here (we have more than one workflow that needs to be generated from a huge number of states/transitions), this approach looks solid.
One suggestion would be to document putting this code into a custom module (and renaming the update hooks accordingly), so that in the future when
content_moderation
is stable, and starts getting its own official update hooks, sites aren't broken thinking they've already run the new update hooks.Comment #36
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedYeah, I mentioned in #32 we should probably put the schema rollback instructions in the issue summary.
I toyed with the idea of dynamically generating workflows based on the states/transitions allowed for specific bundles, but I haven't looked into the complexity of doing this yet.
Comment #37
T-loThanks for this, worked smoothly for me.
One thing to note is if you'd assigned roles to the previous workflow states, you'll now need to add permissions
Comment #38
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedThanks for the feedback! Adding some basic instructions to the issue summary to include #37.
Comment #39
mstef CreditAttribution: mstef commentededit: additional cache clear seems to resolve it. patchworks. thanks!
After update, /admin/config/workflow/workflows throws:
Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException: The "workflow" entity type did not specify a list_builder handler. in Drupal\Core\Entity\EntityTypeManager->getHandler() (line 236 of core/lib/Drupal/Core/Entity/EntityTypeManager.php).
Drupal\Core\Entity\EntityTypeManager->getListBuilder('workflow') (Line: 73)
Drupal\Core\Entity\EntityManager->getListBuilder('workflow') (Line: 22)
Drupal\Core\Entity\Controller\EntityListController->listing('workflow')
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 574)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
call_user_func_array(Object, Array) (Line: 144)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 64)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 75)
Drupal\shield\ShieldMiddleware->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 50)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 656)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
Not sure if it's related to the patch or something specific on my site.
Comment #40
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedThe list builder class is set in workflows_entity_type_build. Still getting the error after a cache rebuild?
Comment #41
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedComment #42
mstef CreditAttribution: mstef commentedAn additional cache clear after updb did the trick. Everything is working well. Thank you to everyone involved for working on this. Great job.
Comment #43
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedComment #44
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedComment #45
jhedstromFor sites migrating from 8.2 that have translations enabled, the fix over in #2881645: ContentModerationState 'workflow' column empty for all non-default translations. will keep things working. Without that, the non-default language moderation states don't get properly migrated.
Comment #46
jhedstromI'm working on a site that is migrating some 130k moderation state revisions, so this bit where it loads them all in memory had to be refactored. I don't have the code to post yet, but will try to do so shortly.
In that code I've also split up each discreet operation into a separate update hook, so ones that require utilizing a batch can do so, and those that don't are kept simple.
Comment #47
jhedstromAnother issue I just ran into, if the old entity reference
moderation_state
field (attached to entities/bundles) had base_field_overrides defined, those needed to be removed or the system got really confused. I'm not familiar with how/when those base_field_overrides get created though, just something I ran into...Comment #48
ansitun CreditAttribution: ansitun as a volunteer and commentedHi,
I have tried to upgrade from drupal 8.2.7 to the latest version, drupal 8.3.3, I have many content entities that have content moderation enabled; I have followed the below steps for the upgrade:
1.
composer update
[since my codebase is setup with composer]2. Apply the patch and did
composer install
3.
drush updb
4.
drush entity-updates
because there were entity changes5. Removed the patch and did
composer install
6.
drush entity-updates
and I get this error:Kindly let me know if you find a patch for this.
Comment #49
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedI believe the error was introduced by #2779931: Add storage exception that enforces unique content_entity_type_id and content_entity_id on the content moderation state content entity, and add access control handler to forbid all access. We need to add the change to the max_length to the upgrade path or the index we create is too long.
Re-queuing the tests to see if they catch this.
Comment #51
jhedstromThis is the code for a site that has about 10 different 'workflows' in 8.2.x. I've edited the file to only show 3 for demonstration purposes. The other major difference from the approach here is that it uses a temporary table for the moderation state migration since there were too many to do in a single pass in memory.
https://gist.github.com/jhedstrom/0a1723a94484e3a0e46e2692d4698fe6
I was going to try to apply some of this to the patch here, but realized that due to the custom workflow logic, it wouldn't be applicable, and each site using more than one workflow would need their own logic for that bit of the migration.
Comment #52
jhedstromI tried adding this bit to handle the column length change:
but that failed to update the actual column length, because from what I can tell, the schema isn't updated if a setting is changed. I've taken the approach of directly updating the sql table definitions:
which seems to work out.
Comment #53
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedAwesome, I was going to open up an issue for 8.2 to 8.3 upgrade path and request future schema breaks not be backported. #52 is a great start.
Comment #54
Rop CreditAttribution: Rop as a volunteer commentedI did an upgrade from 8.27 to 8.3.3 using #29 patch which worked fine.
Only now when upgrading from 8.3.3 to 8.3.4 I get the before mentioned error (#48) :
Where is #52 to be placed?
Comment #55
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedComment #56
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedIt's clear we're going to have to organise these into patches that upgrade from very specific releases. To make things easier I have:
If @jhedstrom wants to cut a patch for that last issue, that would be awesome, otherwise I can upload it.
I think it'll be easiest to assume the schema version is 8000 before applying and running any of the patches, but make sure we're include checks and balances at the top of each update hook to make sure the schema change hasn't already been applied.
Comment #57
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedComment #58
ahebrank CreditAttribution: ahebrank commentedEDIT: never mind, I need to read these upgrade threads more carefully.
Comment #60
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedComment #61
nlisgo CreditAttribution: nlisgo commentedFor my needs, I am using content moderation in a limited way. I have decided that uninstalling and reinstalling the module at the appropriate time is the best way forward. As long as I do this at a moment that no content is pending review for my project this will have zero impact.
In an update hook, I delete all of the existing content moderation states and then I can uninstall the module. My deployment workflow is to perform a configuration import after a `drush updatedb -y` so the module will be reinstalled immediately after uninstalling it and with the configuration that I have specified.