3

Using a JWT authentication, the user doesn't have to log in each time he uses my app.

I would like to save in DB the last time the user used/opened the app.

Technically, I have a set of REST API that could audit each request and save in database the timestamp corresponding to the current user access but ... for each request?!

What is a good practice to store this information without involving database each time the user makes a request?

Mik378
  • 3,838
  • 7
  • 33
  • 60
  • Can you explain why you need this? What you are going to use the timestamp for will determine the best way to do this. For a start you probably already are recording each users last access time in your HTTP server logs. Is this enough? Is this to audit user activity later or for some real time analysis? – Cormac Mulhall Aug 05 '15 at 08:16
  • It is a "feature" lf my app: showing the last member connection to others users. – Mik378 Aug 05 '15 at 08:43
  • In that case I would store the last access time in the database with a threshold of a minute or so. Since you are probably getting the User record on each request you just check the time on the next request. If it is not outside the threshold don't bother updating the database. Fine granularity is not needed in that use case (A user won't care if last request was 1:34 or 1:35). Why make your life hard when the user won't notice. – Cormac Mulhall Aug 05 '15 at 08:52
  • What is the difference with others answers below ? – Mik378 Aug 05 '15 at 08:55
  • None, I think Ivan is right on the money. Write to the DB and if you are worried about performance issues (not something to worry about at the start) introduce a threshold. That would be the best practice. A bigger issue is retrieving the value for other users. This gets complicated given the network of users (this if Facebooks problem, getting information based on their friend network). But different question :-) – Cormac Mulhall Aug 05 '15 at 09:03
  • "This gets complicated given the network of users (this if Facebooks problem, getting information based on their friend network)" => What is the link with the OP ?! – Mik378 Aug 05 '15 at 09:36
  • "showing the last member connection to others users" The "other users" bit. Showing a user their last login is easy. Showing it to other users gets more complications due to how you need to construct the relationships. But that is another issue. The point is that you cannot make design decisions about databases or response times, or request numbers, in isolation. The specific problem being solved guides the design. There is no one fit solution, so it is difficult to tell you what the best thing is without knowing the details of the application you are building. – Cormac Mulhall Aug 05 '15 at 11:39
  • Simple question: where did you see I mention Facebook? I didn't mention it at all. To make "other user" sees last connection of others, a simple basic query would do the trick. – Mik378 Aug 05 '15 at 11:45
  • I was using Facebook as an example, as Facebook deal with the problem of sharing one users data with a group of other users all the time due to the friend system. With them it is so bad they have had to invent NoSQL solutions to deal with this as regular joins in a relational database can't handle it. So I could say to you just stick it in the database, but that might not be the correct design decision based on your use case. Just trying to explain that the "good practice" you asked for is highly dependent on what you want to use that timestamp for. – Cormac Mulhall Aug 05 '15 at 11:48
  • Or to put it another way "showing the last member connection to others users" could be either insanely simple or insanely hard depending on what that actually means (all users, some users, users based on permissions, users based on relationship to the last connected user, last connected user not including the current user etc etc), and that will determine the solution to how you store this. – Cormac Mulhall Aug 05 '15 at 11:52
  • I'm using Neo4j for that. Best fit for this case ;) – Mik378 Aug 05 '15 at 11:53

2 Answers2

3

You can set a threshold.
For instance, you can store that someone visited at 14:00PM, and then audit if the difference between then and the current time is more than 30 minutes, so you have 2 updates per hour at most.

If you have a stateful service, another approach would be to store the time of each request in memory (default session driver, Redis, Memcache, whatever).
That way, you can detect when they leave your app and store their last time just once, at the end.

Update:

Another way would be to serve a request timestamp with your API tokens if you use, say, JWT. If not, you can still send cookies or store them in user's localStorage. Then they will send you their last access time with each request and you can utilize the first suggestion without having to touch the database.

Ivan Batić
  • 171
  • 3
  • "and then audit if the difference between then and the current time is more than 30 minutes" => it would involve a database query to read the last saved date to compare to ... – Mik378 Aug 02 '15 at 00:35
  • Is your api stateless? – Ivan Batić Aug 02 '15 at 00:36
  • My api is stateless yes. – Mik378 Aug 02 '15 at 00:37
  • Consider the second approach then. You don't need to have a session in memory, you can just store a map of user ids and their last visit timestamps. – Ivan Batić Aug 02 '15 at 00:38
  • In Memcache or Redis so (to have fast access)? – Mik378 Aug 02 '15 at 00:38
  • 1
    They both store data in memory and will give you the fastest access possible. However, Memcache is only a key->value map with 1MB limit per record. Redis does not have these limitations, has multiple data structures that you can store and query and is more versatile and powerful all-round. – Ivan Batić Aug 02 '15 at 00:44
  • The drawback would be: each request, there's a need of a **network call** to Redis to save the current time associated to the current user. – Mik378 Aug 02 '15 at 00:49
  • Why would you need to have Redis on another server over the network instead of keeping the data on the same machine? If your application is clustered, Redis can be clustered too. You need to have that data *somewhere*. Temporary in-memory storage is probably the best you can get, given that you are developing for numerous stateless requests. – Ivan Batić Aug 02 '15 at 00:54
  • I'm using a Heroku cluster of 3 servers. Thus, I imagine the Redis instance should either be duplicated/replicated on those 3 servers OR external (network call) in order to be shared. – Mik378 Aug 02 '15 at 01:02
  • Sorry, but I don't figure out your update. Whether the timestamp is set in the JWT token or generated by the server doesn't change the issue at all. This timestamp **needs to be stored somewhere**. Besides, JWT token is generated at authentication time. As being very long (about 60 days), the last access time stored within it (encrypted) would be directly stale... – Mik378 Aug 02 '15 at 01:22
  • 1
    You get a timestamp in the request, and send the current time as a new one, which you will get on the next request. No database interaction. When the timestamp you get has more than 30 minutes difference, you insert two visit records, one for the last timestamp and one for the current time (if you need logs). You can't have persistent data without touching any data storage ever. At some point, you have to store it and/or read it. – Ivan Batić Aug 02 '15 at 01:23
  • I get it, thanks. I'm curious about some applications that are capable to be accurate to the seconds (not 30 minutes) and involving numerous stateless calls. Like for instance: Tinder on iOS. – Mik378 Aug 02 '15 at 01:38
2

Consider using a queue, where each method access resulting an entry, and use a

  1. timer job
  2. queue length reaching certain threshold

to flush the queue into the database.

Low Flying Pelican
  • 1,602
  • 9
  • 13