From 8f94d8389795fbe890c6742a503f595ef75202ea Mon Sep 17 00:00:00 2001 From: webchick <drupal@webchick.net> Date: Tue, 25 Aug 2015 14:55:42 -0700 Subject: [PATCH] Issue #2545672 by mikeryan, benjy, phenaproxima: Handle various migration interruption scenarios --- core/modules/migrate/src/Entity/Migration.php | 8 ++ .../migrate/src/Entity/MigrationInterface.php | 9 +++ .../modules/migrate/src/MigrateExecutable.php | 7 ++ .../migrate/src/Tests/MigrateEventsTest.php | 13 +-- .../src/Tests/MigrateInterruptionTest.php | 81 +++++++++++++++++++ .../src/Plugin/migrate/source/DataSource.php | 10 ++- 6 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 core/modules/migrate/src/Tests/MigrateInterruptionTest.php diff --git a/core/modules/migrate/src/Entity/Migration.php b/core/modules/migrate/src/Entity/Migration.php index cb79a3ef238f..735e8833276a 100644 --- a/core/modules/migrate/src/Entity/Migration.php +++ b/core/modules/migrate/src/Entity/Migration.php @@ -458,6 +458,14 @@ public function getMigrationResult() { return \Drupal::keyValue('migrate_result')->get($this->id(), static::RESULT_INCOMPLETE); } + /** + * {@inheritdoc} + */ + public function interruptMigration($result) { + $this->setStatus(MigrationInterface::STATUS_STOPPING); + $this->setMigrationResult($result); + } + /** * {@inheritdoc} */ diff --git a/core/modules/migrate/src/Entity/MigrationInterface.php b/core/modules/migrate/src/Entity/MigrationInterface.php index 5a6192de1a1d..b39ae6a6daeb 100644 --- a/core/modules/migrate/src/Entity/MigrationInterface.php +++ b/core/modules/migrate/src/Entity/MigrationInterface.php @@ -206,6 +206,15 @@ public function setMigrationResult($result); */ public function getMigrationResult(); + /** + * Signal that the migration should be interrupted with the specified result + * code. + * + * @param int $result + * One of the MigrationInterface::RESULT_* constants. + */ + public function interruptMigration($result); + /** * Get the normalized process pipeline configuration describing the process * plugins. diff --git a/core/modules/migrate/src/MigrateExecutable.php b/core/modules/migrate/src/MigrateExecutable.php index f7cbd3f73904..fccb667bfcd3 100644 --- a/core/modules/migrate/src/MigrateExecutable.php +++ b/core/modules/migrate/src/MigrateExecutable.php @@ -298,10 +298,17 @@ public function import() { unset($sourceValues, $destinationValues); $this->sourceRowStatus = MigrateIdMapInterface::STATUS_IMPORTED; + // Check for memory exhaustion. if (($return = $this->checkStatus()) != MigrationInterface::RESULT_COMPLETED) { break; } + // If anyone has requested we stop, return the requested result. + if ($this->migration->getStatus() == MigrationInterface::STATUS_STOPPING) { + $return = $this->migration->getMigrationResult(); + break; + } + try { $source->next(); } diff --git a/core/modules/migrate/src/Tests/MigrateEventsTest.php b/core/modules/migrate/src/Tests/MigrateEventsTest.php index 65070d464f20..aa1aafafb535 100644 --- a/core/modules/migrate/src/Tests/MigrateEventsTest.php +++ b/core/modules/migrate/src/Tests/MigrateEventsTest.php @@ -95,8 +95,9 @@ public function testMigrateEvents() { $event = $this->state->get('migrate_events_test.map_save_event', []); $this->assertIdentical($event['event_name'], MigrateEvents::MAP_SAVE); - $this->assertIdentical($event['fields']['sourceid1'], 'dummy value'); - $this->assertIdentical($event['fields']['destid1'], 'dummy value'); + // Validating the last row processed. + $this->assertIdentical($event['fields']['sourceid1'], 'dummy value2'); + $this->assertIdentical($event['fields']['destid1'], 'dummy value2'); $this->assertIdentical($event['fields']['source_row_status'], 0); $event = $this->state->get('migrate_events_test.map_delete_event', []); @@ -105,13 +106,15 @@ public function testMigrateEvents() { $event = $this->state->get('migrate_events_test.pre_row_save_event', []); $this->assertIdentical($event['event_name'], MigrateEvents::PRE_ROW_SAVE); $this->assertIdentical($event['migration']->id(), $migration->id()); - $this->assertIdentical($event['row']->getSourceProperty('data'), 'dummy value'); + // Validating the last row processed. + $this->assertIdentical($event['row']->getSourceProperty('data'), 'dummy value2'); $event = $this->state->get('migrate_events_test.post_row_save_event', []); $this->assertIdentical($event['event_name'], MigrateEvents::POST_ROW_SAVE); $this->assertIdentical($event['migration']->id(), $migration->id()); - $this->assertIdentical($event['row']->getSourceProperty('data'), 'dummy value'); - $this->assertIdentical($event['destination_id_values']['value'], 'dummy value'); + // Validating the last row processed. + $this->assertIdentical($event['row']->getSourceProperty('data'), 'dummy value2'); + $this->assertIdentical($event['destination_id_values']['value'], 'dummy value2'); // Generate a map delete event. $migration->getIdMap()->delete(['data' => 'dummy value']); diff --git a/core/modules/migrate/src/Tests/MigrateInterruptionTest.php b/core/modules/migrate/src/Tests/MigrateInterruptionTest.php new file mode 100644 index 000000000000..ece13feb0ee6 --- /dev/null +++ b/core/modules/migrate/src/Tests/MigrateInterruptionTest.php @@ -0,0 +1,81 @@ +<?php + +/** + * @file + * Contains \Drupal\migrate\Tests\MigrateInterruptionTest. + */ + +namespace Drupal\migrate\Tests; + +use Drupal\migrate\Entity\Migration; +use Drupal\migrate\Event\MigratePostRowSaveEvent; +use Drupal\migrate\MigrateMessage; +use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Event\MigrateEvents; +use Drupal\migrate\MigrateExecutable; +use Drupal\simpletest\KernelTestBase; + +/** + * Tests interruptions triggered during migrations. + * + * @group migrate + */ +class MigrateInterruptionTest extends KernelTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = ['migrate', 'migrate_events_test']; + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + \Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_ROW_SAVE, + array($this, 'postRowSaveEventRecorder')); + } + + /** + * Tests migration interruptions. + */ + public function testMigrateEvents() { + // Run a simple little migration, which should trigger one of each event + // other than map_delete. + $config = [ + 'id' => 'sample_data', + 'migration_tags' => ['Event test'], + 'source' => ['plugin' => 'data'], + 'process' => ['value' => 'data'], + 'destination' => ['plugin' => 'dummy'], + 'load' => ['plugin' => 'null'], + ]; + + $migration = Migration::create($config); + + /** @var MigrationInterface $migration */ + $executable = new MigrateExecutable($migration, new MigrateMessage); + // When the import runs, the first row imported will trigger an interruption. + $result = $executable->import(); + + $this->assertEqual($result, MigrationInterface::RESULT_INCOMPLETE); + + // The status should have been reset to IDLE. + $this->assertEqual($migration->getStatus(), MigrationInterface::STATUS_IDLE); + } + + /** + * Reacts to post-row-save event. + * + * @param \Drupal\Migrate\Event\MigratePostRowSaveEvent $event + * The migration event. + * @param string $name + * The event name. + */ + public function postRowSaveEventRecorder(MigratePostRowSaveEvent $event, $name) { + $event->getMigration()->interruptMigration(MigrationInterface::RESULT_INCOMPLETE); + } + +} diff --git a/core/modules/migrate/tests/modules/migrate_events_test/src/Plugin/migrate/source/DataSource.php b/core/modules/migrate/tests/modules/migrate_events_test/src/Plugin/migrate/source/DataSource.php index da74c5a861a7..3e9fb014d88d 100644 --- a/core/modules/migrate/tests/modules/migrate_events_test/src/Plugin/migrate/source/DataSource.php +++ b/core/modules/migrate/tests/modules/migrate_events_test/src/Plugin/migrate/source/DataSource.php @@ -31,11 +31,15 @@ public function fields() { * {@inheritdoc} */ public function initializeIterator() { - return new \ArrayIterator(array(array('data' => 'dummy value'))); + return new \ArrayIterator([ + ['data' => 'dummy value'], + ['data' => 'dummy value2'], + ]); + } public function __toString() { - return ''; + return 'Sample data for testing'; } /** @@ -50,7 +54,7 @@ public function getIds() { * {@inheritdoc} */ public function count() { - return 1; + return 2; } } -- GitLab