[SAK-39908] Method to check if a user is active, ie 'online' Created: 14-May-2010  Updated: 25-Apr-2018  Resolved: 19-May-2010

Status: CLOSED
Project: Sakai
Component/s: Kernel
Affects Version/s: None
Fix Version/s: 2.8.x

Type: (Deprecated) Contributed Patch Priority: Major
Reporter: Steve Swinsburg Assignee: Steve Swinsburg
Resolution: Fixed Votes: 0
Labels: API
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: Text File KNL-497-2.patch     Text File KNL-497-3.patch     Text File KNL-497.patch    
Issue Links:
Depend
is depended on by SAK-27335 Add online status widget CLOSED
Incorporate
incorporates SAK-39896 Method to get most recent event time ... CLOSED
Relate
relates to SAK-39602 ability to retrieve the time of an Ev... CLOSED
relates to SAK-39463 Add USER_ID field to SAKAI_EVENT CLOSED
is related to SAK-39468 Update SakaiCluster to check on SESSI... RESOLVED
Previous Issue Keys: KNL-497

 Description   

This is a patch to check if a user (or set of users) have an active Sakai session, ie if they are 'online'.



 Comments   
Comment by Steve Swinsburg [ 15-May-2010 ]

The attached patch adds this functionality. It adds the following three methods to check an individual user's most recent active session/online status, as well as for multiple users.

/**

  • Check if a userId has an active Sakai session.
  • @param userId userId to check
  • @return true if active, false if not
    */
    public boolean isUserActive(String userId);

/**

  • Get the most recent Sakai session that is active, for a given user
  • @param userId userId to check
  • @return most recent UsageSession or null if none
    */
    public UsageSession getActiveUserSession(String userId);

/**

  • Get the most recent active UsageSessions for the given users.
  • @param userIds userIds to check
  • @return Map of userId and UsageSession. The returned map will not contain a record for the userId if there is no active session.
    */
    public Map<String, UsageSession> getActiveUserSessions(List<String> userIds);
Comment by David Horwitz [ 16-May-2010 ]

Steve a comment now that I think on this and your described usagae and I think it may lead to perfomance problems. I think you need something like:

public List<String> getOnlineUsers(List<String> userIds);

Where you pass a list of userids (the users contacts) and the service passes back a list of the users who are online. Otherwise you will be doing 1 query for each user in the person's connection list.

Comment by Steve Swinsburg [ 16-May-2010 ]

It's already there:
public Map<String, UsageSession> getActiveUserSessions(List<String> userIds);

And it's a single DB query.

Comment by David Horwitz [ 16-May-2010 ]

ahh good missed that

Comment by David Horwitz [ 16-May-2010 ]

Applied patch - waiting for hudson builds before updating snapshots

Comment by Steve Swinsburg [ 16-May-2010 ]

I'm attaching a second patch which adds one more method, to improve performance when you want to check a list of users to see which ones have active sessions, but don't need the full UsageSession.

/**

  • Get the list of users with active Sakai sessions, given the supplied list of userIds.
  • @param userIds userIds to check
  • @return List of userIds that have active Sakai sessions
    */
    public List<String> getActiveUsers(List<String> userIds);
Comment by Steve Swinsburg [ 16-May-2010 ]

Attached updated patch that includes the mocks.

Comment by David Horwitz [ 17-May-2010 ]

comitted patch

Comment by Matthew Buckett [ 17-May-2010 ]

Are you wanting the whole usage session back so you can do active/idle based on the last active properties of the usage session?
I'm just curious why you're after the whole UsageSession.

Comment by Steve Swinsburg [ 17-May-2010 ]

Various reasons, one was the active/idle but that is also available via KNL-498. The other was to see how long a user has been online for. Plus it will come in handy if we decide to do any login location checks in the future.

Comment by Steve Swinsburg [ 18-May-2010 ]

Will change this approach to use cached data instead.

Comment by Steve Swinsburg [ 18-May-2010 ]

Attached a third patch. This remove the two patches that were committed, and provides an alternate implementation that uses a cache instead so never hits the database. It works via the login and logout methods, adding/removing entires from the cache.

I have checked that when a session times out, they are also removed from the cache so this should be quite effective and accurate.

