Net::LDAP connection semantics problem and nested blocks

Hi,

I am working with the Net::LDAP library to communicate with active directory. The connection semantics of that library are causing me problems which I am trying to solve. The library was designed to connect and then disconnect from the LDAP server before and after each operation. I am running into a problem where the server is refusing connections because of too many successive connections.

Some background....

I have a Net::LDAP subclass that among other things, supplies credential and connection information to the parent on construction based on a single key string that is given.

class MyLDAP < Net::LDAP

   @cache = {}

   def self.get(key)
     if @cache.has_key? key
       @cache[key]
     else
       @cache[key] = CortecsLDAP.new(key)
     end
   end

   def initialize(key)
     super self.connect_details_for_key(key)
   end

   def self.connect_details_for_key(key)
     # Connect to DB and get details here relating to key
   end

The caching business is because this it is common for 1000+ ldap operations to happen in one execution so I thought I was being more efficient in holding the connections open like that.

The semantics of Net::LDAP however open and close underling tcp connection before and after each operation. Basically making my caching obsolete. However, Net::LDAP provides the method open() at the class and object level which takes a block and passes a Net::LDAP object to the block, keeping its connection open for the duration of the block. What I would like to do is be able to hold open all of the objects in the cache for the duration of a block. This is probably what I would want the interface to look like....

LDAPSession.open do | ldap_connection_cache |
   my_ldap_instance = ldap_connection_cache.get('some_key')
   my_other_ldap_instance = ldap_connection_cache.get('some_other_key')
end

The important thing here is that my_ldap_instance and my_other_ldap_instance remain open for the whole time they are in the block they are in. The only way to keep a Net::LDAP connection open is to use the open() method described above and pass it a block.

I hope that makes sense to someone who can help.

Cheers.

···

-----------------------------
Luke Daley
http://www.ldaley.com
-----------------------------

What LDAP operations are you performing here?

···

On 10/31/06, Luke Daley <ld@ldaley.com> wrote:

Hi,

I am working with the Net::LDAP library to communicate with active
directory. The connection semantics of that library are causing me
problems which I am trying to solve. The library was designed to
connect and then disconnect from the LDAP server before and after
each operation. I am running into a problem where the server is
refusing connections because of too many successive connections.

In the particular case when I hit the problem, it was an attribute level modify operation. I realise my original post was extremely hard to decipher, I was rather pressed for time when I put it together.

I have a script which consumes 'jobs' off a queue. One of the jobs may be to update the name details for a particular user in domain a, the next to update the phone number in domain b. Therefore opening one connection using open() is not an option.

I have thought long and hard about this and I can't come up with a solution. The only way for this to work the way I want would be to have a flag that changes the connection model and maybe allows me to specify when to open/close the underlying connection. However, I see from the in code comments that it was an explicit design decision to have the API work the way it does. I do understand the reason for this, but it just doesn't suit my needs for this.

If this is something that you don't wish too change (quite reasonable) I will go back to the other ruby ldap options.

On a side note, your ldap implementation performs very well compared to the Perl net::ldap which I was surprised about. It may have been a peculiarity of my environment, but when I moved to Perl to get the task I needed done, there was a noticeable slow down in processing large result sets compared to your ruby implementation.

Cheers.

···

On 01/11/2006, at 3:11 PM, Francis Cianfrocca wrote:

What LDAP operations are you performing here?

-----------------------------
Luke Daley
http://www.ldaley.com
-----------------------------

In regard to the design decision in the library: it's essential to be
able to document and depend on the network behavior of Net::LDAP.
That's more important than that it obey one semantics or another. The
decision came from years of experience with the various
Michigan-derived C implementations, which try to wrap the connection
away from the API, which in turn leads to bizarre, hard-to-find bugs.

Why don't you put your queue consumer inside the Net::LDAP#open block
and let it run as long as it wants to? You can put multiple #open
calls on different threads if you have to connect to more than one
LDAP server. The only thing I would be concerned about is that the
LDAP servers may time out your connections, but you can manage that by
putting your own timeout in your Queue-query.

···

On 11/5/06, Luke Daley <ld@ldaley.com> wrote:

On 01/11/2006, at 3:11 PM, Francis Cianfrocca wrote:

> What LDAP operations are you performing here?

In the particular case when I hit the problem, it was an attribute
level modify operation. I realise my original post was extremely hard
to decipher, I was rather pressed for time when I put it together.

I have a script which consumes 'jobs' off a queue. One of the jobs
may be to update the name details for a particular user in domain a,
the next to update the phone number in domain b. Therefore opening
one connection using open() is not an option.