defect
Michael Ströder
michael at stroeder.com
Wed Jun 26 22:58:24 CEST 2002
Leif Hedstrom wrote:
> Well, I don't know if this is the same problem I had with Python LDAP v1.x,
> we haven't tested v2.x yet. But, the result() function in Python LDAP can go
> into a very tight poll loop, with extreme effects if the Python process is
> running on the same machines as the LDAP server. Python will almost
> completely starve slapd for any CPU time ...
>
> Adding a short sleep() in the polling loop of ldapobject.result() helps, a
> lot.
Yes, you're right. A time.sleep(0.0001) right before the inner
result() call makes python-ldap hand over the CPU to the OS.
> while all:
> while ldap_result[0] is None:
> if (timeout>=0) and (time.time()-start_time>timeout):
> self._ldap_call(self._l.abandon,msgid)
> raise _ldap.TIMELIMIT_EXCEEDED(
> "LDAP time limit (%d secs) exceeded." % (timeout)
> )
> ldap_result = self._ldap_call(self._l.result,msgid,0,0)
> if ldap_result[0] is None:
> time.sleep(.01)
> if ldap_result[1] is None:
> break
It's possible to make it somewhat simpler since we have a first
result() call before the while loops.
while all:
while ldap_result[0] is None:
if (timeout>=0) and (time.time()-start_time>timeout):
self._ldap_call(self._l.abandon,msgid)
raise _ldap.TIMELIMIT_EXCEEDED(
"LDAP time limit (%d secs) exceeded." % (timeout)
)
time.sleep(0.0001)
ldap_result = self._ldap_call(self._l.result,msgid,0,0)
if ldap_result[1] is None:
break
all_results.extend(ldap_result[1])
ldap_result = None,None
return all_results
> Alternatively, adding
> an (arbitrarily) long timeout in the call to ldap_result() also accomplishes
> the same thing, like:
>
> ldap_result = self._ldap_call(self._l.result,msgid,0,15 * 60)
Which is a bad idea in a multi-threaded environment.
> I haven't dug deep into this problem yet, to figure out if this is an
> OpenLDAP library problem, or a Python LDAP problem.
The main problem here is that the OpenLDAP libs are not
thread-safe. Therefore a module-wide lock is needed to serialize
all calls into OpenLDAP libs. But just wrapping ldap_result() with
locking around it would lead to blocking threads if one thread is
waiting for large search results. That's why I implemented
LDAPObject.result() in Python like it is today. Unfortunately we
cannot deal with waiting for data at the socket level which leads
to higher-level polling loop.
Everybody is encouraged to try the time.sleep(0.0001) hack and
look how it "feels" now. BTW: it makes the simple benchmark I
posted yesterday slower. It always depends what you wanna
optimize... ;-)
I suspect this still might not fully explain the
30s-vs.-immediate-results reports. There may be more issues with
other effects like Mauro described.
I guess the best solution would be if somebody with some spare
cycles digs into OpenLDAP's libldap_r to check if it's ready for
use with python-ldap. This would make it possible to do a finer
grained locking on LDAP connections instead of module-wide locking
allowing different threads with different LDAPObject instances to
run without blocking each other.
Ciao, Michael.
More information about the python-ldap
mailing list