Problem/Motivation

In order to enable recording of the last imported date in a migration, the yaml key:

trackLastImported: true

must be set in the migration yaml. This is the only configuration key throughout the core migration system, including source, process, and destination configuration parameters which uses a camel case key instead of a snake case key.

In addition, this key and how to use it is not documented anywhere in the migration code. There are many configuration keys documented in SourcePluginBase, such as track_changes, and high_water_property. trackLastImported is a key on the main migration, however, not on the base source plugin. The main migration documentation consists of, in its entirety:

/**
 * Defines the Migration plugin.
 *
 * The migration plugin represents one single migration and acts like a
 * container for the information about a single migration such as the source,
 * process and destination plugins.
 */

In addition to all that, we have no test coverage of the trackLastImported feature.

Proposed Resolution

Deprecate trackLastImported, isTrackLastImported(), setTrackLastImported(), and getTrackLastImported() and always track. See #33, #38, and #40.

Remaining tasks

User interface changes

API changes

Migration YAML will deprecate support for trackLastImported. The last import time is now always saved.

Data model changes

Release notes snippet

Migrations will now always record a timestamp for the last date each row was imported.

Original Issue Summary

Setting the following `track_last_imported: true` in a migration.yml does not result in the migration property being set. The migrat_map database for the migration has no data save under the "last_imported" field.

It can be set in a custom migration but that should not be necessary. It should be supported the same way 'track_changes', 'skip_count' and 'cache_counts' are.

Steps to reproduce

1. Add `track_last_imported: true` to the source: property of your migration.
2. Import configuration.
3. Run the migration.
4. Inspect the database table 'migrate_map_' and validate that the db field 'last_imported' is 0 for all rows that were just imported by the migration. The setting had no effect.

Proposed resolution

Use the current property name for tracking last import, 'trackLastImported'. Unfortunately, this is not using snake_case convention.
Add tests to prove that tracking last import works when the the migration uses a property name of 'trackLastImported'.

id: test
trackLastImported: true

Or

Should the property name be changed to 'track_last_imported'

Its related to this issue https://www.drupal.org/project/migrate_plus/issues/3082078 but that issue is against migrate_plus, which is probably not the place to get it resolved.

Issue fork drupal-3198608

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

swirt created an issue. See original summary.

swirt’s picture

swirt’s picture

Status: Active » Needs review

This should be credited to @kevinquillen as it was his proposed solution on the other issue.

swirt’s picture

Steps to validate

1. Add `track_last_imported: true` to the source: property of your migration.
2. Import configuration.
3. Run the migration.
4. Inspect the database table 'migrate_map_' and validate that the db field 'last_imported' contains a timestamp for all rows that were just imported by the migration.

swirt’s picture

StatusFileSize
new1.28 KB
quietone’s picture

Version: 8.9.x-dev » 9.2.x-dev
StatusFileSize
new4.87 KB

Add `track_last_imported: true` to the source: property of your migration.

The track last imported is a property of the migration and should be set there. And, in fact, it is but the name is trackLastImported. If that is used it will work.

id: test
trackLastImported: true

I am not sure if the property name should change or not. It is used as a variable name in a few places which I guess adds to the confusion.

