NewsC


Using Microsoft Transaction Server with VFP

 

          By Rick Strahl
          http://www.west-wind.com/

 

          Last update: November 5, 1999

 

 

Microsoft has been on course to integrate into the Enterprise for a few years now and there's been hard push at the company to provide tools to make the move to scalable server based applications as easy as possible for developers. One of the main components that figures heavily in Microsoft's WinDNA (Windows interNet Architecture) plans is Microsoft Transaction Server. This tool defies easy classifications as it provides a host of useful and powerful functionalities that have traditionally been very difficult to program. This very diversity is one of the reasons MTS is often misunderstood and difficult to get a handle on. In this article I'll address several of the key features and what they mean to VFP developers.

 

This document covers the following topics:

         

 

¨        An overview of Microsoft Transaction Servers main features

¨        How MTS works

¨        The impact of Transaction Server in server based applications

¨        How to create VFP COM components for use in MTS

¨        Specific discussions of several technologies in MTS:

¨        Just In Time Activation

¨        Distributed Transaction Management

¨        Role based security

¨        An analysis of what these features actually mean for VFP applications

 

 

So what's the big idea?

So, you've been building applications that use COM. You have a few server applications that are humming away but in the back of your mind you're thinking: "I don't really know what to do when the time comes to scale this application both on the local machine and across the network." The focus of Microsoft Transaction Server is in helping making this process easier by providing an abstraction layer that handles many of these details for you. You've probably heard about 3 and n-tier programming, which should contain middle tier servers. One of the tasks of a middle tier component is to be able to abstract the scalability issues – MTS can fill this place rather nicely by hosting your components.

 

MTS accomplishes most of its functionality through a concept called packages (to be renamed COM+ applications in Windows 2000). Packages are host containers for COM components. To create a COM component that runs in MTS you create the component and then add it to one of these packages. A package is a grouping of components that share the package's attributes. The package provides a common context to all components that reside within it. The context allows MTS to manage the components inside, rather than allowing your application to access them directly. It does so through an abstraction layer that relies on COM marshalling and remapping registry entries of your components to itself. When a component is invoked by a client (using CREATEOBJECT() or the CoCreateInstance() API just like you always do), the MTS package is activated instead and the MTS package then in turn creates your COM object and passes any calls to methods and properties forward to the actual component running within the package context.

 

If you're thinking this will slow down the performance of your COM components, you're right. MTS packages are typically implemented Out Of Process, which requires all the overhead of an EXE server, plus additional overhead for the package to call your component. However, some of the overhead is gained back through some caching that occurs inside of the MTS package as it has the ability to manage the components running inside of it.

 

The good news is that MTS can potentially provide a number of serious scalability improvements like pool management and resource sharing. The bad news is that MTS in its current version doesn't do much of this yet. What it does provide though, through this abstraction layer is functionality that's very easy to access and implement from within your applications. Some of this functionality has been traditionally very hard to implement – MTS can make this task almost easy.

What does MTS provide?

Let's look at what functionality you can gain by running inside of a Transaction Server package. Not an easy task as even a passing discussion of all of the functionality would cover a full book. MTS appears to have so much seemingly unrelated functionality wrapped up into a single product. However, all of this functionality relies heavily on the packaging mechanism just discussed and so it makes sense to provide it all within the scope of MTS.

 

MTS wraps core functionality that has to do with building scalable applications and implements functionality that has been mainly implemented for large mainframe systems. It provides functionality in a large variety of areas starting with scalability, distributed transaction management for SQL, a simple security model, COM object packaging, server based context management and adminstration and monitoring features. Let's look at a few if these in detail.

 

¨        Scalability and Resource management of COM objects—Just In Time Activation (JITA)
This feature is probably the most important one that developers are looking for as a feature from MTS. COM objects loaded through MTS can be automatically unloaded and reloaded. MTS caches the VFP runtime, so load and reload is relatively fast. MTS may also cache COM references of your object, so if objects are loaded in quick cycles your servers are never unloaded/reloaded even though your client code releases references. MTS handles all of this through its own abstraction layer, saving resources on the server. But there is a cost: COM operation through MTS can be considerably slower than calling COM objects directly depending on how you implement MTS functionality in your components.

