Package for monitoring users' online status / presence?


#1

I am looking for a package for monitoring the online status / presence of users. I’ve seen a lot of these user presence packages on Atmosphere (e.g. tmeasday:presence or 3stack:presence), but I am still unsure which of these will fulfil my requirements best and - more important - how well they behave “in the wild”. So I would like to ask the community for their experiences. My use-case is a chat-like application with different chat rooms, but I need the online status of a user also in other places (like a users or a friends list).

Here’s my wish-list:

  • Store arbitrary data with the status. Not only should the set of status be customizable, but I would also like to store additional information like the chat room the user is currently in. It should be easy to change this status information, e.g. by setting the chat room number as soon as the user enters the chat room, and removing it if the user either leaves explicitly or a time-out occurs.

  • Status tracking should work across horizontally scaled servers. IIRC, there is some packages like tmeasday:presence that do not support this well. Others likes 3stack:presence state that they clear the online status of all users if a new server is starting, and I am not quite sure impact this behaviour has if I use this in practice with a lot of users. I can imagine that this will cause a lot of load on the database if all users re-connect at the same time.

Which packages are you using, and what experience did you make with them?

EDIT: One functionality I’ve forgot: I also need stable support for mobile devices.


#2

https://atmospherejs.com/mizzao/user-status
try that one


#3

Thanks, that looks interesting. But does it scale to multiple servers? I read in the docs that the user presence status is held in an in-memory collection. Seems as if this only works on one single server instance.

EDIT: Just read in the issues that it does not support multiple instances.


#4

Let me know if you find something usefull.
But there is not many possibilities for scalable solution - cause in Meteor, all communication need to happen over mongo if we are not going to prepare dedicated server group which will hold the state.
So if we are going to keep state in separate cluster/service, what technology?
Redis seems to be perfect candidate.

But than it is not pure Meteor solution.


#5

I plan to do something quite similar to what you describe eventually. From all that I’ve read, there isn’t anything out there suited for what I want.

