LDAP SDK Features: Connection Pooling

In order to get the best performance and efficiency from a directory-enabled application, you will likely want to use some form of connection pooling.  Most LDAP APIs provide support for some level of connection pooling, but I’m not aware of any other API that provides anywhere near the power and convenience of the connection pooling capabilities of the UnboundID LDAP SDK for Java.  This post will examine many of the connection pooling features that it provides.

Connection Pool Implementations

The UnboundID LDAP SDK for Java actually provides two connection pool implementations.  The com.unboundid.ldap.sdk.LDAPConnectionPool class provides a single set of connections intended to be used for all types of operations. The com.unboundid.ldap.sdk.LDAPReadWriteConnectionPool class maintains two sets of connections, one set intended for use in processing write (add, delete, modify, and modify DN) operations, and the other for use in processing read (bind, compare, and search) operations. The former is the best choice for environments in which all of the directory servers have the same capabilities, while the latter is better for environments containing some a mix of read-only and read-write servers.  The read-write connection pool implementation may also be useful in environments in which it is recommended to try to send all writes to the same server, even if all of the servers technically support read and write operations.

Failover and Load Balancing

Connection pools can be created so that all connections in the pool are established to the same server, or they can be spread across multiple servers.  This can be accomplished through the com.unboundid.ldap.sdk.ServerSet API, which is used to determine which server to use at the time that a connection is created.  There are three server set implementations provided in the LDAP SDK, and you can create your own with whatever logic you choose. The included implementations are:

  • com.unboundid.ldap.sdk.SingleServerSet — This is a simple implementation in which connections will always be established to the same server.
  • com.unboundid.ldap.sdk.RoundRobinServerSet — This is an implementation that allows you to specify a list of servers. The first attempt to create a connection will use the first server, the second will choose the second server, and so on, and once it hits the end of the list it will wrap back around to the beginning.   If the selected server is unavailable, it will skip ahead to the next server in the list, and will only fail if none of the servers are available.
  • com.unboundid.ldap.sdk.FailoverServerSet — This is an implementation that allows you to specify an ordered list of servers, and when attempting to establish a new connection it will always try the first server, but if it isn’t available then it will try the second, then the third, and so on. This implementation also provides the ability to fail over between server sets rather than individual servers, which can provide a lot of flexibility (e.g., try round-robin across all servers in the local data center, but if none of them are available then try round-robin across all the servers in a remote data center).

When a connection pool is created with a server set, then that server set will be used to establish all connections in that pool.  For example, if you create a pool with ten connections and you use the round-robin server set with a list of two servers, then five of the connections in that pool will be established to the first server and five to the second.  This provides a simple mechanism for client-side load balancing, since operations performed using pooled connections will be spread across the two servers.  It also provides better availability, since if either server becomes unavailable then connections established to it will fail over to the other (and in many cases it will be transparent to the application).

If all of the servers defined for use in a connection pool are unavailable at the same time, then obviously any attempt to use that connection pool during that time will fail.  However, as soon as any of those servers is back online, then the pool will resume normal operation and operations will again be successful without the need to restart the application or re-create the pool.

Checking out and Releasing Connections

Traditionally, connection pools simply maintain a set of connections that the application can check out, use as needed, and return.  Although the implementations provided by the UnboundID LDAP SDK for Java go well beyond this, it still possible to check out and release connections as needed.

In the LDAPConnectionPool implementation, a connection can be obtained using the getConnection() method. The application can then do the appropriate processing, and when complete it can release the connection back to the pool with the releaseDefunctConnection(LDAPConnection) method. If an error occurs during processing that indicates the connection is no longer viable, then the releaseDefunctConnection(LDAPConnection) method should be used instead, which will cause the provided connection to be closed and discarded a new connection created in its place.

The LDAPReadWriteConnectionPool implementation provides similar methods for checking out and releasing connections, but there are separate methods for interacting with the read and write pools. The getReadConnection() method may be used to check out a connection from the read pool, and the getWriteConnection() method will check out a connection from the write pool. When returning connections, the releaseReadConnection(LDAPConnection) and releaseWriteConnection(LDAPConnection) methods may be used to return valid connections, and the releaseDefunctReadConnection(LDAPConnection) and releaseDefunctWriteConnection(LDAPConnection) methods may be used to return connections that are no longer valid.

Performing Operations in the Pool

One nice feature about the connection pools provided by the UnboundID LDAP SDK for Java is that they include convenience methods for performing operations using connections from that pool.  As a result, it is not necessary to check out and release connections in order to process an operation because the pool will do it on behalf of the requester.  It will also perform any necessary error handling, and evaluate the result code to indicate whether the connection should be destroyed and a new connection created in its place.

