This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.
To Cache or not to Cache | |
Ted Pattison | |
hen you're designing a Web application, state management always involves tough choices. For instance, you must decide whether it's necessary to make a trip to the database management system (DBMS) to access data. There are times when avoiding round-trips can give you a definite performance boost, and times when reading and writing directly to the DBMS during a client request is the only sensible option. Caching Data on the Web Server Many developers have found they can significantly improve application performance with caching techniques that share in-memory data across a set of ASP pages and COM objects. The increase in application performance usually results from a reduced number of round-trips to the DBMS. It's much faster for a Web application to process a client's request using data that's close at hand, as opposed to reading and writing data that lives across the network in a DBMS.
When many users are accessing your Web application at the same time, the ASP runtime processes client requests using a pool of single-threaded apartment (STA) worker threads. When ASP pages create objects from one of your components, these objects get distributed across the various threads in the pool. Whenever each specific thread creates an object from your ActiveX DLL for the first time, the DLL initializes all its .BAS module data in TLS.
Now let's discuss the second problem with .BAS module data. Having multiple copies of a variable causes problems with data consistency; namely, different objects in the same process see different instances of the same variable. You can't guarantee which objects share the same instance. This means that one object running in a multithreaded environment cannot reliably see changes written to the variable by another object. This makes it impossible (or at least very difficult) to use .BAS module variables that are read/write as opposed to read-only. Using the Shared Property ManagerThe second important caching technique involves using the SPM, which is a small component library that's built into both the MTS and COM+ runtimes. The SPM is a name/value dictionary that allows you to read and write to named property values. One of the big advantages to using the SPM is that it allows objects running on different threads within the same process to see a single instance of a named property value, as shown in Figure 3. This means that the SPM uses memory more efficiently than .BAS module data. In addition, updates to the SPM by one object can reliably be seen by all the other objects inside the same process, regardless of who's running on what thread.
Since shared properties in a property group are accessible from any thread, it's important to think through all relevant issues regarding concurrency and synchronization. Fortunately, the SPM provides an easy-to-use scheme where an object can acquire an exclusive lock while making a series of read and write operations to properties within the same group. In other words, the SPM makes it possible for an object to perform a sequence of read and/or write operations in isolation. This can prevent scenarios where one object sees the partial, incomplete work of another. Using an ASP Application Object The final caching technique I'm going to examine is the use of the ASP Application object. A Visual Basic-based object that's been created from an ASP page can create and access an ASP Application variable like this: |
Dim appl As ASPTypeLibrary.Application
Set appl = GetObjectContext("Application")
appl.Value("MyProp") = "My quintessential value"
Of course, you must reference both the ASP type library and the MTS and COM+ type library in order to access the ASP Application object in this fashion. You should also note that with COM+, the IISIntrinsics attribute of your configured component must be set to True. This is the default value, so you usually don't have to adjust anything. Also note that this attribute is not accessible through the COM+ Services administrative tool and is only accessible through the COM+ Admin objects. The ASP Application object differs from the SPM in that the names of its variables do not have a processwide scope. Instead, ASP Application variables are scoped within an IIS application (that is, a virtual directory). It's possible for two different IIS applications to run in the same process. The ASP Application variables of one IIS application are invisible and inaccessible to the other. This is a good thing because one IIS application cannot step on the ASP Application variables of another IIS application if there's a naming conflict. However, it's limiting because you cannot share ASP Application variables across IIS applications. (I guess this is the reason they're called Application variables to begin with.) When it comes to synchronization and locking, the ASP Application object doesn't provide any control over granularity. There is only one coarse-grained lock per application. This scheme is not nearly as flexible as the SPM because locking ASP Application variables is an all-or-nothing proposition. With the SPM you can lock shared properties in one group without locking the shared properties in other groups. In contrast, whenever a Visual Basic-based object or an ASP page acquires the lock on the ASP Application object, all other requests attempting to access ASP Application variables are blocked until the lock is released. Like the SPM, the ASP Application object never uses shared locks; it always uses exclusive locking. As you can imagine, designs that frequently lock the ASP Application object can create quite a bottleneck in high-volume applications. Unlike the SPM, the ASP Application object doesn't force you to use locks if you don't want them. Let's look at an example of when this is helpful. Imagine that you're required to initialize a large set of environmental variables in the ASP Application object upon application startup. If you acquire a lock on the ASP Application object, you can guarantee that no client request sees your data until you've initialized everything into a valid state. Once you've completed the initialization, you can release the lock and use these variables in a read-only fashion for the rest of the application's lifetime. As long as the data is read-only, there's no reason to acquire and release locks. The fact that ASP Application variables can be accessed without locking means that they are slightly faster than using the SPM. There's one last advantage that ASP Application variables have over the other two techniques. They can be accessed directly from ASP pages as well as Visual Basic-based components. This can be very convenient in projects where the developers using ASP and those using Visual Basic are working side by side. Choosing the Best Approach I've examined three of the more common server-side data-caching techniques used by Visual Basic-based programmers. You now know to ask the right questions. Is achieving better performance more important than conserving memory? Can you use cached data in a read-only fashion, or is it necessary to make updates over the lifetime of the application? Do you need locking to synchronize access and maintain data consistency? If you don't need locking, how can you avoid it? If you do need locking, how can you minimize the amount of items you're locking down at any one time? Asking these questions will help you the next time you need to design a data-caching scheme for a Web application.
Limitations of Data Caching Techniques When you design a caching scheme, you typically make a copy of frequently used data items and store them in a place that provides faster access. As long as the real data items (that live in the DBMS) and the copies (that live on the Web server) stay in sync, things are fine. However, when data needs to be updated, it introduces issues related to consistency. For example, if another application writes changes to the DBMS, the copies on the Web server can become inconsistent. You should observe that it's much easier to design a caching scheme for data that's read-only as opposed to data that's updated over the lifetime of an application. Locking and Concurrency A noteworthy limitation with both the SPM and the ASP Application object is their inability to use shared locks. Both the SPM and the ASP Application only offer exclusive locking. Once a client request acquires an exclusive lock for a particular data item, it blocks every other client until the lock is released. In many situations, exclusive locking is overkill. Shared locks can offer the required levels of consistency with significantly less impact on an application's concurrency. Enforcing the ACID Rules While the SPM and the ASP Application object provide isolation through exclusive locking, they are unlike a DBMS because they cannot enforce the ACID rules (atomicity, consistency, isolation, and durability). For a primer on these rules, see the sidebar "Transactions in Distributed Applications" in the article "Use MSMQ and MTS to Simplify the Building of Transactional Applications" (MSJ, July 1998). Sharing Data Across Process and Machine Boundaries Neither the SPM nor the ASP Application object provide a way to share data across process boundaries. Two Visual Basic-based objects can only see the same SPM data when they run inside the same process. Two Visual Basic-based objects (or ASP pages) can only see the same ASP Application object data when they run inside the same process and the same ASP Application. |
Ted Pattison is an instructor and researcher at DevelopMentor (https://www.develop.com), where he co-manages the Visual Basic-based curriculum. The second edition of Ted's book, Programming Distributed Applications with COM and Visual Basic 6.0, was recently published by Microsoft Press. |
From the November 2000 issue of MSDN Magazine.