Introduction
Server solutions are designed for high multiuser load and fault tolerance often used scalable architecture (Web Farm for example). In such kind of solution, it is an attractive idea to build stateless server modules (without ASP.NET session for example). Thus, authorization of user/session of each request should be implemented in the server module in a custom way. This tip describes an optimized approach to resolve the issue.
Background
The approach was used to quick fix overloaded Web Service on Web Farm. Web Service receives SessionGUID
and before process request makes a request to MS SQL Sessions table to check the session activity and receives User attributes. The fault was caused by unexpected increased number of users of the system and authorization was a bottleneck.
And now I got the idea to rewrite it well, build a performance test and discuss. Perhaps it makes sense to use this approach not only as a hotfix. Maybe this approach can be useful to solve and some other issues of overloaded clusters.
Algorithms
The Task
- Verify the existence of a session
- Get corresponding user data with roles of the user
- Update session activity timestamp (no frequently than is necessary)
The Basic Algorithm
- At Login: User authentication
SessionGUID
generation and add Session table record - At Request: Check whether there is a record with the
SessionGUID
- Load User with Roles and update Activity timestamp (if needed)
- At Logoff: Delete Session record with the
SessionGUID
Assumptions for Optimization
- The code is very often executed from parallel threads
- It does not matter that servers cannot determine instantaneous closing of the session (only during a predetermined period)
- It does not matter that servers cannot receive user attributes updates instantly (only during a predetermined period)
The Optimization
- The
Session
records in-memory cache - Use a permanent background thread to refresh the
Session
records with defined time period - When the Cache Manager does not find an entry in the
Session
cache, it tries to load it from database (because an actual result of the operation should be received immediately and cannot be postponed up to cache refresh)
Using the Code
I used the following test database:
Sessions handling implemented in two modes:
public interface ISessionManager { ... }
public class SessionManager : ISessionManager { ... }
public class SessionCacheManager : ISessionManager { ... }
Cache controller is implemented in a separate class: SessionCache
.
The solution includes a test command line application.
SessionCacheManager
uses static
fields. In ASP.NET, it may by useful to replace it to an in-process cache storage.
Testing
Execution time of 500 authorization operations, depending on the number of concurrent threads of execution:
- BLUE (basic algorithm) - unexpected deviation at start
- RED (optimized algorithm) - is stable and expectedly fast
Points of Interest
May be interesting as a sample of usage effective thread lockers: ReaderWriterLockSlim
and Interlocked
History
- December-2015: v1.0.0.1 Initial release