(9.2.x)$ grep -ri track_last_imported core/modules/migrate
core/modules/migrate/src/Plugin/MigrationInterface.php:   * @param bool $track_last_imported
core/modules/migrate/src/Plugin/MigrationInterface.php:  public function setTrackLastImported($track_last_imported);
core/modules/migrate/src/Plugin/Migration.php:  public function setTrackLastImported($track_last_imported) {
core/modules/migrate/src/Plugin/Migration.php:    $this->trackLastImported = (bool) $track_last_imported;

This patch add tests to show that Sql.php save a timestamp to 'last_imported' when tracking is enabled and that 'last_imported' is empty when it is not set. Also, added a test of getting the trackLastImported to MigrationTest.

swirt’s picture

Good point about setting it in the right spot as a property of the migration rather than the source. It seems like an anti-pattern to set it with camelCase. Aren't yml config properties on migrations pretty standardised on snake_case?

quietone’s picture

Issue summary: View changes

@swirt, yes, the convention is snake_case and I guess it should change. If someone out there is using trackLastImported though we have to maintain BC and I don't see anything in the deprecation policyfor class properties. I was planning on asking in Slack or the next migrate meeting.

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.

natedouglas’s picture

StatusFileSize
new4.87 KB

Reroll for 9.1.x for those of us who need it.

quietone’s picture

@natedouglas, thanks for the patch for 9.1.x. If the patch is working for you add a comment explaining the circumstances. It will help move the issue towards RTBC is there a report that it works. Thx.

indytechcook’s picture

Status: Needs review » Reviewed & tested by the community
StatusFileSize
new4.91 KB

Updated Patch.

indytechcook’s picture

StatusFileSize
new4.87 KB

Let's try that again

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 14: track-last-updated-tests-3198608-13.patch, failed testing. View results

indytechcook’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new4.87 KB

I promise I'm not dumb. Another reroll of the patch with fixed spelling mistakes.

larowlan’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: +Needs issue summary update

Thanks folks, so it looks like the final outcome here is that we're adding new tests to support trackLastImported - so can we get an issue summary and title update to support the resolution

mikelutz’s picture

No, That is not the final outcome, I think we will go ahead and add support for snake case and deprecate the camel case and add a bunch of documentation.

@indytechcook, please do not RTBC your own patches. You may set your own patches to 'needs review', and a community member will review them and set them to reviewed and tested by the community if appropiate. This issue still needs work.

mikelutz’s picture

Title: track_last_imported configuration not supported. » trackLastImported YAML key to enable tracking the last import date of a row is undocumented and confusing.
Issue summary: View changes
Status: Needs review » Needs work
mikelutz’s picture

Category: Bug report » Task

And now that I went through updating all that, I must ask.. why is this even a configuration option, why don't we just track this all the time? I don't think there is a performance issue, to add one more column (that already exists) into a database insert/update (which already exists)

The feature is completely undocumented, and the data is inaccessible without looking into the database, or using migrate tools, so I don't see any BC implications.

At worst, if there actually is a performance issue, It seems like something like this should be opt-out, not opt-in. This seems like pretty useful information for people doing custom migrations.

Thoughts?

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new6.88 KB

@mikelutz, thanks.

This patch does steps 1, 3, and 5 in the Remaining steps. There is no interdiff because this is a new approach.

todo:
2) Deprecate use of trackLastImported and standardize on using snake case for migration configuration keys. I don't see anything in href="https://www.drupal.org/node/2856615">Drupal core deprecation policy about how to deprecate a class property. I guess I am missing something?
4) Create a follow-up (novice?) ticket to improve documentation in the Migration class in general, giving more details about all the supported keys.

quietone’s picture

StatusFileSize
new6.67 KB
new469 bytes
quietone’s picture

I was curious about where this might be used. I found that there are two uses of setTrackLastImported(TRUE); in contrib. And then found an older related issue, which was closed as works as designed. But now we are dealing with all the confusion.

quietone’s picture

Issue summary: View changes

Follow up made #3226401: Add documentation of migration yml files and it has a patch available for review.

That just leaves whatever needs to be done remove the property.

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.

mikelutz’s picture

Status: Needs review » Needs work

Deprecating a class property requires implementing __get and __set magic methods that detect when people attempt to write/read from the old property and then triggers an error and writes/reads the new property instead.

Technically we would also default the new property to false while triggering an error if it isn't set in a migration and then switch the default to true in D10, but I really don't think we need a shim for the behavior change here. The change is not breaking, we are simply storing additional information with each run. If trackLastImported didn't already exist as an opt-in then we would have absolutely no problem adding this tracking as an opt-out feature in any minor release, and given the lack of documentation around trackLastImported, I'm of the opinion that it's use was confined to a few people who turned it on because they happened to stumble across the fact that they could. I think for BC purposes we should make sure we turn it off and trigger an error if anybody explicitly set trackLastImported to false, but for anybody that did not use the key at all, I'm content to turn this extra tiny bit of data storage on. Again, the column already exists, we should just use it.

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new2.87 KB
new8.66 KB

@mikelutz, thanks.

I didn't do anything about deprecating the property because there is I can't find anything in Drupal deprecation policy about it. Nor could I find an issue on the topic. Anyone know of any?

This patch changes the default to FALSE and includes a first attempt at the deprecations, with tests. The deprecation message does need a link to the still to be created change record.

mikelutz’s picture

Status: Needs review » Needs work
+++ b/core/modules/migrate/src/Plugin/Migration.php
@@ -142,6 +157,13 @@ class Migration extends PluginBase implements MigrationInterface, RequirementsIn
+  /**
+   * Track time of last import if TRUE.
+   *
+   * @var bool
+   */
+  protected $track_last_imported = TRUE;
+

We don't actually have to change the name of this variable, in fact, we shouldn't as snake case is appropriate for an object member. This whole migration class loops through the configuration and sets an object member for whatever happens to be there, which we need to fix anyway in a follow up issue, as future versions of php are going to complain about that anyway. Just default ::trackLastImported to TRUE, in the constructor, check for $configuration['trackLastImported'] if it's set, throw a deprecation error. if it's set and false, set ::trackLastImported to false and throw a deprecation error. If $configuration['track_last_imported'] is set and false, set ::trackLastImported to FALSE. unset both configuration keys afterward if they exist. None of the rest of the logic should have to change. We want to deprecate the configuration key, but we don't need to touch the variable name.

mikelutz’s picture

Issue tags: +Portland2022
quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new7.42 KB

@mikelutz, thanks for the review.

I admit I had trouble unpacking #28 but I do get the intent, which I agree with. However, I don't think that the default value should change from FALSE to TRUE. With that in mind I tried to implement a simpler approach and thus no interdiff. One question that came up was should we handle the case where both trackLastImported and track_last_imported defined?

Status: Needs review » Needs work

The last submitted patch, 30: 3198608-29.patch, failed testing. View results

quietone’s picture

Status: Needs work » Needs review

random fail, Drupal\Tests\quickedit\FunctionalJavascript\QuickEditFileTest::testRemove

mikelutz’s picture

I'll take look when I get into the conference room. Why don't you think we should just enable this by default? I'm still of the opinion it's a non breaking change that would improve developer experience with near zero performance impact, but I'd love to understand any objections. I may very well be missing something.

quietone’s picture

StatusFileSize
new7.79 KB

Just seems wrong to change behavior. But your right that it is a non breaking change. This should be the patch you wanted in #28.

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.

longwave’s picture

I didn't realise this feature existed. I was looking in the migrate map tables for something unrelated and spotted that the last import timestamp was 0; I agree with #20 and #33 that this should be enabled by default, and I'm not even sure why we need a flag to control it.

mikelutz’s picture

I really agree with longwave that we don't need to let people opt-out from this. We could definitly simplify the code if we just remove the option completely. @quietone, @heddn, @benjifisher - Can anyone think of a good reason to keep an opt-out flag here? The only justification I can think of is performance, but my gut says that is negligible even on large data sets, and for the 99% of people that would leave this in, losing the processing around the flag would technically be more performant, (though again, negligible). I'm really of the opinion that unless I see a benchmark test showing significant performance degradation by adding this column that we should just remove the flag completely. Again, the column already is allocated as a non-nullable 10 digit integer, the row insert/update happens whether the extra couple bytes of data for that column is included or not. Mysql has to write SOMETHING there anyway at least on insert, whether that's 0 or a real number doesn't really matter.

heddn’s picture

I can't think of a good reason not to track it. Feedback posted below.