¨        Distributed Transaction Management
This is where MTS gets its name. MTS allows database-independent management of transactions spanning multiple data sources and multiple COM objects. Essentially MTS can wrap operations into its own transactional monitor, which works through the Distributed Transaction Controller (DTC), causing any ODBC/OleDB database operations to be monitored. Components can call transactional functions to complete or abort operations, at which time MTS—rather than the application—causes the data sources to commit or revert. Data sources must be DTC compliant, which means SQL Server or Oracle at this time. (This may also apply to other data sources with third-party drivers.) The Visual FoxPro ODBC driver is not DTC compliant so you can't have VFP data participate in distributed transactions, but you can of course use VFP data in your COM objects as long as they don't require DTC transactions.
The key feature of this technology is that transactions have been abstracted to the COM layer. Rather than coding transactional SQL syntax, you can use code-based logic to deal with transactions. The transactions are SQL back-end independent (as long as the back end is DTC capable). Transactions can also span multiple databases and even across servers running different SQL back ends, such as SQL Server and Oracle. A single transaction could wrap data access that writes to both! If you've written code for this type of transaction before you know how difficult this task is and MTS now makes this a breeze.

¨        Role-based security
MTS implements a new security model that allows you to configure roles for a component. The roles are configured at the component level and mapped to NT users. Your code can then query for the roles under which the component is running, rather than using the much more complex NT Security model and functions to figure out user identity. Roles are also useful for deployment because they can be packaged with the component and can be automatically installed on other machines if the necessary accounts exist. Roles are similar to NT groups, but they are applied specifically to a package and span all of the components that live in the package.

¨        Packaging technology
MTS includes a packaging mechanism that allows taking a package (an MTS word for an application or group of objects), which might contain multiple COM objects, and packing it up into a distribution file. The distribution file contains all binary files (DLLs, type libraries and support files) as well as all information about the registry and the security settings of the component. This package can be installed on another machine for instant uptime.

¨        Remoting Support
In Process DLL COM objects can not be natively remoted to another machine. DCOM requires out of process servers in order to run. With MTS package mechanism the package can be activated across a network connection which in turn allows your In Process objects to be run over the network. An additional benefit with this functionality is the fact that a client is never directly accessing the component on the other end. If a DCOM connection dies it tends to orphan the running server on the server machine. With MTS the package can catch connection problems and automatically release and possibly even reload the server after a connection has been lost and re-established from the client side.

¨        Context management
MTS includes a static data store object similar to ASP's Application object to allow components to store data for state-keeping and inter-component communication.

 

Multithreaded COM is finally here with Service Pack 3

I've written a number of articles in this magazine that pointed out the serious issues related to the fact that the Visual FoxPro runtime was unable to handle multiple simultaneous method calls. Almost any IIS article I had to postscript with a scalability warning. This meant that ASP pages and MTS requests had to wait for another method call to finish before they could be processed resulting in very limited load capability of Visual FoxPro COM objects in a multithreaded environment such as IIS.

 

Well, no more! With Service Pack 3 of Visual Studio, Microsoft has totally revamped the VFP runtime with a new multi-threaded version in a new seperately available file called vfp6t.dll. This new runtime has moved all of VFP's global data into thread local storage that provides the data abstraction needed to run COM objects on different threads totally independent of each other. Since each instance gets its own local data there's no interference between a 'global' instance of VFP as there was (and still is with the 'standard runtime') with previous versions. The result is that VFP finally is capable of making simultaneous method calls from multi-threaded clients.

 

To build components for the new runtime there's a new command BUILD MTDLL, which builds a DLL that links with the new vfp6t.dll file. A new project build option is also available which accomplishes the same task:

 

 

If you're using Visual FoxPro with Active Server Pages, MTS or any other multi-threaded client you'll want to upgrade to SP3 as soon as possible.

 

