Problem/Motivation
This is a follow-on to #2443081: Make MigrateExecutable statistics publicly available, particularly https://www.drupal.org/node/2443081#comment-10004395 forward. We would like the Drupal 8 migration ecosystem to be at least as rich as it was in Drupal 7, but right now from contrib we don't have enough insight into what's happening during a migration to do that without additional core support. Rather than build things like counters for items processed/imported/ignored/etc. into the core services, a more flexible approach would be to dispatch events at "interesting" points in the migration process, allowing contrib/custom modules to add feedback and control mechanisms without cluttering up the core services. With these events, a bunch of logically front-end responsibilities (such as time/memory limiting) now in the core service can be moved to contrib (see #2541580: Remove obsoleted functionality from core Migrate, and a number of issues requesting features to help contrib implement front-end features can be closed.
Proposed resolution
- Add events to ID map save and delete operations - immediately, this allows us to remove statistics tracking entirely from core and let the contrib tools have complete control. #2443081: Make MigrateExecutable statistics publicly available
- Use events to implement the missing pre/post import/rollback functionality. #2501731: Migrate pre/post Import/Rollback methods not invoked
- Add events before and after calling the destination import process, allowing per-row operations equivalent to Migrate 2's prepare() and complete() methods.
Remaining tasks
Implement tests for the new events.- Close a bunch of core issues that are no longer needed.
User interface changes
N/A.
API changes
Addition of events.
Data model changes
N/A
Comment | File | Size | Author |
---|---|---|---|
#39 | interdiff.txt | 6.11 KB | mikeryan |
#39 | dispatch_events_at_key-2535458-38.patch | 37.03 KB | mikeryan |
Comments
Comment #1
mikeryanHere's the last patch from #2443081: Make MigrateExecutable statistics publicly available, implementing the ID map events, rerolled. Getting too late today, but my next step will be adding the pre/post import
/rollback(first we have to implement rolling back) events.Comment #2
mikeryanComment #3
mikeryanComment #4
dawehnerNice!
Conceptually I think this is wrong. THe event dispatcher should be part of the migration executable not of the migration entity itself.
You can use ->willReturnValue() instead
Comment #5
mikeryanCould you elaborate? The events I'm anticipating will be dispatched from migration classes and their plugins, so I don't see the advantage of putting the dispatcher in the executable.
Comment #6
mikeryanBut, now looking at the executable a little more closely, I can see places events may be useful from there as well...
Comment #7
mikeryan@dawehner - Then again, how are you seeing MigrateExecutable's dispatcher being used from, say, the id map plugin? Right now it retrieves it from its migration, do we really want it to know anything about MigrateExecutable?
Comment #8
mikeryanAdding pre/post import events, which addresses (at least) #2501731: Migrate pre/post Import/Rollback methods not invoked and #2429089: Track the last imported time for migrations. See #2536454: Use event listeners to implement a bunch of stuff, where migrate_plus is using the events to report statistics when importing, and last imported time.
Comment #9
mikeryanOK, added pre/post save events (dispatched before and after calling the destination import()). This immediately allows contrib migrate_tools to implement --limit and --feedback without requiring any specific support in the core API. And, although I haven't fully integrated it into migrate_tools yet, it allows us to take the time limit and memory limit stuff out of the core MigrateExecutable - with these events, contrib is able to implement that stuff on the frontend, where it belongs. I'm thinking the idlist support (#2522012: Remove broken idlist handling, replace with more robust exception handling ) could also be done in contrib as well with these events, although we'll also want a means to alter the query to optimize SQL-based sources.
At this point, this patch is plenty big enough (and maybe it should even be split out) - I'll wait for feedback before adding any more events.
Comment #10
mikeryan19 files changed, 553 insertions(+), 712 deletions(-)
Comment #11
mikeryanComment #12
phenaproximaThis can be simplified to
$migration->method('getEventDispatcher')->willReturn($event_dispatcher)
.Comment #13
mikeryanHere's a new patch reflecting @phenaproxima's suggestions, interdiff is hefty...
Overall: 22 files changed, 492 insertions(+), 781 deletions(-)
Comment #14
mikeryanCapturing (before I forget again), @phenaproxima pointed out we can avoid the getEventDispatcher() method in the id map plugin (added just to support mocking in unit tests) with the "reflection hack":
Comment #15
mikeryanComment #16
mikeryanComment #17
mikeryanTests added - ready for final review.
Comment #18
mikeryanWith a couple of nits picked by @phenaproxima...
Comment #19
mikeryanComment #20
benjy CreditAttribution: benjy at CodeDrop commentedI had a quick look at the patch and I like it, in general i'm +1 for this. Will try do a more thorough review this week
Comment #21
mikeryanAt @EclipseGC's suggestion, splitting this into two patches - addition of the events, and removal of all the stuff that becomes unnecessary with the events.
Comment #22
mikeryanComment #23
EclipseGc CreditAttribution: EclipseGc commentedWhy is the event dispatcher optional in the constructor? Is this being instantiated in such a way that we don't have access to the container outside of this class? Or is this a BC effort of some sort?
Instead of doing this in the constructor, it seems better to do it in a corresponding getter if this needs doing at all. Logic in the constructor should be avoided if possible, and the rest of core does optional calls to \Drupal::service() in getters. Even if it's protected, I think that'll be a "better" approach.
All in all this looks pretty good, but it's hard to mentally divide the two competing patches here. I'd suggest one patch with the event stuff and a follow up to remove the various count/limit/etc stuff in this patch. I think that will make both easier to get in.
Eclipse
Comment #24
mikeryanYes, this is being used in contrib (migrate_plus and migrate_upgrade), so trying not to break users (I'll update those ASAP when this is committed, but there will be people updating core without updating their contrib modules).
Comment #25
mikeryanOK, here it is with just the events added, we'll reap the LOC-- benefits in #2541580: Remove obsoleted functionality from core Migrate.
Comment #26
webchickAccording to mikeryan, this blocks migrate Drush commands (e.g. the ability to do partial imports) from being completed. Escalating.
Comment #27
phenaproximaI have nitpicks, but they're just coding style and terminology-related. Beyond these, the patch looks fine to me.
Can this say entity, not object?
$migration's description should be indented another space and be entity, not object.
s/object/entity, everywhere it occurs.
s/object/plugin
Parameter descriptions should be indented an extra space, and $map's should say 'plugin', not 'object'.
Obviously the indentation and terminology fixes should be applied to all of the event classes, not just the ones listed here. But you get the idea. :)
Comment #28
EclipseGc CreditAttribution: EclipseGc commentedSorry if I was unclear before. This looks great, and the NULL eventDispatcher in the __construct() in the previous patch was also great. So constructor injection is still totally ++, but if nothing was injected, this protected getter is also ++. Just don't put this logic in the __construct() method.
Hopefully that makes sense. I've not dug through the tests in depth, but the code changes are looking pretty good here. I'll try to give the tests a once over later today or tomorrow.
Comment #29
mikeryan@phenaproxima suggested a refactor here, so I'd wait until my next patch upload.
Comment #30
mikeryanUpdated for the above review comments. Also, phenaproxima suggested the event listeners could be directly on MigrateEventsTest. I also moved the migration configuration into MigrateEventsTest, at this point most of the migrate_events_test module is gone, but I haven't quite figured out how to move the source/destination plugin classes (because discovery).
looked to me like I could get the source plugin registered with ID 'data', but did not work. Thoughts?
Comment #31
mikeryanAnd now the correct interdiff...
Comment #32
benjy CreditAttribution: benjy at CodeDrop commentedPatch looks good, nice work! Few small nitpicks and then +1 for RTBC.
Should be \Drupal\migrate\Event\MigrateMapDeleteEvent
Gets the map plugin.
Gets the...
Gets the row object.
Gets the event dispatcher
Shame we need this class and could we have re-used EmptySource?
Comment #33
mikeryanDocblocks fixed in latest patch.
The problem with EmptySource is, it's empty. We need to actually pass a data row through the pipeline to get all the events triggered with verifiable data.
Thanks!
Comment #34
benjy CreditAttribution: benjy at CodeDrop commentedEmptySource does have a row? It's just the id that is empty?
Comment #35
benjy CreditAttribution: benjy at CodeDrop commentedOther than that +1 for RTBC
Comment #36
mikeryanAh, it does have a row of sorts,
\ArrayIterator(array(array('id' => '')))
. But how do we get validatable data migrated out of that?Comment #37
mikeryanLet's followup the testing issue with #2544690: Create new source plugin taking data from plugin config.
Comment #38
phenaproximaWorks for me.
Comment #39
mikeryanAs requested, clarified use of state to test events.
I'll write up a change record describing the added events.
Comment #40
mikeryanCR draft added: https://www.drupal.org/node/2544874.
Comment #41
webchickWow, great CR!!
Went through this patch with @mikeryan and @phenaproxima earlier today. The patch is very well-documented and restores functionality found in Migrate module in D7. Without this, modules such as Migrate Plus can't port.
I inquired about "why events vs. hooks?" and the answer was basically because we're trying to build this in a future-proof way. Since we're writing this in 2015 and not in 2012 and earlier, like most of D8's code, this makes sense to me. I know there have been some concerns about using events in places, but I think it was more when it created a mish-mash of approaches. This is consistent and well-documented.
Committed and pushed to 8.0.x. Thanks!
Comment #43
webchickCR published, too.