logo Sabbasoft
 

logo VB2TheMax

logo Code Architects

Welcome to Sabbasoft

 

MTS FAQ

 

    Please note: what stated in the MTS FAQ may not apply to COM+ 

 

1)

  Why is dangerous the use of the New keyword in VB to create COM objects registered under MTS?
2)   What's difference between an MTS transaction and an MTS activity?
3)   When do I have to use CreateObject and when CreateInstance?
4)   Statefull - stateless programming - How to store state across transaction boundaries
5)   What is the best setting of the "Run as" option in a server package ?
6)   Should I use Createinstance or CreateObject for ADO objects ?
7)   MTS Internal flags, that is how MTS tracks transaction outcome 
8)   How do i debug components under the VC++ IDE ?
9)   What are the security extension features MTS adds to standard DCOM ?
10)   What about syncronization issues in MTS ?
11)   Can i administer the MTS Catalog in a a programmatic way ?
12)   I want to use MTS only for its component deployment facilities. Is there any issue I should be warned about ?
13)   Why is it a bad idea to use "Global" variables, that is Public (and private) variables at module level ?
14)   Problems exporting Packages with classes that implements interfaces defined in "external" Type Libraries 
15)   A few words from Michael D. Long about connection pooling
16)   When to call Set Complete ? 
17)   Where is MTS in W2K ?
18)   Everything works fine on both client machines when somebody is logged in on the server machine. But when nobody is logged in, we get an error.
19)   Why do I get an Access Denied Error when I try to raise an event from my MTS component to my client ?
20)   Connection pooling
21)   MTS compatibility of databases
22)   Debugging of components under the VB IDE
23)   Load balancing in MTS
 

 

1) Why is dangerous the use of the New keyword in VB to create COM objects registrered under MTS ?

The New keyword is the only way to go when you want to create objects that are defined as PublicnotCreatable or Private (CreateObject doesn't work).
As you know VB lets you use New to create Multiuse classes as well. Unfortunately the New keyword has a couple of drawbacks: 

1) In the situation where the caller and the callee are in the same EXE/DLL COM component VB perform an internal creation of the COM object bypassing the COM run-time (and the MTS run-time if the object is registered under MTS). 
While this fact is not a problem under out-of-MTS development, the situation gets very dangerous under MTS. What happens is that, since MTS is not notified of the object creation, the newly created object gets access to the ObjectContext of the father and MTS sees the code running inside method calls of the child object as "inline-code" of the father. I leave as an excercise to the reader what can happen if the child object calls SetComplete (against the ObjectContext of the father).

2) When you declare an object (Interface) with the syntax
Dim x as new xx 
VB checks every time the object is referenced in the code. If it is Nothing VB silently (re)creates it (This means you'll never get a "object variable or with block not set" error).  If you are not aware of this your code could follow unxexpected execution paths.

Consider the following code:

Public sub DoWork()   
dim x as new XX XX is registered as requires transaction
x.dosomemorework Vb creates it, the component ball start spinning in the MTS explorer, the transaction start
set x = nothing The ball stop spinning
if x is nothing then VB recreates it, the ball start spinning again, the transaction start again
Format Drive C this code will never be executed
end if  
  From here to when x goes out of scope x is activated (and probably you don't know this). 
End Sub  

2) What's difference between a MTS transaction and an MTS activity ?

  An activity is a logical thread of execution. An activity spawns across threads, processes (packages) and machines. An object shares the same activity with its father object (provided it's not the root of the activity) if the father used CreateInstance to create it. This is true no matter the father and child transaction settings are. The concept of activity is used by MTS to handle concurrent access and re-entrancy.
The objects involved in an MTS transaction are all part of the same activity.
The objects involved in an MTS transaction are a subset (that can match with the whole)  of the objects that share the same activity, this because there could be some objects that are declared as "Does Not Support Transaction".

 

3) When do I have to use CreateObject and when CreateInstance?

  You have to use CreateInstance if:
- You are in a component running under MTS AND the object you want to create is registered under MTS AND you want that the transaction (the activity in the most general case when no transactional objects are involved) flows from the father object to the child object.
You have to use CreateObject if:
- The caller hasn't got a TransactionContext, that is, you want to instantiate the root object of a transaction from the base-client
OR
- The caller has got a TransactionContext but you want that the child object lives in a new different transaction (read activity in the most general case). Note that you would have achieved the same effect if you had used CreateInstance but the child object had been declared under MTS as "Requires  New Transaction".

4) Statefull - stateless programming - How to store state across transaction boundaries

  The MTS programming model suggests to call SetComplete as often as possible to achieve scalability via the "release resources as soon as you can" paradigma. This is called "stateless programming" cause your object is destroied after each method call that invoked SetComplete (or SetAbort). There are lot of discussion & misunderstandings about this. 
