future timestamp as key in firebase - firebase

I need to create the notifications for future events. for example similar to reminders. I was thinking that I can use the the server timestamp + future time as the key and than pop only the notifications with the timestamp older than current server time. However I need the server time.
I can't use client side time stamp as it might be out of sync.
Is there a way to get the current time stamp from firebase?
I know that there is a placeholder for timestamp which is replaced on the server side. Can I use this as a key?
I can imagine that probably not so is there a way to listen to this event when the placeholder is replaced with real timestamp?
or generally is there any other sensible method around this problem?

You're probably solving the X/Y problem here, and you might be better off explaining your use case and getting a better overall solution; fetching a server timestamp to use on the client seems extremely likely to be a conceptual problem.
For example, if my goal is just to display the time in messages, I can simple call set, monitor the path, and display what it returns:
var ref = new Firebase(URL);
ref.on('child_added', function(snap) {
// client just listens on path, records have serve timestamp when they arrive
console.log('the last event was added at', snap.val().time);
});
ref.push({ name: 'Kato', time: Firebase.ServerValue.TIMESTAMP });
If you are queuing future events, just store them in a different path and move them to the "present" path when ready. Rather than trying to fetch "now" and insert it later.
If you are set on fetching the timestamp, the simplest way would be to set up a dummy path and set the value against it:
new Firebase(URL).transaction(function(currValue) {
return Firebase.ServerValue.TIMESTAMP;
}, function(err, success, snap) {
console.log('the current server timestamp', snap.val());
});

Related

Firebase Timestamp in cloud function not displaying time

I am using this to get the timestamp
admin.database.ServerValue.TIMESTAMP
But in log getting this while doing console the variable
{ '.sv': 'timestamp' }
anyone help me out in this.
Actually i want to get the timestamp then compare it with db timestamp
The admin.database.ServerValue.TIMESTAMP does not contain the actual server-side timestamp, but is merely a marker (the { '.sv': 'timestamp' } that you see). The database server recognizes this marker on write operations, and then writes the server-side timestamp in its place.
This means that you can't get the server-side timestamp without writing to the database. A simple way to see how to get this is:
let ref = firebase.database().ref("test");
ref.on("value", function(snapshot) {
console.log(snapshot.val());
})
ref.set(admin.database.ServerValue.TIMESTAMP)
When you run this code, your log will show three values:
null
This is the current value in the database when you attach the listener with on("value". Here I'm assuming the test node didn't exist yet, so the value would be null.
1573659849577
This is an estimate that the client makes when you the ref.set(...) statement executes. So the client estimates what it thinks the server timestamp may be, and fires a value event. You can use this to update the UI immediately, so that the user doesn't have to wait.
1573659859162
This is the value that the server actually wrote to the database, and then sent back to the client. So this is the actual server-side timestamp that you're looking for.
In theory the client-side estimate (2) and server-side value (3) may be the same, in which case you wouldn't get the third event. But I've never seen that in practice, as they're always off by at least a couple of milliseconds.

Querying Firestore for items with timestamp before today

In the new Cloud Firestore you can set a server timestamp on my message object using the following line during creation of the object:
showtime: firebase.firestore.FieldValue.serverTimestamp()
This is useful for messages from any user as it does not rely in each users local clock.
The challenge I am having is in querying and setting the showtime field afterwards. What I want to do is look up any message that has a showtime that is early than 'now'. In my application many messages are pushed into the future. I only want the ones with a showtime before today to be returned and displayed.
Here is my query:
var query = firebase.firestore()
.collection('messages')
.where('owner','==',userID)
.where('showtime','<',timenow)
.orderBy('showtime', 'desc')
.limit(25);
The challenge is that I do not know how to get the current time (on the server) to use in the query. There is a now() call on the type Timestamp in Firebase, but I am unsure how to call it AND I am not sure based on some other questions here whether the Firebase timestamp matches the Cloud Firestore timestamp!
So the question is: How do I set a variable called timenow to be the current time on the server and use it in a query to pull 25 messages before now? (in Javascript on the client and also extra credit for in a function on the server)
A quick follow on question is then how I update the showtime timestamp to be one week later than now?
Hope we have some Firebase / Cloud Firestore mavens on Stackoverflow!
** Choosing an answer below with the caveat of a feature request: A call in Firebase to the server to request a current timestamp so that the client code can work off one standard clock. **
Expanding on the excellent answer by Alex Dunlop, I was in a similar situation and it made sense once I realized that Firestore operates according to two design principles (or at least, that is my analysis)
The emphasis is on simple queries that stream live updates to you. To enable this, queries are by design limited in complexity. This takes load of the Firestore database server and allows it to stream (effectively re-run) queries fast.
Because of the limited query language and few 'server side' operators (FieldValue is one of the few), Google can optimize the queries mercilessly.
There are many more complex operations that developers require. Instead of implementing them in Firestore, developers are asked to spin up a cloud function which makes them responsible (in code and cost) for that additional complexity.
If you come from a conventional DB design like Postgres, the lack of query expressiveness and server-side operations is stunning. If you think of the Firestore use case and the principles, it all makes sense.
Try Firestore security rules with data validation:
match /messages/{msgId} {
// optionally: request.time + someBuffer
allow read: if request.time > resource.data.showtime;
}
I don't think you want to trust the client (since you mentioned you want to block client access to future showtimes). Besides changing their clock, they could just edit the javascript.
A buffer might be necessary to not invalidate the query if there is some discrepancy between client provided Date.now() and Firestore rules request.time, sepecifically if the request.time happens to be earlier than client date, then the query would have documents falling outside valid range and fail.
Checking the read timestamp on an empty snapshot seems to work:
const serverTime = (await db.collection('nonexistent').get()).readTime;
Agreed with one of the other comments that this extra call could be expensive, but this should work in cases where an accurate "not before" server time is important for correctness.
You are right serverTimestamp() is exactly for getting a timestamp on the server and not relying on the users local clock. One thing to note is that generally sending a message and getting the timestamp from a users local clock is going to be okay as a message timestamp is not extremely time sensitive. Sure you would like to know when the message is sent but if it is within 1-2 seconds not a problem in most cases.
If you are doing a query on the client side your query should not be based on the server time it should be based on the client side. As it is a client query not a server query.
Here is a client query that you are after.
const currentTime = new Date();
const query = firebase.firestore()
.collection('messages')
.where('owner','==',userID)
.where('showtime','<',currentTime)
.orderBy('showtime', 'desc')
.limit(25);
This query will get 25 messages with a 'showtime' after the current time on the client.
Now if the messages need to be extremely time sensitive and you do absolutely need the messages to based off the server timestamp I recommend that instead of doing a query on the client like above you set up a cloud function api.
Have a look at the firebase docs for calling cloud functions directly if you haven't before.
Here is what you would want your cloud function to look like:
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp()
exports.getUserMessages = functions.https.onCall((data, context) => {
const uid = context.auth.uid;
const firestore = admin.firestore();
firestore.collection('messages')
.where('owner', '==', uid)
.where('showtime', '<', new Date())
.orderBy('showtime', 'desc')
.limit(25)
.get()
.then(snapshot => {
return snapshot;
});
});
This will get the messages based of the server timestamp. One thing to note is UNLESS you need this to be extremely time sensitive this is not a good idea. As this call has to do an extra unnecessary call every time you call it. Because you are doing a call to the cloud function and then you are doing a query to the firestore database.
I would recommend that instead of doing it based on the server time you do it based on client timestamp. 99 times out of 100 the time difference between client and server is not worth the extra double calls you are doing, especially when you think about scaling everything up when you get more users.
Hope that answered your question :)

meteor: how to stop asynchronous call?

