As for similar questions on this topic and on ChildEventListener, there is no relevant answer, so heres mine.
I have a local SQLite DB which holds all the data, I also have Firebase realtime database which I'm updating with new entries or real time changes across all users. I'm currently doing it with the use of ChildEventListener as follows:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getDatabase().getReference();
DatabaseReference childRef = rootRef.child("my_root");
ChildEventListener eventListener = new ChildEventListener()
{
....
};
childRef.addChildEventListener(eventListener);
As for functionality, With this code I can get realtime changes on childs, get new entries, deleted childs and everything I need but there is one problem. When this specific activity with the listener loads up, the onChildAdded listener gets called enormous amounts of times for every child on this root, as stated on the documentation:
child_added is triggered once for each existing child and then again every time a new child is added to the specified path
So I though to gain focus on the items that I really need and I have done it with:
rootRef.orderByKey().startAt("-WhatTF123456789")...
But then I have lost my CRUD capabilities because it's listening to the new entries and not all of them.
So I came up with a solution. Keep node with all the changes that has been made to the FireBase database and a node with all the users that have read and made the changes to the local DB to know who needs an update, Then use addChildEventListener to this specific node. But that seems redundant.
What is my options to handle this kind of situation?
The onChildAdded listener gets called enormous amounts of times for every child on this root.
As you already mentioned and as the docs states, this is the expected behaviour. Usually, is not recommended to attach a ChildEventListener on a node (root node) that contains huge amount of data. Please be careful about this practice because when downloading large amount of data, you can get erros like: OutOfMemoryError. This is happening because you implicitly download the entire node that you are listening to, along with all the data beneath it. That data might be present as simple properties or, as complex objects. So it can be considered a waste of resource and bandwidth. In this case, the best approach is to flatten the database as much as possible. If you are new to NoSQL databases, this practice is called denormalization and is a common practice when it comes to Firebase. For a better understanding, I recommend you take a look at:
This video, Denormalization is normal with the Firebase Database.
Official docs regarding Best practices for data structure in Firebase realtime database.
My answer from this post: What is denormalization in Firebase Cloud Firestore?
This article, Structuring your Firebase Data correctly for a Complex App.
This article, NoSQL data modeling techniques.
Please also note that when you are duplicating data, there is one thing that need to keep in mind. In the same way you are adding data, you need to maintain it. With other words, if you want to update/detele an item, you need to do it in every place that it exists.
I also recommend you to see the last part of my answer from the following post:
What is the correct way to structure this kind of data in firestore?
It is for Cloud Firestore but same rules apply to Firebase realtime database.
But then I have lost my CRUD capabilities because it's listening to the new entries and not all of them.
Everything in Firebase is about listeners. You cannot get realtime updates for objects within a node, unless you are listening to them. So you cannot limit the results and expect to get updates from objects that you are not listening to. If you need to get updates for all objects within a node, you need to listen to all of them. Because this approach isn't practical at all, you can either use denormalization as explained above or to restrict the results by using queries that can help you limit the amount of data that you get from the database. Regarding your solutions, the second one is much preferred but you can also consider another approach which would be to load data in smaller chunks according to a timestamp property, or according to any other property that you need.
Edit: According to your comment:
Can you please provide tests for each solution (1.denormalization, 2.my solution) examine use of bandwidth and resources and which one is really preferred?
All data is modeled to allow the use-cases that an app requires. Unfortunately, I cannot do tests because it really depends on the use-case of the app and the amount of data that it contains. This means that what works for one app, may be insufficient for another app. So the tests might not be correct for everyone. The denormalization process or your solution is entirely dependent on how you intend to query the database. In the list above, I have added a new resource which is an answer of mine regarding the denormalization tehnique in NoSQL databases. Hope it will also help feature visitors.
I would make a root node with the name, for example, MaintenanceUpdate.
All clients are subscribed to changes here.
As soon as MaintenanceUpdate becomes = true, all clients unsubscribe from changes to the main "database". And then (when MaintenanceUpdate = false) are re-subscribed again.
At this time you are updating the database.
I have similar requirements, with Firebase and Room, while I've solved it alike this:
public class BaseModel extends BaseObservable implements IDataModel {
/** Sqlite default PK */
private int itemId = 0;
/** Firebase uniqueId */
#ColumnInfo(name = SqliteBaseHelper.KEY_FIREBASE_UNIQUE_ID)
protected String uniqueId = null;
/** Firebase lastSync */
#ColumnInfo(name = SqliteBaseHelper.KEY_FIREBASE_LAST_SYNC)
protected long lastSync = 0;
...
}
this means, when a local record has a KEY_FIREBASE_UNIQUE_ID which is null and the KEY_FIREBASE_LAST_SYNC is 0, it has to be inserted into Firebase - else it would check, when running a synchronization AsyncTask, if the local or remote record needs to be updated. this is because the main issue is, that when inserting remotely, the ChildEventListener will attempt to synchronize duplicates to the same client - unless having such indicators for the synchronization status in place, locally and remotely. the local primary keys might vary across the clients (depending for how long they were offline and how many records where locally inserted during the offline state), while the synthetic KEY_FIREBASE_UNIQUE_ID is used for identifying them; it's the "key to success".
Related
I am performing add to cart feature on Android and my idea to do that is if nodes are given like this:
Nodes:
Items:
|
|--item1Key--
|
|--price:10
|
|--itemkey2--
|
|--price:20
User:
|
|--inCartItems:
|
|--itemKey1
|
|--itemKey2
I am doing this by storing only the keys of items added in the cart to inCartItems, not the price because if somehow I will admin will change the price from Items node then it will not reflect inCartItmes node when I will store the price too in inCartItems node.
Can anyone suggest me better way to do that? All answers will be
appreciated.
Now in this case when we store only the keys of items then there generates the issue given in my previous problem
Previous problem
Edit --
// Here I successfully got all the keys in lyKey
for(ItemsExploreModel favkeys : ltKey)
{
// Toast.makeText(getContext(), favkeys.getKey(), Toast.LENGTH_SHORT).show();
mDatabase.getReference().child(FirebaseVar.ALLITEMS).child(favkeys.getKey()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot2) {
if (snapshot2.exists())
{
ItemsExploreModel adp = snapshot2.getValue(ItemsExploreModel.class);
ItemsExploreModel adp2 = new ItemsExploreModel(snapshot2.getKey());
list.add(adp);
listKey.add(adp2);
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
Here I have done the same thing with Adding into Favorite.
As per to my experience working with Firebase database, it is completely fine to make nested queries.
However Dharmaraj's answer is also good (to use cloud function to update data at multiple places) but it make duplicate copies of same data and in some case cloud function failed to update the data, that time it'll return wrong data.
The structure you have prepared is good. No need to duplicate the data (Single Source of Data). So on change in data, you don't need to worry about the other places where those changes has to be performed.
However, you just need to make nested query on app side.
The nested query you are performing is wrong. You are adding ValueEventListener.
According to this doc, ValueEventListener adds continuous listener on that node of child. And as per your needs, you don't need to continuously listen to the data changes while listing card items. So I would suggest you to use `OnCompleteListener (documentation) to read item data once only that will improve your app performance as well.
I'm some kind of late to the party, but I would like to point out a few things.
If you duplicate the entire object (including the price), then it's true, if the admin/system changes a price, it won't be reflected in the cart, since there is nothing built-in that propagates that. This practice of duplicating data is called denormalization, and it's a quite common practice when it comes to NoSQL databases. The linked answer is tagged with google-cloud-firestore, but the same rules apply also in the case of the firebase-realtime-database.
As also #Dharmaraj mentioned in his answer, you can create a synchronization mechanism between the actual item and the item in the cart, by creating a function that should immediately fire, when a price is changed. But this also means that the price should be updated in all carts in which the item exists.
Is it still worth performing this operation? The answer is no, it doesn't make any sense to update such an amount of data. Why? Because there might be some users that can leave the cart as it is, without finishing the purchase. So you'll end up updating some records that don't require that. So in my opinion, the best option that you have is to check if the price of the items has changed, when the user finishes the purchase. In that way, you'll only update the price for those users who are really willing to finish the purchase.
And going forward, that's not always the best option to go ahead with. Imagine you're inside an online shop, and you find an offer with 30% off and you add that item to the cart. One minute later you want to finish the purchase but the price has changed. That's kind of weird and can be considered a really bad user experience because anyone would like to buy the item at the price it was displayed when it was added to the cart and not at some other (bigger) price. So take this situation also into consideration.
Can this denormalization be avoided?
Yes, it can. You can always read the items in the cart and keep them in sync with the actual items. When a price is changed, you can notify the user in real-time, so it can take some actions according to it. But that will only work if you'll use a persistent listener. This means that you have to use a Query#addValueEventListener() and not a Query#addListenerForSingleValueEvent(ValueEventListener listener) or a Query#get().
And the last thing, when it comes to Android, when using such code on the client, please note that there is nothing wrong with nested listeners as long as you remove them according to the lifecycle of your activity.
After fetching the inCartItems, you can then query each item's price with another query. That'll ensure you are fetching the latest prices from the seller/admin.
If you copy the prices to each inCartItem, you can still update all prices once admin changes the price RTDB triggered Cloud Function, but you can update a maximum of 16 MB of data when using Firbase SDKs at once (256 MB in case of REST API). Once you get plenty of data, you'll have to throttle the updates in a way. Firebase also offers a REST API, so users don't even have to open your app once they figure out you are updating the prices in cart only when they open the app and any client side listener triggers. Hence, I would prefer a Cloud Function in case you are duplicating the data.
Also, if you've written the code correctly, I doubt if any of the Cloud Functions will fail unless there's any edge case.
There may be some race conditions that users sees Price 1 but while they checkout, it's Price 2 already.
The first method seems better as users will always see latest price.
I have a scenario where any update/change in the data by a cms user through application/CMS needs the approval of the admin/authorizer user. There may be multiple changes in one update in a single document/record. This approval will not be done in real-time and may take few hours or may be days. Authorizer may also reject the change. So in this case what would be the best way to keep this data alive without comitting it to the database till approval or rejection. Should I create temporary or duplicate tables to keep this data temporarily in the db? But this will result in large number of temporary tables(one for each table). Or is there any other option at developer/application/java end? I am using here Oracle with Java.
You need to better understand the problem.
You do not require one datastore,
you require two datastores.
Datastore one (possible table one) will contain unapproved changes.
This is the "proposed" state.
You will write and commit all data into this datastore as soon as the user requests the change.
Datastore two (possible table two) will contain the approved changes;
this is the "real" state.
Once a change that is in datastore one has been reviewed and approved,
you must apply the change here.
A possible other solution is to use a Kafka topic:
Use a Kafka topic to store the unapproved changes.
Feed the topic to reviewers.
When approved, note the decision (in the same topic) and write the change to the database.
Note:
datastore 1 and datastore 2 can be the same table,
just have a column to indicate "approved change",
"declined change",
and "pending change".
You can always have draft and final copy of the data. Draft copy will save your work in draft mode, committed and operation like save / confirm from app can copy this into final version.
This requires one more record to identify draft / final version and you should be using draft data to show on UI.
So I am wondering about how necessary it is to store variables in the realtime firebase database if I want all Users at access the same dynamic variable.
So for instance, I have a arraylist that stores the list of open games, and if I want this list to update in realtime for every user should this List in firebase realtime database?
Sorry for the simplicity of the question
Yes, it may be a simple question, but it surely pops in head of everyone, once.
I think for updating any list dynamically in real time, would require you to access any kind of database.
It is not necessary to have it on Firebase database, but any database online, that can tell every open instance of your app that the list has to be updated at a particular instant.
The main reason of why you need it to be on database is updating it in real time and that too dynamically.
If it's not dynamic, meaning the content that you need, can be hardcoded then one way would be placing everything you need in your code and using timer or something like that to fire at particular moments to update things in your app.
Also that aside, sorting, storing and changing data is much simpler using a database, which also becomes one more reason for you to use a database like Firebase to keep content of your app that has to be updated frequently in real time.
You can know more about database in this Google link, I found.
I was trying to develop an mobile app which have some similar idea just like Uber, which is real time update driver & customer location. So here I am seeking for suggestion for what I was thinking for the app structure.
For what I research, in order to provide fast real time update location, I may need to make use of real time database such as Firebase for the backend. So, I was thinking to combine 2 different type of database to achieve what I was thinking...
Firebase - For real time fast update user location
MySQL - For backend api business logic
However, I have no experience with firebase, so I hope you all can give some advise. I plan to only store the user location coordinate information in firebase database, then retrieve it from mobile app to update realtime.
My problem is I not sure should I forever persist those driver coordinate data in firebase database? Since the coordinate data keep changing update in firebase, so should I delete those coordinate data from firebase as soon as the driver have reach the destination. (No need to keep those data persist, only real time data keep change on firebase)
Thanks for reading such long question, I will also happy that if you all can remind to me any other concern if I use 2 different database for my application.
You'll typically keep a list of drivers and their locations in Firebase:
driverlocations
driver1id: location
driver2id: location
This means that you're not adding new data, but updating existing data. So you don't have to worry about the list constantly growing.
To ensure you don't have stale data for drivers that closed the app/stopped driving, you can use Firebase's onDisconnect() handlers to remove the data when they disconnect.
Now querying this data for nearby drivers is still tricky. You'll want to look at GeoFire for that. I recently explained why that is needed and how it works here: Sort array by distance near user location from firebase
I have an SQLite database which I have to be constantly retrieving data from. Changes may be done to the data between each retrieval.
My goal is to maximize the app performance, so what is the fastest way to do this retrieving?
I can imagine 2:
constantly opening and closing new cursors
query all data at the beginning and store it in an ArrayList. When changing the data, change both SQLite DB and the ArrayList using indexOf.
---- EDITED ----
I need the data to create markers in a google's map.
I have considered using CursorLoader but as I don't need to interact whith other apps I don't want to use Content Providers.
Would creating a custom loader be a good idea?
In short, while it's not always that simple, the fastest way to do things is all at once.
Constantly making calls to and from a database can really make your apps performance bottleneck, especially if it's to a server and not just your devices SQLite database.
Depending on what you're doing with the data, you may be able to look into something like a CursorAdapter which handles the display of rows from the database, and each time you insert/update a row, the CursorAdapter will update the ListView accordingly. It also handles the opening/closing/moving to next of the Cursor, making it very readable and easy for developers to follow.
Again, however, try to do things in as few calls as possible. If you stick to using an ArrayList:
Make one call in the beginning for all items.
Loop through that cursor and add items to an array list.
Use the array list as a cache. Sure, you could update the DB each time you update the list (which might be safest, IMO), or you can just loop through the list and insert/update/delete when the app closes. If you take that approach, make sure you do so in a method like onPause(), as it is one of the earliest methods in which an Activity can be killed.
Perfect use case for a CursorLoader. Given a query, it'll keep your list adapter up to date with the latest data, assuming you notify when changes happen in the DB. It also conveniently handles activity lifecycle events for your (ie. it'll close the cursor when the activity finishes, stop updating when it pauses, etc.).
The fastest way is obviously to not use a database at all. However, that is clearly not a solution unless you find some way of exposing your array to access from elsewhere.
Using a database is a convenient way of centralising the data so many users can access the data and have the data up-to-date at all times. Unfortunately this is the slowest option.
Choosing your middle-ground between speed and availability is a difficult task. You have to find a balance between stale data and throughput.
If, for example, you would be comfortable with a picture of the data that was valid just 5 seconds ago then you could probably cache the data locally in your array and arrange for some mechanism to keep it up-to-date running behind the scenes.
If a 5 minute lag was acceptable you could probably arrange for a regular push to database.
Also, any mechanism you use must also handle parallel changes to the data - perhaps two users change the same datum at the same time.
You just need to decide on where to strike your balance.