Node Express Connect - Session Management
Asked Answered
R

1

8

I've written a session store driver for ArangoDB for ConnectJS. It is working, although still very much in alpha, but I have a couple questions.

First sessions that have an expires attribute of "false" only remain for the duration of the user-agent. I've noticed that session.destroy() is not called when the browser window is closed. This results in an "abandoned" session left in the store. How can I effectively clear these out? Is there a way to search for and destroy abandoned sessions on a scheduled basis?

Second, I have implemented the minimum requirements for my session store as outlined on this page: http://www.senchalabs.org/connect/session.html (close to the bottom)

That would be get, set, and destroy. The other two recommended methods are length and clear. What exactly should these methods do? I assume length returns the length of time a session has been active? How is 'clear' different than destroy?

Rhotacism answered 7/3, 2014 at 22:38 Comment(1)
I'm not sure what length and clear should do, as they aren't implemented in the RedisStore connect references, so I wouldn't worry about them.Hilariahilario
G
6

Unless you rigged up some event on the client to notify the server the window is closing, the server would have no way of knowing the session is no longer used.

You want to mentally think about sessions as two parts. One part is the token (the cookie) that is passed between node and the browser. The second is the actual persistence of sessions in a store (either the basic MemoryStore or Redis, or your new session store for another database). All the connect session code is doing is matching these up with every request.

  • Check for a session cookie
  • If one exists, try to look it up in the store
  • Make the retrieved data from the store available to the request
  • At the end of the request, update the TTL information for the cookie
  • Write the session back to the store

Notice that unless you are using the MemoryStore, Node doesn't have the session data in memory other than while your request is operating on it. (Well, it would be in memory for a while but would be unreferenced and subject to garbage collection). When you think about various deployment scenarios this makes sense.

Thus, the job of server-side expiration of sessions falls to the Store itself. One of the reasons Redis is great for this is because it manages expiring things automatagically, which you can see connect-redis doing in its set operation:

  RedisStore.prototype.set = function(sid, sess, fn){
    sid = this.prefix + sid;
    try {
      var maxAge = sess.cookie.maxAge
        , ttl = this.ttl
        , sess = JSON.stringify(sess);

      ttl = ttl || ('number' == typeof maxAge
          ? maxAge / 1000 | 0
          : oneDay);

      debug('SETEX "%s" ttl:%s %s', sid, ttl, sess);
      this.client.setex(sid, ttl, sess, function(err){
        err || debug('SETEX complete');
        fn && fn.apply(this, arguments);
      });
    } catch (err) {
      fn && fn(err);
    } 
  };

You can see that it divides TTL by 1000 because it uses seconds rather than millis for its expiration. The most popular MongoDB Session store uses MongoDB's TTL feature in the same way.

So this was a long way of saying that you will either rely on your DB engine to provide server-side expiration of sessions automatically or you need to implement expiration yourself. You could have a process outside of your node app (maybe another node process) that does it or your store implementation could install a SetInterval task to periodically check and clean it. As an example, a MySQL-based session store does just that

Regarding the second part of your question, what are length and clear doing? The commenter is correct that RedisStore doesn't implement these and they can probably be ignored safely, however you can see their implementations in the MemoryStore source code. Not too exciting.

clear empties all the sessions and the callsback if a callback is provided:

MemoryStore.prototype.clear = function(fn){
  this.sessions = {};
  fn && fn();
};

length simply calls back with the number of sessions in the store:

MemoryStore.prototype.length = function(fn){
  fn(null, Object.keys(this.sessions).length);
};

Hope this was helpful.

Gotham answered 3/4, 2014 at 15:18 Comment(2)
Answered all my questions perfectly. Thanks!Rhotacism
@Rhotacism - Glad to hear. I'm surprised I hadn't noticed the question earlier.Gotham

© 2022 - 2024 — McMap. All rights reserved.