Does Spring Session management take care of asynchronous calls?
Say that we have multiple controllers and each one is reading/writing different session attributes. Will there be a concurrency issue as the session object is entirely written/read to/from external servers and not the attributes alone?
We are facing such an issue that the attributes set from a controller are not present in the next read... this is an intermittent issue depending on the execution of other controllers in parallel.
When we use the session object from the container we never faced this issue... assuming that it is a direct attribute set/get happening right on to the session object in the memory.
The general use case for the session is storing some user specific data. If I am understanding your context correctly, your issue describes the scenario in which a user, while for example being authenticated from two devices (for example a PC and a phone - hence withing the bounds of the same session) is hitting your backend with requests so fast you face concurrency issues around reading and writing the session data.
This is not a common (and IMHO reasonable) scenario for the session, so projects such as spring-data-redis or spring-data-gemfire won't support it out of the box.
The good news is that spring-session was built with flexibility in mind, so you could of course achieve what you want. You could implement your own version of SessionRepository and manually synchronize (for example via Redis distributed locks) the relevant methods. But, before doing that, check your design and make sure you are using session for the right data storage job.
This question is very similar in nature to your last question. And, you should read my answer to that question before reading my response/comments here.
The previous answer (and insight) posted by the anonymous user is fairly accurate.
Anytime you have a highly concurrent (Web) application/environment where many different, simultaneous HTTP requests are coming in, accessing the same HTTP session, there is always a possibility for lost updates caused by race conditions between competing concurrent HTTP requests. This is due to the very nature of a Servlet container (e.g. Apache Tomcat, or Eclipse Jetty) since each HTTP request is processed by, and in, a separate Thread.
Not only does the HTTP session object provided by the Servlet container need to be Thread-safe, but so too do all the application domain objects that your Web application puts into the HTTP session. So, be mindful of this.
In addition, most HTTP session implementations, such as Apache Tomcat's, or even Spring Session's session implementations backed by different session management providers (e.g. Spring Session Data Redis, or Spring Session Data GemFire) make extensive use of "deltas" to send only the changes (or differences) to the Session state, there by minimizing the chance of lost updates due to race conditions.
For instance, if the HTTP session currently has an attribute key/value of 1/A and HTTP request 1 (processed by Thread 1) reads the HTTP session (with only 1/A) and adds an attribute 2/B, while another concurrent HTTP request 2 (processed by Thread 2) reads the same HTTP session, by session ID (seeing the same initial session state with 1/A), and now wants to add 3/C, then as Web application developers, we expect the end result and HTTP session state to be, after request 1 & 2 in Threads 1 & 2 complete, to include attributes: [1/A, 2/B, 3/C].
However, if 2 (or even more) competing HTTP requests are both modifying say HTTP sessoin attribute 1/A and HTTP request/Thread 1 wants to set the attribute to 1/B and the competing HTTP request/Thread 2 wants to set the same attribute to 1/C then who wins?
Well, it turns out, last 1 wins, or rather, the last Thread to write the HTTP session state wins and the result could either be 1/B or 1/C, which is indeterminate and subject to the vagaries of scheduling, network latency, load, etc, etc. In fact, it is nearly impossible to reason which one will happen, much less always happen.
While our anonymous user provided some context with, say, a user using multiple devices (a Web browser and perhaps a mobile device... smart phone or tablet) concurrently, reproducing this sort of error with a single user, even multiple users would not be impossible, but very improbable.
But, if we think about this in a production context, where you might have, say, several hundred Web application instances, spread across multiple physical machines, or VMs, or container, etc, load balanced by some network load balancer/appliance, and then throw in the fact that many Web applications today are "single page apps", highly sophisticated non-dumb (no longer thin) but thick clients with JavaScript and AJAX calls, then we begin the understand that this scenario is much more likely, especially in a highly loaded Web application; think Amazon or Facebook. Not only many concurrent users, but many concurrent requests by a single user given all the dynamic, asynchronous calls that a Web application can make.
Still, as our anonymous user pointed out, this does not excuse the Web application developer from responsibly designing and coding our Web application.
In general, I would say the HTTP session should only be used to track very minimal (i.e. in quantity) and necessary information to maintain a good user experience and preserve the proper interaction between the user and the application as the user transitions through different parts or phases of the Web app, like tracking preferences or items (in a shopping cart). In general, the HTTP session should not be used to store "transactional" data. To due so is to get yourself into trouble. The HTTP session should be primarily a read heavy data structure (rather than write heavy), particularly because the HTTP session can be and most likely will be accessed from multiple Threads.
Of course, different backing data stores (like Redis, and even GemFire) provide locking mechanisms. GemFire even provides cache level transactions, which is very heavy and arguable not appropriate when processing Web interactions managed in and by an HTTP session object (not to be confused with transactions). Even locking is going to introduce serious contention and latency to the application.
Anyway, all of this is to say that you very much need to be conscious of the interactions and data access patterns, otherwise you will find yourself in hot water, so be careful, always!
Food for thought!
I'm looking to store sessions in an external data store instead of in memory. I want to do this in order to have session data available to all my Jetty server instances.
So I read the Jetty docs:
Session Clustering with a Database
Session Clustering with MongoDB
Both docs have the following statement:
The persistent session mechanism works in conjunction with a load balancer that supports stickiness.
My question is - why do I need stickiness in this mechanism? The whole point is to allow any server in the cluster serve any request because the session data is stored externally.
The problem is that the servlet specification does not provide any transactional semantics around sessions. Thus, unless your sessions are sticky its possible for concurrent requests involving the same session to go to multiple servers and produce inconsistent results, depending on the interleaving of the writes. If your sessions are read-mostly you may get away with non-sticky sessions, although you may find that sessions time out a little sooner or a little later than you'd expect, due to having multiple servers trying to manage the same session.
My challenge is the following. I'd like to uphold the Post/Redirect/Get pattern for posted forms, but after the redirect I still want to show the state of the form in case of invalid fields. This information can be passed by storing it in session quite easily.
However these sites usually run on multiple webservers behind a load balancer, which are configured NOT to be sticky. There is also no shared session state on purpose.
To use local session state on the webservers would be preferrable for scalability, but to do that you need sticky sessions. Sticky sessions are however a hindrance to the people who manage the servers, because after removing some servers from the load balancer to do a release, they still have to wait some 10 to 15 minutes before all sessions have ended. Without stickiness it's almost instantaneously.
What would be really cool is if we could make the session sticky for just one single subsequent request after a POST or REDIRECT. Or even have total control over when to enable or disable stickiness from code.
Does anyone know if this behaviour is possible? By settings a certain cookie perhaps? Or some http header?
Few ideas:
Central cache server
save it to a cache server (prefered redis) with expiration
redirect to the get action with a parameter in the url. Using this parameter restore the model.
State in the url
if the model is small You can save it as parameters in the url.
One approach to high scalability is to use network load balancing to split processing load between several servers.
One challenge that this approach presents is where servers are state aware - storing user state in a "session".
One solution to this problem is "sticky session" (aka "session affinity") where each user is assigned to a single server and his/her state data is contained on that server exclusively throughout the duration of the session.
What are the Pros and Cons of the "sticky session" approach? Do you use it and if so are you satisfied with it?
Pros:
It's easy-- no app changes required.
Better utilizes local RAM caches (e.g. look up user profile once, cache it, and can re-use it on subsequent visits from same user)
Cons:
If the server goes down, session is lost. (Note that this is a con of storing session info locally on the web server, not of sticky sessions per se). If what's in the session is really important to the user (e.g. a draft email) or to the site (e.g. a shopping cart) then losing one of your servers can be very painful.
Depending on "sticky" implementation in your load balancer, may direct unequal load to some servers vs. others
Bringing a new server online doesn't immediately give the new server lots of load. If you have a dynamic load-balancing system to deal with spikes, stickiness may slow your ability to respond quickly to a spike. That said, this is somewhat of a corner case and really only applies to very large and sophisticated sites.
If you have relatively few users but a single user's traffic can swamp one server (e.g. complex pages with SSL, AJAX, dynamically-generated images, dynamic compression, etc.), then stickiness may hurt end-user response time since you're not spreading a single user's load evenly across servers. If you have a lot of concurrent users, this is a non-issue since all your servers will be swamped!
But if you must use server-local session state, sticky sessions are definitely the way to go. Even if you don't use server-local session state, stickiness has benefits when it comes to cache utilization (see above). Your load balancer should be able to look at HTTP cookies (not only IP address) to determine stickiness, since IP addresses can change during a single session (e.g. docking a laptop between a wired and wireless network).
Even better, don't use session state on the web server at all! If session state is very painful to lose (e.g. shopping carts), store it in a central database and clear out old sessions periodically. If session state is not critical (e.g. username/avatar URL), then stick it in a cookie-- just make sure you're not shoving too much data into the cookie.
Modern versions of Rails, by default, store session variables in a cookie for the reasons above. Other web frameworks may have a "store in cookie" and/or "store in DB" option.
I have a Spring 2.5.6/Flex application setup and running with Spring Security 2.0.4. Recently a load balancer (A Foundry ServerIron 4g http://www.foundrynet.com/products/a...ems/si-4g.html) was put into place and now I am getting cross domain errors. Basically the load balancer is firing off a request to myloadbalancer.abc.com and myrealserver1.abc.com is being returned as the domain name. Spring security is forwarding the request to the real server somehow. How can I get around this?
Also the ConcurrentSessionFilter is no longer working. The application is set up to disable concurrent logons, but this functionality stopped after the application was put behind the load balancer. I believe there are multiple Oracle Application Servers being clustered together as well. I have never dealt with clustering or load balancers before and I wasn't aware that the software had to be written differently in certain areas.
These sound like separate issues to me, but I need help for both.
Concerning your second problem:
If the ConcurrentSessionFilter stopped working (i.e. does not prevent concurrent sessions anymore), that could be due to clustered application containers with sticky sessions.
In such a setup, each of the cluster's nodes works independently and doesn't share state with other nodes. Instead, the load balancer makes sure that existing sessions will always be served by the same node.
Now Spring Security's ConcurrentSessionController works by mapping sessions to principals. The controller itself relies on the HttpSessionEventPublisher sending ApplicationEvents on start and termination of user sessions.
Everything is will work fine if someone intending to open more than one session ends up on the same node he already has a session opened. HttpSessionEventPublisher informs the concurrent session mechanism of the session's creation and authentication will fail because there is already a session associated with this user. On a different node however, there is no session for that user yet, so ConcurrentSessionController does not complain and login succeeds.
Fortunately, solving the problem should be easy: Implement your own SessionRegistry and use a shared data store for all nodes (e.g. the application's database).