As long as you don't want MTS to handle the transaction issues involved in DB writings operations this paradigma is just a reccomandation to scale, MTS doesn't force you to follow these guide-lines. 
If you want to use MTS as the way to coordinate the commit of DB operations (mark the objects as Support/Requires transaction) then the Setcomplete/SetAbort call as one more effect:  it makes your component vote for a commit of DB writings operation. 
I want to point your attention to what is said above: the same call has 2 effects that are (apparently) unrelated, we could reprhase it in this way: In the MTS nations if citizens want to vote then they they have to die (sounds like some south America places .. doesn't it ? :) )
By the way: If noone votes then the transaction is committed, but this is a very bad programming practice and it works only if the root object of the transaction is released by the base-client within the DTC transaction time-out (default 60 seconds)

After you have mastered this new programming model you realize that, nevertheless "stateless" is good, you can't develop an application that doesn't requires some pieces of informations that spawns across transactions boundaries in the business-logic tier. 
The Wintel platform offers you a certain number of different approaches/technologies to solve such issue but not ultimately the "good" answer. 
In the following are enumerated hopefully all the available options you have and point your attention to some general architetture issues to help you finding the "good enough answer" for the  particular requirements of your application. 

The "data" you need to store are divided into two categories:
- Store application level data / objects, cause you  may have objects that have a non neglettable start-up time or data that get a while to load from a DB.
- Store session specific data or objects of each base-client (the username and password at least !!).

Two main guide lines should be given :
- Stay away as much as you can from caching COM objects
- Stay really really away from the temptation of implementing a singleton object to store application data.
There are different, non trivial, threadind issues in COM that shows up when you want to "reuse"objects.
If you are not aware of them you will get you into serious troubles. Your application  would "randomly" crash or would slow down its performances. (more later)
Generally the alternative of caching object state is better.
- Stay away as much as you can from caching datas in a file system,  try instead to cache them in RAM.

About session data: you can decide what to store at the client side and what at the server side. The more you store at the client side the less you charge you server but the more you stress your network (these datas have to travel with each method call to the server)If you choose to store your session data in the middle tier you have two approaches available:
- Persist the data in their "native format"
- Put the data in a statefull component
If you choose the second solution you then have to decide if 
- The base clients holds a reference to this object during the session 
- You park the object somewhere (the GIT not the SPM!! see later) in the midlle tier and your business objects picks it up any time time they need it (implementing a ticket mechanism).

As you can see there are different architettural solutions. Here is now what you have available in the Wintel platform:
Please note: In the following the terms thread and apartment are interchangeable since we made the assumption that we are talking about VB COM objects that are STA COM objects.

Data chaching (in access speed order):
- SPM
- Registry, FileSystem
- DB

The SPM looks appealing but remember that:
- SPM datas has package wide visibility only. 
- They do not survive a package shutdown.
One solution that can partially works is to wrap in a com component the access to the SPM. Put this component in a package and set the package property "keep running when idle to true". In this way all the components in all the packages that pass through this wrapper component for the acces to the SPM will share the same set of variables.
There are still two issues:
- Your wrapper component could became a bottleneck
- If the package crashes (or you server as a whole) you have lost all the datas.
COM Objects caching :
- GIT (Global Interface Table)
- SPM : DON'T !!!!
and if you are developing under IIS:
- Application object
- Session object 

It sounds tempting to store COM objects in the SPM, doesn't it ? The problem is that the SPM let's you do so but then know you are getting into serious "random" troubles. 
The reason comes from the fact that STA (Single threaded apartment) COM objects (the only one that VB can make) have thread affinity. STA objects can be accesed only from the thread they were created in. Since the SPM ignores this COM rule what happens in a basic situation is:
- The base client creates COM object A and calls a method against it. In the method body object A creates COM object B and store it in the SPM, supposing that B threading model is compatible with A threading model, both these two objects are running in thread X. When your work is done you call setcomplete/abort and MTS destroy object A. 
Now the base clients calls another method on object A, MTS picks up a thread from its thread pool and recreates it probably in it, let's say in thread Y. In the method call A picks up object B and tries to use it .. BOOM ..  you are accessing B from  a thread different from the one where it was created. In low-load situation (like your development machine you could have a chance to have object A created the second time in thread X again .. so it seems that it works .. but it won't all the times, and not for user in production environment.

A good way to go is using the GIT (Global Interface Table). It basically works like this: you pass the GIT a pointer to an object and the GIT gives you back a cookie. Whoever in the same process has this cookie can ask for the object pointer back. It's apparently like SPM, BUT the GIT provides all the marshalling/unmarshalling operations that are needed when it detects that the object is required by someone running in an apartment that is not the one where the object in the GIT was created.
The GIT is not accessible by VB directly. Don Box has made a wrapper around it to let's you acces it in VB.

For Internet/Intranet development only:
IIS lets you store only MTA objects in the Application object (so VB developers are out).
IIS lets you store STA objects in the Session object. It sounds like a viable solution but there is a problem. IIS thread pooling mechanism is smarter than the SPM.  It recognize that the object you stored has thread affinity, as a consequence it  will force all subsequent methods calls of that session to go into the same thread where the object stored in the session objects resides. In such situation things will then keep on working but you will suffer performance problems since that session will be stucked into one thread. If that thread is busy doing other operation the client associated to this session will have to wait.

To summarize we can say that the GIT is the best way to goif you want to cache COM objects, just remember that the GIT is process (read package) wide. Different packages won't share the same GIT. Personally i wrapped the D. Box wrapper  around another COM object and deploied such COM object in a separate, dedicated package. I think it could work deploying the D. Box wrapper directly in a package but i didn't try. If you try doing this let me know if it works.

5) What is the best setting of the "Run as" option in a server package ?

  When you deploy COM objects in dlls  no security issues arise since dlls run in the process of the caller.