The provided connection pool implementations implement the com.unboundid.ldap.sdk.LDAPInterface interface, which is the same interface implemented by the LDAPConnection class.  You can process a search using a pooled connection by invoking one of the LDAPConnectionPool.search or LDAPReadWriteConnectionPool.search methods just like you can call LDAPConnection.search.  Because they all implement the same interface, it’s relatively simple to write an application that can be used to work with either single connections or connection pools.

The LDAPConnectionPool class also provides a processRequests(List<LDAPRequest>,boolean) method that can be used to process multiple requests in succession on a single connection from the pool. In this case, the results of those operations will be returned in a list, and in the event that an any of those requests does not complete successfully then it may either continue attempting to process subsequent requests in the lists or it may stop at that point.

Connection Availability

It is important to understand the behavior that a connection pool exhibits whenever a connection is needed but none are immediately available. The UnboundID LDAP SDK for Java provides two settings that can be used to control the connection pool’s behavior in this case. The first setting is whether the connection pool should be allowed to create a new connection if one is needed but none are available, and the second is how long to wait for a connection to become available before either creating a new connection or returning an error. These settings are controlled by the setCreateIfNecessary(boolean) and setMaxWaitTimeMillis(long) methods, respectively.

With the combination of these settings, any of the following behaviors may be selected if a connection is needed but none are available:

  • The connection pool should immediately throw an exception
  • The connection pool should immediately create a new connection
  • The connection pool should immediately create a new connection
  • The connection pool should wait for up to a specified length of time for a connection to become available, and if that time passes without a connection becoming available then it will throw an exception
  • The connection pool should wait for up to a specified length of time for a connection to become available, and if that time passes without a connection becoming available then it will throw an exception
  • The connection pool should wait as long as it takes for a connection to become available.

If the pool is configured to create new connections if none are available, then there may be periods of time in which there are more total connections associated with that connection pool than the maximum number of connections to maintain in the pool.  In this case, the behavior that the pool will exhibit when a connection is released will depend on the number of connections available in the pool at that time.  As long as the number of available connections in the pool is less than the maximum, then the connection will be released back to the pool and will be made available for use by subsequent requests.  If an attempt is made to release a connection when the pool already has the maximum number of available connections, then the released connection will be closed.  This provides a scalable and efficient way for the connection pool to grow in size as needed under heavy load while ensuring that it doesn’t hold onto too many connections during slower periods.

Making it Easier to Write Directory-Enabled Applications

I’ve been working with LDAP directory servers for about ten years, and for that entire time I’ve also been writing code.  I’ve written a lot of server-side code in the course of building directory servers, but I’ve written even more client-side code for interacting with them.  Unfortunately, I’ve always been a bit disappointed with the APIs that are available for LDAP communication, especially those for use in Java applications.

Java should be a great language for writing directory-enabled applications.  It’s fast, has a broad standard library, offers a number of frameworks for developing web-based and desktop applications, and it’s easy to write robust code in a short period of time.  Unfortunately, the APIs available for LDAP communication are pretty limited.  JNDI is a common choice because it’s part of the core Java runtime, but it’s very cumbersome and confusing and provides rather limited access to the elements of the LDAP protocol.  The Netscape Directory SDK for Java is more user friendly than JNDI, but it’s fairly buggy (especially under load), supports a pretty limited set of controls and extended operations, and is really showing its age after not having any new releases since 2002.  I’ve never actually used JLDAP, but it looks to expose pretty much the same API as the Netscape SDK and has also gone several years without a new release.

