Performance Design Principles - State Management
- J.D. Meier, Srinath Vasireddy, Ashish Babbar, Rico Mariani, and Alex Mackman
Evaluate Stateful vs. Stateless Design
Stateful components hold onto state in member variables for completing a logical activity that spans multiple calls from the client. The state may or may not be persisted after the completion of an operation. Stateful components can produce optimized performance for certain load conditions. The client makes all the requests to a particular instance of the component to complete the operation. Hence, stateful components result in clients having affinity with the component.
The caveat for stateful components is that they may hold onto server resources across calls until the logical activity is complete. This can result in increased contention for resources. The server continues to hold onto resources even if the client does not make subsequent calls to complete the operation and would release them only if there is a timeout value set for the activity.
Affinity is an important issue for stateful design. The client is tied to a particular instance because of localized state. This may be a disadvantage if you have a remote application tier and have scalability goals that mean you need to scale out your application tier. The affinity ties clients to a particular server, making it impossible to truly load balance the application.
As discussed earlier, having state for components is a design tradeoff, and the decision requires inputs that relate to your deployment requirements (for example, whether or not you have a remote application tier) and your performance and scalability goals.
If you decide on a stateless component design, you then need to decide where to persist state outside of the components so that it can be retrieved on a per-request basis.
Consider Your State Store Options
If you use a stateless component design, store state where it can be retrieved most efficiently. Factors to consider that influence your choice include the amount of state, the network bandwidth between client and server, and whether state needs to be shared across multiple servers. You have the following options to store state:
- On the client. If you have only small amounts of state and sufficient network bandwidth, you can consider storing it at the client and passing it back to the server with each request.
- In memory on the server. If you have too much state to be passed from the client with each request, you can store it in memory on the server, either in the Web application process or in a separate local process. Localized state on the server is faster and avoids network round trips to fetch the state. However, this adds to memory utilization on the server.
- In a dedicated resource manager. If you have large amounts of state and it needs to be shared across multiple servers, consider using SQL Server. The increase in scalability offered by this approach is achieved at the cost of performance because of additional round trips and serialization.
Minimize Session Data
Keep the amount of session data stored for a specific user to a minimum to reduce the storage and retrieval performance overheads. The total size of session data for the targeted load of concurrent users may result in increased memory pressure when the session state is stored on the server, or increased network congestion if the data is held in a remote store.
If you use session state, there are two situations you should avoid:
- Avoid storing any shared resources. These are required by multiple requests and may result in contention because the resource is not released until the session times out.
- Avoid storing large collections and objects in session stores. Consider caching them if they are required by multiple clients.
Free Session Resources As Soon As Possible
Sessions continue to hold server resources until the data is explicitly cleaned up or until the session times out.
You can follow a two-pronged strategy to minimize this overhead. At design time, you should ensure that the session state is released as soon as possible. For example, in a Web application, you may temporarily store a dataset in a session variable so that the data is available across pages. This data should be removed as soon as possible to reduce load. One way to achieve this is to release all session variables containing objects as soon as the user clicks on a menu item.
Also, you should tune your session timeout to ensure that the session data does not continue to consume resources for long periods.
Avoid Accessing Session Variables from Business Logic
Accessing session variables from business logic makes sense only when the business logic is interspersed along with presentation code as a result of tight coupling. You may require this in some scenarios, as discussed in "Coupling and Cohesion" earlier in this chapter.
However, in the majority of cases, you benefit from loosely coupled presentation and business logic, partitioned in separate logical layers. This provides better maintainability and improved scalability options. It is most frequently user interface–related state that needs to be persisted across calls. Therefore, session-related state should be part of the presentation layer. In this way, if the workflows of the user interface change, it is only the presentation layer code that is affected.