SignalR should handle connections limit in browser #2744

Closed
SlyNet opened this Issue Dec 7, 2013 · 10 comments

Comments

Projects
None yet
5 participants
@SlyNet

SlyNet commented Dec 7, 2013

Currently using SignalR without websockets will prevent you from openning more then 6-7 tabs of application, since browsers have limit.

It looks like infrastructure related task to handle this limit since SignalR is responsible for selecting appropriate transport.

Maybe it can be handled with communication between tabs, when one tab opens connection that is used by every other tab.

@davidfowl

This comment has been minimized.

Show comment
Hide comment
@davidfowl

davidfowl Dec 7, 2013

Member

Good suggestion but realistically speaking, this is something that is hard to do reliably that we're not going to try to solve anytime in the near future. It's a limitation of browsers that you have to live with.

There are techniques for working around the limit such as picking a new subdomain per tab (this is what facebook does) or even trying to do something crazy with local/session storage to pub/sub between tabs but that turns out to be flaky and unreliable.

So as much as I would love to us to solve this problem, we're not going to leave this issue open because we're just not going to do it. I'll make sure we document the techniques for working around these limitations on your applications on asp.net/signalr.

Member

davidfowl commented Dec 7, 2013

Good suggestion but realistically speaking, this is something that is hard to do reliably that we're not going to try to solve anytime in the near future. It's a limitation of browsers that you have to live with.

There are techniques for working around the limit such as picking a new subdomain per tab (this is what facebook does) or even trying to do something crazy with local/session storage to pub/sub between tabs but that turns out to be flaky and unreliable.

So as much as I would love to us to solve this problem, we're not going to leave this issue open because we're just not going to do it. I'll make sure we document the techniques for working around these limitations on your applications on asp.net/signalr.

@davidfowl davidfowl closed this Dec 7, 2013

@a-h

This comment has been minimized.

Show comment
Hide comment
@a-h

a-h Mar 1, 2015

I'm having the same issue, where users open multiple tabs and then the browser connection limit is reached.

I'd read the scaleout documentation and we'd implemented Redis scaleout but didn't catch anything in the documentation around browser connection limits.

My team has implemented the local storage option, using the pattern described here: http://blog.fastmail.com/2012/11/26/inter-tab-communication-using-local-storage/ - as per the author's experience, we didn't catch the issue until production usage when we found out that our users like to open a lot of tabs.

There's a implementation of the pattern at http://www.nmjenkins.com/intertab.html

@davidfowl - Could I ask why you think that this implementation would be flaky and not suitable for inclusion into SignalR?

I'd rather this behaviour was supported in SignalR than rolling our own, so I'd put effort into a pull request if I thought it would be accepted.

@tejacques at callr might also be interested, since his library relies on SignalR - tejacques/callr#9

a-h commented Mar 1, 2015

I'm having the same issue, where users open multiple tabs and then the browser connection limit is reached.

I'd read the scaleout documentation and we'd implemented Redis scaleout but didn't catch anything in the documentation around browser connection limits.

My team has implemented the local storage option, using the pattern described here: http://blog.fastmail.com/2012/11/26/inter-tab-communication-using-local-storage/ - as per the author's experience, we didn't catch the issue until production usage when we found out that our users like to open a lot of tabs.

There's a implementation of the pattern at http://www.nmjenkins.com/intertab.html

@davidfowl - Could I ask why you think that this implementation would be flaky and not suitable for inclusion into SignalR?

I'd rather this behaviour was supported in SignalR than rolling our own, so I'd put effort into a pull request if I thought it would be accepted.

@tejacques at callr might also be interested, since his library relies on SignalR - tejacques/callr#9

@tejacques

This comment has been minimized.

Show comment
Hide comment
@tejacques

tejacques Mar 1, 2015

@a-h funny you should bring this up. I actually solved this problem in a similar way. There's a branch on callr dedicated to it, as well as my tejacques/crosstab repo.

I've actually discovered quite a bit since originally looking into this, and as @davidfowl mentioned it is very difficult to solve this problem reliably.