I have also removed the methods that returned UsageSessions. It is now unnecessary for the work this was meant to support. I am leaving the patches here in case we want to reimplement those methods.

Comment by Steve Swinsburg [ 18-May-2010 ]

Committed. There is one limitation to this approach. If a user opens multiple browser windows and logs in as the same user in both, they will only get one entry in the cache, so when they logout of one of these windows, they will be removed from the cache and therefore appear inactive, even though they are still logged into the other one.

I believe this is an acceptable tradeoff since a user would rarely login with the same username in both windows, if they do this it would most likely be different usernames. We could get around this by storing the sessionId in the cache instead of the userId, however this will then require us to retrieve the UsageSession for each entry, and getting the userId from there, so we might as well just use the database to start with.

Also, if a user purposely logs out of one window, we could assume that they are in fact logging out, so should be removed from the cache.

Comment by Stephen Marquard [ 19-May-2010 ]

Interesting problem... one scenario here would be webdav. However, the period of inaccuracy would just be until another event came along for the same user from the original session so it doesn't sound too bad.

Comment by Steve Swinsburg [ 19-May-2010 ]

Actually it would be until they login again. They are added to the cache in the login() and removed in the logout(). Might need to change the approach if this isn't satisfactory?

Comment by Stephen Marquard [ 19-May-2010 ]

You could just add to the cache when you see any event if they're not already there - you need to update it anyway when you see a new event surely? Also if an app server comes up in a cluster, you want it to start tracking users who are already logged in on other app servers.

Comment by Steve Swinsburg [ 19-May-2010 ]

The update for each event was happening for tracking the last event time (doing that for KNL-498), but it's a separate cache to just being active (this JIRA). But another problem is that if an admin resets the caches they will then appear offline, so will combine caches into one, storing the time of the event to the user only. Then if a user is in the cache they are active, but we can also get the time of that event, ala KNL-498. WDYT?

Comment by Matthew Buckett [ 19-May-2010 ]

Are we coping with an admin restarting a node (eg: due to a crash)?
Does a newly started node come up with an empty cache of people online?

Comment by Steve Swinsburg [ 19-May-2010 ]

With the mods I'm making, each event will update the cache entry for that user. So if a new node comes up, then the next event for that user will make that person appear online.

Comment by Matthew Buckett [ 19-May-2010 ]

What I'm getting as is that if you have 2 nodes in your cluster and restart one of them, then everyone who reconnects to the fresh node won't see the people who are on the other node until they generate an event. When node comes back up it starts with am empty table of active users.

So depending on which node you connect to depends on what users you will see online.

On a second point do we have guaranteed delivery order for events? Eg is it possible for a user to do something (eg download a file) which generates an event and then logout, but when the events are processed at the second node in the cluster the logout happens before the other event.

Comment by Stephen Marquard [ 19-May-2010 ]

AFAIK events are processed in order. On the startup issue, I'd say some imprecision is acceptable as the status is to be used for informative purposes mainly (like presence). One should just document in the API perhaps the limits on accuracy so others don't later use it in a way that assumes it's more authoritative than it actually is.

Comment by Steve Swinsburg [ 19-May-2010 ]

Ok I've reverted and checked in a new interface and implementation which provides this info. I have reverted all previous changes made so as to combine this all into one spot and one cache. I have documented the interface as SM mentioned above. Tested well. r77575

Once thing I did notice though is Event.getUserId() is always null, it even says so in the method docs, so we need to get it from the UsageSessionService using Event.getSession(id). This always hits the database (UsageSessionServiceAdapter > ClusterStorage > getSession). IMO this should also be cached or a new column added to the SAKAI_EVENT table so the userId can be stored by the Event, and retrieved directly, rather than going through a UsageSession every time.

Comment by Stephen Marquard [ 19-May-2010 ]

+1 to adding USER_ID to SAKAI_EVENT as per KNL-364.

Comment by Steve Swinsburg [ 19-May-2010 ]

While we are on it, there is no way to get the time of an event either. KNL-500

Comment by Steve Swinsburg [ 19-May-2010 ]

1.2 snapshot artifacts updated.

Generated at Wed Sep 18 05:07:05 CDT 2019 using Jira 8.0.3#800011-sha1:073e8b433c2c0e389c609c14a045ffa7abaca10d.