We're running BizTalk 2006 R2 32 bit and we have some Send Ports using the
WCF-Custom transport with wsFederationHttpBinding. For this binding we've
added a custom ClientCredentials behavior that creates SAML tokens using
some shared custom security assemblies where we override a couple of the WCF
security classes.
One of these classes is the
System.IdentityModel.Tokens.SecurityToken.GetTokenCore(TimeSpan timeout)
method. In this method, we're using the
System.ServiceModel.Security.WSSecurityTokenSerializer to serialize the SAML
token by calling the WriteToken(XmlWriter, SecurityToken) method.
Now the problem is that this single call to WriteToken() takes 220 ms when
using the security framework in the above BizTalk setup. However, when
running the same code in a pure .NET application, the overhead is down to 3
ms. This is consistent reproducable behavior.
Any suggestions as to what could cause this overhead?
Kind regards,
Claus
-Dan
Hi Dan,
First runs takes even longer, and its only that specific call in our
assembly that subsequently has the performance overhead.
Yes, we have a dedicated Send host instance for the WCF adapter.
We just tried reintroducing a "security wcf proxy service" that we
previously had because of an SAML issue we could not get to work in the
BizTalk WCF adapter. So instead of:
BTS (WCF HTTP Federation) -> Service
We now have:
BTS (WCF BasicHttp) -> IIS Proxy (WCF HTTP Federation) -> Service
The second setup runs 2-3 times faster even though we introduce a second wcf
service.
http://msdn.microsoft.com/en-us/library/aa561380.aspx
http://msdn.microsoft.com/en-us/library/aa560822.aspx
I just want to be certain you have isolated that the issue is in fact
this call and not something else. Maybe you should give the CLR
Profiler a go and see what it can find.
Kind Regards,
-Dan
We've traced (using DbgView) the code in such detail that yeah, the culprit
is that specific call in regards to performance overhead.
We actually have another call using a framework method somwhere else in the
security code that also has a performance overhead compared to when its
running in a WCF service in IIS. It takes about 111 ms to run under BTS and
< 1 ms to run in IIS. This method call is
System.IdentityModel.Tokens.SecurityKey.EncryptKey() and
it is called in a SamlManager class that is being accessed in an override of
the ClientCredentialsSecurityTokenManager class method
CreateSecurityTokenProvider(). Its purpose is to create a SamlAssertion and
SecurityToken based Proof Token.
The CreateSecurityTokenProvider returns an instance of an override of the
SecurityTokenProvider class. This class contains an override of the method
GetTokenCore() that returns a SecurityToken. It is in this method that the
beforementioned WsSecurityTokenSerializer.WriteToken() is called.
And again. Its just these two calls that show so great a performance
difference in BTS and IIS.
Since we generate a new SAML token for each outgoing call to services from
BTS, having a performance overhead of at least 330 ms per call is too much.
Regards,
Claus
Kind Regards,
-Dan
I'll give CLR Profiler a shot although it was my understanding that this
tool was primarily for memory related issues?
No, the time measured is a Diagnostics.Trace.WriteLine just before and after
the mentioned method calls.
Kind regards,
Claus
Just revisting this issue, since we seem to have narrowed it down a little.
The overhead only occurs when we're running the Send Port host instance
under a local computer account (ie "<computer>\BizTalkUser"). If we run the
host instance under a domain account the overhead disappears.
We also discovered that we could introduce the same overhead in a "normal"
WCF service if the service app pool is running under a local computer
account, so this is not limited to BizTalk. If we run the app pool under the
default Network Service account or a domain account, the overhead is gone
yet again.
Giving the local account membership of the local Administrators group
changes nothing - still overhead.
The domain user and the local user is members of the same local computer
groups.
The two C# operations that have overhead (where only one of them is
mentioned in the quotations above) both seem to relate to certificates, so
we tried installing the needed certificates to store location CurrentUser
instead of LocalMachine, but that did not help. FileMon does not seem to
show any overhead when the filesystem is accessing the private keys of the
certificate.
Conclusion: Not really sure why, but on our servers an overhead is
introduced when running custom STS code in WCF ClientCredentials under the
identity of local accounts. The morale of the story? Use domain accounts :)
Regards,
Claus
Also yes, great point, ALWAYS use Domain Accounts for anything but dev
systems.
Kind Regards,
-Dan
Hi Dan,
No haven't had the chance to test if this is the same under 3.5.
However, looking through the net I found other issues somewhat related to
ours. For example at
http://weblogs.asp.net/cibrax/archive/2007/10/08/certificate-access-error-for-wcf-hosted-services.aspx
it is mentioned that using accounts with user profiles not already loaded
causes issues duing serializing and encrypting of tokens.
Since the Network Service account is always loaded there might be some
overhead involved here (possibly loading local account user profiles), but
this would require that domain accounts are being handled differently than
local accounts since these do not experience the overhead. I haven't found
any info on that yet.
This of course is all just speculation - I haven't had time to dig in any
further.
Kind regards,
Claus