ember-data-model-fragments icon indicating copy to clipboard operation
ember-data-model-fragments copied to clipboard

can't unload/destroy fragments?

Open abuiles opened this issue 8 years ago • 6 comments

Not sure if this is a bug or desired feature, but If I do the following:

fragment = this.get('store').createFragment('thingy')
fragment.unloadRecord()

I get the error:

EmberError {description: undefined, fileName: undefined, lineNumber: undefined, message: "Attempted to handle event `unloadRecord` on <garaj…:ember2843:> while in state root.loaded.created. ", name: "Error"…}

Should the record be just removed?

abuiles avatar May 20 '16 16:05 abuiles

Definitely not ideal behavior 🙈

I assume what you're trying to accomplish is to release the fragment from the store so it can be GC'd. Since fragments don't have unique ids, there's no identity map in the store like there is for models, only references from models to fragments. This makes unloading the fragment basically a no-op, so really you can just .destroy() the fragment and never look back (assuming it's not referenced by a record).

Note that destroying unreferenced fragments is actually really important. Since there's no back-door references to fragments in the store, there's no way to clean them up automatically like models. Fragments that are referenced by models are destroyed automatically in the model's willDestroy hook (see #175), but fragments that aren't referenced by a model are never GC'd unless they are manually destroyed. This doesn't usually affect app performance, but it can have a significant impact on test suites where fragments are abandoned in a common code path.

I'm curious what you think would be better behavior of Fragment#unloadRecord:

  1. No-op
  2. No-op with console warning
  3. Ember.assert

slindberg avatar Jun 18 '16 23:06 slindberg

@slindberg 3 Ember.assert since it leads to leaking objects.

ffaubry avatar Dec 05 '16 23:12 ffaubry

We are experiencing some hardship with this and wonder if anything has been or could be done about it. We are using model fragments, and our data updates frequently. We unload the model data from the store for the model that the fragments are included in to keep the memory footprint down. However, the model fragments as shown in the Ember inspector don’t appear to unload with their parent so appear to grow infinitely. It also doesn’t seem to be possible to do a store.unloadAll on the fragments. So is the only solution to iterate over all the fragments and do a destroy() on each? Or is there another solution?

GeekSMN avatar Jan 18 '17 17:01 GeekSMN

Currently experiencing same issue as @GeekSMN.

rsiemens avatar Feb 08 '17 20:02 rsiemens

Looking into this further it seems to be something with Ember inspector that holds onto them.

With Ember Inspector:

const store = App.__container__.lookup('service:store');
store.unloadAll();
let fragments = store.peekAll('cell');
fragments.get('firstObject')  // --> fragment

Without Ember Inspector (incognito window):

const store = App.__container__.lookup('service:store');
store.unloadAll();
let fragments = store.peekAll('cell');
fragments.get('firstObject')  // --> undefined

rsiemens avatar Feb 08 '17 20:02 rsiemens

I made a small test project using the examples of the documentation (a Person model with a Name fragment). I added this method so I can trace the number of fragments in the store in the console:

window.numOfFragments = () => {
  return this.store.peekAll('name').get('length'));
};

First I push some data to the store in the application route model hook:

this.store.push({
  data: {
    id: 'p1',
    type: 'person',
    attributes: {
      name: {
        first: 'Keith',
        last: 'Moon'
      }
    }
  }
});

Right now numOfFragments returns 0. It also doesn't display the fragment in the Ember Inspector. I suspect a fragment is only created when altering the fragment data.

Now I create a completely new person record using store.createRecord in an action triggered by a button. I don't have to create the name fragment explicitly since I added a default value to the Person model just as the documentation suggests.

name: fragment('name', { defaultValue:{ first:'', last:'' } })

The new fragment is visible in the Ember Inspector and numOfFragments returns 1.

Next I remove the new record using either unloadRecord or destroy directly on the record or unloadRecord or destroyRecord on the store. Whatever method I use, the result is the same. The record is indeed gone, but the fragment is still there and numOfFragments still returns 1. So even when destroying the containing record the fragment persists resulting in the same problem as @GeekSMN describes.

I tried to find a solution in the ext.js file of this addon by adding this.store.unloadRecord(fragment) to the willDestroy hook, but I get the same error as @abuiles describes.

Using a browser with or without the Ember Inspector doesn't seems to make a difference.

Baukereg avatar Feb 22 '18 10:02 Baukereg