Problem/Motivation
Was trying to use highwater fields, and notices a number of problems.
- The highwater support is spread among the source plugin, migration plugin, and MigrateExecutable classes, yet really is purely a source-specific feature and should be contained to the source plugin.
- The configuration property highWaterProperty violated Drupal naming standards.
The config schema is missing, so it's actually not possible to define a migration with that in the first place :)No longer applicable since migrations are now plugins, not configuration entities.- SqlBase, when building the query, uses $high_water for *both* the name of the field and the value, so the field name overwrites the value, and it builds a query like this: condition('changed', 'changed', '>')
- Also, in there, I *think* we should additionally add a orderBy($high_water_field), otherwise it could happen that we set the value to a random higher value and still have records with lower values that are not migrated, or the other way round, that we send the high water value lower again and have to re-import stuff
Example:
You have 3 records. changed 1, 3, 2. you import the first two, 1, 3. highwater is set to the last one you imported, 3, and 2 is never imported. or the other way round, they are in this order: 3, 2, 1. 1 is the last one to be imported, so the highwater field at the end is 1 and 2 and 3 will be imported again.(assuming a partial import for some scenarios above)
- Again assuming that you're doing a partial import, if you have a lot of records with the same value there and don't import them all, it could happen then you skip some. let's say you have 100 records with a value of 1. then you import 50 of them and set highwater value to 1. the next time the query runs, it will do changed > 1 and the other 50 records will be skipped
=> So the query condition should be =>. Values with identical records will have to be loaded again, and possibly require additional special casing somewhere. If they are in the map and have the same value, then it's probably OK. If they are not in the map at all yet, we need to import them.
Proposed resolution
- Fix the SqlBase optimization to properly incorporate the highwater value test.
- Rename highWaterProperty to high_water_property.
- Move all highwater support within the source plugin.
Remaining tasks
None.
User interface changes
N/A
API changes
- highWaterProperty renamed to high_water_property, and moved from the top-level migration plugin configuration into the source plugin configuration.
- high_water_property subfield 'field' renamed to 'name'.
- Public methods getHighWater(), getHighWaterProperty(), and saveHighWater() removed from MigrationInterface.
| Comment | File | Size | Author |
|---|---|---|---|
| #183 | interdiff.txt | 936 bytes | mikeryan |
| #183 | move_highwater_field-2485385-183.patch | 41.51 KB | mikeryan |
| #180 | move_highwater_field-2485385-180.patch | 40.46 KB | mikeryan |
| #144 | 2485385-144.patch | 40.47 KB | xjm |
Comments
Comment #1
berdirpatch with some fixes, does not yet address 4.
Comment #3
mikeryanCoding standard is underscore-separated. Also, I think "field" is better than "property" here.
Needs to be fleshed out farther, defining 'name' as required and 'alias' as optional.
In Migrate Classic™, properly ordering the query has been the responsibility of whoever defined the query. I'm a little concerned automatically forcing the order here might not work for all queries. Also, in some cases the query definer might want to use a second (or beyond) field for ordering when there are dupes in the highwater field.
Comment #4
berdir1. I'm just adding the schema based on how the code is already using it. Changing it here would result in a much bigger patch.. Could be argued that's ok since it's broken right now anyway.
2. Yes ;) That was the minimium to get it imported ;)
3. You don't write source queries yourself anymore for many cases. But yes, I was wondering about this too. But if we can automatically add a condition, then it should be fine to add an order by as well? It's the source base class, if you have a different query, you can always change it...
Comment #5
berdirOne more problem.
I know that the migrate people like type safe checks, but type safe checks when there actually is no type safety guaranteed is bad.
getHighWater() on the first run returns NULL, not a string.
Comment #6
benjy commentedo, that never used to be the case in D7, from memory it was an empty string.
Comment #7
berdirThat's possible, but it's based on the key value store in 8.x, and the default value is NULL there. You could pass in '' as a default value, but IMHO, NULL makes a lot more sense than an empty string for that...
Comment #8
benjy commentedYes I agree, I was just pointing out before that it was likely un-changed code (or logic) from D7.
Comment #9
phenaproximaMade a few changes here.
First, I think that the high-water property should be defined as part of the source plugin's configuration, not as a top-level property of the migration. High-water properties don't apply to all migrations; and for migrations that do use them, they only have any meaning to the source plugin anyway. And on top of that, they really (so far, anyway) only have meaning for SQL-based sources. So this patch alters the config schema so that high_water_property is a property of the migrate_source_sql data type.
Secondly, there were a couple of PHPUnit tests which ostensibly use high-water properties, but they were not setting values which would actually be used by the SqlBase plugin (it was looking for $high_water_property['name'], and the tests were setting $high_water_property['field']). That's fixed now.
Comment #10
phenaproximaWhoops, forgot to adjust a few tests.
Comment #11
berdirSee #5, a type safe check here doesn't make sense, since this will return NULL if there's nothing. Or getHighWater() needs to cast to a string().
Other than that nice. I don't think this addresses all my concerns, but let's get something in and then we improve it further later.
Probably also needs some actual tests with an actual config entity or at least failing due to the wrong code?
Comment #12
phenaproximaLooking at the Migration class, we might have to restore highWaterProperty to the config schema, because $highWaterProperty is a property of the Migration entity type, so methods probably depend on it. What do you think?
I totally agree this should have some real tests.
Comment #13
benjy commentedWhy? If you handle it in your custom source it should work just fine?
Comment #14
phenaproximaRe-added the high water property to config schema and fixed broken tests (which were using a nonsensical high-water property in their $migrationConfiguration, thus causing SQL errors -- another example of something that had never worked, but been covered up by age and bugs).
There already is at least one passing test which uses the high-water property (CommentSourceWithHighWaterTest), so I'm removing the "needs tests" tag.
Comment #16
phenaproximaThis fixes the only real test of high-water property support (CommentSourceWithHighWaterTest) against HEAD, to prove the problem actually exists and is rectified by the patch in #18.
Comment #17
phenaproximaDerp, duplicate comment.
Comment #18
phenaproximaFixing them thar test failures in #14.
Comment #20
berdirThis should be removed now?
So, camel case or not? :)
So this is now a type save check against NULL.
We should update the documentation of that method to say that it can return NULL if there is no value yet, and enforce that this is the case ?: NULL or so, and not FALSE or '' or 0.
Also, the testcase base class still defaults to an empty ''. We should also make sure that ensures that the method is called if we define it (atleastOnce()).
Comment #21
phenaproximaAll fixed.
Comment #22
berdirPlayed a bit with the test.
This is kind of how I'd expect a highwater test to look like. Making sure that there actually are more rows in the database than we'd expect (the test passes on HEAD but that's completely bogus since there is nothing else that it could return) + some asserts on the actual query object.
Also removed an order by from the comment source. I think that's not needed now that sql base is adding that automatically.. not sure if it was an attempt to make high water work in the past. I don't think we should rely on the source to add the correct order.
This will fail right now because the tests hardcode the assumption that count($source) needs to return the same count as results.. but that is not the case if you are using highwater and according to @mikeryan that is by design (although it is not quite clear to me how you'd then display a useful count of how much still needs to be imported?). The good thing is that #2499793: Several migrate_drupal migrations fatal error on count() is going to fix that behavior for us and then we can define the test so that it expects a different count for that query. So we'll wait on that...
There is one other scenario that we should be testing to test the NULL/'' behavior. And that would be yet another test that defines a highwater property but no original highwater value. Maybe the property should just be set in CommentTestBase. The result is that it must not add the condition then. Thinking about it, we might have another hidden bug there.. because the order by *has* to be there even if there is no current value.. so that we process them in the right order.
Comment #23
phenaproximaUnblocked.
Comment #24
berdirYes. pretty sure this needs work, though :)
Comment #25
berdirhigh_water_property vs highWaterProperty is still inconsistent. See for example SourcePluginBase::__construct() and MigrateExecutable::import().
So we are definitely still missing test coverage..
We are testing the sql source that automatically filters, but we are not checking that the saved value is updated and a second run will then use an updated value.
I've been testing this with a custom source and one additional problem that I kind of mentioned before is order. We either need to document that source plugins *must* return the records sorted by the highwater field if supporter or somehow be able to deal with them not being ordered. For example, not assume that the last row has the highest highwater value, which is what MigrateExecutable::import() currently does, but keep the highest value of all rows.
Comment #26
blazey commentedHi, I've also been trying to fix high water property. My solution is very similar so I won't post it here. The Kernel test, however, could be useful (https://www.drupal.org/node/2690757#comment-10988135). Shall I create a separate issue containing just the test?
Comment #27
berdirPlease add the test here. Missing tests is the main reason this isn't moving forward.
Comment #28
blazey commentedAttaching Kernel test.
Comment #29
blazey commentedAttached patch is an attempt to combine migrate_sqlbase_highwater_fix-2690757-7.patch and 2485385-21.patch (re-rolled).
Comment #31
heddnThis needs a re-roll after #2694009: MigrationInterface doesn't include get() was committed.
Comment #32
heddnAlso, if we rename the high water property, we might consider changing the name of the associated getter too.
Comment #33
mikeryanComment #34
rakesh.gectcrComment #35
rakesh.gectcrComment #36
heddnThe next step here is to replace all the
calls to
->getFoo()calls.Comment #38
mikeryanWe have a Major issue for documented functionality that is badly broken - how did we never set it to migrate-critical?
Comment #39
mikeryanLooked at the last patch, it needs more than the get() calls changed (still based on migrations as configuration entities).
Comment #40
quietone commentedI'm working on rerolling the patch from #29 and stuck on errors from this test, core/modules/migrate/tests/src/Kernel/SQLBaseTest.php. The error is:
Testing the patch as is to see what else is broken.
Comment #42
mikeryanRequeued the test, I don't see why it would trigger that error.
Comment #46
mikeryanNot a complete review, just trying to guess at what might be triggering the test failure...
Now that migrations are plugins rather than config entities, 'migrate.migration.' should not be part of the .yml filename.
Comment #47
phenaproximaPostponing on #2560795: Source plugins have a hidden dependency on migrate_drupal. @mikeryan and I think that it's the reason for the mysterious test failure in #40.
Comment #48
mikeryanThe blocker is committed, let's retest.
Comment #50
quietone commentedEven though this is still blocked I want to address the failures and be ready for when it is unblocked.
The error was a result of the source test table being created in the destination database. The table was being created by the test module and that is now being done in the test, HighWaterTest.php.
And there is a lot of renaming in this patch as well. The high water test has been renamed from SQLBaseTest to HighWaterTest since it is testing the high water functionality and it follows the naming convention. The test module has been renamed to migrate_high_water_test In a similar fashion the test source plugin, the test table and the migration have all had a name change. I think it is much clearer that these are for high water tests and nothing else.
In order to run these tests without the blocker patch, the modules 'migrate_drupal' and 'fields' are installed in HighWaterTest.php. It is marked with an @todo, that seemed the best way to mark it.
Now let's see if there are any other errors.
Comment #52
quietone commentedNot sure how that error slipped in.
Comment #54
quietone commentedRetesting. Failure in OptionsFieldUITest.php appears to be unrelated, and that test passes locally .
Comment #56
hussainwebI tried to make the high water mark logic a bit simpler with a new method. Also, I am not sure why the patch was named as 'test-only'. I checked the patches before and it seemed to be the whole patch file.
Comment #58
hussainwebThe fails are due to #2749955: Random fails in UpdatePathTestBase tests.
Comment #59
hussainwebThe failure is resolved.
Comment #60
chx commentedThanks for writing this. This is a very nice patch. But if you are breaking BC anyways, then please understand the sorry state for highwater and everything else baked into SourcePluginBase::next() is the very reason I am advocating for breaking up source into an iterator, into row additions and filters in #2543552: Modernize migration source plugins. If people would have the time and the willingness to work on it, would be great. It is extremely hard for me to just go and code some core because it would be nicer. I don't have such willingness for that after all that transpired. I just don't. I know what needs to be done but I can't.
The test from this patch could mostly be reused, most of the highwater functionality could be reused as well.
Comment #61
hussainweb@chx, thank you for pointing me to that issue. I will try to catch up on the discussion in the coming week.
I was also not very concerned about breaking BC because migrate is experimental and we have done things like this in the past.
Comment #62
chx commented> I was also not very concerned about breaking BC because migrate is experimental
Wish certain elements of the community agreed with you.
Comment #64
phenaproximaI think this looks great. My only complaints are nitpicks.
Bit of a nitpick, but can we follow the formatting used in ->fields()? (i.e., the opening bracket of each array on the same line as the opening paren)
Extra empty line.
Nit: it's should be its.
Should be
->fields([...Let's use willReturnValue() for readability.
Comment #65
quietone commentedAll nitpicks fixed.
Comment #67
quietone commentedFixed typo.
Comment #68
phenaproximaThanks, @quiteone. This looks pretty righteous to me, but I'd like to the RTBC to come from one of Migrate's expert plumbers (@benjy, @chx, or @mikeryan)...
Comment #69
benjy commentedPatch looks good to me.
One confusing aspect is we now have
getHighWaterProperty(),getHighWater()andgetHighWaterField()all on the migration. Probably out of scope here but it would have been nice to have a HighWater value object that encapsulated those methods, and just have the one getHighWater() method on the migration. Alternatively, MigrateExecutable is now the only thing callinggetHighWaterProperty()like so:We could easily change that so that the executable didn't need any knowledge of the high water property at all?
Maybe a follow-up?
Comment #70
ayalon commentedI tested the patch but in my humble opinion, only the test works but beside that patch is not working.
The problem is, that in the test the configuration for the property is set up:
But outside of the test, the yml configuration never gets picked up and therefore the whole highwater stuff is not working. Any suggestions?
Comment #71
ayalon commented*edited* seems not to be a solution.
Comment #72
ayalon commentedI try to debug the highwater functionality and while debugging, I found out, that the highwater value is saved for every row:
web/core/modules/migrate/src/MigrateExecutable.php
As there is only one field in the KeyValueStore, does it really make sense to save the date for every row?
Comment #73
berdirThat is unrelated to this ticket and on purpose. The migration could die with a fatal error any time and it does this to make sure that the highwater value is accurate.
Comment #74
quietone commentedCreated a follow up issue as suggested in #69, #2785233: Encapsulate HighWater methods
Comment #75
mikeryanSorry I haven't looked closely at this for a while - the implementation looks good, but I agree with phenaproxima's comment back in #9 - this configuration belongs on the source plugin, not on the migration.
Comment #76
quietone commentedOK. Patch reworked to put the high water on the source plugin. Also, had to modify MigrateSqlSourceTestCase so that the expectations (is the right way to say that) are set only on testRetrieval. That is the only method that initializes the iterator which is where the high water checks are.
Comment #78
quietone commentedHmm, the status changed to Needs work while the test in #76 was still running. It did in fact pass, so changing to Needs review.
Comment #79
phenaproximaWhy not simply call $this->getHighWaterProperty()?
Ditto here.
Comment #80
quietone commentedDarn. Meant to fix that.
Comment #81
mikeryanLooks good - and tested in the (semi-)real world, at #2609310: Add highwater/track_changes examples to beer migrations. However, one more thing - there's really no logical reason for the Migration plugin to know anything about highwater marks, it should all be entirely contained within the source plugin. Now, while we're already breaking BC by changing the highwater property name, that's not a big deal (especially since it didn't really work) - but changing MigrationInterface would be. So, my suggestion is:
Comment #82
quietone commented@mikeryan, thanks for breakdown of the tasks. I usually think in lists, so that was helpful, and clear. Everything has been addressed but these are my first deprecations and wrappers. And there are still failing tests. Hopefully, only MigrateSourceTest.php and CommentSourceWithHighWaterTest.php.
Comment #86
phenaproximaComment #87
phenaproximaI am praying to Cthulhu that this passes the tests. It turns out we ran smack-dab into dependency injection hell with this one -- MigrateSqlSourceTestCase (upon which many if not all source plugin tests are based) is a unit test, and the patch in #82 was trying to pull the high water value directly out of \Drupal -- and understandably so, because adjusting the dependencies is quite tricky. This patch removes getHighWaterStorage() in favor of actual dependency injection, which involves changing base class constructors. That constitutes a BC break, but that's OK because this issue was already tagged as such.
MigrateSqlSourceTestCase is, in my opinion, a mess. It's doing a lot of mocking and set-up that would be totally unnecessary if it were a kernel test, and this patch does nothing to fix that -- in fact, by introducing a new mockContainer() method, it might even be exacerbating the problem. But rewriting MigrateSqlSourceTestCase as a kernel test is very far out of the scope of this patch, so we'll have to get to it later.
Comment #89
phenaproximaScrewed up the mocked
keyvalueservice. Derp.Comment #91
phenaproximaMissed a spot or two. If nothing else, this should greatly reduce the number of failures.
Comment #93
phenaproximaOkay...as of this patch, SourcePluginBase itself now implements ContainerFactoryPluginInterface. Kind of a sweeping change, but this does open the door to proper dependency injection for all source plugins. So it's a win for best practices, but hopefully not so far out of scope that we can no longer commit it. (If it is, I have yet another trick up my sleeve.)
Comment #95
phenaproximaAt long last, a shattered and shell-shocked phenaproxima (hopefully) crosses the finish line. Working on our source plugin tests is like stumbling blind through a thorny jungle in hell.
Comment #96
mikeryanI'm not sure how I feel about about changing SourcePluginBase - that's a more far-reaching change than I had hoped. But, it may be necessary, I see...
Why are public functions added to MigrateSourceInterface? Highwater support should be entirely encapsulated within the source plugin, the internals shouldn't be exposed.
Comment #97
phenaproximaOkay. This patch fixes the tests without imposing DI on everything. It's messy as all hell, but that is because the source plugin tests are so awful -- they mock the universe, and they have to be incredibly careful about when they do things, or the whole house of cards implodes. It's a nightmare. Let's get #2791119: Write meaningful Migrate source tests in ASAP and we can start to undo the damage.
I still need to hide all the high-water stuff from outside code, but that should be relatively easy now that the tests are passing. On my local machine, anyway.
Comment #98
phenaproximaOK, this removes the changes to MigrateSourceInterface and makes the high-water methods of SourcePluginBase protected.
Comment #100
phenaproximaOK, so...the source plugin needs to be able to reset the high-water mark once rollback is complete. After discussion with @mikeryan on IRC, we agreed that the best way to do that was to allow source and destination plugins to be event subscribers and react to various events dispatched by MigrateExecutable. That's what I've done here. Dear lordy, let it pass the tests.
Comment #102
phenaproximaAnd that, kids, is why I don't like using invocation assertions in unit tests.
Comment #103
mikeryanWhy weren't we able to remove this from the Migration plugin along with the rest of the highwater methods?
Description needed.
Comment #104
phenaproximaBoth fixed. I ran the migrate and migrate_drupal test suites before posting this patch, so I have high hopes that it will pass Drupal CI.
Comment #105
phenaproximaRemoved a couple of dead use statements.
Comment #106
mikeryanokie dokie!
Comment #107
xjmNice to see another Migrate critical RTBC!
This issue is technically rc eligible according to https://www.drupal.org/core/d8-allowed-changes#rc. However, given the importance of this issue, I'm actually going to tag it as an RC target so that it is a top priority to add during RC in case it is not committed before the commit freeze Tuesday morning.
The title and summary of this issue describe various problems but not the proposed resolution or the API changes. Can we get a CR for this issue and if possible retitle/re-summarize it to describe the fix? (The word "Various" in a title always makes me nervous.) :)
Comment #108
alexpottRe-using the event system seems pretty clever but I wonder if it could get us into trouble. This could cause a double registration if the plugins were registered a services and tagged with 'event subscriber'. The double registration might occur if the an import and rollback occurred in the same process. Do we have other use-cases for this functionality other than listening to MigrateEvents::POST_ROLLBACK?
Comment #109
mikeryanChange record drafted and issue summary updated.
Would a double registration cause the registered event handlers to be evoked multiple times, or is the dispatcher smart enough to dedupe them? I'll see if I can figure that out...
Comment #110
mikeryanYep, they get evoked multiple times.
@phenaproxima - what if we go back to calling
$source->postRollback()directly, as was discussed in IRC along the way to this solution?Comment #111
alexpottWe could have an interface on the plugin's to say that they have a postRollback method and call it if the plugins implement it.
Comment #112
phenaproximaOkay, I changed the approach. I think this way will please everybody.
I've created a new event subscriber that subscribes to the pre-import, post-import, pre-rollback, and post-rollback events. It handles the events by...calling the relevant methods on the source and destination plugins, if they implement them (which it verifies using
method_exists()). I don't like the idea of having a new interface to define the event handling, because that's pretty much what EventSubscriberInterface is for.Comment #113
phenaproximaRollbackAwareInterface is not a thing -- it's an artifact from another approach I tried. I could roll a new patch, but can it just be removed on commit?
Comment #115
phenaproximaComment #116
phenaproximaIced unused implementation of getSubscribedEvents() in SourcePluginBase.
Comment #117
mikeryanLet's do this thing!
Comment #118
xjmRetitling based on the CR. Thanks @mikeryan!
Comment #119
xjmComment #120
alexpottI really like the idea of a bridge service. Nice one.
Look like interfaces are appropriate here. That way we also get documentation (which this patch is lacking) for free. We need an interface per method (MigratePluginPreImportInterface etc...) Also I think we should pass the event to the method.
here we could pass the invoke method the interface as well as the method to make checking it simple.
Comment #121
phenaproximaInterfaces are appropriate, but I had decided to skip them for now in the name of getting this damn patch done, since it's one of the final blockers to Migrate API stability. I guess I don't mind putting interfaces on the plugins, but having an interface for each event seems clunky. I'd rather define two interfaces: ImportAwareInterface (defining preImport and postImport methods), and RollbackAwareInterface (preRollback and postRollback). So that's what I've done in this patch.
Comment #122
mikeryanLooks good - just one more thing, can you update the change record to document the new interfaces?
Comment #123
phenaproximaSince ImportAwareInterface and RollbackAwareInterface are not strictly related to the high-water fixes, I have opened a new change record.
Comment #124
mikeryanGood call, thanks!
Comment #125
alexpottSo what's odd to me is that we save the highwater mark row by row on the way in but if we're rolling back we just null it at the end. Is there anyway we can make it right all the time? Ie... if a rollback breaks there is no guarantee that a user would reset the migrate status.
Comment #126
phenaproximaChanging it would probably be a much bigger API break than this patch already constitutes. @mikeryan can confirm or deny that, but either way, it seems to me that changing migrations' highwater handling during rollback is well outside the scope of this issue. This patch is about getting highwater functionality to work the way it was always supposed to, but didn't due to a bug in SqlBase.
Comment #127
mikeryanConsider if we rollback 10 items. To make the highwater "right" would require first that the 10 items rolled back would have been the last 10 imported. Actually, maybe the 10 with the highest source-side highwater field values would be strictly speaking the ideal. And then the highwater mark would need to be set to the source highwater field value of the 11th item - the next one to be rolled back. Keep in mind that the rollback process is an iterator (currently unsorted) over the map table, and does not use the source.
If someone wants to tackle this - good luck with that (in a followup - I think that's out of scope here). In the meantime, nulling the highwater mark is the safest recourse - the worst-case scenario if rollback is incomplete and for some reason the migrator decides to run an import rather than complete the rollback, the previously-imported items will be reimported. All that's lost is time, not data.
Comment #128
phenaproximaFixing a TODO pointed out by @alexpott. Seeing as how 128 is a holy computer number, it will be very satisfying if this is the one that gets committed. =P
Comment #129
phenaproximaw00t! Back to RTBC.
Comment #130
alexpottCommitted and pushed 0308eb7 to 8.3.x and 7c8ebc3 to 8.2.x. Thanks!
Whilst reviewing this patch I pondered if we could set the highwater properly during rollback (see #125 and #127). After thinking about @mikeryan's response some more I think we should more the NULL setting from postRollback() to preRollback() this means that is the rollback fatals for any reason then the highwater is correctly indeterminate rather than wrong.
Fixed on commit. Yep we have automated coding standards for that :)
Comment #133
mikeryan@alexpott: Good point, followup created: #2800715: Reset highwater mark *before* rolling back.
Thanks!
Comment #134
xjmI'm pretty sure this just broke HEAD. Having trouble confirming locally because
InstallUninstallTestis not exactly laptop-friendly.Comment #135
chx commented*knock, knock* is this on? Did anyone read #60? Why did this continue much less went in?
Comment #137
xjmConfirmed. I had to roll this back. Here's the fail:
https://www.drupal.org/pift-ci-job/462223
Comment #138
xjm@chx, #60 was answered. Migrate is in alpha. Until it is in beta, there is no BC promise. This issue is one of the last BC breaks listed as critical, so we are getting close to a beta that we all hope to have very soon.
Edit: Or if the question is "Why don't we break BC even more and block it on even better improvements," the answer is "because we want to get to a beta very soon".
Comment #139
mikeryanSo, MigrateEvents not found is something I've heard of before (without this patch) - people have reported it but I've never been able to reproduce it, nor figure out how that would happen - if migrate is enabled, the MigrateEvents class should be available. There's actually an open issue for this: #2776235: Cached autoloader misses cause failures when missed class becomes available
Comment #140
mikeryanSo, basically, there's nothing actually wrong with the highwater patch, it's that the event subscriber is triggering that existing autoloader issue. I'm curious, though, why it only surfaced after the commit?
Comment #141
xjm@mikeryan, got me unfortunately. Usually I'd say something between the test run and commit but in this case that seems unlikely since the patch is right from this morning. It could be something @alexpott changed on commit causing a regression unexpectedly.
Comment #142
mikeryanRetesting on 8.2.x and 8.3.x.
Comment #144
xjmHere's the patch with @alexpott's coding standards fix; can't imagine this would have caused any difference. Maybe 8.2.x vs. 8.3.x.
Comment #145
xjmI queued both branches and all three PHP versions. The fail was across DBs in HEAD.
Comment #147
xjmUgh, sorry, I failed to push 8.2.x. Retesting those now that I did. And of course the 5.6 fail is irrelevant because of #2762549: Drupal\field\Tests\Update\FieldUpdateTest, Drupal\views\Tests\Update\EntityViewsDataUpdateTest and Drupal\comment\Tests\CommentFieldsTest fail on 8.1.x.
Comment #149
phenaproximaIgnoring the 5.6 failure (as per @xjm's comment in #147), I'm seeing two things:
1. The patch fails absolutely consistently on PHP 7.
2. We never tested the patch on PHP 7 (or, indeed, 5.6) before committing it.
I'm testing it locally on PHP 7 to see if that is the source of the problem.
Comment #150
chx commented#138 let me clarify: I argued in #60 that this issue should be shuttered and the code reused in #2543552: Modernize migration source plugins which would break out highwater into its own plugin.
Comment #151
alexpottSo the problem is that on PHP7 content_translation is install before migrate and the autoloader caches the missing class in \Drupal\content_translation\ContentTranslationUpdatesManager::getSubscribedEvents(). The only way to fix it in this issue is to do a module exists check instead. Other possibilities are to change multiple module install to be batched and do one by one (slow) or the way would be to wrap the classloader in something that would make it possible to remove cached misses from the classmap.
Comment #153
phenaproximaComment #154
mikeryanInterdiff looks good to me, pending reroll and test success - we can use #2776235: Cached autoloader misses cause failures when missed class becomes available for potentially following up with those other possibilities.
Thanks alexpott!
Comment #155
heddnComment #156
heddnMaybe @alexpott can comment on this, but the diff stats from #151 seem to also include some stuff for the outside_in module. I didn't include them in the re-roll. The diff stats make more sense when compared to #128.
Comment #157
alexpottOopsie my diff included some stuff it should not... thanks @heddn
Comment #159
alexpottOkay so the module exists check is not going to work because we're inside a container rebuild and all bets are off :(
Therefore we need to replace the class loader when we update modules on the kernel.
I did not mean to change the version of this issue in #157 - sorry.
Comment #161
alexpottNew patch with the essential bits of #2776235: Cached autoloader misses cause failures when missed class becomes available just to see if it works...
Comment #162
alexpottProper interdiff.
Comment #163
alexpottComment #164
phenaproximaHeyo! Looks like @alexpott's fix corrects the problem. So it seems to me that we need to postpone this on #2776235: Cached autoloader misses cause failures when missed class becomes available which, I'm told, is going to be a critical.
Once this is unblocked, we'll need to re-roll this patch again, without the change to ContentTranslationUpdatesManager.
Comment #165
catchJust want to see what happens with this. Event classes are one of my many complaints with the Symfony event system. interdiff is vs. #155
Comment #166
catchSo while the other bug is a real bug, we're also not doing ourselves any favours here either.
The hooks system allows modules to declare hook implementations regardless of whether another module is installed or not. The hook (like event listeners) only gets invoked when it's invoked anyway.
This works with event listeners, but the convention of putting the event name in an (arbitrary) constant on the event subclass discourages this, so we end up registering an event depending on whether the module that might invoke the event exists or not. What appears superficially to be more robust, turns out to be extremely fragile. Just using the string also means the subscriber would survive a class rename, which is if anything more likely than changing the string.
Comment #167
mikeryan@catch: I see your point, but addressing the event constant class pattern seems out-of-scope here - that's been the practice throughout core, if we are to change it that should be an issue of its own.
I'm going to resubmit the tests on the patch as originally committed (#144) - if those pass, can we get this in?
Comment #168
mikeryanOops - I saw #2796953: [regression] Plugins extending from classes of uninstalled modules lead to fatal error during discovery was committed and confused it with #2776235: Cached autoloader misses cause failures when missed class becomes available, which is the issue we've been waiting on here, and which has not been committed.
Given it seems like RC2 is imminent and we really want to get in there, I'm willing to go ahead with catch's answer - RTBC on the assumption the full set of tests I'm about to launch passes.
Comment #169
mikeryanTemporary to make it easy to launch all the 8.3.x tests.
Comment #170
mikeryanDidn't actually help...
Comment #171
mikeryanRandom bot fail on PHP 5.5/SQLite 3.8 - passed on rerun, but for some reason the red one didn't get removed. Anyway, passing all tests now...
Comment #173
phenaproximaLooks like this is going to need a reroll for 8.2.x.
Comment #174
phenaproximaRerolled against 8.2.x, accounting for the changes introduced by #2684567: Requiring a migration w/ a source plugin using a generator fatals. No interdiff due to rejected hunks.
Comment #175
alexpottI don't think we should be making this change in this issue. We might very well decide to make a recommendation not to use events classes like this but that should be discussed in its own issue. I think we should do #2776235: Cached autoloader misses cause failures when missed class becomes available before this - so reviews there are welcome :)
Comment #176
mikeryanThe attached patch combines the latest patch here minus the MigrateEvents avoidance with the autoloading patch at https://www.drupal.org/node/2776235#comment-11659157, so the testbot can verify that this specific victim of the cached autoloading will be fixed by that patch.
Comment #178
mikeryanNow that #2776235: Cached autoloader misses cause failures when missed class becomes available is in, I'm retesting the patch #144 (the patch that was originally committed here, without any later workarounds for the related issue).
Comment #179
mikeryanOf course...
Comment #180
mikeryanRerolled, I'll initiate the full set of tests.
Comment #183
mikeryan#2791119: Write meaningful Migrate source tests got in the way a bit there...
Comment #184
mikeryanHope my little reroll doesn't take away my RTBC privileges...
Comment #185
phenaproximaI don't think re-rolls ever remove RTBC privileges :)
Comment #186
alexpottSecond time lucky...
Committed and pushed 4259a06 to 8.3.x and c0e3342 to 8.2.x. Thanks!
Comment #190
geek-merlinComment #191
wim leers