Problem/Motivation

This builds upon #3190815: Source count caching broken: impossible to enable source count caching for SqlBase-based source plugins (plus, unneeded cache I/O) to fix the caching of the source count.

Source plugins that need to calculate the count of rows are overriding SourcePluginBase::count() which has the caching logic and that effectively prevents the use of the cache for the count of rows. The typical pattern is that there's extra work happening in ::initializeIterator(), and hence ::count() is overridden too. And because of crucial bugs in SourcePluginBase::count() and especially SqlBase::count(), people have been forced to override ::count().

The relevant code is in \Drupal\migrate\Plugin\migrate\source\SourcePluginBase::count() + \Drupal\migrate\Plugin\migrate\source\SourcePluginBase::$cacheCounts.

Therefore, once #3190815: Source count caching broken: impossible to enable source count caching for SqlBase-based source plugins (plus, unneeded cache I/O) is fixed, we'll be able to land this too, as will many contrib modules. For example: #3190804: Allow source counts to be cached: implement ::doCount() instead of ::count() and #3190800: Allow source counts to be cached: implement ::doCount() instead of ::count().

Steps to reproduce

Proposed resolution

Change any source plugins overriding \Drupal\migrate\Plugin\migrate\source\SourcePluginBase::count, which has the caching logic, to use \Drupal\migrate\Plugin\migrate\source\SqlBase::doCount instead.

The exception is EmbeddedDataSource.

Steps to reproduce

Proposed resolution

Change any source plugins overriding \Drupal\migrate\Plugin\migrate\source\SourcePluginBase::count, which has the caching logic, to use \Drupal\migrate\Plugin\migrate\source\SqlBase::doCount instead.

The exception is EmbeddedDataSource.

Remaining tasks

Review change record and the updated IS
Commit and smile.

User interface changes

N/A

API changes

N/A

Data model changes

N/A

Release notes snippet

N/A

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers created an issue. See original summary.

Wim Leers’s picture

The typical pattern is that there's extra work happening in ::initializeIterator(), and hence ::count() is overridden too.

Because of crucial bugs in SourcePluginBase::count() and especially SqlBase::count(), people have been forced to override ::count() instead of ::doCount().

Therefore, once #3190815: Source count caching broken: impossible to enable source count caching for SqlBase-based source plugins (plus, unneeded cache I/O) is fixed, we'll be able to land this too, as will many contrib modules. For example: #3190804: Allow source counts to be cached: implement ::doCount() instead of ::count() and #3190800: Allow source counts to be cached: implement ::doCount() instead of ::count().

Wim Leers’s picture

