-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
unloadRecord of child record causes hasMany relationship to be invalid using [email protected] #4986
Comments
Just briefly skimming the change log, this commit #4593 looks suspicious. However, I don't currently know enough about the ember-data internals/haven't dug into it enough to have an educated guess. |
I've written a test case for this here. Please let me know if my understanding of how this should behave is incorrect. sudowork@89a34ba And I've bisected to verify that ba5874b does pass the test; however, 6d96eda (from #4593) fails the test. |
Calling |
cc @hjdivad |
I just experienced this as well. I think that unloading records of a hasMany leaves the underlying many array with internalModels with null records. I'm not sure this is intended, and don't know what we want for the relationship (does the many array should be cleared, or still contain 'empty' internalModels ?) |
I had to rollback because i experienced this. When i unload records it doesn't seem to fully clear reflexive relationships. Any time i tried to access a hasMany relationship in another model where one of the hasMany has been unloaded it explodes saying that the record is undefined. The 'unloaded' record still appears in the hasMany canonicalState but it has the flag I tried to make twiddle for this but twiddle hasn't been updated to 2.13 yet. |
I'm happy to attempt a fix for this as long as there's some agreement over what the correct behavior should be @sly7-7 @stefanpenner @hjdivad |
Here is a little twiddle demonstrating the error in 2.13 when unloading an inverse hasMany-relationship: ember-data version can be switched in twiddle.json |
Now that ember-twiddle supports ember-data 2.13, here's the repo I had created as a Twiddle: https://ember-twiddle.com/df7fa30d2eb3d32efe16185f4f376128?openFiles=controllers.todo-list.js%2C Some interesting things:
|
This is also a show stopper for us. I can appreciate the fact that it's nice to synchronize this unloadRecord action with the runloop to prevent redundancy. Unfortunately this broke in several things in our application. Most cases were as @sudowork stated, using Also, FWIW some of these were also using |
Wondering if #5011 fixes this? I'll try in my app tomorrow. |
@workmanw I thought the same thing, but I tried in my app, and for relationships, the internalModel has still their records set to null. |
Yeap, I can confirm that unfortunately this issue still exists in our application w/ 2.14.2. We're stuck on ember-data-2.12.x for the time being. |
So I've spent almost all day trying to refamiliarize myself with ember data internals which seem to be a moving target. Looking back at when this last worked, ember-data-2.12.x, did the following: clearRelationships() {
this.eachRelationship((name, relationship) => {
if (this._relationships.has(name)) {
let rel = this._relationships.get(name);
rel.clear();
rel.destroy();
}
});
Object.keys(this._implicitRelationships).forEach((key) => {
this._implicitRelationships[key].clear();
this._implicitRelationships[key].destroy();
});
} See: v2.12.2/internal-model.js The key here is that it called However, with the update from #4593, it no longer takes this action when the inverseKey is undefined. The reason is that there is now a I can confirm that making this change fixes the issue with the test repo provided by @sudowork . However it still leaves an issue with my application which is using filtering. :( I did not yet submit a PR because I'm not confident that this isn't really just me playing whack-a-mole. I think I'll need several more days to a week to fully grok everything that's going on. If someone from the core team wants to help me out I'm happy to explore some options. |
I think just the following will also cause this: // model:foo
save() {
return this._super()
.then(() => {
if (!this.get('canHaveBars')) {
this.get('bars').forEach((bar) => {
bar.unloadRecord();
});
}
});
} |
I'm also waiting the resolution for this problem |
Could a maintainer at least acknowledge this? This is stopping me from upgrading too, and now that 2.14 is a thing, I'm a bit concerned about being two versions behind. |
acknowledged + I am actively working/thinking/exploring this. Sorry, I should have left a message. Thanks for the reminder. |
Thanks @stefanpenner -- that sets the mind at ease. :) |
Facing similar problem. Is the bug fixed in ember version 2.14.0 ? |
@rinoldsimon We encountered this issue trying to upgrade to 2.14.4 and so are staying on 2.12.2 for now. |
Just a quick update. We have found the issue, ED is working around behavior in ember. We have just landed a change in ember emberjs/ember.js#15510 which will make fixing this issue in ember-data simpler. I aim to land the fix sometime in the next week as time permits. Sorry about letting this one linger so long. |
Awesome, thanks so much for the update @stefanpenner ! |
…rd() For an async relationship [x, y] with x.unloadRecord(), now adjusts only the relationship’s currentState, leaving that relationship’s canonical state alone, ensuring the existing client-side delete semantics are preserved. But when that relationship is reloaded, the canonicalState consulted. For sync relationship [x, y] with x.unloadRecord(), both currentState and canonical state are updated. This is to mirror the client-side delete semantics. But since we cannot reload a sync relationship we must assume this to be the new canonical state and rely on subsequent `push` or `adapterPayloads` or manual `store.push` to update. This aims to: * [FIX] hasMany arrays never contain dematerialized records (so they no longer become broken) * [FIX] using unloadRecord as a type of client side delete is restored * [PRESERVE] the garbage collector pass to cleanup orphaned models * [PRESERVE] second access to a relationship which did contain an unloadRecord to cause a reload note: if both sides of a relationships are unloaded, the above doesn’t apply. This is largely just when members of a loaded relationship are themselves unloaded. [fixes #4986 #5052 #4987 #4996]
…rd() For an async relationship [x, y] with x.unloadRecord(), now adjusts only the relationship’s currentState, leaving that relationship’s canonical state alone, ensuring the existing client-side delete semantics are preserved. But when that relationship is reloaded, the canonicalState consulted. For sync relationship [x, y] with x.unloadRecord(), both currentState and canonical state are updated. This is to mirror the client-side delete semantics. But since we cannot reload a sync relationship we must assume this to be the new canonical state and rely on subsequent `push` or `adapterPayloads` or manual `store.push` to update. This aims to: * [FIX] hasMany arrays never contain dematerialized records (so they no longer become broken) * [FIX] using unloadRecord as a type of client side delete is restored * [PRESERVE] the garbage collector pass to cleanup orphaned models * [PRESERVE] second access to a relationship which did contain an unloadRecord to cause a reload note: if both sides of a relationships are unloaded, the above doesn’t apply. This is largely just when members of a loaded relationship are themselves unloaded. [fixes #4986 #5052 #4987 #4996]
…rd() For an async relationship [x, y] with x.unloadRecord(), now adjusts only the relationship’s currentState, leaving that relationship’s canonical state alone, ensuring the existing client-side delete semantics are preserved. But when that relationship is reloaded, the canonicalState consulted. For sync relationship [x, y] with x.unloadRecord(), both currentState and canonical state are updated. This is to mirror the client-side delete semantics. But since we cannot reload a sync relationship we must assume this to be the new canonical state and rely on subsequent `push` or `adapterPayloads` or manual `store.push` to update. This aims to: * [FIX] hasMany arrays never contain dematerialized records (so they no longer become broken) * [FIX] using unloadRecord as a type of client side delete is restored * [PRESERVE] the garbage collector pass to cleanup orphaned models * [PRESERVE] second access to a relationship which did contain an unloadRecord to cause a reload note: if both sides of a relationships are unloaded, the above doesn’t apply. This is largely just when members of a loaded relationship are themselves unloaded. [fixes #4986 #5052 #4987 #4996]
…rd() For an async relationship [x, y] with x.unloadRecord(), now adjusts only the relationship’s currentState, leaving that relationship’s canonical state alone, ensuring the existing client-side delete semantics are preserved. But when that relationship is reloaded, the canonicalState consulted. For sync relationship [x, y] with x.unloadRecord(), both currentState and canonical state are updated. This is to mirror the client-side delete semantics. But since we cannot reload a sync relationship we must assume this to be the new canonical state and rely on subsequent `push` or `adapterPayloads` or manual `store.push` to update. This aims to: * [FIX] hasMany arrays never contain dematerialized records (so they no longer become broken) * [FIX] using unloadRecord as a type of client side delete is restored * [PRESERVE] the garbage collector pass to cleanup orphaned models * [PRESERVE] second access to a relationship which did contain an unloadRecord to cause a reload note: if both sides of a relationships are unloaded, the above doesn’t apply. This is largely just when members of a loaded relationship are themselves unloaded. [fixes #4986 #5052 #4987 #4996]
…rd() For an async relationship [x, y] with x.unloadRecord(), now adjusts only the relationship’s currentState, leaving that relationship’s canonical state alone, ensuring the existing client-side delete semantics are preserved. But when that relationship is reloaded, the canonicalState consulted. For sync relationship [x, y] with x.unloadRecord(), both currentState and canonical state are updated. This is to mirror the client-side delete semantics. But since we cannot reload a sync relationship we must assume this to be the new canonical state and rely on subsequent `push` or `adapterPayloads` or manual `store.push` to update. This aims to: * [FIX] hasMany arrays never contain dematerialized records (so they no longer become broken) * [FIX] using unloadRecord as a type of client side delete is restored * [PRESERVE] the garbage collector pass to cleanup orphaned models * [PRESERVE] second access to a relationship which did contain an unloadRecord to cause a reload note: if both sides of a relationships are unloaded, the above doesn’t apply. This is largely just when members of a loaded relationship are themselves unloaded. [fixes #4986 #5052 #4987 #4996]
…rd() For an async relationship [x, y] with x.unloadRecord(), now adjusts only the relationship’s currentState, leaving that relationship’s canonical state alone, ensuring the existing client-side delete semantics are preserved. But when that relationship is reloaded, the canonicalState consulted. For sync relationship [x, y] with x.unloadRecord(), both currentState and canonical state are updated. This is to mirror the client-side delete semantics. But since we cannot reload a sync relationship we must assume this to be the new canonical state and rely on subsequent `push` or `adapterPayloads` or manual `store.push` to update. This aims to: * [FIX] hasMany arrays never contain dematerialized records (so they no longer become broken) * [FIX] using unloadRecord as a type of client side delete is restored * [PRESERVE] the garbage collector pass to cleanup orphaned models * [PRESERVE] second access to a relationship which did contain an unloadRecord to cause a reload note: if both sides of a relationships are unloaded, the above doesn’t apply. This is largely just when members of a loaded relationship are themselves unloaded. [fixes #4986 #5052 #4987 #4996]
…rd() For an async relationship [x, y] with x.unloadRecord(), now adjusts only the relationship’s currentState, leaving that relationship’s canonical state alone, ensuring the existing client-side delete semantics are preserved. But when that relationship is reloaded, the canonicalState consulted. For sync relationship [x, y] with x.unloadRecord(), both currentState and canonical state are updated. This is to mirror the client-side delete semantics. But since we cannot reload a sync relationship we must assume this to be the new canonical state and rely on subsequent `push` or `adapterPayloads` or manual `store.push` to update. This aims to: * [FIX] hasMany arrays never contain dematerialized records (so they no longer become broken) * [FIX] using unloadRecord as a type of client side delete is restored * [PRESERVE] the garbage collector pass to cleanup orphaned models * [PRESERVE] second access to a relationship which did contain an unloadRecord to cause a reload note: if both sides of a relationships are unloaded, the above doesn’t apply. This is largely just when members of a loaded relationship are themselves unloaded. [fixes #4986 #5052 #4987 #4996]
Example repo and integration test to reproduce the issue
Update: I wrote a test case to test this behavior sudowork@89a34ba. I bisected the issue to have started with 6d96eda (from #4593).
Update 2: Converted the repo I had to an ember-twiddle now that I can load 2.13: https://ember-twiddle.com/df7fa30d2eb3d32efe16185f4f376128?openFiles=controllers.todo-list.js%2C
Steps to reproduce:
todo-list
whichhasMany
todo
items astodos
already loaded in the store and is currently in scope.todos.removeObject(todo)
to remove the item from the relationshipthis.store.unloadRecord(todo)
to clean up the record from the storeget(this, 'model.todos')
again to load thetodo
records. (Expected result:[]
, Actual result: Assertion error thrown).The assertion error thrown is due to this assertion that checks that
manyArray.isEvery('isEmpty', false)
on[null]
.Gifs
2.13.1 (broken)
2.12.2 (working)
Note: On 2.12, it does not even go through
getRecords()
when retrieving the records again.The text was updated successfully, but these errors were encountered: