Problem/Motivation

The actual rollback is very slow because it removes the destination objects and mapping records item by item. In D7, destinations were able to declare a bulkRollback() method. Migrations with such destinations took advantage of using faster APIs to delete the destination objects.

Proposed resolution

This change doesn't switch any destination plugin to use fast rollback. It only creates the API that allows destination plugins to implement such rollbacks. As a followup, we have to research if some of the existing core destination plugins can take advantage of using fast rollbacks. However the patch contains a testing destination plugin (TestFastRollbackDestination) proving how such a plugin should be implemented.

Solution: Add back the ability to run fast/bulk rollbacks but make it optional and defaulting to item-by-item rollback.

  • A new destination interface MigrateDestinationFastRollbackInterface
  • Destinations able to run fast rollbacks should explicitly demand this by adding the destination setting fast_rollback: true in their config, in migration .yml file.
  • Add event dispatching for such operations.
  • A fast rollback can have 2 types: batch or all-in-one rollbacks. By setting the destination setting fast_rollback_batch_size: 0, in the migration .yml file, the rollback will run in a single step (all-in-one). When the value is positive integer, that will override the default batch size of 50.
  • Rework MigrateExecutable::rollback() to allow batch and all-in-one fast rollbacks. The actual item-by-item rollback become just a particular case of batch rollbacks where the batch size is 1.
  • Add a new methods MigrateIdMapInterface::deleteMultiple() and MigrateIdMapInterface::deleteAll() that allows bulk deletion of map items.
  • Add a test together with a testing destination plugin allowing fast rollbacks.

Remaining tasks

  • Decide if some of the core destination plugins can be converted to use fast rollbacks (follow-ups).

User interface changes

None.

API changes

  • New interface:
    interface MigrateDestinationFastRollbackInterface {
      public function rollbackMultiple(array $destination_ids);
      public function rollbackAll();
    }
    
  • New methods in MigrateIdMapInterface interface:
    public function deleteMultiple(array $source_ids, $messages_only = FALSE);
    public function deleteAll($messages_only = FALSE);
    
  • New migrate events:
    • MigrateEvents::MAP_DELETE_MULTIPLE
    • MigrateEvents::MAP_DELETE_ALL
    • MigrateEvents::PRE_ROW_DELETE_MULTIPLE
    • MigrateEvents::POST_ROW_DELETE_MULTIPLE
    • MigrateEvents::PRE_ROW_DELETE_ALL
    • MigrateEvents::POST_ROW_DELETE_ALL
  • New event wrapper classes:
    class MigrateMapDeleteAllEvent extends Event {...}
    
    class MigrateMapDeleteMultipleEvent extends Event {...}
    
    class MigrateRowDeleteAllEvent extends Event {...}
    
    class MigrateRowDeleteMultipleEvent extends Event {...}
    

Data model changes

A destination plugin implementing MigrateDestinationFastRollbackInterface accepts two new configuration options:

  1. fast_rollback (bool): If is missed or false, the migration rollback will run on a per-item bases, as the actual behaviour.
  2. fast_rollback_batch_size (int): Has effect only if fast_rollback is true. If is missed the default value is MigrateDestinationFastRollbackInterface::BATCH_SIZE (50). If is a positive integer, the rollback will run in batches of this size. If is set to zero (0) the rollback will run in a single step.
CommentFileSizeAuthor
#37 2821988-37.patch33.59 KBpfrenssen
#35 2821988-35.patch33.59 KBpfrenssen
#30 interdiff-25-to-30.txt2.5 KBclaudiu.cristea
#30 support_fast_rollbacks-2821988-30.patch33.91 KBclaudiu.cristea
#25 interdiff-24-to-25.txt1.17 KBclaudiu.cristea
#25 support_fast_rollbacks-2821988-25.patch33.64 KBclaudiu.cristea
#24 support_fast_rollbacks-2821988-24.patch33.54 KBclaudiu.cristea
#24 interdiff-22-to-24.txt13.43 KBclaudiu.cristea
#22 support_fast_rollbacks-2821988-22.patch32.62 KBclaudiu.cristea
#22 interdiff-21-to-22.txt12.64 KBclaudiu.cristea
#21 interdiff_to_19.txt1.73 KBclaudiu.cristea
#21 support_fast_rollbacks-2821988-21.patch22.14 KBclaudiu.cristea
#19 interdiff_to_17.txt830 bytesclaudiu.cristea
#19 support_fast_rollbacks-2821988-19.patch21.94 KBclaudiu.cristea
#17 interdiff_to_13.txt1.51 KBclaudiu.cristea
#17 support_fast_rollbacks-2821988-17.patch21.92 KBclaudiu.cristea
#15 interdiff_to_13.txt1.51 KBclaudiu.cristea
#15 support_fast_rollbacks-2821988-15.patch27.16 KBclaudiu.cristea
#13 support_fast_rollbacks-2821988-13.patch21.71 KBclaudiu.cristea
#13 interdiff_to_7.txt26.8 KBclaudiu.cristea
#7 interdiff.txt6.66 KBclaudiu.cristea
#7 support_bulk_rollbacks-2821988-7.patch15.88 KBclaudiu.cristea
#2 support_bulk_rollbacks-2821988-2.patch15.88 KBclaudiu.cristea
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

