To understand what MTS is and what it does, we need to first make one very important point clear. This software should really have been named Microsoft Component Server, not Microsoft Transaction Server. MTS is all about managing the way applications use components, and not just about managing transactions. Yes, transactions are a big part of many applications we write and MTS can help to manage these—but MTS also provides a very useful service for applications that don’t use transactions at all.
We're already tossing about words like 'transactions'—which are short on real meaning at the moment. To be able to define MTS accurately, we first need to understand what goes on inside it in the most basic way. That's what we'll do in this section, and you won't see anything more about transactions until later in the chapter.
We'll start by looking at the way we traditionally use components within our applications. This will help you to understand how and why MTS can make the whole process more efficient.
Using Components Without MTS
When we use a component in an ASP page, for example, we have to create it before we can use it. The sequence of events is:
For example, in the previous chapter, we created and used an instance of the
component like this:WCCFinance
...
Set objFinance = Server.CreateObject("WCCFinance.PaymentTerms")
objFinance.TotalPrice = Request.Form("txtTotalPrice")
objFinance.InterestRate = Request.Form("txtInterestRate")
objFinance.MonthlyPayment = Request.Form("txtMonthlyPayment")
intResult = objFinance.GetNumberPayments
'rest of page
...
The Problem - Holding Component Instances
Although we didn’t destroy the component explicitly in our page, it is implicitly destroyed as soon as the last reference to it is lost—i.e. when our page finishes executing, the results have been sent to the client, and the ASP code is removed from memory. Because we held the reference until our page was complete, the component was retained in memory. Even while it's not being used, it is taking up resources on the server.
In a multi-user environment, where there are high demands on server resources, it makes sense to destroy each instance of a component as quickly as possible. We could have improved the behavior of our page by adding a line that destroys it directly after the call to the method that provides the result:
Set objFinance = Server.CreateObject("WCCFinance.PaymentTerms")
objFinance.TotalPrice = Request.Form("txtTotalPrice")
objFinance.InterestRate = Request.Form("txtInterestRate")
objFinance.MonthlyPayment = Request.Form("txtMonthlyPayment")
intResult = objFinance.GetNumberPayments
Set objFinance = Nothing
'rest of page
However, if we decide that we need to use the component again later in the page, we will then have to create a new instance. We'll also have to set all the properties again, because the new instance will contain the default values. In this case we often tend to hang on to our object instead of explicitly destroying it, because it is far slower and much less efficient to keep recreating it.
It's also possible to hold on to component instances throughout the life of an application or user session in ASP, by defining it in
. This is fine if it is used very regularly, but forces it to stay in memory for the life of the application or session.global.asa
Holding Database Connections
While holding on to component instances is bad enough, there is an even worse scenario. Most applications require access to a data store of some kind, usually a database such as Oracle, SQL Server, Sybase, etc. To provide database access in a component, we generally create a connection to the appropriate database through an OLE-DB or ODBC driver.
Database connections are expensive things to hang onto. There are a limited number available in any system, and they also take time and server resources both to create and maintain. Holding onto one in your ASP page is fine until you get a lot of users. At this point the database driver will begin to refuse connections, or the server will run out of resources. On the other hand, creating them over and over again slows the application down. Take, for instance, this pseudo code:
get a database connection
read some records
process some user-input values
process the record values
store the updated records
read some more records
calculate values for new records
create and store the new records
close and free the database connection
All the time this code is executing it holds onto the database connection, preventing other pages from using it. What we should really be doing is:
get a database connection
read some records
close and free the database connection
process some user-input values
process the record values
get a database connection
store the updated records
read some more records
close and free the database connection
calculate values for new records
get a database connection
create and store the new records
close and free the database connection
Now, the connection is available to other users while we are doing some other processing in our code. The problem is that the page will perform far more slowly. To help in this situation, many database drivers implement connection pooling to minimize the delays.
Database Connection Pooling
Rather than have to create a new database connection each time a user requests one, many database (or data store) drivers, such as the ODBC 3.0 or later drivers for SQL Server, provide connection pooling. After a connection has been created, used by an application, and then freed when the application has finished with it, the driver holds the connection in memory in a pool.
When another application requests a connection the driver software searches the pool of available unused connections and, if it finds one that matches the requirements, supplies this instead of creating a new one. This provides much faster response. However, applications still have to acquire, use, and then free the connections, and this is still a time-consuming business.
Note that connection pooling is not enabled by default in IIS 3, as it is in IIS4. In IIS 3 it can be enabled via a Registry setting—check out the IIS documentation for more details.
Providing Persistent Context for Components
MTS gets round the problems of component creation and destruction by providing a pool of component instances in much the same way as the data store drivers can provide a pool of available connections. However, it does this in a much more intelligent and comprehensive way.
MTS can provide a component to an application on demand, and allow other applications to use the component when the first one is just hanging on to the component instance, but not actually using it. It does this by fooling the application into thinking it still holds a reference to the component, when in fact MTS has spirited it away while the application wasn't looking and given it to someone else.
If the application suddenly wants to use the component that it thinks it's holding a reference to, MTS rushes around the pool of available instance and steals one from another application. Only if there are no available instances does it create a new one, and hand it over to the first application.
This rather risky-sounding technique is called persistent context, and it works because MTS provides a substitute 'dummy' context to the application, which thinks this is a real component. What it's actually got, of course, is just an empty shell...
How MTS Provides Persistent Context
To see how MTS provides persistent context, take a look at this following series of events. Imagine we have three clients that at some point require access to a component that is on the server, which already provides instances to clients. In the following diagram (stage one) only clients A and B are using instances of the component. Client C has not yet created an instance of the component:
What's happened is that MTS has intercepted the calls from A and B that create the component instances (in our code earlier this was
), and instead created virtual context objects and returned references to these, rather than references to the component instances themselves. By reading the interface of the component it can (as we saw in the previous chapter) tell what functions the component provides, and thereby impersonate this interface in the context object.CreateObject
A context object is not actually a physical object in itself. In COM terms, MTS creates a context and class factory wrapper for the component, and links this to a class factory object. However, the way it is actually implemented internally is not important here.
Now (at stage two), client A has finished using the component, but has held on to the reference to it—in other words it hasn’t destroyed it by setting the object variable it received when it created the instance to
. What it's actually holding on to, of course, is the context object and not the component. MTS reclaims the component instance and returns it to the pool of available instances. At the same time, client C has requested an instance of the component, so MTS creates the context object for client C and links it to the instance of the component that it just took away from client A:Nothing
But, in stage three, client A wants to use the component again, maybe by calling another method within it. Unfortunately client C is still using it, but client B has temporarily finished with its instance of the component so MTS can grab this one and pass it to client A—by pointing A's context object to this instance. Notice that client A will use this component instance without being able to tell that it is a different instance to the one it used last time:
So, MTS can provide a seamless context for each client application for the life of the page or application process. The clients don’t realize that the objects they are referencing are being constantly shuffled around behind the scenes. Like a good magic show, it's all smoke and mirrors.
State and the Component Context
The big question is, of course, how does MTS know when an application has finished using a component instance? Obviously when the application tries to destroy the object either explicitly (with
objectvariable Set
=
) or implicitly when the page or process is complete, MTS knows that the object can be returned to the pool and the application's context object is then destroyed. But this is only half the story.Nothing
We looked at how an application may hold on to an instance of a component right through its lifetime. However, it may not actually be using the component all the time, and this is just the point where MTS needs to be able to reclaim the object and let other applications or processes use it. This is done by a couple of special lines of code added to the component.
When a component has finished a task, it calls the
or SetComplete
methods of its context object, which effectively tell MTS that the component has finished the task and that it no longer requires its internal state to be maintained—i.e. all the data it was holding has either been returned to the client or stored permanently on disk. At this point MTS knows that it can reclaim the component and use it again elsewhere. When the original application comes back to use the component again, MTS can provide a fresh copy of it with default values for all the properties, etc. SetAbort
This last point is vitally important to bear in mind. MTS can only reuse a component when the client application no longer expects the state to be the same as before. In other words, property settings and other information stored inside the component will probably not be the same as the last time it used the component. The application must reset any values that it needs by, for example, setting all the properties again.
It's important to realize that it is the design of a component that dictates whether it requires its internal state to be maintained in order to function properly. Essentially, an object (in the traditional sense of the word) is a collection of data and methods that act upon the data. Most traditional client/server components follow a stateful paradigm, in which an object retains its data and methods in memory. Such components are created early and kept around for the entire duration of processing—and only released when the process completes.
Component Caching and Pooling
Originally it was suggested that MTS would provide pooling of component instances, as hinted at in the section above. In fact in the current release—and probably for the foreseeable future—this is not actually the case. The
method that MTS implements for each installed component has no effect at present. CanBePooled
The non-implementation of object pooling arises because all components currently used in MTS run under the Apartment threading model. However, if you mark the objects as Both threaded (only C++ or J++ support this, and not VB) then these object will be able to be pooled when (and if) MTS does support this feature in the future.
MTS will cache the component itself, but it destroys instances of it when they are freed by the application that uses them; in other words when the application ends (or the ASP page is complete), or when the application or ASP code releases it's reference to the component explicitly. This may, for example, be the result of executing the line
objectvariable Set
=
. Nothing
However, MTS can efficiently create new instances of the component on demand from the cached object, so the lack of component instance pooling is not generally a disadvantage. In the cached state, a component is said to be deactivated. By default MTS maintains a component object in this state for three minutes after the last time it was referenced by an application, though this delay can be changed. The process of caching components like this is often referred to as Just-In-Time (JIT) Activation.