Design Strategies for Scalable Active Server Applications
MSDN Content Development Group
This article covers design strategies that enable Active Server Pages (ASP) transaction-processing applications to scale gracefully in a distributed server. Scaling gracefully means that the application maintains its functional integrity and uses available capacity effectively as it is distributed across multiple computers.
I'll show how you can partition an application along transactional lines into stateless, encapsulated requests that eliminate the need to cache data in the ASP Session object. By eliminating the need for the Session object, you eliminate dependency on cookies, and you enable the application to be easily distributed across multiple Microsoft® Internet Information Servers (IIS).
I’ll also discuss using a stateless object model for your middle-tier service objects where no object (property) data is saved between method calls. Using a stateless object model for your service objects is more efficient than using a stateful object model because the stateless model does not require Microsoft Transaction Server (MTS) to cache object property data each time it deactivates the object, nor does it need to restore the data when it activates the object. By making your application MTS friendly in this manner, you are preparing it to effectively use additional computing capacity as it is added.
Finally, I’ll show how to reduce a major database query cost in transaction-processing applications by sharing presentation data across sessions. The job of retrieving data for presentation in the user interface can generate more query work in a typical transaction-processing application than data operations that actually modify data. If this presentation data is stored in shared presentation cache objects, multiple sessions can use it to supply the user interface from an in-process cache that eliminates expensive and redundant queries on the back end database.
This article applies to n-tiered transaction-processing applications using Microsoft Active Server Pages 2.0. Application services are provided by Component Object Model (COM) servers running under Microsoft Transaction Server 2.0 and using Microsoft SQL Server 6.5 with the Microsoft Distributed Transaction Coordinator (DTC).
Strength in Numbers
One of the many advantages of developing server applications for the Microsoft Windows NT® operating system is that server capacity can be built up incrementally by distributing the workload of an application over multiple computers. The tremendous amount of capacity that can be developed in a distributed scenario was illustrated in a recent Microsoft demonstration in which a small group of computers, running Windows NT Server 4.0, Microsoft SQL Server 6.5, the Distributed Transaction Coordinator, and Microsoft Transaction Server, were able to process banking transactions at a rate of a billion transactions per day (about a quarter of the world’s banking transaction volume).
If you design your ASP application using a stateless request model, you will be able to distribute it across multiple IIS servers and to use the tremendous capacity that can be developed across multiple computers. If that isn't reason enough to get you to use a stateless request model, consider that by encapsulating application requests and removing dependence on a session state, pieces of your code can be easily reused and your applications are easier to maintain.
Figure 1. A distributed Internet server
Figure 1 shows a distributed Internet server that provides high-volume online transaction processing (OLTP). It consists of the following:
HTTP servers. A system of independent IIS servers, using any distribution or load-balancing strategy, services HTTP requests from the Internet. As demand for any resource increases, that resource or branch can be routed to an underutilized computer. In order to exploit this powerful distribution capability, your application needs to encapsulate HTTP requests so that they provide all the information necessary to fulfill the request. By eliminating reliance on server caching for a client between requests, the application request set becomes stateless. Although ASP provides the Session object for state caching on the server, dependence on this feature may limit an application’s ability to scale because the Session object is not shared across IIS servers. Also, because the session identifier that is used to retrieve session data is sent to the browser as a cookie, using the ASP Session object makes your application prone to failure with browsers that lack support for cookies or when users configure their browsers to reject cookies.
Layered application services (COM servers). Layers of COM middleware, distributed across computers using DCOM under the management of MTS, service the application. Layers are defined along boundaries of functionality and reusability, with actual component boundaries determined by resource and distribution requirements. Layers of service objects between the client and the back-end database provide code reusability, efficient resource utilization, data security, and transactional integrity. MTS controls object instantiation so that it is practical for the application to obtain object references only when the object’s services are needed and to release objects as soon as they are not needed. Most of the objects, with the exception of the shared presentation data objects, are stateless in that they don’t cache data between method calls. The stateless object model reduces the amount of data that is constantly being cached and restored as MTS dispenses object resources for many concurrent users. Presentation data objects are exceptions to the stateless object model because they share data. This reduces redundant trips to the database in order to service the user interface for multiple concurrent users.
Distributed relational-database management system servers (SQL Server and DTC). The back-end database management system provides persistent data services, where the resource manager (DTC), as utilized by the middleware layers under MTS, guarantees the consistency of transactions that are distributed across multiple SQL Server databases.
The Encapsulated Request
If you design your ASP application using a stateless request/response model, (where the application has no dependence on data being cached in the ASP Session object), you will be able to distribute it across multiple IIS servers and realize tremendous gains in capacity. In addition to easy scalability, you eliminate dependence on client-side cookies. The stateless model also encourages you to design your ASP application as a collection of encapsulated request/response pairs that can be reused in multiple applications. For example, a standard customer-editing application could be used by many other applications within an organization. The cost of these advantages is to forego the programming convenience that the ASP Session object provides and to include more data in the request/response pairs. Because each request contains the user ID and password or other authentication data, you will probably want to use encryption in order to secure the application. If your application uses a basic HTML interface, you may want to make system data in the request/response group hard to read when a user views HTML source code.
A Rich HTTP Conversation Without ASP Session Caching
The following sequence of pseudo HTTP requests and responses demonstrates how an application can provide a rich interface for transaction processing without depending on the server to cache data in the ASP Session object. This conversation consists of a series of round trips, with each trip consisting of an HTTP request, processing of the transaction by the application, and an HTTP response. The HTTP request is handled by whichever IIS server is drawn by the load-balancing scheme for the request’s intended resource (the URL). The request is complete enough to contain all the data that the server will need to submit a transaction to the application services and to return an HTTP response to the user’s browser.
The user fills in the logon form and submits it.
If the user selects a customer and asks for customer detail, the system submits the following request. In addition to passing the selected customer PKId with the request, FirstCustomerId is included with the request and with the subsequent response so that the application can return the user to the same segment in the customer list. By following this technique of adding placeholders to the request and response, you can maintain user interface continuity without caching Session state in IIS.
If the user selects the Edit button, the following request is sent. If Add Customer had been selected from the customer-list interface, a similar request, with a different Action value and without a CustomerId, would have been sent.
If you really need to cache something about a user session, write it to the back-end database. The back-end database is a good place to store any necessary session-specific state because it is designed for consistency across a distributed system.
Because the server application is not responsible for keeping track of where each user is in the application, its operation consists of merely fulfilling HTTP requests as they arrive. By eliminating state caching, the server can service more requests. The same principle can be applied to middle-tier service objects within your application. Because each service object that your application instantiates consumes server resources, an application that uses resources efficiently should get object references as late as possible before the object is needed and release them as soon as possible. MTS manages object instantiation so that initialization delay is reduced, but an object model that does not depend on object state can be instantiated quicker than one that depends on cached state because there is less initialization overhead. During the time that your application holds a reference to an object, MTS deactivates the object between method calls in order to free resources. A stateful object model requires MTS to restore cached property values each time your object is referenced and, therefore, runs less efficiently as your application services more simultaneous users.
Sharing Cached Presentation Service Objects
The strategy of using a stateless object model for your service objects has an important exception in the presentation services cache. Presentation objects that contain collections of data objects should be shared because the cost of retrieving the data and keeping it consistent is high. Although some overhead is required to establish a notification path so that presentation caches on multiple computers are notified when data changes, this cost is much lower than the cost of allowing each session to query the database directly each time the interface needs to be updated. Figure 2 shows one example of a distributed ASP application using presentation caches. In this example, each ASP application serves a portion of a larger application. The presentation cache object, Application(“oPcache”), is instantiated when the ASP application starts. When oPcache initializes, it registers itself with oCaches, a singleton COM server that can be referenced by every utility object that provides insert, update, and delete services. Whenever a utility object performs an Insert, Update or Delete, oCaches is notified and oCaches then notifies all of the oCache objects in its list.
Figure 2. A shared presentation cache
The most effective way to increase the capacity of your ASP applications is to distribute the application across multiple computers. The three strategies discussed here ensure that your ASP application will maintain its functional integrity and efficiently use added capacity as it is distributed across computers. Use a stateless request model that doesn’t rely on the ASP Session object to hold session state. This strategy avoids dependence on cookies to maintain session continuity and allows you to easily distribute the application across multiple IIS servers. Use a stateless object model in your middle-tier service objects in order to save MTS the work of caching and restoring property data for each method call. Finally, share presentation data objects, which contain collections of application data objects, among all sessions in order to reduce the number of redundant database queries required to maintain the user interface.