On the contrary, when you deploy your COM objects into an .EXE, you have to deal with such issues. 
(D)COM needs to know under what security principal (read identity) the process, where your COM objects lives in, has to be associated with. 
(D)COM wants to know other details as well, like who has the rights to launch and to access the COM objects and what's the lowest level of security your COM objects must accept when it negotiates a connection from a caller.
These options can be set at run-time (but you have to deal with DCOM APIs) or can be configured at deployment time using the Dcomfig utility. If there are no security settings defined for your Component then (D)COM uses default access permission. 

The settings you have available in (D)COM for the Run as setting are:
- As interactive user: The process run under the identity of the user currently logged in the system. This settings is OK only during the development phase. If noone is logged in the system the object creation will fail.
- As the launching User: The process run under the identity of the caller process. This is not an advisable setting to set up a manageable security policy.
- As This User: In this case you have to specify a local or domain user. This is the best setting to go under a production environment.

If what is described above is clear, there is just one more thing to know when you come to MTS: Each MTS server package is a separate surrogate process where your dlls run in. If you decide to deploy your dlls into an MTS Server package  you have to remember that every access to the COM objects implemented in such dlls will have to deal with (D)COM security if the caller is outside the same MTS package.

6) Should I use Createinstance or CreateObject for ADO objects ?

Use CreateObject or the New keyword to create ADO objects. They are not COM objects registered under MTS.

 

