[python-ldap] Patch for ReconnectLDAPObject
Dylan Jay
djay at pretaweb.com
Wed Aug 28 04:21:43 CEST 2013
Hi,
This patch seems to work really well. Any chance of getting it included in a release?
---
Dylan Jay
Technical Solutions Manager
PretaWeb: Multisite Performance Support
P: +612 80819071 | M: +61421477460 | twitter.com/pretaweb | linkedin.com/in/djay75
On 13/07/2013, at 10:26 AM, Jonathan Giannuzzi <jonathan at giannuzzi.be> wrote:
> Dylan Jay wrote:
>> The details of how reproduce this are in the post by Maurits van Rees
>>
>> https://bugs.launchpad.net/ldapuserfolder/+bug/650371
>
> The last comment (#6) shows that after a failed reconnect, the next call to reconnect() will get stuck in an infinite loop.
> That is because the _pending_reconnect flag is not reset when the reconnection eventually fails.
> The fix for that is pretty easy:
>
> --- a/ldap/ldapobject.py 2013-07-13 00:31:13.000000000 +0200
> +++ b/ldap/ldapobject.py 2013-07-13 00:54:10.000000000 +0200
> @@ -792,6 +792,7 @@
> ))
> reconnect_counter = reconnect_counter-1
> if not reconnect_counter:
> + self._pending_reconnect = 0
> raise
> if __debug__ and self._trace_level>=1:
> self._trace_file.write('=> delay %s...\n' % (self._retry_delay))
>
> But that made me question why that flag was needed at all. I assumed it was in order to prevent multiple threads from calling reconnect at the same time.
> Unfortunately a quick test (two threads calling search_s in a loop, then kill the connection while they are running) shows that the reconnection is far from being thread-safe.
> The main problem is that if a call is being made while the reconnect is being done, the internal structures might be invalid (because of "del self._l" and "SimpleLDAPObject.unbind_s(self)").
>
> So I made a patch with the following assumptions in mind:
> • If multiple synchronous calls fail at the same time, only one reconnect should happen. This would avoid having a successful reconnect followed by another useless reconnect.
> • When a reconnection is needed, new synchronous calls should be blocked until the reconnection is done.
> • Pending synchronous calls need to finish before a reconnection can happen.
> • Asynchronous calls can happen while a reconnection takes place, but we want to avoid getting AttributeError exceptions because self._l is not present anymore.
> • Moreover, if asynchronous calls were blocked during a reconnection, we might wait indefinitely for a result with an unknown msgid. Asynchronous calls should thus be prepared to handle "LDAP connection invalid" LDAPError exceptions.
> • One big lock for all synchronous calls (i.e. in _apply_method_s) would have a big performance impact (around 60% slower in one of my test cases).
>
> This makes the patch unfortunately quite complex. Here is what is changed:
> • It mostly uses thread conditions to achieve those goals: one condition to control whether a reconnection is pending and another to control the amount of pending synchronous calls. This part of the patch is well commented.
> • The call to start_tls_s done inside reconnect is done directly via SimpleLDAPObject to avoid recursively calling itself.
> • self._l is not deleted explicitly anymore to avoid AttributeError exceptions. It is replaced by another object anyway, so it will get deleted eventually.
> •
> I tested the patch a lot, but only under Python 2.7. AFAIK it should also work in previous releases, but it should be tested.
>
> Let me know if you have any comments on this.
>
> Best regards,
> Jonathan Giannuzzi
> <ldapobject_threadsafe.diff>
More information about the python-ldap
mailing list