HCdev
HCdev

Reputation: 550

SignalR javascript client side timer hammering the server for connections

I've got an implementation of SignalR which is being used as a chat client. The problem seems to be that I have a client side timer, that is pinging the server to notify all other clients of a user's status.

In this instance, I have a heartbeat and a latestactivity. The heartbeat hits the server every fifteen seconds or so, and the latestactivity tracks the user's interactivity. These two values are passed to the server so that all other clients can be notified of everyone else's status. Ie: If User A hasn't moved their mouse for over a minute, when their heartbeat hits the server, it'll broadcast to every other user via SignalR that they are now 'away'..

Anyways, I have an issue where there seems to be an exponential increase in SignalR connections the greater the number of user's that connect to the chat app.

This is the source of the client js timer that I suspect is fishy but I'm not sure whY:

define(['jquery', 'underscore', 'backbone'],
function ($, _, Backbone) {

    var Timer = Backbone.Model.extend({
        defaults: {
            interval: 1 * 10 * 1000,
            timeout: null
        },

        initialize: function (options) {
            _.bindAll(this, 'start', 'tick', 'stop', 'tickNow');
            if (options.interval) {
                this.set('interval', options.interval);
            }
        },

        start: function () {
            var timer = setTimeout(this.tick, this.get('interval'));
            this.set('timeout', timer);
        },

        tick: function () {
            var self = this;
            self.trigger('timerexpired', this);
            self.start();
        },

        tickNow: function () {
            var self = this;
            self.stop();
            self.trigger('timerexpired', this);
            self.start();
        },

        stop: function () {
            clearTimeout(this.get('timeout'));
        }
    });

    return Timer;
});

Upvotes: 1

Views: 1109

Answers (3)

halter73
halter73

Reputation: 15234

You shouldn't simply pass this.tick as the first parameter to setTimeout, because when the tick method gets executed this will be bound to the global window object instead of the Timer instance.

You should also probably ensure you are not calling start when the Timer instance is already running. I would change the following methods like so:

    start: function () {
        var self = this;
        if (this.get('timeout') !== null) {
            throw "start must not be called when the Timer is already running";
        }
        // creating a closure ensures this is bound correctly in tick()
        var timer = setTimeout(function () {
            self.tick(); 
        }, this.get('interval'));
        this.set('timeout', timer);
    },

    tick: function () {
        this.set('timeout', null);
        this.trigger('timerexpired', this);
        this.start();
    },

    stop: function () {
        clearTimeout(this.get('timeout'));
        this.set('timeout', null);
    }

The obvious way to check that the Timer is working correctly is to bind a call to console.log to the timerexpired event to make sure the event isn't being triggered more than expected.

You should post the code where you are stopping and starting SignalR connections if you want help determining why the number of concurrent connections is growing out of control.

Upvotes: 0

Frank Radocaj
Frank Radocaj

Reputation: 381

I notice that you are using the this keyword in the start timer method. It could be that your call to this.tick call is out of context when it actually fires. Try:

start: function () {
    var self = this;
    var timer = setTimeout(this.tick, self.get('interval'));
    this.set('timeout', timer);
},

In your browser, chuck a breakpoint into the tick method and check that you are indeed referencing the right timer.

Upvotes: 2

flavian
flavian

Reputation: 28511

I don't see how the progression would necessarily be exponential, but the linear progression will certainly tend to it. Basically, you have two main things you can do here:

  • Use long polling/throttling on the client side, but you are already sending requests out every 15 seconds, so that's not breaching browser limitations.
  • On the server side, you most definitely need a load balancer if you want a large scale ajax application. Go for Amazon auto-scaling in the Cloud, that would likely help you and just use a load balancer. That will not be cheap and it is directly proportional with the activity you experience.

Upvotes: 1

Related Questions