7) MTS Internal flags, that is how MTS tracks transaction outcome 
Most of the text of this FAQ (#11) is taken from the Ted Pattison article on MSJ - October 1999

The Above figure shows a diagram of the root object, its context wrapper, and the MTS transaction in which they are running. It also shows some important internal flags maintained by MTS. These variables aren’t directly exposed to you, but you should understand how they influence the MTS runtime’s decision to commit or abort the transaction.
The transaction as a whole maintains a Boolean value that indicates whether the transaction should be aborted. I’ll call this the doomed flag. This value is initially set to False when the MTS transaction is created. When this flag is set to True, the MTS runtime knows to abort the transaction.
Now, when does the MTS runtime inspect the doomed flag? And how can you change the value of the doomed flag?
To answer these questions you must understand that the root object plays a very important role in every MTS transaction. There are two cases when the MTS runtime inspects the doomed flag: when the root objects returns control to its caller and when the root object is deactivated. Let’s look at these two cases and see what the MTS runtime does after inspecting the doomed flag.
The first case is when the root object returns control to its caller. The flow of execution always passes through the context wrapper first. When control passes through the context wrapper, the MTS runtime inspects the doomed flag. If the doomed flag is False, the MTS runtime doesn’t do anything. If the MTS runtime finds that the doomed flag has been set to True by one of the objects inside the transaction, it aborts the transaction and deactivates all of its objects except for the root object. As you will see, this situation is undesirable. The transaction has been aborted, but the root object remains activated in a futile state. You can avoid this by following the rules I’ll outline later.
The second case when the MTS runtime inspects the doomed flag is when the root object is deactivated. When the root object is deactivated during an active transaction, the MTS runtime inspects the doomed flag and releases the transaction by calling either Commit or Abort. If the doomed flag is set to False, the MTS runtime calls Commit; if the flag is set to True, the MTS runtime calls Abort.
....
There are two flags maintained inside the context wrapper for every MTS transactional object. The first one is the happy flag. It has an initial value of True when the context wrapper is created. When an object running inside an MTS transaction is deactivated, the MTS runtime examines its happy flag. If the happy flag is set to False, the MTS runtime sets the transaction’s doomed flag to True. Once the transaction’s doomed flag is set to True, it can’t be reversed. This has a powerful implication: if the root object or any other object inside a transaction is deactivated in an unhappy state, the transaction is doomed to failure....
In addition to DisableCommit and EnableCommit (I omitted the description of DisableCommit and EnableCommit cause they are generally not really usefull in MTS components design), two other important methods let you control your transaction: SetComplete and SetAbort. Like the other two methods, these cast a vote by modifying the happy flag. SetComplete sets the happy flag to True, while SetAbort sets it to False. SetComplete and SetAbort are different from the other two methods because they set the done flag to True. As you’ll see, the done flag has a dramatic effect.
If the done flag is set to True, the MTS runtime deactivates the object. Therefore, when an MTS object calls SetComplete or SetAbort, the MTS runtime deactivates it upon return of the method call.
It’s important to use SetComplete and SetAbort at the appropriate times. When you cast your vote by calling DisableCommit or EnableCommit, the transaction and all its locks are held until the base client releases the root object. Calling SetComplete or SetAbort is much better because the root object forces the MTS runtime to release the transaction. 
In other words - (mine)- calling SetAbort/SetComplete at the proper time guarantee you that your component will scale, it's no longer responsability of the client to release server objects as soon as possible. Even if the clients using the server component are not designed with distribution and scalability in mind they will not overload the server.  Suppose hundreds of MTS objects instances spinning while the users are out having a coffe (cause the UI programmer will sure have declared your MTS objects as instances variables and created them in the OnLoad event of the MDI form, didn't he  ? ;) )


8) How do I debug components under the VC++ IDE ?
First read
Q166275 - HOWTO Debug a Native Code Visual Basic Component in VC++
from the Microsoft KB, then read the following tips that swhite@barkbark.demon.co.uk kindly let me publish in these FAQ.

Some notes I made about this. Hope they help.


Can't debug? It's recommended that you create an NT user under which your MTS package(s) run. If you do this, make sure you switch your compent's package back to Interactive user (on the Identity tab) before you attempt to step into your code to debug it. Otherwise, the debug session will stop as soon as it has started with no apparent reason why.

Can't debug? Make sure any components you wish to debug are registered with a Server Package and not a Library Package in MTS. For instance, in production the Data Server will be registered with a Library Package so that it will activate in its Client's process, thus preventing cross-process calls. But to debug it, change its Package activation to Server.

Can't debug? On the Debug tab of your project's Settings, set the executable to C:\WINNT\system32\mtx.exe, and the Program arguments to /p:"Your Package". If you wish to debug data and business servers at once, open two instances of Visual C++ and set the packages accordingly.

Can't debug? The only way to placate the bad-tempered god of MTS debugging is to remember to go through the laborious ritual of: confirm packages are Library, confirm Debug Project Settings are correct, build data and business servers, Refresh All Components, Shut down Server Processes on My Computer, Shut down each Package you're debugging, set breakpoints in each project you wish to debug, start debugging (F5) in each project you wish to debug, and finally open your Client and start debugging. If the debugger doesn't stop at your breakpoints then make sure MSDTC is running. If it is then reboot and start again. If that doesn't work, format and rebuild NT (I'm serious!).
It's worth it in the end.


9) What are the security extension features MTS adds to standard DCOM ?
MTS security model is based on Roles. Roles are entities under which you gather together a number of NT users that are logically equivalent regarding the security requirements of your component.
This means that the MTS security model is based on the assumption that the original caller identity is available when you hit the package boundary.
In a distributed environment this security model seems to be clashing with the way COM passes identity principals across processes and hosts.
Suppose Alice in the host A makes a call to a COM object in hosts B running as BOB. BOB authenticates Alice, enter a new thead and calls CoImpersonateClient using Alice identity token. Now within this thread there is an outgoing call to a COM object running as Charlie in host C.
Since COM knows that NTLM (the WNT 4.0 built-in authentication protocol) doesn't support cross-host delegation, it calls into Charlie using the Process token, not thread token.
MTS role based security model seems then to turn completely unusefull (think about components accessed from IIS where the process token is IWAM_MACHINE for all the users that surf into your App from the Web). 
Clearly there is a way out from this. It turns out that MTS has an interesting feaure that uses to fix this problem. If your process token is included include into the "MTS Impersonator" alias (added as a local group by the Option Pack setup) or if you are running under the system account, MTS makes some interesting out-of-band work to propagate via the objectcontext the thread token to the process of the callee. This is the identity token MTS uses to perform its declarative and programmatic (IsCallerInRole) security checking. In this situation COM and MTS will disagree on the identity of the caller but since you have developed your Application security policy using roles this is of no concern for you. 


10) What about syncronization issues in MTS ?
MTS keeps under the same Activity ID calls that flow through the same logical thread of execution. Thus the Activity ID flows correctly through process and host boundaries. Unfortunately MTS cannot guarantee more syncronization features than the ones that are provided by the STA threading model, that is, it guarantees that all the objects within the same process (package) running in the same Activity (that is STA) will not be accessed concurrently. BUT when your activity flows cross process and host boundaries MTS doesn't guarantee you that metod calls within the same activity will be syncronized. Anyway such a fact is unlikely to be a problem if your client is not a multithreaded one  (like standard VB client App). (thank to Doctor Richard Grimes' book about MTS programming for this explanation) 

11) Can i administer the MTS Catalog in a programmatic way ?
The MTS Catalog can be admnistered in a programmatic way via the MTS 2.0 Admin Type Library implemented in the MTXADMIN.DLL. There is a lower-level access interface exposed by the MTS 2.0 Catalog Server Type Library (mtxcatex.dll) but you can't fully access its funcionalities from VB cause it exposes some non-Automation compatible method signatues ([out] SAFEARRAY).
The hierarchical object model exposed by the Admin Type Library to map the catalog structure is kinda unusual, different from the ones you are used to work with. 
The whole MTS catalog is exposed using only three classes (that implement a single interface):
- Catalog  
- CatalogCollection
- CatalogObject

To connect to an MTS catalog you use the Connect method specifing the server name.
To get an item (a CatalogObject) from a CatalogCollection you use the Item(n) method. The Item method accepts only numeric values and is zero based.
To get the child CatalogCollections of an Item you have to call the GetCollection method on the CatalogCollection the Item comes from (this is the unusual stuff i was talking about before).
The GetCollection method needs to parameters:
- The Name of the requested collection type (Packages,Roles, etc.)
- The key of the father item 

The GetCollection method doesn't populate the collection. You have to call the Populate method to do this.
To persist changes to the Catalog you have to call SaveChanges

 I know it's kinda complicated so i drop some lines of code.

Dim Cat as New Catalog
Dim CatCollRoot as CatalogCollection
Dim CatCollPackages as CatalogCollection
Dim CatCollComponents as CatalogCollection
Dim CatObjPackage as CatalogCollection
set CatCollRoot  = Cat.Connect("MyServer") 
'Pass an empty string to connect to local catalog
CatCollRoot.Populate
set CatCollPackages = CatCollRoot.GetCollection("Packages","")
CatCollPackages.Populate
Set CatObjPackage = CatCollPackages.Item(0)
Set CatCollComponents = CatCollPackages.GetCollection _
("ComponentsInPackage",CatObjPackage.Key)

As you can see, in the case of the root collection the second parameter of the GetCollection method is blank. This happens when the requested collection is hierarchicaly a child of another collection and not of an item. This applies for:

- The Root collection you get from Catalog.Connect
- The RelatedCollectionInfo collection (see below)
- The RelatedPropertyInfo  collection (see below)
- The ErrorInfo collection (not mentioned here)

To facilitate the navigation in to this "poored typed" object model there are two helper CatalogCollections available:
- RelatedCollectionInfo: Gives you the name of all the child CatalogCollections available
- RelatedPropertyInfo: Gives you the name of all the properties that the  items that are part of the collection expose.
Using these two helper classes you can reconstruct the Catalog structure at run-time.

