App level heartbeat to ensure clients get disposed() when they disappear
authorDarren <darren@darrenwhitlen.com>
Tue, 23 Sep 2014 21:11:47 +0000 (22:11 +0100)
committerDarren <darren@darrenwhitlen.com>
Tue, 23 Sep 2014 21:11:47 +0000 (22:11 +0100)
client/src/models/gateway.js
server/client.js

index 7fe7cf3bdd489e26f8c8b44f32e1fccd6cd4a533..4fe707d70959032df2e0a1bf92c9df07d9353785 100644 (file)
@@ -76,6 +76,16 @@ _kiwi.model.Gateway = Backbone.Model.extend({
             // Reset the disconnect_requested flag\r
             that.disconnect_requested = false;\r
 \r
+            // Each minute we need to trigger a heartbeat. Server expects 2min, but to be safe we do it every 1min\r
+            var heartbeat = function() {\r
+                if (!that.rpc) return;\r
+\r
+                that.rpc('kiwi.heartbeat');\r
+                that._heartbeat_tmr = setTimeout(heartbeat, 60000);\r
+            };\r
+\r
+            heartbeat();\r
+\r
             console.log("_kiwi.gateway.socket.on('open')");\r
         });\r
 \r
index 724b51cefa2517e5e851d4587ad5fd98537332be..609c906713e30e15d794d13aafc0e6a1ab745178 100755 (executable)
@@ -52,6 +52,11 @@ var Client = function (websocket, opts) {
     // Handles the kiwi.* RPC functions
     this.attachKiwiCommands();
 
+    websocket.on('message', function() {
+        // A message from the client is a sure sign the client is still alive, so consider it a heartbeat
+        that.heartbeat();
+    });
+
     websocket.on('close', function () {
         websocketDisconnect.apply(that, arguments);
     });
@@ -87,14 +92,40 @@ Client.prototype.sendKiwiCommand = function (command, data, callback) {
 Client.prototype.dispose = function () {
     Stats.incr('client.disposed');
 
-    this.disposed = true;
+    if (this._heartbeat_tmr) {
+        clearTimeout(this._heartbeat_tmr);
+    }
+
     this.rpc.dispose();
+    this.websocket.removeAllListeners();
+
+    this.disposed = true;
     this.emit('dispose');
+
     this.removeAllListeners();
 };
 
 
 
+Client.prototype.heartbeat = function() {
+    if (this._heartbeat_tmr) {
+        clearTimeout(this._heartbeat_tmr);
+    }
+
+    // After 2 minutes of this heartbeat not being called again, assume the client has disconnected
+    console.log('resetting heartbeat');
+    this._heartbeat_tmr = setTimeout(_.bind(this._heartbeat_timeout, this), 120000);
+};
+
+
+Client.prototype._heartbeat_timeout = function() {
+    console.log('heartbeat stopped');
+    Stats.incr('client.timeout');
+    this.dispose();
+};
+
+
+
 Client.prototype.attachKiwiCommands = function() {
     var that = this;
 
@@ -130,6 +161,12 @@ Client.prototype.attachKiwiCommands = function() {
             build_version: args.build_version.toString() || undefined
         };
     });
+
+
+    // Just to let us know the client is still there
+    this.rpc.on('kiwi.heartbeat', function(callback, args) {
+        that.heartbeat();
+    });
 };