I'd like to better understand how the subscription model works - let's say I have some global subscriptions ie. they are loaded when the client starts, assuming it's the client's first connection to the Meteor server then all required data will be populated in minimongo and kept in sync with the server for the duration of the session.
But what happens when the client closes the app and reconnects at later stage:
Is the local store kept indefinitely on the client?
If above is true, then when the user re-connects would the data be synced to handle any differences between the local and server dbs?
Minimongo is an in-memory javascript datastore. Because it is not persisted to disk, none of the data will be available after the client browser/tab is closed. When a client reconnects, minimongo will be empty and all active subscription data will be synced as if for the first time.
Related
I am wondering if IndexedDB is the only cache Firestore uses.
To be clear, I am not talking about persisting data with enableIndexedDbPersistence(). I am talking about an internal store for the sole purpose of optimistic updates when the app is still in state. Is there something similar to redux, svelte store, or InMemoryCache which is run in the background when a subscription is open?
It seems that when I use onSnapShot() for a list of items, and I update an item in the list with setDoc elsewhere, the UI gets updated immediately, optimistically.
Perhaps Firestore is just that quick where the data is sent to the server, changed, and sent back to the client with the UI being updated that quickly, but it seems to me it is an optimistic update.
Does Firestore use any other caching techniques or state management techniques when the app is still running besides IndexedDB?
J
References (maybe releated):
Does Firebase cache the data?
As long as you have an active onSnapShot listener, the Firestore SDK will have a copy of the latest query snapshot for that listener in memory. If you attach another listener to the same (or partially overlapping) data, that listener may get (part of) its data from the existing listener.
So when you perform an update in the same client as where you have a listener, the SDK immediately applies that update to its local copy of the data and fires an event (so that is almost instantly). It then sends the update to the server, which executes it on the backend storage layer. If that update gets rejected (a relatively rare occurrence), the client will revert the change it make locally and fire another event with the corrected state.
An easy way to see this in practice is to perform a write operation that is rejected by your security rules. You'll briefly see the invalid state on the client, before it reverts to the correct state. This invalid state only happens on the client that performs the invalid write, so it's typically fine to ignore it there.
Using
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
Is this guarantee to download the data only one time across the App life/App restarts even if the user has good connection?
N.B: The official docs isn't clear ( at least for me) at this point.
By enabling persistence, any data that the Firebase Realtime Database client would sync while online persists to disk and is available offline, even when the user or operating system restarts the app. This means your app works as it would online by using the local data stored in the cache. Listener callbacks will continue to fire for local updates.
The sole goal of enabling persistence is to ensure that the app continues to work, even when the user starts it when they don't have a connection to the Firebase servers.
The client does send tree of hash values of its restored local state to the server when it connects, which the server then uses to only send the modified segments back. But there is no guarantee on how much data this sends or saves.
If you want to learn more about what Firebase actually does under the hood, I highly recommend enabling debug logging and studying its output on logcat.
For more on the topic, see these questions on Firebase's synchronization strategy.
I'm slightly confused about the availability of a collections, and exactly what happens when I use them in different places. I have a collection at /imports/api/clubs When I use it in my Meteor.methods it works fine. If I then import it into say a template file /imports/ui/pages/new_club.js and insert something into it, will it update the local cache only? Is the local collection reactive, i.e. will my helpers re-run?
A Meteor collection contains 0-N documents. A collection can be managed or unmanaged.
A managed collection:
is persisted via MongoDB on a server. It will survive either the client or the server stopping.
Exists only on the server unless it is published to the client via either the autopublish package or via one or more publications
the server automatically has read-write access to all documents in the collection
publications can be used to restrict the subset of the documents (both which documents and which keys) are available to any particular client.
changes to a collection on the server are automatically and asynchronously propagated to the affected client(s) via Meteor's DDP protocol which normally runs over WebSocket.
if changes are made to a collection from the client, the client's view is immediately updated (called an "optimistic update", part of Meteor's "latency compensation") and then the server attempts to make the same change. The server version ultimately "wins" in that changes from the server are asynchronously re-propagated back to the affected client(s).
An unmanaged collection:
exists only on the client
can be written to and read from only from the client
is not shared between clients
On the server side Meteor has access to the full MongoDB API. On the client side, Meteor has implemented "minimongo" which presents a restricted API. Minimongo is notably missing mongodb's aggregation framework as well as geo-queries.
Finally, to answer your specific question: yes, collections are reactive to changes made anywhere, either on the client you're on, by the server, or initiated on someone else's client
I'm getting the following message from firebase:
runTransactionBlock: usage detected while persistence is enabled. Please be aware that transactions will not be persisted across app restarts.
So what exactly happens after the app restarts? Do the updates in my local database get overwritten due to a sync event from the main database? Something else?
Transactions are not persisted to disk. So when you app is restarted, none of your transactions will be sent to the server.
After regaining connectivity, your local cache will contain the data from the server.
I was wondering if it is possible that a server can subscribe to a collection from the clients so that only the server got everything and the users only have their local copy of the data.
I would like to create a meteor (mobile) app where people can create some content and store it in a collection (only local on the device) that noone can see except the "owner" itself locally and the server who owns everything. If a local collection is created the server subscribes to that collection so the server can collect all the data from every user and show it e.g. on a website. So I have 3 Components. The mobile app that generates the content (clients), the server that collects everything, and a website that only read the db with the complete content.
so,
is it possible that a server can subscribe to every collection from every user?
Yes, but the idea is: every subscribed user (client) is allowed to see his data. Have a look at the collection.allow(options) documentation. A client subscribes to a collection and the server decides what the client can see.
If you have some kind of authentication (or if you use the Accounts Meteor package) you can use Meteor.publish()on server side to only publish what belongs to the current user.
For example:
//Common Collection for both client and server (declared in lib/ for example):
//(will be a full DB on the server and a minimongo with published data only
//on the client)
Data = new Mongo.Collection('Data');
//Server: (publish to client only what belongs to the user)
Meteor.publish('userData', function(){
return Data.find({your_user_id_field:this.userId});
});
//Client: (the client only gets the data that belongs to him/her)
Meteor.subscribe('userData');
[... use the local collection as usual ...]