CatalogCollections let you navigate, enumerate,remove and, in some cases, add objects in the catalog. To perform a number of tasks that need more info to be accomplished you need to use some Utility objects.
You get access to such objects invoking the GetUtilInterface on the appropriate CatalogCollection.
The Utility objects are:

- PackageUtil (GetUtilInterface on the Packages collection)
- ComponentUtil
(GetUtilInterface on the ComponentsInPackage collection)
- RemoteComponentUtil
(GetUtilInterface on the RemoteComponents collection)
- RoleAssociationUtil
(GetUtilInterface on the RolesForPackageComponent or RolesForPackageComponentInterface collection)

12) I want to use MTS only for its component deployment facilities. Is there any issue I should be warned about ?
Let's suppose you have a bunch of COM DLLs that have not been written with MTS in mind. Now you want to deploy these dlls into an MTS server package just to easy the pain of 
- turning them into EXE COM objects 
- use Dcomfig.exe 
to have them accessible via DCOM remotely. 
This means that your COM objects will be registered as "Does not Support Transaction" and they will never call any MTS API method. 
Probably this is not a good choice from an architectural point of view. If your interfaces have not been designed with distribution in mind, they will perform very poorly over the network, but this is not the point i want to stress out here, cause i want to answer to the following question here: will my COM objects will work "as before" after i deployed them under MTS ? The answer is generally yes, they will work as before but with much less performance. Each call to Createobject starts a new activity when the created object is registered under MTS. A new activity means the creation of a new STA where the created object is placed. I suppose there are a lot of cross activations & calls among these dlls you have deployed under MTS. It turns out that what was running under the same thread/STA before the deployment, now spans plenty of activities (threads); so that the code flow is slowed down by lot of cross apartment comunication. Unfortunately there is one situation where things gets even worst: when you use disconnected recordsets (that marshal by value) in your dlls. When disconnected recordsets are passed among components they change their behaviour depending on the fact that they are marshaled or not (check out the visible number of records in a filtered recordset before and after being marshaled, as you have suspected, the filter property is lost after the marshaling, all records are visible again). In such situation, then, you Application can behave differently, not only performance more poorly.
Conclusion: quick and dirty deployment under MTS is most of the time not a good choice. 
Changing CreateObject with GetObjectContext.CreateInstance at the proper places is a must to have things working in an acceptable way. 

13) Why is it a bad idea to use "Global" variables, that is Public (and private) variables at module level ?
All variables at module level are thread specific. Each new thread starts get a new fresh & uninitialized copy of such variables. I've verified that all the activities that are born from the same object context always run in the same apartment (I checked the thread id) as the object context had thread affinity.
What i mean is this: suppose you have an mts object that has a method that returns the thread id and then calls set complete. Each call to the method starts a new activity but the thread id returned is always the same. This could sound tempting to store some state across activity boundaries but since the number of thread available for a package is limited (100 by default) you would end up having, under heavy stress, different activities sharing the same apartment and then the same public .bas variables. with unpredictable results. 

14) Problems exporting Packages with classes that implements interfaces defined in "external" Type Libraries 
MTS Packages are exported for two reasons:
1) Clone the package on another server
2) Produce a setup program that you run on the client computers to configure in a way they can access the server components via DCOM

This procedure is generally a "one click" one, but there are some problems when your classes implements interfaces that are defined in external Type Libraries. When you export the package these Type Libraries are not included in the export directory. This makes unusable the export files for both uses i described above.
There is a simple workaround if you want to exported the package for use #2: just copy manually the type libraries in the export directory, the tlb files will be copied and registered on the client machine.
This tecnique doesn't work for case #1: you cannot successfully import a package on another server even if you have copied the tlbs in the export directory (you got an error). I don't know any workaround for this at present, you have to recreate the package on the server. To easy this problem you can develop a VB program that programmatically register dlls and type libraries on the target computer and then programmatically create the package using the MTS 2.0 Admin Type Library. I've developed such a VB program; it's not an 'All purpouse' one but it's a good starting point you can customize, write me if you want the source code.

15) A few words from Michael D. Long about connection pooling

With MDAC 2.1 SP2 and prior, I have observed that when relying on the OLE DB Provider for ODBC that connection pooling does not appear to work with SQL Server when the OLEDB_Services entry for MSDASQL is set to the default. My conclusion is based on two observations - first, the timing of the .Open method of the connection object and second, the data presented in Magagement \ Current Activity \ Process Info of SS7 Enterprise Manager.
Simply changing the OLEDB_Services entry for MSADSQL to 0 resolves the issue. As a side benefit you can see activity in the ODBC Connection counters under the NT Performance Monitor.   