Is it possible to stop (kill) asynchronous Call?
In my app I have at client side sth like:
Meteor.call('doCalculation', function(err, result) {
//do sth with result
});
'doCalculation' may take long time (this is ok) I dont want user to start new call when he/she has already one running call, I want to allow user to stop current call and submit new one. How correctly do this?
The only idea I have is to communicate between client and server using mongo. In some place in 'doCalculation' function I can observe some mongo document/collection and based on this do sth in the function (e.g. call exception). Do you have any better ideas?
You can use a semaphore for this purpose. When the semaphore is 1, requests are allowed to be sent. When the semaphore is 0, requests are not allowed to be sent. The semaphore should be 1 by default and just before you send the request, you need to set it to 0. When a response is successful, you set the semaphore back to 1.
As about the timeout: You could use a time out using setTimeout after sending the request, like this:
if (semaphore) {
var isTimedOut = false;
var isSuccess = false;
semaphore = 0; //No need to use var keyword, as this should be declared outside of this scope
Meteor.call('doCalculation', function(err, result) {
isSuccess = true;
//do sth with result
});
setTimeout(function() {
if (!isSuccess) {
isTimeout = true;
//do something else, to handle the time out state
}
}, 10000);
}
This is tricky, because you cannot generally set timeouts from the client's point of view. You don't need to, for a bunch of architectural reasons. The most important thing is that if you lose network connectivity or the server crashes (two cases timeouts are designed to manage), the client is aware immediately because it is disconnected. You can use Meteor.status().connected if this happens often.
It sounds like you're running a long calculation on the server. My suggestion is to return a calculationId immediately, and then update a collection with progress, e.g., CalculationProgresses.update(calculationId, {$set: {progress: currentProgress}}) as you calculate. Your UI can then update the progress reactively, in the most convenient way possible.
Note, that when you do run long calculations on the server, you need to occasionally "yield," giving the chance for other work to happen. Node, on which Meteor is based, is tricky for long calculations if you don't master this notion of yielding. In Meteor, you can yield easily by updating a collection (e.g., your progress collection). This will solve lots of problems you're probably experiencing as you write your application.
i think you need a server-side solution for this. if you go with a client-side solution, you don't handle 2 cases:
the user reloads their browser
the user uses 2 browsers
i would create these methods:
isCalculationActive() -- this checks if the user already has a calculation active. on the server, you can either keep that fact in memory or write it to the db. on the client, if this returns false, then you can proceed to call doCalculation(). if true, you can give the user a popup or alert or something to ask if they want to cancel and proceed.
doCalculation() -- this cancels any outstanding calculation by that user and starts a new one.
with these implemented, the user can reload their browser w/o affecting either the running calculation or correct behavior. and if they try a 2nd browser, everything should still work as expected.
if you want to give the user the option to simply stop the job and not start a new one, then you can simply create:
cancelCalculation() -- this cancels any outstanding calculation by that user.

Reactive subscription depending on current time in Meteor

