Implementing an Android+Firebase app, which has a many-to-many relationship: User <-> Widget (Widgets can be shared to multiple users).
Considerations:
- List all the Widgets that a User has.
- A User can only see the Widgets which are shared to him/her.
- Be able to see all Users to whom a given Widget is shared.
- A single Widget can be owned/administered by multiple Users with equal rights (modify Widget and change to whom it is shared). Similar to how Google Drive does sharing to specific users.
One of the approaches to implement fetching (join-style), would be to go with this advice: https://www.firebase.com/docs/android/guide/structuring-data.html ("Joining Flattened Data
"):
// fetch a list of Mary's groups
ref.child("users/mchen/groups").addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildKey) {
// for each group, fetch the name and print it
String groupKey = snapshot.getKey();
ref.child("groups/" + groupKey + "/name").addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println("Mary is a member of this group: " + snapshot.getValue());
}
@Override
public void onCancelled(FirebaseError firebaseError) {
// ignore
}
});
}
});
This prompts the question whether having potentially many listeners will have a negative impact on performance or perhaps would hit some hard limit.
But we get reassured in the doc:
Is it really okay to look up each record individually? Yes. The Firebase protocol uses web sockets, and the client libraries do a great deal of internal optimization of incoming and outgoing requests. Until we get into tens of thousands of records, this approach is perfectly reasonable. In fact, the time required to download the data (i.e. the byte count) eclipses any other concerns regarding connection overhead.
But, to be sure, I've made a little test app which compares 2-approaches:
- Attaching many
ValueEventListener
-s to all widgets one-by-one (as per Firebase's "structuring-data" guide mentioned above) - Attaching a single
ChildEventListener
to a node which hosts all the widgets (requires adequate structuring of User's Widgets under one node)
Tested on 4 different devices and android versions (4.x - 5.x). Firebase lib: 'com.firebase:firebase-client-android:2.3.1'
.
In the first approach the performance was rather disappointing.
I consistently saw ~15-100 events/s. The lowest performance, ~15 events/s was coming up quite often, so looks like it should be taken seriously.
In such case, if the User had 100 Widgets, it would take ~6seconds to get info about all Widgets (e.g. scrolling a list). This is too slow.
With 1000 Widgets, it often took as much as 40 seconds to fetch their info by separate listeners. Way too slow.
In the second approach I observed 200-3000 events/s. Thus 15-30 times faster than the first approach!
So looks like the reassurance in Firebase doc [...] Until we get into tens of thousands of records, this approach is perfectly reasonable [...]
is not really accurate given how slowly it worked.
Given all that, I have 4 inter-related questions.
- Can anyone confirm/disprove my findings given their own experience/benchmarks? Info regarding other platforms also welcome, as there is a plan to develop this app on multiple platforms.
- What could be the reason for such dramatic difference of performance? Internal data structures perhaps?
- Is there any way to improve the performance while still keeping the first (multi-listener) approach?
- Should the multi-listener approach be ditched completely in favor of a denormalized multi-copy approach which was presented in Firebase+Udacity tutorial (https://github.com/udacity/ShoppingListPlusPlus in the "userLists" node - where they keep per-user copies of shopping list info) ? I ask about implications of this approach, in another question - Firebase: structuring data via per-user copies? Risk of data corruption? .
Any other hints/considerations welcome. TIA.
Firebase.getDefaultConfig().setPersistenceEnabled(true)
), so this is not just about bandwidth. – Garwood