We're undertaking a series of large data imports that are currently implemented using migrate.
The source table is located on the same database server as the core Drupal database - indeed it's in the same database. The table currently contains a total of 500,000 records (but will end up larger than that).
We're currently undertaking an initial import, so are running as:
drush mi Migration --limit="9 minutes"
as a periodic scheduled job.
Initial partial imports were perfectly performant. However - as the number of imported items has increased our migrate jobs (Being run through drush) are getting less, and less effective. We've identified that the bottleneck is migrate working out which rows it needs to import. Query logging led us to the following query being repeated over and over again with incrementing OFFSETs:
SELECT l.*
FROM
import_table l
WHERE (provider = 'xxxx')
LIMIT 1000 OFFSET 90000Given the size of our data set it's imperitive that we set a batch_size on the process - loading 500,000 records into memory isn't going to work :)
However - it seems that doing so is causing migrate to have to read all rows in chunks of 1,000 in order to establish if they need migrating instead of joining onto the map table and making the MySQL server do the work. Looking into sql.inc I found the following code:
if (isset($options['batch_size'])) {
$this->batchSize = $options['batch_size'];
// Joining to the map table is incompatible with batching, disable it.
$options['map_joinable'] = FALSE;
}I'm not sure why having a batch_size would be incompatible with joining onto the map table especially as it seems that the two things are likely to go hand in hand with large data sets?
Is this something that can be resolved easily? We'd be happy to help investigate / test or contribute if someone thinks this is achievable. Equally if someone has investigated, and discounted this already that'd be great to know and we'll probably need to roll a custom solution.
| Comment | File | Size | Author |
|---|---|---|---|
| #2 | migrate-scalability.diff | 935 bytes | leewillis77 |
Comments
Comment #2
leewillis77 commentedI've dug a little further into this. It seems that the "incompatibility" was introduced by the patch for https://www.drupal.org/node/2415597.
Reading through the notes on that I can see where the issue lies, the relevant bit from the comments is.
With this in the mind the code currently adds a range in the format (batchNumber * batchSize, batchSize), e.g. you for a batch size of 1000, you get
The original report goes on to note:
The solution in that issue was to block the use of mapJoinable when batches are in use, but it seems like an alternative solution would be to consistently use ->range(0,batchSize) when mapJoinable is set?
The only thing I could see wrong with this would be if some items fail to be processed on a run. In this case they could repeatedly appear in subsequent batches. If you ended up with batchSize items "failing" then potential you'd get starvation where the same items are repeatedly attempted - is that possible here?
Conceptual patch attached - I'd welcome any feedback on whether this is a sensible approach or if there's something else I'm not considering? If someone thinks it's sensible and worth progressing, I'm happy to put in the time to test on our current project.
Comment #3
mikeryanI think that's only workable during initial import - if you're dealing with updated content using track_changes, or doing a --update, you're going to be going over the same stuff repeatedly.