There is a second issue that renders the connection pool ineffective, which is load related. In a heavily utilized environment new connections are handed out even though there are free connections available. 
A portion of this can be explained by "free" connections that are actually enlisted in a transaction, thus these connections can *only* be handed out to a client participating in the same transaction. However, my extensive work with resolving Oracle session leaks has shown that multiple connections are enlisted in a single transaction - as the test code is written this should *never* happen. I have discussed this with Microsoft Support and they agree that this is most likely due to a "race condition" - the connection pooling manager does not mark the released connection available fast enough. 
This results in the thread responsible for locating an available connection creating a new connection, as none are yet available. 


The only solution that I have found is to move to Windows 2000 and build pooled objects that maintain an active connection and perform manual transaction enlistment. This is effectively replacing the connection pool, and as Joe Long (Microsoft employee) has recently pointed out, it can also have positive effects on performance in general. Scalability research on my part indicates that avoiding use of the connection pool results in up to a 2.5x performance gain.  

----------

Follow up

thank michael, a couple of additionqal questions : is there any problem related to mdac 2.1 sp2 using nativeoledb provider for sql server ? about the 2nd problem u talk about, does this applies to both sqloledb & msdasql providers ?  

I have not performed very much testing with OLE DB for SQL Server with MDAC 2.1 SP2 and prior, as the performance was not up to the level of the equivalent ODBC driver.   The second issue applies to both OLE DB and MSDASQL providers. When running against Oracle the same type of session loss was observed, but at a much higher frequency. This appears to be better in MDAC 2.5, though all Oracle sessions will eventually bleed out (no matter how high the value is set) when under heavy load. I have submitted a repro that demonstrates similar behavior with SQL Server 7 SP2 to the MDAC Beta group, and with some luck the issue will be resolved either in 2.6 or the following release.  

16) When to call SetComplete ?

Received from Joseph Geretz


SetComplete does NOT inform MTS that the object is ready for deactivation.
SetComplete informs MTS that *when control returns to the client* (i.e.
control returns to the Context Wrapper on its way back to the client) MTS
should deactivate the object.

The practical implication of this is that, if you are coding an object which
contains a suite of non-transactional stateless methods, you can enforce JIT
activation by coding a single SetComplete in the ObjectControl_Activate
event. (In a non-transactional context, there is no effective difference
between SetComplete and SetAbort, so you can 'pre-call' SetComplete with
impunity.)

17) Where is MTS in W2K ?
MTS is a set of services (object brokering and Transaction coordination) built on top of COM. It is available on WN4 if you install the Option Pack.
These services have been brought inside the new release of the COM Services, that are named COM+. COM+ is available in the W2K platform, you don't need any additional software.  

18) Everything works fine on both client machines when somebody is logged in on the server machine. But when nobody is logged in, we get an error.
If your package is set to run under the identity of the  selected "InteractiveUser" it means that it has to run under the identity of the user that is currently logged in. If no one is logged in, there is no "Interactive User", so the call will fail. "Interactive user" is usefull only for debugging on a developer machine.

19) Why do I get an Access Denied Error when I try to raise an event from my MTS component to my client ?
The client executable doesn't have the proper DCOM security settings to let the server call it back. In order to have it working you have two solutions:

1) Open wide DCOM security settings on the client side

2) Add the proper DCOM security settings for the client executable in the registry (you cannot call CoInitializeSecurity in a VB client). 
a) Add a new key with the name of the client executable under the AppId key 
b) Generate a GUID with guidgen.exe
c) Add a key with such value under the AppId key
d) Add a string value under the key created in step a whose name is AppID and the value is the generated GUID vaue
e) Add the proper security settings under the GUID key.

Example:

REGEDIT4] [HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\yourclient.exe] @="YourClient" 
"AppID"="{YourGuid}" [HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{YourGuid}] @="YourClient" 
"AuthenticationLevel"=dword:00000001

See http://msdn.microsoft.com/library/psdk/com/reg_33y1.htm for further info.

 

20) Connection Pooling
Connection pooling is an essential feature for distributed scalable applications. Note that database connection pooling is provided by the ODBC or OLEDB drivers not by MTS/COM+, still it's in MTS or COM+ that connection pooling shows its greatest benefits. Connection pooling reduces overall overhead that appears due to rather often open/close operations on database connections. But, to ensure that an application (read - component) is "pooling compliant", design and implementation of the application should consider following restrictions:

