I need to measure how quickly a user responds to something in my app. Their response is in the form of sending a Meteor Method Call to the server.
I have a lot of users doing this at the same time, so I’m not wanting to just do a Date.now()
in the method code on the server because that seems like it would be prone to delay if the server is processing a large queue of responses. And this needs to be precise as possible.
I don’t want to take the value from a timestamp from the client because 1) their clock may be off and 2) it’s not secure even if their clock was in sync with the server.
One idea I had was trying to figure out how to get the Date
HTTP header from the actual request using a package like gadicohen:headers but it doesn’t include any date or timestamp info. I’m assuming the header Date
field would have a time that’s less prone to server business though even this is probably prone to some delay if the server is busy. Not sure, but I assume so.
Does anyone know how to access the time a Meteor Method response originated and/or when it was received by the server from within the method’s server code (versus doing a timestamp when the server code for the method actually runs)? Is there some DDP or request data somewhere?
Or another way to solve this?
Do you have a lot of blocking code server-side?
How precise does the timestamp really need to be?
Ideally I would like it to be within one or two seconds of when the user actually responded. No blocking code, but with a few hundred or even a few thousand requests all coming in at the same time (forgot to mention that detail) the running of the method code that gets a timestamp in code would be prone to a processing delay.
Hence reaching for some other timestamp in the header that’s hopefully more accurate to when the response was actually sent.
If I understand it correctly you are not interested in the absolute time of an event but rather in a users reaction time and that could be calculated as the difference of two dates on the client.
I am definitely interested in the absolute time of an event. But you have to use the server. You can’t simply use a timestamp from the client because their clock may not be set correctly (not everyone sets their time to use automatic). If their clock is not accurate the milliseconds returned by Date.now()
is also not accurate. Additionally you’re now trusting a timestamp from the client which isn’t secure and prone to being manipulated.
I’m trying to figure out the time a response is actually sent and/or received by the server. I’m assuming I can’t truly know the “sent” part because that would be trusting the client. So the next best thing would be the time the server receives it - as accurately as possible. But not when the Meteor server gets around to processing the method call’s server code, as I assume this could be heavily delayed in the event of a massive amount of requests - for example:
- You send a response from your client
- It reaches the server and gets queued behind 500 other responses
- The server method code finally gets to your response and calls
Date.now()
Your Date.now()
is now your response time + the time to process the 500 other responses ahead of yours. Yes, I know there’s multi-tasking and I’m sure it’s not a queue of 500 linear requests, but this is just an example. I’m sure there would be some processing time if there were a lot of requests to be processed. More than one or two seconds is too much.
I’m assuming somewhere there’s a date stored of when the server actually received the request. If the request is sitting in the event loop queue, surely a date was given to it. The following list of HTTP headers lists a date
field that is defined as “The date and time that the message was originated (in “HTTP-date” format as defined by RFC 7231 Date/Time Formats).” I was hoping to access this value using the above headers package I mentioned but it’s not shown.
DDP is normally done over websockets, and although there are some protocol-specific headers for websockets, there doesn’t seem to be anything which allows you to retrieve a packet-received timestamp.
I suspect that short of enhancing the ddp-server, that may be the only way to do this. Generally speaking as long as you don’t queue individual client calls (i.e. you use thus.unblock()
or write async
methods), it’s probably going to be good enough in most cases.
If you remove network latency (also not trivial) from the result, you will get a more accurate measure.
What about using a work-around that would have the client fire an actual HTTP request that would then get “wired” into to the Meteor Method code on the server? I guess this would be no longer using a Meteor Method. Can a Meteor server do that? We use iron-router and have a few server-only routes for processing different things (for example responses from our static landing page’s Contact Us form). A server-only route could end up calling the function that the Meteor Method did. Then we’d theoretically have a full HTTP headers with a timestamp… worth testing.
I wonder what side-effects would happen by not using a Meteor Method - would have to do authentication somehow. As whatever happens under the hood with Meteor Methods, it works really well, handles lots of simultaneous requests, and does indeed run them very fast.
You can use the webapp package for that. You’ll probably want to put a uuid on the HTTP call so you can match it to the same identifier passed to the method
Is that sometime I could create a PR for and it would actually get accepted by MDG? Or would it be more of a feature-request and MDG may or may not get around to it.
For now I can just fork, edit, and use a local version of ddp-server (or whichever component needs the timestamp added to it) if I wanted to right?
You can use a local version by placing it in a folder called packages
in the root of your project directory.
There isn’t any possible way to actually use a client supplied value? Through some kind of encryption or something? I just assumed anything from the client could ultimately be manipulated. Including manipulating a value before encryption.