+++ b/core/modules/migrate/src/Plugin/Migration.php
@@ -38,12 +38,15 @@
+ * - track_last_imported: (optional) If set to false a timestamp will not be
+ * added to the row indicating the time this row was imported. Default to FALSE.

If set to true, a timestamp of the date the last row was imported will be tracked. Defaults to true.

quietone’s picture

If we are not going to use it then I guess we should deprecated MigrationInterface::isTrackLastImported, \Drupal\migrate\Plugin\MigrationInterface::setTrackLastImported and \Drupal\migrate\Plugin\MigrationInterface::getTrackLastImported.

benjifisher’s picture

+1 for removing the option and always tracking the timestamp.

I agree that the performance difference is likely to be negligible. If someone has a reasonably large migration to test (one that takes at least 5 minutes to run) it would be reassuring to see some timing data with and without the trackLastImported option set.

The only question I see is how hard we have to work to deprecate the option. I see that #28 recommends throwing deprecation errors if either version (snake_case or camelCase) of the option is set. Do we need to do that? Do we throw any sort of error if a migration has random, unsupported keys in the YAML? I think not, since I sometimes copy YAML files from a configuration directory (supported by migrate_plus) to mymodule/migrations/ and I do not see any warnings about migration_group and other keys.

I will play devil's advocate. These two hypothetical objections are not serious.

  • Privacy: We are storing more information. Maybe a timestamp somehow identifies some of the other data in the migration.
  • BC: someone wrote code that relies on the 0 saved in the database.
quietone’s picture

Version: 9.5.x-dev » 10.0.x-dev
Issue summary: View changes
Issue tags: -Needs issue summary update
StatusFileSize
new7.84 KB

This patch deprecates trackLastImported, isTrackLastImported(), setTrackLastImported(), and getTrackLastImported().

No interdiff because this is a different solution.

longwave’s picture

Status: Needs review » Needs work
+++ b/core/modules/migrate/src/Plugin/Migration.php
@@ -358,6 +363,12 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
+    // Set trackLastImported independent of the following  mergeDeepArray().

This comment is out of place, probably copy/paste error - not sure we need a comment here at all.

Otherwise this looks ready to go.

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new734 bytes
new7.76 KB

Sometimes I think I am famous for copy/paste errors because I do it so much.

All fixed.

mikelutz’s picture

Version: 10.0.x-dev » 10.1.x-dev
Status: Needs review » Needs work

Super close here. I just noticed we are deprecating in 10.0.0 though, I think we need to target 10.1.0, right?

mrinalini9’s picture

Status: Needs work » Needs review
StatusFileSize
new7.74 KB
new6.56 KB

Updated patch #43 by addressing #44, please review it.

longwave’s picture

Status: Needs review » Reviewed & tested by the community

#45 looks great to me.

benjifisher’s picture

I queued a test of #45, but it is not being re-tested regularly. Maybe another comment while still RTBC will wake up the testbot.

quietone’s picture

StatusFileSize
new7.74 KB

Patch is still not retesting. This time let's try a uploading the patch again.

benjifisher’s picture

StatusFileSize
new1.53 KB
new7.77 KB

The patch in #45 (re-uploaded in #48) needs a reroll. I am attaching a new patch and a diff.

This was an easy reroll, so I am leaving the status at RTBC.

Easy means that I applied the patch to a commit on the 10.1.x branch, then rebased on the HEAD of that branch. I let Git do the work. Then I generated the new patch.

It looks as though the conflicting commit comes from #3181778: [w/c September 17th] Replace t() with $this->t() in all plugins. The title of that issue looks consistent with the attached diff.

alexpott’s picture

Issue summary: View changes

Adjusting issue credit and fixing issue summary to agree with the solution.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed eefe3a8 and pushed to 10.1.x. Thanks!

  • alexpott committed eefe3a8 on 10.1.x
    Issue #3198608 by quietone, swirt, indytechcook, benjifisher, mrinalini9...
benjifisher’s picture

Issue summary: View changes

Fix some typos.

Status: Fixed » Closed (fixed)

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