So, I’m going to do it myself. I plan to implement sessions(not like the meteor Session class but more like one you would see in a stateless PHP/MySQL app. Mostly Likely I will settle ofn handle sessions per-node with redis, with the ability to selectively/infrequently sync to the cluster should a node go down.

And then I’ll have chat room/messenger functionality over clustered redis backed by MySQL for cold storage. This includes shared Online presence info. And quite possibly even a little analytics/tracking, not only of current room; is the user is typing a new message? Or, are they just browsing the site not using the chat at all? We’ll then I’d use that info to dialdown the realtime-yness of that chat. Same if they don’t have the tab focuses and haven’t used the page in 15 minutes.

I’ll OpenSauce a lot of the stuff I do, but don’t plan to start on that for at least a month and a half. The leading redis package hasn’t changed in quite a while & is quite patchy so Imma have to put a lot of work into that… Anyway, idk the exact specifics of what you want/how much you’re willing/able to do to implement this functionality, But there’s certainly a lot of room for new solutions in the aspect of meteor… I’d be glad to work with you, but I’ll be sure to update this post when I get some of that out in anycase.

@shock seems like you’ve looked into this. Something you are/thinking about seriously considering?


#6

I feel like all the state detecting questions were already answered in mizzao/user-status
The only thing missing is using 1 thing as single point of truth for user status which can every app instance share.

Does Rocket chat for example has this covered somehow?


#7

mizzao:user-status does not support horizontal scaling. The only one that seems to handle that usecase seems to be konecty:user-presence on top of konecty:multiple-instances-status


#8

Hi guys

Any new thoughts on what to use?

My requirements are multi-server, and to store arbitrary data with the status. But I can happily roll my own data store, most important is it works reliably for multi-server.

My front runners are Konecty:user-presence and 3stack:presence. However both have not been updated for 4mths and 3stack only has 74 installs. :confused:

Looking forward to your thoughts.


#9

looking over the source code on Github, the package is quite simplistic - what’re the primary obstacles for running this across instances?


#10

https://atmospherejs.com/danimal/userpresence

Works across multiple servers and handles servers going down.


#11

You should take a look at
https://github.com/mizzao/meteor-user-status/issues/21 and perhaps
propose a PR?


#12

Dan just last week I read through your code and was amazaed by how succint it was. I had a similar requirement with some extra sugar so could not directly use the package.

I feel bad that I did not actually suggest the OP your package. I should have. It is clearly a high-quality no-fuss package that delivers what it offers.

The konekty package however is a little more feature-rich and uses a slightly different approach.


#13

well, I’d rather write it from scracth with a different approach.

  • I don’t like depending on coffescript
  • I want to write it in ES6
  • I don’t think it’s necessary to ID server instances, nor ristrict myself to a singular server instance.

I have some possibly naive ideas I want to play around with, and see how it goes :sunny: I want the simplest approach possible.


#14

@goatic take a look at @Dan’s package! It is really good.


#15

I’ve put some effort into changing things a bit - I’ve essentially made 3 packages that compose into kind of the same thing.

  • presence-server
  • presence-user
  • presence-anonymous

I’ll just throw in the code, as it is quite short

presence-server:

//convert seconds to milliseconds for readability
const 	toMillis	= seconds => seconds * 1000,
		
		//setup rates & diffs
		UPDATE_RATE = toMillis(15),
		CLEAR_RATE	= toMillis(5),
		CLEAR_DIFF	= toMillis(30),

		//assign ID to local server instance
		serverId 	= Random.id(),

		//setup collection for server instances
		servers  	= new Mongo.Collection('presence-server')

//index fields on server collection
servers._ensureIndex({updatedAt:1})

//ping the server collection instance to keep it alive
Meteor.setInterval(() => {
	servers.upsert({_id:serverId}, {updatedAt: Date.now()})
}, UPDATE_RATE)

//remove dead server collection instances
Meteor.setInterval(() => {
	servers.remove({updatedAt:{$lt:Date.now() - CLEAR_DIFF}})
}, CLEAR_RATE)

//exports for extension
export {serverId, servers}

presence-user:

import {serverId, servers} from 'meteor/goatic:presence-server'

//Enumerate presence fields
const PRESENCE = {
	IS_ONLINE: 		'presence.isOnline',
	SERVER_ID: 		'presence.serverId',
	LAST_ACTIVITY: 	'presence.lastActivity'
}

//Observe when servers are disconnected and disconnect their users
servers.find().observeChanges({removed(id) {
	Meteor.users.update({
		[PRESENCE.IS_ONLINE]: true, 
		[PRESENCE.SERVER_ID]: id
	},{
		$set:{
			[PRESENCE.IS_ONLINE]: false
		}, 
		$unset:{
			[PRESENCE.SERVER_ID]: ''
	}})
}})

//Index presence fields
Meteor.users._ensureIndex({
	[PRESENCE.IS_ONLINE]: 	1,
	[PRESENCE.SERVER_ID]: 	1,
	[PRESENCE.LAST_ACTIVITY]: 1
})

//autopublish to have everyone subscribe
Meteor.publish(null, function() {

	//stop publication if the user isn't logged in or logs out
	if(!this.userId)
		return this.ready()

	//otherwise give the user presence
	Meteor.users.update({_id: this.userId},{
		$set:{
			[PRESENCE.IS_ONLINE]: true,
			[PRESENCE.SERVER_ID]: serverId
		}, 
		$unset:{
			[PRESENCE.LAST_ACTIVITY]: ''
	}})

	//remove presence when the user disconnects
	this.onStop(() => {
		Meteor.users.update({_id:this.userId},{
			$set:{
				[PRESENCE.IS_ONLINE]: 	false,
				[PRESENCE.LAST_ACTIVITY]: Date.now()
			}, 
			$unset:{
				[PRESENCE.SERVER_ID]: ''
		}})
	})

	//finally make the publication ready
	return this.ready()
})

presence-anonymous

import {serverId, servers} from 'meteor/goatic:presence-server'

//setup collection for anonymous users
const anonymous = new Mongo.Collection('presence-anonymous')

//Observe when servers are disconnected and remove their anonymous users
servers.find().observeChanges({removed(id){
	anonymous.remove({serverId:id})
}})

//index fields on anonymous collection
anonymous._ensureIndex({
	serverId: 1
})

//autopublish to have everyone subscribe
Meteor.publish(null, function() {

	//stop the publication if the user is logged in or logs in
	if(this.userId)
		return this.ready()

	//add the connection id to the collection
	anonymous.insert({_id:this.connection.id, serverId})

	//remove connection id on stop
	this.onStop(() => anonymous.remove(this.connection.id))

	//finally make the publication ready
	return this.ready()
})

Let me know what you think :sunny:


#16

I like the anonymous touch, but then you should facilitate a way to get that information from the client.


#17

Yeah, it’s all in the connection of the publish context, I just really wanted to add anonymous before going to bed :sunny: What I really wanted with this, was the ability to compose with presence-server for whatever situation that relies on server instances :slight_smile:

What do you mean by facilitating a way of getting of getting what information from the client? Beyond the userId and connection info, I don’t see what kind of information that should be?

Also, I want to add this is a very basic suggestion on a more extendable and maintainable approach, IMO. It’s not meant to be feature rich, complete or anything :wink:


#18

I just mean some syntactic sugar, perhaps some object that you export which exposes the connection id as well as some other information like perhaps the timestamp for the initial handshake etc


#19

Ah - I’ll leave the sugar for a less naïve implementation :slight_smile: the DB pollin could be refactored into using mongos “time to live”, but this kind of config doesn’t seem to work from within Meteor.
For example example._ensureIndex({expireAt:1},{expireAfterSeconds:0})


#20

Hmm, that’s interesting, I thought it did work :frowning: