Skip to content

Merger tolerates backend disconnect (fixes #231) - #260

Merged
psi29a merged 3 commits into
masterfrom
fixes/merger-connection-lost
Aug 25, 2026
Merged

Merger tolerates backend disconnect (fixes #231)#260
psi29a merged 3 commits into
masterfrom
fixes/merger-connection-lost

Conversation

@psi29a

@psi29a psi29a commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Merger tolerates backend disconnect (fixes #231)

Two related pathologies fixed:

  1. _clientQueue used to blindly iterate self.clients and call send_multiResponse, which raised LDAPClientConnectionLostException if a backend had already lost its connection. Now we skip disconnected clients and swallow the race between .connected and .send raising.

  2. queue() sized its response Queue to len(self.clients) at first-response time, so a backend that never responded would hang the search forever. Replaced with an {expected, responses} dict whose expected count is decremented both on each arriving response and on each backend disconnect. When expected drops to zero the accumulated best response is forwarded; if no backend answered at all, a synthetic LDAPOther is returned.

_cbConnectionMade now hooks each backend's notifyOnDisconnect (from batch 5) to prune it from self.clients and nudge every outstanding merge. LDAPClientTestDriver in testutil grew a matching notifyOnDisconnect so tests can exercise the disconnect path.

Regression test in test_merger covers the "one of two backends dies before the search is dispatched" case. Full suite green on py3.11 / 3.13 / 3.14.

psi29a and others added 2 commits August 25, 2026 16:34
Two related pathologies fixed:

1. _clientQueue used to blindly iterate self.clients and call
   send_multiResponse, which raised LDAPClientConnectionLostException
   if a backend had already lost its connection. Now we skip
   disconnected clients and swallow the race between .connected and
   .send raising.

2. queue() sized its response Queue to len(self.clients) at
   first-response time, so a backend that never responded would hang
   the search forever. Replaced with an {expected, responses} dict
   whose expected count is decremented both on each arriving response
   and on each backend disconnect. When expected drops to zero the
   accumulated best response is forwarded; if no backend answered at
   all, a synthetic LDAPOther is returned.

_cbConnectionMade now hooks each backend's notifyOnDisconnect (from
batch 5) to prune it from self.clients and nudge every outstanding
merge. LDAPClientTestDriver in testutil grew a matching
notifyOnDisconnect so tests can exercise the disconnect path.

Regression test in test_merger covers the "one of two backends dies
before the search is dispatched" case. Full suite green on
py3.11 / 3.13 / 3.14.
@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.57143% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.52%. Comparing base (2158d74) to head (09dbe74).
⚠️ Report is 8 commits behind head on master.

Files with missing lines Patch % Lines
ldaptor/protocols/ldap/merger.py 68.42% 7 Missing and 5 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #260      +/-   ##
==========================================
+ Coverage   83.49%   83.52%   +0.03%     
==========================================
  Files          87       87              
  Lines       11666    11748      +82     
  Branches     1004     1012       +8     
==========================================
+ Hits         9740     9812      +72     
- Misses       1815     1821       +6     
- Partials      111      115       +4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Codecov flagged patch coverage at 78.57% on #260 (12 lines missed).
Add three focused tests:

* mid-flight death: seed a merge with one Done, then disconnect the
  peer; the decrement-and-check loop finishes the merge with the
  survivor's response.

* all backends die: seed an empty merge, drop both backends; the
  synthetic LDAPOther is emitted.

* defensive-paths sweep: _cbClientLost on an already-pruned proto,
  _checkComplete on an unknown id, _clientQueue skipping a client whose
  connected flag was flipped without notifyOnDisconnect firing, and
  _clientQueue swallowing LDAPClientConnectionLostException.

merger.py patch coverage is now 100% (module 85% -> 91%; remaining
holes are pre-existing: _failConnection, the TLS branch, the dead
assert, and the __main__ demo).
@psi29a
psi29a merged commit 1306fde into master Aug 25, 2026
6 checks passed
@psi29a
psi29a deleted the fixes/merger-connection-lost branch August 25, 2026 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Merger example - Connection lost

1 participant