5

I would like to make a chat using Firebase. I need to display for each users the list of group they belong to and also for each group all the members.

Because of how Firebase is designed, and in order to have acceptable performance, I am think making a list of all the groups containing the list of members, and for each users, an entry with all the group they belong too.

My first question is, is it the right approach?

If so, my second question is how can I atomically add (or removed) a user, i.e. making sure both the user is added in the group and the group added into the user or not added at all (i.e. never stored at 1 location only and make the DB inconsistent) ?

maalls
  • 749
  • 8
  • 20
  • Can you please post your data model? It looks like you are are on the right track with the denormalized approach. For the second part, you may have to use a service (or a factory) to make sure users and groups data are updated in a single place. What have have you done so far? – Saeed D. Apr 25 '15 at 18:33
  • What I have in mind is simply having /groups/{group_id}/members and /users/{user_id}/groups with members an array of users ids and groups an array of group ids. User and Group will be updated in the same place but I want to make sure the database is always consistent, even in the extreme case where the server crash after updating group but before updating the user. – maalls Apr 25 '15 at 18:43
  • I changed my question to make it more clear. – maalls Apr 25 '15 at 18:45
  • You can make atomic changes across multiple paths now. See [this blog post](https://www.firebase.com/blog/2015-09-24-atomic-writes-and-more.html) for details. – Kato Dec 29 '15 at 22:42

1 Answers1

2

The data model you have proposed is consistent with recommendations for firebase. A more detailed explanation is outlined in best way to structure data in firebase

users/userid/groups
groups/groupid/users

Firebase provides .transaction for atomic operations. You may chain multiple transactions to ensure data consistency. You may also use onComplete callback function. For detailed explanation refer to firebase transaction. I am using this same approach to keep multiple message counts up to date for a dashboard display.

Sample code for nested transaction:

ref.child('users').child(userid).child(groupid).transaction(function (currentData) {
  if (currentData === null) {
    return groupid;
  } else {
    console.log('groupid already exists.');
    return; // Abort the transaction.
  }
  }, function (error, committed, snapshot) {
    if (committed) {
      console.log('start a new transaction');
      ref.child('groups').child(groupid).child(userid).tranaction(function (currentData) {
        if (currentData === null) {
            return userid;
        } else {
            console.log('Userid already exists.');
            //add code here to roll back the groupid added to users
            return; // Abort the transaction.
        }
      }, function (error, committed, snapshot) {
         if (error) {
             //rollback the groupid that was added to users in previous transaction
         }
      })
  }
});
Community
  • 1
  • 1
Saeed D.
  • 1,125
  • 1
  • 11
  • 23
  • Firebase's transaction are basically useful to update a value based on it previous value. But it doesn't help atomic operations accross multiple locations. – maalls Apr 26 '15 at 01:57
  • Update function is one of the uses of transaction, you may also use the onComplete callback to start a new transaction, creating nested transactions. I added some sample code. I am already using this approach and it works for me. – Saeed D. Apr 26 '15 at 06:27
  • thanks but sorry, what is the code doing or when should I use it ? Is it for adding a new user to a group ? – maalls Apr 26 '15 at 12:18
  • When a user joins a group, you have to add the user to that group and add the groupie to that user. This would be similar to your traditional database transaction. This was your original question as I understood it. – Saeed D. Apr 26 '15 at 14:00
  • Pardon me for being extremely late to this discussion, i am facing a similar situation myself... My question was... What to do if the rollback transaction failed :-/ – Kushan Nov 21 '16 at 16:58
  • @Saeed D, if the internet connection drops in between transactions you will have no way to roll back as I see it, meaning that if internet drops here `if (committed) { console.log('start a new transaction');` the code below it will no longer be evaluated. Am I right? – bibscy Aug 05 '17 at 07:30
  • @SaeedD. what happens if the internet connection drops immediately after you receive the response from the first transaction. Does it not mean that your second transaction would never be executed. Assume this scenario: - first transaction finishes executing, you get the response in the completion handler, now the internet connection drops, on the user interface an activity indicator is presented, the user quits the app. Second transaction will never be executed. Am I right? Please see my question https://stackoverflow.com/questions/45508007 Thank you – bibscy Aug 06 '17 at 20:20