Mustafa Berkay Mutlu
Mustafa Berkay Mutlu

Reputation: 2124

How to skip initial OnChildAdded event firing in Firebase Database

There is a users subtree in my Firebase Database which keeps basic user info and following/followers for that user, this subtree is structured like this:

"users": {
  "userId-1": { 
    "userName": "Namey McNameface",

    "following": {
      "followingId-1": true,
      "followingId-2": true,
      .
      .
    },
    "followers": {
      "followerId-1": true,
      "followerId-2": true,
      .
      .
    }
  },
  "userId-2": {},
  "userId-3": {},
  .
  .
}

I want to send a notification to user's phone whenever someone starts to following him/her. In my own server, I listen to the followers subtree. When a child added, user will see a notification. The problem is that, at some point I will need to deploy the next version of my server and when I do that OnChildAdded event will fire for all children (followers) and all users will see wrong notifications about their followers starting to follow them.

I can store the sent-notification info in my database to solve this problem, but this will be a time-consuming job for me.

Another way is that I can skip the initial firing of OnChildAdded event, if that is possible in Firebase Database.

Thank you in advance.

Edit:

I am using Java and Spring for my server. This is my FirebaseService. I am creating a UserListener and starting to listen all users.

@Service
public class FirebaseService {
    public FirebaseService() {
        UserListener userListener = new UserListener();
        FirebaseDatabase.getInstance().getReference(Tag.USERS).addChildEventListener(userListener);
    }
}

This is UserListener. It creates a FollowerListener to listen followers subtree for each user.

public class UserListener implements ChildEventListener {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String s) {
        String userId = dataSnapshot.getKey();
        FollowerListener followerListener = new FollowerListener(userId);
        FirebaseDatabase.getInstance().getReference(Tag.USERS).child(userId).child(Tag.FOLLOWERS).addChildEventListener(followerListener);
    }

    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String s) {

    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {

    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String s) {

    }

    @Override
    public void onCancelled(DatabaseError databaseError) {

    }
}

This is FollowerListener. When a user ID is added to followers, FollowerListener sends notification. It basically gets the followed user's FirebaseInstanceId and follower user's screen name. Then sends notification.

public class FollowerListener implements ChildEventListener {
    private final String followedUserId;

    public FollowerListener(String followedUserId){
        this.followedUserId = followedUserId;
    }

    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String s) {
        String followerUserId = dataSnapshot.getKey();
        FirebaseDatabase.getInstance().getReference(Tag.USERS).child(followedUserId)
                .addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        User followedUser = dataSnapshot.getValue(User.class);

                        if(followedUser != null){
                            FirebaseDatabase.getInstance().getReference(Tag.USERS).child(followerUserId)
                                    .addListenerForSingleValueEvent(new ValueEventListener() {
                                        @Override
                                        public void onDataChange(DataSnapshot dataSnapshot) {
                                            User followerUser = dataSnapshot.getValue(User.class);

                                            if(followerUser != null){

                                                // Send notification to followed user.

                                                NotificationInfo notificationInfo = new NotificationInfo();
                                                notificationInfo.setTitle("test title");
                                                notificationInfo.setText(followerUser.getScreenName() + " seni takip etmeye başladı.");
                                                notificationInfo.setClickAction(Tag.ACTION_GO_TO_PROFILE);

                                                NotificationData data = new NotificationData();
                                                data.setUserId(followerUserId);

                                                NotificationRequestBean bean = new NotificationRequestBean();
                                                bean.setTo(followedUser.getFirebaseInstanceId());
                                                bean.setBody("test body");
                                                bean.setTitle("test title");
                                                bean.setPriority("normal");
                                                bean.setDelayWhileIdle(false);
                                                bean.setNotification(notificationInfo);
                                                bean.setData(data);

                                                AppUtils.sendNotification(bean);
                                            }
                                        }

                                        @Override
                                        public void onCancelled(DatabaseError databaseError) {

                                        }
                                    });
                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
    }

    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String s) {

    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {

    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String s) {

    }

    @Override
    public void onCancelled(DatabaseError databaseError) {

    }
}

The problem is whenever I deploy the server, FollowerListener attaches to all users's followers subtree. Then OnChildAdded event is firing for each follower initially. It should not send notifications every time I deploy the server.

Thanks.

Upvotes: 2

Views: 1165

Answers (1)

Mustafa Berkay Mutlu
Mustafa Berkay Mutlu

Reputation: 2124

I've found a very simple solution for this problem. I just added a boolean field in the followers subtree called processedByServer. So it became like this:

"followers": {
  "followerId-1": {
    "processedByServer": true // already processed.
  },
  "followerId-2": {
    "processedByServer": false // not processed yet, but will be.
  },
  .
  .
}

This processedByServer is initially false, after I made my necessary processing in the server (send notification etc.) I change this value to true. The server only needs to process when this field is false. So after I redeploy/reboot my server, it won't send wrong notifications anymore.

But there is also downsides of this solution:

  1. I added an extra field just for that, but I think it's ok.
  2. When the server restarts, it has to check all of the processedByServer fields in the followers subtree, which may be a time consuming job for the server.

Upvotes: 1

Related Questions