Problem/Motivation

From #2281691: User interface for migration-based upgrades. Much of the exception handling in the Migrate UI is problematic, including:

  • Throwing or catching \Exception instead of something more specific.
  • Very long try/catch blocks for unclear purpose.
  • Catching exceptions without reporting what they were or why.
  • Possibly relying on exceptions for normal/expected validation code flow?

Much has happened to the code since this original review, and the once large form is now split into several forms. See #2918761: Break up MigrateUpgradeForm into smaller forms and #2682585: Rename MigrationCreationTrait as it no longer creates migrations - it configures them. Each of the six points includes a new note stating where the relevant piece of code now resides. Of the original 6 points, 2 are no longer relevant.

The following points are from reviews on #2281691: User interface for migration-based upgrades:

  1. This is now in CredentialForm.php, it is no longer a massive try block.
    +++ b/core/modules/drupal_upgrade/src/Form/MigrateUpgradeForm.php
    @@ -0,0 +1,1196 @@
    +    try {
    +
    ...
    +    catch (\Exception $e) {
    

    This is a MASSIVE try block. Which calls are we catching exceptions for? Why can't they be individually wrapped with classed exceptions?
    Also there is a blank line.

  2. This is now in CredentialForm.php, part of the try block associated with #6.
    +++ b/core/modules/migrate_drupal/src/MigrationCreationTrait.php
    @@ -0,0 +1,205 @@
    +    catch (\Exception $e) {
    ...
    +      throw new \Exception('Source database does not contain a recognizable Drupal version.');
    

    Is there a classed exception to catch or throw instead? Using \Exception is not as ideal

  3. This is now in MigrationConfigurationTrait.php
    +++ b/core/modules/migrate_drupal/src/MigrationCreationTrait.php
    @@ -0,0 +1,222 @@
    +    catch (\Exception $e) {
    +      // The table might not exist for example in tests.
    +    }
    

    I think @tim.plunkett mentioned this too, but this is way too broad a catch. We should have a followup issue to fix this because it is just swallowing any error. If there is a specific exception that is happening then we should handle it more directly or look at underlying causes.

  4. This looks fixed. This is now in MigrationConfigurationTrait.php and the PluginNotFoundException was removed (somewhere) from the getMigrations method.
    +++ b/core/modules/migrate_drupal/src/MigrationCreationTrait.php
    @@ -0,0 +1,222 @@
    +      // Migrations which are not applicable given the source and destination
    +      // site configurations (e.g., what modules are enabled) will be silently
    +      // ignored.
    +      catch (RequirementsException $e) {
    +      }
    +      catch (PluginNotFoundException $e) {
    +      }
    

    This seems a fragile way to handle this; is this related to the existing followup about detecting applicable migrations?

  5. This is now in MigrationConfigurationTrait.php and can be fixed in this issue.
    +++ b/core/modules/migrate_drupal/src/MigrationCreationTrait.php
    @@ -0,0 +1,222 @@
    +      catch (\PDOException $e) {
    +        $version_string = FALSE;
    +      }
    

    We are doing this catch if the system table exists, but not if the key_value table exists.

  6. This is now in CredentialForm.php
    +++ b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
    @@ -0,0 +1,1227 @@
    +    catch (\Exception $e) {
    +      $error_message = [
    +        '#type' => 'inline_template',
    +        '#template' => '{% trans %}Resolve the issue below to continue the upgrade.{% endtrans%}{{ errors }}',
    +        '#context' => [
    +          'errors' => [
    +            '#theme' => 'item_list',
    +            '#items' => [$e->getMessage()],
    +          ],
    +        ],
    +      ];
    

    Also (again), \Exception is way generic, and using a try/catch like this to handle expected validation code flow is problematic.

Proposed resolution

Evaluate the types of exceptions used, the reasons they are being caught, the use of try/catch for code flow, and the potential loss/hiding of debug information.

  1. No longer relevant.
  2. Reworked that section of code.
  3. Changed to a PDOException in this patch
  4. No longer relevant. Fixed elsewhere
  5. Accessing the key_value tables is change to a try/catch in this patch
  6. Changed from catching Exception to catching DatabaseException in this patch

Remaining tasks

TBD

User interface changes

N/A

API changes

TBD

Data model changes

TBD

CommentFileSizeAuthor
#60 interdiff-55-60.txt3.82 KBquietone
#60 2679835-60.patch8.46 KBquietone
#55 interdiff.txt3.3 KBquietone
#55 2679835-55.patch8.4 KBquietone
#54 interdiff.txt4.21 KBquietone
#54 2679835-54.patch7.07 KBquietone
#49 2679835-49.patch6.75 KBjofitz
#49 interdiff-47-49.txt690 bytesjofitz
#47 2679835-47.patch6.75 KBjofitz
#47 interdiff-45-47.txt831 bytesjofitz
#45 2679835-45.patch6.74 KBjofitz
#36 interdiff.txt651 bytesquietone
#36 2679835-36.patch6.78 KBquietone
#34 interdiff.txt2.95 KBquietone
#34 2679835-34.patch6.89 KBquietone
#31 interdiff.txt2.07 KBquietone
#31 2679835-31.patch5.77 KBquietone
#29 interdiff.txt1.59 KBquietone
#29 2679835-30.patch5.83 KBquietone
#28 interdiff.txt3.18 KBquietone
#28 2679835-28.patch4.87 KBquietone
#26 interdiff.txt1.27 KBquietone
#26 2679835-26.patch4.13 KBquietone
#23 2679835-23.patch3.87 KBquietone
#12 2679835-12.patch4.61 KBheddn
#12 interiff_7-12.txt3.41 KBheddn
#7 2679835-7.patch1.27 KBquietone
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

xjm created an issue. See original summary.

xjm’s picture

Issue summary: View changes
mikeryan’s picture

This is a MASSIVE try block. Which calls are we catching exceptions for? Why can't they be individually wrapped with classed exceptions?

It originally wrapped a single call to $this->createMigrations(). At the moment, I think only the getMigrationTemplates() call at the beginning of the try needs to be in here, and that's throwing \Exception (see next point).

Is there a classed exception to catch or throw instead? Using \Exception is not as ideal

There isn't any existing exception that's relevant here - we should create a DrupalVersionException or somesuch.

I think @tim.plunkett mentioned this too, but this is way too broad a catch. We should have a followup issue to fix this because it is just swallowing any error. If there is a specific exception that is happening then we should handle it more directly or look at underlying causes.

@abhishek-anand? Did you have a particular testing problem inspiring that catch?

This seems a fragile way to handle this; is this related to the existing followup about detecting applicable migrations?

I.e., catching PluginNotFoundException and RequirementsException - yes, this is for skipping inapplicable migrations. There are two scenarios - where a migration references a source or destination plugin which is defined by a module which is not enabled (thus, we do not want to perform migration for that module), we get PluginNotFoundException. And if the plugin's module is enabled but there's a missing requirement (e.g., the corresponding module is not enabled in the source database), checkRequirements() throws RequirementsException.

We are doing this catch if the system table exists, but not if the key_value table exists.

Yes, the key_value branch should also be prepared for PDO exceptions.

Also (again), \Exception is way generic, and using a try/catch like this to handle expected validation code flow is problematic.

See the first two points above.

abhishek-anand’s picture

Hi Mike,

Did you have a particular testing problem inspiring that catch?

No, I had to get the system data, so I reused to code from \Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase::getSystemData

Didn't pay enough attention to the catch, just replicated it.

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

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.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.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

quietone’s picture

Status: Active » Needs review
FileSize
1.27 KB

This patch only addresses item #5 in the IS.

maxocub’s picture

Assigned: Unassigned » maxocub

Assigning for review.

maxocub’s picture

Assigned: maxocub » Unassigned
Status: Needs review » Needs work

I think the item #5 from the issue summary is fixed with this patch, but we need to keep thinking about the other points and @mikeryan's suggestions in #3. Back to needs work.

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

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now 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.

mikeryan’s picture

Issue tags: +Migrate UI
heddn’s picture

Status: Needs work » Needs review
FileSize
3.41 KB
4.61 KB

I think this addresses all the points in the IS. Let's see how tests fair. I did all the work, then reviewed back to see that there was already a patch in #7 that does about the same thing. So, there's also an interdiff also against @quietone's work.

Status: Needs review » Needs work

The last submitted patch, 12: 2679835-12.patch, failed testing.

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

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now 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.

quietone’s picture

Assigned: Unassigned » quietone

Assigning to myself.

quietone’s picture

Assigned: quietone » Unassigned

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.

quietone’s picture

Issue summary: View changes
Status: Needs work » Postponed
quietone’s picture

Issue summary: View changes
Status: Postponed » Needs work
Issue tags: +Needs reroll

No longer postponed, and will need a reroll.

quietone’s picture

Status: Needs work » Postponed

It was reverted, back to postponed.

quietone’s picture

Status: Postponed » Needs work
quietone’s picture

Issue summary: View changes

The try catch blocks in the IS have moved to new files. As a first step to identify what needs to be done, add the new filenames to the IS.

quietone’s picture

Let's try a reroll.

quietone’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 23: 2679835-23.patch, failed testing. View results

quietone’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
4.13 KB
1.27 KB

Didn't think that was going to work. How about this.

Status: Needs review » Needs work

The last submitted patch, 26: 2679835-26.patch, failed testing. View results

quietone’s picture

quietone’s picture

Status: Needs work » Needs review
FileSize
5.83 KB
1.59 KB

Add a catch for BadPluginDefinitionException to CredentialForm and a wider catch \Exception in the IncrementalForm.

Status: Needs review » Needs work

The last submitted patch, 29: 2679835-30.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

quietone’s picture

Status: Needs work » Needs review
FileSize
5.77 KB
2.07 KB

I think I uploaded a wrong patch.

heddn’s picture

  1. +++ b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php
    @@ -199,29 +202,50 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    +    catch (\Exception $e) {
    

    Do we still need to catch /Exception here or can we catch something else?

  2. +++ b/core/modules/migrate_drupal_ui/src/Form/IncrementalForm.php
    @@ -105,7 +105,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    +      catch (\Exception $exception) {
             // Hide DB exceptions and forward to the DB credentials form. In that
             // form we can more properly display errors and accept new credentials.
    

    We need to update the comment here then. If we are catching \Exception, we are hiding all errors, not just from the database.

heddn’s picture

Status: Needs review » Needs work
quietone’s picture

Status: Needs work » Needs review
FileSize
6.89 KB
2.95 KB

1. Changed to DatabaseException
2. Now that the next step for the Incremental form is the Credential form, there isn't anything to validate here. The validate function can be removed.

heddn’s picture

Status: Needs review » Reviewed & tested by the community

There are no more large try/catch blocks. There is no more generic catching of \Exception. The only thing I could find was a few unused Use statements, but that can be fixed on commit. IS seems up to date. LGTM.

BTW, I did some work on this way back in #10, but things have changed dramatically since then and I don't think any of my changes from that patch are actually even in the final version. So I think that still means I can RTBC this thing.

CredentialForm:

use Drupal\Core\Database\DatabaseNotFoundException;
use Drupal\Core\Database\DatabaseAccessDeniedException;
quietone’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
6.78 KB
651 bytes

Removing the two unused statements.

heddn’s picture

Status: Needs review » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 36: 2679835-36.patch, failed testing. View results

alexpott’s picture

Status: Needs work » Reviewed & tested by the community

Testbot had a hiccup re-rtbcing

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php
    @@ -199,29 +202,49 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    +    $error_message = [
    +      '#title' => $this->t('Resolve the issue below to continue the upgrade.'),
    +      '#theme' => 'item_list',
    +    ];
    

    We never have more than one item in the list as far as I can see so I think we don't need the item list anymore. Which means we can remove all the rendering.

  2. +++ b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php
    @@ -199,29 +202,49 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    +    catch (BadPluginDefinitionException $e) {
    

    PHPStorm reckons this is never thrown by setupMigrations. Which is interesting.

  3. +++ b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php
    @@ -199,29 +202,49 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    +    catch (RequirementsException $e) {
    

    How can this be thrown? It's explicitly caught in \Drupal\migrate_drupal\MigrationConfigurationTrait::getMigrations()?

alexpott’s picture

Category: Bug report » Task
Priority: Major » Normal

I think this is just a normal task. There's no bug that we're fixing explicitly here.

quietone’s picture

Status: Needs work » Postponed

Postponing on #2562845: Validate file path on Credential form which is also changing the error messages.

jofitz’s picture

Status: Postponed » Needs work

#2562845: Validate file path on Credential form is now in so no longer postponed.

jofitz’s picture

Assigned: Unassigned » jofitz
Issue tags: +Needs reroll

I'll take a look at the reroll.

jofitz’s picture

Assigned: jofitz » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
6.74 KB

Re-rolled.

Status: Needs review » Needs work

The last submitted patch, 45: 2679835-45.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jofitz’s picture

Status: Needs work » Needs review
FileSize
831 bytes
6.75 KB

Fix test failures.

Status: Needs review » Needs work

The last submitted patch, 47: 2679835-47.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jofitz’s picture

Status: Needs work » Needs review
FileSize
690 bytes
6.75 KB

Resolve the latest test failures.

Status: Needs review » Needs work

The last submitted patch, 49: 2679835-49.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jofitz’s picture

Hmm, I'm not sure I've grasped this fully. @quietone would you mind taking over?

quietone’s picture

Assigned: Unassigned » quietone

Not at all.

quietone’s picture

@Jo Fitzgerald, I don't know why but the migrations aren't getting the database connection.

quietone’s picture

Status: Needs work » Needs review
FileSize
7.07 KB
4.21 KB

Let's try this.

quietone’s picture

Now that it has passed tests, time for some cleanup and I wasn't working from head when making the previous patch. This removes the renderer service from the Credential form and adds another if block in the CredentialFrom:validateForm() for readability. At least I find it easier to skim through that code now.

Haven't run the test locally so here goes.

quietone’s picture

Assigned: quietone » Unassigned

All ready for a review.

heddn’s picture

Assigned: Unassigned » heddn

Assigning to myself for review this week.

phenaproxima’s picture

Title: Exception handling in Migrate UI » Improve exception handling in Migrate UI
Status: Needs review » Needs work

This is a definite improvement. Awesome work!

  1. +++ b/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php
    @@ -56,7 +57,7 @@ protected function getSystemData(Connection $connection) {
    +    catch (\PDOException $e) {
    

    Is this the exception we should catch? I know DatabaseExceptionWrapper is a thing, and it always wraps a \PDOException, so it might help to catch that instead (or its interface, DatabaseException)? Not sure what the best course of action is here.

  2. +++ b/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php
    @@ -183,10 +184,18 @@ protected function getLegacyDrupalVersion(Connection $connection) {
    +      catch (\PDOException $e) {
    +        $version_string = FALSE;
    +      }
    

    Same here.

  3. +++ b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php
    @@ -220,31 +211,48 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    +        $this->errors[$database['driver'] . '][database'] = $e->getMessage();
    

    The key here is very hard to parse at a glance. Can we do this in two lines, and re-use $error_key elsewhere in the method:

    $error_key = $database['driver'] . '[database';
    $this->errors[$error_key] = $e->getMessage();
    
  4. +++ b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php
    @@ -220,31 +211,48 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    +      // Get the Drupal version of the source database.
    

    Let's move this comment above the if (!$this->errors) and expand it to "Validate the Drupal version of the source database."

  5. +++ b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php
    @@ -220,31 +211,48 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    +      // Setup migrations and save form data to private store.
    

    Let's move this above the if (!$this->errors).

phenaproxima’s picture

Assigned: heddn » Unassigned

Unassigning.

quietone’s picture

@phenaproxima, Thanks for the quick review.

1 and 2. I wasn't sure either but the DatabaseExceptionWrapper doc states "This wrapper class serves only to provide additional debug information."" and that seems like a good thing. Therefor these exceptions are changed to the DatabaseExceptionWrapper.
3,4,5. Fixed.

phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community

I like it! RTBC once green on all backends.

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.

catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed 543a559 and pushed to 8.7.x. Thanks!
Fixed this on commit:


FILE: .../core/modules/migrate_drupal/src/MigrationConfigurationTrait.php
----------------------------------------------------------------------
FOUND 0 ERRORS AND 1 WARNING AFFECTING 1 LINE
----------------------------------------------------------------------
 197 | WARNING | [x] A comma should follow the last multiline array
     |         |     item. Found: 'system'
----------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------

  • catch committed 543a559 on 8.7.x
    Issue #2679835 by quietone, Jo Fitzgerald, heddn, alexpott, phenaproxima...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.