Write-with-no-read causes backtracking re-render assertion
Any assignment of a Model attribute that happens within a tracking frame causes an immediate backtracking re-render assertion, even when there is no corresponding read.
You attempted to update `theField` on `<model::thing:null>`, but it had already been used previously in the same computation.
Reproduction app: https://github.com/ef4/ember-data-bug-repro
Reproduction commit for ease of reading: https://github.com/ef4/ember-data-bug-repro/commit/fa6cb53e47b6557995fddb253b5fe21cf2ee6be1
The reproduction is very small. There's a single attr() which is never read anywhere in the app. Assigning to it during a render (so within a tracking frame) throws the assertion anyway. The app should throw an immediate assertion.
I hit this issue while upgrading an app from ember-data 5.3.13 to 5.8.0.
Some clarifying points about the reproduction:
createRecordin a constructor was just a convenience here, you can movecreateRecordto a more conventional place and the bug remains.- I'm fully aware that mutating a tracked property during render is a bad idea and I've been telling people so for a long time, but the fact remains that the stated policy in the text of this assertion as well as the behavior of ember-data 5.3 is that this is OK as long as no read has happened first.
- This bug is probably also a performance bug, because it implies we're doing extra unintended notifications of property changes. To hit the assertion, we had to incorrectly cause this property to be marked as consumed, and that has costs.
To hit the assertion, we had to incorrectly cause this property to be marked as consumed, and that has costs
if you want to initialize a field without a read it has to be done during createRecord, it's the double initiation in your repo that's causing this.
We do lint against use of createRecord inside tracking contexts in the recommended config, you can find that here https://warp-drive.io/guides/linting/
I've been meaning to also add a rule to not call createRecord and then immediately mutate
We do lint against use of createRecord inside tracking contexts
Sure but that's not required to hit this problem. Also, tracking contexts are not, in general, statically detectable, so the lint rule can't really save people.
I've been meaning to also add a rule to not call createRecord and then immediately mutate
That is also not required to hit this problem. It was just a convenient way to make an example that fits on one screen.
This is a pretty wild semantic change to sneak into a minor release. And I don't really think we can argue with a straight face that it only accidentally worked in every ember-data release up through 5.3 and really it's a bug that it didn't already assert before. The text of the assertion itself makes it clear what the expectation is for users: it's not that they can't do any mutations in tracking context, it's that they can't read-then-write in tracking context.
(Believe me, I wish we had instead forbid all mutation in tracking context, it's a better policy! Just not one that can be dropped on people without a migration path.)
Stepping through all the change notification code, it surely looks like a bunch of unintended work is happening here to cause the assertion. Are you really saying it's intentional that all writes suddenly count as reads too? Why is that beneficial?