When programming an application to run in a web server or app server, there is often the need to retain a value from interaction to interaction. It is easy, and very temping, to create a new session variable at the moment, and store a value there, and retrieve it at a different point in the interaction. It is an easy solution, that causes a large maintenance problem, this post explains why.
First, we need to clarify the kind of application development this post is relevant for. Web application, as you, present web pages to the user. The user will fill in a few values, and press a button, sending information from that page to the server. The server receives that in something we should call a “controller”. The controller determines what should come next, and generates a “View” which is the next web page.
An effective way to implement this is that View A presents a form of some time. Those values are posted to Controller A, which invokes View B, which submits to Controller B, which presents View C, and so on. Obviously it can be more complicated than this, because multiple view could submit to the same controller, and the same controller can cause multiple views to result. And not all user interface is a linear sequence of screens, there might be a lot of back and forth. However, sometimes the UI is a wizard-like sequence of UI screens.
An example is logging in: you are presented with some screen, you press the login button with displays the login prompt, then you submit your username/password, then if all is OK, you see the things that only authenticated people can see.
We like to think of the web and web interactions as being “stateless”. This means that you can access anything at any time, and it does not matter what you did a moment ago. OF course, if you are editing content, that content is being saved in the database. But stateless means that there is no special state around you interaction with the site. Of course, logging in is one of the main exceptions of the stateless concept. Clearly accessing a page after logging in is quite different than accessing before logging in.
This kind of state is stored in something called a “session”. In a J2EE server, the session is an object in memory which is maintained as long as the user is interacting with the server. If the user stops, then after some time (usually 30 minutes) all the session information is thrown away and the memory reclaimed. The session object is the perfect place to keep your current login permission, and other things about “what you are currently doing” that would be OK to throw away if you walk away for more than a few minutes.
What is the Danger of using Session Object
The session object allows you to create properties at any time, associating a value with a name. Any part of the code can set a session variable, and any other part of the code can read a session variable.
Session variables are a kind of “global variable” that are accessible to any part of the code, and fall prey to all the normal problems about variables that are accessible everywhere. It is hard to identify the logic behind how those values are being used. You can search and find all the places that read or set the variable, but it can be very difficult to determine the order, or the situations, that this code is used. For example, ControllerXYZ might read the value, and ControlMNO might set it, but working out which of these controllers are called first might be a challenge because it depends on the view presented to the user, and the option that user puts in, and there can be complicated branching in the user behavior.
Using arbitrarily named session variables in a module designed to be reused is a particularly bad idea because one can’t know what session variables that host application will be using. Multiple modules might end up using the same variable for different reasons. Since session variables do not have to be declared, there is no way for the compiler to detect that the same variable is being used multiple different ways.
It is a bad programming practice because it is hard to maintain, since there is no central list of all the session variables. The practice of creating session variables when needed often leads to having duplicate variables with the same values in them. Then, when designing a new piece of code, it is often difficult to know which to use, because they may not be updated entirely consistently.
What To Do Instead
One needs to think carefully and with intention about the information that the server must maintain in order to support the user through a sequence of UI screens. Of course you need to remember whether a user is logged in or not, and probably the system is going to need to know exactly which user logged in reliably. If the system allows a record to be locked for editing, preventing others from editing at the same time, then there should be a place to record the records that are locked by this user, and naturally a way to free up those locks when the session is discarded.
A global record structure is a better idea than independently created and updated session variables, because this global structure has a list of all the values, and a programmer using one of the values has the opportunity to think about the other values.
For example, if you make a “ticket booking” site, where the user specifies the destination of travel, along with start and end dates, in order to display a list of flights to choose from, it would be logical to store the query parameters in the session. A properly designed object should be created that all variety of queries that a user might use, and the query object should be stored as a single object that is manipulated consistently. You get into problem when a programmer decides to store some “aspect” of the query separately … for example whether this is award travel or regular purchase. Spreading this information out into separate variables has all the problems of global variables. Packing all things to do with a query into a well designed object keeps everything together and helps assure consistency.
Of course, a global record has another advantage, and that has type-safety. The session variables which programmers create on the fly are usually strings, which might be interpreted differently at different spots in the code. Standard OO design principles help avoid many problem associated with keeping data consistent.
The only things you should put into that global state record are things that truly are global to that session. Often developers forget that users can open a second window on the session. Developer might do something silly like keepign track of the position in a wizard in the session. For example, imagine a wizard like sequence of 5 screens. The developer might decide to remember if the user was on screen 1, 2, 3, 4, or 5. But putting this in the session globals has a big problem when the user is on step 3 of one wizard, and opens another window, to start at the beginning. Is the user on screen 1 or on screen 3? Both obviously, and the session global value will be wrong for one of these. Do not store anything about a particular display state in the session! Only things that are truly global for the user.
Sometimes you have no choice: For example the user query example above. Consider what happens if a user runs a query for a particular trip for particular dates, and then opens another window and run a different search. Most such applications will throw away the former query, and start a new one. Then, if the user returns to the former window, they will get an error saying “sorry, your previous results are not available, please start over”. This is a decision based on the idea that you really don’t have the resources to run multiple queries simultaneously, and so your only choice is to give the user an error message. However, care should be given to thinking about how to detect that the user switched windows, and to accurately identify thsi with an error, and NOT to just move forward using the current query with a window that was originally displaying the earlier query.
Duplication Into Session
Another problem I have seen recently is programmers reading some value from the DB, and then saving a copy of that in a session variable as faster cache. The programmer is thinking that this is just a “faster” version of the DB. The problem, of course is that someone else using the system (in a different session) might change the DB. When that happens, the DB and session variable are out of synch, and all sorts of strange behaviors appear: some of the code may be using the value from the DB, and other parts of the code is using the session variable, and this causes inconsistent screens. Often such values are used to control what people can do, and you can get into situations where one piece of code is preventing action based on the DB value, and another part on the session value, and this can make a Catch-22 situation where the user is locked out of all action.
A specific example is a piece of code that would lock a record for editing by marking a column in the DB, and then also marking a variable in the session. When testing as a single user you never notice any problem. But multi-user testing caused situations where the record was locked, but the UI did not think so, and so was preventing the user from being able to edit the record. Also the opposite situation, where the UI was allowing the user to edit, but at the time of saving, the engine was rejecting the changes because the record was not locked.
Clearly, the solution here is to avoid saving the same value in two places. Anytime you have the same value in two places, there is a possibility fo them to be out of sync. If the official value is in the DB, then you have to actually check the DB every time, for every significant function.