claudiu.cristea created an issue. See original summary.

claudiu.cristea’s picture

Title: Support bulk rollback » Support bulk rollbacks
Status: Active » Needs review
FileSize
15.88 KB

This is only a PoC

claudiu.cristea’s picture

Issue summary: View changes
claudiu.cristea’s picture

Issue summary: View changes
claudiu.cristea’s picture

Issue summary: View changes

Status: Needs review » Needs work

The last submitted patch, 2: support_bulk_rollbacks-2821988-2.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
15.88 KB
6.66 KB

wrong namespace

claudiu.cristea’s picture

Issue summary: View changes

Status: Needs review » Needs work

The last submitted patch, 7: support_bulk_rollbacks-2821988-7.patch, failed testing.

mikeryan’s picture

This made more sense in D7, when there was for example a node_delete_multiple() which would do bulk table row deletes against node tables. In D8, I don't see such optimizations in the entity storage implementations for the entities themselves - SqlContentEntityStorage::doDeleteFieldItems() does optimize, though, so there would be some gain, according to how many fields your entities have.

Given rollback (especially in high volumes) is a relatively rare operation, I wouldn't be inclined to complicate things unless/until a substantial performance improvement can be demonstrated.

claudiu.cristea’s picture

I have a use case where I'm migrating one million of items into a custom table. The rollback iterates million of times and takes a huge amount of time when it simply needs a TRUNCATE.

I agree that, probably, isn't too much gain on entity destinations but, at least. we should let the door open for custom destinations to implement the interface and design their own bulk rollback strategy.

claudiu.cristea’s picture

@mikeryan,

We need to keep the door open for such rollbacks, even we don't add any use case now in Drupal core. At least we should implement from the patch the following, before Migrate gets stable:

  1. +++ b/core/modules/migrate/src/Event/MigrateEvents.php
    @@ -47,6 +47,22 @@
    +  const MAP_MULTIPLE_DELETE = 'migrate.map_multiple_delete';
    
    @@ -167,6 +183,36 @@
    +  const PRE_MULTIPLE_ROW_DELETE = 'migrate.pre_multiple_row_delete';
    ...
    +  const POST_MULTIPLE_ROW_DELETE = 'migrate.post_multiple_row_delete';
    

    Migrate event constants.

  2. +++ b/core/modules/migrate/src/Event/MigrateMapMultipleDeleteEvent.php
    @@ -0,0 +1,60 @@
    +class MigrateMapMultipleDeleteEvent extends Event {
    
    +++ b/core/modules/migrate/src/Event/MigrateMultipleRowDeleteEvent.php
    @@ -0,0 +1,60 @@
    +class MigrateMultipleRowDeleteEvent extends Event {
    

    The event objects.

  3. +++ b/core/modules/migrate/src/Event/MigrateMultipleRowDeleteEvent.php
    index eacf9ae..04593b7 100644
    --- a/core/modules/migrate/src/MigrateExecutable.php
    
    --- a/core/modules/migrate/src/MigrateExecutable.php
    +++ b/core/modules/migrate/src/MigrateExecutable.php
    

    The changes in MigrateExecurable that are allowing bulk rollbacks to be triggered.

  4. +++ b/core/modules/migrate/src/Plugin/MigrateDestinationBulkRollbackInterface.php
    @@ -0,0 +1,29 @@
    +interface MigrateDestinationBulkRollbackInterface {
    

    The interface.

  5. +++ b/core/modules/migrate/src/Plugin/MigrateIdMapInterface.php
    @@ -147,6 +147,14 @@ public function delete(array $source_id_values, $messages_only = FALSE);
    +  public function deleteBySourceIdsHash(array $source_ids_hashes);
    

    The MigrateIdMapInterface method addition.

and probably we should add a simple test just to prove that this works + documentation.

What about this plan?

claudiu.cristea’s picture

Title: Support bulk rollbacks » Support fast rollbacks
Issue summary: View changes
Status: Needs work » Needs review
Issue tags: +Needs tests
FileSize
26.8 KB
21.71 KB

As I suggested also on IRC, we should keep the door open for such destination plugins that know to perform fast rollbacks.

  • Removed the entity use-case. Right now there's no use-case in the patch but I will add one as a test. To be investigated in a followup if we can implement the new interface for some entities but here we just allow such cases.
  • Fast rollbacks can be of 2 types: batch or all-in-one rollbacks.
  • IS updated
  • interdiff is not too relevant.

Status: Needs review » Needs work

The last submitted patch, 13: support_fast_rollbacks-2821988-13.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
27.16 KB
1.51 KB

Fixed the batch rollback.

Status: Needs review » Needs work

The last submitted patch, 15: support_fast_rollbacks-2821988-15.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
21.92 KB
1.51 KB

Wrong patch.

Status: Needs review » Needs work

The last submitted patch, 17: support_fast_rollbacks-2821988-17.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
21.94 KB
830 bytes

Destination plugin class was not correctly referred.

Status: Needs review » Needs work

The last submitted patch, 19: support_fast_rollbacks-2821988-19.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
22.14 KB
1.73 KB
claudiu.cristea’s picture

Added tests.

@todo: Fix the edge case where the batch size is 1 >>> fallback to normal rollback.

Status: Needs review » Needs work

The last submitted patch, 22: support_fast_rollbacks-2821988-22.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
13.43 KB
33.54 KB

This looks ready for review.

claudiu.cristea’s picture

Docs fixing.

The last submitted patch, 24: support_fast_rollbacks-2821988-24.patch, failed testing.

claudiu.cristea’s picture

Issue summary: View changes
claudiu.cristea’s picture

Issue summary: View changes
claudiu.cristea’s picture

Issue summary: View changes

Updating IS.

claudiu.cristea’s picture

More Docs & IS fixes.

Status: Needs review » Needs work

The last submitted patch, 30: support_fast_rollbacks-2821988-30.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
phenaproxima’s picture

Assigned: Unassigned » phenaproxima

Self-assigning for review.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

pfrenssen’s picture

FileSize
33.59 KB

Rerolled against latest HEAD.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

pfrenssen’s picture

Rerolled against 8.4.x.

pfrenssen’s picture

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

This is a feature request so this should be rolled against 8.5.x now. I checked and the patch from #37 applies cleanly.

heddn’s picture

Status: Needs review » Needs work
Issue tags: +Migrate BC break

NW for the small first nit.

  1. +++ b/core/modules/migrate/src/Event/MigrateEvents.php
    @@ -46,6 +46,37 @@
    +   * deleted from a migration's map table (implying they were been rolled back).
    ...
    +   * from a migration's map table (implying they were been rolled back). The
    

    Nit: not sure if this is supposed to say, "they were rolled back" or "they were not rolled back". Clarification here would help.

  2. +++ b/core/modules/migrate/src/Plugin/MigrateDestinationFastRollbackInterface.php
    --- a/core/modules/migrate/src/Plugin/MigrateIdMapInterface.php
    +++ b/core/modules/migrate/src/Plugin/MigrateIdMapInterface.php
    
    +++ b/core/modules/migrate/src/Plugin/MigrateIdMapInterface.php
    @@ -138,6 +138,24 @@ public function messageCount();
    +  public function deleteMultiple(array $source_ids, $messages_only = FALSE);
    ...
    +  public function deleteAll($messages_only = FALSE);
    

    This is a BC break, but the chances of folks building their own map implementation are fairly low. To make this even more BC compatible, perhaps add another interface and implement it on Sql id map that has these methods. I think that the BC break is a bit of a non-starter for some maintainers of migrate, so splitting this out would take away that contention.

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

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

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

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

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.