Keep in mind though that although multi-threading is crucially important for scaling applications, that it's not a silver bullet that will solve all your problems. In fact, you'll possibly face other more serious issues like possible server overloading due to too many requests processing simultaneously and overloading the resources available on your server. Multi-threading can also cause operations to slow down as NT's thread management and simultaneous operation of requests can significantly affect performance. Multi-threading rarely provides better performance, rather it provides better responsiveness where the overall request times may increase, but more people may receive their results in a timely manner. Weigh these things carefully and know your bottlenecks so you don't get surprised by an overloaded server.

 

Do you need Microsoft Transaction Server?

Before you decide to use MTS you should ask yourself whether you really need its functionality. If you don't need any of its specialty features like role-based security or multi-phase commit of database transactions, there might be no good reason to bother with it. Although scalability is one of the often touted features of MTS, the current release of MTS does not provide much in this area at this time. Even when running the new multi-threaded VFP runtime, MTS is not much more than a wrapper around your COM object. This out-of-process COM object wrapper in turn acts as a proxy and hosts all access to your component via passthrough. All object access occurs through an out-of-process COM call, and there's the additional overhead of passing the data through two interface layers. The result is that running components inside MTS tends to be slower than running them as plain in-process COM objects, without gaining any real scalability benefits at least at this time.

 

Figure 1.1 –MTS hosts your components inside of its own out of process package container. The proxy passes through all object calls to your actual component. This layer of abstraction allows MTS to manage your components for you.

 

Here's how it works. MTS hosts your COM objects inside a package, which is an out-of-process component you can see on the Processes tab of the Task Manager running as Mtx.exe. Each package uses its own Mtx.exe host to encapsulate its application COM objects. When an MTS component is installed into MTS, the ClassIds and ProgIds are mapped to the package, which in turn knows how to pass on any interface calls directly to your actual COM components residing inside the package. The end result is that a client application never has a direct connection to your COM object, but always has a proxy reference to the MTS package, which passes through all interface calls.

 

Through some registry mapping, an instance of this object is instantiated when you issue CREATEOBJECT. The package then creates your object and calls the methods in typical COM passthrough fashion using proxy and stub architecture that is also used for cross process and DCOM marshalling of interface access calls. Any method calls made to the component hit the package container's proxy object first, and then are passed down to your internal COM object. Figure 1.1 shows the context object that sits between the client and your component – this context object provides the abstraction layer that MTS uses to provide its features to your COM components. Figure 1.2 shows how objects are called from an ASP page which is a typical example of a multi-threaded client calling your COM components. Note that if multiple simultaneous requests are made on your component, multiple instances start up and run inside the MTS package. How these object references to this package object is provided is up to MTS – it could be a brand new object, or a recycled reference. If you're using the new multi-threaded runtime available with Visual Studio SP3, you'll be able to get simultaneous method calls with the original version of VFP 6.0.

The standard runtime calls to your component will block, essentially allowing only a single method call to occur at a time in a given package. If you use ASP with or without MTS make sure you download and install the new SP3 runtime to get maximum functionality from your VFP COM components (see sidebar).

 

Figure 1.2 – When running from a multi-threaded client like IIS MTS allows multiple VFP COM objects to run simultaneously as long as Visual Studio SP3 is installed. Each client request (ASP page hit here) instantiates a new instance of the COM object inside of the MTS package. Note that the package does not run inside of the calling process (IIS).

 

The MTS package provides component services I mentioned earlier and isolates your component from the client process. (Hey, that's not really a feature since you can do that with out-of-process components on your own—but that's marketing for you.)

 

Out-of-process packages – known as Server Packages -  are the norm, but MTS also supports in-process packages which are called Library Packages. These work the same as out-of-process packages but run inside the client process. However, I've seen problems with this approach, and it seems Microsoft provided this feature more for its own system services (like IIS virtual applications) than anything else. Running components in in-process packages has resulted in many weird and unexplainable crashes and lockups in my tests, where the out-of-process packages of the same exact configuration work fine. Your mileage might vary. In-process packages are slightly faster, but they also void the process-isolation features that protect the client process from a crashing component in your package. You might not be able to restart in-process packages, and they often require the calling process to shut down before refreshing or updating the components. I recommend to stick to Server Packages.

The state of the stateless

