A wholeSubtree LDAP search returns 1,000 clinician entries and stops - how does the client tell a complete answer from a truncated one?
answer
- the visible rows all look the same
- the last message carries the verdict
- two ceilings, and one is not yours
- zero means unrestricted, not empty
- success (0) against three truncation codes
basics
~20 sCompleteness is carried by the result code on the SearchResultDone that ends the search: success (0) means the whole scope was evaluated, while sizeLimitExceeded (4), timeLimitExceeded (3) or adminLimitExceeded (11) mean the server stopped early.
solid answer
~40 sThe entries a search delivers are individual `SearchResultEntry` messages, and each one is valid whether or not the search finished. The search then ends with exactly one `SearchResultDone`, and its result code is what distinguishes a complete answer from a truncated one: `success (0)` says the server evaluated the whole scope, while `sizeLimitExceeded (4)`, `timeLimitExceeded (3)` and `adminLimitExceeded (11)` say it stopped. Two independent ceilings can trip - the `sizeLimit` and `timeLimit` the client set in its own `SearchRequest`, and an administrative limit the server enforces that the client never asked for. A client that reads entries and ignores the final result code cannot distinguish "these are all the clinicians" from "these are the first thousand", and that is the commonest production incident on a wide subtree search.
code
pseudocode · 16 linesentries = empty list
for each message in search_response_stream:
if message is SearchResultEntry then
append message.entry to entries
else if message is SearchResultDone then
if message.resultCode = success (0) then
return entries // the whole scope was evaluated
if message.resultCode = sizeLimitExceeded (4)
or message.resultCode = timeLimitExceeded (3)
or message.resultCode = adminLimitExceeded (11) then
reject "partial result" // entries are real, the list is not complete
reject message.diagnosticMessagego deeper
Recall that a search ends with one final message and that its result code, not the entries themselves, says whether the whole answer arrived.
Explain the two independent ceilings, which result code each produces, and why a sizeLimit of zero means unrestricted rather than empty.
Diagnose the silent case: a job that reads entries and ignores the result code produces a plausible, wrong answer, and narrowing the base DN beats raising a server limit.
Decide what an estate's lookups are allowed to ask for at all: unbounded subtree searches are a coupling between directory growth and every consumer's correctness.
## Two ceilings, two owners A search can stop early for reasons that belong to different parties, and a client that only knows about its own will misread the other. | Ceiling | Who sets it | Where it lives | Result code when it trips | |---|---|---|---| | Size | The client | `sizeLimit` in the `SearchRequest` | `sizeLimitExceeded (4)` | | Time | The client | `timeLimit` in the `SearchRequest` | `timeLimitExceeded (3)` | | Administrative | The server's operator | Server configuration, not the request | `adminLimitExceeded (11)` | RFC 4511 defines `sizeLimitExceeded (4)` and `timeLimitExceeded (3)` in terms of the limit the **client specified**, and `adminLimitExceeded (11)` as an administrative limit being exceeded. In practice a directory server may report its own ceiling as `(4)`, so a client should treat all three as one thing: *the answer you are holding is not the whole answer*. ## Why the entries cannot tell you A search response is a stream. The server sends zero or more `SearchResultEntry` messages, each carrying an entry's DN and a `PartialAttributeList` of the attributes requested, and then exactly one `SearchResultDone`. The entries already sent when a limit trips are real, matching, correct entries - the server does not retract them. The stream simply ends earlier than it would have. So the shape of a truncated answer and the shape of a complete one are **identical** up to the final message. Everything that distinguishes them is in the `LDAPResult` carried by `SearchResultDone`: its result code, plus `matchedDN` and the free-text `diagnosticMessage`, which is meant for a human reader and is not machine-parseable. This is why "we got results, so the search worked" is the wrong test, and why a client library that exposes only the list of entries hides the one field that matters. ## The zero trap `sizeLimit` and `timeLimit` are integers, and **zero means no client-requested restriction** - not "return nothing" and not "stop immediately". Reading zero as an empty answer is a common misreading of the field. Two consequences follow: - Sending zero does not make a search unbounded. It removes the client's own cap and leaves the server's administrative limit in force, which is frequently the one that was going to trip anyway. - Sending a small non-zero value is a deliberate statement: *I expect at most this many, tell me if there are more*. A `sizeLimit` of one is a neat existence check, because `sizeLimitExceeded (4)` then means "more than one matched". ## What a silent truncation does downstream Take the teaching hospital again: one naming context holds every clinician across three sites, and a nightly job runs a `wholeSubtree` search from the naming context to build an on-call roster. 1. The estate grows past the server's administrative limit. 2. The search now stops after the entries it has found, which happen to cluster in the two sites whose organizational units sort earliest. 3. The job reads the entries, never inspects `SearchResultDone`, and writes a roster. 4. The third site vanishes from the roster with no error anywhere, and the failure surfaces as a staffing problem rather than a directory problem. Every step is ordinary. The only defect is a result code nobody read. ## Making the search answerable The repair is to stop asking a question whose honest answer is too big: - **Narrow the base DN.** Searching from one site's organizational unit rather than the naming context removes candidates instead of testing them, and it is usually the largest single win. - **Narrow the scope.** Where the entries you want sit exactly one level down, `singleLevel` bounds the walk regardless of what is below. - **Tighten the filter.** Conjoin with `&` over attributes the server indexes, so the candidate set shrinks early rather than late. - **Ask for fewer attributes.** `AttributeSelection` does not reduce the entries searched, but it reduces what each one costs to send, which is often what the time limit is actually measuring. - **Read the result code, always.** Treat anything other than `success (0)` on a search you intend to act on as "do not trust this list", and log the `diagnosticMessage` for the operator rather than parsing it. Raising the server's administrative limit is the move that feels like a fix and is not: it buys a fixed amount of headroom, hides the problem until the estate grows again, and does nothing about the client that still is not reading the result code.
- In an LDAP SearchRequest, what does a sizeLimit of 0 mean?That **no client-requested size restriction** is in effect - not that zero entries should come back. The same reading applies to `timeLimit`. Zero removes the client's own ceiling only: any administrative limit the directory server enforces still applies, and can still truncate the answer with `adminLimitExceeded (11)`. Sending zero is therefore a statement about what the client wants, not a guarantee about what it will get.
- What distinguishes sizeLimitExceeded (4) from adminLimitExceeded (11)?RFC 4511 defines `sizeLimitExceeded (4)` in terms of the size limit the **client specified** in its request, and `adminLimitExceeded (11)` as an **administrative** limit being exceeded - one the client never asked for and cannot see in its own request. A client should still handle both identically, because implementations differ in which they report for a server-side ceiling, and either way the list in hand is incomplete.
- Why is an empty LDAP search result sometimes a scope problem rather than a filter problem?Because scope selects the candidate entries before the filter is evaluated against any of them. A `singleLevel` search from a naming context that holds an organizational unit per site tests only those site entries - the clinician entries one level further down are never candidates at all. The filter can be perfect and the answer still empty, and the search reports `success (0)` while it happens.
- Does hitting a limit invalidate the entries the client already received?No. Each `SearchResultEntry` the server sent is a genuine match that satisfied both the scope and the filter, and it stays valid. What the truncation destroys is the claim that the set is **complete**. That distinction matters for what you may do with the list: counting, rostering, reconciling or deleting on the strength of it are all unsafe, while acting on one named entry you found in it is fine.
It is the difference between a results page that ends "no more matches" and one that ends "stopped after 1,000". The rows above the footer look identical either way; only the footer says whether you are looking at the whole answer.
saying these in an interview costs you the question
- Treats the entries received as the complete answer
- Thinks a sizeLimit of 0 means return no entries
- Believes the server returns nothing at all when a limit trips
- Assumes only the client's own limits can truncate a search
- Reads an empty result as proof that nothing matched
- Fixes truncation by raising the server's administrative limit