Issue tags: +Novice
+++ b/core/modules/content_translation/src/Plugin/migrate/source/d7/EntityTranslationSettings.php
@@ -188,7 +188,7 @@ public function getIds() {
-  public function count($refresh = FALSE) {
+  public function doCount() {

Nit: should be protected.

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.

Wim Leers’s picture

Title: [PP-1] Allow source counts to be cached: implement ::doCount() instead of ::count() » Allow source counts to be cached: implement ::doCount() instead of ::count()
Status: Postponed » Needs review
Issue tags: -Novice
FileSize
6.13 KB
786 bytes

Status: Needs review » Needs work

The last submitted patch, 5: 3190818-5.patch, failed testing. View results

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
5.24 KB
6.16 KB

Looks like #3 which I fixed in #5 wasn't the only one! 🙈

Status: Needs review » Needs work

The last submitted patch, 7: 3190818-7.patch, failed testing. View results

Wim Leers’s picture

Well that is an interesting test failure:

1) Drupal\Tests\migrate\Kernel\Plugin\source\MigrationSourceCacheTest::testCacheCountsNotContaminated
PHPUnit\Framework\Exception: mmap() failed: [12] Cannot allocate memory

mmap() failed: [12] Cannot allocate memory
PHP Fatal error:  Out of memory (allocated 66328723456) (tried to allocate 262144 bytes) in /var/www/html/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php on line 500
Fatal error: Out of memory (allocated 66328723456) (tried to allocate 262144 bytes) in /var/www/html/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php on line 500

Re-testing…

Wim Leers’s picture

I have not yet been able to figure out what exactly in \MigrationSourceCacheTest::testCacheCountsNotContaminated() is causing the memory leak, but it sure smells like an infinite recursion problem 🤔

narendraR’s picture

narendraR’s picture

FileSize
599 bytes

Interdiff

Wim Leers’s picture

Wow, great catch! Infinite recursion 🙈

+++ b/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php
@@ -117,4 +117,11 @@ class EmbeddedDataSource extends SourcePluginBase {
+  /**
+   * {@inheritdoc}
+   */
+  protected function doCount() {
+    return count($this->dataRows);
+  }

Instead of this, let's do

  /**
   * {@inheritdoc}
   */
  public function count($refresh = FALSE) {
    // We do not want this source plugin to have a cacheable count.
    // @see \Drupal\migrate_cache_counts_test\Plugin\migrate\source\CacheableEmbeddedDataSource
    return count($this->dataRows);
  }

i.e. let's not add ::doCount() because it's dead code then.

Let's instead update the existing ::count() implementation and explain in a comment why it's implementing ::count() and not ::doCount(): to bypass caching. Its subclass is the one that is cacheable.

narendraR’s picture

Status: Needs work » Needs review
FileSize
6.39 KB
804 bytes

Thanks for the review. Here is the updated patch and interdiff file.

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community
+++ b/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php
@@ -114,6 +114,8 @@ class EmbeddedDataSource extends SourcePluginBase {
   public function count($refresh = FALSE) {
+    // We do not want this source plugin to have a cacheable count.
+    // @see \Drupal\migrate_cache_counts_test\Plugin\migrate\source\CacheableEmbeddedDataSource
     return count($this->dataRows);
   }

… and that's why this is the only example where we do not change ::count() to ::doCount()!

The last submitted patch, 2: 3190818-2.patch, failed testing. View results

The last submitted patch, 2: 3190818-2.patch, failed testing. View results

The last submitted patch, 2: 3190818-2.patch, failed testing. View results

The last submitted patch, 2: 3190818-2.patch, failed testing. View results

quietone’s picture

Issue summary: View changes
quietone’s picture

Issue tags: -migrate-d7-d8

While this clearly changes D6 and D7 source plugins I am not adding migrate-d6-d8 and am removing migrate-d7-d8. The original intention for those were for the Task migration issues for migrating database contents.

With the patch applied I grepped for 'function count' in source plugin directories. The latter two should not be changed, that is good. But why is ContentEntity not included. Oh, I see, it doesn't need to be. That method includes a call to the parent::count(). So that is right.

$ grep -r 'function count' core/modules/*/src/Plugin/migrate/source/
core/modules/migrate_drupal/src/Plugin/migrate/source/ContentEntity.php:  public function count($refresh = FALSE) {
core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php:  public function count($refresh = FALSE) {
core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php:  public function count($refresh = FALSE) {

Yes, RTBC

benjifisher’s picture

Why is the testbot exercising the patch from #2?

I will trigger a re-test of the patch from #14.

quietone’s picture

@benjifisher. Yikes! I didn't even notice that last night when I updated the IS. Glad you caught it.

xjm’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs change record, +Needs issue summary update

Can we get a CR for this?

I'm also wondering about the disruption of just deleting all those public count implementations. I assume/hope that there is a parent count() implementation that any callers or child implementations would be using instead? Can we think of any potential disruptions to callers or child implementations from the logic being moved in this way?

Thanks!

Wim Leers’s picture

#24: Yes, exactly. They would all be inheriting \Drupal\migrate\Plugin\migrate\source\SourcePluginBase::count().

quietone’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs change record, -Needs issue summary update

Updated the IS and the change record.

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community

Awesome, thanks!

quietone’s picture

Your welcome!

Woohoo!

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 60f9243 and pushed to 9.3.x. Thanks!

As we've got a CR here going to commit to only 9.3.x. I think the BC / API implications of this change are minimal but better to take a more cautious path.

  • alexpott committed 60f9243 on 9.3.x
    Issue #3190818 by Wim Leers, narendraR, quietone, xjm: Allow source...

Status: Fixed » Closed (fixed)

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