In an application that allows realtime chat between clients, I aim to integrate functionality that allows to define messages that are delivered at future points in time.
In the following example, I am able to insert messages, which are inserted directly into the template. However, I would like to display only messages that have a time smaller or equal to the current time, but automatically display messages that have future time points as soon as the time is reached. For example, if I insert a message from the console, which should be displayed 30 seconds in the future by calling Meteor.call("createMessage", 30000, "hello in 30 seconds"), the message should be automatically displayed after 30 seconds.
I started restricting the query in the publish function to time: {'$lt': new Date()}. However, I have trouble in making this reactive. I unsuccessfully tried several combinations of Tracker.autorun and cursor.observe.
Can anybody give me a hint how I accomplish the desired reactivity within the following running example?
1) html file
<body>
{{> chat}}
</body>
<template name="chat">
{{#each chatMessages}}
{{time}} - {{message}} <br>
{{/each}}
</template>
2) js file
//server and client
Messages = new Mongo.Collection("messages"); //{time: Sun Nov 02 2014 22:17:32 GMT+0100 (CET), message: "hello"}
//server
if(Meteor.isServer){
Meteor.methods({
'createMessage': function(timeOffset, message){
Messages.insert({
time: new Date(new Date().getTime() + timeOffset),
message: message
});
}
});
Meteor.publish("messages", function(){
return Messages.find({
//time: {'$lt': new Date()}
});
});
}
//client
if (Meteor.isClient) {
Template.chat.helpers({
chatMessages: function(){
return Messages.find({});
}
});
Tracker.autorun(function (){
mySub = Meteor.subscribe('messages');
});
}
If Date() was a reactive datasource, it would work but it's not.
You can create a Timer at server side that will handle it. The best design I see id: pick the next message future date and set a Timer with the time difference and also get the next message time. Of course it's dependes on how your application works.
Read more about Timers in Meteor: https://docs.meteor.com/#/full/timers
Reactivity means that the view reflects the data sources used to make that view, updates when those sources change, and only then (as a rule of thumb).
Therefore, if we want to accomplish what is described using reactivity, we must introduce a reactive change when the message goes live (the outlined model does not have such a change).
Two ways to accomplish this that I can think of:
Add an 'isLive' field to the message, and have the server change it at the right time, using timed callbacks and Meteor.startup (to avoid losing messages in case of a reboot). Somewhat complex, but clean and performant (when properly implemented).
Add a currentDate Session variable and use Meteor.setInterval etc. on the client, to keep it as current as you need (Because Session variables are reactive).
In the second alternative, the user could just change their system clock or use the javascript console to access future messages. Also, reactive events with a set interval seem rather contrived, unless that interval is significant to the problem domain itself (like a calendar app changing the 'today' session variable used to draw the red circle, every midnight).
A simpler (better?) non-reactive solution might be to simply render the future messages as hidden elements, and use javascript timers to show them at the right time. But it all depends on what you are dealing with of course.

Meteor Deps.Autorun know when data has been fully fetched

Is there a way to know when data has been initially fully fetched from the server after running Deps.autorun for the first time?
For example:
Deps.autorun(function () {
var data = ItemsCollection.find().fetch();
console.log(data);
});
Initially my console log will show Object { items=[0] } as the data has not yet been fetched from the server. I can handle this first run.
However, the issue is that the function will be rerun whenever data is received which may not be when the full collection has been loaded. For example, I sometimes received Object { items=[12] } quickly followed by Object { items=[13] } (which isn't due to another client changing data).
So - is there a way to know when a full load has taken place for a certain dependent function and all collections within it?
You need to store the subscription handle somewhere and then use the ready method to determine whether the initial data load has been completed.
So if you subscribe to the collection using:
itemSub = Meteor.subscribe('itemcollections', blah blah...)
You can then surround your find and console.log statements with:
if (itemSub.ready()) { ... }
and they will only be executed once the initial dataset has been received.
Note that there are possible ocassions when the collection handle will return ready marginally before some of the items are received if the collection is large and you are dealing with significant latency, but the problem should be very minor. For more on why and how the ready () method actually works, see this.
Meteor.subscribe returns a handle with a reactive ready method, which is set to true when "an initial, complete snapshot of the record set has been sent" (see http://docs.meteor.com/#publish_ready)
Using this information you can design something simple such as :
var waitList=[Meteor.subscribe("firstSub"),Meteor.subscribe("secondSub"),...];
Deps.autorun(function(){
// http://underscorejs.org/#every
var waitListReady=_.every(waitList,function(handle){
return handle.ready();
});
if(waitListReady){
console.log("Every documents sent in publications is now available.");
}
});
Unless you're prototyping a toy project, this is not a solid design and you probably want to use iron-router (http://atmospherejs.com/package/iron-router) which provides great design patterns to address this kind of problems.
In particular, take a moment and have a look at these 3 videos from the main iron-router contributor :
https://www.eventedmind.com/feed/waiting-on-subscriptions
https://www.eventedmind.com/feed/the-reactive-waitlist-data-structure
https://www.eventedmind.com/feed/using-wait-waiton-and-ready-in-routes

Resources