Optimizing Search Performance

As with any query language, many factors affect the overall performance of an LDAP query. As developers, we typically want our queries to be fast. We also know that it usually takes a while to learn all the tips and tricks, but it is this knowledge that separates the novices from the pros.

This section focuses on making the right choices to get the fastest queries possible.

Choosing the Right Search Root

One of the most important performance improvement tips is to use a SearchRoot that is the closest parent to the objects we wish to find. We then must scope the search correctly. Here are the binding locations and scopes, presented in order from best to worst.

  1. If we know where our objects are located, we bind directly to the objects' parent container using the container's DN and scope the search using SearchScope.OneLevel.
  2. If our objects are in separate containers, we set the SearchScope to the closest common parent container and specify the SearchScope.Subtree option. If the objects are widely distributed across many containers and levels in the directory hierarchy, we may need to use the root of the partition. As we described in Chapter 3, we generally use the RootDSE object to find the DN of the naming context for the directory partition. Typically, the partition's DN that we will use is specified by the defaultNamingContext attribute of the RootDSE object.
  3. Finally, if we are not sure which domain our objects are in (or if local replicas are not available), we set the SearchRoot to the global catalog server and perform a subtree search. Keep in mind that the global catalog contains only a subset of the full object attributes, so it may become necessary to rebind to the object using its DN to return all of its attributes.

Choosing the Right Scope

Looking at our available search scopes (Base, OneLevel, and Subtree), it would seem intuitively obvious to choose the narrowest scope available that will return what we want. Indeed, this is exactly the case, but the importance of this cannot be overstated. The difference between using a OneLevel search versus a Subtree search can mean an order of magnitude difference in speed and efficiency. Putting aside base-level searches (Base) for now, the biggest performance differentiator is going to be between using OneLevel and Subtree. This especially holds true when searching with nonindexed attributes. If we are vigilant in using an indexed attribute in our filter at all times, we can usually extract pretty good performance as well.

Now, if we happen to know the exact DN of the object we wish to search for, we can use a Base search to return its attributes. While there are definitely some uses for Base searches (attribute scope queries, for example), keep in mind that it might actually be easier simply to create a DirectoryEntry for the object instead. This is actually how the DirectoryEntry retrieves an object's attributes under the hood in the first place!

Creating Efficient Queries

Once the search scope and starting location are as efficient as possible, we can focus on other areas to help us improve the performance and efficiency of our queries. Table 5.4 lists some common areas that a developer can focus on to bring performance gains.

Table 5.4. Performance Tips

Tip

Effect

Use indexed attributes in filters when possible.

Try whenever possible to have at least one indexed attribute listed in the query filter. As an example, in Windows 2000, using objectCategory in lieu of or in conjunction with the objectClass attribute is more efficient, as the former is indexed. It turns out that objectClass is indexed by default in Windows 2003, but the premise is the same for other attributes.

Return only the data required.

By default, a search will return all of the normal attribute values for an object. If we need only a few of these, we can improve performance tremendously, simply by specifying the values we need.

Request only the number of results needed.

We should always try to specify the number of results we wish to receive with the SizeLimit property, unless we are executing a paged search.

Avoid ANR searches.

Ambiguous name resolution (ANR) is a terrific feature, but it can quickly get out of control in queries that are more complicated. It is important to remember that the actual ANR filter internally expands into a much larger filter that might drag down performance.

Avoid careless substring searches.

Use substring search filters with care. Whenever possible, place the wildcard at the end of the value ("sn=d*") in order to preserve index usage.

 

Avoid other wildcard positions when possible ("sn=*unn" or "sn=*un*"). If this is not possible, then create a medial or "tuple" index on this attribute. Avoid unnecessary substring searches in general.

Avoid sorting.

Sorting can be a big performance killer. Even when performing it in the most efficient manner (see Sorting Search Results, in Chapter 4), it still can suck performance from our applications. We should always consider whether we can push the sorting to the client. If we can, this will always be more efficient than performing the sort server side.

Properly place AND and OR operators.

This is a tricky one to understand sometimes. Suppose we had the following type of filter (in English): "Select all users that have a last name of either kaplan or dunn." We could express that filter using something like this:

 
(&
(objectCategory=person)
(objectClass=user)
(|
(sn=dunn)
(sn=kaplan)
)
)
 
 

It turns out this is not the most efficient way to do this. We really want to do something like this:

 
(|
(&
(objectClass=user)
(objectCategory=person)
(sn=dunn)
)
(&
(objectClass=user)
(objectCategory=person)
(sn=kaplan)
)
)
 
 

The query processor has been improved in Windows 2003, making this not as big an issue, but it should be kept in mind.

Avoid bitwise comparisons.

We demonstrated how to use bitwise comparisons in Chapter 4 and also suggested using them sparingly. The reason is that a bitwise comparison prevents the use of an index for that attribute. Therefore, when using these types of comparisons, it is important to keep in mind the first tip in this table and to use other indexed attributes when possible to limit the impact.

Avoid using the NOT (!) operator.

This is a subtle point, but the query processor processes objects that we might not have permission to view, and attributes that do not have a value as meeting the filter criteria, resulting in unnecessary matches. Additionally, this operator prevents the use of indices on affected attributes.

Use connection caching.

Opening and closing connections is inefficient. In the section ADSI Connection Caching Explained, in Chapter 3, we describe how to do this properly.

Query an object only once.

Instead of constantly requerying an object and generating network traffic and overhead, we should query the object only once and cache the data locally.

Avoid referral chasing if possible.

Referral chasing can send us bouncing from server to server looking for objects. This can be very time consuming to perform. When querying for objects that might exist in another domain in the same forest, consider using the global catalog rather than referrals. This is generally more efficient.

 

Turn Caching Off When Possible

The CacheResults property of DirectorySearcher manipulates an underlying ADSI feature that enables client-side caching. By default, this property is set to true and caching is enabled in SDS. As we mentioned previously in Chapter 4, this property has little bearing for us in .NET. Internally, the entire result set is held in an ArrayList object in our SearchResultCollection object, so we will not actually contact the ADSI local cache when enumerating a result set more than once. It is safe to set this to false and lower the unmanaged memory requirements for our application.

Part I: Fundamentals

Introduction to LDAP and Active Directory

Introduction to .NET Directory Services Programming

Binding and CRUD Operations with DirectoryEntry

Searching with the DirectorySearcher

Advanced LDAP Searches

Reading and Writing LDAP Attributes

Active Directory and ADAM Schema

Security in Directory Services Programming

Introduction to the ActiveDirectory Namespace

Part II: Practical Applications

User Management

Group Management

Authentication

Part III: Appendixes

Appendix A. Three Approaches to COM Interop with ADSI

Appendix B. LDAP Tools for Programmers

Appendix C. Troubleshooting and Help

Index



The. NET Developer's Guide to Directory Services Programming
The .NET Developers Guide to Directory Services Programming
ISBN: 0321350170
EAN: 2147483647
Year: 2004
Pages: 165

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net