From b8ec44293f15d0e7ddeecb849850394872aa21ef Mon Sep 17 00:00:00 2001 From: Gary Sharp Date: Thu, 17 Apr 2014 17:00:02 +1000 Subject: [PATCH] Parallelize AD searching Where multiple queries are required to complete a search, the query is parallelized --- .../ActiveDirectory/ActiveDirectory.cs | 2 +- .../ActiveDirectory/Internal/ADInterop.cs | 122 +++++++++++------- 2 files changed, 78 insertions(+), 46 deletions(-) diff --git a/Disco.Services/Interop/ActiveDirectory/ActiveDirectory.cs b/Disco.Services/Interop/ActiveDirectory/ActiveDirectory.cs index 9e083bc9..189a5315 100644 --- a/Disco.Services/Interop/ActiveDirectory/ActiveDirectory.cs +++ b/Disco.Services/Interop/ActiveDirectory/ActiveDirectory.cs @@ -233,7 +233,7 @@ namespace Disco.Services.Interop.ActiveDirectory var ldapSamAccountName = computerId.Item2.EndsWith("$") ? computerId.Item2 : computerId.Item2 + "$"; var ldapFilter = string.Format(ldapFilterTemplate, ldapSamAccountName); - var ldapResult = ADInterop.SearchDomain(Domain, DomainController, Domain.DistinguishedName, ldapFilter, 1, loadProperites).FirstOrDefault(); + var ldapResult = ADInterop.SearchAll(Domain, DomainController, ldapFilter, 1, loadProperites).FirstOrDefault(); if (ldapResult != null) MachineAccount = ldapResult.AsMachineAccount(loadAdditionalProperties); diff --git a/Disco.Services/Interop/ActiveDirectory/Internal/ADInterop.cs b/Disco.Services/Interop/ActiveDirectory/Internal/ADInterop.cs index 79675600..b40ecc8f 100644 --- a/Disco.Services/Interop/ActiveDirectory/Internal/ADInterop.cs +++ b/Disco.Services/Interop/ActiveDirectory/Internal/ADInterop.cs @@ -317,34 +317,56 @@ namespace Disco.Services.Interop.ActiveDirectory.Internal { return SearchAll(Domain, LdapFilter, null, LoadProperties); } - public static IEnumerable SearchAll(IEnumerable> DomainsWithController, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - var query = DomainsWithController - .SelectMany(d => SearchAll(d.Item1, d.Item2, LdapFilter, ResultLimit, LoadProperties)); - - if (ResultLimit.HasValue) - query = query.Take(ResultLimit.Value); - - return query.ToList(); - } public static IEnumerable SearchAll(IEnumerable Domains, string LdapFilter, int? ResultLimit, string[] LoadProperties) { - var query = Domains - .SelectMany(domain => SearchAll(domain, LdapFilter, ResultLimit, LoadProperties)); + if (Domains == null || Domains.Count() == 0) + return Enumerable.Empty(); + + var queries = Domains.Select(d => Tuple.Create(d, (DomainController)null)).ToList(); + + return SearchAll(queries, LdapFilter, ResultLimit, LoadProperties); + } + public static IEnumerable SearchAll(IEnumerable> DomainsWithController, string LdapFilter, int? ResultLimit, string[] LoadProperties) + { + var queries = DomainsWithController.ToList(); + + IEnumerable results; + + switch (queries.Count) + { + case 0: + results = Enumerable.Empty(); + break; + case 1: + var singleQuery = queries.First(); + results = SearchDomain(singleQuery.Item1, singleQuery.Item2, null, LdapFilter, ResultLimit, LoadProperties); + break; + default: + var taskFactory = new TaskFactory>(); + var tasks = queries + .Select(query => + taskFactory.StartNew(() => + SearchDomain(query.Item1, query.Item2, null, LdapFilter, ResultLimit, LoadProperties)) + ).ToArray(); + Task.WaitAll(tasks); + results = tasks.SelectMany(t => t.Result); + break; + } if (ResultLimit.HasValue) - query = query.Take(ResultLimit.Value); + results = results.Take(ResultLimit.Value); - return query.ToList(); + return results.ToList(); + } + public static IEnumerable SearchAll(ActiveDirectoryDomain Domain, string LdapFilter, int? ResultLimit, string[] LoadProperties) + { + return SearchAll(Domain, null, LdapFilter, ResultLimit, LoadProperties); } public static IEnumerable SearchAll(ActiveDirectoryDomain Domain, DomainController DomainController, string LdapFilter, int? ResultLimit, string[] LoadProperties) { return SearchDomain(Domain, DomainController, null, LdapFilter, ResultLimit, LoadProperties); } - public static IEnumerable SearchAll(ActiveDirectoryDomain Domain, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - return SearchDomain(Domain, null, LdapFilter, ResultLimit, LoadProperties); - } + public static IEnumerable SearchScope(string LdapFilter, string[] LoadProperties) { return SearchScope(Domains, LdapFilter, LoadProperties); @@ -369,25 +391,9 @@ namespace Disco.Services.Interop.ActiveDirectory.Internal { return SearchScope(Domain, DomainController, LdapFilter, null, LoadProperties); } - public static IEnumerable SearchScope(IEnumerable> DomainsWithController, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - var query = DomainsWithController - .SelectMany(d => SearchScope(d.Item1, d.Item2, LdapFilter, ResultLimit, LoadProperties)); - - if (ResultLimit.HasValue) - query = query.Take(ResultLimit.Value); - - return query.ToList(); - } public static IEnumerable SearchScope(IEnumerable Domains, string LdapFilter, int? ResultLimit, string[] LoadProperties) { - var query = Domains - .SelectMany(domain => SearchScope(domain, LdapFilter, ResultLimit, LoadProperties)); - - if (ResultLimit.HasValue) - query = query.Take(ResultLimit.Value); - - return query.ToList(); + return SearchScope(Domains.Select(d => Tuple.Create(d, (DomainController)null)), LdapFilter, ResultLimit, LoadProperties); } public static IEnumerable SearchScope(ActiveDirectoryDomain Domain, string LdapFilter, int? ResultLimit, string[] LoadProperties) { @@ -395,23 +401,49 @@ namespace Disco.Services.Interop.ActiveDirectory.Internal } public static IEnumerable SearchScope(ActiveDirectoryDomain Domain, DomainController DomainController, string LdapFilter, int? ResultLimit, string[] LoadProperties) { - if (Domain.SearchContainers == null) + if (Domain.SearchContainers == null || Domain.SearchContainers.Count == 0) return Enumerable.Empty(); - var query = Domain.SearchContainers - .SelectMany(container => SearchDomain(Domain, DomainController, container, LdapFilter, ResultLimit, LoadProperties)); + var query = new List>() { + Tuple.Create(Domain, DomainController) + }; + + return SearchScope(query, LdapFilter, ResultLimit, LoadProperties); + } + public static IEnumerable SearchScope(IEnumerable> DomainsWithController, string LdapFilter, int? ResultLimit, string[] LoadProperties) + { + var queries = DomainsWithController.SelectMany(d => d.Item1.SearchContainers, (d, sc) => Tuple.Create(d.Item1, d.Item2, sc)).ToList(); + + IEnumerable results; + + switch (queries.Count) + { + case 0: + results = Enumerable.Empty(); + break; + case 1: + var singleQuery = queries.First(); + results = SearchDomain(singleQuery.Item1, singleQuery.Item2, singleQuery.Item3, LdapFilter, ResultLimit, LoadProperties); + break; + default: + var taskFactory = new TaskFactory>(); + var tasks = queries + .Select(query => + taskFactory.StartNew(() => + SearchDomain(query.Item1, query.Item2, query.Item3, LdapFilter, ResultLimit, LoadProperties).ToList()) + ).ToArray(); + Task.WaitAll(tasks); + results = tasks.SelectMany(t => t.Result); + break; + } if (ResultLimit.HasValue) - query = query.Take(ResultLimit.Value); + results = results.Take(ResultLimit.Value); - return query.ToList(); + return results.ToList(); } - public static IEnumerable SearchDomain(ActiveDirectoryDomain Domain, string SearchRoot, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - return SearchDomain(Domain, null, SearchRoot, LdapFilter, ResultLimit, LoadProperties); - } - public static IEnumerable SearchDomain(ActiveDirectoryDomain Domain, DomainController DomainController, string SearchRoot, string LdapFilter, int? ResultLimit, string[] LoadProperties) + private static IEnumerable SearchDomain(ActiveDirectoryDomain Domain, DomainController DomainController, string SearchRoot, string LdapFilter, int? ResultLimit, string[] LoadProperties) { string ldapServer = DomainController == null ? Domain.DnsName : DomainController.Name; string searchRoot = SearchRoot ?? Domain.DistinguishedName;