Here are some of the unmentioned issues that I've run into:

  • Subscriptions:
    • These need to be kept track of for each tab at all times on all tabs. (In case master tab switches, you'll need to resubscribe to the union of all of them, and proxy to each tab only the subscriptions that tab is subscribed to).
    • Track which channels are subscribed to overall, as well as by this particular tab. They are often different.
    • Track which hub state is sent by each particular tab.
    • When the master tab changes, the new master must subscribe to all of the events.
  • IE StorageEvents trigger on the same tab. These must be ignored to conform to the spec.
  • IE8 StorageEvents are not up to spec. See tejacques/crosstab#6 and tejacques/IE8-EventListener.
  • Attempting to use this on any device which runs tabs single threaded, or freezes inactive tabs will break. crosstab deals with this by user-agent sniffing devices up front to measure support, and pinging the master with a timeout in the background. This is very important because mobile is the largest growing sector of the web. If you ignore mobile it will come back to bite you. There is no easy/reliable feature detection for this that I am aware of.
  • Intercepting SignalR's callbacks involves digging into the implementation specific jQuery callbacks. This is brittle and will break when SignalR removes the jQuery dependency, and might be unfixable depending on how they change it.

After going through all of these, I decided not to merge my solution with callr's master branch, because there are likely still other issues I haven't found, and using a bunch of subdomains is just too easy in comparison.

@a-h funny you should bring this up. I actually solved this problem in a similar way. There's a branch on callr dedicated to it, as well as my tejacques/crosstab repo.

I've actually discovered quite a bit since originally looking into this, and as @davidfowl mentioned it is very difficult to solve this problem reliably.

Here are some of the unmentioned issues that I've run into:

  • Subscriptions:
    • These need to be kept track of for each tab at all times on all tabs. (In case master tab switches, you'll need to resubscribe to the union of all of them, and proxy to each tab only the subscriptions that tab is subscribed to).
    • Track which channels are subscribed to overall, as well as by this particular tab. They are often different.
    • Track which hub state is sent by each particular tab.
    • When the master tab changes, the new master must subscribe to all of the events.
  • IE StorageEvents trigger on the same tab. These must be ignored to conform to the spec.
  • IE8 StorageEvents are not up to spec. See tejacques/crosstab#6 and tejacques/IE8-EventListener.
  • Attempting to use this on any device which runs tabs single threaded, or freezes inactive tabs will break. crosstab deals with this by user-agent sniffing devices up front to measure support, and pinging the master with a timeout in the background. This is very important because mobile is the largest growing sector of the web. If you ignore mobile it will come back to bite you. There is no easy/reliable feature detection for this that I am aware of.
  • Intercepting SignalR's callbacks involves digging into the implementation specific jQuery callbacks. This is brittle and will break when SignalR removes the jQuery dependency, and might be unfixable depending on how they change it.

After going through all of these, I decided not to merge my solution with callr's master branch, because there are likely still other issues I haven't found, and using a bunch of subdomains is just too easy in comparison.

@a-h

This comment has been minimized.

Show comment
Hide comment
@a-h

a-h Mar 2, 2015

Thanks so much for your detailed reply. I'll tackle the SignalR issue with subdomains and see how we get on.

a-h commented Mar 2, 2015

Thanks so much for your detailed reply. I'll tackle the SignalR issue with subdomains and see how we get on.

@slimjack

This comment has been minimized.

Show comment
Hide comment
@slimjack

slimjack May 7, 2015

Try IWC-SignalR. It allows to have single connection for all windows. It is based on localStorage.

slimjack commented May 7, 2015

Try IWC-SignalR. It allows to have single connection for all windows. It is based on localStorage.

@tejacques

This comment has been minimized.

Show comment
Hide comment
@tejacques

tejacques May 7, 2015

@slimjack: This is really interesting, to see how someone else is attempting to solve this. I discovered some issues which I added to the IWC library's issue tracker. Are you using this in production for anything? If so, the best way I've found to quickly test/verify is to run a controlled experiment where some users get the subdomain solution and others get the library solution. If the aggregate stats for traffic/activity in the group using the library solution are equivalent (or better) then you know you've covered enough edge cases to be worth using.

@slimjack: This is really interesting, to see how someone else is attempting to solve this. I discovered some issues which I added to the IWC library's issue tracker. Are you using this in production for anything? If so, the best way I've found to quickly test/verify is to run a controlled experiment where some users get the subdomain solution and others get the library solution. If the aggregate stats for traffic/activity in the group using the library solution are equivalent (or better) then you know you've covered enough edge cases to be worth using.

@slimjack

This comment has been minimized.

Show comment
Hide comment
@slimjack

slimjack May 14, 2015

@tejacques Thanks for your discovering and remarks.
Yes, this library is used in production, but SignalR connection is used not so intensive to think about performance. But it's interesting for me to compare localStorage solution with subdomain solution. As I understand, you mean to simulate an intensive usage of SignalR from multiple windows simultaneously. And during this load I should measure "number of requests per second" or "bytes per second". Is this what you meant?

@tejacques Thanks for your discovering and remarks.
Yes, this library is used in production, but SignalR connection is used not so intensive to think about performance. But it's interesting for me to compare localStorage solution with subdomain solution. As I understand, you mean to simulate an intensive usage of SignalR from multiple windows simultaneously. And during this load I should measure "number of requests per second" or "bytes per second". Is this what you meant?

@tejacques

This comment has been minimized.

Show comment
Hide comment
@tejacques

tejacques May 14, 2015

@slimjack Sorry if I didn't explain that very well. I meant more in terms of just recording usage numbers on the back end, rather than raw bytes or requests per second. An example would be if you were using SignalR for something like chat.

Assume you have two solutions to the multiple connection issue for SignalR:

Control: Using different subdomains
Experiment: Using localStorage / inter-tab communication

You'd want to run an experiment on some potion of the users of your site (say 10%, decided by username or something similar) and look at the impact on chat usage for users in the experiment vs. control (what % of users chat at all, mean/median # of chats sent, etc.).

If you determine that there is no difference, or a statistically significant positive impact from the experiment / localStorage solution after running it for a reasonable amount of time on a large enough sample size, then you can have confidence that you've covered all of the major edge cases.

At the very least I can tell you it's very difficult to get equivalent numbers here, especially if you further break it down by browser type. Safari specifically does not allow using localStorage when in Private Browsing mode, which is a surprisingly common use case, so you sort of need both solutions anyway. This is more or less the place I got to when I responded the first time.

If you'd like to discuss this more we can move discussion to your project repo.

@slimjack Sorry if I didn't explain that very well. I meant more in terms of just recording usage numbers on the back end, rather than raw bytes or requests per second. An example would be if you were using SignalR for something like chat.

Assume you have two solutions to the multiple connection issue for SignalR:

Control: Using different subdomains
Experiment: Using localStorage / inter-tab communication

You'd want to run an experiment on some potion of the users of your site (say 10%, decided by username or something similar) and look at the impact on chat usage for users in the experiment vs. control (what % of users chat at all, mean/median # of chats sent, etc.).

If you determine that there is no difference, or a statistically significant positive impact from the experiment / localStorage solution after running it for a reasonable amount of time on a large enough sample size, then you can have confidence that you've covered all of the major edge cases.

At the very least I can tell you it's very difficult to get equivalent numbers here, especially if you further break it down by browser type. Safari specifically does not allow using localStorage when in Private Browsing mode, which is a surprisingly common use case, so you sort of need both solutions anyway. This is more or less the place I got to when I responded the first time.

If you'd like to discuss this more we can move discussion to your project repo.

@slimjack slimjack referenced this issue in slimjack/IWC-SignalR May 14, 2015

Open

Comparison with subdomain solution #8

@slimjack

This comment has been minimized.

Show comment
Hide comment
@slimjack

slimjack May 14, 2015

@tejacques, let's continue discussion in IWC-SignalR repo.

@tejacques, let's continue discussion in IWC-SignalR repo.

@a-h

This comment has been minimized.

Show comment
Hide comment
@a-h

a-h May 15, 2015

I ended up putting in 10 subdomains and selecting one at random to make the connection to in the SignalR javascript. The implementation time was < 2 hours, for adding DNS entries in test and production systems, adding host name bindings in IIS, writing the javascript to select a DNS entry at random and updating the web.config files to have a list of SignalR endpoints to use in it and to enable CORS (my app was already in a subdomain). I've had no defects reported in production by hundreds of users who use the system each day. It's the edge cases that @tejacques describes, like Safari not allowing localStorage in private browsing that mean I wouldn't risk a localStorage based solution to multiple tabs. I'm pretty sure I wouldn't have found that problem while testing...

a-h commented May 15, 2015

I ended up putting in 10 subdomains and selecting one at random to make the connection to in the SignalR javascript. The implementation time was < 2 hours, for adding DNS entries in test and production systems, adding host name bindings in IIS, writing the javascript to select a DNS entry at random and updating the web.config files to have a list of SignalR endpoints to use in it and to enable CORS (my app was already in a subdomain). I've had no defects reported in production by hundreds of users who use the system each day. It's the edge cases that @tejacques describes, like Safari not allowing localStorage in private browsing that mean I wouldn't risk a localStorage based solution to multiple tabs. I'm pretty sure I wouldn't have found that problem while testing...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment