Open iSazonov opened 4 years ago
Tagging @safern, @viktorhofer as an area owner
@tquerec does this sound right? Perhaps the two different methods are considering a broader set of results than is expected.
UserPrincipal.FindByIdentity seems to be doing some sort of query on each network interface. Disabling the 'Client for Microsoft Networks' protocol on my VPN interface reduced the time for FindByIdentity from several seconds down to several milliseconds. I found this workaround here: https://stackoverflow.com/questions/7533790/findbyidentity-performance-differences
Has anyone profiled the problem yet? If it is due to slow network interface querying, perhaps we should create an external issue with Windows.
I have found the same issue. Disabling Netios in TCP/IP helps to some extent, but the acutal issue is this call stack which tried to lookup for any passed user/group the domain controller via Netbios, regardless if the TCP Netbios settings are enabled or not.
System.DirectoryServices.AccountManagement.ni.dll!System.DirectoryServices.AccountManagement.SAMStoreCtx.FindNativeByNT4IdentRef(System.Type, System.String) |- System.DirectoryServices.ni.dll!System.DirectoryServices.PropertyCollection.get_Item(System.String) | System.DirectoryServices.ni.dll!System.DirectoryServices.PropertyValueCollection..ctor(System.DirectoryServices.DirectoryEntry, System.String) | System.DirectoryServices.ni.dll!System.DirectoryServices.PropertyValueCollection.PopulateList() | |- System.DirectoryServices.ni.dll!System.DirectoryServices.DirectoryEntry.get_AdsObject() | | System.DirectoryServices.ni.dll!System.DirectoryServices.DirectoryEntry.Bind() | | System.DirectoryServices.ni.dll!System.DirectoryServices.DirectoryEntry.Bind(Boolean) | | System.DirectoryServices.ni.dll!System.DirectoryServices.Interop.UnsafeNativeMethods.ADsOpenObject(System.String, System.String, System.String, Int3 | | System.DirectoryServices.ni.dll!DomainBoundILStubClass.IL_STUB_PInvoke(System.String, System.String, System.String, Int32, System.Guid ByRef, System | | activeds.dll!ADsOpenObject | | adsnt.dll!CWinNTNamespace::OpenDSObject | | adsnt.dll!GetObjectW | | adsnt.dll!GetGroupObject | | adsnt.dll!ValidateGroupObject | | |- adsnt.dll!WinNTGetCachedDCName | | | adsnt.dll!WinNTGetCachedObject | | | adsnt.dll!DsGetDcNameNTWrapper | | | logoncli.dll!DsGetDcNameW | | | logoncli.dll!DsGetDcNameWithAccountW | | | |- logoncli.dll!DsWsaGetDcName | | | | logoncli.dll!DsIGetDcName | | | | logoncli.dll!NetpDcGetName | | | | |- logoncli.dll!NetpDcGetNameNetbios
The NetDcGetNameNetbio waits for 40s until it times out. That happens even when you disable Netbios in TCP/IP settings.
Is there some way to disable this call somehow, when no NetBios server is available?
Later calls are cached so this impact is not always present, but during boot it will delay things a lot.
Update After disabling NetBios over TCP on ALL network interfaces the issue did go away.
Is there a particular reason to .NET not having Principal.FindByIdentityAsync?
Is there a particular reason to .NET not having Principal.FindByIdentityAsync?
I think it's because the underlying Win32-API calls are not asynchronous (e.g. network calls with IO Completion Ports).
See https://learn.microsoft.com/en-us/windows/win32/api/lmaccess/nf-lmaccess-netgetdcname
.
Well you could wrap the synchronous call in an async Task (e.g. Task.Run / Task.Factory.StartNew), but that's not "truly asynchronous".
@pierophp:
Except for network communication I would not use truly async calls. In my experience async is just a synonym for making obvious delays harder to analyze for no gains. In the end you have threads waiting on your behalf without any context. If that pattern is so good why does the underlying OS use almost everywhere blocking synchronous calls? Things quickly fall apart of you mix async calls with parallel loops and hope for performance improvements. If you try to e.g. write from dozens of threads to a single disk to multiple files you will find out that a single thread will perform much better. There may be gains if you go multi threaded but you need to measure first how much async IO the underlying storage can efficiently handle.
FindByIdentity is network communication. It's an LDAP query to a directory server.
FindByIdentity is network communication. It's an LDAP query to a directory server.
That's right, but the underlying Win32-call is a synchronous call. I haven't found an async equivalent for this. So if you wan't it async, it's just blocking wrapped in a Task.
In PowerShell repo we started porting Microsoft.PowerShell.LocalAccounts module to System.DirectoryServices.AccountManagement API (from p/invokes). We had to use workarounds because Principal.FindByIdentity() methods is extremely slow - up to 100x(!) vs Principal.PrincipalSearcher().
On my notebook
Get-LocalUser name
ported cmdlet takes 2.8 sec(!) vs 25 ms if a workaround is used.