Search code examples
javaandroidreal-timefirebase

Firebase rooms with limited number of participants


In Firebase, creating "rooms" such as for chats is easy, just as documented in their various samples.

For the data structure of the chat, I would use something like this:

rooms
    room1
        member_count
        members
            user1
            user2
        messages
            message1

But now I would like to create to create a limit on the number of participants per room, say 3 users per chat room.

How can you do this?

In their docs, the one thing that looked most promising was using transactions. Can you verify that this is a good way to go? Or is this the wrong approach?

What about a solution like this?

Firebase countRef = new Firebase("https://mychat.firebaseIO-demo.com/rooms/room1");
countRef.runTransaction(new Transaction.Handler() {
    @Override
    public Transaction.Result doTransaction(MutableData currentData) {
        int oldMemberCount = currentData.child("member_count").getValue(Integer.class);
        currentData.child("member_count").setValue(oldMemberCount + 1); // try to update member count
        return Transaction.success(currentData);
    }

    @Override
    public void onComplete(FirebaseError error, boolean committed, DataSnapshot currentData) {
        if (error != null || !commited) {
            // rollback value (how? just do nothing?)
        }
        else {
            // transaction has been commited (value has already been saved?)
            currentData.child("members").child(CURRENT_USER_UUID).setValue(CURRENT_USER_NAME); // add user to the members list
        }
    }
});

It would be great if you could comment on this approach. Furthermore, one cannot be satisfied in that situation of course if the transaction has failed. The user still wants to join, no matter that there was another user trying to join at the same time. So what do to? Put this code into a function and call the function again in the error case?

Edit:

To create a new room with automatically unique ID, one could certainly use push() on a Firebase reference.

But if you want to add members to that room then, the problem described above remains. An alternative solution could be to set users' priority in the member list when joining. When setting their priority to the current timestamp, one could then limit the member list callbacks to 3 (members). But that doesn't seem to be elegant nor clean.


Solution

  • If you have a fixed (and relatively small) number of participants per room, it'd be best to use transactions. However, it might be best to create well-named objects for each person in a chat room, for example:

    /rooms
      /<roomid, generated by push()>
        /users
          one: null
          two: null
          three: null
    

    Joining a room would look like (code in JavaScript, please convert to Java as appropriate);

    var userid = "myuserid";
    var ref = new Firebase("<my-firebase>.firebaseio.com/rooms/<roomid>/users");
    ref.transaction(function(users) {
      if (!users.one) {
        // Claim slot 1
        users.one = userid;
        return users;
      } else if (!users.two) {
        // Claim slot 2
        users.two = userid;
        return users;
      } else if (!users.three) {
        // Claim slot 3
        users.three = userid;
        return users;
      }
      // Room is full, abort the transaction.
      return;
    }, function(err, committed, snapshot) {
      if (committed && !err) {
        // Joined room successfully.
      } else {
        // Could not join room because it was full.
      }
    });
    

    Firebase will automatically call the transaction function if it fails to commit the value to the server. In addition to the code above, you'll also need to implement some security rules that prevent users from claiming a slot that's already taken:

    {
      "rules": {
        "rooms": {
          "$roomid": {
            "users": {
              "$slot": {
                ".write": "!data.exists()"
              }
            }
          }
        }
      }
    }
    

    You can upload these rules via Forge, the graphical debugger for your Firebase and you should be good to go!