Web developers often refer to session state stores and caches interchangeably, while in actuality they serve different purposes.
A cache serves as a caching layer between a web application and an external data source. Caches exist mainly to lighten the load on the external data source, thus improving the performance of the application.
The purpose of a session state store is to store a user’s workspace. Session state stores enable client activity to be persisted consistently across several HTTP requests.
Applications that utilize a cache write to the external data source, typically a database, and read from the cache. This technique can significantly improve the performance of applications that mostly read from the data source.
Caches are designed to be read speedily, simultaneously by many clients and threads. Some cache implementations can link the cached object to the data source, such that if the data source is updated, the cache is invalidated.
Session state stores are not linked to external data sources by design, although it can be consumed by an application to do so. While it may be necessary to store certain parts of a client’s session state to a database, there is usually no need to store all of the session state to a database. In fact, many database-less web applications rely solely on session state to operate.
Session state implementations are designed such that each client has exclusive access to its session data. Even though caches can be consumed by an application to simulate this, exclusivity is not enforced and there is always a chance that a client will be able to access another client’s session data due to either poor design or security flaws.
A distributed cache spreads out an application’s caching layer across many machines, which allow high-traffic web applications to scale out by adding more machines as demand increases. Performance can also be improved by distributing session state data across many machines; therefore, it is worthwhile to examine the requirements and nuances of cached data and session state before deciding on a distributed solution to apply.
Distributed caches, like local caches, are most effective when used to cache data that changes infrequently. They also support simultaneous fast reads of a cached object by many threads. This is where session state sharply differs from cached data.
Session state has little need for speedy multiple-thread access to a single stored resource because a client can only exclusively read or update its session. In addition, the usage pattern of session state is unpredictable. Some applications update session data very frequently while some do not. Session state storage designers normally safely assume that session state is write-heavy.
Caches are configured by default to use either an optimistic concurrency mechanism or no concurrency control at all, to access cached data. This design is driven by the strong requirement to eliminate blocking by any means possible, and works superbly due to the lower proportion of writes to reads.
Session state stores, on the other hand, utilize a pessimistic concurrency mechanism to access stored data. This works effectively because of the exclusive nature of resource access.
The number of concurrent session state accesses to a stored resource can increase if a user opens up several web browser instances of the same application or if the application makes use of numerous AJAX calls. Notwithstanding multiple instances and AJAX-intensive applications, a user’s session cannot have more than a handful of concurrent access attempts.
A pessimistic concurrency mechanism, as used by session state stores, can gracefully handle a few concurrent accesses on a write-heavy resource, and more importantly, provide consistent data to all operations. Inconsistencies in served data can arise, if a cache with no concurrency control is employed to store write-heavy session state. This problem becomes more apparent if the application is AJAX-intensive.
Critical applications that rely on session state require failover and redundancy support. These features are usually built into commercial session state storage solutions.
Caches have no need for failover or redundancy because caches are simply a caching layer: if the requested data cannot be retrieved from the cache, it can always be fetched from the primary source. Therefore, most distributed cache implementations do not support failover or redundancy; issues solution architects seldom remember when moving session storage to a distributed cache.
The conundrum of where to store session state arises when an application needs to scale to accommodate more users.
While there are a few commercial distributed session state storage solutions, there are no free robust alternatives, and the usual consensus is to store session state in freely available distributed cache solutions, or eliminate session state entirely from the application.
Moreover, even when session state is manageably stored in a distributed cache, most often, the same servers that are caching infrequently changing data are used to store session state. Sharing the cache this way leads to performance degradation.
This occurs because whenever the cache server needs to store a new cached object or remove an expired one, it has to momentarily suspend all read operations internally on all other cached objects until the object is added or removed. The overall outcome is sub-optimal reads for cached infrequently changing data.
Developers and architects should carefully weigh the aforementioned issues before moving locally stored session state to a distributed storage and should, whenever possible, opt for a solution that was specifically built for distributed session state storage.
Wouldn’t it be nice if C# code could be compiled directly to machine code? Having such a compiler would position C# as a serious system programming language.
Developers would be able to write system software for routers, for instance, in C#.
I don’t see any reason why such a compiler should not exist. In fact, the creation of a native C# compiler will be well justified.
C# is a well designed programming language. It would be a shame if the language is stuck forever with the .NET/Mono frameworks, especially if you consider that there is no reason why the language has to be inextricable tied to these frameworks.
There is no requirement that C# code must compile to IL and there are no language-level assumptions that the compiled code has to be machine-independent.
High-level features routinely used by developers such as threading and reflection, are .NET library calls and have no connection to the language itself. The only high-level feature that the language implies is a garbage collection system. In fact there are language-level hints that C# can be a system-level programming language -- how often do you use the volatile, stackalloc and fixed keywords?
The C# developer base is huge, so a native C# compiler will push the language even further to new platforms and projects that are currently unsuitable for development with C#. It will enable developers to write ALL their code; high-level and low-level in C#. Higher-level code will be compiled to IL, whereas lower-level code will be compiled to machine code.
A native C# compiler would be great for coding libraries, for instance, a proprietary encryption or compression algorithm. With a native C# compiler, an algorithm can be coded in C# and compiled as a native code library. This library can be linked to and used in systems without any .NET (or alternative) frameworks.
The best part is that if this library is needed for a .NET/Mono project -- all that is needed is recompilation and the algorithm will scale to managed code without having to port the library or use unmanaged calls. It will work great in both managed and unmanaged worlds.
The language is an open standard, so anyone with the time, expertise and resources can create a native C# compiler. Also, to be successful, this compiler only needs to support C# version 2.0.
Language features introduced in versions 3.0 and 4.0 are not that important and can be considered “Microsoft extensions” to the C# language. Indeed, Anders Hejlsberg admitted that features added in C# 2.0 were features they didn’t have time or didn’t know how to properly implement in C# 1.0.
If you are still not convinced about the viability of such a compiler, take a look at the compilers available for the D language. They are living proof that such a compiler is feasible and will compile a modern language directly to machine code, complete with a (small) runtime, memory management and type system.