In order to use MTS and gain scalability benefits you have to think about building stateless components. Stateless objects are objects that don't rely on property values to maintain their "state" or context between method calls. These objects make no assumptions about any values for themselves or other "global" aspects of the system. They reestablish state themselves when they start up, by using parameters or values stored in a database or establishing state from scratch. The typical steps for a stateless object are:

 

1.      Receive a method call

2.      Establish state

3.      Run application logic

4.      Release state

5.      Return results to the client

 

The important thing here is that each method call cannot rely on property values being set a certain way when the method is entered. When a server is stateless the server has the ability to recycle or unload the object without the client noticing that underlying server reference is different. And this is exactly what MTS can do behind the scenes for you without you having to write any code to make it happen. This and many other tasks are the responsibility of the package. In theory, this is how MTS can abstract many scalability issues for you, so object management code does not need to be written by a client application. Your job is to create the object and forget about it – MTS will manage the component and makes sure that it's available when you need it. That's the theory anyway, but currently this functionality is very limited and as we'll see in a minute, also very inefficient. This may change in the future though.

 

Regardless, if you're going to build an effective component for use with MTS, it should be stateless. For MTS this means that when a method call completes it should be able to release the object and hand you a brand-new copy on the next request. In order for this to work, your component cannot rely on non-default property settings used by multiple consecutive method calls.

 

If the object is stateless, MTS can take the existing object reference and recycle it to pass on to another client. That's the theory, anyway, but in reality MTS destroys your object after each call to release state (which releases state for the object – more on this in a second) and then totally re-creates it when you make another method call, rather than simply caching existing references. MTS does not provide object pooling yet – this is a feature that likely will be provided in later versions of MTS. The current behavior provides a safe environment for legacy (non-MTS) components by always guaranteeing startup state to a stateless component that is not the optimal way to cache objects. Pooling on the other hand would simply keep an instance of an object alive and reuse that existing instance. Pooling is much more efficient, but potentially problematic for legacy objects that don't know about state and MTS context objects.

 

To manage state MTS introduces a concept called Context. The context object is essentially a reference to the MTS package, which maintains the state of the COM objects inside of it. A COM component can retrieve a reference to this context object with CREATEOBJECT("MTXAS.AppServer.1"). From the object the current package's context can be retrieved with the GetObjectContext() method. This reference lets you control the status of state by committing transactions using the SetComplete() or SetAbort() methods of the context object. When you call the latter methods MTS lets the object go stateless – it actually releases the object reference completely causing the COM object to unload.

 

Although objects should be stateless, you can also keep state in objects simply by not calling SetComplete() or SetAbort() at the end of a method call. If you do this, the object reference behaves like a typical COM object that is stateful – all the property values and internal object state remain intact between method calls until you finally call SetComplete or SetAbort. This is great for legacy servers that weren't designed for stateless operation to start with. However, stateful servers don't scale well. While servers have state MTS can't manage them. This means they tie up the resources they use and also could hang because of a lost connection or other failure from the client. Because the object isn't managed MTS can't trap these failures as it could with a stateless object.

VFP MTS Basics

To demonstrate how MTS works, I'll build a small functional sample that calls a simulated long-running method called SlowHit(). SlowHit() doesn't do anything other than wait for a specified number of seconds passed in as a parameter to simulate multiple simultaneously running requests on this server. To start, create the basic server code:

 

DEFINE CLASS MTSDemo AS Custom OLEPUBLIC

 

oMTS = .NULL.

hFile = 0

 

FUNCTION Init

  THIS.WriteOutput("Init Fired")

  THIS.oMTS = CREATEOBJECT("MTXAS.AppServer.1")

ENDFUNC

 

FUNCTION Destroy

  THIS.WriteOutput("Destroy Fired")

  IF THIS.hFile > 0

    FCLOSE(THIS.hFile)

  ENDIF

ENDFUNC

 

************************************************************************

* MTSDemo :: SlowHit

*********************************

***  Function: Allows you to simulate a long request. Pass number of

***            seconds. Use MTS transactions

************************************************************************

FUNCTION SlowHit

LPARAMETER lnSecs

LOCAL loMTS, loContext, x

 

THIS.Writeoutput("SlowHit Fired" + TRANSFORM(lnSecs))

loContext = THIS.oMTS.GetObjectContext()

 

lnSecs = IIF(EMPTY(lnSecs), 0, lnSecs)

 

DECLARE Sleep IN Win32API INTEGER

FOR x = 1 TO lnSecs

  FOR x = 1 TO lnSecs

     Sleep(1000)

  ENDFOR

ENDFOR

 

DECLARE INTEGER GetCurrentThreadId IN Win32API

 

IF !ISNULL(loContext)

   loContext.SetComplete()

ENDIF

 

RETURN GetCurrentThreadId()

ENDFUNC

 

 

FUNCTION WriteOutput

LPARAMETER lcString

IF THIS.hFile = 0

   Declare INTEGER GetCurrentThreadId IN Win32API

   THIS.hFile = FCREATE(Sys(2023) + "\MTSDemo"  +SYS(2015) + ".txt")

ENDIF

IF THIS.hFile # -1

  FWRITE(THIS.hFile, lcString + CHR(13) + CHR(10))

ENDIF

 

ENDFUNC

 

ENDDEFINE

 

A few optional pieces in this code deal with logging information to disk using the WriteOutput method —I'll discuss these later. The key feature of a COM object built for use in MTS is the retrieval of an object reference to the MTXAS.AppServer.1 object, which is the MTS base object. To retrieve the current package context for the currently active request, use its GetObjectContext() method and store it to a variable:

 

THIS.oMTS = CREATEOBJECT("MTXAS.AppServer.1")

loContext = THIS.oMTS.GetObjectContext()

 

I stuck the CreateObject() call into the Init event of the class so I don't have to retype the code that has to run in each call method. As you'll see in a minute, the Init of your server fires every time a method call is made to your object, so this is no more efficient than directly calling the same code in each method. However, it saves some typing and centralizes the code.

 

The only thing left to do in your server method code is to call either SetComplete() or SetAbort()—it doesn't matter which one you call, since this component isn't participating in any SQL transactions. To tell MTS that it's okay to release resources on the object, use:

 

IF !ISNULL(loContext)

   loContext.SetComplete()

ENDIF

 

Now create a project and add a PRG file (or VCX if you choose to go that route) named MTSDemos. Now compile this server into a COM object:

 

BUILD MTDLL MTSDemos FROM MTSDemos

 

If you don't have the new multi-threaded runtime included with SP3 (see sidebar), just use BUILD DLL instead—but realize your server will block simultaneous client requests – no concurrent access on that object will be possible.

 

Before dumping the server into MTS, use the following code to test it:

 

o = CREATEOBJECT("MTSDemos.MTSDemo")

? o.SlowHit(5)

 

This should run the server, wait for five seconds and then spit out a thread ID number. If that works, move this component into MTS by following these steps:

 

1.      Start the Microsoft Management Console.

2.      In the tree view, select Microsoft Transaction Server, then My Computer, then Packages Installed.

3.      Click the New icon on the toolbar and create an empty package named MTS Book Samples (the name doesn't matter really, but it matches the image below).

4.      Select the new package, press the right mouse key, and click Properties.

5.      Click the Identity tab and notice that the default is set to Interactive User, which is the currently logged-in account. If your component runs prior to login, the SYSTEM account will be used. Here you can also change the user account to a specific user account. Note: This setting determines the user context and the rights for what resources your component can access, regardless of the client. Save and exit this dialog.

6.      Go down one more level to Components and click the New icon.

7.      Click Install New Components and select MTSSserver.dll which we just created above. If you aren't using the new multi-threaded runtime, also add the MTSDemos.tlb file. Click OK.

8.      In the component's Properties, set its Transaction options. If you don't use distributed transactions through DTC, set the "Does not support Transactions" option, which will result in slightly faster performance  than the other options. Use "Supports Transactions" if you have a mixed-request load. Typically you'll want to set this to "Supports Transactions" The figure below  shows how the installed component should look inside MTS.

 

Figure 1.3 – The MTS Explorer allows you to manage packages and the components inside of them. Components are added to packages and you can configure both the package and each individual component. Note that you can see active object status in the Explorer to give you a quick view of activity. This is very useful in understanding on how MTS works.</