1) Connections should be opened using the same identity (UserName/Password).
A connection opened with one identity cannot be reused for a request with another identity. Opening connections with different identities increases pool size and makes its usage worthless.
2) Connection can not be reused across processes. Hence each MTS package holds it own pool of connections.
3) Underlying database should be "MTS compatible".
4) ODBC driver(s) or OLE DB Provider(s) being used should support MTS.

MDAC versions related issues:

1) MDAC 1.5.   Provides only (?) ODBC pooling
2) MDAC 2.0.   OLE DB pooling is disabled by default for OLE DB Provider for ODBC. ODBC pooling is being used
3) MDAC 2.1.   OLE DB pooling is being used for all providers by default. OLE DB connection pooling timeout has a fixed value - 60 seconds.
For ODBC sources you can get back to ODBC pooling by deleting OLEDB_SERVICES registry entry. To do it - run REGEDIT utility, choose Edit/Find, type "Microsoft OLE DB Provider for ODBC Drivers" (omit quotes). Find will show
entries under the provider's CLSID. Click on the CLSID and delete OLEDB_SERVICES value (not just set to zero).
4) MDAC 2.5.  In beta stage, not released yet. Overall performance will be increased (at least Microsoft says so). OLE DB connection pooling timeout will be changeable.


21) MTS compatibility of databases
The phrase "a database is MTS compatible" generally means that the database supports distributed transactions (XA or OLE DB protocol) and there is an ODBC driver or/and OLE DB provider that could be used by a component in MTS environment. The main requirement for an MTS compliant ODBC driver is to be thread-safe.
If a database doesn't support MTS, one won't get advantages of MTS transaction management architecture. Though it's still possible to perform any operations on the database and use "traditional" BeginTrans/Commit/Rollback.

Current list of databases that support MTS:

Microsoft SQL Server 6.5
IBM DB2
Informix
Oracle 7.3.3 and later releases
Sybase Adaptive Server Enterprise

Very often developers mistakenly are trying to use MS Access as an underlying database. MS Access does not support MTS.


22) Debugging of components under the VB IDE

It was and it is easy to debug MTS components written on Visual C++ version 5 and higher. Debugging components written on Visual Basic 5 is possible under VC IDE. Since version 6 Visual Basic allows to do debugging under its own IDE. Things that a developer should keep in mind while preparing to debug session:

1) Set appropriate timeouts. If you're going to drill your code down thoroughly it is almost obvious that you'll need longer timeouts on connection pooling and package idle time.
2) Package of the component must be shut down before debug session, i.e. before pressing F5 button don't forget to do "Shut Down" action on the package from "Transaction Server Explorer"
3) Debugging VB5 components in VC IDE sometimes is almost worthless because of bad variables visibility.
4) Debugging VB6 components is possible only with WinNT4 Service Pack 4 installed and is not possible on Windows 9x platform. The proper settings for the software and other issues are well described in VB6 Readme file (READMEVB.HTM)
5) It's better to debug  VB6 components having them removed from MTS environment, otherwise you'll need to reinstall the components in MTS (when debugging, VB6 replaces registration information of the component and, for example, instead of "C:\MyComponents\MyModule.dll" you'll see "C:\Program Files\Microsoft Visual Studio\VB98\VB6DEBUG.DLL" registered under MTS).
6) "Stateless component remains activated after a call" issue. When debugging MTS components under VB IDE an MTS component might be shown as "activated" in MTS Explorer even if you're sure that this component is stateless (i.e. you call SetComplete or SetAbort during each method call). It is neither an MTS glitch nor a developer's mistake. This happens because of VB IDE. During debugging (especially in step by step mode) VB calls some interfaces of the component. Also, if by any reason you decided to put the component in the watch window, don't be surprised as well.


23) Load balancing in MTS
MTS2.0 does not provide load balancing (LB). Microsoft suggests us to implement custom LB. For example, by dividing users into the groups and referring those groups to different MTS nodes. Or, in case of usage of IIS, implement "smart redirecting" among different IISs (like it's done on hotmail.com).
COM+ has built-in LB. It is realized as a component wich receives client requests and redirects them to a less loaded server. This component may be replaced by a custom component which provides more complicated custom rules of LB.

 

Comments, suggestions .. whatever .. drop me a line if you wish 

 

You are visitor number Since 2001/01/01