In an earlier post, I mentioned that while you can process multiple searches in parallel to speed up an application that needs multiple pieces of information to do its work, that generally only works if those searches are independent. If the searches are related (meaning, that you need the results of one to construct another request), then this approach won’t work. Fortunately, the Ping Identity Directory Server offers an LDAP join control that allows you to perform a search that not only retrieves the entries matching the search criteria, but that also joins those entries with related entries as specified by the join rule.
Wouldn’t it be nice if there were something similar for write operations? While LDAP allows you to send multiple write requests concurrently on the same or different connections, you can’t really do that if there are dependencies between those write operations. For example, let’s say that you want to add an entry along with some subordinate entries. You can’t add a child before creating its parent, and if you try to send them in parallel, then maybe it’ll work and maybe it won’t. But here again, the Ping Identity Directory Server has you covered, this time in the form of the multi-update extended operation.
As its name implies, the multi-update operation allows you to send multiple updates (any combination of add, delete, modify, modify DN, and password modify extended operations) in a single request that will be processed in the order that you provide them. At the very least, this allows you to reduce the amount of time required to process those operations because there’s only one round trip between the client and the server. But it also allows you to decide what happens if an error occurs while processing any of those updates, and here you have three options:
- You can have the processing occur atomically so that no changes will be applied unless all of them are processed successfully, and so that no client will be able to see the data in an intermediate state with only some of the changes completed. You can get this same benefit from LDAP transactions as described in RFC 5805 (which the Ping Identity Directory Server also supports), but the multi-update operation is more efficient because there’s only a single request and response, whereas LDAP transactions require a separate network round trip for each of the changes, plus additional round trips when starting and ending the transaction.
- You can have processing stop after the first error. Any writes that succeeded before the error will be preserved, but any changes in the multi-update request after the one that caused the error will be ignored. Clients may be able to see the data in an intermediate state while these operations are being processed.
- You can have processing continue until all of the operations have been attempted. Any of the changes that are successful will remain in place, and again, clients may be able to see the data in an intermediate state while they are being processed.
The Multi-Update Extended Request
The multi-update extended request has an OID of 1.3.6.1.4.1.30221.2.6.17 and a value with the following ASN.1 encoding:
MultiUpdateRequestValue ::= SEQUENCE { errorBehavior ENUMERATED { atomic (0), quitOnError (1), continueOnError (2), ... }, requests SEQUENCE OF SEQUENCE { updateOp CHOICE { modifyRequest ModifyRequest, addRequest AddRequest, delRequest DelRequest, modDNRequest ModifyDNRequest, extendedReq ExtendedRequest, ... }, controls [0] Controls OPTIONAL, ... }, ... }
As you might expect, the request just specifies the behavior to use in case an error is encountered during processing and the set of requests to be processed. Note that while the ASN.1 definition above does allow for any kind of extended request to be included, the only one that the Ping Identity Directory Server currently allows in a multi-update request in the password modify extended request.
The UnboundID LDAP SDK for Java offers support for the multi-update extended request through the MultiUpdateExtendedRequest class, with an assist from the MultiUpdateErrorBehavior enum. If you want to use the multi-update extended operation through some other API, you’ll need to encode the request for yourself.
The Multi-Update Extended Result
The multi-update extended result has an OID of 1.3.6.1.4.1.30221.2.6.18 and a value with the following encoding:
MultiUpdateResultValue ::= SEQUENCE { changesApplied ENUMERATED { none (0), all (1), partial (2), ... }, responses SEQUENCE OF SEQUENCE { responseOp CHOICE { modifyResponse ModifyResponse, addResponse AddResponse, delResponse DelResponse, modDNResponse ModifyDNResponse, extendedResp ExtendedResponse, ... }, controls [0] Controls OPTIONAL, ... }, ... }
There are two components to the extended result value:
- An indicator as to whether none, all, or some of the changes were applied.
- The results for all of the operations that were attempted. The results will be listed in the same order as in the request, and each operation result may optionally include the response controls for that operation.
If only a portion of the operations were attempted (for example, because the server stopped processing the multi-update operation after an error was encountered while processing one of the changes and did not attempt any of the others after that), then there may be fewer results than there were requests.
The UnboundID LDAP SDK for Java offers support for the multi-update extended result through the MultiUpdateExtendedResult class and the MultiUpdateChangesApplied enum. To use this extended operation in another API, you’ll need to decode the result value on your own.
Supported Controls
There are two categories of controls that may be used in conjunction with the multi-update extended request: those that can be attached to the multi-update extended operation itself, and those that can be attached to the individual operation requests inside the multi-update request value.
The controls that may be attached to the multi-update extended operation itself are:
- Get Backend Set ID (only for atomic requests)
- Intermediate Client
- Proxied Authorization v1
- Proxied Authorization v2
- Route To Backend Server (only for atomic requests)
- Transaction Settings (only for atomic requests)
The controls that may be attached to operation requests inside the multi-update request value are:
- Account Usable
- Assertion
- Intermediate Client
- Get Backend Set ID (only for non-atomic requests)
- Hard Delete
- Manage DSA IT
- Password Policy
- Post-Read
- Pre-Read
- Replication Repair
- Route To Backend Server (only for non-atomic requests)
- Soft Delete
- Subtree Delete
- Undelete
An Example Using the UnboundID LDAP SDK for Java
The ldapmodify tool provided as part of the UnboundID LDAP SDK for Java already includes support for the multi-update extended operation (via the --multiUpdateErrorBehavior argument), and you can find the code for that tool at https://github.com/pingidentity/ldapsdk/blob/master/src/com/unboundid/ldap/sdk/unboundidds/tools/LDAPModify.java.
However, that version of ldapmodify has a lot of features, and the multi-update support is only a tiny portion of it. For the sake of clearer illustration, I wrote a much simpler command-line tool that serves as a clearer demonstration of the multi-update operation. It operates much like ldapmodify, but it only reads the changes from an LDIF file, and it sends them to the server all at once through a multi-update operation. You can find that example at https://github.com/dirmgr/blog-example-source-code/tree/master/multi-update.