There are several questions related to firestore cost, but I couldn't find the one which clarify the question in my head.
I have two cases, and I'd like to know the estimated cost, the document read count, in each case.
Let's assume that I have a one page app which shows 10 users. Opening the app attaches the listener to the userList collection and listens 10 documents from that collection, and closing the app detach the listener from firestore.
Case 1:
If there is no update on any document, I open and close the app, and open it again within 30 minutes. What the document read count would be? 10, 20, or any other?
Case 2:
I open and close the app; one document is updated, and I open it again within 30 minutes. What the document read count would be? 11, 21, or any other?
It depends on what you mean by "close the app".
If you have a listener that gets cut off due to loss of network connectivity, but the app process is still running, the listener will automatically reattach when the network comes back. If the network comes back within 30 minutes, you are not charged for updates. If the network comes back after 30 minutes, you are charged for new query.
If you have a listener that gets cut off because the app process was terminated by the OS, and later gets reattached when the app is launched again, you will be charged for another query.
If the app is simply backgrounded but not terminated, and the listener is still active in the background, there are no changes in behavior, but you are still paying for document updates during the time it is still added but before the app process eventually loses network and is terminated completely.
If your code removes a listener and adds it again, you will be charged for a new query.
You will have to figure out which of these situations apply. The SDK doesn't track the user's intent. It just tracks the behavior of the network, and is affected by the state of the process, as managed by the OS. The user's action of "closing an app" could involve any number of details which are not immediately obvious.
Related
In the MV2 extension, I attach a Firestore onSnapshot listener in the persistent background page. As I understand it: 1. Firestore downloads all documents on first attaching of the listener, and afterwards, 2. only downloads the changed documents when they change. Since this listener persists over several hours, the total number of Firestore read counts (and hence the cost) is low.
But in the MV3 extension, the service worker (which houses the Firestore listener) is destroyed after five minutes. Therefore, the onSnapshot listener will be destroyed and re-attached several times in just a few hours. On every re-attachment, that listener would potentially re-download all of the user data. So, if the listener gets destroyed and attached five times, we incur five times as many document read counts (and hence the cost) in an MV3 extension as compared to an MV2 extension.
I'd like to understand:
Does using IndexedDB persistence help significantly reduce the document read counts? Even the when the service worker is restarted.
In case we are not using IDB persistence, how is the billing done? For example, the billing docs state that:
Also, if the listener is disconnected for more than 30 minutes (for example, if the user goes offline), you will be charged for reads as if you had issued a brand-new query.
If I re-attach the listener within 15-20 minutes, does it again incur document reads on all user data?
I tried writing a small example myself, and monitor the results on Cloud Console Monitoring to measure "Firestore Instance - Document Reads". However, I was not able to get clear results from it.
Note: For the purpose of discussion, I will avoid the workaround for making service-workers persistent, and focus on a worst case assuming that this workaround does not work.
Does using IndexedDB persistence help significantly reduce the document read counts? Even the when the service worker is restarted.
Yes. Upon a reconnect within the 30m interval, most documents can be read from the disk cache and won't have to be read from/on the server.
In case we are not using IDB persistence, how is the billing done? ... If I re-attach the listener within 15-20 minutes, does it again incur document reads on all user data?
If there is no data in the disk cache and no existing listener on the data, the documents will have to read from the server, and thus you will be charged for each document read there.
Having spent long hours trying to find documentation and help around this resulting in nothing, I have decided to reach out to the community.
I would like to read messages from a topic subscription. Using the message, a UI is populated for a human to work on it. The time it approximately takes to process each message is 15 minutes and each client can work on only one message. At the end of processing the message, the client can either decide to stop processing messages or request a new message.
With the max lock time set at 5 minutes on the subscription, I need to be able to automatically renew my lock for up to 15 minutes.
The first attempted approach was to use CreateReceiver and fetch the message, read it and Complete message when done. The issue with this is I have not been able to figure out how to automatically renew the lock for 15 minutes. I see the RenewLockAsync function but would like for this to be automatic and not have to run a background timer to keep track of the expiring lock.
The second attempted approach was to try using ServiceBusClient.CreateProcessor() with options to set the AutoLockRenewal timespan. The issue faced here is with the processor itself running based on events in the background. Since I need to populated a UI, I need to be able to stop the processor after the message has been read, return the callback and once the human interaction is done, complete the message. I have been unable to find a way to do this.
What would be a good approach to achieve this? The subscription acts as a workqueue that multiple people pull items from and individually work them. Any help in a proposing an approach to this is appreciated.
I didn't find any solutions to avoid reading data from the server when using get(). However, I might found a solution but it's not clear to me if it will work. I found that when using the real-time feature, the client will continuously update as the data changes. So per my understanding, if nothing is changed on the server, no reads charged, right?
However, I read that the listener should be removed, and I understood why, what I cannot understand is, if I close the app (listener is removed) and I open the app the second day, am I charged again for the data that was cached a day before?
I'm really confused because I also read that:
Also, if the listener is disconnected for more than 30 minutes (for example, if the user goes offline), you will be charged for reads as if you had issued a brand-new query.
Removing the listener and going online, are not the same exact thing?
I found that when using the real-time feature, the client will continuously update as the data changes. So per my understanding, if nothing is changed on the server, no reads charged, right?
Every query that reaches the server will incur reads for documents returned by the query. Whenever a document is returned from the server, it costs a read. If you have a listener on a set of query results where only one document changes while the listener is active, it costs one read, because only one document must come from the server, and the rest are already in memory. They stay in memory until the listener is removed.
if I close the app (listener is removed) and I open the app the second day, am I charged again for the data that was cached a day before?
Yes. Whenever the results come from the server, you will be billed for those reads. The cache is not used to satisfy query results when using the server as a source.
Removing the listener and going online, are not the same exact thing?
They are not the same thing. Removing a listener says that you're completely done with the results of the query. Going online temporarily and coming back online just resumes the existing query.
If the listener is disconnected for more than 30 minutes (for example,
if the user goes offline), you will be charged for reads as if you had
issued a brand-new query.
Does this still apply if persistence is enabled?
Situation 1: App is offline for over 30 minutes. Persistence is enabled and reads data from cache. Does reading documents from cache count as read operations?
Situation 2: App is online but no added/modified/deleted operations occur. Persistence is enabled and all data exists in cache. Does opening my app after 30 minutes cause read operations if no new data has been added/modified/deleted?
Firestore documentation
In both cases, if some read operation is satisfied only by the local cache, it is not billed.
The issue with the documentation that you quoted about listeners is specifically regarding the total results of a query that could return multiple documents over time. Note that a query listener can generate updates for new or changed documents indefinitely over time. But if your query listener is disconnected for more than 30 minutes, you are billed for the entire query again, and do not pick up where the listener may have left off previously with partial or in-progress results.
I have a server that keeps 2 booleans. These booleans change every 15 seconds.
Whenever I wake my Apple Watch, I want the complication to show the current state. How can I do it withough exhausting the budget early on?
The best way would be to fetch the newest state into the complication whenever I wake my watch. The only possible way I see would be to poll the server (either directly or via my phone) every 15 seconds. The problem is that I'd soon use up all the allotted time.
It would be great if I could make the complication only update when the watch was woken up. Can that be done?
Is there a way to not fetch data unless you need it?
No.
By "waking the watch," you're speaking of activating the watch either by interacting with it, or by raising your wrist. Regardless of the manner, the watch can either wake to the watch face, or to the last activity (which is controlled by the Wake Screen setting).
If it wakes to the watch face, this is independent of your app, watch extension, or complication controller. There is no notification you can use to handle that scenario.
If it wakes to an activity, it may not be your activity. If it were your activity, all you could do would be to stop updating when your watch app was active.
Either way, there is no contingency to only update the complication when the watch is awake.
If you think about what you're asking, it runs contrary to Apple's guidelines, as users expect to glance at the watch face and already see current complication data. The system expects you to provide updates when the watch is not awake, so the information will be immediately visible when the watch does wake.
Updating Complication Data
Of the available update approaches, these won't handle your requirements:
PKPushTypeComplication push notifications
This would be ideal, if you were not updating frequently and constantly throughout the day.
Apple applies a daily limit to the number of pushes of this type that you send from your server. If you exceed the limit, subsequent pushes are not delivered.
Scheduled automatic updates
The issue here is that the minimum scheduled update interval is 10 minutes, so you wouldn't have current complication info for the remaining 9-3/4 minutes.
Scheduled updates are useful for apps whose data changes at predictable times. When a scheduled update occurs, ClockKit calls the requestedUpdateDidBegin or requestedUpdateBudgetExhausted method of your data source first.
These approaches might handle your requirements, but you'd have to try them and determine which one meets your needs.
Watch extension + background NSURLSession
This may place more of a drain on battery, but it would work even if not in range of the phone.
Manual update via WCSession transferCurrentComplicationUserInfo
If you're in range of your phone, the more likely approach would be for your phone to (always) poll your server, then provide constant updates.
When your iOS app receives updated data intended for your complication, it can use the Watch Connectivity framework to update your complication right away. The transferCurrentComplicationUserInfo: method of WCSession sends a high priority message to your WatchKit extension, waking it up as needed to deliver the data. Upon receiving the data, extend or reload your timeline as needed to force ClockKit to request the new data from your data source.
Note that complication transfers are budgeted in iOS 10. This approach would not work if you were performing large numbers of updates per day.
Having said all that, an alternate approach would be to monitor these booleans on the server side, and only send out a notification if there was a problem or change. You didn't explain exactly what these booleans indicate, but there are other approaches for monitoring, which would avoid you having to constantly poll a server (from your phone or watch).
If that's not an option, you really should consider either viewing the server data on your phone, or switching to a far less frequent update interval for your complication. Apple discourages such frequent updates, and even their own (stock or weather) complications are not updating several times a minute.
Update:
What if I only wanted to update the complication when user requested it himself(=clicked the complication)? He is not really interested in the state all the time.
Can you (only) update a complication when the watch awakes? No.
Can you update an extension when the extension awakes? Yes.
The user needs to consider what a complication is meant to be. It's designed to be regularly updated to show current information. There's no mechanism to not update the complication because the user is mostly not interested in knowing the state.
Could you tap on a complication to open its app? Yes. But the complication itself would be showing stale data, and the user would have to do more than raise their wrist to see current state.
If you consider what the user is asking, they're not describing a complication (which shows current state), but a way to see the current state when they request it.
That mechanism is different from a complication. They're really describing a Glance (or an app) which the user swipes up (or opens) to see.
If they want live updates, it can be done via WCSession updateApplicationContext.
Use the updateApplicationContext:error: method to communicate recent state information to the counterpart. When the counterpart wakes, it can use this information to update its own state. Sending a new dictionary with this method overwrites the previous dictionary.
The way that works is your phone sends background updates to the watch. The watch stores the most recent update, which will be available to it when your app or Glance wakes up. The user views the app or Glance, and it displays the most recent value which the watch stored. While open, it continues to update itself as new updates arrive. While closed (e.g., inactive, asleep), the watch stores the most recent update on behalf of your app or glance.
If the user doesn't need the app or glance to update itself every 15 seconds, then you wouldn't need to poll, and you could simply use a NSURLSession to fetch the current state when the extension awakes.
If you explain to the user that there's no way to update a complication when the user raises their wrist, and that they don't care about knowing the state all the time (which a complication is meant to do), then show them what a Glance can do, you'll find it far easier to accomplish what the user seems to want, ideally without unnecessarily draining the battery.