Session Affinity
Last updated March 09, 2020
Table of Contents
Session affinity, sometimes referred to as sticky sessions, is a platform feature that associates all HTTP requests coming from an end-user with a single application instance (web dyno).
Enable session affinity
To enable session affinity:
$ heroku features:enable http-session-affinity
Session Affinity is available for applications in the Common Runtime. It is currently not available for applications in Private Spaces.
What is session affinity
Sticky sessions allow applications and frameworks to make assumptions about traffic coming their way. They can, more or less, assume that what worked for a single-dyno system can keep working when you add more dynos.
In a system without session affinity, if the programmer developed a way to store user data in memory (and nowhere else), this would work fine on one dyno. However, as soon as the system moved to two dynos or more, servers running on different dynos may handle different requests, meaning that different requests will have different views of server-local data. For example, if someone stores profile photos on disk on one dyno and the next request hits a different dyno, the photo won’t be there.
Session affinity therefore allows framework writers and designers to avoid the more difficult issues and requirements that multi-backend systems tend to have.
The assumptions sticky sessions allow also have a few other benefits:
- You can cache more data locally and keep state local, reducing the number of database operations.
- The software may have fewer dependencies and becomes easier to run locally and test.
- It is often easier to reason about a system locally than to reason about it globally.
Caveats
In the more general case, regardless of the sticky session implementation chosen, there are potential problems:
- Unresponsive, slow, overloaded, or crashed dynos showing as unreachable to the router
- Dyno cycling
- Scaling dynos up and down
In all of these cases, even if for a brief period of time, a dyno that is still up and running may not accept new connections or handle new requests, or is entirely down and will never handle them.
Under these scenarios, a client may be redirected to an alternate dyno. The session affinity mechanism will resume from that endpoint, but the original one will be abandoned.
You should therefore plan accordingly. Session affinity is usually an optimization and not a replacement for long-lived connections.
How it works
Heroku’s session affinity mechanism has the following properties:
- The effect of a dyno joining or leaving the entire set of dynos for an app is minimized.
- A dyno being unresponsive causes no downtime to the client. The request is routed to a different dyno.
- It is portable across applications and requires no modifications to the application, no matter the platform or language.
- It works on arbitrary clients behind arbitrary ISPs or infrastructure, as long as they support HTTP cookies.
The requirement for HTTP cookies is there because it has been judged the most reliable and least invasive means by which to offer session affinity (compared with other means such as IP addresses or URL-based mechanisms).
As soon as the feature is enabled on an application, the Heroku router will start adding an HTTP cookie named heroku-session-affinity
to every new request and response.
This cookie contains no private application information and doesn’t require request- or response-related data to function.
Based on this cookie value, the Heroku router will be able to determine the appropriate dyno for each request.
The Heroku router will also be able to know when new dynos have been added to the fleet, so that when a scale-up event happens, instead of ending in a scenario where all dynos but the new one serve requests, a fraction of all of them will be moved to occupy the new dyno.
If 4 dynos are each holding 10 connections and a 5th dyno is added, you should therefore expect roughly 2 connections (on average) from each existing dynos to be shifted to the 5th dyno to make sure all resources are used equally.
The opposite is also true. When a dyno goes away, its connections randomly get redistributed to other available dynos, giving a roughly equal redistribution.
Invalidating session affinity
While requests will automatically be redistributed when you scale up or down, there might be some situations where you’ll want to manually redistribute traffic. You can force a client to get assigned a new random dyno by clearing the heroku-session-affinity
cookie. After doing this, the next request will cause the Heroku router to treat it as a brand new client and it will be assigned a new dyno by setting a new cookie.
Disable session affinity
To disable session affinity:
$ heroku features:disable http-session-affinity