Problem/Motivation

Source id is already Sql id_map getSourceIDsHash, but it is more efficient to sort it directly in Row.

The source id values should be ordered in the same order as the data returned by a source plugin's getIds() method.

Proposed resolution

Make the sort in Row->getSourceIdValues function.

Remaining tasks

Write a patch.

Comments

edysmp created an issue. See original summary.

edysmp’s picture

Status: Active » Needs review
StatusFileSize
new1.96 KB
heddn’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

What are the efficiencies gained by moving the logic? Is it performance? Plus needs tests.

mikeryan’s picture

For context, I see that the use case is #2698067: Make idlist command support multiple sourceids. Not a matter of efficiency, but generality - it's better to guarantee the proper ordering of multi-value source keys back at the source, than only when hashing them.

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

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should 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.

mitrpaka’s picture

StatusFileSize
new1.97 KB

Testing if needs re-roll and had some offset. Updating patch.

patching file core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
Hunk #1 succeeded at 183 (offset -5 lines).
patching file core/modules/migrate/src/Row.php
Hunk #1 succeeded at 109 (offset -5 lines).
edysmp’s picture

Status: Needs work » Needs review
mikeryan’s picture

Status: Needs review » Needs work

So far so good, now for testing.

mitrpaka’s picture

Status: Needs work » Needs review
StatusFileSize
new3.46 KB
new1.39 KB

Initial test case added.

mikeryan’s picture

Status: Needs review » Needs work
Issue tags: -Needs tests

Just a little documentation needed, I think.

  1. +++ b/core/modules/migrate/src/Row.php
    @@ -109,7 +109,7 @@ public function __construct(array $values = [], array $source_ids = [], $is_stub
        *   An array containing the values of the source identifiers.
    

    Let's specify that the result is ordered according to the order in $this->sourceIds.

  2. +++ b/core/modules/migrate/src/Row.php
    @@ -109,7 +109,7 @@ public function __construct(array $values = [], array $source_ids = [], $is_stub
    +    return array_merge(array_flip(array_keys($this->sourceIds)), array_intersect_key($this->source, $this->sourceIds));
    

    Four array functions in one line makes this a bit... dense - it takes running through what's happening here from the inner calls outwards to understand how it works. A comment would be helpful, explaining that array_flip(array_keys()) presents a keyed array in the properly order, and merging with array_intersect_key fills in the values.

mitrpaka’s picture

Status: Needs work » Needs review
StatusFileSize
new3.84 KB
new802 bytes

Initial documentation added. As I'm not native English speaking person, please help on proper wording. Thanks.

mikeryan’s picture

Status: Needs review » Reviewed & tested by the community

I think that's fine, thanks!

alexpott’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
@@ -183,20 +183,7 @@ public static function create(ContainerInterface $container, array $configuratio
   public function getSourceIDsHash(array $source_id_values) {

Hmmm ugh the docs for this say

It is public only for testing purposes.

We should have used reflection for that. Because this change could break existing migrate plugins. I guess we should try and work out how worth it this change actually is.

mikeryan’s picture

Assigned: Unassigned » mikeryan
mikeryan’s picture

Assigned: mikeryan » Unassigned
Priority: Normal » Minor
Status: Needs review » Postponed (maintainer needs more info)

Hrrm, yes, "public only for testing purposes" is an abomination not our best work - but, fixing that would be a matter for a separate issue.

So, I was going to say I don't see how this would break any existing migrate plugins. It's Row::getSourceIdValues() whose behavior is being expressly changed, and that change is from an unspecified ordering of the returned array to one that is ordered by the source ID definition, which shouldn't break anything unless it were depending on whatever order it happened to be getting (and note that A. Most often there's only one field, and B. Most of the time even with multiple fields they'll usually be in the right order anyway).

However, turning it over in my mind, there is actually a subtle BC break here - the $source_id_values argument to getSourceIDsHash() previously could be in any order because it sorted them, but now it will require the incoming array to already be sorted. As it happens (as near as I can see), all the core usages either pass through Row::getSourceIdValues() or are inherently sorted, but we can't guarantee that will always be the case. At this point, I'm questioning as @alexpott did whether this is worthwhile - it seems safer to just leave well enough alone and guarantee the source fields are sorted (if somehow the order of fields for a given row differed between runs, that would lead to some *very* difficult to diagnose problems, I think). If we are to go forward, at the very least getSourceIDsHash() needs to document that the input array needs to be properly sorted.

@edysmp/@mitrpaka - What do you think? Do you still want to pursue this?

mikeryan’s picture

Oh, but back to the original use case - we could still make Row::getSourceIdValues() sort its return value properly, but leave the sorting in getSourceIDsHash() alone.

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.

mikeryan’s picture

Status: Postponed (maintainer needs more info) » Needs work

Per my last comment, this could still be done if we remove the change to getSourceIDsHash().

Launching tests to confirm, but this likely will need a reroll at this point...

jofitz’s picture

Status: Needs work » Needs review
StatusFileSize
new2.46 KB
new3.16 KB

Removed the change to getSourceIDsHash() and updated array syntax.

mikeryan’s picture

Status: Needs review » Reviewed & tested by the community

Looks good, thanks!

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/modules/migrate/src/Row.php
    @@ -106,10 +106,14 @@ public function __construct(array $values = [], array $source_ids = [], $is_stub
    +    // First, return the array of keys properly ordered with
    +    // array_flip(array_keys()). Then, fill in values by merging with
    +    // array_intersect_key().
    

    This comment should just describe we want to occur rather than be a verbal description of the implementation. Ie. something like Return the source values in the same order as the source identifiers. Mind you we could consider omitting this given the @return just above.

  2. +++ b/core/modules/migrate/src/Row.php
    @@ -106,10 +106,14 @@ public function __construct(array $values = [], array $source_ids = [], $is_stub
    +    return array_merge(array_flip(array_keys($this->sourceIds)), array_intersect_key($this->source, $this->sourceIds));
    

    Why not just array_merge($this->sourceIds, array_intersect_key($this->source, $this->sourceIds)); since the keys are already properly ordered and array_merge() will replace the values using the array_interest_key() return?

jofitz’s picture

Status: Needs work » Needs review
StatusFileSize
new2.28 KB
new793 bytes
  1. Removed the redundant comment (the @return gives enough information and is immediately above).
  2. Simplified the return statement, as recommended by @alexpott and stepped through the code to check that it still made sense (it does).
mikeryan’s picture

Status: Needs review » Reviewed & tested by the community

Changes look good to me.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 22: sort_surceids_in_row-2698023-22.patch, failed testing.

jofitz’s picture

Status: Needs work » Reviewed & tested by the community

Re-tested - all fine, back to RTBC.

alexpott’s picture

Status: Reviewed & tested by the community » Needs review

Does this mean we can remove...

    // When looking up the destination ID we require an array with both the
    // source key and value, e.g. ['nid' => 41]. In this case, $source_id_values
    // need to be ordered the same order as $this->sourceIdFields().
    // However, the Migration process plugin doesn't currently have a way to get
    // the source key so we presume the values have been passed through in the
    // correct order.
    if (!isset($source_id_values[0])) {
      $source_id_values_keyed = [];
      foreach ($this->sourceIdFields() as $field_name => $source_id) {
        $source_id_values_keyed[] = $source_id_values[$field_name];
      }
      $source_id_values = $source_id_values_keyed;
    }

And just do:

return hash('sha256', serialize(array_map('strval', array_values($source_id_values))));

The code that checks if (!isset($source_id_values[0])) { looks super brittle.

heddn’s picture

Status: Needs review » Needs work

Re #26: Yes, we should be able to get rid of that. Do we have tests? I'm sure we do, otherwise things would start falling on their face.

jofitz’s picture

Status: Needs work » Needs review
StatusFileSize
new3.65 KB
new1.38 KB

Let's see what the testbot thinks of that change...

heddn’s picture

Status: Needs review » Reviewed & tested by the community

And back to RTBC.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 28: sort_surceids_in_row-2698023-28.patch, failed testing.

heddn’s picture

Status: Needs work » Reviewed & tested by the community

Random testbot failure.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 28: sort_surceids_in_row-2698023-28.patch, failed testing.

jofitz’s picture

Status: Needs work » Reviewed & tested by the community

Come on, testbot! Back to RTBC.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 28: sort_surceids_in_row-2698023-28.patch, failed testing.

joelpittet’s picture

Status: Needs work » Reviewed & tested by the community
gábor hojtsy’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: +Baltimore2017
+++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
@@ -183,20 +183,7 @@ public static function create(ContainerInterface $container, array $configuratio
   public function getSourceIDsHash(array $source_id_values) {
-    // When looking up the destination ID we require an array with both the
-    // source key and value, e.g. ['nid' => 41]. In this case, $source_id_values
-    // need to be ordered the same order as $this->sourceIdFields().
-    // However, the Migration process plugin doesn't currently have a way to get
-    // the source key so we presume the values have been passed through in the
-    // correct order.
-    if (!isset($source_id_values[0])) {
-      $source_id_values_keyed = [];
-      foreach ($this->sourceIdFields() as $field_name => $source_id) {
-        $source_id_values_keyed[] = $source_id_values[$field_name];
-      }
-      $source_id_values = $source_id_values_keyed;
-    }
-    return hash('sha256', serialize(array_map('strval', $source_id_values)));
+    return hash('sha256', serialize(array_map('strval', array_values($source_id_values))));
   }

Hm, so this is a public function but the change affects what it expects as its argument, so at least it would come with a docs change? What about others possibly invoking this method?

heddn’s picture

StatusFileSize
new766 bytes
tomogden’s picture

Status: Needs review » Reviewed & tested by the community

#36: Doc change now specifies what is expected.

gábor hojtsy’s picture

Status: Reviewed & tested by the community » Needs review

Hm, as per #15, @mikeryan was leaning to keep the behavior as-is in my reading. It would be nice to get a confirmation form him or to keep the logic in line with his comment.

At this point, I'm questioning as @alexpott did whether this is worthwhile - it seems safer to just leave well enough alone and guarantee the source fields are sorted (if somehow the order of fields for a given row differed between runs, that would lead to some *very* difficult to diagnose problems, I think).

(emphasis mine)

mikeryan’s picture

Status: Needs review » Needs work

Right, in #26 @alexpott asked if we could remove the sorting code from getSourceIDsHash(), and in response it was removed. However, as I pointed out in #15, although existing calls to getSourceIDsHash() will pass the source in sorted, if anyone in contrib/custom modules is calling it without sorting the keys, they'll get a very subtle break in that the hashes won't necessarily match up. I think we should leave getSourceIDsHash() as-is - it needs to continue guaranteeing that the IDs are sorted before hashing.

catch’s picture

#37 has the documentation change but is missing the rest of the patch. Does it just need to be combined with #28?

This issue took a couple of turns so tagging for issue summary update.

mohit_aghera’s picture

Status: Needs work » Needs review
StatusFileSize
new4.14 KB
new766 bytes

Merging patch of #28 and #37

mohit_aghera’s picture

Issue summary: View changes
mohit_aghera’s picture

StatusFileSize
new3.04 KB
new1.38 KB
+++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
@@ -183,20 +186,7 @@ public static function create(ContainerInterface $container, array $configuratio
-    // When looking up the destination ID we require an array with both the
-    // source key and value, e.g. ['nid' => 41]. In this case, $source_id_values
-    // need to be ordered the same order as $this->sourceIdFields().
-    // However, the Migration process plugin doesn't currently have a way to get
-    // the source key so we presume the values have been passed through in the
-    // correct order.
-    if (!isset($source_id_values[0])) {
-      $source_id_values_keyed = [];
-      foreach ($this->sourceIdFields() as $field_name => $source_id) {
-        $source_id_values_keyed[] = $source_id_values[$field_name];
-      }
-      $source_id_values = $source_id_values_keyed;
-    }
-    return hash('sha256', serialize(array_map('strval', $source_id_values)));
+    return hash('sha256', serialize(array_map('strval', array_values($source_id_values))));

Removing this change mentioned by Mike.

mikeryan’s picture

Status: Needs review » Reviewed & tested by the community
gábor hojtsy’s picture

Status: Reviewed & tested by the community » Needs work

Thanks for moving this forward. Just some small updates left AFAIS.

+++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
@@ -174,7 +174,10 @@ public static function create(ContainerInterface $container, array $configuratio
-   * It is public only for testing purposes.
+   * It is public only for testing purposes. The source id values should be
+   * ordered in the same order as the data returned by a source plugin's
+   * getIds() method.
+   * @internal

This added part of the comment is not true anymore, so it should be removed. IMHO a newline before @internal should also be added.

gaurav.kapoor’s picture

Status: Needs work » Needs review
StatusFileSize
new2.87 KB
new568 bytes
mikeryan’s picture

Status: Needs review » Reviewed & tested by the community

  • Gábor Hojtsy committed 5c46a67 on 8.4.x
    Issue #2698023 by Jo Fitzgerald, mitrpaka, mohit_aghera, gaurav.kapoor,...
gábor hojtsy’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: -Needs issue summary update

Thanks for the update. Also this now only does what the issue summary said, so no need to update the issue summary. The change is backwards compatible given that no ordering was ensured on getSourceIdValues() before. Committed!

  • Gábor Hojtsy committed ae40afd on 8.3.x
    Issue #2698023 by Jo Fitzgerald, mitrpaka, mohit_aghera, gaurav.kapoor,...

Status: Fixed » Closed (fixed)

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