Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


18 Common Programming Traps in MTS : Page 2

This article contains 18 great tips for improving the performance of MTS applications as well as making their debugging easier. Whenever you start a new MTS application using VB5 or VB6, check out this list of the usual mistakes many developers make, regardless of their expertise level.


Problem 4: Not Using New
Very often when developers have heard about the problems with using New and MTS (and the trouble is that there is a risk that you won't get a context wrapper since the SCM (Service Control Manager) under some circumstances won't be involved), they stop using New even to create objects that are not MTS objects, such as ADO recordsets. Of course, CreateObject works too, but it is slower since the ProgID used with CreateObject has to be translated into a CLSID by the SCM. (Another drawback is that if you misspell the ProgID, the compiler won't tell you about it.)

Set rst = CreateObject("ADODB.Recordset")

is slower than

Set rst = New ADODB.Recordset

I did a little test by running a loop in an MTS object, creating 1000 ADO recordsets. When I used New instead of CreateObject in the loop, it went 200% faster.

Problem 5: Not Using SPM
Most MTS programmers have heard about the "SPAM" or SPM (Shared Property Manager), but few use it. (SPM could be used for sharing global data between different MTS objects, and users. See a textbook about MTS for more information.)

Let's say you have static data like the VAT percentage, then it is not a good idea to read it from the database all the time when you can cache it in SPM. When I did 100 calls from a base-client to an MTS object getting the VAT-percentage it was 130% faster to use the SPM instead of going to the database all the time.

My example is a very simple situation. You can easily come up with scenarios where the percentage will be much higher.

Problem 6: Believing that Objects Are Serialized When Moved Between Processes and Hosts
A common first approach to moving data is to use objects and collections of objects. This is good, but not when it comes to distributed systems. The reason is that the data is not serialized automatically. The universal marshaller can't do that. So you will only send references over the network and when a receiver of a reference calls the object, the real call will go by the reference back to the original location.

A very typical scenario where this is obvious is if you are in an MTS object and want to fetch 1000 rows from the database and for each row build an object (you are object-oriented right?) and add the object to a collection. When finished you return the collection to the client. At the client, inspection of four properties on each object will take place. This is slower than just returning a disconnected recordset to the client! (A disconnected recordset doesn't have the same marshalling problem. It will marshal just fine.)

Client code for the slow case:

Dim objPersons As TestApp.IPersonsCol
Dim objPerson As TestApp.IPerson
Dim colPersons As Collection
Set objPersons = CreateObject("TestApp.Persons")
Set colPersons = objPersons.GetAll()
For Each objPerson In colPersons
With objPerson
lngId = .Id
strFname = .FName
strLname = .LName
strPhone = .Phone
End With

Client code for the fast case:

Dim objPersons As TestApp.IPersonsRst
Dim rstPersons As ADODB.Recordset
Set objPersons = CreateObject("TestApp.Persons")
Set rstPersons = objPersons.GetAll()
With rstPersons
Do While Not .EOF
lngId = !id
strFname = !fname
strLname = !lname
strPhone = !phone
End With

The fast case is more than 3000% faster. The orthodox approach to object-orientation sometimes comes at a high price here, right?

Problem 7: Too Many Server Packages
Many developers think that an MTS package should be the same as a package in their UML (Unified Modeling Language) diagrams. So for example they will have MTS packages called Application, Domain and Data Access. If those packages are not library packages, there will be process communication between them leading to proxy/stub-pairs and a lot of unnecessary overhead.

When I tested this I had only two packages. The base-client did 100 calls to package A and in each call the operation did a call on an operation in another object (sending a ByVal string). If the second object lived in the same package as the first one, it was 1500% percent faster than if they lived in separate server packages.

You will not have this difference if the second object lives in a library package, since library packages load in the process of the first package.

Problem 8: Stored Procedures Not Useful Any More
This is a misunderstanding that occurs sometimes. Just because you have MTS and n-tier applications, it doesn't mean you are not allowed to use stored procedures. In fact, you will pay a performance penalty if you dont!

From a base-client I called an MTS object 100 times. Each time the object did three updates against the database. In the second case the object only did one call to a sp, which did the same three updates. The second case was 50% faster. And this was when I had MTS and the database server running on the same machine. Do you think the difference will be less if I move the database to another server?

Problem 9: Not Remembering That the Administrator Rules
It's not something you like to think about, but the administrator of the MTS server decides a lot about your application. J

If you design an MTS component for a specific transaction setting, then it could lead to a catastrophe if the transaction setting is incorrect. The same goes for security. A simple solution to this could be to check in the operation that you have the environment that you think, otherwise you raise an error.

If Not m_objCtx.IsInTransaction Then
Err.Raise jnskErrNotInTransaction, _
"TestApp.CriticalStuff", _
"This component is not correctly " & _
"installed regarding " & _
"transaction support!"
End If

You can use the same technique as shown above to check IsSecurityEnabled.

Dont forget that there are good reasons for the declarative way of handling transaction and such. Do not try to overrule that by code. I'm just trying to show that in specific situations, it could be good to know a little about the environment.

Problem 10: Catching Errors Without Setabort
It is very easy to make mistakes with error handling when you build an old client server application, but this will usually only affect one user at a time. If you make similar mistakes in distributed applications, you can affect the lives of hundreds of users, for example, by locking out a resource such as a table in the database.

Take this code snippet in a transactional MTS object for example.

strSQL = "UPDATE table SET column = 'value'"
con.Execute strSQL
'Let's assume an error will happen here
Err.Raise jnskErrSomething, "...", "..."

The above will cause something bad to happen. I didnt forget to catch the error, but the SetAbort will not happen since it comes in too late. Since the MTS object is transactional, a lot of locks will be held in the database until the object is deactivated or the transaction dies (by default after 60 seconds).

Let's assume the error is presented to the user in a message box. If the user presses OK within 60 seconds, the update will be committed. If not, all the locks (and there can be many in this case!) will be held for 60 seconds and then the transaction will be rolled back automatically.

You dont need this behaviour. Be very sure you catch all errors, use SetAbort and use it before raising the error to the client!

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date