Skip to content
This repository was archived by the owner on May 19, 2025. It is now read-only.
This repository was archived by the owner on May 19, 2025. It is now read-only.

Loss of Data - belongsTo disappearing  #364

Open

Description

@brendanoh

So I am having an issue for the 2nd time on an Emberfire project. Last time was almost a year ago when I was putting together a meetup preso on building a chat client in Ember and Firebase.

I have model's with belongsTo relationships and during local debugging the belongsTo keys in forge disappear and my relationship is gone.

I cannot reproduce it and it feels like it only seems to happen during heavy local debugging but it has happened half a dozen times times or so on this recent project so I feel confident it is real and not in my head.

I have found Angularfire issues posted with potentially similar data loss problems but none in this repo. And they all seem related to use of FB internals that I do not touch.

I am wondering if anyone else has experience this as it is very frustrating.

Activity

  1. tstirrat commented on Feb 8, 2016

    @tstirrat
    Contributor

    Does this happen when you delete a record locally? there is some code that tries to remove orphan hasMany links from the server on delete. Could this be it?

  2. brendanoh commented on Feb 8, 2016

    @brendanoh
    Author

    @tstirrat no not during delete (I think).

    I believe it is happening when I am debugging a save after an edit and the browser is stopped at a debugger statement and then reloads after I make some live code changes and doesnt seem to actually hit the model.save() point. Perhaps it is a partial save of some sort?

  3. tstirrat commented on Feb 8, 2016

    @tstirrat
    Contributor

    Ahh, yes, it could be related to us saving relationship links in subsequent requests. This will be fixed in #337

  4. brendanoh commented on Feb 9, 2016

    @brendanoh
    Author

    Well that is good. This seems like such a big issue I am surprised (and thankful) no one else has hit it :-)

  5. tstirrat commented on Feb 16, 2016

    @tstirrat
    Contributor

    I wonder if this is related to #367 where the the belongsTo is nullified due to some inverse relationship sync issue, and when you save the model it deletes the belongsTo key.

    Does that ring a bell? are you updating a hasMany and the later realizing the inverse belongsTo is null?

  6. ewalti commented on Mar 28, 2016

    @ewalti

    I'm running in to belongsTo relationships as well on 1.6.6. Worked fine on 1.6.0. The model is very simple:

    // keyboard/model.js
    export default DS.Model.extend({
      title: attr('string'),
      photo: belongsTo('photo')
    });
    
    // photo/model.js
    export default DS.Model.extend({
      title: attr('string'),
      url: attr('string'),
    });
    

    If I create a keyboard, persist it, then create a photo, persist it then try to link it to a keyboard it all falls apart.

    const { set } = Ember;
    
    let self = this;
    this.store.createRecord('keyboard', {title: 'someTitle'}).save()
    .then((keyboard) => {
      self.createRecord('photo', {title: 'someOtherTitle'}).save()
      .then((photo) => {
        set(keyboard, 'photo', photo).save()
        .then((response) => {
          console.log('response', response);
        })
      })
    })
    

    The above results in {{keyboard.photo}} == null. The photo object is persisted, the keyboard is persisted, but the keyboard object does not have a photo key.

    I've tried photo: belongsTo('photo', {async: true, inverse: null}) to no avail.

    Now, if I define keyboard: belongsTo('keyboard') on the photo model, everything works fine. But the photo model will "belong" to many different model types, so it shouldn't have this sort of relationship.

    I may just end up using a hasMany for the moment and just select .firstObject to circumvent this issue. Is there a better way to handle this?

  7. zoltan-nz commented on Apr 9, 2016

    @zoltan-nz

    Looks, it is a real issue, I have the same problem with belongsTo, hasMany. Firebase randomly remove relations, so data will be inconsistent. It is happening with async: true or without async also.

  8. ewalti commented on Apr 9, 2016

    @ewalti

    I ended up using a hasMany and just using {model}.firstObject for the moment. Granted you end up peppering your code with this.get('someModel').clear() and what not. I can look at issuing a pull request once this app launches

  9. zoltan-nz commented on Apr 12, 2016

    @zoltan-nz

    I solved it with manually remove the previous relationship and adding the new one after.

        saveAuthor(author, book) {
          book.get('author').then((previousAuthor) => {
            previousAuthor.get('books').then((previousAuthorBooks) => {
              previousAuthorBooks.removeObject(book);
              previousAuthor.save();
            });
          });
    
          // Setup the new relation
          book.set('author', author);
          book.save().then(() => author.save());
          book.set('isAuthorEditing', false);
        },

    Working demo: https://library-app.firebaseapp.com/books

  10. tstirrat commented on Apr 12, 2016

    @tstirrat
    Contributor

    @ewalti @zoltan-nz can someone make a MCVE for me, so I can investigate? Thanks.

  11. zoltan-nz commented on Apr 12, 2016

    @zoltan-nz

    @tstirrat http://emberjs.jsbin.com/juwasa/edit?html,js,output

    Start playing with Author names, change them with the select box (after select, they will saved immediately).

  12. florathenb commented on Aug 1, 2016

    @florathenb

    Are there any news on this issue?
    I am having the same sort of problem.
    When changing a route and loading objects from the store, the belongsTo relation is always null.
    -> new Server request, objects are loaded correctly, belongsTo relation is set.
    -> change route, same object, relation is null

  13. brendanoh commented on Aug 1, 2016

    @brendanoh
    Author

    @florathenb can you post the two models?

    Brendan

  14. florathenb commented on Aug 1, 2016

    @florathenb

    task:
    export default Model.extend({
    label: attr('string'),
    clearanceId: belongsTo('clearance'),
    comment: attr('string'),
    settings: attr('')
    });

    activity:
    export default Model.extend({
    comment: attr('string'),
    priority: attr('number'),
    timesheetIds: hasMany('timesheet'),
    taskId: belongsTo('task'),
    color: attr('string'),
    colorBox: Ember.computed('color', function _colorComputed() {
    return '

    ';
    }),
    numericID: function(){
    let id = this.get('id');
    if (id) { return +id; }
    }.property('id'),
    prio: function(){
    let prio = this.get('priority');
    if (prio != null) { return +prio; }
    else { return 99; }
    }.property('priority'),
    });

    losing the relation from activity to task

  15. brendanoh commented on Aug 1, 2016

    @brendanoh
    Author

    In my person model I have this:
    organizations: DS.hasMany('organization', {async: true, inverse: 'people'}),

    In my organization model I have this:
    members: DS.hasMany('person', { async: true, inverse: 'organizations'} ),

    You don't have any inverse relationships setup but if you never need the ACTIVITY within the TASK that is likely OK. I am not sure on async: true I thought that was still a requirement although it is supposed to be the default. If this helps here is what I am doing where I have no loss.

    When a person logs in I load all of their organizations.

    When they add or edit an organization I save both sides the following way in my org-editor component:

        saveOrganization() {
          const self = this;
          const organization = this.get('organization');
          const person = this.get('person');
          organization.set('updatedBy', person);
          organization.get('members').pushObject(person);
          organization.save().then(function(org) {
            self.clearMessage();  
            person.get('organizations').pushObject(org);
            person.save().then(function() {
              self.sendAction('action');  
            });
          });
        },  
    

    @florathenb I hope that helps.

  16. florathenb commented on Aug 1, 2016

    @florathenb

    problem is, i lose the relation allthough i am not even changing or saving anything..

    @brendanoh thanks for the answer anyway!!

  17. brendanoh commented on Aug 1, 2016

    @brendanoh
    Author

    I am not sure I understand what " i lose the relation although i am not even changing or saving anything." means.

    At some point you need to save the data in firebase, then you see it in the firebase console but later when you load the activity the taskId is gone?

    What is the ROUTE doing that could remove the belongsTo? Is it reloading either TASK or ACTIVITY?

  18. tstirrat commented on Aug 31, 2016

    @tstirrat
    Contributor

    Been away due to family emergency. Finally getting back into things.

    Often when you unexpectedly lose (or gain data) in the relationships on the client side without saving any data, it is usually from one of two things:

    • You have a hasMany defined in the other model that is being automatically set up as an inverse and this causes the belongsTo to be removed once the other model loads. To fix this you should try setting inverse: null or setting the inverse to the correct location. But you should check the next reason before doing this.
    • You have defined the relationship in both directions, but have only stored it on one side in the DB. When the hasMany side loads without the appropriate parent key present, it causes the Ember Data's inversion mechanics to erase the belongsTo side without warning.

    A real world example: I have a chat structure where a user has openChatRooms: DS.hasMany('room', ..., and each room has creator: DS.belongsTo(user, .... openChatRooms is a subset of possible chatrooms that are actively joined.

    Without setting inverse: null, a findAll('room') loads all rooms, and causes my openChatRooms to gain every single room where the creator was set to my user. This happens because each time a room is loaded, it tries to guess inverted rels, it sees a likely candidate user.openChatRooms thinking that is my list of user.createdRooms (which I haven't defined, and don't care about). It starts pushing room ids into the hasMany if they don't already exist.

    I hope this helps you narrow down what might be happening in your case.

    The moral of this story is: Try setting all of your relationships to inverse: null unless you really need the inverses. Only use inverses if you're truly going to need to traverse the relationship from the other side.. and if you do really need the inverse relationship, be explicit and save both records when you change any side of the relationship because Ember Data may silently nullify/change one side of the relationship.

  19. frcake commented on Apr 5, 2019

    @frcake

    Interesting, I have a (similar?) issue where the users in my db get deleted for no apparent reason. This happens sparsely/randomly and by looking at sentry logs I can see it happening right after a login, where a "no record found" error will pop.

    The user model in my project has all the entities belonging to him and he as an entity belongs to no model, also all the relationships are inverse: null ..

    I tried recreating the issue by spamming the login with logins/outs but nothing, i also tried many other stupid/random things someone could do, again nothing. We used to think that this error occurred only after a new deployment where a user wouldn't refresh his page and things would go wrong, but it seems that this is not the case.

    "emberfire": "^2.0.10"

    The login code is as follows

    /app/controllers/sign-in.js
    
    import Ember from 'ember';
    import { task, timeout } from 'ember-concurrency';
    import FindQuery from 'ember-emberfire-find-query/mixins/find-query';
    
    const DEBOUNCE_MS = 400
    
    export default Ember.Controller.extend(FindQuery, {
      application: Ember.inject.controller('application'),
      session: Ember.inject.service(),
      firebase: Ember.inject.service(),
      store: Ember.inject.service('store'),
      buttonLabel: 'Sign In',
      provider: "password",
    
      signInUser: task(function*() {
        try {
          yield this.get('session').open('firebase', {
            provider: this.get('provider'),
            email: this.get('email') || '',
            password: this.get('password') || '',
          });
    
          this.transitionToRoute('welcome');
    
        } catch (ex) {
          this.get('application').set('visible', true);
          this.get('application').set('alertMessage', ex.message);
          this.get('application').set('type', 'danger');
        }
      }).drop(),
    });
    

    and the logout

    /app/controllers/application.js
    
    import Ember from 'ember';
    import firebase from 'firebase';
    
    export default Ember.Controller.extend({
      firebase: Ember.inject.service(),
      actions: {
        signOut() {
         this.set('firebase.u.Ra.$',{});
          this.get('session').close().then(() => {
           this.transitionToRoute('/');
          });
        }
      }
    });
    

    I was forced to do this weird this.set('firebase.u.Ra.$',{}); cause after each logout we'd have many errors popping up for breaking references and such.

    I don't know if everything done here is correct, or best practice, but our knowledge about emberfire grew as the project was being developed.

    Should i post this here or create another issue maybe?

  20. brendanoh commented on Apr 5, 2019

    @brendanoh
    Author

    @frcake EmberFire has changed dramatically since my last issue of this type so I am not sure if this issue will help you in figuring your issue out.

    this.set('firebase.u.Ra.$',{}); and the errors related to that seem super odd to me. Seems possible that those errors are related to this issue but dont know enough to say for sure.

    What does your user model look like?

  21. frcake commented on Apr 5, 2019

    @frcake

    @brendanoh thanks for the fast response!

    I too found it weird and difficult to overcome the errors on logout, others have the same issues too like https://stackoverflow.com/questions/38085030/closing-an-emberfire-torii-session-and-logging-out-throws-permission-denied-erro

    (now that i see this post again, there's a newer answer, i might check that out too)

    here's my user model

    import DS from 'ember-data';
    import { computed } from '@ember/object';
    
    export default DS.Model.extend({
      email: DS.attr('string'),
      bankStreet: DS.attr('string'),
      name: DS.attr('string'),
      surname: DS.attr('string'),
      organizationType: DS.attr('string'),
      organizationDescription: DS.attr('string'),
      organizationName: DS.attr('string'),
      taxNumber: DS.attr('string'),
      taxRegion: DS.attr('string'),
      street: DS.attr('string'),
      region: DS.attr('string'),
      profession: DS.attr('string'),
      telephoneNumber: DS.attr('string'),
    
      // Associations
      pawnForms: DS.hasMany('pawn-form', { async: true, inverse: null }),
      deliveryReports: DS.hasMany('delivery-report', { async: true, inverse: null }),
      customers: DS.hasMany('customer', { async: true, inverse: null }),
      calendarEvents: DS.hasMany('calendar-event'),
      // Computed
      fullName: computed('name', function() {
        return this.get('name') + ' ' + this.get('surname')
      }),
    
      fullAddress: computed('street', function() {
        return this.get('street') + ' ' + this.get('region')
      })
    });
    

    All in all, it's very strange when things work nicely 99% of the time to have that weird errors pop out and delete an entity from the firebase ?!

    An exception would look like this

    Error: no record was found at https://asdf.firebaseio.com/users/EIRI3WWPy6TBb5nYcJ8MIBcw9eC2
      at ? (/assets/vendor-a274f5466e5371ddd606ba5896e5f204.js:10345:55)
      at _(/assets/vendor-a274f5466e5371ddd606ba5896e5f204.js:3765:25)
      at R(/assets/vendor-a274f5466e5371ddd606ba5896e5f204.js:3773:9)
      at C(/assets/vendor-a274f5466e5371ddd606ba5896e5f204.js:3771:43)
      at e.invokeWithOnError(/assets/vendor-a274f5466e5371ddd606ba5896e5f204.js:1628:237)
      at e.flush(/assets/vendor-a274f5466e5371ddd606ba5896e5f204.js:1615:172)
      at e.flush(/assets/vendor-a274f5466e5371ddd606ba5896e5f204.js:1631:15)
      at e.end(/assets/vendor-a274f5466e5371ddd606ba5896e5f204.js:1639:9)
      at _boundAutorunEnd(/assets/vendor-a274f5466e5371ddd606ba5896e5f204.js:1635:388)
    

    There are also some other similar errors that originate from other routes now that i check again, pfff major headscratcher..!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions