et’s face it. Caching is a fancy was of saying that you are going to save some type of data somewhere. In the n-tier web application model, your tiers are typically made up of the presentation layer (ASP), the business logic and data access layer (COM+), and the backend data layer (SQL Server, DB2, Oracle, etc). Each tier has its own ways of caching data. In ASP you can use the Application object, which is useful for storing data that seldom changes, such as XML or HTML menus, while databases are the best place to maintain user state. To cache data in the middle-tier, COM+ provides the SPM.
Side note: when caching data, cache data relevant to the tier the data resides on. For example, do not cache HTML menus in the SPM. By doing that, you must make a component call from ASP to the middle-tier just to get an HTML menu, which is presentation layer data. See what I’m getting at?
The SPM is not exactly shared memory, but can be easily used to perform similar functionality. Visual Basic developers can use the COM+ Services Type Library to invoke the SPM and manage cached data within COM+ applications. This article will demonstrate how to use the SPM from Visual Basic 6, as well as provide an in-depth description of the SPM from a Visual Basic programmer’s perspective (to use the SPM, create a reference in Visual Basic to the COM+ Services Type Library; this will expose the interface needed to manage SPM groups and properties).
Data in the SPM is managed within groups, which in turn contain properties. SPM properties are similar to that of a dictionary object. Meaning, they are used as name/value pairs. This lets you assign and retrieve values of SPM properties based on their names. SPM groups are also given names, which allow you to manage any number of different groups, each containing any number of different properties.
To begin, let’s review the architecture behind COM+ applications.
COM+ Applications and the DLLHOST Process
Let’s continue the discussion by reviewing how COM+ manages components. First, there are two types of COM+ applications: server and library. Server applications are out-of-process while library applications are in-process; meaning, library applications run under the process and context of the calling application. COM+ server applications are housed in a process called DLLHOST.EXE, and each server application has its own unique DLLHOST.EXE process. This process is managed by COM+ and is assigned its own process ID (PID); this allows you to view the processes for COM+ server applications within Task Manager. (Note: The PID becomes important when troubleshooting hangs or crashes of COM+ server applications. The Status View in Component Services displays the PID for each server application. You can then use Task Manager to find the PID in question and view its CPU usage, memory usage, thread counts, handle counts, etc. At that point, you could issue a kill command on the process experiencing difficulty.)
Why is any of this relevant to the SPM? As you’ll see later on, data in the SPM resides in the memory of a COM+ server application the DLLHOST process. So it’s important to know that data in the SPM can be used only by components in the same DLLHOST process. A component running under one COM+ server application cannot access data in the SPM of another COM+ server application. Cross-process accessibility is not available for the SPM.
The diagram below shows two COM+ server applications. Each has its own set of components, as well as SPM groups and properties. The components in COM+ application A can only access the SPM for its own DLLHOST process. Conversely, components in COM+ application A cannot access data in the SPM of COM+ application B.
OK, now onto some code.
Creating SPM Groups
Before you can store data in the SPM, you must create an SPM group to hold your data. This is done as such:
Const GRP_NAME As String = "MyGroup"Dim blnExists As BooleanDim spmMgr As COMSVCSLib.SharedPropertyGroupManagerDim spmGrp As COMSVCSLib.SharedPropertyGroupSet spmMgr = New COMSVCSLib.SharedPropertyGroupManager' create the SPM groupSet spmGrp = spmMgr.CreatePropertyGroup(GRP_NAME, LockSetGet, _ Process, blnExists)
The function definition for CreatePropertyGroup looks like this:
CreatePropertyGroup(NameOfGroup, IsolationMode, ReleaseMode, ExistsFlag)
The CreatePropertyGroup method requires some interesting parameters. NameOfGroup is self-explanatory, but the other three need some clarification. IsolationMode determines the locking mechanism used by the SPM group. The ReleaseMode parameter determines how long data in the SPM group remains in memory. ExistsFlag is an output value that tells the caller if the group already existed in memory. The IsolationMode and ReleaseMode parameters require further explanation, which is done later in this article.
At this point, it’s interesting to note that calls to the SPM are slightly different than most. To create a group or property in the SPM, you use a corresponding “create” method. To check if an SPM group or property already exists, you use the same methods. This explains the purpose of the ExistsFlag parameter. In the above example, a call to CreatePropertyGroup returns blnExists. If this value is False, the group was created in the SPM. A value of True means the group already exists in memory; therefore, it will not be created again. The reasoning behind this is simple: if a group or property is constantly being created in the SPM, the whole purpose of caching is defeated.
Now that you’ve got your group, let’s put some data in it.
Creating SPM Properties
Data values that are put into SPM groups are called properties. The code below demonstrates how to create a property in a given SPM group:
Const GRP_NAME As String = "MyGroup"Const PRP_NAME As String = "MyProperty"Dim spmMgr As COMSVCSLib.SharedPropertyGroupManagerDim spmGrp As COMSVCSLib.SharedPropertyGroupDim spmPrp As COMSVCSLib.SharedPropertyDim blnGrpExists As BooleanDim blnPrpExists As BooleanSet spmMgr = New COMSVCSLib.SharedPropertyGroupManager' create the SPM group; remember that the group might already existSet spmGrp = spmMgr.CreatePropertyGroup(GRP_NAME, _ LockSetGet, Process, blnGrpExists)' create the SPM property; the property might also already existSet spmPrp = spmGrp.CreateProperty(PRP_NAME, blnPrpExists)
The function definition for CreateProperty looks like this:
The NameOfProperty parameter is easy, and ExistsFlag is used in the same fashion as mentioned above.
So, you’ve got a group that now contains a property, but the property doesn’t have a value. Let’s take a look at how to set and get property values.
Getting and Setting Property Values
Fortunately, getting and setting SPM property values is very simple. Once you have a group and property created, the code below can be used to get and set property values (assuming the variables have already been declared):
' get property valuevntValue = spmPrp.Value' set property valuespmPrp.Value = vntValue
It’s important to note that all values assigned to SPM properties are of type Variant. This allows you to store simple data values in the SPM, including arrays. To assign an array to a property, you could use code similar to this:
Dim arrData(0 To 3) As StringarrData(0) = "A"arrData(1) = "B"arrData(2) = "C"arrData(3) = "D"spmPrp.Value = arrData
You must resist the temptation to put Visual Basic object references in the SPM. That can lead to dangerous and unexpected results because Visual Basic objects use the STA threading model; therefore, caching those references could greatly reduce concurrency. The flip-side of that is storing MTA objects references (C++ components) in the SPM because concurrency does not become an issue. As an example, storing a reference to the Microsoft FreeThreadedDOMDocument in the SPM would be safe because it is an MTA object. For more information, please read Ted Pattison’s book on programming COM+ with Visual Basic (mentioned again at the end of this article).
You may wonder how long your SPM groups will reside in memory and if or when the groups will go away. The answer depends on which release mode you use when creating the groups. COM+ provides two release modes when creating SPM groups?Process and Standard.
When using the Process release mode, your SPM groups will live in memory for the entire life of the DLLHOST process for which it was created. Therefore, as soon as an object is instantiated, the SPM groups and the data in them will live in memory until the DLLHOST process ends or goes out of scope, which under normal circumstances occurs when the shutdown time for the component application has been reached (other causes are when a component application is manually shutdown or deleted).
The Standard release mode works in a slightly different fashion. Instead of staying in memory for the life of the DLLHOST process, SPM groups live until all objects in the DLLHOST process have been destroyed (using normal reference counting). For example, every time you instantiate a component, an internal counter increases by one. The opposite occurs as you destroy objects (Set object = Nothing). This means that SPM groups created with the Standard release mode are put into memory on the first object instantiation and removed from memory once the internal counter gets back to zero. Once this happens, all data in your SPM groups will be destroyed. This process starts over when the next object is instantiated and the counter increases.
In most instances, you’ll want to keep the data in SPM groups around for the life of the component application. Why? Because it increases your caching time of the data you have stored in the SPM. This means you’ll use the Process release mode much more often than the Standard release mode.
Now that we’ve covered the ReleaseMode parameter, let’s chat about the IsolationMode.
When using components to access data in SPM groups, whether it’s reading or writing, an exclusive lock is enabled. This means that all access to data in SPM groups is serialized. However, the real question is how long is the lock enabled? The answer is determined by which isolation mode you chose when creating the SPM groups. Two isolation modes are associated with the SPM: LockMethod and LockSetGet.
When LockMethod is used, the exclusive lock is enabled for the duration of the current method call. This means that if other objects are trying to access the same SPM group, they must wait until the current method has finished executing. This is useful when updating more than one property in an SPM group because it can help to ensure data consistency among all properties of the group. For example, if you have two properties in a group that are dependant on each other, the LockMethod mode can be used to update both properties at the same time, under the same exclusive lock. This will ensure that subsequent access to those properties will return the correct values.
The LockSetGet isolation mode enables the exclusive lock for a much shorter period of time. Instead of waiting until the method call is complete, the lock is turned on for only the amount of time it takes to read or write the value of a property in an SPM group. This is very useful when concurrency is of utmost importance, especially when used under a high volume, high peak load. However, the LockSetGet isolation mode is more prone to data inconsistency. Using the example of two dependant SPM properties, if you need to update both properties and you use LockSetGet, the updates to those properties will occur under two different exclusive locks instead of one. Because of this behavior, you cannot always maintain data consistency between those two dependant properties.
Note that you can certainly create SPM groups based on the LockMethod mode and SPM groups that use the LockSetGet mode. It comes down to a matter of concurrency versus data consistency. It’s important to design and create your SPM groups with those two concepts in mind.
Both isolation modes can be used for reading and writing SPM group properties. If your design is a write once/read often design, then the LockSetGet mode will probably be your choice. If your design requires many updates and many reads to SPM group properties, the safe bet is to use LockMethod. But you must also take into consideration the expected load for your COM+ application. If your load is relatively small, performing updates to SPM properties probably won’t adversely affect your application. However, if your volume is high, those write operations to SPM properties can cause severe bottlenecks in your COM+ application, thus greatly reducing the scalability of your application overall.
With all this being said, it’s important to note the SPM is not without its limitations. These are outlined below.
As you can see, the SPM is very useful for storing simple data values in the memory of a COM+ application; however, the SPM is not without its drawbacks.
First and foremost, you must resist the temptation to cache object references in the SPM. Doing so can severely affect the performance, reliability, and scalability of your application.
Another issue is that read/write operations in the SPM are non-transactional. Because of this, if you update an SPM property from within a COM+ transaction, the change is permanent, even if you rollback the transaction.
And as mentioned above, the SPM uses an exclusive lock for both the reading and writing of SPM properties, which means that all access to the SPM is serialized. Because of this, an application under heavy load that makes numerous updates to SPM properties will suffer the consequences.
OK, on to an example you can actually use.
So now you might be asking, When would I want to actually use the SPM? Good question. There are a couple different scenarios. You could use the SPM to store database connection strings. Connection strings typically don’t change between releases of an application so this is a valid candidate for using the SPM. Or let’s say you have several methods of a component that need to read configuration-type data from XML at run-time. For this, you could load the XML string into the SPM for quick access during the method calls. These aren’t the only uses, but this article should give you the knowledge of what to store in the SPM and when to put it there.
Back to storing database connection strings in the SPM. I have a DLL named SPMVB6.dll that contains one class module?MyDatabase.cls. This component is used to connect to a SQL Server database; its connection string is stored as a global private constant. The connection string could have easily been read at object construction or stored in a .udl or XML file, etc., but it’s not really important where the connection string starts out at?only where it ends up. Also included in the component are a couple methods used to manage data the SPM.
The design of the MyDatabase component is as such: the MyDatabase.cls module uses Class_Initialize to put the connection string in the SPM. The OpenDBConnection method then retrieves the connection string from the SPM to establish database connectivity. Because this article focuses on the SPM and not database connections, I’ve not provided a method to close the database connection or anything else related to the database connection. The OpenDBConnection method makes use of the SPM methods, which are what you really want to see. There are numerous ways to manage database connections, but that is not the topic of this article.
Note about the following code: no error handling routines have been added. The code is kept as thin as possible for the purpose of this article. Most, if not all of the code in this article, can be used out of the box, but be sure to add in your own error handling.
That being said, here’s all the code for the MyDatabase.cls module:
Option ExplicitPrivate Const CONN_STR As String = "Provider=SQLOLEDB.1;" & _