3

We have some COM+ code written in everybody's favourite language(?) -- VB6. This COM+ component calls into a standard COM component written by a 3rd party which performs calls into a SQL Server database. We don't do anything fancy in the COM+ -- nothing more than (this is an example only; we don't really call our functions doStuff :-) ):

Function doStuff
   Dim o As Library.Object
   Set o = New Library.Object
   str = o.DoSomething()
   Set o = Nothing
   doStuff = str
End Function

As a horrendously quick stress test, we've wrapped the call up in a drop-dead simple VBScript that simple creates object, calls method in a loop, sets object to nothing, and repeats this a few times. We then run four of these concurrently within a command prompt.

What we experience is the four COM+ windows stopping dead as if they've blocked each other in some way. Based on the behaviour of the output it looks like the different windows are sharing instances of objects somewhere along the way: for example, the speed at which the output appears between windows syncs between the windows ... so two can be blistering along at speed, whilst the other two spit out a line a second (and when they spit the line out, they do so at the same time).

Then ultimately, all four windows seem to stop dead - within component services, we see the Call Time start to climb (so from the few milliseconds per call, it climbs to 30, 40 seconds). Sometimes dllhost.exe fails and we get a COM Surrogate error dialog appear (at which point the window recovers as a new dllhost is spawned).

There is no activity on the database, so we've ruled out that things are blocking at the database layer. We seem to have achieved better results by setting the COM+ component to "Transactions: disabled", but the hangs don't disappear. Rather than new, we're going to try creating the COM object with CreateObject to see what that does (if anything). Objects are set to Nothing once finished with both in COM+ and at the VBScript layer.

Worth noting that if the 3rd party library is called directly from VBScript (bypassing COM+), no issues occur. Thus it seems as if it's something to do with the way that COM+ interacts with COM objects, but other than fiddling with different settings under the objects properties in Component Services, not sure really what else is happening.

Any suggestions as to what's happening under the hood to cause this? Or settings to tweak?

Extra Info
In answer to questions in answers:

Further work... It looks like it's a sync issue somewhere deep in COM+ or COM. In our test script, if we add a random delay of 10-50ms on each iteration, the problem disappears. If we have a fixed delay, we lock. Some googling around seems to show that it can be a problem on heavily loaded COM+ with an STA, which is documented here on an MS blog. It'd be nice to go back to a Server 2000 box or a Server 2003 SP1 box: that might have to be the next thing to see...

3 Answers 3

2

It sounds like you are potentially hitting an issue with COM+ and STA out of apartment calls.

Microsoft used to have a great article published by Michael McKeown called "Preserving Application Performance When Porting from MTS to COM+" discussing this but it looks it has been removed (there is an archived version here).

Basically, the COM+ STA thread pool binds up to 5 activities to each STA thread. When you make an out of apartment call (3rd party component or SQL Server) COM+ allows other requests to be serviced as another activity on the STA thread. This can happen for up to 5 activities (per thread). Also once control has been given to another activity the original activity cannot regain control until the second activity is complete. Under heavy load and/or if the calls are "long running" then the time for the first activity to complete is the sum of the times for all of the other activities (on the thread) to complete. This can kill your performance.

If you are able to switch the setting for your entire COM+ server you can configure COM+ to use the old MTS 100 STA thread approach. See Registry key for tuning COM+ thread and activity for the details. You can see if that helps your performance. The other approach would be to avoid STA components.

Sign up to request clarification or add additional context in comments.

1 Comment

This is pretty much the conclusion I'd got to based on google and experiementation... nice to have it confirmed though. Typically the calls should not be long running (and aren't in normal operation) -- so we're hitting some limitation that basically breaks the thread pool marshalling. Need to review whether dropping the server into the old approach is the best way to proceed: I suspect we'll try and avoid it by not hammering the server (after all, we only hit it during a potentially artifical load test) unless we reach no other option.
0

Perhaps COM+ Object Pooling Concepts and related articles like Configuring a Component to Be Pooled will be helpful.

Poolable objects must meet certain requirements to enable a single object instance to be used by multiple clients. For example, they can't hold client state or have any thread affinity.

3 Comments

As this is a VB6 COM+ object, it's not possible to set pooling options as VB6 COM+ objects are STA only. The 3rd party component is not registered in COM+ -- it is a standard COM component.
Well that's true enough. Could it be related to use of global variables in the VB6 code? support.microsoft.com/kb/815053
Nope -- checked for that. We've also double checked the Unattended Execution flags and Retained In Memory are set correctly as per support.microsoft.com/kb/264957
0

2 things come to mind:

  1. Have you tried making the o variable local instead of module level?

    Function doStuff Dim o as Library.Object
    Set o = New Library.Object str = o.DoSomething() Set o = Nothing doStuff = str End Function

  2. Are you that the Library.Object component and the .DoSomething method does not contain global variables (or MessageBox statements)?

  3. Can you throw logging statements after each line to see where the code chokes?

  4. Hit up ProcMon to see when it stops hitting the registry. Did the last call fail? If so, to where?

1 Comment

1. Does exist, just missing [fixed now]; 2. No MessageBox - the DLL isn't designed to interact with the UI directly - globals don't know; 3. & 4. Something we may need to do: my suspision is that it's the call into the 3rd party lib though... as an aside, just added more information to the question: it appears COM+ with STA can lock up in some circumstances under heavy load...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.