Today, UnboundID is correcting this problem with our release of the UnboundID LDAP SDK for Java.  It is a user-friendly, high-performance, feature-rich, and completely free Java API for communicating with LDAP directory servers and performing other directory-related tasks.  Some of the benefits that it provides include:

  • It is completely free to use and redistribute.  The LDAP SDK is available under either the GNU General Public License v2 (GPLv2) or the UnboundID LDAP SDK Free Use License.
  • It provides a broad feature set.  In addition to providing full support for the core LDAPv3 protocol, it also includes support for 17 standard controls, 4 standard extended operations, and 6 standard SASL mechanisms.  It also provides a number of related APIs for things like LDIF, base64 and ASN.1 parsing, working with root DSE and changelog entries, enhanced schema support, command line argument processing, and SSL/TLS communication.
  • It is much more convenient and easy to use than other LDAP APIs,  It is often possible to do what you want with quite a bit less code than the alternatives, and its use of Java features like generics, enums, annotations, and varargs can further enhance the development experience.
  • It provides support for connection pooling and client-side failover and load balancing.  Connections can be easily spread across multiple directory servers, and you can even have read and write operations sent to different sets of servers.  The connection pool classes implement the same interface as individual connections, so you can process operations using pooled connections without needing to deal with checking out and releasing connections and performing all of the necessary error handling (although that’s possible too if you need it).
  • It provides excellent performance and scalability.  My testing has shown it to be significantly faster than either JNDI or the Netscape SDK, and the searchrate tool that we include as an example can handily outperform the popular C-based version provided by another vendor.
  • It has no external dependencies.  Everything is included in a single jar file, and the only requirement is a Java SE 5.0 or higher runtime.
  • It is robust and reliable.  We have an extensive test suite for the SDK itself with over 26,000 test cases covering over 94% of the code.  The LDAP SDK is also used as an integral part of other UnboundID products, so it benefits from the testing we do for them as well.  It’s frequently subjected to very heavy and highly concurrent workloads so there shouldn’t be any surprises when moving your applications from testing into production (at least, not because of the LDAP SDK).
  • It includes generous documentation.  In addition to a more thorough overview of the benefits our LDAP SDK provides over other the alternatives, it also includes a getting started guide, Javadoc documentation with lots of examples, and a number of sample programs demonstrating the use of various SDK components.
  • Commercial support is available.  This can help ensure fast access to patches for any problems found in the LDAP SDK, and may also be used to request enhancements and additional functionality.  Developer support is also available to assist you in using the LDAP SDK to create directory-enabled applications.

You can find the UnboundID LDAP SDK for Java available for download at http://www.unboundid.com/products/ldapsdk/.  All of the documentation is available there as well (and also in the product download), including some frequently asked questions and a more detailed list of the advantages it offers over other LDAP APIs.

Sleeping with Solaris

I recently came across an interesting issue on Solaris.  I was trying to do something pretty simple:

  1. Check to see if there’s work to do.
  2. If there is work to do, then do it and go back to step 1.
  3. Sleep for a short length of time and go back to step 1.

This worked great on Linux, but not so well on Solaris.  Things were getting done in fractions of a millisecond on Linux, but it was taking over ten milliseconds on Solaris.  I hypothesized that the sleep might be the problem, and a quick test confirmed it.  It turns out that on Solaris in the default configuration you can’t sleep for less than ten milliseconds at a time (at least in Java).  This is because on Solaris, the Java sleep method is dependent on system clock ticks, and the default configuration uses 100 ticks per second, or one tick every ten milliseconds.  Any attempt to sleep for less than that time will get rounded up to ten milliseconds, and attempts to sleep for a length of time greater than that will be rounded up to the nearest multiple of ten milliseconds.

You can change this behavior by adding the following to the /etc/system file and rebooting the system:

set hires_tick=1

This will change the default from 100 ticks per second to 1000 (which means you can sleep in increments of one millisecond instead of ten).  There is also a way to fine-tune how many ticks per second you want (via hires_hz), but changing that isn’t supported and every mention of it I can find indicates that you don’t want to increase the frequency anyway because it will probably degrade performance as it will become expensive to update the tick counter too often.

There are, of course, other ways to accomplish the task I had originally set out to do (e.g., blocking queues, semaphores, wait/notify, etc.), but the bigger takeaway here is that you shouldn’t depend on algorithms involving fine-grained sleeping, especially if your application needs to run on Solaris.

The Future of SLAMD

Even to the casual observer it’s not too hard to notice that not much has happened with SLAMD in quite a while.  In fact, it’s been almost two years since my last commit to the public repository.  There are several reasons for this, including:

  • It works pretty doggone well in its current state.  I still use it quite a bit and hear that several other people do, too.  There are definitely ways that it could be improved, but so far it has been able to meet my needs.  Most of the SLAMD code that I have written since then has been in the form of new jobs that were pretty task-specific and not something that are likely to be useful in other environments.
  • Most of the time, I have been really busy with other things.  I just haven’t had nearly as much time to invest in it as I would have liked.  Of course, I did have a forced two-month vacation near the end of last year, but the terms of my severance stated that I wasn’t allowed to do any work and I didn’t want to press my luck.  After that period ended I’ve been going full-steam-ahead on something else.
  • There are parts of it that could do with a redesign.  The code used to generate the administrative interface is currently all held in one large file and could stand to be broken up.  There are also many areas in which updating the code to require Java 5 would allow it to be much more efficient and scalable, and the introduction of features like generics and enums would make the code easier and safer to edit.

Ultimately, I think that it’s at the point where it would be better to invest the effort in a clean rewrite than to try to build upon what’s there now, but so far I haven’t had much opportunity to do either one of them.  It’s definitely something that I would like to do and I’m hopeful that I might have the time to do it at some point in the future.  I have a lot of ideas for interesting and powerful enhancements, so I don’t want to count it out just yet.